diff --git a/Dockerfile b/Dockerfile index 74590a92..cca69042 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,6 +3,7 @@ FROM node:22-slim AS frontend RUN corepack enable && corepack prepare pnpm@10.32.1 --activate WORKDIR /app/web COPY web/package.json web/pnpm-lock.yaml ./ +COPY web/vendor/foliate-js ./vendor/foliate-js RUN --mount=type=cache,target=/root/.local/share/pnpm/store \ pnpm install --frozen-lockfile COPY web/ . diff --git a/Dockerfile.dev b/Dockerfile.dev index 2b2faf86..d70cf7cf 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -5,6 +5,7 @@ FROM node:22-slim AS frontend RUN corepack enable && corepack prepare pnpm@10.32.1 --activate WORKDIR /app/web COPY web/package.json web/pnpm-lock.yaml ./ +COPY web/vendor/foliate-js ./vendor/foliate-js RUN --mount=type=cache,target=/root/.local/share/pnpm/store \ pnpm install --frozen-lockfile COPY web/ . diff --git a/cmd/silo/main.go b/cmd/silo/main.go index 0f0334fe..172c7fa2 100644 --- a/cmd/silo/main.go +++ b/cmd/silo/main.go @@ -47,6 +47,7 @@ import ( "github.com/Silo-Server/silo-server/internal/clientip" "github.com/Silo-Server/silo-server/internal/config" "github.com/Silo-Server/silo-server/internal/database" + "github.com/Silo-Server/silo-server/internal/ebooks" evt "github.com/Silo-Server/silo-server/internal/events" "github.com/Silo-Server/silo-server/internal/historyimport" "github.com/Silo-Server/silo-server/internal/imagecache" @@ -941,6 +942,7 @@ func main() { var seasonRepo *catalog.SeasonRepository var episodeRepo *catalog.EpisodeRepository var audiobookEnricher *audiobooks.Enricher + var ebookEnricher *ebooks.Enricher if needsWorkers && deps.DB != nil && deps.FileRepo != nil { chainRepo := metadata.NewChainRepository(deps.DB) skippedRootRepo = metadata.NewSkippedRootRepository(deps.DB) @@ -1022,6 +1024,14 @@ func main() { personRepo, providerIDRepo, ) + ebookEnricher = ebooks.NewEnricher( + deps.DB, + chainRepo, + pluginResolver, + itemRepo, + personRepo, + providerIDRepo, + ) // Always wire the image resolver so plugin-prefixed URLs (e.g. // metadb://) can be resolved to presigned HTTP URLs in API responses. @@ -1044,6 +1054,9 @@ func main() { audiobookEnricher.SetImageCacher(imageCacher) audiobookEnricher.SetFFmpegPath(scanner.FFmpegPathFromFFprobe(scanner.FFprobePathFromFFmpeg(cfg.Playback.FFmpegPath))) } + if ebookEnricher != nil { + ebookEnricher.SetImageCacher(imageCacher) + } } matchWorker = metadata.NewMatchWorker(metadataService, deps.FileRepo, cfg.Matcher.Workers, cfg.Matcher.BatchSize, 30*time.Second) @@ -1586,6 +1599,9 @@ func main() { if audiobookEnricher != nil { taskMgr.Register(tasks.NewSyncAudiobookMetadataTask(audiobookEnricher)) } + if ebookEnricher != nil { + taskMgr.Register(tasks.NewSyncEbookMetadataTask(ebookEnricher)) + } if pluginInstallationStore != nil && pluginRuntimeConfigStore != nil && pluginService != nil { pluginTasks, err := plugins.NewTaskRegistryWithTypedResolver(pluginInstallationStore, pluginRuntimeConfigStore, pluginService).Tasks(appCtx) if err != nil { diff --git a/docs/pr124-ebooks-review-hardening.md b/docs/pr124-ebooks-review-hardening.md new file mode 100644 index 00000000..f727fd60 --- /dev/null +++ b/docs/pr124-ebooks-review-hardening.md @@ -0,0 +1,82 @@ +# PR #124 (ebooks) — Review & Hardening Pass + +Date: 2026-06-10 +Scope: full review of the ebook feature branch (`work/ebooks-reader-base`) followed by +fixes for every finding. Two review rounds were performed (a full-PR review, then an +adversarial edge-case/security pass over both the original code and the first round of +fixes), with all changes verified against the full Go test suite, the full web test +suite, production frontend build, ESLint/Prettier, and `make verify-local-paths`. +Commands assume the repository root is the cwd. + +## Security fixes + +- **Stored XSS via book content (high):** book sections render in same-origin blob + iframes with `allow-scripts` (WebKit requirement). A Content-Security-Policy is now + served with all SPA HTML responses (`internal/server/frontend.go`) — blob/srcdoc + documents inherit it, so `script-src 'self' 'wasm-unsafe-eval'` blocks script + execution from book content on all browsers. Threat model documented on the policy + constant. `X-Content-Type-Options: nosniff` added to frontend, jellycompat, and + ebook file responses. +- Ebook file serving can no longer fall through to `application/octet-stream` for an + admitted file (extension/container whitelist and MIME resolution share one resolver), + closing a browser-sniff escape combined with the `?token=` query-auth fallback. +- External links in books are intercepted app-side: http(s) only, opened with + `noopener,noreferrer` (blocks reverse tabnabbing and `javascript:` URLs). +- Request size caps (`http.MaxBytesReader`, 413) on progress/config/annotation writes; + `Content-Disposition` built with `mime.FormatMediaType` (RFC 5987, no header + injection); annotation PATCH is atomic (`SELECT ... FOR UPDATE`) with + presence-aware field semantics and invariant re-validation. +- Verified safe under adversarial review: no IDOR (all reader-state SQL scoped to + user+profile+content), no path traversal, range serving via stdlib, progress input + validation. Accepted residual: CSS-only loads inside book iframes can leak the + book's own content but cannot reach tokens, DOM, or navigation. + +## Reliability fixes + +- **Scanner missing-file reconciliation (critical class):** ebook scans now mark + missing files like video/audio, with real walk-failure tracking (failed roots are + excluded from deletion), symlinked-root support via the shared logical walker, and + the empty-root cleanup allowance — an unmounted share can no longer wipe a library. +- **Enrichment:** provider errors record failures (dedicated `ebook_enrichment_state` + backoff table, capped retries) instead of permanently stamping items done; + unconfigured chains and scan-window races skip without stamping. No longer shares + `media_items.refresh_failures` with the metadata refresh-debt system. +- Ebook items are created `pending` and promoted to `matched` on enrichment (backfill + migration included), making curated-metadata protection real: matched items keep + provider titles/people/series on re-scan (fill-empty only). +- PDF metadata: head+tail window scan (non-linearized PDFs keep the Info dict at the + end), delimiter-aware key matching, head-wins merge. `.md` dropped as an ebook + format; plain `.fb2` reads capped like `.fbz`. +- FK-cascade indexes added for the reader-state tables. + +## Consistency & correctness fixes + +- Hidden-history (`user_history_hidden_items`) gating applied to every ebook progress + surface (watched/in-progress filters, all sort plans, Continue Reading, sort + metrics, `Played` state, recommendation signals) with the exact video semantics. +- `GetItemWatchers` counts distinct watchers (no binge inflation) and the + `minWatchers` floor counts distinct accounts, not profiles; ebook reading now feeds + implicit taste signals and taste-seed candidates; Continue Reading pages past + dismissals and dedupes across pages. +- The 0.9 finished threshold is centralized (`models.EbookFinishedProgressThreshold`). +- Reader frontend: open-flow race teardown (no wrong-file progress saves), ordered + progress writes with `pagehide`/`visibilitychange` flush, settings no longer + clobbered by late server config, TTS stop actually stops, Media Session cleanup, + 512 MiB download guard, fraction bookmarks navigable. + +## Feature completion + +- Native read-state endpoints: `POST/DELETE /watched/{id}` and `/history/remove` + accept ebook content IDs (mark read = progress 1.0 preserving location; mark unread + mirrors video unwatch and clears position; history removal hides without losing + position). Web UI exposes Mark Read/Unread on the item page and card menus, with + Continue Reading dismiss copy and dismissal-path ID encoding fixed. + +## Client follow-ups (Android / Apple) + +- Ebook leaf user data encodes the reading ratio as `position_seconds` (0..1) with + `duration_seconds = 1`; do not render absolute times for ebooks. +- `/watched/{id}` returns `{type: "ebook", affected_count: 1, played: bool}` and the + existing watched SSE event fires. +- Reading progress is deliberately per-book (cross-format): one position per content + ID. diff --git a/docs/superpowers/plans/2026-06-08-ebook-reader-full-parity.md b/docs/superpowers/plans/2026-06-08-ebook-reader-full-parity.md new file mode 100644 index 00000000..729f6992 --- /dev/null +++ b/docs/superpowers/plans/2026-06-08-ebook-reader-full-parity.md @@ -0,0 +1,301 @@ +# Ebook Reader Full Parity Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Bring the core ebook reader up to the personal reader feature set with server-persisted config, annotations/bookmarks, selection tools, reading aids, and advanced settings. + +**Architecture:** Keep ebook reading in core, matching audiobooks: core owns the ebook media type and reader behavior; metadata remains plugin-provided. Extend the existing `/ebooks/{content_id}` reader API and `EbookReaderHandler` rather than creating a separate plugin-style reader subsystem. Persist per-user/per-profile/per-book state in Postgres so settings, annotations, and bookmarks roam across devices. + +**Tech Stack:** Go/Chi handlers, pgx/Postgres migrations, React 19, TanStack Query, Foliate/readest reader wrapper, Vitest, Go unit tests. + +--- + +## File Structure + +- `migrations/sql/20260608000300_ebook_reader_state.sql` + - Creates `ebook_reader_config` and `ebook_reader_annotations`. +- `internal/api/handlers/ebook_reader.go` + - Adds reader config and annotation types, store interfaces, handlers, validation, and Postgres store methods. +- `internal/api/handlers/ebook_reader_test.go` + - Adds handler tests for config, annotation CRUD, access scoping, and validation. +- `internal/api/router.go` + - Wires config/annotation stores and routes under `/ebooks/{content_id}`. +- `web/src/reader/FoliateBookReader.tsx` + - Adds selection/annotation/readable-text/content-popup reader handles and applies saved config. +- `web/src/pages/EbookReader.tsx` + - Adds server-backed settings, annotation/bookmark UI, selection tools, TTS controls, wake-lock/e-ink controls, and advanced reader controls. +- `web/src/hooks/useTTS.ts`, `web/src/hooks/useScreenWakeLock.ts`, `web/src/hooks/useEinkMode.ts` + - Port focused personal-reader hooks into core web. +- `web/src/reader/ebookReaderApi.ts` + - Centralizes reader config and annotation API calls for tests and page usage. +- `web/src/pages/EbookReader.test.tsx`, `web/src/reader/FoliateBookReader.test.ts` + - Adds UI and wrapper tests. + +## Task 1: Server-Persisted Reader Config + +**Files:** +- Create: `migrations/sql/20260608000300_ebook_reader_state.sql` +- Modify: `internal/api/handlers/ebook_reader.go` +- Modify: `internal/api/handlers/ebook_reader_test.go` +- Modify: `internal/api/router.go` +- Create: `web/src/reader/ebookReaderApi.ts` +- Modify: `web/src/pages/EbookReader.tsx` +- Modify: `web/src/pages/EbookReader.test.tsx` + +- [ ] **Step 1: Write failing Go handler tests** + +Add tests that: +- `GET /ebooks/{content_id}/reader-config` returns `{ "config": {} }` when no row exists. +- `PUT /ebooks/{content_id}/reader-config` saves JSON config for the authenticated user/profile/content. +- inaccessible content returns 404 through the existing `ItemAccess` path. +- invalid non-object config returns 400. + +Run: + +```bash +go test ./internal/api/handlers -run 'TestEbookReader.*Config' -count=1 +``` + +Expected: fail because config store/handlers do not exist. + +- [ ] **Step 2: Add migration** + +Create `ebook_reader_config`: + +```sql +CREATE TABLE ebook_reader_config ( + user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, + profile_id TEXT NOT NULL, + content_id TEXT NOT NULL REFERENCES media_items(content_id) ON DELETE CASCADE, + config JSONB NOT NULL DEFAULT '{}'::jsonb, + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + PRIMARY KEY (user_id, profile_id, content_id) +); + +CREATE INDEX ebook_reader_config_profile_updated + ON ebook_reader_config (user_id, profile_id, updated_at DESC); +``` + +- [ ] **Step 3: Implement config store and handlers** + +Add `EbookReaderConfig`, `EbookReaderConfigStore`, `HandleGetConfig`, `HandleSaveConfig`, and `PGEbookReaderConfigStore` methods in `ebook_reader.go`. + +Validation: +- authenticated user required +- `content_id` required +- `config` must decode to a JSON object +- content access checked with `FileAuthorizer.ItemAccess.EnsureAccessible` + +- [ ] **Step 4: Wire routes and store** + +In `internal/api/router.go`, create the store when `deps.DB != nil`, assign it to `ebookReaderHandler`, and add: + +```go +r.Get("/{content_id}/reader-config", ebookReaderHandler.HandleGetConfig) +r.Put("/{content_id}/reader-config", ebookReaderHandler.HandleSaveConfig) +``` + +- [ ] **Step 5: Add web API wrapper** + +Create `web/src/reader/ebookReaderApi.ts` with: + +```ts +export function ebookReaderConfigPath(contentID: string): string +export async function fetchEbookReaderConfig(contentID: string): Promise> +export async function saveEbookReaderConfig(contentID: string, config: Record): Promise> +``` + +- [ ] **Step 6: Move settings persistence from localStorage to server** + +Update `EbookReader.tsx` to fetch reader config on load and save settings through the config endpoint. Keep a local fallback only until the server value arrives. + +- [ ] **Step 7: Verify and commit** + +Run: + +```bash +go test ./internal/api/handlers -run 'TestEbookReader.*Config' -count=1 +cd web && pnpm test src/pages/EbookReader.test.tsx src/reader/FoliateBookReader.test.ts --run +cd web && pnpm build +``` + +Commit: + +```bash +git add migrations/sql/20260608000300_ebook_reader_state.sql internal/api/handlers/ebook_reader.go internal/api/handlers/ebook_reader_test.go internal/api/router.go web/src/reader/ebookReaderApi.ts web/src/pages/EbookReader.tsx web/src/pages/EbookReader.test.tsx +git commit -m "feat: persist ebook reader config" +``` + +## Task 2: Server-Persisted Annotations And Bookmarks + +**Files:** +- Modify: `migrations/sql/20260608000300_ebook_reader_state.sql` +- Modify: `internal/api/handlers/ebook_reader.go` +- Modify: `internal/api/handlers/ebook_reader_test.go` +- Modify: `internal/api/router.go` +- Modify: `web/src/reader/ebookReaderApi.ts` +- Modify: `web/src/reader/FoliateBookReader.tsx` +- Modify: `web/src/pages/EbookReader.tsx` + +- [ ] **Step 1: Write failing annotation handler tests** + +Cover list, create, update, delete. Test annotation fields: + +```json +{ + "kind": "highlight", + "cfi_range": "epubcfi(/6/4,/1:0,/1:12)", + "selected_text": "sample text", + "note": "note text", + "style": "highlight", + "color": "#facc15" +} +``` + +Run: + +```bash +go test ./internal/api/handlers -run 'TestEbookReader.*Annotation|TestEbookReader.*Bookmark' -count=1 +``` + +Expected: fail because annotation APIs do not exist. + +- [ ] **Step 2: Add annotation table** + +Extend migration with: + +```sql +CREATE TABLE ebook_reader_annotations ( + id TEXT PRIMARY KEY, + user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, + profile_id TEXT NOT NULL, + content_id TEXT NOT NULL REFERENCES media_items(content_id) ON DELETE CASCADE, + kind TEXT NOT NULL CHECK (kind IN ('highlight', 'note', 'bookmark')), + cfi_range TEXT, + location TEXT, + selected_text TEXT NOT NULL DEFAULT '', + note TEXT NOT NULL DEFAULT '', + style TEXT NOT NULL DEFAULT 'highlight', + color TEXT NOT NULL DEFAULT '#facc15', + metadata JSONB NOT NULL DEFAULT '{}'::jsonb, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + CHECK ((kind = 'bookmark' AND location IS NOT NULL) OR (kind <> 'bookmark' AND cfi_range IS NOT NULL)) +); + +CREATE INDEX ebook_reader_annotations_book + ON ebook_reader_annotations (user_id, profile_id, content_id, updated_at DESC); +``` + +- [ ] **Step 3: Implement annotation store and handlers** + +Add handlers: +- `GET /ebooks/{content_id}/annotations` +- `POST /ebooks/{content_id}/annotations` +- `PATCH /ebooks/{content_id}/annotations/{annotation_id}` +- `DELETE /ebooks/{content_id}/annotations/{annotation_id}` + +- [ ] **Step 4: Wire Foliate annotation drawing and selection** + +Extend `FoliateBookReaderHandle` with: +- `createSelectionAnnotation()` +- `clearSelection()` + +Draw stored annotations using Foliate `addAnnotation`. + +- [ ] **Step 5: Add reader UI** + +Add: +- selection popover +- highlight button +- note button +- bookmark button +- annotation list in the side panel +- delete/update note controls + +- [ ] **Step 6: Verify and commit** + +Run handler tests, web tests, and build. Commit: + +```bash +git commit -m "feat: add ebook annotations and bookmarks" +``` + +## Task 3: Reader Tools, TTS, And Reading Aids + +**Files:** +- Create: `web/src/hooks/useTTS.ts` +- Create: `web/src/hooks/useScreenWakeLock.ts` +- Create: `web/src/hooks/useEinkMode.ts` +- Modify: `web/src/reader/FoliateBookReader.tsx` +- Modify: `web/src/pages/EbookReader.tsx` +- Modify: `web/src/pages/EbookReader.test.tsx` + +- [ ] **Step 1: Port focused hooks from personal reader** + +Bring over: +- Web Speech TTS controller +- screen wake lock hook +- e-ink body-class hook + +- [ ] **Step 2: Add readable text handle** + +Expose `getReadableText()` from `FoliateBookReader` for TTS. + +- [ ] **Step 3: Add UI controls** + +Add side-panel controls for: +- Speak current text +- Pause/resume/stop +- voice/rate/pitch +- wake lock +- e-ink mode + +- [ ] **Step 4: Add content popups and helpers** + +Add selection popover actions: +- define +- translate + +Add reader content popup handling for footnotes/images/tables when Foliate emits them. + +- [ ] **Step 5: Verify and commit** + +Run focused web tests and build. Commit: + +```bash +git commit -m "feat: add ebook reader tools and aids" +``` + +## Task 4: Advanced Reader Settings + +**Files:** +- Modify: `web/src/reader/FoliateBookReader.tsx` +- Modify: `web/src/pages/EbookReader.tsx` +- Modify: `web/src/pages/EbookReader.test.tsx` + +- [ ] **Step 1: Add advanced reader settings** + +Add server-persisted settings for: +- RTL +- writing mode +- zoom/scale +- brightness +- hyphenation toggle +- custom font selection/upload if API support is added in a later task + +- [ ] **Step 2: Verify and commit** + +Run focused tests and build. Commit: + +```bash +git commit -m "feat: add ebook advanced reader settings" +``` + +## Self-Review + +- The plan covers server persistence first, matching the user's explicit preference. +- The plan keeps ebook features in core and metadata in plugins, matching the audiobook pattern. +- The plan avoids extra ebook catalog tables; added tables are reader-user-state only. +- The biggest implementation risk is annotation rendering against Foliate selection APIs; Task 2 isolates it after backend persistence exists. +- The migration filename is intentionally after existing ebook migrations and can be adjusted if another migration is added before implementation. diff --git a/docs/superpowers/plans/2026-06-08-ebook-reader-shell-parity.md b/docs/superpowers/plans/2026-06-08-ebook-reader-shell-parity.md new file mode 100644 index 00000000..73ed0a0d --- /dev/null +++ b/docs/superpowers/plans/2026-06-08-ebook-reader-shell-parity.md @@ -0,0 +1,68 @@ +# Ebook Reader Shell Parity Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Bring the core ebook reader up to the first useful parity slice from `personal/silo-plugin-ebooks`: TOC, search, progress scrub, reader settings, and keyboard navigation. + +**Architecture:** Keep the current core reader API and persistence model. Extend `FoliateBookReader` with imperative methods for TOC, search, href/fraction navigation, and runtime style updates; keep `EbookReader` responsible for the chrome/panels/settings state. Store reader preferences in localStorage for this first local slice so no backend schema is added. + +**Tech Stack:** React 19, React Router, TanStack Query, foliate-js custom element, existing Silo UI components, Vitest. + +--- + +### Task 1: Reader Handle Capabilities + +**Files:** +- Modify: `web/src/reader/FoliateBookReader.tsx` +- Test: `web/src/reader/FoliateBookReader.test.ts` + +- [ ] Add exported types `ReaderSettings`, `ReaderSearchOptions`, `ReaderSearchResult`, and `ReaderReadyState`. +- [ ] Extend `FoliateBookReaderHandle` with `goToFraction`, `goTo`, `search`, `clearSearch`, and `applySettings`. +- [ ] Add a `settings?: ReaderSettings` prop and convert `readerStyles()` to accept settings. +- [ ] Preserve existing progress save/restore behavior. +- [ ] Verify with `pnpm vitest run src/reader/FoliateBookReader.test.ts`. + +### Task 2: TOC and Search Panel + +**Files:** +- Modify: `web/src/pages/EbookReader.tsx` +- Test: `web/src/pages/EbookReader.test.tsx` + +- [ ] Add panel state for `toc`, `searchResults`, `searchTerm`, and current tab. +- [ ] Render side panel tabs for Contents, Search, and Settings. +- [ ] Wire TOC items to `readerRef.current.goTo(href)`. +- [ ] Wire search form to `readerRef.current.search(term)` and result clicks to `goTo(cfi)`. +- [ ] Verify with `pnpm vitest run src/pages/EbookReader.test.tsx`. + +### Task 3: Reader Settings + +**Files:** +- Modify: `web/src/pages/EbookReader.tsx` +- Modify: `web/src/reader/FoliateBookReader.tsx` +- Test: `web/src/pages/EbookReader.test.tsx` + +- [ ] Add localStorage-backed settings for theme, font size, font family, line height, margin, max width, spread, and flow. +- [ ] Render compact controls in the Settings tab. +- [ ] Apply settings live through `FoliateBookReader`. +- [ ] Verify settings persist across remount in tests. + +### Task 4: Progress Scrub and Keyboard Navigation + +**Files:** +- Modify: `web/src/pages/EbookReader.tsx` +- Modify: `web/src/reader/FoliateBookReader.tsx` +- Test: `web/src/pages/EbookReader.test.tsx` + +- [ ] Add a progress range input in the header. +- [ ] Call `goToFraction` on commit/change and update display from relocate events. +- [ ] Add ArrowLeft/ArrowRight keyboard navigation on the reader page. +- [ ] Verify with page-level tests. + +### Task 5: Local Verification + +**Files:** +- No source changes. + +- [ ] Run `cd web && pnpm run build`. +- [ ] Run `GOWORK=off go test ./internal/api/handlers ./internal/scanner`. +- [ ] Keep changes local; do not push. diff --git a/go.mod b/go.mod index f3013dc9..bb9ec600 100644 --- a/go.mod +++ b/go.mod @@ -33,6 +33,7 @@ require ( github.com/zishang520/socket.io/v2 v2.5.0 go.n16f.net/thumbhash v1.1.0 golang.org/x/image v0.41.0 + golang.org/x/net v0.55.0 google.golang.org/genproto/googleapis/rpc v0.0.0-20260420184626-e10c466a9529 gopkg.in/natefinch/lumberjack.v2 v2.2.1 ) @@ -70,7 +71,6 @@ require ( github.com/zishang520/webtransport-go v0.9.1 // indirect go.opentelemetry.io/otel/sdk/metric v1.41.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/net v0.55.0 // indirect ) require ( diff --git a/internal/api/handlers/catalog.go b/internal/api/handlers/catalog.go index 04b33405..38eb6efe 100644 --- a/internal/api/handlers/catalog.go +++ b/internal/api/handlers/catalog.go @@ -10,6 +10,7 @@ import ( "strings" "time" + apimw "github.com/Silo-Server/silo-server/internal/api/middleware" "github.com/Silo-Server/silo-server/internal/catalog" "github.com/Silo-Server/silo-server/internal/models" "github.com/Silo-Server/silo-server/internal/sections" @@ -89,6 +90,7 @@ func (h *CatalogHandler) HandleGetCatalog(w http.ResponseWriter, r *http.Request h.itemsH.accessFilter(r), overlaySummaries, store, + apimw.GetUserID(r.Context()), profileID, ) items := make([]itemListResponse, 0, len(result.Items)) diff --git a/internal/api/handlers/catalog_resources.go b/internal/api/handlers/catalog_resources.go index efce0399..efb38368 100644 --- a/internal/api/handlers/catalog_resources.go +++ b/internal/api/handlers/catalog_resources.go @@ -497,8 +497,8 @@ func (h *CatalogResourceHandler) enrichItemDetail(r *http.Request, detail *catal detail.SeasonUserData = h.items.getAggregateUserData(r, episodes) } } - case "movie", "episode", "audiobook": - detail.SeasonUserData = h.items.getLeafUserData(r, detail.ContentID) + case "movie", "episode", "audiobook", "ebook": + detail.SeasonUserData = h.items.getLeafUserData(r, detail.ContentID, detail.Type) applyEffectiveEditionPreference(detail.SeasonUserData, &detail.EffectiveVersionEditionKey) } diff --git a/internal/api/handlers/ebook_read_state.go b/internal/api/handlers/ebook_read_state.go new file mode 100644 index 00000000..e6ad71c4 --- /dev/null +++ b/internal/api/handlers/ebook_read_state.go @@ -0,0 +1,150 @@ +package handlers + +import ( + "context" + "fmt" + "time" + + "github.com/Silo-Server/silo-server/internal/catalog" + "github.com/Silo-Server/silo-server/internal/models" +) + +// Ebook read state lives exclusively in ebook_reader_progress: every consumer +// (user-state Played, catalog watched/in_progress filters, Continue Reading, +// sort metrics, recommendations) keys off progress crossing +// models.EbookFinishedProgressThreshold. The native watched endpoints therefore +// write that table instead of user_watch_progress/user_watch_history. The +// helpers live in this package — next to the ebook progress store and types — +// because the handler layer is the only producer today: the webhook-import +// matcher (internal/historyimport) only matches movie/series/episode kinds, so +// imported mark-played events can never resolve to an ebook content ID. + +// EbookReadStateStore is the ebook_reader_progress surface needed to set or +// clear an ebook's read state from the watched endpoints. +// *PGEbookReaderProgressStore implements it. +type EbookReadStateStore interface { + Get(ctx context.Context, userID int, profileID string, contentID string) (*EbookReaderProgress, error) + Upsert(ctx context.Context, progress EbookReaderProgress) error + Delete(ctx context.Context, userID int, profileID string, contentID string) error +} + +// EbookReaderProgressReadWriter is the full progress-store surface the items +// handler needs: batch reads for list/user-state responses plus read-state +// writes for the watched endpoints. +type EbookReaderProgressReadWriter interface { + EbookReaderProgressLister + EbookReadStateStore +} + +// markEbookRead marks an ebook as finished by upserting its reader-progress +// row with progress = 1.0 (>= models.EbookFinishedProgressThreshold). An +// existing row keeps its file_id and location so the reader still reopens at +// the user's last position even though the book is marked read; a missing row +// references the item's preferred reader file (resolved lazily via +// defaultFileID, since ebook_reader_progress.file_id is NOT NULL) with an +// empty location meaning "start at the beginning". A fresh updated_at also +// resurfaces a previously hidden book, mirroring how re-watching resurfaces +// hidden video history. +func markEbookRead( + ctx context.Context, + store EbookReadStateStore, + userID int, + profileID, contentID string, + now time.Time, + defaultFileID func(ctx context.Context) (int, error), +) error { + existing, err := store.Get(ctx, userID, profileID, contentID) + if err != nil { + return err + } + progress := EbookReaderProgress{ + UserID: userID, + ProfileID: profileID, + ContentID: contentID, + Progress: 1.0, + UpdatedAt: now, + } + if existing != nil { + progress.FileID = existing.FileID + progress.Location = existing.Location + } else { + fileID, err := defaultFileID(ctx) + if err != nil { + return err + } + progress.FileID = fileID + } + return store.Upsert(ctx, progress) +} + +// markEbookUnread clears an ebook's read state by deleting its reader-progress +// row — the direct analog of the video unwatch path, where +// userstore.ClearProgress DELETEs the user_watch_progress row (dropping the +// resume position along with the watched flag). +func markEbookUnread( + ctx context.Context, + store EbookReadStateStore, + userID int, + profileID, contentID string, +) error { + return store.Delete(ctx, userID, profileID, contentID) +} + +// setEbookReadState applies the watched toggle for an ebook target resolved by +// resolveWatchedTargets, which has already enforced the access filter on the +// item. Scoping matches the rest of the ebook progress code: user_id + +// profile_id. +func (h *ItemsHandler) setEbookReadState( + ctx context.Context, + userID int, + profileID, contentID string, + read bool, + filter catalog.AccessFilter, +) error { + if h.ebookReadStateStore == nil { + return fmt.Errorf("ebook reader progress store is not configured") + } + if !read { + return markEbookUnread(ctx, h.ebookReadStateStore, userID, profileID, contentID) + } + return markEbookRead(ctx, h.ebookReadStateStore, userID, profileID, contentID, time.Now().UTC(), func(ctx context.Context) (int, error) { + return h.defaultEbookFileID(ctx, contentID, filter) + }) +} + +// defaultEbookFileID resolves the file a fresh mark-read progress row should +// reference, respecting the caller's access filter. +func (h *ItemsHandler) defaultEbookFileID(ctx context.Context, contentID string, filter catalog.AccessFilter) (int, error) { + if h.fileRepo == nil { + return 0, catalog.ErrItemNotFound + } + files, err := h.fileRepo.GetByContentID(ctx, contentID) + if err != nil { + return 0, err + } + file := preferredEbookReadFile(catalog.FilterMediaFilesByAccess(files, filter)) + if file == nil { + return 0, catalog.ErrItemNotFound + } + return file.ID, nil +} + +// preferredEbookReadFile mirrors the web reader's default file choice +// (web/src/pages/ItemDetail/EbookContent.tsx preferredReadVersion): prefer an +// EPUB version, otherwise the first reader-supported ebook file. Callers must +// pass access-filtered files. +func preferredEbookReadFile(files []*models.MediaFile) *models.MediaFile { + var first *models.MediaFile + for _, file := range files { + if !isEbookFile(file) { + continue + } + if ebookReaderFormat(file.FilePath, file.Container) == "epub" { + return file + } + if first == nil { + first = file + } + } + return first +} diff --git a/internal/api/handlers/ebook_read_state_test.go b/internal/api/handlers/ebook_read_state_test.go new file mode 100644 index 00000000..94464695 --- /dev/null +++ b/internal/api/handlers/ebook_read_state_test.go @@ -0,0 +1,230 @@ +package handlers + +import ( + "context" + "errors" + "testing" + "time" + + "github.com/Silo-Server/silo-server/internal/catalog" + "github.com/Silo-Server/silo-server/internal/models" +) + +// fakeEbookReadStateStore is an in-memory EbookReaderProgressReadWriter keyed +// by content ID for a single (user, profile) scope. +type fakeEbookReadStateStore struct { + rows map[string]EbookReaderProgress +} + +func newFakeEbookReadStateStore() *fakeEbookReadStateStore { + return &fakeEbookReadStateStore{rows: map[string]EbookReaderProgress{}} +} + +func (f *fakeEbookReadStateStore) Get(_ context.Context, _ int, _ string, contentID string) (*EbookReaderProgress, error) { + row, ok := f.rows[contentID] + if !ok { + return nil, nil + } + return &row, nil +} + +func (f *fakeEbookReadStateStore) Upsert(_ context.Context, progress EbookReaderProgress) error { + f.rows[progress.ContentID] = progress + return nil +} + +func (f *fakeEbookReadStateStore) Delete(_ context.Context, _ int, _ string, contentID string) error { + delete(f.rows, contentID) + return nil +} + +func (f *fakeEbookReadStateStore) ListByContentIDs(_ context.Context, _ int, _ string, contentIDs []string) (map[string]EbookReaderProgress, error) { + result := make(map[string]EbookReaderProgress, len(contentIDs)) + for _, contentID := range contentIDs { + if row, ok := f.rows[contentID]; ok { + result[contentID] = row + } + } + return result, nil +} + +// fakeEbookFileProvider implements EpisodeFileProvider for ebook file lookups. +type fakeEbookFileProvider struct { + files map[string][]*models.MediaFile + err error +} + +func (f *fakeEbookFileProvider) GetByContentID(_ context.Context, contentID string) ([]*models.MediaFile, error) { + if f.err != nil { + return nil, f.err + } + return f.files[contentID], nil +} + +func (f *fakeEbookFileProvider) GetByEpisodeID(context.Context, string) ([]*models.MediaFile, error) { + return nil, nil +} + +func (f *fakeEbookFileProvider) ListByContentIDs(context.Context, []string) (map[string][]*models.MediaFile, error) { + return nil, nil +} + +func TestMarkEbookReadPreservesExistingFileAndLocation(t *testing.T) { + ctx := context.Background() + store := newFakeEbookReadStateStore() + store.rows["ebook-1"] = EbookReaderProgress{ + UserID: 42, + ProfileID: "profile-1", + ContentID: "ebook-1", + FileID: 7, + Location: "epubcfi(/6/14!/4/2/14)", + Progress: 0.42, + UpdatedAt: time.Date(2026, 6, 1, 8, 0, 0, 0, time.UTC), + } + now := time.Date(2026, 6, 9, 12, 0, 0, 0, time.UTC) + + err := markEbookRead(ctx, store, 42, "profile-1", "ebook-1", now, func(context.Context) (int, error) { + t.Fatal("defaultFileID must not be called when a progress row exists") + return 0, nil + }) + if err != nil { + t.Fatalf("markEbookRead: %v", err) + } + + row := store.rows["ebook-1"] + if row.Progress != 1.0 { + t.Fatalf("progress = %v, want 1.0", row.Progress) + } + if row.FileID != 7 || row.Location != "epubcfi(/6/14!/4/2/14)" { + t.Fatalf("file/location not preserved: %+v", row) + } + if !row.UpdatedAt.Equal(now) { + t.Fatalf("updated_at = %v, want %v", row.UpdatedAt, now) + } +} + +func TestMarkEbookReadUsesDefaultFileWhenNoProgressExists(t *testing.T) { + ctx := context.Background() + store := newFakeEbookReadStateStore() + now := time.Date(2026, 6, 9, 12, 0, 0, 0, time.UTC) + + err := markEbookRead(ctx, store, 42, "profile-1", "ebook-1", now, func(context.Context) (int, error) { + return 12, nil + }) + if err != nil { + t.Fatalf("markEbookRead: %v", err) + } + + row, ok := store.rows["ebook-1"] + if !ok { + t.Fatal("expected a progress row to be created") + } + if row.Progress != 1.0 || row.FileID != 12 || row.Location != "" { + t.Fatalf("row = %+v, want progress 1.0 with file 12 and empty location", row) + } + if row.UserID != 42 || row.ProfileID != "profile-1" { + t.Fatalf("row scope = user %d profile %q, want user 42 profile-1", row.UserID, row.ProfileID) + } +} + +func TestMarkEbookReadPropagatesDefaultFileError(t *testing.T) { + ctx := context.Background() + store := newFakeEbookReadStateStore() + + err := markEbookRead(ctx, store, 42, "profile-1", "ebook-1", time.Now().UTC(), func(context.Context) (int, error) { + return 0, catalog.ErrItemNotFound + }) + if !errors.Is(err, catalog.ErrItemNotFound) { + t.Fatalf("err = %v, want catalog.ErrItemNotFound", err) + } + if len(store.rows) != 0 { + t.Fatalf("no row must be written on error, got %+v", store.rows) + } +} + +func TestMarkEbookUnreadDeletesProgressRow(t *testing.T) { + ctx := context.Background() + store := newFakeEbookReadStateStore() + store.rows["ebook-1"] = EbookReaderProgress{ContentID: "ebook-1", FileID: 7, Progress: 1.0} + + if err := markEbookUnread(ctx, store, 42, "profile-1", "ebook-1"); err != nil { + t.Fatalf("markEbookUnread: %v", err) + } + if _, ok := store.rows["ebook-1"]; ok { + t.Fatal("progress row must be deleted on mark unread") + } +} + +func TestPreferredEbookReadFilePrefersEpub(t *testing.T) { + pdf := &models.MediaFile{ID: 1, BaseType: "ebook", FilePath: "/books/a.pdf", Container: "pdf"} + epub := &models.MediaFile{ID: 2, BaseType: "ebook", FilePath: "/books/a.epub", Container: "epub"} + video := &models.MediaFile{ID: 3, BaseType: "movie", FilePath: "/movies/a.mkv", Container: "mkv"} + + if got := preferredEbookReadFile([]*models.MediaFile{video, pdf, epub}); got == nil || got.ID != 2 { + t.Fatalf("preferredEbookReadFile = %+v, want epub file 2", got) + } + if got := preferredEbookReadFile([]*models.MediaFile{video, pdf}); got == nil || got.ID != 1 { + t.Fatalf("preferredEbookReadFile = %+v, want first reader-supported file 1", got) + } + if got := preferredEbookReadFile([]*models.MediaFile{video}); got != nil { + t.Fatalf("preferredEbookReadFile = %+v, want nil without ebook files", got) + } +} + +func TestSetEbookReadStateMarkReadShowsPlayedUserState(t *testing.T) { + ctx := context.Background() + store := newFakeEbookReadStateStore() + handler := &ItemsHandler{ + ebookProgressStore: store, + ebookReadStateStore: store, + fileRepo: &fakeEbookFileProvider{files: map[string][]*models.MediaFile{ + "ebook-1": {{ID: 5, BaseType: "ebook", FilePath: "/books/b.epub", Container: "epub"}}, + }}, + } + + if err := handler.setEbookReadState(ctx, 42, "profile-1", "ebook-1", true, catalog.AccessFilter{}); err != nil { + t.Fatalf("setEbookReadState(read): %v", err) + } + + userStore := newProfileTestStore(t) + items := []*models.MediaItem{{ContentID: "ebook-1", Type: "ebook", Title: "Book"}} + states, err := resolveItemUserStatesWithOptions(ctx, userStore, "profile-1", nil, items, itemUserStateOptions{ + UserID: 42, + EbookProgressStore: store, + }) + if err != nil { + t.Fatalf("resolveItemUserStatesWithOptions: %v", err) + } + if states["ebook-1"] == nil || !states["ebook-1"].Played { + t.Fatalf("user state = %+v, want played after mark read", states["ebook-1"]) + } + + // A marked-read book is finished, not in progress: the reader-progress + // response derived from the row must exclude it from Continue Reading. + row := store.rows["ebook-1"] + if row.Progress < models.EbookFinishedProgressThreshold { + t.Fatalf("progress %v must cross the finished threshold", row.Progress) + } + + if err := handler.setEbookReadState(ctx, 42, "profile-1", "ebook-1", false, catalog.AccessFilter{}); err != nil { + t.Fatalf("setEbookReadState(unread): %v", err) + } + if _, ok := store.rows["ebook-1"]; ok { + t.Fatal("mark unread must delete the reader progress row") + } +} + +func TestSetEbookReadStateFailsWithoutAccessibleFile(t *testing.T) { + ctx := context.Background() + store := newFakeEbookReadStateStore() + handler := &ItemsHandler{ + ebookProgressStore: store, + ebookReadStateStore: store, + fileRepo: &fakeEbookFileProvider{}, + } + + err := handler.setEbookReadState(ctx, 42, "profile-1", "ebook-1", true, catalog.AccessFilter{}) + if !errors.Is(err, catalog.ErrItemNotFound) { + t.Fatalf("err = %v, want catalog.ErrItemNotFound", err) + } +} diff --git a/internal/api/handlers/ebook_reader.go b/internal/api/handlers/ebook_reader.go new file mode 100644 index 00000000..40d4865e --- /dev/null +++ b/internal/api/handlers/ebook_reader.go @@ -0,0 +1,1069 @@ +package handlers + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "mime" + "net/http" + "os" + "path/filepath" + "strconv" + "strings" + "time" + + "github.com/go-chi/chi/v5" + "github.com/google/uuid" + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgxpool" + + apimw "github.com/Silo-Server/silo-server/internal/api/middleware" + "github.com/Silo-Server/silo-server/internal/catalog" + "github.com/Silo-Server/silo-server/internal/models" +) + +// EbookReaderProgress is the persisted reader position for one profile and ebook. +type EbookReaderProgress struct { + UserID int `json:"-"` + ProfileID string `json:"-"` + ContentID string `json:"content_id"` + FileID int `json:"file_id"` + Location string `json:"location"` + Progress float64 `json:"progress"` + UpdatedAt time.Time `json:"updated_at"` +} + +type EbookReaderProgressStore interface { + Get(ctx context.Context, userID int, profileID string, contentID string) (*EbookReaderProgress, error) + Upsert(ctx context.Context, progress EbookReaderProgress) error +} + +// EbookReaderConfig is persisted reader configuration for one profile and ebook. +type EbookReaderConfig struct { + UserID int `json:"-"` + ProfileID string `json:"-"` + ContentID string `json:"content_id"` + Config json.RawMessage `json:"config"` + UpdatedAt time.Time `json:"updated_at"` +} + +type EbookReaderConfigStore interface { + Get(ctx context.Context, userID int, profileID string, contentID string) (*EbookReaderConfig, error) + Upsert(ctx context.Context, config EbookReaderConfig) error +} + +// EbookReaderAnnotation is a persisted reader annotation or bookmark. +type EbookReaderAnnotation struct { + ID string `json:"id"` + UserID int `json:"-"` + ProfileID string `json:"-"` + ContentID string `json:"content_id"` + Kind string `json:"kind"` + CFIRange string `json:"cfi_range,omitempty"` + Location string `json:"location,omitempty"` + SelectedText string `json:"selected_text"` + Note string `json:"note"` + Style string `json:"style"` + Color string `json:"color"` + Metadata json.RawMessage `json:"metadata"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +type EbookReaderAnnotationStore interface { + List(ctx context.Context, userID int, profileID string, contentID string) ([]EbookReaderAnnotation, error) + Create(ctx context.Context, annotation EbookReaderAnnotation) error + // Update atomically rewrites the scoped annotation: the stored row is + // read under a write lock, merge is invoked with the current value, and + // the merged result is written back in the same transaction so concurrent + // PATCHes cannot lose updates. It returns (nil, nil) when no annotation + // matches the scope and propagates merge errors unchanged. + Update( + ctx context.Context, + userID int, + profileID string, + contentID string, + annotationID string, + merge func(existing EbookReaderAnnotation) (EbookReaderAnnotation, error), + ) (*EbookReaderAnnotation, error) + Delete(ctx context.Context, userID int, profileID string, contentID string, annotationID string) error +} + +// EbookReaderHandler serves ebook files for the in-app reader. +type EbookReaderHandler struct { + FileAuthorizer *MediaFileAuthorizer + ProgressStore EbookReaderProgressStore + ConfigStore EbookReaderConfigStore + AnnotationStore EbookReaderAnnotationStore +} + +func NewEbookReaderHandler(authorizer *MediaFileAuthorizer) *EbookReaderHandler { + return &EbookReaderHandler{FileAuthorizer: authorizer} +} + +// HandleReadFile serves an ebook file inline with byte-range support. +func (h *EbookReaderHandler) HandleReadFile(w http.ResponseWriter, r *http.Request) { + if apimw.GetUserID(r.Context()) == 0 { + writeError(w, http.StatusUnauthorized, "unauthorized", "Authentication required") + return + } + if h == nil || h.FileAuthorizer == nil { + writeError(w, http.StatusServiceUnavailable, "unavailable", "Ebook reader is not configured") + return + } + + contentID := strings.TrimSpace(chi.URLParam(r, "content_id")) + fileID, err := strconv.Atoi(chi.URLParam(r, "file_id")) + if contentID == "" || err != nil || fileID <= 0 { + writeError(w, http.StatusBadRequest, "bad_request", "content_id and file_id are required") + return + } + + file, err := h.FileAuthorizer.Authorize(r, fileID) + if err != nil { + h.writeReadError(w, err) + return + } + if file == nil || file.ContentID != contentID || !isEbookFile(file) { + writeError(w, http.StatusNotFound, "not_found", "Ebook file not found") + return + } + + if err := serveEbookInline(w, r, file); err != nil { + if errors.Is(err, catalog.ErrItemNotFound) { + writeError(w, http.StatusNotFound, "not_found", "Ebook file not found") + return + } + writeError(w, http.StatusInternalServerError, "internal_error", "Failed to serve ebook file") + } +} + +// Request body caps for the ebook reader write endpoints. Progress payloads +// are tiny; reader config and annotations can carry user text and metadata +// but should never approach these limits. +const ( + ebookReaderProgressMaxBodySize = 16 << 10 // 16 KiB + ebookReaderConfigMaxBodySize = 256 << 10 // 256 KiB + ebookReaderAnnotationMaxBodySize = 256 << 10 // 256 KiB +) + +// decodeEbookReaderBody decodes a JSON request body with a hard size cap. +// It writes the error response and returns false when decoding fails. +func decodeEbookReaderBody(w http.ResponseWriter, r *http.Request, maxBodySize int64, dst any) bool { + r.Body = http.MaxBytesReader(w, r.Body, maxBodySize) + if err := json.NewDecoder(r.Body).Decode(dst); err != nil { + var maxBytesErr *http.MaxBytesError + if errors.As(err, &maxBytesErr) { + writeError(w, http.StatusRequestEntityTooLarge, "too_large", "Request body is too large") + return false + } + writeError(w, http.StatusBadRequest, "bad_request", "Invalid request body") + return false + } + return true +} + +type ebookReaderProgressRequest struct { + FileID int `json:"file_id"` + Location string `json:"location"` + Progress float64 `json:"progress"` +} + +type ebookReaderConfigRequest struct { + Config json.RawMessage `json:"config"` +} + +type ebookReaderAnnotationRequest struct { + Kind string `json:"kind"` + CFIRange string `json:"cfi_range"` + Location string `json:"location"` + SelectedText string `json:"selected_text"` + Note string `json:"note"` + Style string `json:"style"` + Color string `json:"color"` + Metadata json.RawMessage `json:"metadata"` +} + +// ebookReaderAnnotationPatchRequest distinguishes absent fields (nil: keep the +// stored value) from present fields (set, including empty values to clear). +type ebookReaderAnnotationPatchRequest struct { + Kind *string `json:"kind"` + CFIRange *string `json:"cfi_range"` + Location *string `json:"location"` + SelectedText *string `json:"selected_text"` + Note *string `json:"note"` + Style *string `json:"style"` + Color *string `json:"color"` + Metadata json.RawMessage `json:"metadata"` +} + +func (h *EbookReaderHandler) HandleGetProgress(w http.ResponseWriter, r *http.Request) { + userID := apimw.GetUserID(r.Context()) + profileID := apimw.GetProfileID(r.Context()) + if userID == 0 { + writeError(w, http.StatusUnauthorized, "unauthorized", "Authentication required") + return + } + if h == nil || h.ProgressStore == nil || h.FileAuthorizer == nil || h.FileAuthorizer.ItemAccess == nil { + writeError(w, http.StatusServiceUnavailable, "unavailable", "Ebook reader progress is not configured") + return + } + + contentID := strings.TrimSpace(chi.URLParam(r, "content_id")) + if contentID == "" { + writeError(w, http.StatusBadRequest, "bad_request", "content_id is required") + return + } + if err := h.FileAuthorizer.ItemAccess.EnsureAccessible(r.Context(), contentID, requestAccessFilter(r)); err != nil { + h.writeReadError(w, err) + return + } + + progress, err := h.ProgressStore.Get(r.Context(), userID, profileID, contentID) + if err != nil { + writeError(w, http.StatusInternalServerError, "internal_error", "Failed to load ebook progress") + return + } + if progress == nil { + writeJSON(w, http.StatusOK, map[string]any{}) + return + } + writeJSON(w, http.StatusOK, progress) +} + +func (h *EbookReaderHandler) HandleSaveProgress(w http.ResponseWriter, r *http.Request) { + userID := apimw.GetUserID(r.Context()) + profileID := apimw.GetProfileID(r.Context()) + if userID == 0 { + writeError(w, http.StatusUnauthorized, "unauthorized", "Authentication required") + return + } + if h == nil || h.ProgressStore == nil || h.FileAuthorizer == nil { + writeError(w, http.StatusServiceUnavailable, "unavailable", "Ebook reader progress is not configured") + return + } + + contentID := strings.TrimSpace(chi.URLParam(r, "content_id")) + if contentID == "" { + writeError(w, http.StatusBadRequest, "bad_request", "content_id is required") + return + } + + var req ebookReaderProgressRequest + if !decodeEbookReaderBody(w, r, ebookReaderProgressMaxBodySize, &req) { + return + } + req.Location = strings.TrimSpace(req.Location) + if req.FileID <= 0 || req.Location == "" || req.Progress < 0 || req.Progress > 1 { + writeError(w, http.StatusBadRequest, "bad_request", "file_id, location, and progress are required") + return + } + + file, err := h.FileAuthorizer.Authorize(r, req.FileID) + if err != nil { + h.writeReadError(w, err) + return + } + if file == nil || file.ContentID != contentID || !isEbookFile(file) { + writeError(w, http.StatusNotFound, "not_found", "Ebook file not found") + return + } + + progress := EbookReaderProgress{ + UserID: userID, + ProfileID: profileID, + ContentID: contentID, + FileID: req.FileID, + Location: req.Location, + Progress: req.Progress, + UpdatedAt: time.Now().UTC(), + } + if err := h.ProgressStore.Upsert(r.Context(), progress); err != nil { + writeError(w, http.StatusInternalServerError, "internal_error", "Failed to save ebook progress") + return + } + writeJSON(w, http.StatusOK, progress) +} + +func (h *EbookReaderHandler) HandleGetConfig(w http.ResponseWriter, r *http.Request) { + userID := apimw.GetUserID(r.Context()) + profileID := apimw.GetProfileID(r.Context()) + if userID == 0 { + writeError(w, http.StatusUnauthorized, "unauthorized", "Authentication required") + return + } + if h == nil || h.ConfigStore == nil || h.FileAuthorizer == nil || h.FileAuthorizer.ItemAccess == nil { + writeError(w, http.StatusServiceUnavailable, "unavailable", "Ebook reader config is not configured") + return + } + + contentID := strings.TrimSpace(chi.URLParam(r, "content_id")) + if contentID == "" { + writeError(w, http.StatusBadRequest, "bad_request", "content_id is required") + return + } + if err := h.FileAuthorizer.ItemAccess.EnsureAccessible(r.Context(), contentID, requestAccessFilter(r)); err != nil { + h.writeReadError(w, err) + return + } + + config, err := h.ConfigStore.Get(r.Context(), userID, profileID, contentID) + if err != nil { + writeError(w, http.StatusInternalServerError, "internal_error", "Failed to load ebook reader config") + return + } + if config == nil { + writeJSON(w, http.StatusOK, map[string]any{"config": map[string]any{}}) + return + } + writeJSON(w, http.StatusOK, config) +} + +func (h *EbookReaderHandler) HandleSaveConfig(w http.ResponseWriter, r *http.Request) { + userID := apimw.GetUserID(r.Context()) + profileID := apimw.GetProfileID(r.Context()) + if userID == 0 { + writeError(w, http.StatusUnauthorized, "unauthorized", "Authentication required") + return + } + if h == nil || h.ConfigStore == nil || h.FileAuthorizer == nil || h.FileAuthorizer.ItemAccess == nil { + writeError(w, http.StatusServiceUnavailable, "unavailable", "Ebook reader config is not configured") + return + } + + contentID := strings.TrimSpace(chi.URLParam(r, "content_id")) + if contentID == "" { + writeError(w, http.StatusBadRequest, "bad_request", "content_id is required") + return + } + + var req ebookReaderConfigRequest + if !decodeEbookReaderBody(w, r, ebookReaderConfigMaxBodySize, &req) { + return + } + if !jsonObject(req.Config) { + writeError(w, http.StatusBadRequest, "bad_request", "config must be a JSON object") + return + } + if err := h.FileAuthorizer.ItemAccess.EnsureAccessible(r.Context(), contentID, requestAccessFilter(r)); err != nil { + h.writeReadError(w, err) + return + } + + config := EbookReaderConfig{ + UserID: userID, + ProfileID: profileID, + ContentID: contentID, + Config: req.Config, + UpdatedAt: time.Now().UTC(), + } + if err := h.ConfigStore.Upsert(r.Context(), config); err != nil { + writeError(w, http.StatusInternalServerError, "internal_error", "Failed to save ebook reader config") + return + } + writeJSON(w, http.StatusOK, config) +} + +func jsonObject(raw json.RawMessage) bool { + if len(raw) == 0 { + return false + } + var value map[string]any + return json.Unmarshal(raw, &value) == nil && value != nil +} + +func (h *EbookReaderHandler) HandleListAnnotations(w http.ResponseWriter, r *http.Request) { + userID, profileID, contentID, ok := h.annotationRequestScope(w, r) + if !ok { + return + } + items, err := h.AnnotationStore.List(r.Context(), userID, profileID, contentID) + if err != nil { + writeError(w, http.StatusInternalServerError, "internal_error", "Failed to load ebook annotations") + return + } + writeJSON(w, http.StatusOK, map[string]any{"items": items}) +} + +func (h *EbookReaderHandler) HandleCreateAnnotation(w http.ResponseWriter, r *http.Request) { + userID, profileID, contentID, ok := h.annotationRequestScope(w, r) + if !ok { + return + } + var req ebookReaderAnnotationRequest + if !decodeEbookReaderBody(w, r, ebookReaderAnnotationMaxBodySize, &req) { + return + } + annotation, err := buildEbookReaderAnnotation(req) + if err != nil { + writeError(w, http.StatusBadRequest, "bad_request", err.Error()) + return + } + now := time.Now().UTC() + annotation.ID = uuid.NewString() + annotation.UserID = userID + annotation.ProfileID = profileID + annotation.ContentID = contentID + annotation.CreatedAt = now + annotation.UpdatedAt = now + if err := h.AnnotationStore.Create(r.Context(), annotation); err != nil { + writeError(w, http.StatusInternalServerError, "internal_error", "Failed to create ebook annotation") + return + } + writeJSON(w, http.StatusCreated, annotation) +} + +func (h *EbookReaderHandler) HandleUpdateAnnotation(w http.ResponseWriter, r *http.Request) { + userID, profileID, contentID, ok := h.annotationRequestScope(w, r) + if !ok { + return + } + annotationID := strings.TrimSpace(chi.URLParam(r, "annotation_id")) + if annotationID == "" { + writeError(w, http.StatusBadRequest, "bad_request", "annotation_id is required") + return + } + var req ebookReaderAnnotationPatchRequest + if !decodeEbookReaderBody(w, r, ebookReaderAnnotationMaxBodySize, &req) { + return + } + // The merge runs inside the store transaction between the locked read and + // the write, so concurrent PATCHes serialize instead of losing updates. + var validationErr error + updated, err := h.AnnotationStore.Update( + r.Context(), userID, profileID, contentID, annotationID, + func(existing EbookReaderAnnotation) (EbookReaderAnnotation, error) { + merged, mergeErr := mergeEbookReaderAnnotationPatch(existing, req) + if mergeErr != nil { + validationErr = mergeErr + return EbookReaderAnnotation{}, mergeErr + } + merged.UpdatedAt = time.Now().UTC() + return merged, nil + }, + ) + if validationErr != nil { + writeError(w, http.StatusBadRequest, "bad_request", validationErr.Error()) + return + } + if err != nil { + writeError(w, http.StatusInternalServerError, "internal_error", "Failed to update ebook annotation") + return + } + if updated == nil { + writeError(w, http.StatusNotFound, "not_found", "Ebook annotation not found") + return + } + writeJSON(w, http.StatusOK, updated) +} + +func (h *EbookReaderHandler) HandleDeleteAnnotation(w http.ResponseWriter, r *http.Request) { + userID, profileID, contentID, ok := h.annotationRequestScope(w, r) + if !ok { + return + } + annotationID := strings.TrimSpace(chi.URLParam(r, "annotation_id")) + if annotationID == "" { + writeError(w, http.StatusBadRequest, "bad_request", "annotation_id is required") + return + } + if err := h.AnnotationStore.Delete(r.Context(), userID, profileID, contentID, annotationID); err != nil { + writeError(w, http.StatusInternalServerError, "internal_error", "Failed to delete ebook annotation") + return + } + w.WriteHeader(http.StatusNoContent) +} + +func (h *EbookReaderHandler) annotationRequestScope(w http.ResponseWriter, r *http.Request) (int, string, string, bool) { + userID := apimw.GetUserID(r.Context()) + profileID := apimw.GetProfileID(r.Context()) + if userID == 0 { + writeError(w, http.StatusUnauthorized, "unauthorized", "Authentication required") + return 0, "", "", false + } + if h == nil || h.AnnotationStore == nil || h.FileAuthorizer == nil || h.FileAuthorizer.ItemAccess == nil { + writeError(w, http.StatusServiceUnavailable, "unavailable", "Ebook annotations are not configured") + return 0, "", "", false + } + contentID := strings.TrimSpace(chi.URLParam(r, "content_id")) + if contentID == "" { + writeError(w, http.StatusBadRequest, "bad_request", "content_id is required") + return 0, "", "", false + } + if err := h.FileAuthorizer.ItemAccess.EnsureAccessible(r.Context(), contentID, requestAccessFilter(r)); err != nil { + h.writeReadError(w, err) + return 0, "", "", false + } + return userID, profileID, contentID, true +} + +// validateEbookReaderAnnotation enforces the annotation invariants shared by +// create and patch: a known kind, a location for bookmarks, a CFI range for +// highlights/notes, and object-shaped metadata. +func validateEbookReaderAnnotation(annotation EbookReaderAnnotation) error { + if annotation.Kind != "highlight" && annotation.Kind != "note" && annotation.Kind != "bookmark" { + return fmt.Errorf("kind must be highlight, note, or bookmark") + } + if annotation.Kind == "bookmark" { + if annotation.Location == "" { + return fmt.Errorf("location is required for bookmarks") + } + } else if annotation.CFIRange == "" { + return fmt.Errorf("cfi_range is required for annotations") + } + if !jsonObject(annotation.Metadata) { + return fmt.Errorf("metadata must be a JSON object") + } + return nil +} + +func buildEbookReaderAnnotation(req ebookReaderAnnotationRequest) (EbookReaderAnnotation, error) { + kind := strings.ToLower(strings.TrimSpace(req.Kind)) + if kind == "" { + kind = "highlight" + } + metadata := req.Metadata + if len(metadata) == 0 { + metadata = json.RawMessage(`{}`) + } + style := strings.TrimSpace(req.Style) + if style == "" { + style = "highlight" + } + color := strings.TrimSpace(req.Color) + if color == "" { + color = "#facc15" + } + annotation := EbookReaderAnnotation{ + Kind: kind, + CFIRange: strings.TrimSpace(req.CFIRange), + Location: strings.TrimSpace(req.Location), + SelectedText: strings.TrimSpace(req.SelectedText), + Note: strings.TrimSpace(req.Note), + Style: style, + Color: color, + Metadata: metadata, + } + if err := validateEbookReaderAnnotation(annotation); err != nil { + return EbookReaderAnnotation{}, err + } + return annotation, nil +} + +// mergeEbookReaderAnnotationPatch applies a PATCH request onto the stored +// annotation. Absent fields keep their stored values; present fields are set +// to the provided value (empty values clear). The merged result is validated +// against the same invariants as annotation creation. +func mergeEbookReaderAnnotationPatch( + existing EbookReaderAnnotation, + req ebookReaderAnnotationPatchRequest, +) (EbookReaderAnnotation, error) { + merged := existing + if req.Kind != nil { + merged.Kind = strings.ToLower(strings.TrimSpace(*req.Kind)) + } + if req.CFIRange != nil { + merged.CFIRange = strings.TrimSpace(*req.CFIRange) + } + if req.Location != nil { + merged.Location = strings.TrimSpace(*req.Location) + } + if req.SelectedText != nil { + merged.SelectedText = strings.TrimSpace(*req.SelectedText) + } + if req.Note != nil { + merged.Note = strings.TrimSpace(*req.Note) + } + if req.Style != nil { + merged.Style = strings.TrimSpace(*req.Style) + } + if req.Color != nil { + merged.Color = strings.TrimSpace(*req.Color) + } + if req.Metadata != nil { + merged.Metadata = req.Metadata + } + if err := validateEbookReaderAnnotation(merged); err != nil { + return EbookReaderAnnotation{}, err + } + return merged, nil +} + +func (h *EbookReaderHandler) writeReadError(w http.ResponseWriter, err error) { + switch { + case errors.Is(err, catalog.ErrItemNotFound), errors.Is(err, catalog.ErrEpisodeNotFound): + writeError(w, http.StatusNotFound, "not_found", "Ebook file not found") + default: + writeError(w, http.StatusInternalServerError, "internal_error", "Failed to authorize ebook file") + } +} + +func serveEbookInline(w http.ResponseWriter, r *http.Request, file *models.MediaFile) error { + f, err := os.Open(file.FilePath) + if err != nil { + if os.IsNotExist(err) { + return catalog.ErrItemNotFound + } + return fmt.Errorf("opening ebook file: %w", err) + } + defer f.Close() + + stat, err := f.Stat() + if err != nil { + return fmt.Errorf("stat ebook file: %w", err) + } + + w.Header().Set("Content-Type", ebookMimeType(file.FilePath, file.Container)) + w.Header().Set("Content-Disposition", inlineContentDisposition(filepath.Base(file.FilePath))) + // Ebook files are served inline on the API origin (and reachable via the + // ?token= query fallback), so browsers must never content-sniff them. + w.Header().Set("X-Content-Type-Options", "nosniff") + http.ServeContent(w, r, stat.Name(), stat.ModTime(), f) + return nil +} + +// inlineContentDisposition builds an inline Content-Disposition header value. +// mime.FormatMediaType handles quoting, backslash escaping, and RFC 5987 +// (filename*) encoding for non-ASCII names. +func inlineContentDisposition(name string) string { + name = strings.TrimSpace(name) + if name == "" || name == "." || name == string(filepath.Separator) { + name = "ebook" + } + if disposition := mime.FormatMediaType("inline", map[string]string{"filename": name}); disposition != "" { + return disposition + } + return `inline; filename="ebook"` +} + +func isEbookFile(file *models.MediaFile) bool { + if file == nil { + return false + } + if !strings.EqualFold(file.BaseType, "ebook") { + return false + } + return ebookReaderFormat(file.FilePath, file.Container) != "" +} + +// ebookReaderFormat resolves the whitelisted reader format for a file from +// its filename extension, falling back to the catalog container when the +// extension is not a known ebook format. It returns "" when neither identity +// is whitelisted. isEbookFile admission and ebookMimeType both key off this +// resolution, so an admitted file always maps to a concrete ebook MIME type +// and never falls through to application/octet-stream. +func ebookReaderFormat(path, container string) string { + if strings.HasSuffix(strings.ToLower(path), ".fb2.zip") { + return "fbz" + } + if format := normalizeEbookReaderFormat(filepath.Ext(path)); format != "" { + return format + } + return normalizeEbookReaderFormat(container) +} + +// normalizeEbookReaderFormat maps an extension or container value onto the +// ebook reader format whitelist, returning "" for unknown values. +func normalizeEbookReaderFormat(value string) string { + format := strings.TrimPrefix(strings.ToLower(strings.TrimSpace(value)), ".") + switch format { + case "epub", "pdf", "mobi", "azw", "azw3", "cbz", "cbr", "fb2", "fbz": + return format + default: + return "" + } +} + +func ebookMimeType(path, container string) string { + switch ebookReaderFormat(path, container) { + case "epub": + return "application/epub+zip" + case "pdf": + return "application/pdf" + case "mobi": + return "application/x-mobipocket-ebook" + case "azw": + return "application/vnd.amazon.ebook" + case "azw3": + return "application/vnd.amazon.mobi8-ebook" + case "cbz": + return "application/vnd.comicbook+zip" + case "cbr": + return "application/vnd.comicbook-rar" + case "fb2": + return "application/x-fictionbook+xml" + case "fbz": + return "application/x-zip-compressed-fb2" + default: + return "application/octet-stream" + } +} + +type PGEbookReaderProgressStore struct { + pool *pgxpool.Pool +} + +func NewPGEbookReaderProgressStore(pool *pgxpool.Pool) *PGEbookReaderProgressStore { + return &PGEbookReaderProgressStore{pool: pool} +} + +type PGEbookReaderConfigStore struct { + pool *pgxpool.Pool +} + +func NewPGEbookReaderConfigStore(pool *pgxpool.Pool) *PGEbookReaderConfigStore { + return &PGEbookReaderConfigStore{pool: pool} +} + +func (s *PGEbookReaderConfigStore) Get(ctx context.Context, userID int, profileID string, contentID string) (*EbookReaderConfig, error) { + if s == nil || s.pool == nil { + return nil, fmt.Errorf("ebook reader config store is not configured") + } + var config EbookReaderConfig + err := s.pool.QueryRow(ctx, ` + SELECT user_id, profile_id, content_id, config, updated_at + FROM ebook_reader_config + WHERE user_id = $1 AND profile_id = $2 AND content_id = $3`, + userID, profileID, contentID, + ).Scan( + &config.UserID, + &config.ProfileID, + &config.ContentID, + &config.Config, + &config.UpdatedAt, + ) + if errors.Is(err, pgx.ErrNoRows) { + return nil, nil + } + if err != nil { + return nil, fmt.Errorf("get ebook reader config: %w", err) + } + return &config, nil +} + +func (s *PGEbookReaderConfigStore) Upsert(ctx context.Context, config EbookReaderConfig) error { + if s == nil || s.pool == nil { + return fmt.Errorf("ebook reader config store is not configured") + } + if _, err := s.pool.Exec(ctx, ` + INSERT INTO ebook_reader_config + (user_id, profile_id, content_id, config, updated_at) + VALUES ($1, $2, $3, $4::jsonb, $5) + ON CONFLICT (user_id, profile_id, content_id) DO UPDATE SET + config = EXCLUDED.config, + updated_at = EXCLUDED.updated_at`, + config.UserID, + config.ProfileID, + config.ContentID, + config.Config, + config.UpdatedAt, + ); err != nil { + return fmt.Errorf("upsert ebook reader config: %w", err) + } + return nil +} + +type PGEbookReaderAnnotationStore struct { + pool *pgxpool.Pool +} + +func NewPGEbookReaderAnnotationStore(pool *pgxpool.Pool) *PGEbookReaderAnnotationStore { + return &PGEbookReaderAnnotationStore{pool: pool} +} + +func (s *PGEbookReaderAnnotationStore) List(ctx context.Context, userID int, profileID string, contentID string) ([]EbookReaderAnnotation, error) { + if s == nil || s.pool == nil { + return nil, fmt.Errorf("ebook reader annotation store is not configured") + } + rows, err := s.pool.Query(ctx, ` + SELECT id, user_id, profile_id, content_id, kind, + COALESCE(cfi_range, ''), COALESCE(location, ''), + selected_text, note, style, color, metadata, created_at, updated_at + FROM ebook_reader_annotations + WHERE user_id = $1 AND profile_id = $2 AND content_id = $3 + ORDER BY updated_at DESC`, + userID, profileID, contentID, + ) + if err != nil { + return nil, fmt.Errorf("list ebook reader annotations: %w", err) + } + defer rows.Close() + var items []EbookReaderAnnotation + for rows.Next() { + annotation, err := scanEbookReaderAnnotation(rows) + if err != nil { + return nil, err + } + items = append(items, annotation) + } + if err := rows.Err(); err != nil { + return nil, fmt.Errorf("iterate ebook reader annotations: %w", err) + } + return items, nil +} + +func (s *PGEbookReaderAnnotationStore) Create(ctx context.Context, annotation EbookReaderAnnotation) error { + if s == nil || s.pool == nil { + return fmt.Errorf("ebook reader annotation store is not configured") + } + _, err := s.pool.Exec(ctx, ` + INSERT INTO ebook_reader_annotations + (id, user_id, profile_id, content_id, kind, cfi_range, location, + selected_text, note, style, color, metadata, created_at, updated_at) + VALUES ($1, $2, $3, $4, $5, NULLIF($6, ''), NULLIF($7, ''), + $8, $9, $10, $11, $12::jsonb, $13, $14)`, + annotation.ID, + annotation.UserID, + annotation.ProfileID, + annotation.ContentID, + annotation.Kind, + annotation.CFIRange, + annotation.Location, + annotation.SelectedText, + annotation.Note, + annotation.Style, + annotation.Color, + annotation.Metadata, + annotation.CreatedAt, + annotation.UpdatedAt, + ) + if err != nil { + return fmt.Errorf("create ebook reader annotation: %w", err) + } + return nil +} + +func (s *PGEbookReaderAnnotationStore) Update( + ctx context.Context, + userID int, + profileID string, + contentID string, + annotationID string, + merge func(existing EbookReaderAnnotation) (EbookReaderAnnotation, error), +) (*EbookReaderAnnotation, error) { + if s == nil || s.pool == nil { + return nil, fmt.Errorf("ebook reader annotation store is not configured") + } + tx, err := s.pool.Begin(ctx) + if err != nil { + return nil, fmt.Errorf("begin ebook reader annotation update: %w", err) + } + defer func() { _ = tx.Rollback(ctx) }() + + existing, err := scanEbookReaderAnnotation(tx.QueryRow(ctx, ` + SELECT id, user_id, profile_id, content_id, kind, + COALESCE(cfi_range, ''), COALESCE(location, ''), + selected_text, note, style, color, metadata, created_at, updated_at + FROM ebook_reader_annotations + WHERE id = $1 AND user_id = $2 AND profile_id = $3 AND content_id = $4 + FOR UPDATE`, + annotationID, userID, profileID, contentID, + )) + if errors.Is(err, pgx.ErrNoRows) { + return nil, nil + } + if err != nil { + return nil, fmt.Errorf("get ebook reader annotation for update: %w", err) + } + + merged, err := merge(existing) + if err != nil { + return nil, err + } + + updated, err := scanEbookReaderAnnotation(tx.QueryRow(ctx, ` + UPDATE ebook_reader_annotations + SET kind = $5, + cfi_range = NULLIF($6, ''), + location = NULLIF($7, ''), + selected_text = $8, + note = $9, + style = $10, + color = $11, + metadata = $12::jsonb, + updated_at = $13 + WHERE id = $1 AND user_id = $2 AND profile_id = $3 AND content_id = $4 + RETURNING id, user_id, profile_id, content_id, kind, + COALESCE(cfi_range, ''), COALESCE(location, ''), + selected_text, note, style, color, metadata, created_at, updated_at`, + annotationID, + userID, + profileID, + contentID, + merged.Kind, + merged.CFIRange, + merged.Location, + merged.SelectedText, + merged.Note, + merged.Style, + merged.Color, + merged.Metadata, + merged.UpdatedAt, + )) + if err != nil { + return nil, fmt.Errorf("update ebook reader annotation: %w", err) + } + if err := tx.Commit(ctx); err != nil { + return nil, fmt.Errorf("commit ebook reader annotation update: %w", err) + } + return &updated, nil +} + +func (s *PGEbookReaderAnnotationStore) Delete(ctx context.Context, userID int, profileID string, contentID string, annotationID string) error { + if s == nil || s.pool == nil { + return fmt.Errorf("ebook reader annotation store is not configured") + } + if _, err := s.pool.Exec(ctx, ` + DELETE FROM ebook_reader_annotations + WHERE id = $1 AND user_id = $2 AND profile_id = $3 AND content_id = $4`, + annotationID, userID, profileID, contentID, + ); err != nil { + return fmt.Errorf("delete ebook reader annotation: %w", err) + } + return nil +} + +func scanEbookReaderAnnotation(scanner interface{ Scan(dest ...any) error }) (EbookReaderAnnotation, error) { + var annotation EbookReaderAnnotation + if err := scanner.Scan( + &annotation.ID, + &annotation.UserID, + &annotation.ProfileID, + &annotation.ContentID, + &annotation.Kind, + &annotation.CFIRange, + &annotation.Location, + &annotation.SelectedText, + &annotation.Note, + &annotation.Style, + &annotation.Color, + &annotation.Metadata, + &annotation.CreatedAt, + &annotation.UpdatedAt, + ); err != nil { + return EbookReaderAnnotation{}, fmt.Errorf("scan ebook reader annotation: %w", err) + } + return annotation, nil +} + +func (s *PGEbookReaderProgressStore) Get(ctx context.Context, userID int, profileID string, contentID string) (*EbookReaderProgress, error) { + if s == nil || s.pool == nil { + return nil, fmt.Errorf("ebook reader progress store is not configured") + } + var progress EbookReaderProgress + err := s.pool.QueryRow(ctx, ` + SELECT user_id, profile_id, content_id, file_id, location, progress, updated_at + FROM ebook_reader_progress + WHERE user_id = $1 AND profile_id = $2 AND content_id = $3`, + userID, profileID, contentID, + ).Scan( + &progress.UserID, + &progress.ProfileID, + &progress.ContentID, + &progress.FileID, + &progress.Location, + &progress.Progress, + &progress.UpdatedAt, + ) + if errors.Is(err, pgx.ErrNoRows) { + return nil, nil + } + if err != nil { + return nil, fmt.Errorf("get ebook reader progress: %w", err) + } + return &progress, nil +} + +// ListByContentIDs lists reading progress for a set of items, excluding rows +// hidden from history. The hidden gating mirrors the video path +// (internal/userstore/pgstore/progress.go): a row is hidden while its +// updated_at <= hidden_before, and counts again once progress is updated +// after the hide. +func (s *PGEbookReaderProgressStore) ListByContentIDs(ctx context.Context, userID int, profileID string, contentIDs []string) (map[string]EbookReaderProgress, error) { + if s == nil || s.pool == nil { + return nil, fmt.Errorf("ebook reader progress store is not configured") + } + if userID <= 0 || profileID == "" || len(contentIDs) == 0 { + return nil, nil + } + rows, err := s.pool.Query(ctx, ` + SELECT user_id, profile_id, content_id, file_id, location, progress, updated_at + FROM ebook_reader_progress + WHERE user_id = $1 AND profile_id = $2 AND content_id = ANY($3::text[]) + AND NOT EXISTS ( + SELECT 1 + FROM user_history_hidden_items hhi + WHERE hhi.user_id = ebook_reader_progress.user_id + AND hhi.profile_id = ebook_reader_progress.profile_id + AND hhi.media_item_id = ebook_reader_progress.content_id + AND ebook_reader_progress.updated_at <= hhi.hidden_before + )`, + userID, profileID, contentIDs, + ) + if err != nil { + return nil, fmt.Errorf("list ebook reader progress: %w", err) + } + defer rows.Close() + + progressByContentID := make(map[string]EbookReaderProgress, len(contentIDs)) + for rows.Next() { + var progress EbookReaderProgress + if err := rows.Scan( + &progress.UserID, + &progress.ProfileID, + &progress.ContentID, + &progress.FileID, + &progress.Location, + &progress.Progress, + &progress.UpdatedAt, + ); err != nil { + return nil, fmt.Errorf("scan ebook reader progress: %w", err) + } + progressByContentID[progress.ContentID] = progress + } + if err := rows.Err(); err != nil { + return nil, fmt.Errorf("iterate ebook reader progress: %w", err) + } + return progressByContentID, nil +} + +func (s *PGEbookReaderProgressStore) Delete(ctx context.Context, userID int, profileID string, contentID string) error { + if s == nil || s.pool == nil { + return fmt.Errorf("ebook reader progress store is not configured") + } + if _, err := s.pool.Exec(ctx, ` + DELETE FROM ebook_reader_progress + WHERE user_id = $1 AND profile_id = $2 AND content_id = $3`, + userID, profileID, contentID, + ); err != nil { + return fmt.Errorf("delete ebook reader progress: %w", err) + } + return nil +} + +func (s *PGEbookReaderProgressStore) Upsert(ctx context.Context, progress EbookReaderProgress) error { + if s == nil || s.pool == nil { + return fmt.Errorf("ebook reader progress store is not configured") + } + if _, err := s.pool.Exec(ctx, ` + INSERT INTO ebook_reader_progress + (user_id, profile_id, content_id, file_id, location, progress, updated_at) + VALUES ($1, $2, $3, $4, $5, $6, $7) + ON CONFLICT (user_id, profile_id, content_id) DO UPDATE SET + file_id = EXCLUDED.file_id, + location = EXCLUDED.location, + progress = EXCLUDED.progress, + updated_at = EXCLUDED.updated_at`, + progress.UserID, + progress.ProfileID, + progress.ContentID, + progress.FileID, + progress.Location, + progress.Progress, + progress.UpdatedAt, + ); err != nil { + return fmt.Errorf("upsert ebook reader progress: %w", err) + } + return nil +} diff --git a/internal/api/handlers/ebook_reader_test.go b/internal/api/handlers/ebook_reader_test.go new file mode 100644 index 00000000..063f0bf8 --- /dev/null +++ b/internal/api/handlers/ebook_reader_test.go @@ -0,0 +1,901 @@ +package handlers + +import ( + "context" + "encoding/json" + "errors" + "mime" + "net/http" + "net/http/httptest" + "os" + "strings" + "testing" + "time" + + "github.com/go-chi/chi/v5" + + apimw "github.com/Silo-Server/silo-server/internal/api/middleware" + "github.com/Silo-Server/silo-server/internal/auth" + "github.com/Silo-Server/silo-server/internal/catalog" + "github.com/Silo-Server/silo-server/internal/models" +) + +func newEbookReaderAuthRequest(method, path string) *http.Request { + req := httptest.NewRequest(method, path, nil) + ctx := apimw.SetClaims(context.Background(), &auth.Claims{ + UserID: 1, + Role: "user", + TokenType: auth.TokenTypeAccess, + }) + ctx = apimw.SetProfileID(ctx, "profile-1") + return req.WithContext(ctx) +} + +func withEbookReaderRouteParams(req *http.Request, contentID, fileID string) *http.Request { + routeCtx := chi.NewRouteContext() + routeCtx.URLParams.Add("content_id", contentID) + routeCtx.URLParams.Add("file_id", fileID) + return req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, routeCtx)) +} + +func withEbookReaderContentRouteParam(req *http.Request, contentID string) *http.Request { + routeCtx := chi.NewRouteContext() + routeCtx.URLParams.Add("content_id", contentID) + return req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, routeCtx)) +} + +func withEbookReaderAnnotationRouteParams(req *http.Request, contentID, annotationID string) *http.Request { + routeCtx := chi.NewRouteContext() + routeCtx.URLParams.Add("content_id", contentID) + routeCtx.URLParams.Add("annotation_id", annotationID) + return req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, routeCtx)) +} + +type fakeEbookProgressStore struct { + progress *EbookReaderProgress + upserted *EbookReaderProgress + err error +} + +func (s *fakeEbookProgressStore) Get( + context.Context, + int, + string, + string, +) (*EbookReaderProgress, error) { + return s.progress, s.err +} + +func (s *fakeEbookProgressStore) Upsert(_ context.Context, progress EbookReaderProgress) error { + if s.err != nil { + return s.err + } + s.upserted = &progress + return nil +} + +type fakeEbookReaderConfigStore struct { + config *EbookReaderConfig + saved *EbookReaderConfig + getErr error + saveErr error +} + +func (s *fakeEbookReaderConfigStore) Get( + context.Context, + int, + string, + string, +) (*EbookReaderConfig, error) { + return s.config, s.getErr +} + +func (s *fakeEbookReaderConfigStore) Upsert(_ context.Context, config EbookReaderConfig) error { + if s.saveErr != nil { + return s.saveErr + } + s.saved = &config + return nil +} + +type fakeEbookReaderAnnotationStore struct { + items []EbookReaderAnnotation + existing *EbookReaderAnnotation + created *EbookReaderAnnotation + updated *EbookReaderAnnotation + mergedFrom *EbookReaderAnnotation + mergeCalls int + deleted string + err error +} + +func (s *fakeEbookReaderAnnotationStore) List(context.Context, int, string, string) ([]EbookReaderAnnotation, error) { + return s.items, s.err +} + +func (s *fakeEbookReaderAnnotationStore) Create(_ context.Context, annotation EbookReaderAnnotation) error { + if s.err != nil { + return s.err + } + s.created = &annotation + s.items = append(s.items, annotation) + return nil +} + +// Update models the PG store contract: read the stored row, invoke merge with +// it, and persist the merged result, all as one atomic operation. +func (s *fakeEbookReaderAnnotationStore) Update( + _ context.Context, + _ int, + _ string, + _ string, + annotationID string, + merge func(existing EbookReaderAnnotation) (EbookReaderAnnotation, error), +) (*EbookReaderAnnotation, error) { + if s.err != nil { + return nil, s.err + } + if s.existing == nil || s.existing.ID != annotationID { + return nil, nil + } + existing := *s.existing + s.mergedFrom = &existing + s.mergeCalls++ + merged, err := merge(existing) + if err != nil { + return nil, err + } + s.updated = &merged + stored := merged + s.existing = &stored + return &merged, nil +} + +func (s *fakeEbookReaderAnnotationStore) Delete(_ context.Context, userID int, profileID, contentID, annotationID string) error { + if s.err != nil { + return s.err + } + s.deleted = annotationID + return nil +} + +func TestEbookReaderServesEbookInlineWithRangeSupport(t *testing.T) { + filePath := writePlaybackTestMediaFile(t, "book.epub") + if err := os.WriteFile(filePath, []byte("0123456789"), 0o644); err != nil { + t.Fatalf("write ebook: %v", err) + } + + handler := NewEbookReaderHandler(&MediaFileAuthorizer{ + FileResolver: stubMediaFileResolver{ + file: &models.MediaFile{ + ID: 42, + ContentID: "ebook-1", + FilePath: filePath, + Container: "epub", + BaseType: "ebook", + }, + }, + ItemAccess: stubItemAccessChecker{}, + }) + + req := newEbookReaderAuthRequest(http.MethodGet, "/ebooks/ebook-1/files/42/read") + req.Header.Set("Range", "bytes=2-5") + req = withEbookReaderRouteParams(req, "ebook-1", "42") + + rr := httptest.NewRecorder() + handler.HandleReadFile(rr, req) + + if rr.Code != http.StatusPartialContent { + t.Fatalf("status = %d, body = %s", rr.Code, rr.Body.String()) + } + if got := rr.Header().Get("Content-Type"); got != "application/epub+zip" { + t.Fatalf("content-type = %q", got) + } + if got := rr.Header().Get("Content-Disposition"); got != "inline; filename=book.epub" { + t.Fatalf("content-disposition = %q", got) + } + if got := rr.Header().Get("X-Content-Type-Options"); got != "nosniff" { + t.Fatalf("x-content-type-options = %q, want nosniff", got) + } + if rr.Body.String() != "2345" { + t.Fatalf("body = %q", rr.Body.String()) + } +} + +func TestEbookReaderServesContainerMimeForMismatchedExtension(t *testing.T) { + filePath := writePlaybackTestMediaFile(t, "book.txt") + if err := os.WriteFile(filePath, []byte("not really"), 0o644); err != nil { + t.Fatalf("write ebook: %v", err) + } + + handler := NewEbookReaderHandler(&MediaFileAuthorizer{ + FileResolver: stubMediaFileResolver{ + file: &models.MediaFile{ + ID: 42, + ContentID: "ebook-1", + FilePath: filePath, + Container: "epub", + BaseType: "ebook", + }, + }, + ItemAccess: stubItemAccessChecker{}, + }) + + req := newEbookReaderAuthRequest(http.MethodGet, "/ebooks/ebook-1/files/42/read") + req = withEbookReaderRouteParams(req, "ebook-1", "42") + + rr := httptest.NewRecorder() + handler.HandleReadFile(rr, req) + + if rr.Code != http.StatusOK { + t.Fatalf("status = %d, body = %s", rr.Code, rr.Body.String()) + } + // The file is admitted via its validated container, so the response must + // carry that container's MIME type, never application/octet-stream. + if got := rr.Header().Get("Content-Type"); got != "application/epub+zip" { + t.Fatalf("content-type = %q, want application/epub+zip", got) + } + if got := rr.Header().Get("X-Content-Type-Options"); got != "nosniff" { + t.Fatalf("x-content-type-options = %q, want nosniff", got) + } +} + +func TestEbookMimeTypeNeverReturnsOctetStreamForAdmittedFiles(t *testing.T) { + cases := []struct { + path string + container string + wantMime string + }{ + {"/library/book.txt", "epub", "application/epub+zip"}, + {"/library/book", "pdf", "application/pdf"}, + {"/library/book.epub", "pdf", "application/epub+zip"}, // known extension wins + } + for _, tc := range cases { + file := &models.MediaFile{FilePath: tc.path, Container: tc.container, BaseType: "ebook"} + if !isEbookFile(file) { + t.Fatalf("isEbookFile(%q, %q) = false, want admitted", tc.path, tc.container) + } + if got := ebookMimeType(tc.path, tc.container); got != tc.wantMime { + t.Fatalf("ebookMimeType(%q, %q) = %q, want %q", tc.path, tc.container, got, tc.wantMime) + } + } +} + +func TestEbookReaderRejectsNonEbookFile(t *testing.T) { + handler := NewEbookReaderHandler(&MediaFileAuthorizer{ + FileResolver: stubMediaFileResolver{ + file: &models.MediaFile{ + ID: 42, + ContentID: "movie-1", + FilePath: "/tmp/movie.mkv", + Container: "mkv", + BaseType: "movie", + }, + }, + ItemAccess: stubItemAccessChecker{}, + }) + + req := newEbookReaderAuthRequest(http.MethodGet, "/ebooks/movie-1/files/42/read") + req = withEbookReaderRouteParams(req, "movie-1", "42") + + rr := httptest.NewRecorder() + handler.HandleReadFile(rr, req) + + if rr.Code != http.StatusNotFound { + t.Fatalf("status = %d, body = %s", rr.Code, rr.Body.String()) + } +} + +func TestEbookReaderRecognizesReadestFormats(t *testing.T) { + cases := map[string]string{ + "book.epub": "application/epub+zip", + "book.pdf": "application/pdf", + "book.mobi": "application/x-mobipocket-ebook", + "book.azw": "application/vnd.amazon.ebook", + "book.azw3": "application/vnd.amazon.mobi8-ebook", + "book.cbz": "application/vnd.comicbook+zip", + "book.cbr": "application/vnd.comicbook-rar", + "book.fb2": "application/x-fictionbook+xml", + "book.fbz": "application/x-zip-compressed-fb2", + "book.fb2.zip": "application/x-zip-compressed-fb2", + "book.md": "application/octet-stream", + "book.unknown": "application/octet-stream", + } + rejected := map[string]bool{"book.md": true, "book.unknown": true} + + for name, wantMime := range cases { + t.Run(name, func(t *testing.T) { + container := name + if name == "book.fb2.zip" { + container = "fbz" + } + file := &models.MediaFile{FilePath: "/library/" + name, Container: container} + if rejected[name] { + file.BaseType = "ebook" + if isEbookFile(file) { + t.Fatalf("%s should not be treated as an ebook reader format", name) + } + if got := ebookMimeType(file.FilePath, file.Container); got != wantMime { + t.Fatalf("ebookMimeType() = %q, want %q", got, wantMime) + } + return + } + file.BaseType = "ebook" + if !isEbookFile(file) { + t.Fatal("expected ebook reader format") + } + if got := ebookMimeType(file.FilePath, file.Container); got != wantMime { + t.Fatalf("ebookMimeType() = %q, want %q", got, wantMime) + } + }) + } +} + +func TestEbookReaderRejectsPlainText(t *testing.T) { + file := &models.MediaFile{ + FilePath: "/library/book.txt", + BaseType: "ebook", + } + + if isEbookFile(file) { + t.Fatal("plain text should not be treated as an ebook reader format") + } + if got := ebookMimeType(file.FilePath, file.Container); got == "text/plain; charset=utf-8" { + t.Fatal("plain text should not have an ebook reader MIME type") + } +} + +func TestEbookReaderRecognizesFBZFromCompoundFilenameWithoutContainer(t *testing.T) { + file := &models.MediaFile{ + FilePath: "/library/book.fb2.zip", + BaseType: "ebook", + } + + if !isEbookFile(file) { + t.Fatal("expected .fb2.zip path to be treated as an ebook reader format") + } +} + +func TestEbookReaderMapsAccessFailureToNotFound(t *testing.T) { + handler := NewEbookReaderHandler(&MediaFileAuthorizer{ + FileResolver: stubMediaFileResolver{ + file: &models.MediaFile{ + ID: 42, + ContentID: "ebook-1", + FilePath: "/tmp/book.epub", + Container: "epub", + BaseType: "ebook", + }, + }, + ItemAccess: stubItemAccessChecker{err: catalog.ErrItemNotFound}, + }) + + req := newEbookReaderAuthRequest(http.MethodGet, "/ebooks/ebook-1/files/42/read") + req = withEbookReaderRouteParams(req, "ebook-1", "42") + + rr := httptest.NewRecorder() + handler.HandleReadFile(rr, req) + + if rr.Code != http.StatusNotFound { + t.Fatalf("status = %d, body = %s", rr.Code, rr.Body.String()) + } +} + +func TestEbookReaderReturnsInternalErrorForUnexpectedAuthorizeFailure(t *testing.T) { + handler := NewEbookReaderHandler(&MediaFileAuthorizer{ + FileResolver: stubMediaFileResolver{err: errors.New("db down")}, + ItemAccess: stubItemAccessChecker{}, + }) + + req := newEbookReaderAuthRequest(http.MethodGet, "/ebooks/ebook-1/files/42/read") + req = withEbookReaderRouteParams(req, "ebook-1", "42") + + rr := httptest.NewRecorder() + handler.HandleReadFile(rr, req) + + if rr.Code != http.StatusInternalServerError { + t.Fatalf("status = %d, body = %s", rr.Code, rr.Body.String()) + } +} + +func TestEbookReaderSavesProgressForAuthorizedEbookFile(t *testing.T) { + store := &fakeEbookProgressStore{} + handler := NewEbookReaderHandler(&MediaFileAuthorizer{ + FileResolver: stubMediaFileResolver{ + file: &models.MediaFile{ + ID: 42, + ContentID: "ebook-1", + FilePath: "/tmp/book.epub", + Container: "epub", + BaseType: "ebook", + }, + }, + ItemAccess: stubItemAccessChecker{}, + }) + handler.ProgressStore = store + + req := httptest.NewRequest( + http.MethodPut, + "/ebooks/ebook-1/progress", + strings.NewReader(`{"file_id":42,"location":"epubcfi(/6/4)","progress":0.42}`), + ) + ctx := apimw.SetClaims(context.Background(), &auth.Claims{ + UserID: 1, + Role: "user", + TokenType: auth.TokenTypeAccess, + }) + req = req.WithContext(apimw.SetProfileID(ctx, "profile-9")) + req = withEbookReaderContentRouteParam(req, "ebook-1") + + rr := httptest.NewRecorder() + handler.HandleSaveProgress(rr, req) + + if rr.Code != http.StatusOK { + t.Fatalf("status = %d, body = %s", rr.Code, rr.Body.String()) + } + if store.upserted == nil { + t.Fatal("expected progress to be saved") + } + if store.upserted.UserID != 1 || store.upserted.ProfileID != "profile-9" { + t.Fatalf("scope = user %d profile %q", store.upserted.UserID, store.upserted.ProfileID) + } + if store.upserted.ContentID != "ebook-1" || store.upserted.FileID != 42 { + t.Fatalf("target = content %q file %d", store.upserted.ContentID, store.upserted.FileID) + } + if store.upserted.Location != "epubcfi(/6/4)" || store.upserted.Progress != 0.42 { + t.Fatalf("progress = %+v", store.upserted) + } +} + +func TestEbookReaderReturnsSavedProgress(t *testing.T) { + updatedAt := time.Date(2026, 6, 8, 12, 0, 0, 0, time.UTC) + handler := NewEbookReaderHandler(&MediaFileAuthorizer{ + FileResolver: stubMediaFileResolver{}, + ItemAccess: stubItemAccessChecker{}, + }) + handler.ProgressStore = &fakeEbookProgressStore{ + progress: &EbookReaderProgress{ + UserID: 1, + ProfileID: "profile-1", + ContentID: "ebook-1", + FileID: 42, + Location: "epubcfi(/6/8)", + Progress: 0.75, + UpdatedAt: updatedAt, + }, + } + + req := newEbookReaderAuthRequest(http.MethodGet, "/ebooks/ebook-1/progress") + req = withEbookReaderContentRouteParam(req, "ebook-1") + + rr := httptest.NewRecorder() + handler.HandleGetProgress(rr, req) + + if rr.Code != http.StatusOK { + t.Fatalf("status = %d, body = %s", rr.Code, rr.Body.String()) + } + body := rr.Body.String() + for _, want := range []string{ + `"content_id":"ebook-1"`, + `"file_id":42`, + `"location":"epubcfi(/6/8)"`, + `"progress":0.75`, + `"updated_at":"2026-06-08T12:00:00Z"`, + } { + if !strings.Contains(body, want) { + t.Fatalf("response %s missing %s", body, want) + } + } +} + +func TestEbookReaderReturnsEmptyConfigWhenNoneSaved(t *testing.T) { + handler := NewEbookReaderHandler(&MediaFileAuthorizer{ + FileResolver: stubMediaFileResolver{}, + ItemAccess: stubItemAccessChecker{}, + }) + handler.ConfigStore = &fakeEbookReaderConfigStore{} + + req := newEbookReaderAuthRequest(http.MethodGet, "/ebooks/ebook-1/reader-config") + req = withEbookReaderContentRouteParam(req, "ebook-1") + + rr := httptest.NewRecorder() + handler.HandleGetConfig(rr, req) + + if rr.Code != http.StatusOK { + t.Fatalf("status = %d, body = %s", rr.Code, rr.Body.String()) + } + if got := strings.TrimSpace(rr.Body.String()); got != `{"config":{}}` { + t.Fatalf("body = %s", got) + } +} + +func TestEbookReaderSavesConfigForAccessibleEbook(t *testing.T) { + store := &fakeEbookReaderConfigStore{} + handler := NewEbookReaderHandler(&MediaFileAuthorizer{ + FileResolver: stubMediaFileResolver{}, + ItemAccess: stubItemAccessChecker{}, + }) + handler.ConfigStore = store + + req := httptest.NewRequest( + http.MethodPut, + "/ebooks/ebook-1/reader-config", + strings.NewReader(`{"config":{"theme":"dark","flow":"scrolled","fontSize":120}}`), + ) + ctx := apimw.SetClaims(context.Background(), &auth.Claims{ + UserID: 1, + Role: "user", + TokenType: auth.TokenTypeAccess, + }) + req = req.WithContext(apimw.SetProfileID(ctx, "profile-9")) + req = withEbookReaderContentRouteParam(req, "ebook-1") + + rr := httptest.NewRecorder() + handler.HandleSaveConfig(rr, req) + + if rr.Code != http.StatusOK { + t.Fatalf("status = %d, body = %s", rr.Code, rr.Body.String()) + } + if store.saved == nil { + t.Fatal("expected config to be saved") + } + if store.saved.UserID != 1 || store.saved.ProfileID != "profile-9" || store.saved.ContentID != "ebook-1" { + t.Fatalf("scope = %+v", store.saved) + } + var config map[string]any + if err := json.Unmarshal(store.saved.Config, &config); err != nil { + t.Fatalf("config json: %v", err) + } + if config["theme"] != "dark" || config["flow"] != "scrolled" || config["fontSize"].(float64) != 120 { + t.Fatalf("config = %#v", config) + } +} + +func TestEbookReaderRejectsNonObjectConfig(t *testing.T) { + handler := NewEbookReaderHandler(&MediaFileAuthorizer{ + FileResolver: stubMediaFileResolver{}, + ItemAccess: stubItemAccessChecker{}, + }) + handler.ConfigStore = &fakeEbookReaderConfigStore{} + + req := newEbookReaderAuthRequest(http.MethodPut, "/ebooks/ebook-1/reader-config") + req.Body = http.NoBody + req = httptest.NewRequest( + http.MethodPut, + "/ebooks/ebook-1/reader-config", + strings.NewReader(`{"config":["bad"]}`), + ).WithContext(req.Context()) + req = withEbookReaderContentRouteParam(req, "ebook-1") + + rr := httptest.NewRecorder() + handler.HandleSaveConfig(rr, req) + + if rr.Code != http.StatusBadRequest { + t.Fatalf("status = %d, body = %s", rr.Code, rr.Body.String()) + } +} + +func TestEbookReaderConfigRequiresAccessibleContent(t *testing.T) { + handler := NewEbookReaderHandler(&MediaFileAuthorizer{ + FileResolver: stubMediaFileResolver{}, + ItemAccess: stubItemAccessChecker{err: catalog.ErrItemNotFound}, + }) + handler.ConfigStore = &fakeEbookReaderConfigStore{} + + req := newEbookReaderAuthRequest(http.MethodGet, "/ebooks/ebook-1/reader-config") + req = withEbookReaderContentRouteParam(req, "ebook-1") + + rr := httptest.NewRecorder() + handler.HandleGetConfig(rr, req) + + if rr.Code != http.StatusNotFound { + t.Fatalf("status = %d, body = %s", rr.Code, rr.Body.String()) + } +} + +func TestEbookReaderCreatesAnnotationForAccessibleEbook(t *testing.T) { + store := &fakeEbookReaderAnnotationStore{} + handler := NewEbookReaderHandler(&MediaFileAuthorizer{ + FileResolver: stubMediaFileResolver{}, + ItemAccess: stubItemAccessChecker{}, + }) + handler.AnnotationStore = store + + req := httptest.NewRequest( + http.MethodPost, + "/ebooks/ebook-1/annotations", + strings.NewReader(`{"kind":"highlight","cfi_range":"epubcfi(/6/4,/1:0,/1:12)","selected_text":"sample text","note":"note text","style":"highlight","color":"#facc15"}`), + ) + ctx := apimw.SetClaims(context.Background(), &auth.Claims{ + UserID: 1, + Role: "user", + TokenType: auth.TokenTypeAccess, + }) + req = req.WithContext(apimw.SetProfileID(ctx, "profile-9")) + req = withEbookReaderContentRouteParam(req, "ebook-1") + + rr := httptest.NewRecorder() + handler.HandleCreateAnnotation(rr, req) + + if rr.Code != http.StatusCreated { + t.Fatalf("status = %d, body = %s", rr.Code, rr.Body.String()) + } + if store.created == nil { + t.Fatal("expected annotation to be created") + } + if store.created.UserID != 1 || store.created.ProfileID != "profile-9" || store.created.ContentID != "ebook-1" { + t.Fatalf("scope = %+v", store.created) + } + if store.created.Kind != "highlight" || store.created.CFIRange != "epubcfi(/6/4,/1:0,/1:12)" { + t.Fatalf("annotation = %+v", store.created) + } + if store.created.SelectedText != "sample text" || store.created.Note != "note text" { + t.Fatalf("annotation text = %+v", store.created) + } + if store.created.ID == "" { + t.Fatal("expected generated annotation id") + } +} + +func TestEbookReaderReturnsAnnotations(t *testing.T) { + updatedAt := time.Date(2026, 6, 8, 12, 0, 0, 0, time.UTC) + handler := NewEbookReaderHandler(&MediaFileAuthorizer{ + FileResolver: stubMediaFileResolver{}, + ItemAccess: stubItemAccessChecker{}, + }) + handler.AnnotationStore = &fakeEbookReaderAnnotationStore{ + items: []EbookReaderAnnotation{ + { + ID: "ann-1", + UserID: 1, + ProfileID: "profile-1", + ContentID: "ebook-1", + Kind: "bookmark", + Location: "epubcfi(/6/8)", + SelectedText: "", + Note: "Saved spot", + Style: "highlight", + Color: "#facc15", + Metadata: json.RawMessage(`{}`), + CreatedAt: updatedAt, + UpdatedAt: updatedAt, + }, + }, + } + + req := newEbookReaderAuthRequest(http.MethodGet, "/ebooks/ebook-1/annotations") + req = withEbookReaderContentRouteParam(req, "ebook-1") + + rr := httptest.NewRecorder() + handler.HandleListAnnotations(rr, req) + + if rr.Code != http.StatusOK { + t.Fatalf("status = %d, body = %s", rr.Code, rr.Body.String()) + } + body := rr.Body.String() + for _, want := range []string{`"items":[`, `"id":"ann-1"`, `"kind":"bookmark"`, `"location":"epubcfi(/6/8)"`} { + if !strings.Contains(body, want) { + t.Fatalf("response %s missing %s", body, want) + } + } +} + +func existingEbookReaderAnnotation() *EbookReaderAnnotation { + createdAt := time.Date(2026, 6, 1, 9, 0, 0, 0, time.UTC) + return &EbookReaderAnnotation{ + ID: "ann-1", + UserID: 1, + ProfileID: "profile-1", + ContentID: "ebook-1", + Kind: "highlight", + CFIRange: "epubcfi(/6/4,/1:0,/1:12)", + SelectedText: "sample text", + Note: "original note", + Style: "highlight", + Color: "#facc15", + Metadata: json.RawMessage(`{"chapter":"one"}`), + CreatedAt: createdAt, + UpdatedAt: createdAt, + } +} + +func patchEbookReaderAnnotation(t *testing.T, store *fakeEbookReaderAnnotationStore, body string) *httptest.ResponseRecorder { + t.Helper() + handler := NewEbookReaderHandler(&MediaFileAuthorizer{ + FileResolver: stubMediaFileResolver{}, + ItemAccess: stubItemAccessChecker{}, + }) + handler.AnnotationStore = store + + req := newEbookReaderAuthRequest(http.MethodPatch, "/ebooks/ebook-1/annotations/ann-1") + req.Body = ioNopCloser{strings.NewReader(body)} + req = withEbookReaderAnnotationRouteParams(req, "ebook-1", "ann-1") + + rr := httptest.NewRecorder() + handler.HandleUpdateAnnotation(rr, req) + return rr +} + +func TestEbookReaderPatchKeepsAbsentFields(t *testing.T) { + store := &fakeEbookReaderAnnotationStore{existing: existingEbookReaderAnnotation()} + + rr := patchEbookReaderAnnotation(t, store, `{"note":"updated","color":"#bfdbfe"}`) + + if rr.Code != http.StatusOK { + t.Fatalf("status = %d, body = %s", rr.Code, rr.Body.String()) + } + if store.updated == nil || store.updated.ID != "ann-1" || store.updated.Note != "updated" || store.updated.Color != "#bfdbfe" { + t.Fatalf("updated = %+v", store.updated) + } + if string(store.updated.Metadata) != `{"chapter":"one"}` { + t.Fatalf("metadata should be preserved when absent, got %s", store.updated.Metadata) + } + if store.updated.Kind != "highlight" || store.updated.CFIRange != "epubcfi(/6/4,/1:0,/1:12)" { + t.Fatalf("absent fields should keep stored values, got %+v", store.updated) + } +} + +func TestEbookReaderPatchClearsExplicitlyEmptyFields(t *testing.T) { + store := &fakeEbookReaderAnnotationStore{existing: existingEbookReaderAnnotation()} + + rr := patchEbookReaderAnnotation(t, store, `{"note":"","metadata":{}}`) + + if rr.Code != http.StatusOK { + t.Fatalf("status = %d, body = %s", rr.Code, rr.Body.String()) + } + if store.updated == nil || store.updated.Note != "" { + t.Fatalf("note should be cleared, got %+v", store.updated) + } + if string(store.updated.Metadata) != `{}` { + t.Fatalf("metadata should be cleared to {}, got %s", store.updated.Metadata) + } +} + +func TestEbookReaderPatchValidatesResultingState(t *testing.T) { + cases := map[string]string{ + "kind change without location": `{"kind":"bookmark"}`, + "clearing required cfi_range": `{"cfi_range":""}`, + "unknown kind": `{"kind":"doodle"}`, + "non-object metadata": `{"metadata":["bad"]}`, + } + for name, body := range cases { + t.Run(name, func(t *testing.T) { + store := &fakeEbookReaderAnnotationStore{existing: existingEbookReaderAnnotation()} + rr := patchEbookReaderAnnotation(t, store, body) + if rr.Code != http.StatusBadRequest { + t.Fatalf("status = %d, body = %s", rr.Code, rr.Body.String()) + } + if store.updated != nil { + t.Fatalf("invalid patch must not reach the store, got %+v", store.updated) + } + }) + } +} + +func TestEbookReaderPatchAllowsKindChangeWithRequiredFields(t *testing.T) { + store := &fakeEbookReaderAnnotationStore{existing: existingEbookReaderAnnotation()} + + rr := patchEbookReaderAnnotation(t, store, `{"kind":"bookmark","location":"epubcfi(/6/8)"}`) + + if rr.Code != http.StatusOK { + t.Fatalf("status = %d, body = %s", rr.Code, rr.Body.String()) + } + if store.updated == nil || store.updated.Kind != "bookmark" || store.updated.Location != "epubcfi(/6/8)" { + t.Fatalf("updated = %+v", store.updated) + } +} + +func TestEbookReaderPatchMergesOntoRowReadInsideStoreUpdate(t *testing.T) { + store := &fakeEbookReaderAnnotationStore{existing: existingEbookReaderAnnotation()} + + rr := patchEbookReaderAnnotation(t, store, `{"note":"merged note"}`) + + if rr.Code != http.StatusOK { + t.Fatalf("status = %d, body = %s", rr.Code, rr.Body.String()) + } + // The handler must not pre-read and merge outside the store: the merge + // runs exactly once, on the row handed over by the (locked) store read. + if store.mergeCalls != 1 { + t.Fatalf("merge calls = %d, want 1", store.mergeCalls) + } + if store.mergedFrom == nil || store.mergedFrom.Note != "original note" { + t.Fatalf("merge input = %+v, want the stored row", store.mergedFrom) + } + if store.updated == nil || store.updated.Note != "merged note" { + t.Fatalf("written row = %+v, want merged note", store.updated) + } + if store.updated.CFIRange != store.mergedFrom.CFIRange || store.updated.Kind != store.mergedFrom.Kind { + t.Fatalf("written row %+v must keep unpatched fields from merge input %+v", store.updated, store.mergedFrom) + } + if !store.updated.UpdatedAt.After(store.mergedFrom.UpdatedAt) { + t.Fatalf("updated_at %v must advance past %v", store.updated.UpdatedAt, store.mergedFrom.UpdatedAt) + } +} + +func TestEbookReaderPatchReturnsNotFoundForMissingAnnotation(t *testing.T) { + store := &fakeEbookReaderAnnotationStore{} + + rr := patchEbookReaderAnnotation(t, store, `{"note":"updated"}`) + + if rr.Code != http.StatusNotFound { + t.Fatalf("status = %d, body = %s", rr.Code, rr.Body.String()) + } +} + +func TestEbookReaderRejectsOversizedProgressBody(t *testing.T) { + store := &fakeEbookProgressStore{} + handler := NewEbookReaderHandler(&MediaFileAuthorizer{ + FileResolver: stubMediaFileResolver{}, + ItemAccess: stubItemAccessChecker{}, + }) + handler.ProgressStore = store + + body := `{"file_id":42,"location":"` + strings.Repeat("x", int(ebookReaderProgressMaxBodySize)) + `","progress":0.5}` + req := newEbookReaderAuthRequest(http.MethodPut, "/ebooks/ebook-1/progress") + req.Body = ioNopCloser{strings.NewReader(body)} + req = withEbookReaderContentRouteParam(req, "ebook-1") + + rr := httptest.NewRecorder() + handler.HandleSaveProgress(rr, req) + + if rr.Code != http.StatusRequestEntityTooLarge { + t.Fatalf("status = %d, body = %s", rr.Code, rr.Body.String()) + } + if store.upserted != nil { + t.Fatalf("oversized body must not be saved, got %+v", store.upserted) + } +} + +func TestInlineContentDispositionProducesParseableHeaders(t *testing.T) { + cases := map[string]string{ + "book.epub": "book.epub", + `bo"ok\.epub`: `bo"ok\.epub`, + `trailing-slash\`: `trailing-slash\`, + "börk \U0001F4DA.epub": "börk \U0001F4DA.epub", + "": "ebook", + ".": "ebook", + } + for name, want := range cases { + t.Run(name, func(t *testing.T) { + got := inlineContentDisposition(name) + mediaType, params, err := mime.ParseMediaType(got) + if err != nil { + t.Fatalf("header %q is malformed: %v", got, err) + } + if mediaType != "inline" { + t.Fatalf("media type = %q", mediaType) + } + if params["filename"] != want { + t.Fatalf("filename = %q, want %q", params["filename"], want) + } + }) + } +} + +func TestEbookReaderDeletesAnnotation(t *testing.T) { + store := &fakeEbookReaderAnnotationStore{} + handler := NewEbookReaderHandler(&MediaFileAuthorizer{ + FileResolver: stubMediaFileResolver{}, + ItemAccess: stubItemAccessChecker{}, + }) + handler.AnnotationStore = store + + req := newEbookReaderAuthRequest(http.MethodDelete, "/ebooks/ebook-1/annotations/ann-1") + req = withEbookReaderAnnotationRouteParams(req, "ebook-1", "ann-1") + + rr := httptest.NewRecorder() + handler.HandleDeleteAnnotation(rr, req) + + if rr.Code != http.StatusNoContent { + t.Fatalf("status = %d, body = %s", rr.Code, rr.Body.String()) + } + if store.deleted != "ann-1" { + t.Fatalf("deleted = %q", store.deleted) + } +} + +type ioNopCloser struct { + *strings.Reader +} + +func (c ioNopCloser) Close() error { return nil } diff --git a/internal/api/handlers/favorites.go b/internal/api/handlers/favorites.go index ef562ee2..17bc0f33 100644 --- a/internal/api/handlers/favorites.go +++ b/internal/api/handlers/favorites.go @@ -40,6 +40,7 @@ type PersonalDataHandler struct { localFavoriteDispatcher LocalFavoriteEventDispatcher profileStaler ProfileStaler profileRefreshRequester ProfileRefreshRequester + ebookProgressStore EbookReaderProgressLister } // NewPersonalDataHandler creates a new PersonalDataHandler. @@ -55,6 +56,10 @@ func (h *PersonalDataHandler) SetDetailService(svc *catalog.DetailService) { h.detailSvc = svc } +func (h *PersonalDataHandler) SetEbookReaderProgressStore(store EbookReaderProgressLister) { + h.ebookProgressStore = store +} + // SetProfileStaler configures an optional staleness trigger for taste profiles. func (h *PersonalDataHandler) SetProfileStaler(ps ProfileStaler) { h.profileStaler = ps @@ -552,7 +557,10 @@ func resolveItemsByIDs(h *PersonalDataHandler, r *http.Request, ids []string) ([ userStates := map[string]*itemUserStateResponse{} if store, profileID, ok := h.userStoreForRequest(r); ok { - if resolvedStates, err := resolveItemUserStates(r.Context(), store, profileID, h.episodeRepo, accessibleItems); err == nil { + if resolvedStates, err := resolveItemUserStatesWithOptions(r.Context(), store, profileID, h.episodeRepo, accessibleItems, itemUserStateOptions{ + UserID: apimw.GetUserID(r.Context()), + EbookProgressStore: h.ebookProgressStore, + }); err == nil { userStates = resolvedStates } } @@ -669,7 +677,11 @@ func (h *PersonalDataHandler) resolveHistoryRemovalMediaItemIDs( return nil, err } switch item.Type { - case "movie": + case "movie", "ebook": + // Hiding an ebook gates ebook_reader_progress reads via + // user_history_hidden_items (updated_at <= hidden_before) + // without deleting the progress row, so the reader position + // survives: hidden is not the same as unread. return []string{item.ContentID}, nil case "series": return h.seriesEpisodeIDs(ctx, item.ContentID) diff --git a/internal/api/handlers/favorites_history_remove_test.go b/internal/api/handlers/favorites_history_remove_test.go new file mode 100644 index 00000000..9295d23a --- /dev/null +++ b/internal/api/handlers/favorites_history_remove_test.go @@ -0,0 +1,113 @@ +package handlers + +import ( + "context" + "net/http" + "net/http/httptest" + "testing" + + "github.com/Silo-Server/silo-server/internal/catalog" + "github.com/Silo-Server/silo-server/internal/models" + "github.com/Silo-Server/silo-server/internal/userstore" +) + +type fakeHistoryItemRepo struct { + items map[string]*models.MediaItem + inaccessible map[string]bool +} + +func (r *fakeHistoryItemRepo) GetByID(_ context.Context, contentID string) (*models.MediaItem, error) { + item, ok := r.items[contentID] + if !ok { + return nil, catalog.ErrItemNotFound + } + return item, nil +} + +func (r *fakeHistoryItemRepo) GetByIDs(_ context.Context, contentIDs []string) ([]*models.MediaItem, error) { + result := make([]*models.MediaItem, 0, len(contentIDs)) + for _, contentID := range contentIDs { + if item, ok := r.items[contentID]; ok { + result = append(result, item) + } + } + return result, nil +} + +func (r *fakeHistoryItemRepo) EnsureAccessible(_ context.Context, contentID string, _ catalog.AccessFilter) error { + if r.inaccessible[contentID] { + return catalog.ErrItemNotFound + } + return nil +} + +func TestHandleRemoveHistoryAcceptsEbookTargets(t *testing.T) { + ctx := context.Background() + store := newProfileTestStore(t) + handler := NewPersonalDataHandler(testUserStoreProvider{store: store}, &fakeHistoryItemRepo{ + items: map[string]*models.MediaItem{ + "ebook-1": {ContentID: "ebook-1", Type: "ebook", Title: "Book"}, + }, + }) + + // Seed a history entry keyed by the ebook content ID so the removal has + // something to hide. RemoveHistoryItems only touches watch history, + // watch progress, and the hidden-items gate — PersonalDataHandler has no + // write access to ebook_reader_progress (its store interface is read-only), + // so the reader position survives: hidden is not the same as unread. + if err := store.AddHistory(ctx, userstore.WatchHistoryEntry{ + ProfileID: "profile-1", + MediaItemID: "ebook-1", + WatchedAt: "2026-06-01T10:00:00Z", + Completed: true, + Source: userstore.WatchHistorySourceManual, + }); err != nil { + t.Fatalf("AddHistory: %v", err) + } + + req := newAuthorizedProfileRequestWithRole( + http.MethodPost, + "/history/remove", + `{"targets":[{"content_id":"ebook-1"}]}`, + "user", + "profile-1", + ) + rr := httptest.NewRecorder() + handler.HandleRemoveHistory(rr, req) + + if rr.Code != http.StatusNoContent { + t.Fatalf("status = %d, body = %s", rr.Code, rr.Body.String()) + } + + entries, err := store.ListHistory(ctx, "profile-1", 10, 0) + if err != nil { + t.Fatalf("ListHistory: %v", err) + } + if len(entries) != 0 { + t.Fatalf("history entries = %+v, want hidden after removal", entries) + } +} + +func TestHandleRemoveHistoryRejectsInaccessibleEbook(t *testing.T) { + store := newProfileTestStore(t) + handler := NewPersonalDataHandler(testUserStoreProvider{store: store}, &fakeHistoryItemRepo{ + items: map[string]*models.MediaItem{ + "ebook-1": {ContentID: "ebook-1", Type: "ebook", Title: "Book"}, + }, + inaccessible: map[string]bool{"ebook-1": true}, + }) + + req := newAuthorizedProfileRequestWithRole( + http.MethodPost, + "/history/remove", + `{"targets":[{"content_id":"ebook-1"}]}`, + "user", + "profile-1", + ) + rr := httptest.NewRecorder() + handler.HandleRemoveHistory(rr, req) + + if rr.Code != http.StatusNotFound { + t.Fatalf("status = %d, body = %s", rr.Code, rr.Body.String()) + } +} diff --git a/internal/api/handlers/items.go b/internal/api/handlers/items.go index 1198bbf0..7697073c 100644 --- a/internal/api/handlers/items.go +++ b/internal/api/handlers/items.go @@ -46,6 +46,10 @@ type LocalWatchEventDispatcher interface { HandleLocalWatchEvent(ctx context.Context, event watchsync.LocalWatchEvent) error } +type EbookReaderProgressLister interface { + ListByContentIDs(ctx context.Context, userID int, profileID string, contentIDs []string) (map[string]EbookReaderProgress, error) +} + // ItemsHandler handles browse, search, item detail, and series endpoints. type ItemsHandler struct { browseRepo *catalog.BrowseRepository @@ -62,6 +66,8 @@ type ItemsHandler struct { profileRefreshRequester ProfileRefreshRequester metadataRefreshRequester MetadataRefreshRequester localWatchDispatcher LocalWatchEventDispatcher + ebookProgressStore EbookReaderProgressLister + ebookReadStateStore EbookReadStateStore EventsHub *evt.Hub UserRepo *auth.UserRepository } @@ -114,6 +120,11 @@ func (h *ItemsHandler) SetLocalWatchEventDispatcher(dispatcher LocalWatchEventDi h.localWatchDispatcher = dispatcher } +func (h *ItemsHandler) SetEbookReaderProgressStore(store EbookReaderProgressReadWriter) { + h.ebookProgressStore = store + h.ebookReadStateStore = store +} + func (h *ItemsHandler) maybeRequestStaleDetailMetadataRefresh(ctx context.Context, detail *catalog.ItemDetail) { if h == nil || detail == nil || h.metadataRefreshRequester == nil { return @@ -376,8 +387,8 @@ func (h *ItemsHandler) HandleGetWatchDetail(w http.ResponseWriter, r *http.Reque } } - if detail.Type == "movie" || detail.Type == "episode" { - detail.UserData = h.getLeafUserData(r, detail.ContentID) + if detail.Type == "movie" || detail.Type == "episode" || detail.Type == "ebook" { + detail.UserData = h.getLeafUserData(r, detail.ContentID, detail.Type) applyEffectiveEditionPreference(detail.UserData, &detail.EffectiveVersionEditionKey) } @@ -432,7 +443,8 @@ func (h *ItemsHandler) handleSetWatchedState(w http.ResponseWriter, r *http.Requ return } - targetType, targets, err := h.resolveWatchedTargets(r.Context(), id, h.accessFilter(r)) + filter := h.accessFilter(r) + targetType, targets, err := h.resolveWatchedTargets(r.Context(), id, filter) if err != nil { switch { case isNotFound(err): @@ -443,12 +455,16 @@ func (h *ItemsHandler) handleSetWatchedState(w http.ResponseWriter, r *http.Requ return } - if h.watchState == nil { + switch { + case targetType == "ebook": + // Ebook read state lives in ebook_reader_progress, not in + // user_watch_progress/user_watch_history; watch providers do not sync + // books, so no local watch event is dispatched. + err = h.setEbookReadState(r.Context(), userID, profileID, id, played, filter) + case h.watchState == nil: writeError(w, http.StatusInternalServerError, "internal_error", "Failed to access user store") return - } - - if played { + case played: leafTargets := make([]watchstate.LeafWatchTarget, 0, len(targets)) for _, target := range targets { leafTargets = append(leafTargets, watchstate.LeafWatchTarget{ @@ -462,7 +478,7 @@ func (h *ItemsHandler) handleSetWatchedState(w http.ResponseWriter, r *http.Requ if err == nil { h.dispatchLocalWatchEvent(r.Context(), watchsync.LocalWatchEventMarkedWatched, userID, profileID, result) } - } else { + default: targetIDs := make([]string, 0, len(targets)) for _, target := range targets { targetIDs = append(targetIDs, target.ContentID) @@ -474,6 +490,10 @@ func (h *ItemsHandler) handleSetWatchedState(w http.ResponseWriter, r *http.Requ } } if err != nil { + if isNotFound(err) { + writeError(w, http.StatusNotFound, "not_found", "Item not found") + return + } writeError(w, http.StatusInternalServerError, "internal_error", "Failed to update watched state") return } @@ -742,7 +762,10 @@ func (h *ItemsHandler) listItemUserStates(r *http.Request, items []*models.Media if !ok { return map[string]*itemUserStateResponse{} } - states, err := resolveItemUserStates(r.Context(), store, profileID, h.episodeRepo, items) + states, err := resolveItemUserStatesWithOptions(r.Context(), store, profileID, h.episodeRepo, items, itemUserStateOptions{ + UserID: apimw.GetUserID(r.Context()), + EbookProgressStore: h.ebookProgressStore, + }) if err != nil { return map[string]*itemUserStateResponse{} } @@ -853,6 +876,7 @@ func (h *ItemsHandler) listSortMetrics( filter catalog.AccessFilter, overlaySummaries map[string]*models.OverlaySummary, store userstore.UserStore, + userID int, profileID string, ) map[string]*sortMetricsResponse { metrics := make(map[string]*sortMetricsResponse, len(items)) @@ -893,7 +917,7 @@ func (h *ItemsHandler) listSortMetrics( } } case "progress", "date_viewed", "plays": - h.listUserSortMetrics(ctx, items, sortField, store, profileID, metrics) + h.listUserSortMetrics(ctx, items, sortField, store, userID, profileID, metrics) case "author", "narrator", "series": h.listAudiobookSortMetrics(ctx, items, sortField, metrics) } @@ -983,6 +1007,7 @@ func (h *ItemsHandler) listUserSortMetrics( items []*models.MediaItem, sortField string, store userstore.UserStore, + userID int, profileID string, metrics map[string]*sortMetricsResponse, ) { @@ -998,6 +1023,10 @@ func (h *ItemsHandler) listUserSortMetrics( if err != nil { return } + ebookProgressMap, err := h.listEbookReaderProgressByContentIDs(ctx, userID, profileID, contentIDs) + if err != nil { + return + } switch sortField { case "progress": @@ -1006,10 +1035,16 @@ func (h *ItemsHandler) listUserSortMetrics( continue } progress, ok := progressMap[item.ContentID] - if !ok || progress.Completed || progress.PositionSeconds <= 0 || progress.DurationSeconds <= 0 { + if ok && !progress.Completed && progress.PositionSeconds > 0 && progress.DurationSeconds > 0 { + ratio := progress.PositionSeconds / progress.DurationSeconds + metrics[item.ContentID] = &sortMetricsResponse{ProgressRatio: &ratio} continue } - ratio := progress.PositionSeconds / progress.DurationSeconds + ebookProgress, ok := ebookProgressMap[item.ContentID] + if !ok || ebookProgress.Progress <= 0 || ebookProgress.Progress >= models.EbookFinishedProgressThreshold { + continue + } + ratio := ebookProgress.Progress metrics[item.ContentID] = &sortMetricsResponse{ProgressRatio: &ratio} } case "date_viewed", "plays": @@ -1038,6 +1073,12 @@ func (h *ItemsHandler) listUserSortMetrics( if progress, ok := progressMap[item.ContentID]; ok && progress.Completed && progress.UpdatedAt > viewedAt { viewedAt = progress.UpdatedAt } + if progress, ok := ebookProgressMap[item.ContentID]; ok && progress.Progress >= models.EbookFinishedProgressThreshold { + ebookViewedAt := progress.UpdatedAt.UTC().Format(time.RFC3339) + if ebookViewedAt > viewedAt { + viewedAt = ebookViewedAt + } + } if viewedAt == "" { continue } @@ -1047,6 +1088,9 @@ func (h *ItemsHandler) listUserSortMetrics( if progress, ok := progressMap[item.ContentID]; ok && progress.Completed && playCount < 1 { playCount = 1 } + if progress, ok := ebookProgressMap[item.ContentID]; ok && progress.Progress >= models.EbookFinishedProgressThreshold && playCount < 1 { + playCount = 1 + } if playCount <= 0 { continue } @@ -1057,6 +1101,18 @@ func (h *ItemsHandler) listUserSortMetrics( } } +func (h *ItemsHandler) listEbookReaderProgressByContentIDs( + ctx context.Context, + userID int, + profileID string, + contentIDs []string, +) (map[string]EbookReaderProgress, error) { + if h == nil || h.ebookProgressStore == nil || userID <= 0 || profileID == "" || len(contentIDs) == 0 { + return nil, nil + } + return h.ebookProgressStore.ListByContentIDs(ctx, userID, profileID, contentIDs) +} + func uniqueItemContentIDs(items []*models.MediaItem) []string { contentIDs := make([]string, 0, len(items)) seen := make(map[string]struct{}, len(items)) @@ -1193,7 +1249,11 @@ func (h *ItemsHandler) toSeasonResponse(r *http.Request, seriesID string, s *mod return resp } -func (h *ItemsHandler) getLeafUserData(r *http.Request, contentID string) *catalog.SeasonUserData { +func (h *ItemsHandler) getLeafUserData(r *http.Request, contentID string, itemType ...string) *catalog.SeasonUserData { + if len(itemType) > 0 && itemType[0] == "ebook" { + return h.getEbookLeafUserData(r, contentID) + } + store, profileID, ok := h.userStoreForRequest(r) if !ok { return nil @@ -1217,6 +1277,38 @@ func (h *ItemsHandler) getLeafUserData(r *http.Request, contentID string) *catal } } +func (h *ItemsHandler) getEbookLeafUserData(r *http.Request, contentID string) *catalog.SeasonUserData { + if h == nil || h.ebookProgressStore == nil { + return nil + } + userID := apimw.GetUserID(r.Context()) + profileID := requestProfileID(r) + if userID <= 0 || profileID == "" || contentID == "" { + return nil + } + + progress, err := h.ebookProgressStore.ListByContentIDs(r.Context(), userID, profileID, []string{contentID}) + if err != nil { + return nil + } + row, ok := progress[contentID] + if !ok || row.Progress <= 0 { + return nil + } + + // Ebooks have no playback duration, so PositionSeconds/DurationSeconds + // encode the 0..1 reading ratio (position=ratio, duration=1). Clients can + // derive percent-complete from the pair as usual, but rendering them as + // absolute times is meaningless for ebooks. Changing the wire shape needs + // coordinated Android/Apple client updates. + return &catalog.SeasonUserData{ + PositionSeconds: row.Progress, + DurationSeconds: 1, + IsInProgress: row.Progress < models.EbookFinishedProgressThreshold, + Played: row.Progress >= models.EbookFinishedProgressThreshold, + } +} + func applyEffectiveEditionPreference(userData *catalog.SeasonUserData, target **string) { if userData == nil || userData.LastEditionKey == nil || *userData.LastEditionKey == "" { return @@ -1261,6 +1353,16 @@ func (h *ItemsHandler) getAggregateUserData(r *http.Request, episodes []*models. } } +// requestProfileID resolves the active profile for a request, falling back to +// the X-Profile-Id header when the auth context does not carry one. +func requestProfileID(r *http.Request) string { + profileID := apimw.GetProfileID(r.Context()) + if profileID == "" { + profileID = r.Header.Get("X-Profile-Id") + } + return profileID +} + func (h *ItemsHandler) userStoreForRequest(r *http.Request) (userstore.UserStore, string, bool) { if h.storeProvider == nil { return nil, "", false @@ -1271,10 +1373,7 @@ func (h *ItemsHandler) userStoreForRequest(r *http.Request) (userstore.UserStore return nil, "", false } - profileID := apimw.GetProfileID(r.Context()) - if profileID == "" { - profileID = r.Header.Get("X-Profile-Id") - } + profileID := requestProfileID(r) if profileID == "" { return nil, "", false } @@ -1468,6 +1567,10 @@ func (h *ItemsHandler) resolveWatchedTargets(ctx context.Context, contentID stri ContentID: item.ContentID, DurationSeconds: h.contentDurationSeconds(ctx, item.ContentID, "", item.Runtime), }}, nil + case "ebook": + // Ebooks have no playback duration; read state is keyed off + // ebook_reader_progress, so the leaf target only carries the ID. + return "ebook", []watchedLeafTarget{{ContentID: item.ContentID}}, nil case "series": if h.episodeRepo == nil { return "", nil, catalog.ErrItemNotFound diff --git a/internal/api/handlers/items_sort_metrics_test.go b/internal/api/handlers/items_sort_metrics_test.go index b8b77ff0..e80e32cc 100644 --- a/internal/api/handlers/items_sort_metrics_test.go +++ b/internal/api/handlers/items_sort_metrics_test.go @@ -40,20 +40,145 @@ func TestListSortMetricsIncludesUserStateSorts(t *testing.T) { } } - progressMetrics := handler.listSortMetrics(ctx, items, "progress", catalog.AccessFilter{}, nil, store, "profile-1") + progressMetrics := handler.listSortMetrics(ctx, items, "progress", catalog.AccessFilter{}, nil, store, 0, "profile-1") progress := progressMetrics["movie-progress"] if progress == nil || progress.ProgressRatio == nil || *progress.ProgressRatio != 0.25 { t.Fatalf("progress metrics = %#v", progress) } - viewedMetrics := handler.listSortMetrics(ctx, items, "date_viewed", catalog.AccessFilter{}, nil, store, "profile-1") + viewedMetrics := handler.listSortMetrics(ctx, items, "date_viewed", catalog.AccessFilter{}, nil, store, 0, "profile-1") if got := viewedMetrics["movie-viewed"]; got == nil || got.ViewedAt != "2026-05-29T18:30:00Z" { t.Fatalf("viewed metrics = %#v", got) } - playsMetrics := handler.listSortMetrics(ctx, items, "plays", catalog.AccessFilter{}, nil, store, "profile-1") + playsMetrics := handler.listSortMetrics(ctx, items, "plays", catalog.AccessFilter{}, nil, store, 0, "profile-1") plays := playsMetrics["movie-plays"] if plays == nil || plays.PlayCount == nil || *plays.PlayCount != 2 { t.Fatalf("plays metrics = %#v", plays) } } + +func TestListSortMetricsIncludesEbookReaderProgress(t *testing.T) { + ctx := context.Background() + store := newProfileTestStore(t) + updatedAt := time.Date(2026, 6, 3, 9, 15, 0, 0, time.UTC) + handler := &ItemsHandler{ + ebookProgressStore: &fakeEbookReaderProgressLister{ + progress: map[string]EbookReaderProgress{ + "ebook-progress": { + UserID: 42, + ProfileID: "profile-1", + ContentID: "ebook-progress", + Progress: 0.37, + UpdatedAt: updatedAt, + }, + "ebook-complete": { + UserID: 42, + ProfileID: "profile-1", + ContentID: "ebook-complete", + Progress: 0.95, + UpdatedAt: updatedAt.Add(time.Hour), + }, + }, + }, + } + items := []*models.MediaItem{ + {ContentID: "ebook-progress", Type: "ebook", Title: "Progress"}, + {ContentID: "ebook-complete", Type: "ebook", Title: "Complete"}, + } + + progressMetrics := handler.listSortMetrics(ctx, items, "progress", catalog.AccessFilter{}, nil, store, 42, "profile-1") + progress := progressMetrics["ebook-progress"] + if progress == nil || progress.ProgressRatio == nil || *progress.ProgressRatio != 0.37 { + t.Fatalf("progress metrics = %#v", progress) + } + + viewedMetrics := handler.listSortMetrics(ctx, items, "date_viewed", catalog.AccessFilter{}, nil, store, 42, "profile-1") + if got := viewedMetrics["ebook-complete"]; got == nil || got.ViewedAt != "2026-06-03T10:15:00Z" { + t.Fatalf("viewed metrics = %#v", got) + } + + playsMetrics := handler.listSortMetrics(ctx, items, "plays", catalog.AccessFilter{}, nil, store, 42, "profile-1") + plays := playsMetrics["ebook-complete"] + if plays == nil || plays.PlayCount == nil || *plays.PlayCount != 1 { + t.Fatalf("plays metrics = %#v", plays) + } +} + +func TestListSortMetricsExcludesHiddenEbookReaderProgress(t *testing.T) { + ctx := context.Background() + store := newProfileTestStore(t) + updatedAt := time.Date(2026, 6, 3, 9, 15, 0, 0, time.UTC) + handler := &ItemsHandler{ + ebookProgressStore: &fakeEbookReaderProgressLister{ + progress: map[string]EbookReaderProgress{ + "ebook-hidden": { + UserID: 42, + ProfileID: "profile-1", + ContentID: "ebook-hidden", + Progress: 0.95, + UpdatedAt: updatedAt, + }, + "ebook-reread": { + UserID: 42, + ProfileID: "profile-1", + ContentID: "ebook-reread", + Progress: 0.95, + UpdatedAt: updatedAt.Add(2 * time.Hour), + }, + }, + hiddenBefore: map[string]time.Time{ + "ebook-hidden": updatedAt.Add(time.Hour), + "ebook-reread": updatedAt.Add(time.Hour), + }, + }, + } + items := []*models.MediaItem{ + {ContentID: "ebook-hidden", Type: "ebook", Title: "Hidden"}, + {ContentID: "ebook-reread", Type: "ebook", Title: "Reread"}, + } + + viewedMetrics := handler.listSortMetrics(ctx, items, "date_viewed", catalog.AccessFilter{}, nil, store, 42, "profile-1") + if got := viewedMetrics["ebook-hidden"]; got != nil { + t.Fatalf("hidden ebook must have no viewed metrics, got %#v", got) + } + if got := viewedMetrics["ebook-reread"]; got == nil || got.ViewedAt != "2026-06-03T11:15:00Z" { + t.Fatalf("progress updated after hidden_before must count again, got %#v", got) + } + + playsMetrics := handler.listSortMetrics(ctx, items, "plays", catalog.AccessFilter{}, nil, store, 42, "profile-1") + if got := playsMetrics["ebook-hidden"]; got != nil { + t.Fatalf("hidden ebook must have no play count, got %#v", got) + } + if got := playsMetrics["ebook-reread"]; got == nil || got.PlayCount == nil || *got.PlayCount != 1 { + t.Fatalf("progress updated after hidden_before must count again, got %#v", got) + } +} + +type fakeEbookReaderProgressLister struct { + progress map[string]EbookReaderProgress + // hiddenBefore models user_history_hidden_items: like the PG store, a + // row is excluded while progress.UpdatedAt <= hidden_before and counts + // again once the progress row is updated after the hide. + hiddenBefore map[string]time.Time +} + +func (f *fakeEbookReaderProgressLister) ListByContentIDs( + _ context.Context, + _ int, + _ string, + contentIDs []string, +) (map[string]EbookReaderProgress, error) { + result := make(map[string]EbookReaderProgress, len(contentIDs)) + for _, contentID := range contentIDs { + progress, ok := f.progress[contentID] + if !ok { + continue + } + if hiddenBefore, hidden := f.hiddenBefore[contentID]; hidden && !progress.UpdatedAt.After(hiddenBefore) { + continue + } + result[contentID] = progress + } + return result, nil +} diff --git a/internal/api/handlers/items_user_data_test.go b/internal/api/handlers/items_user_data_test.go new file mode 100644 index 00000000..81f111d6 --- /dev/null +++ b/internal/api/handlers/items_user_data_test.go @@ -0,0 +1,47 @@ +package handlers + +import ( + "net/http/httptest" + "testing" + "time" + + apimw "github.com/Silo-Server/silo-server/internal/api/middleware" + "github.com/Silo-Server/silo-server/internal/auth" +) + +func TestGetLeafUserDataUsesEbookReaderProgress(t *testing.T) { + handler := &ItemsHandler{ + ebookProgressStore: &fakeEbookReaderProgressLister{ + progress: map[string]EbookReaderProgress{ + "ebook-progress": { + UserID: 7, + ProfileID: "profile-1", + ContentID: "ebook-progress", + Progress: 0.42, + UpdatedAt: time.Date(2026, 6, 5, 10, 0, 0, 0, time.UTC), + }, + "ebook-complete": { + UserID: 7, + ProfileID: "profile-1", + ContentID: "ebook-complete", + Progress: 0.93, + UpdatedAt: time.Date(2026, 6, 5, 11, 0, 0, 0, time.UTC), + }, + }, + }, + } + req := httptest.NewRequest("GET", "/items/ebook-progress", nil) + ctx := apimw.SetClaims(req.Context(), &auth.Claims{UserID: 7}) + ctx = apimw.SetProfileID(ctx, "profile-1") + req = req.WithContext(ctx) + + progress := handler.getLeafUserData(req, "ebook-progress", "ebook") + if progress == nil || progress.Played || !progress.IsInProgress || progress.PositionSeconds != 0.42 || progress.DurationSeconds != 1 { + t.Fatalf("partial ebook user data = %#v", progress) + } + + complete := handler.getLeafUserData(req, "ebook-complete", "ebook") + if complete == nil || !complete.Played || complete.IsInProgress { + t.Fatalf("completed ebook user data = %#v", complete) + } +} diff --git a/internal/api/handlers/libraries.go b/internal/api/handlers/libraries.go index c632d024..fae7be03 100644 --- a/internal/api/handlers/libraries.go +++ b/internal/api/handlers/libraries.go @@ -2034,18 +2034,8 @@ func (h *LibraryHandler) seedDefaultChain(ctx context.Context, libraryType strin return nil } - // Determine which content levels apply. - var levels []string - switch libraryType { - case "series": - levels = []string{"series", "season", "episode"} - case "movies", "movie": - levels = []string{"movie"} - case "audiobooks", "audiobook": - levels = []string{"audiobook"} - case "mixed": - levels = []string{"movie", "series", "season", "episode", "audiobook"} - default: + levels := metadataContentLevelsForLibraryType(libraryType) + if len(levels) == 0 { return nil } @@ -2098,6 +2088,23 @@ func (h *LibraryHandler) seedDefaultChain(ctx context.Context, libraryType strin return entries } +func metadataContentLevelsForLibraryType(libraryType string) []string { + switch libraryType { + case "series": + return []string{"series", "season", "episode"} + case "movies", "movie": + return []string{"movie"} + case "audiobooks", "audiobook": + return []string{"audiobook"} + case "ebooks", "ebook": + return []string{"ebook"} + case "mixed": + return []string{"movie", "series", "season", "episode", "audiobook", "ebook"} + default: + return nil + } +} + // HandleListStaleIDs handles GET /libraries/stale-ids. func (h *LibraryHandler) HandleListStaleIDs(w http.ResponseWriter, r *http.Request) { if h.StaleIDRepo == nil { diff --git a/internal/api/handlers/libraries_metadata_levels_test.go b/internal/api/handlers/libraries_metadata_levels_test.go new file mode 100644 index 00000000..b6a542b2 --- /dev/null +++ b/internal/api/handlers/libraries_metadata_levels_test.go @@ -0,0 +1,26 @@ +package handlers + +import ( + "reflect" + "testing" +) + +func TestMetadataContentLevelsForLibraryTypeIncludesEbooks(t *testing.T) { + cases := []struct { + name string + libraryType string + want []string + }{ + {name: "plural ebooks", libraryType: "ebooks", want: []string{"ebook"}}, + {name: "singular ebook", libraryType: "ebook", want: []string{"ebook"}}, + {name: "mixed includes ebook", libraryType: "mixed", want: []string{"movie", "series", "season", "episode", "audiobook", "ebook"}}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + if got := metadataContentLevelsForLibraryType(tc.libraryType); !reflect.DeepEqual(got, tc.want) { + t.Fatalf("metadataContentLevelsForLibraryType(%q) = %#v, want %#v", tc.libraryType, got, tc.want) + } + }) + } +} diff --git a/internal/api/handlers/recommendations.go b/internal/api/handlers/recommendations.go index efa2b322..13cf808a 100644 --- a/internal/api/handlers/recommendations.go +++ b/internal/api/handlers/recommendations.go @@ -48,6 +48,7 @@ type RecommendationsHandler struct { EpisodeRepo *catalog.EpisodeRepository WatchTonightFetcher watchTonightSectionFetcher CastFetcher cardsCastFetcher + EbookProgress EbookReaderProgressLister // RecWorker enqueues asynchronous taste-profile refreshes after writes // (taste seeding). Optional — when nil, refresh is simply skipped. RecWorker ProfileRefreshRequester @@ -643,7 +644,10 @@ func (h *RecommendationsHandler) loadItemEnrichment( if h.storeProvider != nil { store, storeErr := h.storeProvider.ForUser(ctx, userID) if storeErr == nil && store != nil { - states, stateErr := resolveItemUserStates(ctx, store, profileID, h.EpisodeRepo, mediaItems) + states, stateErr := resolveItemUserStatesWithOptions(ctx, store, profileID, h.EpisodeRepo, mediaItems, itemUserStateOptions{ + UserID: userID, + EbookProgressStore: h.EbookProgress, + }) if stateErr == nil { out.states = states } diff --git a/internal/api/handlers/sections.go b/internal/api/handlers/sections.go index d9551383..ea680a07 100644 --- a/internal/api/handlers/sections.go +++ b/internal/api/handlers/sections.go @@ -34,6 +34,7 @@ type SectionHandler struct { DetailSvc *catalog.DetailService Settings catalog.SettingsStore CollectionRepo *catalog.LibraryCollectionRepository + EbookProgress EbookReaderProgressLister } // NewSectionHandler creates a new SectionHandler. @@ -1380,7 +1381,10 @@ func (h *SectionHandler) listSectionItemUserStates(r *http.Request, items []*mod if err != nil || store == nil { return map[string]*itemUserStateResponse{} } - states, err := resolveItemUserStates(r.Context(), store, profileID, h.EpisodeRepo, items) + states, err := resolveItemUserStatesWithOptions(r.Context(), store, profileID, h.EpisodeRepo, items, itemUserStateOptions{ + UserID: userID, + EbookProgressStore: h.EbookProgress, + }) if err != nil { return map[string]*itemUserStateResponse{} } diff --git a/internal/api/handlers/taste_seed.go b/internal/api/handlers/taste_seed.go index ef70b37b..21f863f9 100644 --- a/internal/api/handlers/taste_seed.go +++ b/internal/api/handlers/taste_seed.go @@ -181,7 +181,10 @@ func (h *RecommendationsHandler) resolveTasteSeedUserStates(ctx context.Context, if err != nil || store == nil { return nil } - states, err := resolveItemUserStates(ctx, store, profileID, h.EpisodeRepo, mediaItems) + states, err := resolveItemUserStatesWithOptions(ctx, store, profileID, h.EpisodeRepo, mediaItems, itemUserStateOptions{ + UserID: userID, + EbookProgressStore: h.EbookProgress, + }) if err != nil { return nil } diff --git a/internal/api/handlers/user_state.go b/internal/api/handlers/user_state.go index 6857a73f..c947b614 100644 --- a/internal/api/handlers/user_state.go +++ b/internal/api/handlers/user_state.go @@ -14,12 +14,28 @@ type itemUserStateResponse struct { InWatchlist bool `json:"in_watchlist"` } +type itemUserStateOptions struct { + UserID int + EbookProgressStore EbookReaderProgressLister +} + func resolveItemUserStates( ctx context.Context, store userstore.UserStore, profileID string, episodeRepo *catalog.EpisodeRepository, items []*models.MediaItem, +) (map[string]*itemUserStateResponse, error) { + return resolveItemUserStatesWithOptions(ctx, store, profileID, episodeRepo, items, itemUserStateOptions{}) +} + +func resolveItemUserStatesWithOptions( + ctx context.Context, + store userstore.UserStore, + profileID string, + episodeRepo *catalog.EpisodeRepository, + items []*models.MediaItem, + options itemUserStateOptions, ) (map[string]*itemUserStateResponse, error) { result := make(map[string]*itemUserStateResponse, len(items)) if store == nil || profileID == "" || len(items) == 0 { @@ -111,6 +127,10 @@ func resolveItemUserStates( if err != nil { return nil, err } + ebookProgressMap, err := resolveEbookProgressForUserStates(ctx, options, profileID, contentIDs) + if err != nil { + return nil, err + } for _, item := range items { if item == nil || item.ContentID == "" { @@ -125,6 +145,8 @@ func resolveItemUserStates( state.Played = allEpisodesCompleted(seriesEpisodes[item.ContentID], progressMap) case "season": state.Played = allEpisodesCompleted(seasonEpisodes[item.ContentID], progressMap) + case "ebook": + state.Played = ebookProgressMap[item.ContentID].Progress >= models.EbookFinishedProgressThreshold default: state.Played = progressMap[item.ContentID].Completed } @@ -134,6 +156,18 @@ func resolveItemUserStates( return result, nil } +func resolveEbookProgressForUserStates( + ctx context.Context, + options itemUserStateOptions, + profileID string, + contentIDs []string, +) (map[string]EbookReaderProgress, error) { + if options.EbookProgressStore == nil || options.UserID <= 0 || profileID == "" || len(contentIDs) == 0 { + return nil, nil + } + return options.EbookProgressStore.ListByContentIDs(ctx, options.UserID, profileID, contentIDs) +} + func allEpisodesCompleted(episodes []*models.Episode, progressMap map[string]userstore.WatchProgress) bool { if len(episodes) == 0 { return false diff --git a/internal/api/handlers/user_state_test.go b/internal/api/handlers/user_state_test.go new file mode 100644 index 00000000..9ff974b3 --- /dev/null +++ b/internal/api/handlers/user_state_test.go @@ -0,0 +1,96 @@ +package handlers + +import ( + "context" + "testing" + "time" + + "github.com/Silo-Server/silo-server/internal/models" +) + +func TestResolveItemUserStatesIncludesCompletedEbookReaderProgress(t *testing.T) { + ctx := context.Background() + store := newProfileTestStore(t) + items := []*models.MediaItem{ + {ContentID: "ebook-complete", Type: "ebook", Title: "Complete Ebook"}, + {ContentID: "ebook-progress", Type: "ebook", Title: "Partial Ebook"}, + } + + states, err := resolveItemUserStatesWithOptions(ctx, store, "profile-1", nil, items, itemUserStateOptions{ + UserID: 42, + EbookProgressStore: &fakeEbookReaderProgressLister{ + progress: map[string]EbookReaderProgress{ + "ebook-complete": { + UserID: 42, + ProfileID: "profile-1", + ContentID: "ebook-complete", + Progress: 0.95, + UpdatedAt: time.Date(2026, 6, 4, 10, 0, 0, 0, time.UTC), + }, + "ebook-progress": { + UserID: 42, + ProfileID: "profile-1", + ContentID: "ebook-progress", + Progress: 0.45, + UpdatedAt: time.Date(2026, 6, 4, 9, 0, 0, 0, time.UTC), + }, + }, + }, + }) + if err != nil { + t.Fatalf("resolveItemUserStatesWithOptions: %v", err) + } + + if states["ebook-complete"] == nil || !states["ebook-complete"].Played { + t.Fatalf("completed ebook state = %+v, want played", states["ebook-complete"]) + } + if states["ebook-progress"] == nil || states["ebook-progress"].Played { + t.Fatalf("partial ebook state = %+v, want not played", states["ebook-progress"]) + } +} + +func TestResolveItemUserStatesExcludesHiddenEbookReaderProgress(t *testing.T) { + ctx := context.Background() + store := newProfileTestStore(t) + updatedAt := time.Date(2026, 6, 4, 10, 0, 0, 0, time.UTC) + items := []*models.MediaItem{ + {ContentID: "ebook-hidden", Type: "ebook", Title: "Hidden Ebook"}, + {ContentID: "ebook-reread", Type: "ebook", Title: "Reread Ebook"}, + } + + states, err := resolveItemUserStatesWithOptions(ctx, store, "profile-1", nil, items, itemUserStateOptions{ + UserID: 42, + EbookProgressStore: &fakeEbookReaderProgressLister{ + progress: map[string]EbookReaderProgress{ + "ebook-hidden": { + UserID: 42, + ProfileID: "profile-1", + ContentID: "ebook-hidden", + Progress: 0.95, + UpdatedAt: updatedAt, + }, + "ebook-reread": { + UserID: 42, + ProfileID: "profile-1", + ContentID: "ebook-reread", + Progress: 0.95, + UpdatedAt: updatedAt.Add(2 * time.Hour), + }, + }, + hiddenBefore: map[string]time.Time{ + "ebook-hidden": updatedAt.Add(time.Hour), + "ebook-reread": updatedAt.Add(time.Hour), + }, + }, + }) + if err != nil { + t.Fatalf("resolveItemUserStatesWithOptions: %v", err) + } + + if states["ebook-hidden"] == nil || states["ebook-hidden"].Played { + t.Fatalf("hidden ebook state = %+v, want not played", states["ebook-hidden"]) + } + if states["ebook-reread"] == nil || !states["ebook-reread"].Played { + t.Fatalf("ebook updated after hidden_before = %+v, want played again", states["ebook-reread"]) + } +} diff --git a/internal/api/handlers/watch_tonight.go b/internal/api/handlers/watch_tonight.go index 2cb52821..10c7e997 100644 --- a/internal/api/handlers/watch_tonight.go +++ b/internal/api/handlers/watch_tonight.go @@ -362,7 +362,10 @@ func (h *RecommendationsHandler) enrichItems(r *http.Request, userID int, profil if h.storeProvider != nil { store, storeErr := h.storeProvider.ForUser(ctx, userID) if storeErr == nil && store != nil { - states, stateErr := resolveItemUserStates(ctx, store, profileID, h.EpisodeRepo, mediaItems) + states, stateErr := resolveItemUserStatesWithOptions(ctx, store, profileID, h.EpisodeRepo, mediaItems, itemUserStateOptions{ + UserID: userID, + EbookProgressStore: h.EbookProgress, + }) if stateErr == nil { stateMap = states } diff --git a/internal/api/router.go b/internal/api/router.go index 2d0a4d4d..e14c0bea 100644 --- a/internal/api/router.go +++ b/internal/api/router.go @@ -370,7 +370,14 @@ func NewRouter(deps Dependencies) chi.Router { var webhookSyncHandler *handlers.WebhookSyncHandler var requestHandler *handlers.RequestsHandler var autoscanHandler *handlers.AutoscanHandler + var ebookReaderHandler *handlers.EbookReaderHandler + var ebookProgressStore *handlers.PGEbookReaderProgressStore + var ebookConfigStore *handlers.PGEbookReaderConfigStore + var ebookAnnotationStore *handlers.PGEbookReaderAnnotationStore if deps.DB != nil { + ebookProgressStore = handlers.NewPGEbookReaderProgressStore(deps.DB) + ebookConfigStore = handlers.NewPGEbookReaderConfigStore(deps.DB) + ebookAnnotationStore = handlers.NewPGEbookReaderAnnotationStore(deps.DB) browseRepo := catalog.NewBrowseRepository(deps.DB) itemRepo = catalog.NewItemRepository(deps.DB) episodeRepo = catalog.NewEpisodeRepository(deps.DB) @@ -421,6 +428,25 @@ func NewRouter(deps Dependencies) chi.Router { if dispatcher, ok := deps.WatchProviderService.(handlers.LocalWatchEventDispatcher); ok { itemsHandler.SetLocalWatchEventDispatcher(dispatcher) } + if ebookProgressStore != nil { + itemsHandler.SetEbookReaderProgressStore(ebookProgressStore) + } + if deps.FileRepo != nil { + ebookReaderHandler = handlers.NewEbookReaderHandler(&handlers.MediaFileAuthorizer{ + FileResolver: deps.FileRepo, + ItemAccess: itemRepo, + EpisodeLookup: episodeRepo, + }) + if ebookProgressStore != nil { + ebookReaderHandler.ProgressStore = ebookProgressStore + } + if ebookConfigStore != nil { + ebookReaderHandler.ConfigStore = ebookConfigStore + } + if ebookAnnotationStore != nil { + ebookReaderHandler.AnnotationStore = ebookAnnotationStore + } + } catalogResourceHandler = handlers.NewCatalogResourceHandler(itemsHandler) catalogHandler = handlers.NewCatalogHandler( catalog.NewCatalogResolver(browseRepo, itemRepo). @@ -499,6 +525,9 @@ func NewRouter(deps Dependencies) chi.Router { if detailSvc != nil { personalDataHandler.SetDetailService(detailSvc) } + if ebookProgressStore != nil { + personalDataHandler.SetEbookReaderProgressStore(ebookProgressStore) + } personalDataHandler.SetEpisodeRepo(episodeRepo) personalDataHandler.SetSeasonRepo(seasonRepo) personalDataHandler.EventsHub = deps.EventsHub @@ -939,6 +968,9 @@ func NewRouter(deps Dependencies) chi.Router { } sectionHandler.EpisodeRepo = episodeRepo sectionHandler.DetailSvc = detailSvc + if ebookProgressStore != nil { + sectionHandler.EbookProgress = ebookProgressStore + } if userRepo != nil { sectionHandler.UserRepo = userRepo } @@ -1121,6 +1153,9 @@ func NewRouter(deps Dependencies) chi.Router { } recsHandler.CalendarRepo = calendarRepo recsHandler.EpisodeRepo = episodeRepo + if ebookProgressStore != nil { + recsHandler.EbookProgress = ebookProgressStore + } if deps.PersonRepo != nil { recsHandler.CastFetcher = deps.PersonRepo } @@ -1708,6 +1743,22 @@ func NewRouter(deps Dependencies) chi.Router { }) } + if ebookReaderHandler != nil { + r.Route("/ebooks", func(r chi.Router) { + r.Use(apimw.RequireProfile) + r.Get("/{content_id}/files/{file_id}/read", ebookReaderHandler.HandleReadFile) + r.Head("/{content_id}/files/{file_id}/read", ebookReaderHandler.HandleReadFile) + r.Get("/{content_id}/progress", ebookReaderHandler.HandleGetProgress) + r.Put("/{content_id}/progress", ebookReaderHandler.HandleSaveProgress) + r.Get("/{content_id}/reader-config", ebookReaderHandler.HandleGetConfig) + r.Put("/{content_id}/reader-config", ebookReaderHandler.HandleSaveConfig) + r.Get("/{content_id}/annotations", ebookReaderHandler.HandleListAnnotations) + r.Post("/{content_id}/annotations", ebookReaderHandler.HandleCreateAnnotation) + r.Patch("/{content_id}/annotations/{annotation_id}", ebookReaderHandler.HandleUpdateAnnotation) + r.Delete("/{content_id}/annotations/{annotation_id}", ebookReaderHandler.HandleDeleteAnnotation) + }) + } + // Subtitle search + AI translation routes. if subtitleSearchHandler != nil { if deps.FileRepo != nil && itemRepo != nil { diff --git a/internal/catalog/browse.go b/internal/catalog/browse.go index f87bd908..41cc3b41 100644 --- a/internal/catalog/browse.go +++ b/internal/catalog/browse.go @@ -767,9 +767,9 @@ func listDistinctPeopleByKindWithSource( } // listDistinctAudiobookSeriesWithSource returns distinct series_name values -// from audiobook_series joined onto the scoped result set. Names are trimmed +// from the active book series table joined onto the scoped result set. Names are trimmed // and case-folded for sort so the picker doesn't show duplicates that differ -// only by whitespace or casing — the literal trimmed series_name is still +// only by whitespace or casing; the literal trimmed series_name is still // returned so existing rules continue to match. // // The DISTINCT happens in an inline subquery (rather than directly on the @@ -791,14 +791,14 @@ func listDistinctAudiobookSeriesWithSource( SELECT name FROM ( SELECT DISTINCT BTRIM(s.series_name) AS name FROM %s - JOIN audiobook_series s ON s.content_id = mi.content_id + JOIN %s s ON s.content_id = mi.content_id %s AND s.series_name IS NOT NULL AND BTRIM(s.series_name) <> '' ) names ORDER BY LOWER(name) ASC LIMIT %d - `, fromClause, browseFilterPrefix(whereClause), catalogFacetMaxValues) + `, fromClause, bookSeriesTableForMediaScope(mediaScope), browseFilterPrefix(whereClause), catalogFacetMaxValues) return queryDistinctStrings(ctx, pool, query, args) } @@ -954,7 +954,7 @@ func searchDistinctAudiobookSeriesWithSource( SELECT name FROM ( SELECT DISTINCT BTRIM(s.series_name) AS name FROM %s - JOIN audiobook_series s ON s.content_id = mi.content_id + JOIN %s s ON s.content_id = mi.content_id %s AND s.series_name IS NOT NULL AND BTRIM(s.series_name) <> '' @@ -962,10 +962,17 @@ func searchDistinctAudiobookSeriesWithSource( ) names ORDER BY LOWER(name) ASC LIMIT %d - `, fromClause, browseFilterPrefix(whereClause), prefixIdx, limit+1) + `, fromClause, bookSeriesTableForMediaScope(mediaScope), browseFilterPrefix(whereClause), prefixIdx, limit+1) return queryFacetSearchResults(ctx, pool, query, args, limit) } +func bookSeriesTableForMediaScope(mediaScope string) string { + if mediaScope == "ebook" { + return "ebook_series" + } + return "audiobook_series" +} + // queryFacetSearchResults executes a facet search query that was built // with LIMIT N+1 and returns the first N rows plus a hasMore flag // indicating whether an additional row was present (i.e. the result set diff --git a/internal/catalog/catalog_parser_test.go b/internal/catalog/catalog_parser_test.go new file mode 100644 index 00000000..8f865ea0 --- /dev/null +++ b/internal/catalog/catalog_parser_test.go @@ -0,0 +1,9 @@ +package catalog + +import "testing" + +func TestParseCatalogMediaScope_AllowsEbook(t *testing.T) { + if got := parseCatalogMediaScope(" ebook "); got != "ebook" { + t.Fatalf("expected ebook media scope, got %q", got) + } +} diff --git a/internal/catalog/catalog_resolver.go b/internal/catalog/catalog_resolver.go index 75bdbd1d..fa26f325 100644 --- a/internal/catalog/catalog_resolver.go +++ b/internal/catalog/catalog_resolver.go @@ -39,10 +39,9 @@ type CatalogFiltersResult struct { Resolutions []string AudioLanguages []string SubtitleLanguages []string - // Authors, Narrators and Series are audiobook-native facets. Populated - // whenever the scope contains audiobook items; empty for video-only - // scopes. They share the item_people / audiobook_series tables so - // callers don't need to switch on media type. + // Authors, Narrators and Series are book-native facets. Authors and + // Narrators use item_people; Series uses the active book series table. + // Video-only scopes return these empty. Authors []string Narrators []string Series []string @@ -64,8 +63,8 @@ type facetFetcher interface { // PeopleByKind returns distinct people names for the given PersonKind // across the scoped result set. Used for Authors / Narrators facets. PeopleByKind(ctx context.Context, kind models.PersonKind, filters BrowseFilters, baseRelation string, mediaScope string) ([]string, error) - // AudiobookSeries returns distinct series_name values from - // audiobook_series joined onto the scoped result set. + // AudiobookSeries returns distinct series_name values from the active + // book series table joined onto the scoped result set. AudiobookSeries(ctx context.Context, filters BrowseFilters, baseRelation string, mediaScope string) ([]string, error) // SearchDistinctArrayColumn prefix-searches an array-column facet // (genres, studios, networks, countries). Returns up to limit @@ -710,7 +709,7 @@ func (r *CatalogResolver) ListFiltersWithOptions(ctx context.Context, req Catalo return r.listFiltersForSource(ctx, filters, options, episodeCatalogBaseRelation, req.Query.MediaScope) } - return r.listFiltersForSource(ctx, filters, options, "media_items mi", "") + return r.listFiltersForSource(ctx, filters, options, "media_items mi", req.Query.MediaScope) } // CatalogFacetSearchResult is the typed return for SearchFacet. Matches @@ -798,10 +797,9 @@ func (r *CatalogResolver) SearchFacet(ctx context.Context, req CatalogRequest, a } baseRelation := "media_items mi" - mediaScope := "" + mediaScope := req.Query.MediaScope if isEpisodeCatalogScope(req.Query.MediaScope) { baseRelation = episodeCatalogBaseRelation - mediaScope = req.Query.MediaScope } facets := r.facets @@ -837,6 +835,9 @@ func dispatchFacetSearch(ctx context.Context, facets facetFetcher, facet string, case "author": return facets.SearchPeopleByKind(ctx, models.PersonKindAuthor, filters, baseRelation, mediaScope, prefix, limit) case "narrator": + if mediaScope == "ebook" { + return nil, false, fmt.Errorf("%w: narrator facet is not available for ebook scope", ErrInvalidCatalogRequest) + } return facets.SearchPeopleByKind(ctx, models.PersonKindNarrator, filters, baseRelation, mediaScope, prefix, limit) case "series": return facets.SearchAudiobookSeries(ctx, filters, baseRelation, mediaScope, prefix, limit) @@ -958,14 +959,16 @@ func (r *CatalogResolver) listFiltersForSource( authors = out return nil })) - eg.Go(withLimit(func() error { - out, err := facets.PeopleByKind(gctx, models.PersonKindNarrator, filters, baseRelation, mediaScope) - if err != nil { - return fmt.Errorf("listing catalog narrators: %w", err) - } - narrators = out - return nil - })) + if mediaScope != "ebook" { + eg.Go(withLimit(func() error { + out, err := facets.PeopleByKind(gctx, models.PersonKindNarrator, filters, baseRelation, mediaScope) + if err != nil { + return fmt.Errorf("listing catalog narrators: %w", err) + } + narrators = out + return nil + })) + } eg.Go(withLimit(func() error { out, err := facets.AudiobookSeries(gctx, filters, baseRelation, mediaScope) if err != nil { @@ -1087,7 +1090,7 @@ func validateCatalogExactCollectionRequest(req CatalogRequest) error { func validateCatalogOverlayQuery(searchQuery string, def QueryDefinition, ruleFields, sortFields map[string]bool, allowRelevance bool) error { if !IsValidMediaScope(def.MediaScope) { - return fmt.Errorf("%w: media_scope must be 'movie', 'series', 'episode', 'audiobook', or 'video'", ErrInvalidCatalogRequest) + return fmt.Errorf("%w: media_scope must be 'movie', 'series', 'episode', 'audiobook', 'ebook', or 'video'", ErrInvalidCatalogRequest) } if def.Match != "" && def.Match != "all" && def.Match != "any" { return fmt.Errorf("%w: match must be 'all' or 'any'", ErrInvalidCatalogRequest) diff --git a/internal/catalog/catalog_resolver_test.go b/internal/catalog/catalog_resolver_test.go index 6cd46054..ef4f89a2 100644 --- a/internal/catalog/catalog_resolver_test.go +++ b/internal/catalog/catalog_resolver_test.go @@ -39,6 +39,21 @@ func TestValidateCatalogQueryRequest_AllowsEpisodeMediaScope(t *testing.T) { } } +func TestValidateCatalogQueryRequest_AllowsEbookMediaScope(t *testing.T) { + req := CatalogRequest{ + Source: CatalogSourceQuery, + Query: QueryDefinition{ + MediaScope: "ebook", + Match: "all", + Sort: QuerySort{Field: "title", Order: "asc"}, + }, + } + + if err := validateCatalogQueryRequest(req, true); err != nil { + t.Fatalf("expected ebook media scope to be accepted, got %v", err) + } +} + func TestValidateCatalogQueryRequest_AllowsAddedAtFilter(t *testing.T) { req := CatalogRequest{ Source: CatalogSourceQuery, @@ -244,6 +259,67 @@ func (s *stubFacetFetcher) SearchAudiobookSeries(ctx context.Context, filters Br return nil, false, nil } +type recordingFacetFetcher struct { + mu sync.Mutex + peopleKinds []models.PersonKind + searchPeopleKind models.PersonKind + searchMediaScope string +} + +func (s *recordingFacetFetcher) DistinctArrayColumn(ctx context.Context, column string, filters BrowseFilters, baseRelation string, mediaScope string) ([]string, error) { + return nil, nil +} + +func (s *recordingFacetFetcher) DistinctScalarColumn(ctx context.Context, column string, filters BrowseFilters, baseRelation string, mediaScope string) ([]string, error) { + return nil, nil +} + +func (s *recordingFacetFetcher) Resolutions(ctx context.Context, filters BrowseFilters, baseRelation string, mediaScope string) ([]string, error) { + return nil, nil +} + +func (s *recordingFacetFetcher) JSONBLanguages(ctx context.Context, column string, filters BrowseFilters, baseRelation string, mediaScope string) ([]string, error) { + return nil, nil +} + +func (s *recordingFacetFetcher) SubtitleLanguages(ctx context.Context, filters BrowseFilters, baseRelation string, mediaScope string) ([]string, error) { + return nil, nil +} + +func (s *recordingFacetFetcher) PeopleByKind(ctx context.Context, kind models.PersonKind, filters BrowseFilters, baseRelation string, mediaScope string) ([]string, error) { + s.mu.Lock() + s.peopleKinds = append(s.peopleKinds, kind) + s.mu.Unlock() + if kind == models.PersonKindAuthor { + return []string{"Author"}, nil + } + return []string{"Narrator"}, nil +} + +func (s *recordingFacetFetcher) AudiobookSeries(ctx context.Context, filters BrowseFilters, baseRelation string, mediaScope string) ([]string, error) { + return nil, nil +} + +func (s *recordingFacetFetcher) SearchDistinctArrayColumn(ctx context.Context, column string, filters BrowseFilters, baseRelation string, mediaScope string, prefix string, limit int) ([]string, bool, error) { + return nil, false, nil +} + +func (s *recordingFacetFetcher) SearchDistinctScalarColumn(ctx context.Context, column string, filters BrowseFilters, baseRelation string, mediaScope string, prefix string, limit int) ([]string, bool, error) { + return nil, false, nil +} + +func (s *recordingFacetFetcher) SearchPeopleByKind(ctx context.Context, kind models.PersonKind, filters BrowseFilters, baseRelation string, mediaScope string, prefix string, limit int) ([]string, bool, error) { + s.mu.Lock() + s.searchPeopleKind = kind + s.searchMediaScope = mediaScope + s.mu.Unlock() + return []string{"Author"}, false, nil +} + +func (s *recordingFacetFetcher) SearchAudiobookSeries(ctx context.Context, filters BrowseFilters, baseRelation string, mediaScope string, prefix string, limit int) ([]string, bool, error) { + return nil, false, nil +} + // countingExecutor is a previewExecutor stub that records the number of // PreviewPage calls and the AccessFilter passed in (so tests can confirm // NamePrefix made it through). It returns an empty result set so the resolver @@ -327,3 +403,92 @@ func TestListFiltersWithOptions_RunsFacetQueriesConcurrently(t *testing.T) { barrier.assertConcurrent(t) } + +func TestListFiltersWithOptions_EbookScopeSkipsNarratorFacet(t *testing.T) { + facets := &recordingFacetFetcher{} + resolver := &CatalogResolver{ + browseRepo: &BrowseRepository{}, + facets: facets, + } + + result, err := resolver.ListFiltersWithOptions( + context.Background(), + CatalogRequest{ + Source: CatalogSourceQuery, + Query: QueryDefinition{MediaScope: "ebook"}, + }, + AccessFilter{}, + CatalogFilterOptions{}, + ) + if err != nil { + t.Fatalf("ListFiltersWithOptions returned error: %v", err) + } + + facets.mu.Lock() + defer facets.mu.Unlock() + for _, kind := range facets.peopleKinds { + if kind == models.PersonKindNarrator { + t.Fatal("ebook filters should not request narrator facets") + } + } + if len(result.Narrators) != 0 { + t.Fatalf("ebook filters returned narrators = %v, want empty", result.Narrators) + } +} + +func TestSearchFacet_EbookScopeForwardsMediaScope(t *testing.T) { + facets := &recordingFacetFetcher{} + resolver := &CatalogResolver{ + browseRepo: &BrowseRepository{}, + facets: facets, + } + + result, err := resolver.SearchFacet( + context.Background(), + CatalogRequest{ + Source: CatalogSourceQuery, + Query: QueryDefinition{MediaScope: "ebook"}, + }, + AccessFilter{}, + "author", + "Au", + 20, + ) + if err != nil { + t.Fatalf("SearchFacet returned error: %v", err) + } + if len(result.Matches) != 1 || result.Matches[0] != "Author" { + t.Fatalf("matches = %v, want Author", result.Matches) + } + + facets.mu.Lock() + defer facets.mu.Unlock() + if facets.searchPeopleKind != models.PersonKindAuthor { + t.Fatalf("searchPeopleKind = %v, want author", facets.searchPeopleKind) + } + if facets.searchMediaScope != "ebook" { + t.Fatalf("search mediaScope = %q, want ebook", facets.searchMediaScope) + } +} + +func TestSearchFacet_EbookScopeRejectsNarratorFacet(t *testing.T) { + resolver := &CatalogResolver{ + browseRepo: &BrowseRepository{}, + facets: &recordingFacetFetcher{}, + } + + _, err := resolver.SearchFacet( + context.Background(), + CatalogRequest{ + Source: CatalogSourceQuery, + Query: QueryDefinition{MediaScope: "ebook"}, + }, + AccessFilter{}, + "narrator", + "Na", + 20, + ) + if err == nil { + t.Fatal("expected narrator facet to be rejected for ebook scope") + } +} diff --git a/internal/catalog/detail.go b/internal/catalog/detail.go index 4c56ba73..e46b016a 100644 --- a/internal/catalog/detail.go +++ b/internal/catalog/detail.go @@ -157,6 +157,9 @@ type ItemDetail struct { // Audiobook-specific detail. Present only when Type == "audiobook". Audiobook *AudiobookDetailExtension `json:"audiobook,omitempty"` + + // Ebook-specific detail. Present only when Type == "ebook". + Ebook *EbookDetailExtension `json:"ebook,omitempty"` } type AudiobookDetailExtension struct { @@ -201,6 +204,13 @@ type AudiobookNarration struct { Narrators []string `json:"narrators"` } +type EbookDetailExtension struct { + Authors []AudiobookPerson `json:"authors"` + Publisher string `json:"publisher,omitempty"` + Series *AudiobookSeriesGroup `json:"series,omitempty"` + Related AudiobookRelatedContent `json:"related"` +} + // ItemUserState is per-profile viewer state included in item detail responses. type ItemUserState struct { Played bool `json:"played"` @@ -895,6 +905,9 @@ func (s *DetailService) buildMediaItemDetail(ctx context.Context, item *models.M if item.Type == "audiobook" { detail.Audiobook = s.buildAudiobookExtension(ctx, item, detail.Versions, crewCredits, filter) } + if item.Type == "ebook" { + detail.Ebook = s.buildEbookExtension(ctx, item, crewCredits, filter) + } // Series folder paths from confirmed claims when available, otherwise from // the file links that currently belong to the item. This keeps provisional @@ -1036,6 +1049,26 @@ func (s *DetailService) buildAudiobookExtension( } } +func (s *DetailService) buildEbookExtension( + ctx context.Context, + item *models.MediaItem, + crew []CrewCredit, + filter AccessFilter, +) *EbookDetailExtension { + if item == nil { + return nil + } + return &EbookDetailExtension{ + Authors: audiobookPeopleFromCrew(crew, models.PersonKindAuthor.String()), + Publisher: firstNonEmptyString(item.Studios), + Series: s.fetchEbookSeries(ctx, item.ContentID, filter), + Related: AudiobookRelatedContent{ + AlsoByAuthor: s.fetchEbookAlsoByAuthor(ctx, item.ContentID, filter), + Similar: s.fetchEbookSimilarByGenres(ctx, item.ContentID, filter), + }, + } +} + func audiobookPeopleFromCrew(crew []CrewCredit, job string) []AudiobookPerson { out := make([]AudiobookPerson, 0) for _, credit := range crew { @@ -1107,15 +1140,23 @@ func appendAudiobookItemAccessConditions( } func (s *DetailService) fetchAudiobookAlsoByAuthor(ctx context.Context, contentID string, filter AccessFilter) []AudiobookRelatedItem { + return s.fetchBookAlsoByAuthor(ctx, contentID, "audiobook", filter) +} + +func (s *DetailService) fetchEbookAlsoByAuthor(ctx context.Context, contentID string, filter AccessFilter) []AudiobookRelatedItem { + return s.fetchBookAlsoByAuthor(ctx, contentID, "ebook", filter) +} + +func (s *DetailService) fetchBookAlsoByAuthor(ctx context.Context, contentID string, mediaType string, filter AccessFilter) []AudiobookRelatedItem { if s == nil || s.itemRepo == nil || s.itemRepo.pool == nil { return []AudiobookRelatedItem{} } - args := []any{contentID, models.PersonKindAuthor} - argIdx := 3 + args := []any{contentID, models.PersonKindAuthor, mediaType} + argIdx := 4 conditions := []string{ "ip1.content_id = $1", "ip1.kind = $2", - "m2.type = 'audiobook'", + "m2.type = $3", } if !appendAudiobookItemAccessConditions("m2", filter, &conditions, &args, &argIdx) { return []AudiobookRelatedItem{} @@ -1157,13 +1198,21 @@ func (s *DetailService) fetchAudiobookAlsoByAuthor(ctx context.Context, contentI } func (s *DetailService) fetchAudiobookSimilarByGenres(ctx context.Context, contentID string, filter AccessFilter) []AudiobookRelatedItem { + return s.fetchBookSimilarByGenres(ctx, contentID, "audiobook", filter) +} + +func (s *DetailService) fetchEbookSimilarByGenres(ctx context.Context, contentID string, filter AccessFilter) []AudiobookRelatedItem { + return s.fetchBookSimilarByGenres(ctx, contentID, "ebook", filter) +} + +func (s *DetailService) fetchBookSimilarByGenres(ctx context.Context, contentID string, mediaType string, filter AccessFilter) []AudiobookRelatedItem { if s == nil || s.itemRepo == nil || s.itemRepo.pool == nil { return []AudiobookRelatedItem{} } - args := []any{contentID, models.PersonKindAuthor} - argIdx := 3 + args := []any{contentID, models.PersonKindAuthor, mediaType} + argIdx := 4 conditions := []string{ - "m.type = 'audiobook'", + "m.type = $3", "m.content_id <> $1", "m.genres && (SELECT array_agg(g) FROM this_genres)", `NOT EXISTS ( @@ -1216,14 +1265,27 @@ func (s *DetailService) fetchAudiobookSimilarByGenres(ctx context.Context, conte } func (s *DetailService) fetchAudiobookSeries(ctx context.Context, contentID string, filter AccessFilter) *AudiobookSeriesGroup { + return s.fetchBookSeries(ctx, contentID, "audiobook", "audiobook_series", filter) +} + +func (s *DetailService) fetchEbookSeries(ctx context.Context, contentID string, filter AccessFilter) *AudiobookSeriesGroup { + return s.fetchBookSeries(ctx, contentID, "ebook", "ebook_series", filter) +} + +func (s *DetailService) fetchBookSeries(ctx context.Context, contentID string, mediaType string, tableName string, filter AccessFilter) *AudiobookSeriesGroup { if s == nil || s.itemRepo == nil || s.itemRepo.pool == nil { return nil } - args := []any{contentID} - argIdx := 2 + switch tableName { + case "audiobook_series", "ebook_series": + default: + return nil + } + args := []any{contentID, mediaType} + argIdx := 3 conditions := []string{ "root.content_id = $1", - "m.type = 'audiobook'", + "m.type = $2", } if !appendAudiobookItemAccessConditions("m", filter, &conditions, &args, &argIdx) { return nil @@ -1236,13 +1298,13 @@ func (s *DetailService) fetchAudiobookSeries(ctx context.Context, contentID stri COALESCE(m.poster_path, ''), s.series_name, s.series_index - FROM audiobook_series root - JOIN audiobook_series s ON LOWER(s.series_name) = LOWER(root.series_name) + FROM %s root + JOIN %s s ON LOWER(s.series_name) = LOWER(root.series_name) JOIN media_items m ON m.content_id = s.content_id WHERE %s ORDER BY s.series_index NULLS LAST, LOWER(m.sort_title) LIMIT 30 - `, strings.Join(conditions, " AND ")) + `, tableName, tableName, strings.Join(conditions, " AND ")) rows, err := s.itemRepo.pool.Query(ctx, query, args...) if err != nil { return nil diff --git a/internal/catalog/detail_audiobook_test.go b/internal/catalog/detail_audiobook_test.go index 35763e1a..6b1b06e8 100644 --- a/internal/catalog/detail_audiobook_test.go +++ b/internal/catalog/detail_audiobook_test.go @@ -76,3 +76,27 @@ func (r *recordingCatalogImageResolver) ResolveImageURLs(_ context.Context, path } return out } + +func TestBuildEbookExtensionUsesAuthorsAndPublisherWithoutNarrators(t *testing.T) { + crew := []CrewCredit{ + {Name: "Becky Chambers", Job: models.PersonKindAuthor.String(), PersonID: "10"}, + {Name: "Narrator Should Not Apply", Job: models.PersonKindNarrator.String(), PersonID: "20"}, + } + item := &models.MediaItem{ + ContentID: "ebook-1", + Type: "ebook", + Studios: []string{"Tor"}, + } + + extension := (&DetailService{}).buildEbookExtension(nil, item, crew, AccessFilter{}) + + if extension == nil { + t.Fatal("buildEbookExtension returned nil") + } + if len(extension.Authors) != 1 || extension.Authors[0].Name != "Becky Chambers" { + t.Fatalf("Authors = %+v, want Becky Chambers", extension.Authors) + } + if extension.Publisher != "Tor" { + t.Fatalf("Publisher = %q, want Tor", extension.Publisher) + } +} diff --git a/internal/catalog/item_repo.go b/internal/catalog/item_repo.go index 716a81c5..015d3be9 100644 --- a/internal/catalog/item_repo.go +++ b/internal/catalog/item_repo.go @@ -35,6 +35,49 @@ func NewItemRepository(pool *pgxpool.Pool) *ItemRepository { return &ItemRepository{pool: pool} } +// GetPoster returns the current poster path and thumbhash for a media item. +// Missing or NULL values are returned as empty strings. +func (r *ItemRepository) GetPoster(ctx context.Context, contentID string) (posterPath string, posterThumbhash string, err error) { + if r == nil || r.pool == nil { + return "", "", ErrItemNotFound + } + if err := r.pool.QueryRow(ctx, ` + SELECT COALESCE(poster_path, ''), COALESCE(poster_thumbhash, '') + FROM media_items + WHERE content_id = $1 + `, contentID).Scan(&posterPath, &posterThumbhash); err != nil { + if errors.Is(err, pgx.ErrNoRows) { + return "", "", ErrItemNotFound + } + return "", "", fmt.Errorf("getting media item poster: %w", err) + } + return posterPath, posterThumbhash, nil +} + +// SetLocalPoster records a locally extracted poster without ever overwriting +// provider or manually applied artwork: the row is updated only when the item +// has no poster yet or its current poster lives under localPrefix. The +// condition is evaluated atomically in SQL so concurrent metadata writers +// (e.g. the ebook enrichment sweep) cannot be clobbered between a read and a +// write. Returns true when a row was updated. +func (r *ItemRepository) SetLocalPoster(ctx context.Context, contentID, posterPath, thumbhash, localPrefix string) (bool, error) { + if r == nil || r.pool == nil { + return false, ErrItemNotFound + } + tag, err := r.pool.Exec(ctx, ` + UPDATE media_items + SET poster_path = $2, + poster_thumbhash = NULLIF($3, ''), + updated_at = NOW() + WHERE content_id = $1 + AND (poster_path IS NULL OR poster_path = '' OR poster_path LIKE $4 || '%') + `, contentID, posterPath, thumbhash, localPrefix) + if err != nil { + return false, fmt.Errorf("setting local poster: %w", err) + } + return tag.RowsAffected() > 0, nil +} + // GetPosterPath returns the current poster path for a media item. Missing or // NULL poster values are returned as an empty string. func (r *ItemRepository) GetPosterPath(ctx context.Context, contentID string) (string, error) { diff --git a/internal/catalog/query_builder.go b/internal/catalog/query_builder.go index 6767db87..126b30e3 100644 --- a/internal/catalog/query_builder.go +++ b/internal/catalog/query_builder.go @@ -11,6 +11,12 @@ import ( "github.com/Silo-Server/silo-server/internal/models" ) +// EbookFinishedProgressThresholdSQL is models.EbookFinishedProgressThreshold +// rendered as a SQL numeric literal. Queries that classify ebook reader +// progress as finished must interpolate this value instead of hardcoding the +// threshold so the definition of "finished" cannot drift between subsystems. +var EbookFinishedProgressThresholdSQL = strconv.FormatFloat(models.EbookFinishedProgressThreshold, 'f', -1, 64) + type QueryBuilder struct { alias string argIdx int @@ -159,9 +165,10 @@ func (qb *QueryBuilder) UserHistoryCTEArgs() []any { // UserHistoryCTESQL returns the user_last_watched CTE definition. The two // placeholder positions (user_id, profile_id) are bound to the args returned // by UserHistoryCTEArgs and should be the first two entries in the final -// statement's arg list. The CTE aggregates user_watch_history watch_at and -// user_watch_progress.completed=TRUE updated_at into one row per -// media_item_id, filtering out items the user has hidden via +// statement's arg list. The CTE aggregates user_watch_history watched_at, +// user_watch_progress.completed=TRUE updated_at, and completed ebook reader +// progress updated_at into one row per media_item_id, filtering out videos the +// user has hidden via // user_history_hidden_items. The single GROUP BY replaces the two // correlated MAX subqueries the previous lastWatchedExpr emitted (audit // 2026-05-01 §3.1 Pattern B). @@ -187,6 +194,10 @@ func UserHistoryCTESQL(argIdx int) string { SELECT uwp.media_item_id, NULL::timestamptz, uwp.updated_at FROM user_watch_progress uwp WHERE uwp.user_id = $%d AND uwp.profile_id = $%d AND uwp.completed = TRUE + UNION ALL + SELECT erp.content_id AS media_item_id, NULL::timestamptz, erp.updated_at + FROM ebook_reader_progress erp + WHERE erp.user_id = $%d AND erp.profile_id = $%d AND erp.progress >= %s ) src WHERE NOT EXISTS ( SELECT 1 FROM user_history_hidden_items hhi @@ -195,7 +206,7 @@ func UserHistoryCTESQL(argIdx int) string { AND COALESCE(src.uwh_at, src.uwp_at) <= hhi.hidden_before ) GROUP BY src.media_item_id -)`, argIdx, argIdx+1, argIdx, argIdx+1, argIdx, argIdx+1) +)`, argIdx, argIdx+1, argIdx, argIdx+1, argIdx, argIdx+1, EbookFinishedProgressThresholdSQL, argIdx, argIdx+1) } func (qb *QueryBuilder) BuildSortClause(sortConfig QuerySort) (string, []any, error) { @@ -298,7 +309,8 @@ func (qb *QueryBuilder) BuildSortPlan(sortConfig QuerySort) (QuerySortPlan, erro return plan, nil case "series": plan.Joins = []string{fmt.Sprintf( - "LEFT JOIN audiobook_series sort_series ON sort_series.content_id = %s.content_id", + "LEFT JOIN %s sort_series ON sort_series.content_id = %s.content_id", + qb.bookSeriesTable(), qb.alias, )} // Sort by series name primarily, then by series_index so books @@ -385,7 +397,7 @@ func (qb *QueryBuilder) buildRule(rule QueryRule) (string, error) { case "narrator": return qb.buildPersonClause(models.PersonKindNarrator, rule) case "series": - return qb.buildAudiobookSeriesClause(rule) + return qb.buildBookSeriesClause(rule) case "watched": return qb.buildWatchedClause(rule) case "favorited": @@ -502,14 +514,21 @@ func (qb *QueryBuilder) personKindSortJoin(kind models.PersonKind, alias string) ) } -func (qb *QueryBuilder) buildAudiobookSeriesClause(rule QueryRule) (string, error) { +func (qb *QueryBuilder) bookSeriesTable() string { + if qb.mediaScope == "ebook" { + return "ebook_series" + } + return "audiobook_series" +} + +func (qb *QueryBuilder) buildBookSeriesClause(rule QueryRule) (string, error) { qb.args = append(qb.args, rule.Value) clause := fmt.Sprintf(`EXISTS ( SELECT 1 - FROM audiobook_series s + FROM %s s WHERE s.content_id = %s.content_id AND LOWER(BTRIM(s.series_name)) = LOWER(BTRIM($%d)) - )`, qb.alias, qb.argIdx) +)`, qb.bookSeriesTable(), qb.alias, qb.argIdx) qb.argIdx++ if rule.Op == "is_not" { return "NOT (" + clause + ")", nil @@ -947,6 +966,9 @@ func (qb *QueryBuilder) buildInProgressClause(rule QueryRule) (string, error) { if !ok { return "", fmt.Errorf("in_progress requires a boolean value") } + if qb.mediaScope == "ebook" { + return qb.buildEbookInProgressClause(value), nil + } qb.args = append(qb.args, qb.userID, qb.profileID) clause := fmt.Sprintf(`EXISTS ( SELECT 1 @@ -971,6 +993,32 @@ func (qb *QueryBuilder) buildInProgressClause(rule QueryRule) (string, error) { return clause, nil } +func (qb *QueryBuilder) buildEbookInProgressClause(value bool) string { + qb.args = append(qb.args, qb.userID, qb.profileID) + clause := fmt.Sprintf(`EXISTS ( + SELECT 1 + FROM ebook_reader_progress erp + WHERE erp.user_id = $%d + AND erp.profile_id = $%d + AND erp.content_id = %s.content_id + AND erp.progress > 0 + AND erp.progress < %s + AND NOT EXISTS ( + SELECT 1 + FROM user_history_hidden_items hhi + WHERE hhi.user_id = $%d + AND hhi.profile_id = $%d + AND hhi.media_item_id = erp.content_id + AND erp.updated_at <= hhi.hidden_before + ) + )`, qb.argIdx, qb.argIdx+1, qb.alias, EbookFinishedProgressThresholdSQL, qb.argIdx, qb.argIdx+1) + qb.argIdx += 2 + if !value { + return "NOT (" + clause + ")" + } + return clause +} + func (qb *QueryBuilder) buildLastWatchedClause(rule QueryRule) (string, error) { if err := qb.ensureUserScope(rule.Field); err != nil { return "", err @@ -1069,6 +1117,9 @@ func (qb *QueryBuilder) buildReleaseDateClause(rule QueryRule) (string, error) { } func (qb *QueryBuilder) userStateCompletionClause() string { + if qb.mediaScope == "ebook" { + return qb.ebookUserStateCompletionClause() + } qb.args = append(qb.args, qb.userID, qb.profileID) clause := fmt.Sprintf(`( EXISTS ( @@ -1119,6 +1170,28 @@ func (qb *QueryBuilder) userStateCompletionClause() string { return clause } +func (qb *QueryBuilder) ebookUserStateCompletionClause() string { + qb.args = append(qb.args, qb.userID, qb.profileID) + clause := fmt.Sprintf(`EXISTS ( + SELECT 1 + FROM ebook_reader_progress erp + WHERE erp.user_id = $%d + AND erp.profile_id = $%d + AND erp.content_id = %s.content_id + AND erp.progress >= %s + AND NOT EXISTS ( + SELECT 1 + FROM user_history_hidden_items hhi + WHERE hhi.user_id = $%d + AND hhi.profile_id = $%d + AND hhi.media_item_id = erp.content_id + AND erp.updated_at <= hhi.hidden_before + ) + )`, qb.argIdx, qb.argIdx+1, qb.alias, EbookFinishedProgressThresholdSQL, qb.argIdx, qb.argIdx+1) + qb.argIdx += 2 + return clause +} + // lastWatchedExpr returns the SQL expression the executor binds to the // per-user history aggregate. It signals the executor (via // requireUserHistoryCTE) that the user_last_watched CTE and a LEFT JOIN @@ -1342,6 +1415,9 @@ func (qb *QueryBuilder) consumeIntArgs(values []int) ([]string, []any) { } func (qb *QueryBuilder) progressSortPlan() (string, []string, []any) { + if qb.mediaScope == "ebook" { + return qb.ebookProgressSortPlan() + } joinSQL := fmt.Sprintf( `LEFT JOIN ( SELECT uwp.media_item_id AS content_id, @@ -1369,7 +1445,39 @@ func (qb *QueryBuilder) progressSortPlan() (string, []string, []any) { return "sort_progress.progress_ratio", []string{joinSQL}, []any{qb.userID, qb.profileID} } +func (qb *QueryBuilder) ebookProgressSortPlan() (string, []string, []any) { + joinSQL := fmt.Sprintf( + `LEFT JOIN ( + SELECT erp.content_id, + CASE + WHEN erp.progress > 0 + AND erp.progress < %s + AND (hhi.media_item_id IS NULL OR erp.updated_at > hhi.hidden_before) + THEN erp.progress::double precision + ELSE NULL + END AS progress_ratio + FROM ebook_reader_progress erp + LEFT JOIN user_history_hidden_items hhi + ON hhi.user_id = $%d + AND hhi.profile_id = $%d + AND hhi.media_item_id = erp.content_id + WHERE erp.user_id = $%d + AND erp.profile_id = $%d + ) sort_progress ON sort_progress.content_id = %s.content_id`, + EbookFinishedProgressThresholdSQL, + qb.argIdx, + qb.argIdx+1, + qb.argIdx, + qb.argIdx+1, + qb.alias, + ) + return "sort_progress.progress_ratio", []string{joinSQL}, []any{qb.userID, qb.profileID} +} + func (qb *QueryBuilder) dateViewedSortPlan() (string, []string, []any) { + if qb.mediaScope == "ebook" { + return qb.ebookDateViewedSortPlan() + } historyJoin := fmt.Sprintf( `LEFT JOIN ( SELECT uwh.media_item_id AS content_id, MAX(uwh.watched_at) AS viewed_at @@ -1420,7 +1528,38 @@ func (qb *QueryBuilder) dateViewedSortPlan() (string, []string, []any) { return expr, []string{historyJoin, progressJoin}, []any{qb.userID, qb.profileID} } +func (qb *QueryBuilder) ebookDateViewedSortPlan() (string, []string, []any) { + joinSQL := fmt.Sprintf( + `LEFT JOIN ( + SELECT erp.content_id, + CASE + WHEN erp.progress >= %s + AND (hhi.media_item_id IS NULL OR erp.updated_at > hhi.hidden_before) + THEN erp.updated_at + ELSE NULL + END AS viewed_at + FROM ebook_reader_progress erp + LEFT JOIN user_history_hidden_items hhi + ON hhi.user_id = $%d + AND hhi.profile_id = $%d + AND hhi.media_item_id = erp.content_id + WHERE erp.user_id = $%d + AND erp.profile_id = $%d + ) sort_ebook_viewed ON sort_ebook_viewed.content_id = %s.content_id`, + EbookFinishedProgressThresholdSQL, + qb.argIdx, + qb.argIdx+1, + qb.argIdx, + qb.argIdx+1, + qb.alias, + ) + return "sort_ebook_viewed.viewed_at", []string{joinSQL}, []any{qb.userID, qb.profileID} +} + func (qb *QueryBuilder) playsSortPlan() (string, []string, []any) { + if qb.mediaScope == "ebook" { + return qb.ebookPlaysSortPlan() + } historyJoin := fmt.Sprintf( `LEFT JOIN ( SELECT uwh.media_item_id AS content_id, COUNT(*) AS play_count @@ -1471,6 +1610,34 @@ func (qb *QueryBuilder) playsSortPlan() (string, []string, []any) { return expr, []string{historyJoin, progressJoin}, []any{qb.userID, qb.profileID} } +func (qb *QueryBuilder) ebookPlaysSortPlan() (string, []string, []any) { + joinSQL := fmt.Sprintf( + `LEFT JOIN ( + SELECT erp.content_id, + CASE + WHEN erp.progress >= %s + AND (hhi.media_item_id IS NULL OR erp.updated_at > hhi.hidden_before) + THEN 1 + ELSE 0 + END AS completed_play_count + FROM ebook_reader_progress erp + LEFT JOIN user_history_hidden_items hhi + ON hhi.user_id = $%d + AND hhi.profile_id = $%d + AND hhi.media_item_id = erp.content_id + WHERE erp.user_id = $%d + AND erp.profile_id = $%d + ) sort_ebook_plays ON sort_ebook_plays.content_id = %s.content_id`, + EbookFinishedProgressThresholdSQL, + qb.argIdx, + qb.argIdx+1, + qb.argIdx, + qb.argIdx+1, + qb.alias, + ) + return "NULLIF(sort_ebook_plays.completed_play_count, 0)", []string{joinSQL}, []any{qb.userID, qb.profileID} +} + func (qb *QueryBuilder) lastAirDateSortPlan() (string, []string) { expr := fmt.Sprintf("%s.last_air_date_at", qb.alias) return expr, nil // denormalized column — no JOIN required diff --git a/internal/catalog/query_builder_test.go b/internal/catalog/query_builder_test.go index 8c692ffc..7b6b20de 100644 --- a/internal/catalog/query_builder_test.go +++ b/internal/catalog/query_builder_test.go @@ -390,6 +390,161 @@ func TestBuildSortClause_PersonalizedComputedSorts(t *testing.T) { } } +func TestBuildSortClause_EbookPersonalizedComputedSortsUseReaderProgress(t *testing.T) { + builder := NewQueryBuilder("mi").WithMediaScope("ebook").WithUserScope(42, "profile-1") + tests := []struct { + field string + expectJoin []string + expectExpr []string + }{ + { + field: "progress", + expectJoin: []string{ + "FROM ebook_reader_progress erp", + "erp.progress > 0", + "erp.progress < 0.9", + "LEFT JOIN user_history_hidden_items hhi", + "hhi.media_item_id IS NULL OR erp.updated_at > hhi.hidden_before", + }, + expectExpr: []string{"sort_progress.progress_ratio"}, + }, + { + field: "date_viewed", + expectJoin: []string{ + "FROM ebook_reader_progress erp", + "erp.progress >= 0.9", + "LEFT JOIN user_history_hidden_items hhi", + "hhi.media_item_id IS NULL OR erp.updated_at > hhi.hidden_before", + }, + expectExpr: []string{"sort_ebook_viewed.viewed_at"}, + }, + { + field: "plays", + expectJoin: []string{ + "FROM ebook_reader_progress erp", + "erp.progress >= 0.9", + "completed_play_count", + "LEFT JOIN user_history_hidden_items hhi", + "hhi.media_item_id IS NULL OR erp.updated_at > hhi.hidden_before", + }, + expectExpr: []string{"sort_ebook_plays.completed_play_count"}, + }, + } + + for _, tt := range tests { + plan, err := builder.BuildSortPlan(QuerySort{Field: tt.field, Order: "desc"}) + if err != nil { + t.Fatalf("%s: BuildSortPlan returned error: %v", tt.field, err) + } + if len(plan.Args) != 2 || plan.Args[0] != 42 || plan.Args[1] != "profile-1" { + t.Fatalf("%s: expected user scope args, got %v", tt.field, plan.Args) + } + joined := strings.Join(plan.Joins, "\n") + for _, fragment := range tt.expectJoin { + if !strings.Contains(joined, fragment) { + t.Fatalf("%s: expected joins to contain %q, got %q", tt.field, fragment, joined) + } + } + for _, fragment := range tt.expectExpr { + if !strings.Contains(plan.OrderBy, fragment) { + t.Fatalf("%s: expected order by to contain %q, got %q", tt.field, fragment, plan.OrderBy) + } + } + } +} + +func TestBuild_EbookUserStateRulesUseReaderProgress(t *testing.T) { + tests := []struct { + name string + rule QueryRule + expectSQL []string + unexpectSQL []string + }{ + { + name: "watched", + rule: QueryRule{Field: "watched", Op: "is", Value: true}, + expectSQL: []string{ + "FROM ebook_reader_progress erp", + "erp.progress >= 0.9", + "FROM user_history_hidden_items hhi", + "hhi.media_item_id = erp.content_id", + "erp.updated_at <= hhi.hidden_before", + }, + unexpectSQL: []string{"FROM user_watch_progress uwp", "FROM user_watch_history uwh"}, + }, + { + name: "unread", + rule: QueryRule{Field: "watched", Op: "is", Value: false}, + expectSQL: []string{ + "NOT (EXISTS", + "FROM ebook_reader_progress erp", + "erp.progress >= 0.9", + "FROM user_history_hidden_items hhi", + "hhi.media_item_id = erp.content_id", + "erp.updated_at <= hhi.hidden_before", + }, + unexpectSQL: []string{"FROM user_watch_progress uwp", "FROM user_watch_history uwh"}, + }, + { + name: "in progress", + rule: QueryRule{Field: "in_progress", Op: "is", Value: true}, + expectSQL: []string{ + "FROM ebook_reader_progress erp", + "erp.progress > 0", + "erp.progress < 0.9", + "FROM user_history_hidden_items hhi", + "hhi.media_item_id = erp.content_id", + "erp.updated_at <= hhi.hidden_before", + }, + unexpectSQL: []string{"FROM user_watch_progress uwp"}, + }, + { + name: "not in progress", + rule: QueryRule{Field: "in_progress", Op: "is", Value: false}, + expectSQL: []string{ + "NOT (EXISTS", + "FROM ebook_reader_progress erp", + "erp.progress > 0", + "erp.progress < 0.9", + "FROM user_history_hidden_items hhi", + "hhi.media_item_id = erp.content_id", + "erp.updated_at <= hhi.hidden_before", + }, + unexpectSQL: []string{"FROM user_watch_progress uwp"}, + }, + } + + for _, tt := range tests { + clause, args, err := NewQueryBuilder("mi"). + WithMediaScope("ebook"). + WithUserScope(42, "profile-1"). + Build(QueryDefinition{ + MediaScope: "ebook", + Match: "all", + Groups: []QueryGroup{{ + Match: "all", + Rules: []QueryRule{tt.rule}, + }}, + }) + if err != nil { + t.Fatalf("%s: Build returned error: %v", tt.name, err) + } + if len(args) != 2 || args[0] != 42 || args[1] != "profile-1" { + t.Fatalf("%s: expected user scope args, got %v", tt.name, args) + } + for _, fragment := range tt.expectSQL { + if !strings.Contains(clause, fragment) { + t.Fatalf("%s: expected clause to contain %q, got %q", tt.name, fragment, clause) + } + } + for _, fragment := range tt.unexpectSQL { + if strings.Contains(clause, fragment) { + t.Fatalf("%s: did not expect clause to contain %q, got %q", tt.name, fragment, clause) + } + } + } +} + func TestBuild_ResolutionNormalizes4KAndScopesMediaFiles(t *testing.T) { clause, args, err := NewQueryBuilder("mi"). WithLibraryScope([]int{3}). @@ -531,6 +686,26 @@ func TestBuild_SeriesClauseJoinsAudiobookSeries(t *testing.T) { } } +func TestBuild_SeriesClauseJoinsEbookSeriesForEbookScope(t *testing.T) { + clause, args, err := NewQueryBuilder("mi"). + WithMediaScope("ebook"). + Build(QueryDefinition{ + Match: "all", + Groups: []QueryGroup{ + {Match: "all", Rules: []QueryRule{{Field: "series", Op: "is", Value: "Wayfarers"}}}, + }, + }) + if err != nil { + t.Fatalf("Build returned error: %v", err) + } + if len(args) != 1 || args[0] != "Wayfarers" { + t.Fatalf("expected series arg, got %v", args) + } + if !strings.Contains(clause, "FROM ebook_series s") { + t.Fatalf("expected ebook_series join in series clause, got %q", clause) + } +} + func TestBuildSortPlan_AuthorJoinsLateralOnPersonKindAuthor(t *testing.T) { plan, err := NewQueryBuilder("mi").BuildSortPlan(QuerySort{Field: "author", Order: "asc"}) if err != nil { @@ -576,6 +751,18 @@ func TestBuildSortPlan_SeriesOrdersByNameThenIndex(t *testing.T) { } } +func TestBuildSortPlan_SeriesUsesEbookSeriesForEbookScope(t *testing.T) { + plan, err := NewQueryBuilder("mi"). + WithMediaScope("ebook"). + BuildSortPlan(QuerySort{Field: "series", Order: "asc"}) + if err != nil { + t.Fatalf("BuildSortPlan(series) returned error: %v", err) + } + if len(plan.Joins) != 1 || !strings.Contains(plan.Joins[0], "ebook_series sort_series") { + t.Fatalf("expected LEFT JOIN ebook_series, got %v", plan.Joins) + } +} + func TestBuild_SeriesClauseNegationWrapsInNOT(t *testing.T) { clause, _, err := NewQueryBuilder("mi").Build(QueryDefinition{ Match: "all", @@ -713,3 +900,18 @@ func TestQueryBuilder_LastWatched_RequiresCTE(t *testing.T) { t.Fatalf("after lastWatchedExpr, builder should require CTE") } } + +func TestUserHistoryCTESQLIncludesCompletedEbookReaderProgress(t *testing.T) { + sql := UserHistoryCTESQL(1) + expectedFragments := []string{ + "FROM ebook_reader_progress erp", + "erp.progress >= 0.9", + "erp.content_id", + "erp.updated_at", + } + for _, fragment := range expectedFragments { + if !strings.Contains(sql, fragment) { + t.Fatalf("expected CTE to contain %q, got:\n%s", fragment, sql) + } + } +} diff --git a/internal/catalog/query_definition.go b/internal/catalog/query_definition.go index ccd859c6..a3076c45 100644 --- a/internal/catalog/query_definition.go +++ b/internal/catalog/query_definition.go @@ -113,7 +113,7 @@ const MediaScopeVideo = "video" // is an accepted media_scope value. Empty means unscoped and is valid. func IsValidMediaScope(scope string) bool { switch scope { - case "", "movie", "series", "episode", "audiobook", MediaScopeVideo: + case "", "movie", "series", "episode", "audiobook", "ebook", MediaScopeVideo: return true default: return false @@ -230,7 +230,7 @@ func (q QueryDefinition) ValidateWithOptions(allowPersonalizedSorts, allowPerson } if !IsValidMediaScope(normalized.MediaScope) { - return fmt.Errorf("media_scope must be 'movie', 'series', 'episode', 'audiobook', or 'video'") + return fmt.Errorf("media_scope must be 'movie', 'series', 'episode', 'audiobook', 'ebook', or 'video'") } if normalized.Match != "all" && normalized.Match != "any" { @@ -249,6 +249,9 @@ func (q QueryDefinition) ValidateWithOptions(allowPersonalizedSorts, allowPerson if def.personalized && !allowPersonalizedFields { return fmt.Errorf("groups[%d].rules[%d].field %q requires profile scope", i, j, rule.Field) } + if normalized.MediaScope == "ebook" && rule.Field == "narrator" { + return fmt.Errorf("groups[%d].rules[%d].field %q is not supported for ebook media_scope", i, j, rule.Field) + } if !def.validOps[rule.Op] { return fmt.Errorf("groups[%d].rules[%d].op %q is not supported for field %q", i, j, rule.Op, rule.Field) } @@ -263,6 +266,9 @@ func (q QueryDefinition) ValidateWithOptions(allowPersonalizedSorts, allowPerson if def.personalized && !allowPersonalizedSorts { return fmt.Errorf("sort.field %q requires profile scope", normalized.Sort.Field) } + if normalized.MediaScope == "ebook" && normalized.Sort.Field == "narrator" { + return fmt.Errorf("sort.field %q is not supported for ebook media_scope", normalized.Sort.Field) + } } if normalized.Sort.Order != "" && normalized.Sort.Order != "asc" && normalized.Sort.Order != "desc" { return fmt.Errorf("sort.order must be 'asc' or 'desc'") diff --git a/internal/catalog/query_definition_test.go b/internal/catalog/query_definition_test.go index 91629e40..b95762e1 100644 --- a/internal/catalog/query_definition_test.go +++ b/internal/catalog/query_definition_test.go @@ -72,6 +72,45 @@ func TestValidate_AudiobookMediaScope(t *testing.T) { } } +func TestValidate_EbookMediaScope(t *testing.T) { + qd := QueryDefinition{ + MediaScope: "ebook", + Match: "all", + Groups: []QueryGroup{}, + Sort: QuerySort{Field: "title", Order: "asc"}, + } + if err := qd.Validate(); err != nil { + t.Fatalf("expected ebook media scope to be valid, got %v", err) + } +} + +func TestValidate_EbookMediaScopeRejectsNarratorRule(t *testing.T) { + qd := QueryDefinition{ + MediaScope: "ebook", + Match: "all", + Groups: []QueryGroup{{ + Match: "all", + Rules: []QueryRule{{Field: "narrator", Op: "is", Value: "Narrator"}}, + }}, + Sort: QuerySort{Field: "title", Order: "asc"}, + } + if err := qd.Validate(); err == nil { + t.Fatal("expected ebook narrator rule to be rejected") + } +} + +func TestValidate_EbookMediaScopeRejectsNarratorSort(t *testing.T) { + qd := QueryDefinition{ + MediaScope: "ebook", + Match: "all", + Groups: []QueryGroup{}, + Sort: QuerySort{Field: "narrator", Order: "asc"}, + } + if err := qd.Validate(); err == nil { + t.Fatal("expected ebook narrator sort to be rejected") + } +} + func TestValidateWithSortScope_RejectsPersonalizedSortWithoutProfileScope(t *testing.T) { qd := QueryDefinition{ Match: "all", diff --git a/internal/ebooks/enrichment.go b/internal/ebooks/enrichment.go new file mode 100644 index 00000000..fe1b8124 --- /dev/null +++ b/internal/ebooks/enrichment.go @@ -0,0 +1,765 @@ +package ebooks + +// Enricher periodically enriches ebook media_items that are missing metadata +// by querying the configured metadata-provider chain for each item's library +// folder. + +import ( + "context" + "errors" + "fmt" + "log/slog" + "os" + "reflect" + "strconv" + "strings" + "sync" + "sync/atomic" + "time" + + "github.com/jackc/pgx/v5/pgxpool" + + "github.com/Silo-Server/silo-server/internal/catalog" + "github.com/Silo-Server/silo-server/internal/metadata" + "github.com/Silo-Server/silo-server/internal/models" +) + +const ( + ebookMetadataImageProviderID = "ebook-metadata" + + defaultEnrichBatchSize = 50 + defaultEnrichWorkers = 4 + + // enrichFailureCap is the ebook_enrichment_state.failures count at which + // an ebook stops being claimed for enrichment. Combined with the + // failure-count-first claim ordering this prevents a head-of-line block + // of permanently failing items from starving newer items and hammering + // providers. + enrichFailureCap = 5 +) + +// errEnrichmentSkipped marks an item that could not be attempted at all (no +// library folder linked yet, no providers configured). Skipped items are +// neither stamped as refreshed nor counted against the failure cap, so they +// are retried on every sweep until the missing prerequisite appears. +var errEnrichmentSkipped = errors.New("ebook enrichment skipped") + +func ebookContentType() string { + return "ebook" +} + +func ebookEnrichWorkers() int { + n := defaultEnrichWorkers + if v := os.Getenv("SILO_EBOOK_ENRICH_WORKERS"); v != "" { + if parsed, err := strconv.Atoi(v); err == nil && parsed > 0 { + n = parsed + } + } + if n > defaultEnrichBatchSize { + n = defaultEnrichBatchSize + } + return n +} + +type enrichmentItemRow struct { + ContentID string + Title string + Year int + FolderID int + Language string + Author string + ProviderIDs map[string]string +} + +// Enricher drives the ebook metadata enrichment sweep. +type Enricher struct { + pool *pgxpool.Pool + chainRepo *metadata.ChainRepository + resolver *metadata.PluginResolverAdapter + itemRepo *catalog.ItemRepository + personRepo *catalog.PersonRepository + providerIDs *catalog.ProviderIDRepository + imageCacher metadata.ImageCacher + batchSize int + workers int +} + +func NewEnricher( + pool *pgxpool.Pool, + chainRepo *metadata.ChainRepository, + resolver *metadata.PluginResolverAdapter, + itemRepo *catalog.ItemRepository, + personRepo *catalog.PersonRepository, + providerIDs *catalog.ProviderIDRepository, +) *Enricher { + return &Enricher{ + pool: pool, + chainRepo: chainRepo, + resolver: resolver, + itemRepo: itemRepo, + personRepo: personRepo, + providerIDs: providerIDs, + batchSize: defaultEnrichBatchSize, + workers: ebookEnrichWorkers(), + } +} + +func (e *Enricher) SetImageCacher(cacher metadata.ImageCacher) { + if e == nil { + return + } + e.imageCacher = cacher +} + +func (e *Enricher) Run(ctx context.Context) (int, error) { + if e == nil || e.pool == nil || e.chainRepo == nil { + return 0, nil + } + + items, err := e.claimBatch(ctx) + if err != nil { + return 0, fmt.Errorf("ebook enrichment: claim batch: %w", err) + } + if len(items) == 0 { + return 0, nil + } + + slog.Info("ebook enrichment: sweep started", + "count", len(items), + "workers", e.workers, + ) + + enriched := e.runBatch(ctx, items, e.enrichItem, e.recordEnrichFailure) + + slog.Info("ebook enrichment: sweep complete", + "attempted", len(items), + "enriched", enriched, + ) + return enriched, nil +} + +func (e *Enricher) runBatch( + ctx context.Context, + items []enrichmentItemRow, + enrichFn func(context.Context, enrichmentItemRow) error, + recordFailure func(context.Context, enrichmentItemRow), +) int { + workers := e.workers + if workers <= 0 { + workers = 1 + } + if workers > len(items) { + workers = len(items) + } + + ch := make(chan enrichmentItemRow, workers) + var ( + wg sync.WaitGroup + enriched int64 + ) + for i := 0; i < workers; i++ { + wg.Add(1) + go func() { + defer wg.Done() + for item := range ch { + if ctx.Err() != nil { + continue + } + if err := enrichFn(ctx, item); err != nil { + if errors.Is(err, errEnrichmentSkipped) { + slog.Debug("ebook enrichment: item skipped", + "content_id", item.ContentID, + "title", item.Title, + "reason", err, + ) + continue + } + slog.Warn("ebook enrichment: item failed", + "content_id", item.ContentID, + "title", item.Title, + "error", err, + ) + // A cancelled sweep says nothing about the item itself, + // so it does not count against the failure cap. + if recordFailure != nil && !errors.Is(err, context.Canceled) && !errors.Is(err, context.DeadlineExceeded) { + recordFailure(ctx, item) + } + continue + } + atomic.AddInt64(&enriched, 1) + } + }() + } + for _, item := range items { + if ctx.Err() != nil { + break + } + ch <- item + } + close(ch) + wg.Wait() + return int(enriched) +} + +// claimBatchQuery selects unenriched ebooks. Items with fewer prior failures +// are claimed first and items at/above enrichFailureCap are skipped entirely, +// so a block of permanently failing items cannot occupy every sweep. +const claimBatchQuery = ` + SELECT + mi.content_id, + mi.title, + mi.year, + COALESCE(mil.media_folder_id, 0) AS folder_id, + COALESCE(mf.metadata_language, 'en') AS language, + COALESCE( + (SELECT p.name + FROM item_people ip + JOIN people p ON p.id = ip.person_id + WHERE ip.content_id = mi.content_id + AND ip.kind = 7 + ORDER BY ip.sort_order, ip.id + LIMIT 1), + '' + ) AS author + FROM media_items mi + LEFT JOIN media_item_libraries mil ON mil.content_id = mi.content_id + LEFT JOIN media_folders mf ON mf.id = mil.media_folder_id + LEFT JOIN ebook_enrichment_state ees ON ees.content_id = mi.content_id + WHERE mi.type = 'ebook' + AND (mi.poster_path IS NULL OR mi.poster_path = '') + AND mi.last_refreshed IS NULL + AND COALESCE(ees.failures, 0) < $2 + ORDER BY COALESCE(ees.failures, 0) ASC, mi.created_at ASC + LIMIT $1 +` + +func (e *Enricher) claimBatch(ctx context.Context) ([]enrichmentItemRow, error) { + rows, err := e.pool.Query(ctx, claimBatchQuery, e.batchSize, enrichFailureCap) + if err != nil { + return nil, fmt.Errorf("querying unenriched ebooks: %w", err) + } + defer rows.Close() + + var items []enrichmentItemRow + seen := make(map[string]struct{}) + for rows.Next() { + var item enrichmentItemRow + if err := rows.Scan( + &item.ContentID, + &item.Title, + &item.Year, + &item.FolderID, + &item.Language, + &item.Author, + ); err != nil { + return nil, fmt.Errorf("scanning ebook enrichment row: %w", err) + } + if _, dup := seen[item.ContentID]; dup { + continue + } + seen[item.ContentID] = struct{}{} + items = append(items, item) + } + if err := rows.Err(); err != nil { + return nil, fmt.Errorf("iterating ebook enrichment rows: %w", err) + } + + if e.providerIDs != nil { + for i := range items { + pids, err := e.providerIDs.GetByContentID(ctx, items[i].ContentID) + if err == nil { + items[i].ProviderIDs = providerIDMapFromRows(pids) + } + } + } + + return items, nil +} + +func (e *Enricher) enrichItem(ctx context.Context, item enrichmentItemRow) error { + if item.FolderID == 0 { + // The scanner inserts the library membership after the item upsert, so + // a freshly indexed ebook can be claimed inside that window. Skip it: + // stamping here would terminally mark the item refreshed before any + // provider ever saw it. + return fmt.Errorf("%w: item %s has no library folder yet", errEnrichmentSkipped, item.ContentID) + } + + providers, err := metadata.ResolveChain(ctx, item.FolderID, ebookContentType(), e.chainRepo, e.resolver) + if err != nil { + return fmt.Errorf("resolving ebook chain for folder %d: %w", item.FolderID, err) + } + return e.enrichWithProviders(ctx, item, providers) +} + +// enrichWithProviders runs the provider chain for one claimed item. Outcomes: +// - metadata obtained: persist it and stamp last_refreshed (nil error); +// - providers answered but nothing matched: stamp last_refreshed so the +// item is not re-claimed every sweep (nil error); +// - one or more providers errored and no metadata was obtained: return an +// error so the failure cap/backoff engages, without stamping; +// - no providers configured: skip (no stamp, no failure) so the item is +// retried once a chain exists. +func (e *Enricher) enrichWithProviders(ctx context.Context, item enrichmentItemRow, providers []metadata.Provider) error { + if len(providers) == 0 { + return fmt.Errorf("%w: no metadata providers configured for folder %d", errEnrichmentSkipped, item.FolderID) + } + + accumulator, accumulatedIDs, providerErrs := collectEbookMetadata(ctx, item, providers) + + if !accumulator.HasMetadata && accumulator.PosterPath == "" && accumulator.Overview == "" { + if err := ctx.Err(); err != nil { + // A cancelled sweep says nothing about the item or the providers. + return err + } + if len(providerErrs) > 0 { + // Transient provider trouble must not stamp the item terminally; + // surfacing an error engages the failure cap and backoff instead. + return fmt.Errorf("no metadata obtained, %d provider error(s): %w", + len(providerErrs), errors.Join(providerErrs...)) + } + slog.Info("ebook enrichment: no metadata found", + "content_id", item.ContentID, + "title", item.Title, + ) + return e.stampLastRefreshed(ctx, item.ContentID) + } + + e.cacheRemotePoster(ctx, item.ContentID, accumulator) + + if err := e.persist(ctx, item.ContentID, accumulatedIDs, accumulator); err != nil { + return fmt.Errorf("persisting enrichment for %s: %w", item.ContentID, err) + } + + slog.Info("ebook enrichment: enriched", + "content_id", item.ContentID, + "title", item.Title, + "poster", accumulator.PosterPath != "", + "overview", accumulator.Overview != "", + "people", len(filterEbookPeople(accumulator.People)), + ) + + return nil +} + +// collectEbookMetadata queries every provider in the chain and accumulates +// IDs and metadata. Individual provider failures are collected (not fatal) so +// the caller can distinguish "providers answered, no match" from "providers +// were unreachable". +func collectEbookMetadata(ctx context.Context, item enrichmentItemRow, providers []metadata.Provider) (*metadata.MetadataResult, map[string]string, []error) { + searchQuery, accumulatedIDs := buildEbookSearchQuery(item) + var providerErrs []error + + for _, p := range providers { + sp, ok := p.(metadata.SearchProvider) + if !ok { + continue + } + results, searchErr := sp.Search(ctx, searchQuery) + if searchErr != nil { + slog.Warn("ebook enrichment: search error", + "provider", p.Slug(), + "content_id", item.ContentID, + "error", searchErr, + ) + providerErrs = append(providerErrs, fmt.Errorf("%s search: %w", p.Slug(), searchErr)) + continue + } + if len(results) == 0 { + continue + } + for k, v := range results[0].ProviderIDs { + if v != "" { + if _, exists := accumulatedIDs[k]; !exists { + accumulatedIDs[k] = v + } + } + } + slog.Debug("ebook enrichment: search result", + "provider", p.Slug(), + "content_id", item.ContentID, + "matched_ids", accumulatedIDs, + ) + } + + accumulator := &metadata.MetadataResult{ + ProviderIDs: accumulatedIDs, + } + + for _, p := range providers { + mp, ok := p.(metadata.MetadataProvider) + if !ok { + continue + } + result, getErr := mp.GetMetadata(ctx, buildEbookMetadataRequest(accumulator.ProviderIDs, item.Language)) + if getErr != nil { + slog.Warn("ebook enrichment: GetMetadata error", + "provider", p.Slug(), + "content_id", item.ContentID, + "error", getErr, + ) + providerErrs = append(providerErrs, fmt.Errorf("%s metadata: %w", p.Slug(), getErr)) + continue + } + if result == nil || !result.HasMetadata { + continue + } + mergeEnrichmentProviderIDs(accumulator, result) + metadata.MergeMetadata(result, accumulator, nil, metadata.MergeFillEmpty) + + slog.Debug("ebook enrichment: metadata received", + "provider", p.Slug(), + "content_id", item.ContentID, + "has_poster", result.PosterPath != "", + "has_overview", result.Overview != "", + ) + } + + return accumulator, accumulator.ProviderIDs, providerErrs +} + +func (e *Enricher) cacheRemotePoster(ctx context.Context, contentID string, result *metadata.MetadataResult) { + if e == nil || result == nil || result.PosterPath == "" { + return + } + if !strings.HasPrefix(result.PosterPath, "http://") && !strings.HasPrefix(result.PosterPath, "https://") { + return + } + if isNilImageCacher(e.imageCacher) { + return + } + + cached, err := e.imageCacher.CacheImage(ctx, metadata.CacheImageRequest{ + SourceURL: result.PosterPath, + ProviderID: ebookMetadataImageProviderID, + ContentType: "ebooks", + ContentID: contentID, + ImageType: metadata.ImagePoster, + }) + if err != nil { + slog.Warn("ebook enrichment: poster cache failed, keeping provider URL", + "content_id", contentID, + "url", result.PosterPath, + "error", err, + ) + return + } + if cached == nil { + slog.Warn("ebook enrichment: poster cache returned no result, keeping provider URL", + "content_id", contentID, + "url", result.PosterPath, + ) + return + } + + if storedPath := cachedOriginalImagePath(cached.BasePath, cached.Ext); storedPath != "" { + result.PosterPath = storedPath + } + if cached.Thumbhash != "" { + result.PosterThumbhash = cached.Thumbhash + } +} + +func isNilImageCacher(cacher metadata.ImageCacher) bool { + if cacher == nil { + return true + } + value := reflect.ValueOf(cacher) + switch value.Kind() { + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: + return value.IsNil() + default: + return false + } +} + +func cachedOriginalImagePath(basePath, ext string) string { + if basePath == "" { + return "" + } + if strings.Contains(basePath, "/original.") { + return basePath + } + if ext == "" { + ext = ".jpg" + } + return strings.TrimRight(basePath, "/") + "/original" + ext +} + +func (e *Enricher) persist(ctx context.Context, contentID string, providerIDs map[string]string, result *metadata.MetadataResult) error { + upd := &catalog.MetadataUpdate{} + + if result.PosterPath != "" { + upd.PosterPath = &result.PosterPath + } + if result.PosterThumbhash != "" { + upd.PosterThumbhash = &result.PosterThumbhash + } + if result.BackdropPath != "" { + upd.BackdropPath = &result.BackdropPath + } + if result.BackdropThumbhash != "" { + upd.BackdropThumbhash = &result.BackdropThumbhash + } + if result.LogoPath != "" { + upd.LogoPath = &result.LogoPath + } + if result.Overview != "" { + upd.Overview = &result.Overview + } + if result.Tagline != "" { + upd.Tagline = &result.Tagline + } + if result.ReleaseDate != "" { + upd.ReleaseDate = &result.ReleaseDate + } + if len(result.Genres) > 0 { + genres := append([]string(nil), result.Genres...) + upd.Genres = &genres + } + if len(result.Studios) > 0 { + studios := append([]string(nil), result.Studios...) + upd.Studios = &studios + } + if result.ContentRating != "" { + upd.ContentRating = &result.ContentRating + } + if result.Runtime > 0 { + upd.Runtime = &result.Runtime + } + if result.Year > 0 { + upd.Year = &result.Year + } + + providerIDs = filterEbookProviderIDs(providerIDs) + if e.providerIDs != nil && len(providerIDs) > 0 { + if err := e.providerIDs.ReplaceByContentID(ctx, contentID, providerIDs); err != nil { + slog.Warn("ebook enrichment: failed to persist provider IDs", + "content_id", contentID, + "error", err, + ) + } + } + + if err := e.updateMetadataAndTimestamps(ctx, contentID, upd); err != nil { + return err + } + + authors := filterEbookPeople(result.People) + if len(authors) > 0 && e.personRepo != nil && e.itemRepo != nil { + if err := e.persistPeople(ctx, contentID, authors); err != nil { + slog.Warn("ebook enrichment: failed to persist people", + "content_id", contentID, + "error", err, + ) + } + } + + return nil +} + +func (e *Enricher) updateMetadataAndTimestamps(ctx context.Context, contentID string, upd *catalog.MetadataUpdate) error { + if e.itemRepo == nil { + return nil + } + if err := e.itemRepo.UpdateMetadata(ctx, contentID, upd); err != nil { + return fmt.Errorf("UpdateMetadata: %w", err) + } + return e.stampLastRefreshed(ctx, contentID) +} + +func (e *Enricher) stampLastRefreshed(ctx context.Context, contentID string) error { + if e.pool == nil { + return nil + } + now := time.Now().UTC() + if _, err := e.pool.Exec(ctx, ` + UPDATE media_items + SET last_refreshed = $1, + matched_at = COALESCE(matched_at, $1), + status = CASE WHEN status = 'pending' THEN 'matched' ELSE status END + WHERE content_id = $2 + `, now, contentID); err != nil { + return err + } + // Success clears the enrichment failure backlog. media_items.refresh_failures + // is intentionally left alone: it belongs to the metadata refresh-debt system. + _, err := e.pool.Exec(ctx, ` + DELETE FROM ebook_enrichment_state WHERE content_id = $1 + `, contentID) + return err +} + +// recordEnrichFailure increments the item's ebook_enrichment_state failure +// counter so claimBatch deprioritizes it on the next sweep and stops claiming +// it at enrichFailureCap. The state is dedicated to ebook enrichment; +// media_items.refresh_failures is owned by the metadata refresh-debt system +// and is never touched here. +func (e *Enricher) recordEnrichFailure(ctx context.Context, item enrichmentItemRow) { + if e == nil || e.pool == nil { + return + } + if _, err := e.pool.Exec(ctx, ` + INSERT INTO ebook_enrichment_state (content_id, failures, updated_at) + VALUES ($1, 1, NOW()) + ON CONFLICT (content_id) DO UPDATE SET + failures = ebook_enrichment_state.failures + 1, + updated_at = NOW() + `, item.ContentID); err != nil { + slog.Warn("ebook enrichment: failed to record enrichment failure", + "content_id", item.ContentID, + "error", err, + ) + } +} + +func (e *Enricher) persistPeople(ctx context.Context, contentID string, people []models.ItemPerson) error { + people = filterEbookPeople(people) + if len(people) == 0 { + return nil + } + + persons := make([]models.Person, len(people)) + for i := range people { + persons[i] = people[i].Person + } + + personIDs, err := e.personRepo.BatchFindOrCreate(ctx, persons) + if err != nil { + return fmt.Errorf("BatchFindOrCreate people: %w", err) + } + + linked := make([]models.ItemPerson, 0, len(people)) + for i := range people { + if i >= len(personIDs) || personIDs[i] == 0 { + continue + } + ip := people[i] + ip.Person.ID = personIDs[i] + linked = append(linked, ip) + } + + if len(linked) == 0 { + return nil + } + + existing, err := e.itemRepo.GetPeople(ctx, contentID) + if err != nil { + return fmt.Errorf("get existing people: %w", err) + } + return e.itemRepo.ReplacePeople(ctx, contentID, mergeEbookAuthorCredits(existing, linked)) +} + +// mergeEbookAuthorCredits mirrors the scanner's mergeEbookPeople semantics: +// the provider authors replace existing author (and stale narrator) credits, +// while every other curated people kind on the item is preserved. +func mergeEbookAuthorCredits(existing []models.ItemPerson, authors []models.ItemPerson) []models.ItemPerson { + merged := make([]models.ItemPerson, 0, len(existing)+len(authors)) + for _, p := range existing { + if p.Kind == models.PersonKindAuthor || p.Kind == models.PersonKindNarrator { + continue + } + p.SortOrder = len(merged) + merged = append(merged, p) + } + for _, a := range authors { + a.SortOrder = len(merged) + merged = append(merged, a) + } + return merged +} + +func filterEbookPeople(people []models.ItemPerson) []models.ItemPerson { + authors := make([]models.ItemPerson, 0, len(people)) + for _, person := range people { + if person.Kind != models.PersonKindAuthor { + continue + } + person.SortOrder = len(authors) + authors = append(authors, person) + } + return authors +} + +func buildEbookSearchQuery(item enrichmentItemRow) (metadata.SearchQuery, map[string]string) { + accumulatedIDs := filterEbookProviderIDs(item.ProviderIDs) + if accumulatedIDs == nil { + accumulatedIDs = map[string]string{} + } + return metadata.SearchQuery{ + Title: item.Title, + Year: item.Year, + ContentType: ebookContentType(), + ProviderIDs: accumulatedIDs, + Language: item.Language, + }, accumulatedIDs +} + +func buildEbookMetadataRequest(providerIDs map[string]string, language string) metadata.MetadataRequest { + return metadata.MetadataRequest{ + ProviderIDs: filterEbookProviderIDs(providerIDs), + ContentType: ebookContentType(), + Language: language, + } +} + +func mergeEnrichmentProviderIDs(dst *metadata.MetadataResult, src *metadata.MetadataResult) { + if src == nil || len(src.ProviderIDs) == 0 { + return + } + if dst.ProviderIDs == nil { + dst.ProviderIDs = make(map[string]string, len(src.ProviderIDs)) + } + for k, v := range filterEbookProviderIDs(src.ProviderIDs) { + if v != "" { + if _, exists := dst.ProviderIDs[k]; !exists { + dst.ProviderIDs[k] = v + } + } + } +} + +func filterEbookProviderIDs(providerIDs map[string]string) map[string]string { + if len(providerIDs) == 0 { + return nil + } + filtered := make(map[string]string, len(providerIDs)) + for provider, providerID := range providerIDs { + provider = strings.TrimSpace(provider) + providerID = strings.TrimSpace(providerID) + if provider == "" || providerID == "" { + continue + } + provider = strings.ToLower(provider) + if isEbookASINProvider(provider) { + continue + } + filtered[provider] = providerID + } + if len(filtered) == 0 { + return nil + } + return filtered +} + +func isEbookASINProvider(provider string) bool { + normalized := strings.ReplaceAll(strings.ReplaceAll(provider, "_", ""), "-", "") + return normalized == "asin" || normalized == "audibleasin" +} + +func providerIDMapFromRows(rows []*models.MediaItemProviderID) map[string]string { + if len(rows) == 0 { + return nil + } + m := make(map[string]string, len(rows)) + for _, r := range rows { + if r != nil { + for provider, providerID := range filterEbookProviderIDs(map[string]string{ + r.Provider: r.ProviderID, + }) { + m[provider] = providerID + } + } + } + return m +} diff --git a/internal/ebooks/enrichment_test.go b/internal/ebooks/enrichment_test.go new file mode 100644 index 00000000..571ec23b --- /dev/null +++ b/internal/ebooks/enrichment_test.go @@ -0,0 +1,573 @@ +package ebooks + +import ( + "context" + "errors" + "fmt" + "sort" + "strings" + "sync" + "sync/atomic" + "testing" + "time" + + "github.com/Silo-Server/silo-server/internal/metadata" + "github.com/Silo-Server/silo-server/internal/models" +) + +func TestEbookContentType(t *testing.T) { + if got := ebookContentType(); got != "ebook" { + t.Fatalf("ebookContentType() = %q, want ebook", got) + } +} + +func TestFilterEbookPeopleKeepsAuthorsOnly(t *testing.T) { + people := []models.ItemPerson{ + {Person: models.Person{Name: "Author One"}, Kind: models.PersonKindAuthor, SortOrder: 7}, + {Person: models.Person{Name: "Narrator One"}, Kind: models.PersonKindNarrator, SortOrder: 8}, + {Person: models.Person{Name: "Writer One"}, Kind: models.PersonKindWriter, SortOrder: 9}, + {Person: models.Person{Name: "Author Two"}, Kind: models.PersonKindAuthor, SortOrder: 10}, + } + + got := filterEbookPeople(people) + + if len(got) != 2 { + t.Fatalf("filtered people len = %d, want 2: %+v", len(got), got) + } + for i, p := range got { + if p.Kind != models.PersonKindAuthor { + t.Fatalf("filtered[%d].Kind = %v, want author", i, p.Kind) + } + if p.SortOrder != i { + t.Fatalf("filtered[%d].SortOrder = %d, want %d", i, p.SortOrder, i) + } + } + if got[0].Person.Name != "Author One" || got[1].Person.Name != "Author Two" { + t.Fatalf("filtered author order = %+v", got) + } +} + +func TestEbookEnrichWorkersFromEnv(t *testing.T) { + t.Setenv("SILO_EBOOK_ENRICH_WORKERS", "12") + if got := ebookEnrichWorkers(); got != 12 { + t.Fatalf("ebookEnrichWorkers() = %d, want 12", got) + } + + t.Setenv("SILO_EBOOK_ENRICH_WORKERS", "0") + if got := ebookEnrichWorkers(); got != defaultEnrichWorkers { + t.Fatalf("ebookEnrichWorkers() with zero = %d, want default %d", got, defaultEnrichWorkers) + } + + t.Setenv("SILO_EBOOK_ENRICH_WORKERS", "999") + if got := ebookEnrichWorkers(); got != defaultEnrichBatchSize { + t.Fatalf("ebookEnrichWorkers() capped = %d, want %d", got, defaultEnrichBatchSize) + } +} + +func TestEnricherRunFansOut(t *testing.T) { + const wantWorkers = 4 + const itemCount = 16 + + items := make([]enrichmentItemRow, itemCount) + for i := range items { + items[i] = enrichmentItemRow{ContentID: "test", Title: "t"} + } + + var inFlight int32 + var maxInFlight int32 + var signaled int32 + var wg sync.WaitGroup + wg.Add(wantWorkers) + gate := make(chan struct{}) + + enrich := func(ctx context.Context, item enrichmentItemRow) error { + cur := atomic.AddInt32(&inFlight, 1) + defer atomic.AddInt32(&inFlight, -1) + for { + prev := atomic.LoadInt32(&maxInFlight) + if cur <= prev || atomic.CompareAndSwapInt32(&maxInFlight, prev, cur) { + break + } + } + if atomic.AddInt32(&signaled, 1) <= wantWorkers { + wg.Done() + } + select { + case <-gate: + case <-time.After(2 * time.Second): + } + return nil + } + + go func() { + wg.Wait() + close(gate) + }() + + e := &Enricher{workers: wantWorkers, batchSize: itemCount} + e.runBatch(context.Background(), items, enrich, nil) + + if got := atomic.LoadInt32(&maxInFlight); got < wantWorkers { + t.Errorf("max in-flight = %d, want >= %d", got, wantWorkers) + } +} + +func TestRunBatchRecordsFailuresForFailedItemsOnly(t *testing.T) { + items := []enrichmentItemRow{ + {ContentID: "ok-1"}, + {ContentID: "bad-1"}, + {ContentID: "ok-2"}, + {ContentID: "bad-2"}, + } + + enrich := func(_ context.Context, item enrichmentItemRow) error { + if strings.HasPrefix(item.ContentID, "bad") { + return errors.New("provider exploded") + } + return nil + } + + var mu sync.Mutex + var recorded []string + record := func(_ context.Context, item enrichmentItemRow) { + mu.Lock() + defer mu.Unlock() + recorded = append(recorded, item.ContentID) + } + + e := &Enricher{workers: 2, batchSize: len(items)} + enriched := e.runBatch(context.Background(), items, enrich, record) + + if enriched != 2 { + t.Fatalf("enriched = %d, want 2", enriched) + } + sort.Strings(recorded) + if strings.Join(recorded, ",") != "bad-1,bad-2" { + t.Fatalf("recorded failures = %v, want exactly the failing items", recorded) + } +} + +func TestRunBatchSkipsFailureRecordingOnCancellation(t *testing.T) { + items := []enrichmentItemRow{{ContentID: "cancelled-1"}} + + enrich := func(context.Context, enrichmentItemRow) error { + return fmt.Errorf("search aborted: %w", context.Canceled) + } + + var recorded int32 + record := func(context.Context, enrichmentItemRow) { + atomic.AddInt32(&recorded, 1) + } + + e := &Enricher{workers: 1, batchSize: len(items)} + if enriched := e.runBatch(context.Background(), items, enrich, record); enriched != 0 { + t.Fatalf("enriched = %d, want 0", enriched) + } + if got := atomic.LoadInt32(&recorded); got != 0 { + t.Fatalf("failure recordings = %d, want 0: cancellation must not count against the cap", got) + } +} + +func TestClaimBatchQueryAppliesFailureBackoffAndCap(t *testing.T) { + // Pin the starvation guard: failing items are deprioritized, not retried + // at the head of every sweep, and capped items are never re-claimed. + if !strings.Contains(claimBatchQuery, "LEFT JOIN ebook_enrichment_state ees ON ees.content_id = mi.content_id") { + t.Fatalf("claimBatchQuery must read dedicated ebook enrichment failure state:\n%s", claimBatchQuery) + } + if !strings.Contains(claimBatchQuery, "COALESCE(ees.failures, 0) < $2") { + t.Fatalf("claimBatchQuery must exclude items at the failure cap:\n%s", claimBatchQuery) + } + if !strings.Contains(claimBatchQuery, "ORDER BY COALESCE(ees.failures, 0) ASC, mi.created_at ASC") { + t.Fatalf("claimBatchQuery must claim least-failed items first:\n%s", claimBatchQuery) + } + if strings.Contains(claimBatchQuery, "refresh_failures") { + t.Fatalf("claimBatchQuery must not read media_items.refresh_failures (owned by the metadata refresh-debt system):\n%s", claimBatchQuery) + } + if enrichFailureCap < 1 { + t.Fatalf("enrichFailureCap = %d, want >= 1", enrichFailureCap) + } +} + +func TestEnrichItemSkipsItemWithoutLibraryFolder(t *testing.T) { + // Membership rows are inserted after the item upsert, so a scan-window + // race can claim an item before its folder link exists. The item must be + // skipped (retried next sweep), never stamped or counted as a failure. + e := &Enricher{} + err := e.enrichItem(context.Background(), enrichmentItemRow{ContentID: "no-folder", Title: "t"}) + if !errors.Is(err, errEnrichmentSkipped) { + t.Fatalf("enrichItem error = %v, want errEnrichmentSkipped", err) + } +} + +func TestEnrichWithProvidersSkipsWhenNoProvidersConfigured(t *testing.T) { + e := &Enricher{} + err := e.enrichWithProviders(context.Background(), enrichmentItemRow{ContentID: "c1", FolderID: 7}, nil) + if !errors.Is(err, errEnrichmentSkipped) { + t.Fatalf("enrichWithProviders error = %v, want errEnrichmentSkipped", err) + } +} + +func TestEnrichWithProvidersReturnsFailureWhenAllProvidersError(t *testing.T) { + providerErr := errors.New("provider exploded") + providers := []metadata.Provider{ + &fakeEbookMetadataProvider{slug: "p1", searchErr: providerErr, getErr: providerErr}, + &fakeEbookMetadataProvider{slug: "p2", searchErr: providerErr, getErr: providerErr}, + } + + e := &Enricher{} + err := e.enrichWithProviders(context.Background(), enrichmentItemRow{ContentID: "c1", FolderID: 7, Title: "t"}, providers) + if err == nil { + t.Fatal("enrichWithProviders = nil, want error so the failure cap engages instead of stamping") + } + if errors.Is(err, errEnrichmentSkipped) { + t.Fatalf("enrichWithProviders error = %v, want a recordable failure, not a skip", err) + } + if !errors.Is(err, providerErr) { + t.Fatalf("enrichWithProviders error = %v, want wrapped provider error", err) + } +} + +func TestEnrichWithProvidersStampsWhenProvidersAnswerWithNoMatch(t *testing.T) { + // Providers reachable, genuinely nothing found: nil means the no-match + // path ran and the item was stamped so it is not re-claimed every sweep. + providers := []metadata.Provider{ + &fakeEbookMetadataProvider{slug: "p1"}, + } + + e := &Enricher{} + err := e.enrichWithProviders(context.Background(), enrichmentItemRow{ContentID: "c1", FolderID: 7, Title: "t"}, providers) + if err != nil { + t.Fatalf("enrichWithProviders = %v, want nil for a genuine no-match", err) + } +} + +func TestEnrichWithProvidersReturnsContextErrorOverProviderFailure(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + cancel() + providers := []metadata.Provider{ + &fakeEbookMetadataProvider{slug: "p1", searchErr: ctx.Err(), getErr: ctx.Err()}, + } + + e := &Enricher{} + err := e.enrichWithProviders(ctx, enrichmentItemRow{ContentID: "c1", FolderID: 7}, providers) + if !errors.Is(err, context.Canceled) { + t.Fatalf("enrichWithProviders error = %v, want context.Canceled so cancellation never counts against the cap", err) + } +} + +func TestCollectEbookMetadataAccumulatesProviderErrors(t *testing.T) { + searchErr := errors.New("search down") + getErr := errors.New("metadata down") + providers := []metadata.Provider{ + &fakeEbookMetadataProvider{slug: "broken", searchErr: searchErr, getErr: getErr}, + &fakeEbookMetadataProvider{ + slug: "working", + results: []metadata.SearchResult{{ProviderIDs: map[string]string{"openlibrary": "OL1M"}}}, + result: &metadata.MetadataResult{HasMetadata: true, Overview: "found"}, + }, + } + + accumulator, ids, errs := collectEbookMetadata(context.Background(), enrichmentItemRow{ContentID: "c1", Title: "t"}, providers) + + if len(errs) != 2 || !errors.Is(errs[0], searchErr) || !errors.Is(errs[1], getErr) { + t.Fatalf("provider errors = %v, want both broken-provider errors", errs) + } + if accumulator.Overview != "found" { + t.Fatalf("accumulator overview = %q, want metadata from the working provider", accumulator.Overview) + } + if ids["openlibrary"] != "OL1M" { + t.Fatalf("accumulated IDs = %v, want search-result openlibrary ID", ids) + } +} + +func TestRunBatchDoesNotRecordFailuresForSkippedItems(t *testing.T) { + items := []enrichmentItemRow{{ContentID: "skipped-1"}} + + enrich := func(context.Context, enrichmentItemRow) error { + return fmt.Errorf("%w: no providers", errEnrichmentSkipped) + } + + var recorded int32 + record := func(context.Context, enrichmentItemRow) { + atomic.AddInt32(&recorded, 1) + } + + e := &Enricher{workers: 1, batchSize: len(items)} + if enriched := e.runBatch(context.Background(), items, enrich, record); enriched != 0 { + t.Fatalf("enriched = %d, want 0", enriched) + } + if got := atomic.LoadInt32(&recorded); got != 0 { + t.Fatalf("failure recordings = %d, want 0: skips must not count against the cap", got) + } +} + +func TestMergeEbookAuthorCreditsPreservesOtherPeopleKinds(t *testing.T) { + existing := []models.ItemPerson{ + {Person: models.Person{ID: 10, Name: "Old Author"}, Kind: models.PersonKindAuthor, SortOrder: 0}, + {Person: models.Person{ID: 20, Name: "Manual Writer"}, Kind: models.PersonKindWriter, SortOrder: 1, Character: "essay"}, + {Person: models.Person{ID: 30, Name: "Stale Narrator"}, Kind: models.PersonKindNarrator, SortOrder: 2}, + } + authors := []models.ItemPerson{ + {Person: models.Person{ID: 40, Name: "Provider Author"}, Kind: models.PersonKindAuthor}, + } + + got := mergeEbookAuthorCredits(existing, authors) + + if len(got) != 2 { + t.Fatalf("merged people len = %d, want 2: %+v", len(got), got) + } + if got[0].Person.ID != 20 || got[0].Kind != models.PersonKindWriter || got[0].Character != "essay" || got[0].SortOrder != 0 { + t.Fatalf("preserved non-author credit = %+v", got[0]) + } + if got[1].Person.ID != 40 || got[1].Kind != models.PersonKindAuthor || got[1].SortOrder != 1 { + t.Fatalf("provider author credit = %+v", got[1]) + } +} + +func TestCacheRemotePosterCachesProviderURL(t *testing.T) { + cacher := &fakeEbookImageCacher{} + e := &Enricher{imageCacher: cacher} + result := &metadata.MetadataResult{ + PosterPath: "https://example.test/book.jpg", + } + + e.cacheRemotePoster(context.Background(), "content-1", result) + + if cacher.calls != 1 { + t.Fatalf("CacheImage calls = %d, want 1", cacher.calls) + } + if cacher.req.ProviderID != ebookMetadataImageProviderID { + t.Fatalf("ProviderID = %q, want %q", cacher.req.ProviderID, ebookMetadataImageProviderID) + } + if cacher.req.ContentType != "ebooks" || cacher.req.ContentID != "content-1" { + t.Fatalf("cache target = %q/%q", cacher.req.ContentType, cacher.req.ContentID) + } + if result.PosterPath != "ebook-metadata/ebooks/content-1/poster/original.webp" { + t.Fatalf("PosterPath = %q", result.PosterPath) + } + if result.PosterThumbhash != "thumb" { + t.Fatalf("PosterThumbhash = %q", result.PosterThumbhash) + } +} + +func TestCacheRemotePosterSkipsNilCacher(t *testing.T) { + e := &Enricher{} + result := &metadata.MetadataResult{ + PosterPath: "https://example.test/book.jpg", + } + + e.cacheRemotePoster(context.Background(), "content-1", result) + + if result.PosterPath != "https://example.test/book.jpg" { + t.Fatalf("PosterPath = %q, want provider URL preserved", result.PosterPath) + } +} + +func TestCacheRemotePosterSkipsTypedNilCacher(t *testing.T) { + var cacher *fakeEbookImageCacher + e := &Enricher{imageCacher: cacher} + result := &metadata.MetadataResult{ + PosterPath: "https://example.test/book.jpg", + } + + e.cacheRemotePoster(context.Background(), "content-1", result) + + if result.PosterPath != "https://example.test/book.jpg" { + t.Fatalf("PosterPath = %q, want provider URL preserved", result.PosterPath) + } +} + +func TestCacheRemotePosterSkipsAlreadyCachedPath(t *testing.T) { + cacher := &fakeEbookImageCacher{} + e := &Enricher{imageCacher: cacher} + result := &metadata.MetadataResult{ + PosterPath: "local/ebooks/content-1/poster/original.webp", + } + + e.cacheRemotePoster(context.Background(), "content-1", result) + + if cacher.calls != 0 { + t.Fatalf("CacheImage calls = %d, want 0", cacher.calls) + } + if result.PosterPath != "local/ebooks/content-1/poster/original.webp" { + t.Fatalf("PosterPath = %q", result.PosterPath) + } +} + +func TestCacheRemotePosterPreservesProviderURLOnCacheError(t *testing.T) { + cacher := &fakeEbookImageCacher{err: errors.New("cache failed")} + e := &Enricher{imageCacher: cacher} + result := &metadata.MetadataResult{ + PosterPath: "https://example.test/book.jpg", + } + + e.cacheRemotePoster(context.Background(), "content-1", result) + + if cacher.calls != 1 { + t.Fatalf("CacheImage calls = %d, want 1", cacher.calls) + } + if result.PosterPath != "https://example.test/book.jpg" { + t.Fatalf("PosterPath = %q, want provider URL preserved", result.PosterPath) + } +} + +func TestCacheRemotePosterPreservesProviderURLOnNilCacheResult(t *testing.T) { + cacher := &fakeEbookImageCacher{returnNil: true} + e := &Enricher{imageCacher: cacher} + result := &metadata.MetadataResult{ + PosterPath: "https://example.test/book.jpg", + } + + e.cacheRemotePoster(context.Background(), "content-1", result) + + if cacher.calls != 1 { + t.Fatalf("CacheImage calls = %d, want 1", cacher.calls) + } + if result.PosterPath != "https://example.test/book.jpg" { + t.Fatalf("PosterPath = %q, want provider URL preserved", result.PosterPath) + } +} + +func TestMergeEnrichmentProviderIDsKeepsExistingIDs(t *testing.T) { + dst := &metadata.MetadataResult{ProviderIDs: map[string]string{"isbn": "9780306406157"}} + src := &metadata.MetadataResult{ProviderIDs: map[string]string{"isbn": "new", "openlibrary": "OL1M", "empty": ""}} + + mergeEnrichmentProviderIDs(dst, src) + + if got := dst.ProviderIDs["isbn"]; got != "9780306406157" { + t.Fatalf("isbn = %q, want original", got) + } + if got := dst.ProviderIDs["openlibrary"]; got != "OL1M" { + t.Fatalf("openlibrary = %q, want OL1M", got) + } + if _, exists := dst.ProviderIDs["empty"]; exists { + t.Fatal("empty provider ID should not be merged") + } +} + +func TestMergeEnrichmentProviderIDsDropsAsinIDs(t *testing.T) { + dst := &metadata.MetadataResult{ProviderIDs: map[string]string{"isbn": "9780306406157"}} + src := &metadata.MetadataResult{ + ProviderIDs: map[string]string{ + "ASIN": "B00TEST", + "audible_asin": "B00AUDIO", + "openlibrary": "OL1M", + }, + } + + mergeEnrichmentProviderIDs(dst, src) + + if _, exists := dst.ProviderIDs["ASIN"]; exists { + t.Fatal("ASIN provider ID should not be merged for ebooks") + } + if _, exists := dst.ProviderIDs["audible_asin"]; exists { + t.Fatal("audible_asin provider ID should not be merged for ebooks") + } + if got := dst.ProviderIDs["openlibrary"]; got != "OL1M" { + t.Fatalf("openlibrary = %q, want OL1M", got) + } +} + +func TestBuildEbookSearchQueryUsesFilteredScannerISBN(t *testing.T) { + item := enrichmentItemRow{ + Title: "Tagged Ebook", + Year: 2024, + Language: "en", + ProviderIDs: map[string]string{ + " ISBN ": " 9780306406157 ", + "ASIN": "B00TEST", + "audible_asin": "B00AUDIO", + }, + } + + query, ids := buildEbookSearchQuery(item) + + if query.Title != "Tagged Ebook" || query.Year != 2024 || query.Language != "en" || query.ContentType != "ebook" { + t.Fatalf("query basics = %+v", query) + } + if got := query.ProviderIDs["isbn"]; got != "9780306406157" { + t.Fatalf("query isbn = %q, want scanner ISBN", got) + } + if _, exists := query.ProviderIDs["ASIN"]; exists { + t.Fatal("query should not include ASIN for ebooks") + } + if got := ids["isbn"]; got != "9780306406157" { + t.Fatalf("accumulated isbn = %q, want scanner ISBN", got) + } +} + +func TestFilterEbookProviderIDsDropsAsinAliases(t *testing.T) { + got := filterEbookProviderIDs(map[string]string{ + "ASIN": "B00TEST", + "audibleASIN": "B00AUDIO", + "audible-asin": "B00AUDIO2", + "audible_asin": "B00AUDIO3", + " ISBN ": " 9780306406157 ", + " OpenLibraryID": " OL1M ", + }) + + if got["asin"] != "" || got["audibleasin"] != "" || got["audible-asin"] != "" || got["audible_asin"] != "" { + t.Fatalf("ASIN aliases should be filtered, got %+v", got) + } + if got["isbn"] != "9780306406157" || got["openlibraryid"] != "OL1M" { + t.Fatalf("filtered IDs = %+v, want ISBN and OpenLibraryID only", got) + } +} + +func TestBuildEbookMetadataRequestCarriesAccumulatedISBN(t *testing.T) { + req := buildEbookMetadataRequest(map[string]string{ + " ISBN ": " 9780306406157 ", + "openlibrary": "OL1M", + }, "fr") + + if req.ContentType != "ebook" || req.Language != "fr" { + t.Fatalf("request basics = %+v", req) + } + if got := req.ProviderIDs["isbn"]; got != "9780306406157" { + t.Fatalf("request isbn = %q, want normalized key/value", got) + } + if got := req.ProviderIDs["openlibrary"]; got != "OL1M" { + t.Fatalf("request openlibrary = %q, want OL1M", got) + } +} + +type fakeEbookMetadataProvider struct { + slug string + searchErr error + results []metadata.SearchResult + getErr error + result *metadata.MetadataResult +} + +func (f *fakeEbookMetadataProvider) Slug() string { return f.slug } +func (f *fakeEbookMetadataProvider) Name() string { return f.slug } +func (f *fakeEbookMetadataProvider) ForTypes() []string { return []string{"ebook"} } +func (f *fakeEbookMetadataProvider) Search(context.Context, metadata.SearchQuery) ([]metadata.SearchResult, error) { + return f.results, f.searchErr +} +func (f *fakeEbookMetadataProvider) GetMetadata(context.Context, metadata.MetadataRequest) (*metadata.MetadataResult, error) { + return f.result, f.getErr +} + +type fakeEbookImageCacher struct { + calls int + req metadata.CacheImageRequest + err error + returnNil bool +} + +func (f *fakeEbookImageCacher) CacheImage(_ context.Context, req metadata.CacheImageRequest) (*metadata.CacheImageResult, error) { + f.calls++ + f.req = req + if f.err != nil { + return nil, f.err + } + if f.returnNil { + return nil, nil + } + return &metadata.CacheImageResult{ + BasePath: req.ProviderID + "/" + req.ContentType + "/" + req.ContentID + "/poster", + Thumbhash: "thumb", + Ext: ".webp", + }, nil +} diff --git a/internal/imagecache/imagecache.go b/internal/imagecache/imagecache.go index 79472ad2..f4eb3171 100644 --- a/internal/imagecache/imagecache.go +++ b/internal/imagecache/imagecache.go @@ -114,6 +114,22 @@ func (c *Cacher) CacheAudiobookCover(ctx context.Context, data []byte, contentID return res.BasePath, res.Ext, res.Thumbhash, nil } +// CacheEbookCover stores an embedded ebook cover under +// "local/ebooks/{contentID}/poster/..." using the same poster variants as +// provider-hosted book artwork. +func (c *Cacher) CacheEbookCover(ctx context.Context, data []byte, contentID string) (basePath string, ext string, thumbhash string, err error) { + res, err := c.CacheBytes(ctx, data, CacheRequest{ + ProviderID: "local", + ContentType: "ebooks", + ContentID: contentID, + ImageType: metadata.ImagePoster, + }) + if err != nil { + return "", "", "", err + } + return res.BasePath, res.Ext, res.Thumbhash, nil +} + // CacheBytes performs the same variant generation, thumbhash, and S3 upload as // Cache but starts from raw image bytes already in hand. Used by the // audiobook scanner to push embedded M4B cover art into S3 without round- diff --git a/internal/jellycompat/web.go b/internal/jellycompat/web.go index 694de6cb..4900a924 100644 --- a/internal/jellycompat/web.go +++ b/internal/jellycompat/web.go @@ -44,6 +44,10 @@ func newCompatWebHandler(webFS fs.FS, version string) http.Handler { if version != "" { w.Header().Set("X-Silo-Jellyfin-Web-Version", version) } + // Block content sniffing on everything we serve here. A CSP is + // intentionally NOT set: the vendored jellyfin-web bundle relies on + // inline scripts and would break under any meaningful policy. + w.Header().Set("X-Content-Type-Options", "nosniff") if fileExists(webFS, relPath) { if relPath == "index.html" { diff --git a/internal/metadata/provider_id_integrity.go b/internal/metadata/provider_id_integrity.go index 2850968d..1d45acb6 100644 --- a/internal/metadata/provider_id_integrity.go +++ b/internal/metadata/provider_id_integrity.go @@ -722,6 +722,19 @@ var mediaItemMergeSteps = []mediaItemMergeStep{ last_codec_video = CASE WHEN EXCLUDED.updated_at >= user_watch_progress.updated_at THEN EXCLUDED.last_codec_video ELSE user_watch_progress.last_codec_video END, last_edition_key = CASE WHEN EXCLUDED.updated_at >= user_watch_progress.updated_at THEN EXCLUDED.last_edition_key ELSE user_watch_progress.last_edition_key END`}, {"delete source progress", `DELETE FROM user_watch_progress WHERE media_item_id = $1`}, + {"merge ebook reader progress", ` + INSERT INTO ebook_reader_progress ( + user_id, profile_id, content_id, file_id, location, progress, updated_at + ) + SELECT user_id, profile_id, $2, file_id, location, progress, updated_at + FROM ebook_reader_progress + WHERE content_id = $1 + ON CONFLICT (user_id, profile_id, content_id) DO UPDATE + SET file_id = CASE WHEN EXCLUDED.updated_at >= ebook_reader_progress.updated_at THEN EXCLUDED.file_id ELSE ebook_reader_progress.file_id END, + location = CASE WHEN EXCLUDED.updated_at >= ebook_reader_progress.updated_at THEN EXCLUDED.location ELSE ebook_reader_progress.location END, + progress = CASE WHEN EXCLUDED.updated_at >= ebook_reader_progress.updated_at THEN EXCLUDED.progress ELSE ebook_reader_progress.progress END, + updated_at = GREATEST(ebook_reader_progress.updated_at, EXCLUDED.updated_at)`}, + {"delete source ebook reader progress", `DELETE FROM ebook_reader_progress WHERE content_id = $1`}, {"move history", `UPDATE user_watch_history SET media_item_id = $2 WHERE media_item_id = $1`}, {"merge hidden history", ` INSERT INTO user_history_hidden_items (user_id, profile_id, media_item_id, hidden_before, updated_at) diff --git a/internal/metadata/provider_id_integrity_test.go b/internal/metadata/provider_id_integrity_test.go index ef2ad10f..12441e8a 100644 --- a/internal/metadata/provider_id_integrity_test.go +++ b/internal/metadata/provider_id_integrity_test.go @@ -110,6 +110,25 @@ func TestMergeStepPlaceholdersAreBounded(t *testing.T) { } } +func TestMergeStepsPreserveEbookReaderProgress(t *testing.T) { + var hasMerge bool + var hasDelete bool + for _, step := range mediaItemMergeSteps { + if strings.Contains(step.sql, "ebook_reader_progress") && strings.Contains(step.sql, "INSERT INTO") { + hasMerge = true + } + if strings.Contains(step.sql, "ebook_reader_progress") && strings.Contains(step.sql, "DELETE FROM") { + hasDelete = true + } + } + if !hasMerge { + t.Fatal("media item merge steps should merge ebook_reader_progress rows") + } + if !hasDelete { + t.Fatal("media item merge steps should delete source ebook_reader_progress rows") + } +} + func TestBothCandidatesHaveContentDetectsRealUserData(t *testing.T) { withFiles := canonicalCandidate{ContentID: "a", ActiveFiles: 2} withLibraries := canonicalCandidate{ContentID: "b", LibraryCount: 1} diff --git a/internal/models/ebook_progress.go b/internal/models/ebook_progress.go new file mode 100644 index 00000000..0e249595 --- /dev/null +++ b/internal/models/ebook_progress.go @@ -0,0 +1,8 @@ +package models + +// EbookFinishedProgressThreshold is the reading-progress ratio (0..1) at or +// above which an ebook counts as finished/read. Progress strictly between 0 +// and this threshold counts as in progress. Shared by the reader API, +// catalog query builder, sections, and recommendation signals so the +// definition of "finished" cannot drift between subsystems. +const EbookFinishedProgressThreshold = 0.9 diff --git a/internal/recommendations/accuracy_test.go b/internal/recommendations/accuracy_test.go index 6d5c86b9..20a30f18 100644 --- a/internal/recommendations/accuracy_test.go +++ b/internal/recommendations/accuracy_test.go @@ -142,6 +142,32 @@ func TestApplyMediaTypeFloorNoopsWithoutSupplementalType(t *testing.T) { } } +func TestApplyMediaTypeFloorIncludesEbookSupplement(t *testing.T) { + items := []ScoredItem{ + {MediaItemID: "s1", Score: 1.00}, + {MediaItemID: "s2", Score: 0.99}, + {MediaItemID: "s3", Score: 0.98}, + {MediaItemID: "s4", Score: 0.97}, + {MediaItemID: "s5", Score: 0.96}, + } + candidates := append([]ScoredItem(nil), items...) + candidates = append(candidates, ScoredItem{MediaItemID: "e1", Score: 0.95}) + mediaTypes := map[string]string{ + "s1": "series", + "s2": "series", + "s3": "series", + "s4": "series", + "s5": "series", + "e1": "ebook", + } + + mixed := applyMediaTypeFloor(items, candidates, mediaTypes) + + if got := countMediaType(mixed, mediaTypes, "ebook"); got != 1 { + t.Fatalf("expected 1 ebook from supplemental candidates, got %d in %#v", got, mixed) + } +} + func TestApplyGenreCapDoesNotCollapseConcentratedRows(t *testing.T) { items := []ScoredItem{ {MediaItemID: "a", Score: 1.0}, diff --git a/internal/recommendations/embeddings/text.go b/internal/recommendations/embeddings/text.go index bd15e4a5..faa884dc 100644 --- a/internal/recommendations/embeddings/text.go +++ b/internal/recommendations/embeddings/text.go @@ -63,17 +63,20 @@ func BuildEmbeddingText(item *models.MediaItem) string { parts = append(parts, fmt.Sprintf(`"%s"`, item.Tagline)) } - if item.Type == "audiobook" { - // Audiobooks credit author (kind=7) and narrator (kind=8). Cast/ - // director/writer don't apply. Keep the SQL canonical_text builder - // in recommendations/repo.go in sync with this shape. + if item.Type == "audiobook" || item.Type == "ebook" { + // Books credit authors (kind=7). Audiobooks also credit narrators + // (kind=8); ebooks do not. Cast/director/writer don't apply. Keep + // the SQL canonical_text builder in recommendations/repo.go in sync + // with this shape. var authors, narrators []models.ItemPerson for _, p := range item.People { switch p.Kind { case models.PersonKindAuthor: authors = append(authors, p) case models.PersonKindNarrator: - narrators = append(narrators, p) + if item.Type == "audiobook" { + narrators = append(narrators, p) + } } } sortItemPeople(authors) @@ -174,6 +177,8 @@ func mediaTypeLabel(t string) string { return "TV series" case "audiobook": return "audiobook" + case "ebook": + return "ebook" default: return "movie" } diff --git a/internal/recommendations/embeddings/text_test.go b/internal/recommendations/embeddings/text_test.go index b23b303b..3a90e034 100644 --- a/internal/recommendations/embeddings/text_test.go +++ b/internal/recommendations/embeddings/text_test.go @@ -61,6 +61,42 @@ func TestBuildEmbeddingTextAudiobookUsesAuthorAndNarrator(t *testing.T) { } } +func TestBuildEmbeddingTextEbookUsesAuthorWithoutNarratorOrCast(t *testing.T) { + item := &models.MediaItem{ + Title: "A Wizard of Earthsea", + Type: "ebook", + Year: 1968, + Genres: []string{"Fantasy"}, + Overview: "Ged learns the cost of power.", + People: []models.ItemPerson{ + {Person: models.Person{ID: 1, Name: "Ursula K. Le Guin"}, Kind: models.PersonKindAuthor}, + {Person: models.Person{ID: 2, Name: "Narrator Should Not Apply"}, Kind: models.PersonKindNarrator}, + {Person: models.Person{ID: 3, Name: "Actor Should Not Apply"}, Kind: models.PersonKindActor, Character: "Ged"}, + {Person: models.Person{ID: 4, Name: "Writer Should Not Apply"}, Kind: models.PersonKindWriter}, + }, + } + + text := BuildEmbeddingText(item) + + if !strings.Contains(text, "Fantasy ebook about Ged learns the cost of power.") { + t.Fatalf("ebook lede missing or wrong: %q", text) + } + if !strings.Contains(text, "Written by Ursula K. Le Guin") { + t.Fatalf("ebook author missing: %q", text) + } + for _, unwanted := range []string{ + "Narrated by", + "Narrator Should Not Apply", + "Cast:", + "Actor Should Not Apply", + "Writer Should Not Apply", + } { + if strings.Contains(text, unwanted) { + t.Fatalf("ebook text should not contain %q: %q", unwanted, text) + } + } +} + func TestBuildEmbeddingTextSortsActorsBeforeTopFive(t *testing.T) { item := &models.MediaItem{ Title: "Example", diff --git a/internal/recommendations/personal.go b/internal/recommendations/personal.go index a35919c7..d51ac658 100644 --- a/internal/recommendations/personal.go +++ b/internal/recommendations/personal.go @@ -11,7 +11,7 @@ import ( const aggregateMediaTypeFloorDivisor = 5 -var aggregateSupplementMediaTypes = []string{"movie", "series", "audiobook"} +var aggregateSupplementMediaTypes = []string{"movie", "series", "audiobook", "ebook"} // ForYou returns personalised recommendations grouped by taste clusters. // For cold-start users, non-personalized rows are returned. diff --git a/internal/recommendations/repo.go b/internal/recommendations/repo.go index b4183e10..21c05c1a 100644 --- a/internal/recommendations/repo.go +++ b/internal/recommendations/repo.go @@ -39,21 +39,63 @@ func ensureCanonicalDimensions(vec []float32) ([]float32, error) { const embeddingLockSettingKey = "recommendations.embedding_lock" const minHNSWEfSearch = 200 -const tasteSeedCandidateQuery = ` - WITH watched_counts AS ( - SELECT COALESCE(e.series_id, wp.media_item_id) AS item_id, - COUNT(DISTINCT wp.user_id::text || ':' || COALESCE(wp.profile_id, '')) AS watch_count - FROM user_watch_progress wp - LEFT JOIN episodes e ON e.content_id = wp.media_item_id - WHERE (wp.completed = true OR (wp.duration_seconds > 0 AND wp.position_seconds / wp.duration_seconds >= 0.5)) - AND wp.updated_at > NOW() - INTERVAL '180 days' - GROUP BY 1 +// watchedActivityCTE unifies video watch progress and ebook reader progress +// into one activity stream. Episodes roll up to their parent series via +// COALESCE(e.series_id, ...). The ebook completed flag is derived from the +// shared finished-progress threshold. +var watchedActivityCTE = fmt.Sprintf(` +watched_activity AS ( + SELECT COALESCE(e.series_id, wp.media_item_id) AS item_id, + wp.media_item_id AS leaf_item_id, + wp.user_id, + wp.profile_id, + wp.user_id::text || ':' || COALESCE(wp.profile_id, '') AS watcher_id, + wp.updated_at, + wp.completed AS completed + FROM user_watch_progress wp + LEFT JOIN episodes e ON e.content_id = wp.media_item_id + WHERE (wp.completed = true OR (wp.duration_seconds > 0 AND wp.position_seconds / wp.duration_seconds >= 0.5)) + AND NOT EXISTS ( + SELECT 1 + FROM user_history_hidden_items hhi + WHERE hhi.user_id = wp.user_id + AND hhi.profile_id = wp.profile_id + AND hhi.media_item_id = wp.media_item_id + AND wp.updated_at <= hhi.hidden_before + ) + UNION ALL + SELECT erp.content_id AS item_id, + erp.content_id AS leaf_item_id, + erp.user_id, + erp.profile_id, + erp.user_id::text || ':' || COALESCE(erp.profile_id, '') AS watcher_id, + erp.updated_at, + erp.progress >= %s AS completed + FROM ebook_reader_progress erp + WHERE erp.progress >= 0.5 + AND NOT EXISTS ( + SELECT 1 + FROM user_history_hidden_items hhi + WHERE hhi.user_id = erp.user_id + AND hhi.profile_id = erp.profile_id + AND hhi.media_item_id = erp.content_id + AND erp.updated_at <= hhi.hidden_before + ) +)`, catalog.EbookFinishedProgressThresholdSQL) + +var tasteSeedCandidateQuery = fmt.Sprintf(` + WITH %s, + watched_counts AS ( + SELECT item_id, COUNT(DISTINCT watcher_id) AS watch_count + FROM watched_activity + WHERE updated_at > NOW() - INTERVAL '180 days' + GROUP BY item_id ) SELECT mi.content_id FROM media_items mi LEFT JOIN watched_counts wc ON wc.item_id = mi.content_id - WHERE (mi.status = 'matched' OR mi.type = 'audiobook') - AND mi.type IN ('movie', 'series', 'audiobook') + WHERE %s + AND mi.type IN ('movie', 'series', 'audiobook', 'ebook') AND mi.poster_path IS NOT NULL AND mi.poster_path <> '' ORDER BY COALESCE(wc.watch_count, 0) DESC, @@ -66,7 +108,7 @@ const tasteSeedCandidateQuery = ` CASE WHEN mi.rating_tmdb < 9.5 THEN mi.rating_tmdb END DESC NULLS LAST, mi.year DESC NULLS LAST, mi.content_id ASC - LIMIT $1 OFFSET $2` + LIMIT $1 OFFSET $2`, watchedActivityCTE, recommendationItemEligibilityWhereClause("mi")) // Repo provides database operations for the recommendation system. type Repo struct { @@ -120,6 +162,18 @@ type EmbeddingTextCandidate struct { CanonicalText string } +func embeddingEligibilityWhereClause() string { + return recommendationItemEligibilityWhereClause("mi") +} + +func recommendationItemEligibilityWhereClause(alias string) string { + alias = strings.TrimSpace(alias) + if alias == "" { + alias = "media_items" + } + return fmt.Sprintf("(%s.status = 'matched' OR %s.type = 'audiobook' OR %s.type = 'ebook')", alias, alias, alias) +} + // UpsertEmbedding stores or updates an embedding for a media item. func (r *Repo) UpsertEmbedding(ctx context.Context, itemID string, embedding []float32, model, canonicalText string) error { padded, err := ensureCanonicalDimensions(embedding) @@ -416,18 +470,18 @@ func (r *Repo) findTasteProfileCandidates( // ItemsNeedingEmbedding returns content IDs of media items that either have no // embedding or whose embedding was generated with a different model. -// Audiobooks bypass the status='matched' gate because they don't go through -// the external-provider match pipeline; their tag-derived metadata is -// authoritative as soon as the scan completes. +// Books bypass the status='matched' gate because their scanner/plugin-derived +// metadata is authoritative as soon as the scan/enrichment completes. func (r *Repo) ItemsNeedingEmbedding(ctx context.Context, currentModel string, limit int) ([]string, error) { - rows, err := r.pool.Query(ctx, ` + query := fmt.Sprintf(` SELECT mi.content_id FROM media_items mi LEFT JOIN media_item_embeddings e ON e.media_item_id = mi.content_id - WHERE (mi.status = 'matched' OR mi.type = 'audiobook') + WHERE %s AND (e.media_item_id IS NULL OR e.model != $1) LIMIT $2 - `, currentModel, limit) + `, embeddingEligibilityWhereClause()) + rows, err := r.pool.Query(ctx, query, currentModel, limit) if err != nil { return nil, fmt.Errorf("items needing embedding: %w", err) } @@ -448,11 +502,11 @@ func (r *Repo) ItemsNeedingEmbedding(ctx context.Context, currentModel string, l } func (r *Repo) ListEmbeddingTextCandidates(ctx context.Context, afterID, currentModel string, limit int) ([]EmbeddingTextCandidate, error) { - rows, err := r.pool.Query(ctx, ` + query := fmt.Sprintf(` -- Keep current_text in sync with embeddings.BuildEmbeddingText. This lets -- the embedding job page over only missing, model-stale, or text-stale rows. - -- Audiobook lines map to embeddings.mediaTypeLabel + the author/narrator - -- branch in BuildEmbeddingText; any divergence here forces every audiobook + -- Book lines map to embeddings.mediaTypeLabel + the author/narrator + -- branch in BuildEmbeddingText; any divergence here forces book items -- to re-embed every job run. WITH text_candidates AS ( SELECT mi.content_id, @@ -461,11 +515,11 @@ func (r *Repo) ListEmbeddingTextCandidates(ctx context.Context, afterID, current array_to_string(array_remove(ARRAY[ CASE WHEN cardinality(COALESCE(mi.genres, ARRAY[]::text[])) > 0 AND COALESCE(mi.overview, '') <> '' - THEN array_to_string(mi.genres, ', ') || ' ' || CASE WHEN mi.type = 'series' THEN 'TV series' WHEN mi.type = 'audiobook' THEN 'audiobook' ELSE 'movie' END || ' about ' || substr(mi.overview, 1, 1000) + THEN array_to_string(mi.genres, ', ') || ' ' || CASE WHEN mi.type = 'series' THEN 'TV series' WHEN mi.type = 'audiobook' THEN 'audiobook' WHEN mi.type = 'ebook' THEN 'ebook' ELSE 'movie' END || ' about ' || substr(mi.overview, 1, 1000) WHEN cardinality(COALESCE(mi.genres, ARRAY[]::text[])) > 0 - THEN array_to_string(mi.genres, ', ') || ' ' || CASE WHEN mi.type = 'series' THEN 'TV series' WHEN mi.type = 'audiobook' THEN 'audiobook' ELSE 'movie' END + THEN array_to_string(mi.genres, ', ') || ' ' || CASE WHEN mi.type = 'series' THEN 'TV series' WHEN mi.type = 'audiobook' THEN 'audiobook' WHEN mi.type = 'ebook' THEN 'ebook' ELSE 'movie' END WHEN COALESCE(mi.overview, '') <> '' - THEN CASE WHEN mi.type = 'series' THEN 'TV series' WHEN mi.type = 'audiobook' THEN 'audiobook' ELSE 'movie' END || '. ' || substr(mi.overview, 1, 1000) + THEN CASE WHEN mi.type = 'series' THEN 'TV series' WHEN mi.type = 'audiobook' THEN 'audiobook' WHEN mi.type = 'ebook' THEN 'ebook' ELSE 'movie' END || '. ' || substr(mi.overview, 1, 1000) ELSE NULL END, CASE WHEN COALESCE(mi.year, 0) > 0 @@ -474,11 +528,11 @@ func (r *Repo) ListEmbeddingTextCandidates(ctx context.Context, afterID, current END, CASE WHEN COALESCE(mi.content_rating, '') <> '' THEN 'Rated ' || mi.content_rating ELSE NULL END, CASE WHEN COALESCE(mi.tagline, '') <> '' THEN '"' || mi.tagline || '"' ELSE NULL END, - CASE WHEN mi.type <> 'audiobook' AND actors.names <> '' THEN 'Cast: ' || actors.names ELSE NULL END, - CASE WHEN mi.type <> 'audiobook' AND directors.names <> '' THEN 'Directed by ' || directors.names ELSE NULL END, + CASE WHEN mi.type NOT IN ('audiobook', 'ebook') AND actors.names <> '' THEN 'Cast: ' || actors.names ELSE NULL END, + CASE WHEN mi.type NOT IN ('audiobook', 'ebook') AND directors.names <> '' THEN 'Directed by ' || directors.names ELSE NULL END, CASE - WHEN mi.type = 'audiobook' AND authors.names <> '' THEN 'Written by ' || authors.names - WHEN mi.type <> 'audiobook' AND writers.names <> '' THEN 'Written by ' || writers.names + WHEN mi.type IN ('audiobook', 'ebook') AND authors.names <> '' THEN 'Written by ' || authors.names + WHEN mi.type NOT IN ('audiobook', 'ebook') AND writers.names <> '' THEN 'Written by ' || writers.names ELSE NULL END, CASE WHEN mi.type = 'audiobook' AND narrators.names <> '' THEN 'Narrated by ' || narrators.names ELSE NULL END, @@ -536,7 +590,7 @@ func (r *Repo) ListEmbeddingTextCandidates(ctx context.Context, afterID, current WHERE ip.content_id = mi.content_id AND ip.kind = 8 ) narrators ON TRUE - WHERE (mi.status = 'matched' OR mi.type = 'audiobook') + WHERE %s AND ($1 = '' OR mi.content_id > $1) ) SELECT mi.content_id, @@ -548,7 +602,8 @@ func (r *Repo) ListEmbeddingTextCandidates(ctx context.Context, afterID, current OR mi.canonical_text IS DISTINCT FROM mi.current_text ORDER BY mi.content_id LIMIT $3 - `, afterID, currentModel, limit) + `, embeddingEligibilityWhereClause()) + rows, err := r.pool.Query(ctx, query, afterID, currentModel, limit) if err != nil { return nil, fmt.Errorf("embedding text candidates: %w", err) } @@ -583,7 +638,8 @@ func (r *Repo) EmbeddingCount(ctx context.Context) (int, error) { // (used by the admin worker UI) lines up with what the job actually does. func (r *Repo) TotalMediaItemCount(ctx context.Context) (int, error) { var count int - err := r.pool.QueryRow(ctx, `SELECT COUNT(*) FROM media_items WHERE status = 'matched' OR type = 'audiobook'`).Scan(&count) + query := fmt.Sprintf(`SELECT COUNT(*) FROM media_items mi WHERE %s`, embeddingEligibilityWhereClause()) + err := r.pool.QueryRow(ctx, query).Scan(&count) if err != nil { return 0, fmt.Errorf("total media item count: %w", err) } @@ -1137,6 +1193,46 @@ func (r *Repo) GetWatchProgressForUser(ctx context.Context, userID int, profileI return result, rows.Err() } +// ebookReaderProgressForUserQuery maps ebook reader progress onto the +// WatchProgressRow shape (progress as position with duration 1). Rows hidden +// via user_history_hidden_items are excluded with the same semantics as +// GetWatchProgressForUser: hidden when updated_at <= hidden_before, visible +// again once new reading activity lands after hidden_before. +var ebookReaderProgressForUserQuery = fmt.Sprintf(` + SELECT content_id, + progress::double precision AS position_seconds, + 1::double precision AS duration_seconds, + progress >= %s AS completed, + updated_at + FROM ebook_reader_progress + WHERE user_id = $1 AND profile_id = $2 + AND NOT EXISTS ( + SELECT 1 + FROM user_history_hidden_items hhi + WHERE hhi.user_id = ebook_reader_progress.user_id + AND hhi.profile_id = ebook_reader_progress.profile_id + AND hhi.media_item_id = ebook_reader_progress.content_id + AND ebook_reader_progress.updated_at <= hhi.hidden_before + )`, catalog.EbookFinishedProgressThresholdSQL) + +func (r *Repo) GetEbookReaderProgressForUser(ctx context.Context, userID int, profileID string) ([]WatchProgressRow, error) { + rows, err := r.pool.Query(ctx, ebookReaderProgressForUserQuery, userID, profileID) + if err != nil { + return nil, fmt.Errorf("get ebook reader progress: %w", err) + } + defer rows.Close() + + var result []WatchProgressRow + for rows.Next() { + var wp WatchProgressRow + if err := rows.Scan(&wp.MediaItemID, &wp.PositionSeconds, &wp.DurationSeconds, &wp.Completed, &wp.UpdatedAt); err != nil { + return nil, fmt.Errorf("scan ebook reader progress: %w", err) + } + result = append(result, wp) + } + return result, rows.Err() +} + // RewatchCount holds the number of completed watches for an item. type RewatchCount struct { MediaItemID string @@ -1177,33 +1273,38 @@ func (r *Repo) GetRewatchCounts(ctx context.Context, userID int, profileID strin return result, rows.Err() } +// itemWatchersQuery feeds co-watch matrix computation. watched_activity rolls +// episodes up to their parent series, so a single watcher can produce many +// rows for the same item; the GROUP BY collapses them to one row per +// (watcher, item) before ranking and aggregation so a lone binge-watcher +// cannot satisfy the minimum-watchers threshold ($2) by itself and the +// per-user recency cap ($1) counts distinct items rather than raw rows. +// +// Watcher identity is (user_id, profile_id) for the Jaccard math — profiles of +// one account legitimately have distinct tastes — but the minimum-watchers +// floor ($2) is a sparsity/privacy threshold and must count distinct login +// ACCOUNTS: one household account with N profiles must not satisfy it alone, +// so the HAVING clause counts DISTINCT user_id rather than watcher rows. +var itemWatchersQuery = fmt.Sprintf(` + WITH %s, + user_watches AS ( + SELECT user_id, + watcher_id, + item_id AS media_item_id, + ROW_NUMBER() OVER (PARTITION BY user_id, profile_id ORDER BY MAX(updated_at) DESC) AS rn + FROM watched_activity + GROUP BY user_id, profile_id, watcher_id, item_id + ) + SELECT media_item_id, ARRAY_AGG(watcher_id) AS watchers + FROM user_watches + WHERE rn <= $1 + GROUP BY media_item_id + HAVING COUNT(DISTINCT user_id) >= $2`, watchedActivityCTE) + // ItemWatchers maps item_id to profile identities that watched >= 50% progress. // Used for co-watch matrix computation. func (r *Repo) GetItemWatchers(ctx context.Context, minWatchers int, maxPerUser int) (map[string][]string, error) { - // Get all watch progress entries where progress >= 50% or completed - rows, err := r.pool.Query(ctx, ` - WITH user_watches AS ( - SELECT user_id::text || ':' || COALESCE(profile_id, '') AS watcher_id, - media_item_id, - ROW_NUMBER() OVER (PARTITION BY user_id, profile_id ORDER BY updated_at DESC) AS rn - FROM user_watch_progress - WHERE (completed = true - OR (duration_seconds > 0 AND position_seconds / duration_seconds >= 0.5)) - AND NOT EXISTS ( - SELECT 1 - FROM user_history_hidden_items hhi - WHERE hhi.user_id = user_watch_progress.user_id - AND hhi.profile_id = user_watch_progress.profile_id - AND hhi.media_item_id = user_watch_progress.media_item_id - AND user_watch_progress.updated_at <= hhi.hidden_before - ) - ) - SELECT media_item_id, ARRAY_AGG(watcher_id) AS watchers - FROM user_watches - WHERE rn <= $1 - GROUP BY media_item_id - HAVING COUNT(*) >= $2`, - maxPerUser, minWatchers) + rows, err := r.pool.Query(ctx, itemWatchersQuery, maxPerUser, minWatchers) if err != nil { return nil, fmt.Errorf("get item watchers: %w", err) } @@ -1226,30 +1327,20 @@ func (r *Repo) GetItemWatchers(ctx context.Context, minWatchers int, maxPerUser // GetPopularItems returns the most-played media items over the given number of days. // Episodes are resolved to their parent series. func (r *Repo) GetPopularItems(ctx context.Context, days, limit int) ([]ScoredItem, error) { - rows, err := r.pool.Query(ctx, ` - WITH watched_items AS ( - SELECT COALESCE(e.series_id, wp.media_item_id) AS item_id, - wp.user_id::text || ':' || COALESCE(wp.profile_id, '') AS watcher_id - FROM user_watch_progress wp - LEFT JOIN episodes e ON e.content_id = wp.media_item_id - WHERE (wp.completed = true OR (wp.duration_seconds > 0 AND wp.position_seconds / wp.duration_seconds >= 0.5)) - AND wp.updated_at > NOW() - ($1 || ' days')::interval - AND NOT EXISTS ( - SELECT 1 - FROM user_history_hidden_items hhi - WHERE hhi.user_id = wp.user_id - AND hhi.profile_id = wp.profile_id - AND hhi.media_item_id = wp.media_item_id - AND wp.updated_at <= hhi.hidden_before - ) + query := fmt.Sprintf(` + WITH %s, + watched_items AS ( + SELECT item_id, watcher_id + FROM watched_activity + WHERE updated_at > NOW() - ($1 || ' days')::interval ) SELECT wi.item_id, COUNT(DISTINCT wi.watcher_id) AS watch_count FROM watched_items wi JOIN media_items mi ON mi.content_id = wi.item_id GROUP BY wi.item_id ORDER BY watch_count DESC - LIMIT $2`, - fmt.Sprintf("%d", days), limit) + LIMIT $2`, watchedActivityCTE) + rows, err := r.pool.Query(ctx, query, fmt.Sprintf("%d", days), limit) if err != nil { return nil, fmt.Errorf("get popular items: %w", err) } @@ -1270,17 +1361,18 @@ func (r *Repo) GetPopularItems(ctx context.Context, days, limit int) ([]ScoredIt } // GetRecentlyAddedItems returns items added within the given number of days. -// Audiobooks bypass the matched-status gate because their scan-derived metadata -// is authoritative before any external-provider match exists. +// Audiobooks and ebooks bypass the matched-status gate because their +// scan-derived metadata is authoritative before any external-provider match +// exists. func (r *Repo) GetRecentlyAddedItems(ctx context.Context, days, limit int) ([]ScoredItem, error) { - rows, err := r.pool.Query(ctx, ` - SELECT content_id, created_at - FROM media_items - WHERE (status = 'matched' OR type = 'audiobook') - AND created_at > NOW() - ($1 || ' days')::interval - ORDER BY created_at DESC - LIMIT $2`, - fmt.Sprintf("%d", days), limit) + query := fmt.Sprintf(` + SELECT mi.content_id, mi.created_at + FROM media_items mi + WHERE %s + AND mi.created_at > NOW() - ($1 || ' days')::interval + ORDER BY mi.created_at DESC + LIMIT $2`, recommendationItemEligibilityWhereClause("mi")) + rows, err := r.pool.Query(ctx, query, fmt.Sprintf("%d", days), limit) if err != nil { return nil, fmt.Errorf("get recently added: %w", err) } @@ -1354,21 +1446,11 @@ func (r *Repo) GetTasteSeedCandidates(ctx context.Context, limit, offset int) ([ // GetTopGenres returns the most popular genres by watch count. // Episodes are resolved to their parent series for genre lookup. func (r *Repo) GetTopGenres(ctx context.Context, limit int) ([]string, error) { - rows, err := r.pool.Query(ctx, ` - WITH watched_items AS ( - SELECT COALESCE(e.series_id, wp.media_item_id) AS item_id, - wp.user_id::text || ':' || COALESCE(wp.profile_id, '') AS watcher_id - FROM user_watch_progress wp - LEFT JOIN episodes e ON e.content_id = wp.media_item_id - WHERE (wp.completed = true OR (wp.duration_seconds > 0 AND wp.position_seconds / wp.duration_seconds >= 0.5)) - AND NOT EXISTS ( - SELECT 1 - FROM user_history_hidden_items hhi - WHERE hhi.user_id = wp.user_id - AND hhi.profile_id = wp.profile_id - AND hhi.media_item_id = wp.media_item_id - AND wp.updated_at <= hhi.hidden_before - ) + query := fmt.Sprintf(` + WITH %s, + watched_items AS ( + SELECT item_id, watcher_id + FROM watched_activity ) SELECT g.genre, COUNT(DISTINCT wi.watcher_id) AS watchers FROM watched_items wi @@ -1376,8 +1458,8 @@ func (r *Repo) GetTopGenres(ctx context.Context, limit int) ([]string, error) { CROSS JOIN LATERAL UNNEST(mi.genres) AS g(genre) GROUP BY g.genre ORDER BY watchers DESC - LIMIT $1`, - limit) + LIMIT $1`, watchedActivityCTE) + rows, err := r.pool.Query(ctx, query, limit) if err != nil { return nil, fmt.Errorf("get top genres: %w", err) } @@ -1397,21 +1479,11 @@ func (r *Repo) GetTopGenres(ctx context.Context, limit int) ([]string, error) { // GetGenreSamplerItems returns the most-played media items in a specific genre. func (r *Repo) GetGenreSamplerItems(ctx context.Context, genre string, limit int) ([]ScoredItem, error) { - rows, err := r.pool.Query(ctx, ` - WITH watched_items AS ( - SELECT COALESCE(e.series_id, wp.media_item_id) AS item_id, - wp.user_id::text || ':' || COALESCE(wp.profile_id, '') AS watcher_id - FROM user_watch_progress wp - LEFT JOIN episodes e ON e.content_id = wp.media_item_id - WHERE (wp.completed = true OR (wp.duration_seconds > 0 AND wp.position_seconds / wp.duration_seconds >= 0.5)) - AND NOT EXISTS ( - SELECT 1 - FROM user_history_hidden_items hhi - WHERE hhi.user_id = wp.user_id - AND hhi.profile_id = wp.profile_id - AND hhi.media_item_id = wp.media_item_id - AND wp.updated_at <= hhi.hidden_before - ) + query := fmt.Sprintf(` + WITH %s, + watched_items AS ( + SELECT item_id, watcher_id + FROM watched_activity ) SELECT wi.item_id, COUNT(DISTINCT wi.watcher_id) AS watch_count FROM watched_items wi @@ -1419,8 +1491,8 @@ func (r *Repo) GetGenreSamplerItems(ctx context.Context, genre string, limit int WHERE $1 = ANY(mi.genres) GROUP BY wi.item_id ORDER BY watch_count DESC - LIMIT $2`, - genre, limit) + LIMIT $2`, watchedActivityCTE) + rows, err := r.pool.Query(ctx, query, genre, limit) if err != nil { return nil, fmt.Errorf("get genre sampler items: %w", err) } @@ -1619,21 +1691,12 @@ func (r *Repo) GetAllUsersWithTasteProfiles(ctx context.Context) ([]StaleProfile // GetWatchedItemIDs returns content IDs of media items the user has watched // (>= 50% progress or completed). Episodes are resolved to their parent series. func (r *Repo) GetWatchedItemIDs(ctx context.Context, userID int, profileID string) ([]string, error) { - rows, err := r.pool.Query(ctx, ` - SELECT DISTINCT COALESCE(e.series_id, wp.media_item_id) AS item_id - FROM user_watch_progress wp - LEFT JOIN episodes e ON e.content_id = wp.media_item_id - WHERE wp.user_id = $1 AND wp.profile_id = $2 - AND (wp.completed = true OR (wp.duration_seconds > 0 AND wp.position_seconds / wp.duration_seconds >= 0.5)) - AND NOT EXISTS ( - SELECT 1 - FROM user_history_hidden_items hhi - WHERE hhi.user_id = wp.user_id - AND hhi.profile_id = wp.profile_id - AND hhi.media_item_id = wp.media_item_id - AND wp.updated_at <= hhi.hidden_before - )`, - userID, profileID) + query := fmt.Sprintf(` + WITH %s + SELECT DISTINCT item_id + FROM watched_activity + WHERE user_id = $1 AND profile_id = $2`, watchedActivityCTE) + rows, err := r.pool.Query(ctx, query, userID, profileID) if err != nil { return nil, fmt.Errorf("get watched item IDs: %w", err) } @@ -1697,21 +1760,15 @@ func (r *Repo) GetRecentCompletedItemIDs(ctx context.Context, userID int, profil return []string{}, nil } - rows, err := r.pool.Query(ctx, ` - SELECT media_item_id - FROM user_watch_progress + query := fmt.Sprintf(` + WITH %s + SELECT leaf_item_id + FROM watched_activity WHERE user_id = $1 AND profile_id = $2 AND completed = true - AND NOT EXISTS ( - SELECT 1 - FROM user_history_hidden_items hhi - WHERE hhi.user_id = user_watch_progress.user_id - AND hhi.profile_id = user_watch_progress.profile_id - AND hhi.media_item_id = user_watch_progress.media_item_id - AND user_watch_progress.updated_at <= hhi.hidden_before - ) - ORDER BY updated_at DESC, media_item_id ASC + ORDER BY updated_at DESC, leaf_item_id ASC LIMIT $3 - `, userID, profileID, limit) + `, watchedActivityCTE) + rows, err := r.pool.Query(ctx, query, userID, profileID, limit) if err != nil { return nil, fmt.Errorf("get recent completed item IDs: %w", err) } diff --git a/internal/recommendations/repo_test.go b/internal/recommendations/repo_test.go index da5e52ca..a67d84dd 100644 --- a/internal/recommendations/repo_test.go +++ b/internal/recommendations/repo_test.go @@ -24,6 +24,128 @@ func TestTasteSeedCandidateQueryOrdersByReliableColdStartSignals(t *testing.T) { } } +func TestTasteSeedCandidateQueryIncludesEbooks(t *testing.T) { + query := strings.Join(strings.Fields(tasteSeedCandidateQuery), " ") + + for _, term := range []string{ + "(mi.status = 'matched' OR mi.type = 'audiobook' OR mi.type = 'ebook')", + "mi.type IN ('movie', 'series', 'audiobook', 'ebook')", + } { + if !strings.Contains(query, term) { + t.Fatalf("taste seed candidate query missing %q: %s", term, query) + } + } +} + +func TestItemWatchersQueryCountsDistinctWatchersPerItem(t *testing.T) { + query := strings.Join(strings.Fields(itemWatchersQuery), " ") + + // watched_activity rolls episodes up to their parent series, so one + // watcher can emit many rows per item. The pairs must be deduplicated + // before ROW_NUMBER ranking and the HAVING watcher count, otherwise a + // single binge-watcher inflates both. + assertQueryTermsInOrder(t, query, + "ROW_NUMBER() OVER (PARTITION BY user_id, profile_id ORDER BY MAX(updated_at) DESC) AS rn", + "GROUP BY user_id, profile_id, watcher_id, item_id", + "WHERE rn <= $1", + "GROUP BY media_item_id", + "HAVING COUNT(DISTINCT user_id) >= $2", + ) +} + +func TestItemWatchersQueryMinWatchersCountsDistinctAccounts(t *testing.T) { + query := strings.Join(strings.Fields(itemWatchersQuery), " ") + + // The minimum-watchers floor is a sparsity/privacy threshold: one login + // account with N household profiles must not satisfy it by itself, so the + // HAVING clause counts distinct user_id (accounts), not watcher rows. The + // per-profile watcher_id array is still aggregated for Jaccard math. + assertQueryTermsInOrder(t, query, + "SELECT user_id, watcher_id, item_id AS media_item_id", + "ARRAY_AGG(watcher_id) AS watchers", + "HAVING COUNT(DISTINCT user_id) >= $2", + ) + if strings.Contains(query, "HAVING COUNT(*) >= $2") { + t.Fatal("minWatchers floor must not count per-profile watcher rows") + } +} + +func TestEbookReaderProgressForUserQueryGatesHiddenHistory(t *testing.T) { + query := strings.Join(strings.Fields(ebookReaderProgressForUserQuery), " ") + + // Mirrors GetWatchProgressForUser: rows the user hid stay hidden while + // updated_at <= hidden_before; reading activity after hidden_before + // surfaces again. + assertQueryTermsInOrder(t, query, + "FROM ebook_reader_progress", + "NOT EXISTS", + "FROM user_history_hidden_items hhi", + "hhi.media_item_id = ebook_reader_progress.content_id", + "ebook_reader_progress.updated_at <= hhi.hidden_before", + ) +} + +func TestWatchedActivityCTEIncludesEbookReaderProgress(t *testing.T) { + query := strings.Join(strings.Fields(watchedActivityCTE), " ") + + for _, term := range []string{ + "FROM ebook_reader_progress erp", + "erp.progress >= 0.5", + "erp.progress >= 0.9 AS completed", + "hhi.media_item_id = erp.content_id", + } { + if !strings.Contains(query, term) { + t.Fatalf("watched activity CTE missing %q: %s", term, query) + } + } +} + +func TestEmbeddingEligibilityWhereClauseIncludesBookTypes(t *testing.T) { + clause := embeddingEligibilityWhereClause() + + for _, term := range []string{"mi.status = 'matched'", "mi.type = 'audiobook'", "mi.type = 'ebook'"} { + if !strings.Contains(clause, term) { + t.Fatalf("embedding eligibility clause missing %q: %s", term, clause) + } + } +} + +func TestEmbeddingEligibilityWhereClauseUsesMediaItemsAlias(t *testing.T) { + clause := embeddingEligibilityWhereClause() + + if strings.Contains(clause, " type =") || strings.Contains(clause, " status =") { + t.Fatalf("embedding eligibility clause should qualify media_items columns: %s", clause) + } +} + +func TestRecommendationItemEligibilityWhereClauseIncludesBookTypes(t *testing.T) { + clause := recommendationItemEligibilityWhereClause("mi") + + for _, term := range []string{"mi.status = 'matched'", "mi.type = 'audiobook'", "mi.type = 'ebook'"} { + if !strings.Contains(clause, term) { + t.Fatalf("recommendation item eligibility clause missing %q: %s", term, clause) + } + } +} + +func TestRecommendationItemEligibilityWhereClauseDefaultsAlias(t *testing.T) { + clause := recommendationItemEligibilityWhereClause("") + + if !strings.Contains(clause, "media_items.status = 'matched'") { + t.Fatalf("default eligibility alias missing media_items qualification: %s", clause) + } +} + +func TestTasteProfileRefreshSubjectsQueryIncludesEbookReaderProgress(t *testing.T) { + query := strings.Join(strings.Fields(tasteProfileRefreshSubjectsQuery), " ") + + assertQueryTermsInOrder(t, query, + "SELECT DISTINCT user_id, profile_id FROM user_watch_progress", + "UNION", + "SELECT DISTINCT user_id, profile_id FROM ebook_reader_progress", + ) +} + func assertQueryTermsInOrder(t *testing.T, query string, terms ...string) { t.Helper() diff --git a/internal/recommendations/signals.go b/internal/recommendations/signals.go index c73b4e5a..ee462a95 100644 --- a/internal/recommendations/signals.go +++ b/internal/recommendations/signals.go @@ -14,6 +14,7 @@ const signalPageSize = 1000 type signalRepo interface { GetWatchedItemIDSet(ctx context.Context, userID int, profileID string) (map[string]struct{}, error) GetWatchProgressForUser(ctx context.Context, userID int, profileID string) ([]WatchProgressRow, error) + GetEbookReaderProgressForUser(ctx context.Context, userID int, profileID string) ([]WatchProgressRow, error) GetRecentCompletedItemIDs(ctx context.Context, userID int, profileID string, limit int) ([]string, error) GetRewatchCounts(ctx context.Context, userID int, profileID string) ([]RewatchCount, error) ResolveCanonicalItemIDSet(ctx context.Context, contentIDs []string) (map[string]struct{}, error) @@ -69,6 +70,15 @@ func (s *SignalReader) WatchedItemIDSet(ctx context.Context, userID int, profile }); err != nil { return nil, err } + ebookProgress, err := s.repo.GetEbookReaderProgressForUser(ctx, userID, profileID) + if err != nil { + return nil, err + } + for _, wp := range ebookProgress { + if wp.Completed || watchedProgressThresholdMet(wp.PositionSeconds, wp.DurationSeconds) { + rawIDs = append(rawIDs, wp.MediaItemID) + } + } return s.repo.ResolveCanonicalItemIDSet(ctx, rawIDs) } @@ -97,6 +107,11 @@ func (s *SignalReader) WatchProgressForUser(ctx context.Context, userID int, pro }); err != nil { return nil, err } + ebookProgress, err := s.repo.GetEbookReaderProgressForUser(ctx, userID, profileID) + if err != nil { + return nil, err + } + rows = append(rows, ebookProgress...) return rows, nil } @@ -118,20 +133,38 @@ func (s *SignalReader) RecentCompletedItemIDs(ctx context.Context, userID int, p if err != nil { return nil, fmt.Errorf("list completed progress from store: %w", err) } - sort.SliceStable(progress, func(i, j int) bool { - left := parseSignalTime(progress[i].UpdatedAt, time.Time{}) - right := parseSignalTime(progress[j].UpdatedAt, time.Time{}) - if !left.Equal(right) { - return left.After(right) - } - return progress[i].MediaItemID < progress[j].MediaItemID - }) - - ids := make([]string, 0, min(limit, len(progress))) + completed := make([]WatchProgressRow, 0, len(progress)) for _, wp := range progress { if !wp.Completed { continue } + completed = append(completed, WatchProgressRow{ + MediaItemID: wp.MediaItemID, + Completed: true, + UpdatedAt: parseSignalTime(wp.UpdatedAt, time.Time{}), + }) + } + ebookProgress, err := s.repo.GetEbookReaderProgressForUser(ctx, userID, profileID) + if err != nil { + return nil, err + } + for _, wp := range ebookProgress { + if !wp.Completed { + continue + } + completed = append(completed, wp) + } + sort.SliceStable(completed, func(i, j int) bool { + left := completed[i].UpdatedAt + right := completed[j].UpdatedAt + if !left.Equal(right) { + return left.After(right) + } + return completed[i].MediaItemID < completed[j].MediaItemID + }) + + ids := make([]string, 0, min(limit, len(completed))) + for _, wp := range completed { ids = append(ids, wp.MediaItemID) if len(ids) == limit { break diff --git a/internal/recommendations/signals_test.go b/internal/recommendations/signals_test.go index a3b54480..ae8f54ee 100644 --- a/internal/recommendations/signals_test.go +++ b/internal/recommendations/signals_test.go @@ -14,6 +14,7 @@ type fakeSignalRepo struct { fallbackWatched map[string]struct{} fallbackProgress []WatchProgressRow + ebookProgress []WatchProgressRow fallbackRecentCompleted []string fallbackRewatches []RewatchCount } @@ -26,6 +27,10 @@ func (r *fakeSignalRepo) GetWatchProgressForUser(context.Context, int, string) ( return r.fallbackProgress, nil } +func (r *fakeSignalRepo) GetEbookReaderProgressForUser(context.Context, int, string) ([]WatchProgressRow, error) { + return r.ebookProgress, nil +} + func (r *fakeSignalRepo) GetRecentCompletedItemIDs(context.Context, int, string, int) ([]string, error) { return r.fallbackRecentCompleted, nil } @@ -161,6 +166,52 @@ func TestSignalReaderWatchedSetCanonicalizesStoreProgress(t *testing.T) { } } +func TestSignalReaderWatchedSetIncludesEbookReaderProgress(t *testing.T) { + store := &fakeSignalStore{} + repo := &fakeSignalRepo{ + ebookProgress: []WatchProgressRow{ + {MediaItemID: "ebook-half", PositionSeconds: 0.6, DurationSeconds: 1, UpdatedAt: time.Date(2026, 6, 1, 10, 0, 0, 0, time.UTC)}, + {MediaItemID: "ebook-low", PositionSeconds: 0.2, DurationSeconds: 1, UpdatedAt: time.Date(2026, 6, 1, 11, 0, 0, 0, time.UTC)}, + }, + } + reader := NewSignalReader(repo, fakeSignalProvider{store: store}) + + watched, err := reader.WatchedItemIDSet(context.Background(), 7, "p1") + if err != nil { + t.Fatalf("WatchedItemIDSet returned error: %v", err) + } + + if _, ok := watched["ebook-half"]; !ok { + t.Fatalf("expected ebook-half in watched set, got %#v", watched) + } + if _, ok := watched["ebook-low"]; ok { + t.Fatalf("did not expect low-progress ebook in watched set: %#v", watched) + } +} + +func TestSignalReaderWatchProgressIncludesEbookReaderProgress(t *testing.T) { + store := &fakeSignalStore{progress: []userstore.WatchProgress{ + {ProfileID: "p1", MediaItemID: "movie", PositionSeconds: 60, DurationSeconds: 100, UpdatedAt: "2026-06-01T10:00:00Z"}, + }} + repo := &fakeSignalRepo{ + ebookProgress: []WatchProgressRow{ + {MediaItemID: "ebook", PositionSeconds: 0.42, DurationSeconds: 1, Completed: false, UpdatedAt: time.Date(2026, 6, 1, 11, 0, 0, 0, time.UTC)}, + }, + } + reader := NewSignalReader(repo, fakeSignalProvider{store: store}) + + progress, err := reader.WatchProgressForUser(context.Background(), 7, "p1") + if err != nil { + t.Fatalf("WatchProgressForUser returned error: %v", err) + } + + if !slices.ContainsFunc(progress, func(row WatchProgressRow) bool { + return row.MediaItemID == "ebook" && row.PositionSeconds == 0.42 && row.DurationSeconds == 1 + }) { + t.Fatalf("ebook progress missing from rows: %#v", progress) + } +} + func TestSignalReaderRecentCompletedUsesStoreUpdatedOrder(t *testing.T) { store := &fakeSignalStore{progress: []userstore.WatchProgress{ {ProfileID: "p1", MediaItemID: "older", Completed: true, UpdatedAt: "2026-05-01T10:00:00Z"}, @@ -181,6 +232,29 @@ func TestSignalReaderRecentCompletedUsesStoreUpdatedOrder(t *testing.T) { } } +func TestSignalReaderRecentCompletedIncludesEbookReaderProgress(t *testing.T) { + store := &fakeSignalStore{progress: []userstore.WatchProgress{ + {ProfileID: "p1", MediaItemID: "movie", Completed: true, UpdatedAt: "2026-06-01T10:00:00Z"}, + }} + repo := &fakeSignalRepo{ + ebookProgress: []WatchProgressRow{ + {MediaItemID: "ebook-done", PositionSeconds: 0.95, DurationSeconds: 1, Completed: true, UpdatedAt: time.Date(2026, 6, 3, 10, 0, 0, 0, time.UTC)}, + {MediaItemID: "ebook-open", PositionSeconds: 0.4, DurationSeconds: 1, Completed: false, UpdatedAt: time.Date(2026, 6, 4, 10, 0, 0, 0, time.UTC)}, + }, + } + reader := NewSignalReader(repo, fakeSignalProvider{store: store}) + + ids, err := reader.RecentCompletedItemIDs(context.Background(), 7, "p1", 3) + if err != nil { + t.Fatalf("RecentCompletedItemIDs returned error: %v", err) + } + + want := []string{"ebook-done", "movie"} + if !slices.Equal(ids, want) { + t.Fatalf("recent completed = %#v, want %#v", ids, want) + } +} + func TestSignalReaderRewatchCountsAggregatesCompletedHistory(t *testing.T) { store := &fakeSignalStore{history: []userstore.WatchHistoryEntry{ {ProfileID: "p1", MediaItemID: "rewatched", Completed: true, WatchedAt: "2026-05-01T10:00:00Z"}, diff --git a/internal/recommendations/taste_signals.go b/internal/recommendations/taste_signals.go index 5fa9f95d..028e063a 100644 --- a/internal/recommendations/taste_signals.go +++ b/internal/recommendations/taste_signals.go @@ -12,6 +12,7 @@ const ( canonicalKindSeries canonicalContentKind = "series" canonicalKindSeason canonicalContentKind = "season" canonicalKindEpisode canonicalContentKind = "episode" + canonicalKindEbook canonicalContentKind = "ebook" ) const ( @@ -310,7 +311,11 @@ func buildCanonicalImplicitSignals(progress []WatchProgressRow, rewatches []Rewa if timestamp.After(builder.lastSignalAt) { builder.lastSignalAt = timestamp } - case canonicalKindMovie, canonicalKindSeries: + case canonicalKindMovie, canonicalKindSeries, canonicalKindEbook: + // Ebooks are their own canonical entity. Reader progress rows carry + // progress as position with duration 1, so implicitWatchWeight + // treats the reading ratio exactly like a movie's + // position/duration ratio (finished book == finished movie). signals[signal.Ref.CanonicalID] += signal.Weight * timeDecay(timestamp, now, halfLife) } } diff --git a/internal/recommendations/taste_signals_test.go b/internal/recommendations/taste_signals_test.go new file mode 100644 index 00000000..d6ebc6ae --- /dev/null +++ b/internal/recommendations/taste_signals_test.go @@ -0,0 +1,71 @@ +package recommendations + +import ( + "math" + "testing" + "time" +) + +func TestBuildCanonicalImplicitSignalsIncludesEbookCompletion(t *testing.T) { + now := time.Date(2026, 6, 1, 12, 0, 0, 0, time.UTC) + halfLife := 90.0 + + // Ebook reader progress rows map progress onto position with duration 1, + // so a finished book carries the same ratio shape as a finished movie. + progress := []WatchProgressRow{ + {MediaItemID: "ebook-1", PositionSeconds: 0.95, DurationSeconds: 1, Completed: true, UpdatedAt: now}, + {MediaItemID: "movie-1", PositionSeconds: 5400, DurationSeconds: 5700, Completed: true, UpdatedAt: now}, + } + refs := map[string]canonicalContentRef{ + "ebook-1": {Kind: canonicalKindEbook, CanonicalID: "ebook-1"}, + "movie-1": {Kind: canonicalKindMovie, CanonicalID: "movie-1"}, + } + + signals, completed := buildCanonicalImplicitSignals(progress, nil, refs, now, halfLife) + + ebookWeight, ok := signals["ebook-1"] + if !ok { + t.Fatal("expected ebook completion to produce a canonical implicit signal") + } + movieWeight, ok := signals["movie-1"] + if !ok { + t.Fatal("expected movie completion to produce a canonical implicit signal") + } + if math.Abs(ebookWeight-movieWeight) > 1e-9 { + t.Fatalf("ebook completion weight %v should match movie completion weight %v", ebookWeight, movieWeight) + } + if math.Abs(ebookWeight-WeightWatchHigh) > 1e-9 { + t.Fatalf("ebook completion weight = %v, want WeightWatchHigh (%v) with no decay at t=now", ebookWeight, WeightWatchHigh) + } + if _, ok := completed["ebook-1"]; !ok { + t.Fatal("expected completed ebook to enter the completed set") + } +} + +func TestBuildCanonicalImplicitSignalsWeightsPartialEbookLikeMovie(t *testing.T) { + now := time.Date(2026, 6, 1, 12, 0, 0, 0, time.UTC) + halfLife := 90.0 + + progress := []WatchProgressRow{ + // Mid-read book (60%) mirrors a movie watched to 60%. + {MediaItemID: "ebook-mid", PositionSeconds: 0.6, DurationSeconds: 1, UpdatedAt: now}, + // Abandoned book (<15%) mirrors an abandoned movie: negative signal. + {MediaItemID: "ebook-abandoned", PositionSeconds: 0.05, DurationSeconds: 1, UpdatedAt: now}, + } + refs := map[string]canonicalContentRef{ + "ebook-mid": {Kind: canonicalKindEbook, CanonicalID: "ebook-mid"}, + "ebook-abandoned": {Kind: canonicalKindEbook, CanonicalID: "ebook-abandoned"}, + } + + signals, completed := buildCanonicalImplicitSignals(progress, nil, refs, now, halfLife) + + if got := signals["ebook-mid"]; math.Abs(got-WeightWatchMed) > 1e-9 { + t.Fatalf("mid-read ebook weight = %v, want WeightWatchMed (%v)", got, WeightWatchMed) + } + if got := signals["ebook-abandoned"]; math.Abs(got-WeightWatchLow) > 1e-9 { + t.Fatalf("abandoned ebook weight = %v, want WeightWatchLow (%v)", got, WeightWatchLow) + } + if len(completed) != 0 { + t.Fatalf("no row was completed, got completed set %v", completed) + } +} diff --git a/internal/recommendations/worker.go b/internal/recommendations/worker.go index bc509b52..2a64dc1b 100644 --- a/internal/recommendations/worker.go +++ b/internal/recommendations/worker.go @@ -31,6 +31,19 @@ type Worker struct { cancelFunc context.CancelFunc } +const tasteProfileRefreshSubjectsQuery = ` + SELECT DISTINCT user_id, profile_id FROM user_ratings + UNION + SELECT DISTINCT user_id, profile_id FROM user_taste_profiles + UNION + SELECT DISTINCT user_id, profile_id FROM user_watch_progress + UNION + SELECT DISTINCT user_id, profile_id FROM ebook_reader_progress + UNION + SELECT DISTINCT user_id, profile_id FROM user_favorites + UNION + SELECT DISTINCT user_id, profile_id FROM user_watchlist` + // NewWorker creates a new recommendation Worker. func NewWorker(engine *Engine, embeddingsCron, tasteProfilesCron, cowatchCron, recommendationsCron string) (*Worker, error) { w := &Worker{ @@ -266,16 +279,7 @@ func (w *Worker) doTasteProfiles() { defer cancel() slog.Info("starting taste profile refresh") - rows, err := w.engine.pool.Query(ctx, ` - SELECT DISTINCT user_id, profile_id FROM user_ratings - UNION - SELECT DISTINCT user_id, profile_id FROM user_taste_profiles - UNION - SELECT DISTINCT user_id, profile_id FROM user_watch_progress - UNION - SELECT DISTINCT user_id, profile_id FROM user_favorites - UNION - SELECT DISTINCT user_id, profile_id FROM user_watchlist`) + rows, err := w.engine.pool.Query(ctx, tasteProfileRefreshSubjectsQuery) if err != nil { slog.Error("taste profile query failed", "error", err) return diff --git a/internal/scanner/audiobook.go b/internal/scanner/audiobook.go index a9bbe4f9..2a59cf09 100644 --- a/internal/scanner/audiobook.go +++ b/internal/scanner/audiobook.go @@ -48,7 +48,7 @@ func stripNarratorSuffix(title string) string { // The scanner write path (Task 8) converts this into media_items + // media_files + item_people rows. type parsedAudiobook struct { - Title string + Title string Author string Narrator string Series string diff --git a/internal/scanner/audiobook_cover.go b/internal/scanner/audiobook_cover.go index b4f5b714..6a1440e6 100644 --- a/internal/scanner/audiobook_cover.go +++ b/internal/scanner/audiobook_cover.go @@ -24,6 +24,10 @@ type audiobookCoverCacher interface { CacheAudiobookCover(ctx context.Context, data []byte, contentID string) (basePath string, ext string, thumbhash string, err error) } +type ebookCoverCacher interface { + CacheEbookCover(ctx context.Context, data []byte, contentID string) (basePath string, ext string, thumbhash string, err error) +} + type audiobookCoverMetadataStore interface { GetPosterPath(ctx context.Context, contentID string) (string, error) UpdateMetadata(ctx context.Context, contentID string, upd *catalog.MetadataUpdate) error diff --git a/internal/scanner/ebook.go b/internal/scanner/ebook.go new file mode 100644 index 00000000..96c93827 --- /dev/null +++ b/internal/scanner/ebook.go @@ -0,0 +1,1110 @@ +package scanner + +import ( + "archive/zip" + "bytes" + "encoding/hex" + "encoding/xml" + "fmt" + "html" + "io" + "os" + "path" + "path/filepath" + "strconv" + "strings" + "time" + "unicode" + "unicode/utf8" + + xhtml "golang.org/x/net/html" + "golang.org/x/net/html/charset" + "golang.org/x/text/encoding/charmap" + textunicode "golang.org/x/text/encoding/unicode" +) + +const maxEPUBMetadataEntrySize = 8 * 1024 * 1024 +const maxPDFMetadataScanSize = 2 * 1024 * 1024 + +var ebookExtensions = map[string]bool{ + ".epub": true, + ".pdf": true, + ".mobi": true, + ".azw": true, + ".azw3": true, + ".fb2": true, + ".fbz": true, + ".cbz": true, + ".cbr": true, +} + +type parsedEbook struct { + Format string + Title string + Authors []string + Description string + Publisher string + PublishedAt time.Time + Year int + Language string + ISBN string + Series string + SeriesIndex string + Genres []string + PageCount int + Cover *parsedEbookCover +} + +type parsedEbookCover struct { + ContentType string + Bytes []byte +} + +func SupportsEbookFile(filePath string) bool { + if strings.HasSuffix(strings.ToLower(filePath), ".fb2.zip") { + return true + } + return ebookExtensions[strings.ToLower(filepath.Ext(filePath))] +} + +func parseEbookFile(path string) (book parsedEbook, err error) { + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("panic parsing ebook %s: %v", path, r) + } + }() + format := ebookFileFormat(path) + switch format { + case ".epub": + book, err = parseEbookEPUB(path) + case ".fb2": + book, err = parseEbookFB2(path) + case ".fbz": + book, err = parseEbookFBZ(path) + case ".cbz": + book, err = parseEbookCBZ(path) + case ".pdf": + book, err = parseEbookPDF(path) + case ".mobi", ".azw", ".azw3", ".cbr": + book = parsedEbook{Format: strings.TrimPrefix(format, ".")} + default: + err = fmt.Errorf("unsupported ebook format: %s", filepath.Ext(path)) + } + book.sanitize() + return book, err +} + +func parseEbookPDF(path string) (parsedEbook, error) { + book := parsedEbook{Format: "pdf"} + file, err := os.Open(path) + if err != nil { + return book, err + } + defer file.Close() + + head, tail, err := readPDFMetadataWindows(file) + if err != nil { + return book, err + } + info := parsePDFInfoFields(head) + // A head match comes from a linearized PDF whose Info dictionary sits at + // the start of the file and is authoritative; non-linearized PDFs (the + // common case) store the Info dictionary near the end and usually have no + // head match at all. The tail therefore only fills keys the head did not + // produce, so key-shaped byte noise inside trailing compressed streams can + // never replace a good head value. + for key, value := range parsePDFInfoFields(tail) { + if value != "" && info[key] == "" { + info[key] = value + } + } + book.Title = info["Title"] + book.Authors = splitEbookAuthors(info["Author"]) + book.Description = info["Subject"] + book.Genres = splitPDFKeywords(info["Keywords"]) + for _, value := range []string{ + info["ISBN"], + info["Subject"], + info["Keywords"], + info["Title"], + } { + if isbn := normalizeEbookISBN(value); isbn != "" { + book.ISBN = isbn + break + } + } + if t, ok := parsePDFDate(info["CreationDate"]); ok { + book.PublishedAt = t + book.Year = t.Year() + } + return book, nil +} + +// readPDFMetadataWindows reads the first and last maxPDFMetadataScanSize bytes +// of the file. The windows never overlap: for files at most one window long +// the tail is nil, and for files shorter than two windows the tail starts +// where the head ends. +func readPDFMetadataWindows(file *os.File) (head []byte, tail []byte, err error) { + head, err = io.ReadAll(io.LimitReader(file, maxPDFMetadataScanSize)) + if err != nil { + return nil, nil, err + } + info, err := file.Stat() + if err != nil { + return nil, nil, err + } + size := info.Size() + if size <= int64(len(head)) { + return head, nil, nil + } + tailStart := size - maxPDFMetadataScanSize + if tailStart < int64(len(head)) { + tailStart = int64(len(head)) + } + tail = make([]byte, size-tailStart) + if _, err := file.ReadAt(tail, tailStart); err != nil { + return nil, nil, err + } + return head, tail, nil +} + +func ebookFileFormat(path string) string { + if strings.HasSuffix(strings.ToLower(path), ".fb2.zip") { + return ".fbz" + } + return strings.ToLower(filepath.Ext(path)) +} + +func (b *parsedEbook) sanitize() { + b.Format = strings.TrimPrefix(strings.ToLower(strings.TrimSpace(b.Format)), ".") + b.Title = strings.TrimSpace(b.Title) + b.Description = cleanEbookDescription(b.Description) + b.Publisher = strings.TrimSpace(b.Publisher) + b.Language = strings.TrimSpace(b.Language) + b.ISBN = normalizeEbookISBN(b.ISBN) + b.Series = strings.TrimSpace(b.Series) + b.SeriesIndex = strings.TrimSpace(b.SeriesIndex) + b.Authors = uniqueTrimmedStrings(b.Authors) + b.Genres = uniqueTrimmedStrings(b.Genres) + if b.PageCount < 0 { + b.PageCount = 0 + } + if b.Year == 0 && !b.PublishedAt.IsZero() { + b.Year = b.PublishedAt.Year() + } +} + +func cleanEbookDescription(value string) string { + value = strings.TrimSpace(html.UnescapeString(value)) + if value == "" { + return "" + } + if !strings.Contains(value, "<") || !strings.Contains(value, ">") { + return strings.Join(strings.Fields(value), " ") + } + + tokenizer := xhtml.NewTokenizer(strings.NewReader(value)) + var out strings.Builder + needsSpace := false + writeSpace := func() { + if out.Len() > 0 && !needsSpace { + needsSpace = true + } + } + for { + switch tokenizer.Next() { + case xhtml.ErrorToken: + return strings.Join(strings.Fields(out.String()), " ") + case xhtml.TextToken: + text := strings.TrimSpace(html.UnescapeString(string(tokenizer.Text()))) + if text == "" { + continue + } + if out.Len() > 0 && (needsSpace || !startsWithClosingPunctuation(text)) { + out.WriteByte(' ') + } + out.WriteString(text) + needsSpace = false + case xhtml.StartTagToken, xhtml.SelfClosingTagToken, xhtml.EndTagToken: + name, _ := tokenizer.TagName() + switch strings.ToLower(string(name)) { + case "br", "p", "div", "section", "article", "li", "ul", "ol", "blockquote", "tr": + writeSpace() + } + } + } +} + +func startsWithClosingPunctuation(value string) bool { + for _, r := range value { + switch r { + case '.', ',', ';', ':', '!', '?', ')', ']', '}': + return true + default: + return false + } + } + return false +} + +func looksLikeHTML(value string) bool { + value = strings.TrimSpace(value) + return strings.Contains(value, "<") && strings.Contains(value, ">") +} + +func uniqueTrimmedStrings(values []string) []string { + seen := map[string]struct{}{} + var out []string + for _, value := range values { + trimmed := strings.TrimSpace(value) + key := strings.ToLower(trimmed) + if key == "" { + continue + } + if _, ok := seen[key]; ok { + continue + } + seen[key] = struct{}{} + out = append(out, trimmed) + } + return out +} + +func normalizeEbookISBN(value string) string { + value = strings.ToUpper(strings.TrimSpace(value)) + for _, prefix := range []string{"ISBN-13", "ISBN-10", "ISBN"} { + if strings.HasPrefix(value, prefix) { + value = strings.TrimSpace(strings.TrimPrefix(value, prefix)) + value = strings.TrimLeft(value, ": ") + break + } + } + var out strings.Builder + for _, r := range value { + if r >= '0' && r <= '9' { + out.WriteRune(r) + continue + } + if r == 'X' { + out.WriteRune(r) + } + } + candidate := out.String() + switch len(candidate) { + case 10: + if validISBN10(candidate) { + return candidate + } + case 13: + if validISBN13(candidate) { + return candidate + } + } + return "" +} + +func validISBN10(candidate string) bool { + if len(candidate) != 10 { + return false + } + sum := 0 + for i, r := range candidate { + value := 0 + switch { + case r >= '0' && r <= '9': + value = int(r - '0') + case r == 'X' && i == 9: + value = 10 + default: + return false + } + sum += value * (10 - i) + } + return sum%11 == 0 +} + +func validISBN13(candidate string) bool { + if len(candidate) != 13 { + return false + } + sum := 0 + for i, r := range candidate { + if r < '0' || r > '9' { + return false + } + value := int(r - '0') + if i%2 == 1 { + value *= 3 + } + sum += value + } + return sum%10 == 0 +} + +func parseEbookEPUB(path string) (parsedEbook, error) { + book := parsedEbook{Format: "epub"} + reader, err := zip.OpenReader(path) + if err != nil { + return book, err + } + defer reader.Close() + + container, err := readEPUBZipEntry(&reader.Reader, "META-INF/container.xml") + if err != nil { + return book, err + } + opfPath, err := epubOPFPath(container) + if err != nil { + return book, err + } + opf, err := readEPUBZipEntry(&reader.Reader, opfPath) + if err != nil { + return book, err + } + if err := parseEPUBOPFMetadata(opf, &book); err != nil { + return book, err + } + if cover, err := extractEPUBCover(&reader.Reader, opfPath, opf); err == nil { + book.Cover = cover + } + return book, nil +} + +func parseEbookFB2(path string) (parsedEbook, error) { + book := parsedEbook{Format: "fb2"} + file, err := os.Open(path) + if err != nil { + return book, err + } + defer file.Close() + // Mirror the .fbz entry cap so a plain .fb2 cannot stream unbounded + // bytes through the XML decoder. + info, err := file.Stat() + if err != nil { + return book, err + } + if info.Size() > maxEPUBMetadataEntrySize { + return book, fmt.Errorf("fb2 file too large: %s", path) + } + return parseEbookFB2Reader(io.LimitReader(file, maxEPUBMetadataEntrySize+1), "fb2") +} + +func parseEbookFBZ(path string) (parsedEbook, error) { + book := parsedEbook{Format: "fbz"} + reader, err := zip.OpenReader(path) + if err != nil { + return book, err + } + defer reader.Close() + + for _, file := range reader.File { + if !strings.HasSuffix(strings.ToLower(file.Name), ".fb2") { + continue + } + if file.UncompressedSize64 > maxEPUBMetadataEntrySize { + return book, fmt.Errorf("fbz entry too large: %s", file.Name) + } + entry, err := file.Open() + if err != nil { + return book, err + } + defer entry.Close() + return parseEbookFB2Reader(io.LimitReader(entry, maxEPUBMetadataEntrySize+1), "fbz") + } + return book, fmt.Errorf("fbz archive has no fb2 entry") +} + +func parseEbookCBZ(path string) (parsedEbook, error) { + book := parsedEbook{Format: "cbz"} + reader, err := zip.OpenReader(path) + if err != nil { + return book, err + } + defer reader.Close() + + var coverPage *zip.File + var coverKey string + for _, file := range reader.File { + if !isComicArchivePage(file.Name) { + continue + } + book.PageCount++ + key := normalizedArchivePath(file.Name) + if coverPage == nil || naturalPathLess(key, coverKey) { + coverPage, coverKey = file, key + } + } + if coverPage != nil { + if cover, err := readArchiveImageCover(coverPage); err == nil { + book.Cover = cover + } + } + return book, nil +} + +// naturalPathLess orders archive entry names case-insensitively with digit +// runs compared numerically, so unpadded page numbers ("2.jpg" before +// "10.jpg") and chapter directories ("ch2/" before "ch10/") sort in reading +// order instead of byte order. +func naturalPathLess(a, b string) bool { + for len(a) > 0 && len(b) > 0 { + if isASCIIDigit(a[0]) && isASCIIDigit(b[0]) { + aRun, aRest := splitDigitRun(a) + bRun, bRest := splitDigitRun(b) + aNum := strings.TrimLeft(aRun, "0") + bNum := strings.TrimLeft(bRun, "0") + if len(aNum) != len(bNum) { + return len(aNum) < len(bNum) + } + if aNum != bNum { + return aNum < bNum + } + a, b = aRest, bRest + continue + } + ar, aSize := utf8.DecodeRuneInString(a) + br, bSize := utf8.DecodeRuneInString(b) + al, bl := unicode.ToLower(ar), unicode.ToLower(br) + if al != bl { + return al < bl + } + a, b = a[aSize:], b[bSize:] + } + return len(a) < len(b) +} + +func isASCIIDigit(b byte) bool { + return b >= '0' && b <= '9' +} + +func splitDigitRun(s string) (run string, rest string) { + i := 0 + for i < len(s) && isASCIIDigit(s[i]) { + i++ + } + return s[:i], s[i:] +} + +func isComicArchivePage(name string) bool { + clean := normalizedArchivePath(name) + if clean == "" || strings.HasSuffix(clean, "/") { + return false + } + base := strings.ToLower(filepath.Base(clean)) + if strings.HasPrefix(base, "._") { + return false + } + parts := strings.Split(strings.ToLower(clean), "/") + for _, part := range parts { + if part == "__macosx" { + return false + } + } + switch filepath.Ext(base) { + case ".jpg", ".jpeg", ".png", ".webp", ".gif", ".avif", ".bmp": + return true + default: + return false + } +} + +func normalizedArchivePath(name string) string { + return strings.TrimSpace(strings.ReplaceAll(name, "\\", "/")) +} + +func readArchiveImageCover(file *zip.File) (*parsedEbookCover, error) { + if file == nil { + return nil, fmt.Errorf("nil archive image") + } + if file.UncompressedSize64 > maxEPUBMetadataEntrySize { + return nil, fmt.Errorf("archive cover entry too large: %s", file.Name) + } + entry, err := file.Open() + if err != nil { + return nil, err + } + defer entry.Close() + data, err := io.ReadAll(io.LimitReader(entry, maxEPUBMetadataEntrySize+1)) + if err != nil { + return nil, err + } + if len(data) > maxEPUBMetadataEntrySize { + return nil, fmt.Errorf("archive cover entry too large: %s", file.Name) + } + if len(data) == 0 { + return nil, fmt.Errorf("archive cover entry empty: %s", file.Name) + } + return &parsedEbookCover{ + ContentType: ebookImageContentType(file.Name), + Bytes: data, + }, nil +} + +func parseEbookFB2Reader(reader io.Reader, format string) (parsedEbook, error) { + book := parsedEbook{Format: format} + var fb2 struct { + Description struct { + TitleInfo struct { + Genres []string `xml:"genre"` + Authors []struct { + FirstName string `xml:"first-name"` + MiddleName string `xml:"middle-name"` + LastName string `xml:"last-name"` + Nickname string `xml:"nickname"` + } `xml:"author"` + BookTitle string `xml:"book-title"` + Lang string `xml:"lang"` + Date struct { + Value string `xml:"value,attr"` + Text string `xml:",chardata"` + } `xml:"date"` + Sequences []struct { + Name string `xml:"name,attr"` + Number string `xml:"number,attr"` + } `xml:"sequence"` + } `xml:"title-info"` + PublishInfo struct { + ISBN string `xml:"isbn"` + Publisher string `xml:"publisher"` + Year string `xml:"year"` + } `xml:"publish-info"` + } `xml:"description"` + } + decoder := xml.NewDecoder(reader) + decoder.CharsetReader = ebookXMLCharsetReader + if err := decoder.Decode(&fb2); err != nil { + return book, err + } + + book.Title = fb2.Description.TitleInfo.BookTitle + book.Language = fb2.Description.TitleInfo.Lang + book.Genres = fb2.Description.TitleInfo.Genres + if t, ok := parseEbookDate(firstNonEmpty(fb2.Description.TitleInfo.Date.Value, fb2.Description.TitleInfo.Date.Text)); ok { + book.PublishedAt = t + } + for _, author := range fb2.Description.TitleInfo.Authors { + name := strings.Join(uniqueTrimmedStrings([]string{ + author.FirstName, + author.MiddleName, + author.LastName, + }), " ") + if name == "" { + name = author.Nickname + } + book.Authors = append(book.Authors, name) + } + if len(fb2.Description.TitleInfo.Sequences) > 0 { + book.Series = fb2.Description.TitleInfo.Sequences[0].Name + book.SeriesIndex = fb2.Description.TitleInfo.Sequences[0].Number + } + book.ISBN = fb2.Description.PublishInfo.ISBN + book.Publisher = fb2.Description.PublishInfo.Publisher + if year, err := strconv.Atoi(strings.TrimSpace(fb2.Description.PublishInfo.Year)); err == nil { + book.Year = year + } + return book, nil +} + +func parsePDFInfoFields(data []byte) map[string]string { + fields := map[string]string{} + for _, key := range []string{"Title", "Author", "Subject", "Keywords", "CreationDate", "ISBN"} { + if value, ok := findPDFInfoValue(data, key); ok { + fields[key] = value + } + } + return fields +} + +// pdfWhitespace is the PDF whitespace character set (ISO 32000-1, table 1). +const pdfWhitespace = "\x00\t\n\f\r " + +// isPDFTokenDelimiter reports whether b legally terminates a PDF name token. +// Without this check a key with a shared prefix (e.g. "/TitleSort") would be +// mistaken for the key itself (e.g. "/Title"). +func isPDFTokenDelimiter(b byte) bool { + switch b { + case '\x00', '\t', '\n', '\f', '\r', ' ', '(', ')', '<', '>', '[', ']', '{', '}', '/', '%': + return true + default: + return false + } +} + +// findPDFInfoValue scans every occurrence of "/" in the window and +// returns the first whose token is properly delimited and whose value parses +// as a PDF string. Raw byte search can match key-shaped noise inside +// compressed streams, so a failed parse moves on to the next occurrence +// instead of giving up. +func findPDFInfoValue(data []byte, key string) (string, bool) { + token := []byte("/" + key) + for offset := 0; offset < len(data); { + idx := bytes.Index(data[offset:], token) + if idx < 0 { + return "", false + } + idx += offset + offset = idx + len(token) + rest := data[idx+len(token):] + if len(rest) == 0 { + return "", false + } + if !isPDFTokenDelimiter(rest[0]) { + continue + } + trimmed := bytes.TrimLeft(rest, pdfWhitespace) + if len(trimmed) == 0 { + return "", false + } + if value, ok := readPDFString(trimmed); ok { + return value, true + } + } + return "", false +} + +func decodeEbookXML(data []byte, v any) error { + data = normalizeEbookXMLVersion(data) + decoder := xml.NewDecoder(bytes.NewReader(data)) + decoder.CharsetReader = ebookXMLCharsetReader + return decoder.Decode(v) +} + +func normalizeEbookXMLVersion(data []byte) []byte { + for _, needle := range []string{`version="1.1"`, `version='1.1'`} { + idx := bytes.Index(data, []byte(needle)) + if idx < 0 || idx > 128 { + continue + } + out := append([]byte(nil), data...) + copy(out[idx:], strings.Replace(needle, "1.1", "1.0", 1)) + return out + } + return data +} + +func ebookXMLCharsetReader(label string, input io.Reader) (io.Reader, error) { + name := strings.ToLower(strings.TrimSpace(label)) + if name == "" || name == "utf-8" || name == "utf8" { + return input, nil + } + reader, err := charset.NewReaderLabel(name, input) + if err != nil { + return nil, fmt.Errorf("unsupported ebook XML encoding %q: %w", label, err) + } + return reader, nil +} + +func readPDFString(data []byte) (string, bool) { + switch data[0] { + case '(': + return readPDFLiteralString(data) + case '<': + if len(data) > 1 && data[1] == '<' { + return "", false + } + return readPDFHexString(data) + default: + return "", false + } +} + +func readPDFLiteralString(data []byte) (string, bool) { + if len(data) == 0 || data[0] != '(' { + return "", false + } + var out []byte + depth := 1 + escaped := false + for _, b := range data[1:] { + if escaped { + switch b { + case 'n': + out = append(out, '\n') + case 'r': + out = append(out, '\r') + case 't': + out = append(out, '\t') + case 'b': + out = append(out, '\b') + case 'f': + out = append(out, '\f') + default: + out = append(out, b) + } + escaped = false + continue + } + switch b { + case '\\': + escaped = true + case '(': + depth++ + out = append(out, b) + case ')': + depth-- + if depth == 0 { + return strings.TrimSpace(decodePDFLiteralBytes(out)), true + } + out = append(out, b) + default: + out = append(out, b) + } + } + return "", false +} + +func readPDFHexString(data []byte) (string, bool) { + if len(data) == 0 || data[0] != '<' { + return "", false + } + end := bytes.IndexByte(data[1:], '>') + if end < 0 { + return "", false + } + raw := data[1 : end+1] + var cleaned []byte + for _, b := range raw { + switch { + case b == ' ' || b == '\t' || b == '\r' || b == '\n': + continue + case (b >= '0' && b <= '9') || (b >= 'a' && b <= 'f') || (b >= 'A' && b <= 'F'): + cleaned = append(cleaned, b) + default: + return "", false + } + } + if len(cleaned)%2 == 1 { + cleaned = append(cleaned, '0') + } + decoded := make([]byte, hex.DecodedLen(len(cleaned))) + if _, err := hex.Decode(decoded, cleaned); err != nil { + return "", false + } + return strings.TrimSpace(decodePDFLiteralBytes(decoded)), true +} + +func decodePDFLiteralBytes(data []byte) string { + if len(data) == 0 { + return "" + } + switch { + case bytes.HasPrefix(data, []byte{0xfe, 0xff}): + if decoded, err := textunicode.UTF16(textunicode.BigEndian, textunicode.ExpectBOM).NewDecoder().Bytes(data); err == nil { + return string(decoded) + } + case bytes.HasPrefix(data, []byte{0xff, 0xfe}): + if decoded, err := textunicode.UTF16(textunicode.LittleEndian, textunicode.ExpectBOM).NewDecoder().Bytes(data); err == nil { + return string(decoded) + } + } + // The PDF spec says non-UTF-16 strings are PDFDocEncoding, but real-world + // producers commonly emit UTF-8 (PDF 2.0 even allows a UTF-8 BOM). Only + // fall back to the Windows-1252 approximation for non-UTF-8 bytes so + // UTF-8 metadata is not mojibaked. + data = bytes.TrimPrefix(data, []byte{0xef, 0xbb, 0xbf}) + if utf8.Valid(data) { + return string(data) + } + if decoded, err := charmap.Windows1252.NewDecoder().Bytes(data); err == nil { + return string(decoded) + } + return strings.ToValidUTF8(string(data), "") +} + +func splitEbookAuthors(value string) []string { + value = strings.TrimSpace(value) + if value == "" { + return nil + } + parts := strings.FieldsFunc(value, func(r rune) bool { + return r == ';' || r == '|' + }) + if len(parts) == 1 { + parts = strings.Split(value, " and ") + } + return uniqueTrimmedStrings(parts) +} + +func splitPDFKeywords(value string) []string { + if strings.TrimSpace(value) == "" { + return nil + } + return uniqueTrimmedStrings(strings.FieldsFunc(value, func(r rune) bool { + return r == ',' || r == ';' + })) +} + +func parsePDFDate(value string) (time.Time, bool) { + value = strings.TrimSpace(value) + if strings.HasPrefix(value, "D:") { + value = strings.TrimPrefix(value, "D:") + } + value = strings.TrimSuffix(value, "Z") + if len(value) >= 14 { + if t, err := time.Parse("20060102150405", value[:14]); err == nil && t.Year() > 0 { + return t, true + } + } + if len(value) >= 8 { + if t, err := time.Parse("20060102", value[:8]); err == nil && t.Year() > 0 { + return t, true + } + } + if len(value) >= 4 { + if t, err := time.Parse("2006", value[:4]); err == nil && t.Year() > 0 { + return t, true + } + } + return time.Time{}, false +} + +func readEPUBZipEntry(reader *zip.Reader, name string) ([]byte, error) { + for _, file := range reader.File { + if file.Name != name { + continue + } + entry, err := file.Open() + if err != nil { + return nil, err + } + defer entry.Close() + limited := io.LimitReader(entry, maxEPUBMetadataEntrySize+1) + data, err := io.ReadAll(limited) + if err != nil { + return nil, err + } + if len(data) > maxEPUBMetadataEntrySize { + return nil, fmt.Errorf("epub entry too large: %s", name) + } + return data, nil + } + return nil, fmt.Errorf("epub entry not found: %s", name) +} + +func epubOPFPath(container []byte) (string, error) { + var parsed struct { + Rootfiles []struct { + FullPath string `xml:"full-path,attr"` + } `xml:"rootfiles>rootfile"` + } + if err := decodeEbookXML(container, &parsed); err != nil { + return "", err + } + for _, rootfile := range parsed.Rootfiles { + if strings.TrimSpace(rootfile.FullPath) != "" { + return rootfile.FullPath, nil + } + } + return "", fmt.Errorf("epub container has no rootfile") +} + +func parseEPUBOPFMetadata(opf []byte, book *parsedEbook) error { + var parsed struct { + Metadata struct { + Titles []string `xml:"title"` + Creators []string `xml:"creator"` + Identifiers []string `xml:"identifier"` + Publisher string `xml:"publisher"` + Dates []string `xml:"date"` + Language string `xml:"language"` + Subjects []string `xml:"subject"` + Descriptions []string `xml:"description"` + Meta []struct { + Name string `xml:"name,attr"` + Property string `xml:"property,attr"` + Content string `xml:"content,attr"` + Value string `xml:",chardata"` + } `xml:"meta"` + } `xml:"metadata"` + } + if err := decodeEbookXML(opf, &parsed); err != nil { + return err + } + book.Title = firstNonEmpty(parsed.Metadata.Titles...) + book.Authors = append(book.Authors, parsed.Metadata.Creators...) + book.Publisher = parsed.Metadata.Publisher + book.Language = parsed.Metadata.Language + book.Genres = append(book.Genres, parsed.Metadata.Subjects...) + book.Description = firstNonEmpty(parsed.Metadata.Descriptions...) + for _, identifier := range parsed.Metadata.Identifiers { + if isbn := normalizeEbookISBN(identifier); isbn != "" { + book.ISBN = isbn + break + } + } + for _, date := range parsed.Metadata.Dates { + if t, ok := parseEbookDate(date); ok { + book.PublishedAt = t + book.Year = t.Year() + break + } + } + for _, meta := range parsed.Metadata.Meta { + name := strings.ToLower(strings.TrimSpace(firstNonEmpty(meta.Name, meta.Property))) + value := strings.TrimSpace(firstNonEmpty(meta.Content, meta.Value)) + switch name { + case "calibre:series", "belongs-to-collection": + book.Series = value + case "calibre:series_index", "group-position": + book.SeriesIndex = value + case "calibre:isbn", "isbn", "schema:isbn": + if book.ISBN == "" { + book.ISBN = normalizeEbookISBN(value) + } + } + } + return nil +} + +func extractEPUBCover(reader *zip.Reader, opfPath string, opf []byte) (*parsedEbookCover, error) { + var parsed struct { + Metadata struct { + Meta []struct { + Name string `xml:"name,attr"` + Content string `xml:"content,attr"` + } `xml:"meta"` + } `xml:"metadata"` + Manifest struct { + Items []struct { + ID string `xml:"id,attr"` + Href string `xml:"href,attr"` + MediaType string `xml:"media-type,attr"` + Properties string `xml:"properties,attr"` + } `xml:"item"` + } `xml:"manifest"` + } + if err := decodeEbookXML(opf, &parsed); err != nil { + return nil, err + } + + coverID := "" + for _, meta := range parsed.Metadata.Meta { + if strings.EqualFold(strings.TrimSpace(meta.Name), "cover") { + coverID = strings.TrimSpace(meta.Content) + break + } + } + + // An EPUB3 properties="cover-image" item is authoritative; the EPUB2 + // id frequently points at the XHTML cover *page* + // rather than the image, so it ranks lower and non-image manifest items + // are skipped entirely instead of shadowing a later real cover image. + var coverHref string + var coverType string + coverRank := 0 + for _, item := range parsed.Manifest.Items { + rank := 0 + for _, prop := range strings.Fields(strings.ToLower(item.Properties)) { + if prop == "cover-image" { + rank = 2 + break + } + } + if rank == 0 && coverID != "" && strings.TrimSpace(item.ID) == coverID { + rank = 1 + } + if rank <= coverRank || !isEPUBImageManifestItem(item.MediaType, item.Href) { + continue + } + coverHref = strings.TrimSpace(item.Href) + coverType = strings.TrimSpace(item.MediaType) + coverRank = rank + if coverRank == 2 { + break + } + } + if coverHref == "" { + return nil, fmt.Errorf("epub cover not referenced") + } + + coverPath := resolveEPUBRelativePath(opfPath, coverHref) + data, err := readEPUBZipEntry(reader, coverPath) + if err != nil { + return nil, err + } + if coverType == "" { + coverType = ebookImageContentType(coverPath) + } + return &parsedEbookCover{ContentType: coverType, Bytes: data}, nil +} + +func isEPUBImageManifestItem(mediaType, href string) bool { + mt := strings.ToLower(strings.TrimSpace(mediaType)) + if mt != "" { + return strings.HasPrefix(mt, "image/") + } + return ebookImageContentType(href) != "application/octet-stream" +} + +func resolveEPUBRelativePath(baseFile string, href string) string { + cleanHref := strings.TrimSpace(href) + if decoded, err := urlPathUnescape(cleanHref); err == nil { + cleanHref = decoded + } + baseDir := path.Dir(strings.ReplaceAll(baseFile, "\\", "/")) + if baseDir == "." || strings.HasPrefix(cleanHref, "/") { + baseDir = "" + } + return strings.TrimPrefix(path.Clean(path.Join(baseDir, cleanHref)), "/") +} + +func urlPathUnescape(value string) (string, error) { + var out strings.Builder + for i := 0; i < len(value); i++ { + if value[i] != '%' || i+2 >= len(value) { + out.WriteByte(value[i]) + continue + } + hi := fromHex(value[i+1]) + lo := fromHex(value[i+2]) + if hi < 0 || lo < 0 { + out.WriteByte(value[i]) + continue + } + out.WriteByte(byte(hi<<4 | lo)) + i += 2 + } + return out.String(), nil +} + +func fromHex(b byte) int { + switch { + case b >= '0' && b <= '9': + return int(b - '0') + case b >= 'a' && b <= 'f': + return int(b-'a') + 10 + case b >= 'A' && b <= 'F': + return int(b-'A') + 10 + default: + return -1 + } +} + +func ebookImageContentType(name string) string { + switch strings.ToLower(filepath.Ext(name)) { + case ".jpg", ".jpeg": + return "image/jpeg" + case ".png": + return "image/png" + case ".webp": + return "image/webp" + case ".gif": + return "image/gif" + case ".avif": + return "image/avif" + case ".bmp": + return "image/bmp" + default: + return "application/octet-stream" + } +} + +func parseEbookDate(value string) (time.Time, bool) { + value = strings.TrimSpace(value) + for _, layout := range []string{"2006-01-02", "2006-01", "2006"} { + if t, err := time.Parse(layout, value); err == nil { + return t, true + } + } + return time.Time{}, false +} diff --git a/internal/scanner/ebook_scan.go b/internal/scanner/ebook_scan.go new file mode 100644 index 00000000..ea560edc --- /dev/null +++ b/internal/scanner/ebook_scan.go @@ -0,0 +1,1345 @@ +package scanner + +import ( + "context" + "errors" + "fmt" + "io" + "log/slog" + "os" + "path/filepath" + "sort" + "strconv" + "strings" + "sync" + "sync/atomic" + "time" + "unicode" + + "github.com/Silo-Server/silo-server/internal/idgen" + "github.com/Silo-Server/silo-server/internal/imageutil" + "github.com/Silo-Server/silo-server/internal/models" + "github.com/Silo-Server/silo-server/internal/titleutil" + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgconn" +) + +type ebookSQLExecutor interface { + Exec(ctx context.Context, sql string, arguments ...any) (pgconn.CommandTag, error) +} + +type filesystemMediaItemReader interface { + GetByIDs(ctx context.Context, ids []string) ([]*models.MediaItem, error) +} + +func ebookScanWorkers() int { + if v := os.Getenv("SILO_EBOOK_SCAN_WORKERS"); v != "" { + if n, err := strconv.Atoi(v); err == nil && n > 0 { + return n + } + } + return audiobookScanWorkers() +} + +func (s *Scanner) ScanEbookFolder(ctx context.Context, folder *models.MediaFolder) error { + if s == nil || folder == nil { + return fmt.Errorf("ScanEbookFolder: nil scanner or folder") + } + return s.scanEbookPaths(ctx, folder, folder.Paths, true) +} + +// ebookRootScan captures the walk outcome for one configured library root. +type ebookRootScan struct { + root string + files []string + rootErr error // root stat failed, or the root is not a directory + walkFailures int // entries within the subtree the walk could not read or resolve +} + +// failed reports whether the walk under this root is known to be incomplete. +// Files found under a failed root are still indexed, but the root is excluded +// from missing-file reconciliation: a transient mount/permission problem (or +// a mid-walk subtree error) must not cascade into marking — and, with +// empty_trash_after_scan, deleting — everything under it. +func (r *ebookRootScan) failed() bool { + return r.rootErr != nil || r.walkFailures > 0 +} + +// collectEbookRootScans walks every configured root with the shared +// logical-tree walker (so symlinked roots and symlinked subdirectories +// resolve and scan exactly like the video pipeline) and records per-root walk +// failures. The only error returned is context cancellation. +func collectEbookRootScans(ctx context.Context, folderID int, roots []string) ([]ebookRootScan, error) { + scans := make([]ebookRootScan, 0, len(roots)) + visitedPhysicalDirs := make(map[string]struct{}) + for _, root := range roots { + if err := ctx.Err(); err != nil { + return nil, err + } + cleanRoot := filepath.Clean(strings.TrimSpace(root)) + if cleanRoot == "" || cleanRoot == "." { + continue + } + scan := ebookRootScan{root: cleanRoot} + info, statErr := os.Stat(cleanRoot) + switch { + case statErr != nil: + // Unmounted/missing/permission-broken root: failed, not empty. + scan.rootErr = fmt.Errorf("stat root: %w", statErr) + case !info.IsDir(): + scan.rootErr = fmt.Errorf("root is not a directory after symlink resolution") + } + if statErr == nil { + if err := walkLogicalTree(ctx, cleanRoot, cleanRoot, walkModeEbook, visitedPhysicalDirs, &scan.files, &scan.walkFailures); err != nil { + return nil, err + } + } + if scan.failed() { + slog.Warn("ebook scan: root walk incomplete; root excluded from missing-file reconciliation", + "folder_id", folderID, + "root", cleanRoot, + "walk_failures", scan.walkFailures, + "error", scan.rootErr, + ) + } + scans = append(scans, scan) + } + return scans, nil +} + +// splitEbookReconcileRoots partitions walked roots into the set that may take +// part in missing-file reconciliation and reports whether any reconcilable +// root saw at least one ebook on disk. +func splitEbookReconcileRoots(scans []ebookRootScan) (reconcileRoots []string, sawFiles bool) { + reconcileRoots = make([]string, 0, len(scans)) + for i := range scans { + scan := &scans[i] + if scan.failed() { + continue + } + if len(scan.files) > 0 { + sawFiles = true + } + reconcileRoots = append(reconcileRoots, scan.root) + } + return reconcileRoots, sawFiles +} + +func (s *Scanner) scanEbookPaths(ctx context.Context, folder *models.MediaFolder, roots []string, fullScan bool) error { + if s == nil || folder == nil { + return fmt.Errorf("scanEbookPaths: nil scanner or folder") + } + scans, err := collectEbookRootScans(ctx, folder.ID, roots) + if err != nil { + return err + } + // Every discovered file is indexed, including files under roots whose + // walk partially failed: indexing is additive and safe, only the + // destructive reconciliation below is restricted to cleanly walked roots. + var candidates []string + for i := range scans { + candidates = append(candidates, scans[i].files...) + } + + if len(candidates) == 0 { + return s.reconcileEbookScan(ctx, folder, scans, nil, fullScan) + } + + workers := ebookScanWorkers() + slog.Info("ebook scan: starting", + "folder_id", folder.ID, + "candidates", len(candidates), + "workers", workers, + ) + reportEbookScanProgress(ctx, folder.ID, len(candidates), 0, 0, 0) + + ch := make(chan string, workers*2) + groupLocks := newEbookGroupLocks() + var ( + wg sync.WaitGroup + processed int64 + failed int64 + skipped int64 + failMu sync.Mutex + failures []error + cancelErr error + ) + start := time.Now() + for i := 0; i < workers; i++ { + wg.Add(1) + go func() { + defer wg.Done() + for path := range ch { + if ctx.Err() != nil { + return + } + if err := s.reconcileEbookFile(ctx, folder, path, &skipped, groupLocks); err != nil { + if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { + failMu.Lock() + if cancelErr == nil { + cancelErr = err + } + failMu.Unlock() + return + } + atomic.AddInt64(&failed, 1) + failMu.Lock() + failures = append(failures, fmt.Errorf("%s: %w", path, err)) + failMu.Unlock() + slog.Warn("ebook scan: file failed", + "folder_id", folder.ID, + "path", path, + "error", err, + ) + } + n := atomic.AddInt64(&processed, 1) + if n%500 == 0 || n == int64(len(candidates)) { + failedCount := atomic.LoadInt64(&failed) + skippedCount := atomic.LoadInt64(&skipped) + slog.Info("ebook scan: progress", + "folder_id", folder.ID, + "processed", n, + "failed", failedCount, + "skipped", skippedCount, + "total", len(candidates), + "elapsed_sec", int(time.Since(start).Seconds()), + ) + reportEbookScanProgress(ctx, folder.ID, len(candidates), int(n), int(failedCount), int(skippedCount)) + } + } + }() + } + + for _, p := range candidates { + select { + case ch <- p: + case <-ctx.Done(): + close(ch) + wg.Wait() + return ctx.Err() + } + } + close(ch) + wg.Wait() + if err := ctx.Err(); err != nil { + return err + } + if cancelErr != nil { + return cancelErr + } + + slog.Info("ebook scan: completed", + "folder_id", folder.ID, + "processed", atomic.LoadInt64(&processed), + "failed", atomic.LoadInt64(&failed), + "skipped", atomic.LoadInt64(&skipped), + "elapsed_sec", int(time.Since(start).Seconds()), + ) + if processedCount := atomic.LoadInt64(&processed); processedCount > 0 { + failedCount := atomic.LoadInt64(&failed) + skippedCount := atomic.LoadInt64(&skipped) + if failedCount > 0 && skippedCount == 0 && failedCount == processedCount { + return fmt.Errorf("ebook scan failed for every attempted folder_id=%d: %w", folder.ID, errors.Join(failures...)) + } + } + + seenPaths := make(map[string]bool, len(candidates)) + for _, p := range candidates { + seenPaths[p] = true + } + return s.reconcileEbookScan(ctx, folder, scans, seenPaths, fullScan) +} + +// ebookCleanupGuardRepo is the slice of catalog.FolderRepository the +// empty-root cleanup guard needs. +type ebookCleanupGuardRepo interface { + ConsumeEmptyCleanupAllowance(ctx context.Context, id int) (bool, error) + SetScanWarning(ctx context.Context, id int, code, message string, warnedAt time.Time) error +} + +// ebookEmptyCleanupAllowed mirrors the video pipeline's empty-root guard: a +// scan that saw zero ebooks while the DB still has rows under the scanned +// roots may only reconcile (and therefore delete) when the operator has +// explicitly confirmed the cleanup. Subtree scans never consume the +// folder-level allowance; the next full scan converges them. +func ebookEmptyCleanupAllowed(ctx context.Context, repo ebookCleanupGuardRepo, folderID int, fullScan bool) (bool, error) { + if !fullScan || repo == nil { + return false, nil + } + allowed, err := repo.ConsumeEmptyCleanupAllowance(ctx, folderID) + if err != nil { + return false, fmt.Errorf("checking empty cleanup confirmation for folder %d: %w", folderID, err) + } + if !allowed { + if err := repo.SetScanWarning(ctx, folderID, + "empty_root", + "Scan found 0 media files; cleanup was skipped until deletion is confirmed.", + time.Now().UTC(), + ); err != nil { + return false, fmt.Errorf("recording empty-root warning for folder %d: %w", folderID, err) + } + } + return allowed, nil +} + +// reconcileEbookScan applies the post-walk safety policy and then performs +// missing-file reconciliation for the roots that walked cleanly. +func (s *Scanner) reconcileEbookScan(ctx context.Context, folder *models.MediaFolder, scans []ebookRootScan, seenPaths map[string]bool, fullScan bool) error { + reconcileRoots, sawFiles := splitEbookReconcileRoots(scans) + if len(reconcileRoots) == 0 { + if len(scans) > 0 { + slog.Warn("ebook scan: every root walk failed; skipping missing-file reconciliation", + "folder_id", folder.ID, + ) + } + return nil + } + if s.fileRepo == nil || s.libraryRepo == nil { + return nil + } + + if !sawFiles { + existingCount := 0 + for _, root := range reconcileRoots { + existing, err := s.fileRepo.GetByFolderAndPathPrefix(ctx, folder.ID, root) + if err != nil { + return fmt.Errorf("listing existing ebook files for %q: %w", root, err) + } + existingCount += len(existing) + } + if existingCount > 0 { + var guard ebookCleanupGuardRepo + if s.folderRepo != nil { + guard = s.folderRepo + } + allowed, err := ebookEmptyCleanupAllowed(ctx, guard, folder.ID, fullScan) + if err != nil { + return err + } + if !allowed { + slog.Warn("ebook scan: walk saw zero ebooks but the database has files under the scanned roots; skipping reconciliation until cleanup is confirmed", + "folder_id", folder.ID, + "existing_files", existingCount, + "full_scan", fullScan, + ) + return nil + } + } + } + + if err := s.reconcileMissingEbookFiles(ctx, folder, reconcileRoots, seenPaths); err != nil { + return err + } + if fullScan && s.folderRepo != nil { + // The cleanup either ran with files present or was explicitly + // confirmed; any prior empty-root warning is stale now. + if err := s.folderRepo.ClearScanWarning(ctx, folder.ID); err != nil { + return fmt.Errorf("clearing scan warning for folder %d: %w", folder.ID, err) + } + } + return nil +} + +// reconcileMissingEbookFiles mirrors the video/audio scan cleanup: DB files +// under the scanned roots that were not seen on disk are marked missing, the +// folder trash is optionally emptied, and library memberships are reconciled +// so items with no remaining files are removed (renames therefore converge on +// the newly indexed path instead of leaving a stale duplicate item). +func (s *Scanner) reconcileMissingEbookFiles(ctx context.Context, folder *models.MediaFolder, roots []string, seenPaths map[string]bool) error { + if s.fileRepo == nil || s.libraryRepo == nil || len(roots) == 0 { + return nil + } + + now := time.Now().UTC() + missing := 0 + for _, root := range roots { + existing, err := s.fileRepo.GetByFolderAndPathPrefix(ctx, folder.ID, root) + if err != nil { + return fmt.Errorf("listing existing ebook files for %q: %w", root, err) + } + for _, mf := range existing { + if mf == nil || seenPaths[mf.FilePath] { + continue + } + if mf.MissingSince == nil { + if err := s.fileRepo.MarkMissing(ctx, mf.ID, now); err != nil { + slog.Error("ebook scan: failed to mark file missing", + "folder_id", folder.ID, + "path", mf.FilePath, + "error", err, + ) + continue + } + } + missing++ + } + } + + if s.emptyTrashAfterScan { + trashed, err := s.fileRepo.DeleteMissingByFolder(ctx, folder.ID) + if err != nil { + return fmt.Errorf("emptying trash for folder %d: %w", folder.ID, err) + } + if trashed > 0 { + slog.Info("ebook scan: emptied trash", "folder_id", folder.ID, "deleted", trashed) + } + } + + removedMemberships, deletedItems, orphanedImageDirs, err := s.reconcileLibraryMemberships(ctx, folder.ID) + if err != nil { + return fmt.Errorf("reconciling library membership for folder %d: %w", folder.ID, err) + } + + // Best-effort S3 image cleanup for orphaned items. + if s.s3Client != nil && len(orphanedImageDirs) > 0 { + bucket := s.s3Client.Bucket() + for _, dir := range orphanedImageDirs { + _, _ = s.s3Client.DeletePrefix(ctx, bucket, dir) + } + } + + if missing > 0 || removedMemberships > 0 || deletedItems > 0 { + slog.Info("ebook scan: reconciled missing files", + "folder_id", folder.ID, + "missing", missing, + "memberships_removed", removedMemberships, + "items_deleted", deletedItems, + ) + } + return nil +} + +func reportEbookScanProgress(ctx context.Context, folderID int, total, processed, failed, skipped int) { + reportProgress(ctx, ProgressUpdate{ + Phase: "ebook_scan", + Message: fmt.Sprintf("Scanning ebooks in folder %d", folderID), + CurrentScope: strconv.Itoa(folderID), + TotalFiles: total, + FilesDiscovered: total, + FilesProcessed: processed, + Errors: failed, + Unchanged: skipped, + }) +} + +func (s *Scanner) reconcileEbookFile(ctx context.Context, folder *models.MediaFolder, filePath string, skipped *int64, groupLocks *ebookGroupLocks) error { + info, err := os.Stat(filePath) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + return nil + } + return fmt.Errorf("stat ebook file %s: %w", filePath, err) + } + size := info.Size() + modifiedAt := normalizeFileModifiedAt(info.ModTime()) + + isUnchanged, skipErr := s.ebookFileShouldSkip(ctx, folder, filePath, size, modifiedAt) + if skipErr != nil { + slog.Warn("ebook scan: skip-check failed, falling through", + "folder_id", folder.ID, + "path", filePath, + "error", skipErr, + ) + } else if isUnchanged { + atomic.AddInt64(skipped, 1) + return nil + } + + parsed, err := parseEbookFile(filePath) + if err != nil { + return fmt.Errorf("parse ebook file %s: %w", filePath, err) + } + if parsed.Title == "" { + parsed.Title = ebookTitleFromPath(filePath) + } + + groupKey := ebookContentGroupKey(&parsed, filePath) + unlock := groupLocks.lock(groupKey) + defer unlock() + + contentID, curated, err := s.upsertEbookMediaItem(ctx, folder.ID, filePath, &parsed, groupKey) + if err != nil { + return fmt.Errorf("upsert ebook item: %w", err) + } + if err := s.upsertEbookMediaFile(ctx, folder, contentID, filePath, size, modifiedAt, &parsed, groupKey); err != nil { + return fmt.Errorf("upsert ebook file: %w", err) + } + if err := applyEbookLocalCover(ctx, s.itemRepo, s.imageCacher, contentID, filePath, &parsed); err != nil { + slog.Warn("ebook scan: local cover upload failed", + "folder_id", folder.ID, + "content_id", contentID, + "path", filePath, + "error", err, + ) + } + if err := s.upsertEbookPeople(ctx, contentID, &parsed, curated); err != nil { + return fmt.Errorf("upsert ebook people: %w", err) + } + if err := s.upsertEbookSeries(ctx, contentID, &parsed, curated); err != nil { + return fmt.Errorf("upsert ebook series: %w", err) + } + if err := insertEbookLibraryMembership(ctx, s.fileRepo.Pool(), contentID, folder.ID); err != nil { + return fmt.Errorf("upsert ebook library membership: %w", err) + } + if parsed.ISBN != "" { + if err := insertEbookISBNProviderID(ctx, s.fileRepo.Pool(), contentID, parsed.ISBN); err != nil { + return fmt.Errorf("upsert ebook ISBN provider id: %w", err) + } + } + slog.Info("ebook scan: indexed", + "folder_id", folder.ID, + "content_id", contentID, + "title", parsed.Title, + "authors", parsed.Authors, + "path", filePath, + ) + return nil +} + +func (s *Scanner) ebookFileShouldSkip(ctx context.Context, folder *models.MediaFolder, filePath string, size int64, modifiedAt time.Time) (bool, error) { + if s.fileRepo == nil || s.itemRepo == nil { + return false, nil + } + existing, err := s.fileRepo.ListByObservedRootPath(ctx, folder.ID, filePath) + if err != nil { + return false, fmt.Errorf("list existing files: %w", err) + } + if len(existing) != 1 { + return false, nil + } + mf := existing[0] + if mf.FilePath != filePath || mf.FileSize != size || mf.FileModifiedAt == nil || !sameFileModifiedAt(mf.FileModifiedAt, modifiedAt) { + return false, nil + } + if mf.ContentID == "" { + return false, nil + } + if mf.GroupKeyVersion != ebookGroupKeyVersion { + // The grouping scheme changed since this row was written; reprocess + // once so the stored key is rewritten under the current scheme and + // sibling-format lookups can find it again. + return false, nil + } + statuses, err := s.itemRepo.GetStatusByIDs(ctx, []string{mf.ContentID}) + if err != nil { + return false, fmt.Errorf("get item status: %w", err) + } + return !strings.EqualFold(strings.TrimSpace(statuses[mf.ContentID]), "unmatched"), nil +} + +// upsertEbookMediaItem resolves or creates the media item for the file and +// reports whether the item's metadata is curated (provider-matched), in which +// case dependent writes (people, series) must be fill-empty only. +func (s *Scanner) upsertEbookMediaItem(ctx context.Context, folderID int, filePath string, book *parsedEbook, groupKey string) (string, bool, error) { + if s.itemRepo == nil { + return "", false, fmt.Errorf("itemRepo not configured on Scanner") + } + if s.fileRepo == nil { + return "", false, fmt.Errorf("fileRepo not configured on Scanner") + } + + existingID, err := s.fileRepo.FindContentIDByRootPath(ctx, folderID, filePath, "ebook") + if err != nil { + return "", false, fmt.Errorf("find ebook by root path: %w", err) + } + if existingID != "" { + curated, err := updateExistingEbookMediaItem(ctx, s.itemRepo, s.itemRepo, existingID, book) + if err != nil { + return "", false, err + } + return existingID, curated, nil + } + if existing := s.findEbookByFilePath(ctx, filePath); existing != nil { + if ebookItemHasCuratedMetadata(existing) { + return existing.ContentID, true, nil + } + applyEbookToMediaItem(existing, book) + if existing.SortTitle == "" { + existing.SortTitle = titleutil.DeriveDefaultSortTitle(existing.Title) + } + if err := s.itemRepo.Upsert(ctx, existing); err != nil { + return "", false, err + } + return existing.ContentID, false, nil + } + if existing := s.findEbookByContentGroupKey(ctx, folderID, groupKey, book.Format, filePath); existing != nil { + // The sibling file joins the group either way, but curated metadata + // on a provider-matched item must not be clobbered by whatever this + // file happens to embed. + if ebookItemHasCuratedMetadata(existing) { + return existing.ContentID, true, nil + } + applyEbookToMediaItem(existing, book) + if existing.SortTitle == "" { + existing.SortTitle = titleutil.DeriveDefaultSortTitle(existing.Title) + } + if err := s.itemRepo.Upsert(ctx, existing); err != nil { + return "", false, err + } + return existing.ContentID, false, nil + } + contentID, err := resolveEbookMediaItem(ctx, s.fileRepo, s.itemRepo, folderID, filePath, book) + if err != nil { + return "", false, err + } + return contentID, false, nil +} + +func resolveEbookMediaItem( + ctx context.Context, + rootFinder filesystemRootContentFinder, + itemWriter filesystemMediaItemWriter, + folderID int, + filePath string, + book *parsedEbook, +) (string, error) { + if rootFinder == nil { + return "", fmt.Errorf("root content finder not configured") + } + if itemWriter == nil { + return "", fmt.Errorf("media item writer not configured") + } + existingID, err := rootFinder.FindContentIDByRootPath(ctx, folderID, filePath, "ebook") + if err != nil { + return "", fmt.Errorf("find ebook by root path: %w", err) + } + if existingID != "" { + return existingID, nil + } + + cleanTitle := strings.TrimSpace(book.Title) + if cleanTitle == "" { + cleanTitle = ebookTitleFromPath(filePath) + } + return createEbookMediaItem(ctx, itemWriter, book, cleanTitle) +} + +func createEbookMediaItem(ctx context.Context, itemWriter filesystemMediaItemWriter, book *parsedEbook, cleanTitle string) (string, error) { + id, err := idgen.NextID() + if err != nil { + return "", fmt.Errorf("generate content_id: %w", err) + } + item := &models.MediaItem{ + ContentID: id, + Type: "ebook", + // Explicit "pending" (the item upsert writes the literal status, so the + // DB default never applies): enrichment promotes it to "matched", which + // arms ebookItemHasCuratedMetadata against file-metadata clobbering. + Status: "pending", + Title: cleanTitle, + SortTitle: titleutil.DeriveDefaultSortTitle(cleanTitle), + Year: book.Year, + } + applyEbookToMediaItem(item, book) + if item.SortTitle == "" { + item.SortTitle = titleutil.DeriveDefaultSortTitle(item.Title) + } + if err := itemWriter.Upsert(ctx, item); err != nil { + return "", err + } + return id, nil +} + +// updateExistingEbookMediaItem refreshes a known item from file metadata and +// reports whether the item's metadata is curated (and was therefore left +// untouched). +func updateExistingEbookMediaItem(ctx context.Context, itemReader filesystemMediaItemReader, itemWriter filesystemMediaItemWriter, contentID string, book *parsedEbook) (bool, error) { + if itemReader == nil { + return false, fmt.Errorf("media item reader not configured") + } + if itemWriter == nil { + return false, fmt.Errorf("media item writer not configured") + } + items, err := itemReader.GetByIDs(ctx, []string{contentID}) + if err != nil { + return false, fmt.Errorf("get ebook media item %s: %w", contentID, err) + } + if len(items) == 0 || items[0] == nil { + return false, fmt.Errorf("ebook media item %s not found", contentID) + } + item := items[0] + if ebookItemHasCuratedMetadata(item) { + // A matched item's metadata was curated by a provider or a person; + // re-parsing the file (mtime change, group-key version bump) must not + // clobber it with embedded file metadata. + return true, nil + } + applyEbookToMediaItem(item, book) + if item.SortTitle == "" { + item.SortTitle = titleutil.DeriveDefaultSortTitle(item.Title) + } + return false, itemWriter.Upsert(ctx, item) +} + +// ebookItemHasCuratedMetadata reports whether the item's metadata is owned by +// a provider match (or manual curation) and therefore must not be overwritten +// by metadata embedded in scanned files. +func ebookItemHasCuratedMetadata(item *models.MediaItem) bool { + return item != nil && strings.EqualFold(strings.TrimSpace(item.Status), "matched") +} + +func applyEbookToMediaItem(item *models.MediaItem, book *parsedEbook) { + item.Type = "ebook" + if title := strings.TrimSpace(book.Title); title != "" { + item.Title = title + } + if book.Year > 0 { + item.Year = book.Year + } + if book.Description != "" && (item.Overview == "" || looksLikeHTML(item.Overview)) { + item.Overview = book.Description + } + if book.Publisher != "" { + item.Studios = mergeUniqueStrings(item.Studios, []string{book.Publisher}) + } + if len(book.Genres) > 0 { + item.Genres = mergeUniqueStrings(item.Genres, book.Genres) + } + if !book.PublishedAt.IsZero() && item.ReleaseDate == nil { + rd := book.PublishedAt.UTC().Format("2006-01-02") + item.ReleaseDate = &rd + } + if book.Language != "" && item.OriginalLanguage == "" { + item.OriginalLanguage = book.Language + } +} + +func (s *Scanner) findEbookByFilePath(ctx context.Context, filePath string) *models.MediaItem { + if s.fileRepo == nil { + return nil + } + var existingID string + err := s.fileRepo.Pool().QueryRow(ctx, ` + SELECT mf.content_id + FROM media_files mf + JOIN media_items mi ON mi.content_id = mf.content_id + WHERE mf.file_path = $1 + AND mi.type = 'ebook' + LIMIT 1 + `, filePath).Scan(&existingID) + if err != nil || existingID == "" { + return nil + } + items, err := s.itemRepo.GetByIDs(ctx, []string{existingID}) + if err != nil || len(items) == 0 { + return nil + } + return items[0] +} + +// findEbookByContentGroupKey locates an existing item that a sibling format of +// the same book should join. Grouping merges *different* formats only: an item +// that already owns another file of the same format is excluded, so two +// distinct books whose sparse metadata collides on the same key (e.g. every +// volume carrying the series name as its title) stay separate items instead of +// silently merging. +func (s *Scanner) findEbookByContentGroupKey(ctx context.Context, folderID int, groupKey string, format string, filePath string) *models.MediaItem { + if s.fileRepo == nil || s.itemRepo == nil || strings.TrimSpace(groupKey) == "" { + return nil + } + var existingID string + err := s.fileRepo.Pool().QueryRow(ctx, ` + SELECT mf.content_id + FROM media_files mf + JOIN media_items mi ON mi.content_id = mf.content_id + WHERE mf.media_folder_id = $1 + AND mf.group_key_version = $2 + AND mf.content_group_key = $3 + AND mf.missing_since IS NULL + AND mi.type = 'ebook' + AND NOT EXISTS ( + SELECT 1 FROM media_files dup + WHERE dup.content_id = mf.content_id + AND dup.missing_since IS NULL + AND lower(dup.container) = lower($4) + AND dup.file_path <> $5 + ) + ORDER BY CASE WHEN lower(trim(mi.status)) = 'matched' THEN 0 ELSE 1 END, + mf.id ASC + LIMIT 1 + `, folderID, ebookGroupKeyVersion, groupKey, format, filePath).Scan(&existingID) + if err != nil || existingID == "" { + return nil + } + items, err := s.itemRepo.GetByIDs(ctx, []string{existingID}) + if err != nil || len(items) == 0 { + return nil + } + return items[0] +} + +// ebookGroupKeyVersion versions the ebookContentGroupKey scheme. Bump it +// whenever the key shape changes so rows keyed under an older scheme are +// reprocessed and re-keyed (see ebookFileShouldSkip) instead of silently never +// matching sibling-format lookups again. +const ebookGroupKeyVersion = 2 + +func (s *Scanner) upsertEbookMediaFile(ctx context.Context, folder *models.MediaFolder, contentID string, filePath string, size int64, modifiedAt time.Time, book *parsedEbook, groupKey string) error { + mf := buildEbookMediaFile(folder, contentID, filePath, size, modifiedAt, book, groupKey) + if _, err := s.fileRepo.Upsert(ctx, mf); err != nil { + return fmt.Errorf("upsert media file %s: %w", filePath, err) + } + return nil +} + +func buildEbookMediaFile(folder *models.MediaFolder, contentID string, filePath string, size int64, modifiedAt time.Time, book *parsedEbook, groupKey string) models.MediaFile { + return models.MediaFile{ + ContentID: contentID, + MediaFolderID: folder.ID, + CanonicalRootPath: filePath, + ObservedRootPath: filePath, + ContentGroupKey: groupKey, + GroupKeyVersion: ebookGroupKeyVersion, + BaseTitle: book.Title, + BaseYear: book.Year, + BaseType: "ebook", + IdentityConfidence: ebookIdentityConfidence(book), + FilePath: filePath, + FileSize: size, + FileModifiedAt: &modifiedAt, + Container: book.Format, + Duration: book.PageCount, + ProbeSource: "local", + } +} + +func ebookContentGroupKey(book *parsedEbook, filePath string) string { + if book != nil { + if isbn := normalizeEbookISBN(book.ISBN); isbn != "" { + return "ebook:isbn:" + isbn + } + } + + title := "" + authors := []string(nil) + if book != nil { + title = strings.TrimSpace(book.Title) + authors = book.Authors + } + if title == "" { + title = ebookTitleFromPath(filePath) + } + normalizedTitle := normalizeEbookIdentityPart(title) + if normalizedTitle == "" { + cleanPath := strings.TrimSpace(filepath.Clean(filePath)) + if cleanPath == "." { + return "" + } + return "ebook:path:" + cleanPath + } + + normalizedAuthors := make([]string, 0, len(authors)) + seenAuthors := make(map[string]struct{}, len(authors)) + for _, author := range authors { + normalized := normalizeEbookIdentityPart(author) + if normalized == "" { + continue + } + if _, ok := seenAuthors[normalized]; ok { + continue + } + seenAuthors[normalized] = struct{}{} + normalizedAuthors = append(normalizedAuthors, normalized) + } + sort.Strings(normalizedAuthors) + if len(normalizedAuthors) > 0 { + return "ebook:title_author:" + normalizedTitle + "|" + strings.Join(normalizedAuthors, ",") + } + + dir := strings.TrimSpace(filepath.Clean(filepath.Dir(filePath))) + if dir == "." { + dir = "" + } + return "ebook:title:" + normalizedTitle + "|dir:" + normalizeEbookIdentityPart(dir) +} + +func ebookTitleFromPath(filePath string) string { + base := filepath.Base(filePath) + if base == "." || base == string(filepath.Separator) { + return "" + } + // ".fb2.zip" is a double extension that filepath.Ext (and the + // normalized ".fbz" format token) would leave half-stripped. + if strings.HasSuffix(strings.ToLower(base), ".fb2.zip") { + return base[:len(base)-len(".fb2.zip")] + } + return strings.TrimSuffix(base, filepath.Ext(base)) +} + +func normalizeEbookIdentityPart(value string) string { + value = strings.ToLower(strings.TrimSpace(value)) + var b strings.Builder + previousSpace := false + for _, r := range value { + if unicode.IsLetter(r) || unicode.IsDigit(r) { + b.WriteRune(r) + previousSpace = false + continue + } + if unicode.IsSpace(r) || r == '&' || r == '-' || r == '_' || r == ':' || r == '/' || r == '\\' || r == ',' || r == '.' { + if b.Len() > 0 && !previousSpace { + b.WriteByte(' ') + previousSpace = true + } + } + } + return strings.TrimSpace(b.String()) +} + +type ebookGroupLocks struct { + mu sync.Mutex + locks map[string]*sync.Mutex +} + +func newEbookGroupLocks() *ebookGroupLocks { + return &ebookGroupLocks{locks: make(map[string]*sync.Mutex)} +} + +func (l *ebookGroupLocks) lock(key string) func() { + if l == nil || strings.TrimSpace(key) == "" { + return func() {} + } + l.mu.Lock() + groupLock := l.locks[key] + if groupLock == nil { + groupLock = &sync.Mutex{} + l.locks[key] = groupLock + } + l.mu.Unlock() + + groupLock.Lock() + return groupLock.Unlock +} + +// localEbookPosterPrefix mirrors the storage layout produced by +// imagecache.CacheEbookCover ("local/ebooks/{contentID}/poster/..."); it marks +// posters this pipeline owns and is therefore allowed to refresh. +const localEbookPosterPrefix = "local/ebooks/" + +type ebookCoverMetadataStore interface { + GetPoster(ctx context.Context, contentID string) (posterPath string, posterThumbhash string, err error) + SetLocalPoster(ctx context.Context, contentID, posterPath, thumbhash, localPrefix string) (bool, error) +} + +// applyEbookLocalCover records the best locally available cover for the file: +// a sidecar image that belongs to this book wins over the embedded cover, and +// exactly one cover is applied per reconcile. A sidecar discovery error does +// not block the embedded fallback. +func applyEbookLocalCover(ctx context.Context, store ebookCoverMetadataStore, cacher ebookCoverCacher, contentID string, ebookFilePath string, book *parsedEbook) error { + if store == nil || cacher == nil || contentID == "" { + return nil + } + var data []byte + var sidecarErr error + if ebookFilePath != "" { + cover, _, err := findSidecarBookCover(ebookFilePath) + if err != nil { + sidecarErr = err + } else if cover != nil { + data = cover.Bytes + } + } + if len(data) == 0 && book != nil && book.Cover != nil { + data = book.Cover.Bytes + } + if len(data) == 0 { + return sidecarErr + } + return errors.Join(sidecarErr, cacheEbookCoverBytes(ctx, store, cacher, contentID, data)) +} + +func cacheEbookCoverBytes(ctx context.Context, store ebookCoverMetadataStore, cacher ebookCoverCacher, contentID string, data []byte) error { + if len(data) == 0 { + return nil + } + existingPath, existingThumbhash, err := store.GetPoster(ctx, contentID) + if err != nil { + return fmt.Errorf("get ebook poster for cover: %w", err) + } + existingPath = strings.TrimSpace(existingPath) + if existingPath != "" { + // Provider or manually applied artwork always wins over scan covers. + if !strings.HasPrefix(existingPath, localEbookPosterPrefix) { + return nil + } + // Re-extracted bytes of an unchanged cover hash identically; skip + // the variant regeneration and upload churn. A replaced cover hashes + // differently and falls through to refresh the stale poster. + if thumbhash, err := imageutil.Thumbhash(data); err == nil && thumbhash == existingThumbhash { + return nil + } + } + basePath, ext, thumbhash, err := cacher.CacheEbookCover(ctx, data, contentID) + if err != nil { + return err + } + posterPath := strings.TrimRight(basePath, "/") + "/original" + ext + if _, err := store.SetLocalPoster(ctx, contentID, posterPath, thumbhash, localEbookPosterPrefix); err != nil { + return fmt.Errorf("set ebook local poster: %w", err) + } + return nil +} + +var sidecarCoverNames = []string{"cover", "folder", "front", "poster", "thumbnail"} +var sidecarCoverExtensions = []string{".jpg", ".jpeg", ".png", ".webp", ".avif", ".gif", ".bmp"} + +// findSidecarBookCover looks for cover art next to the ebook file. An image +// named after the book file always belongs to it; the generic artwork names +// (cover.jpg, folder.png, ...) are trusted only when this is the directory's +// sole ebook, so one cover.jpg in a flat multi-book folder is not applied to +// every book in it. +func findSidecarBookCover(ebookFilePath string) (*parsedEbookCover, string, error) { + if ebookFilePath == "" { + return nil, "", nil + } + entries, err := os.ReadDir(filepath.Dir(ebookFilePath)) + if err != nil { + return nil, "", err + } + byName := make(map[string]string, len(entries)) + ebookCount := 0 + for _, entry := range entries { + if entry.IsDir() { + continue + } + if SupportsEbookFile(entry.Name()) { + ebookCount++ + } + byName[strings.ToLower(entry.Name())] = filepath.Join(filepath.Dir(ebookFilePath), entry.Name()) + } + + var candidates []string + if base := strings.ToLower(ebookTitleFromPath(ebookFilePath)); base != "" { + for _, ext := range sidecarCoverExtensions { + candidates = append(candidates, base+ext) + } + } + if ebookCount <= 1 { + for _, name := range sidecarCoverNames { + for _, ext := range sidecarCoverExtensions { + candidates = append(candidates, name+ext) + } + } + } + for _, candidate := range candidates { + path := byName[candidate] + if path == "" { + continue + } + data, err := readSidecarCover(path) + if err != nil { + return nil, path, err + } + return &parsedEbookCover{ + ContentType: ebookImageContentType(path), + Bytes: data, + }, path, nil + } + return nil, "", nil +} + +func readSidecarCover(path string) ([]byte, error) { + file, err := os.Open(path) + if err != nil { + return nil, err + } + defer file.Close() + info, err := file.Stat() + if err != nil { + return nil, err + } + if info.Size() > maxEPUBMetadataEntrySize { + return nil, fmt.Errorf("sidecar cover too large: %s", path) + } + data, err := io.ReadAll(io.LimitReader(file, maxEPUBMetadataEntrySize+1)) + if err != nil { + return nil, err + } + if len(data) > maxEPUBMetadataEntrySize { + return nil, fmt.Errorf("sidecar cover too large: %s", path) + } + if len(data) == 0 { + return nil, fmt.Errorf("sidecar cover empty: %s", path) + } + return data, nil +} + +func insertEbookLibraryMembership(ctx context.Context, exec ebookSQLExecutor, contentID string, folderID int) error { + _, err := exec.Exec(ctx, ` + INSERT INTO media_item_libraries (content_id, media_folder_id, first_seen_at) + VALUES ($1, $2, NOW()) + ON CONFLICT (content_id, media_folder_id) DO NOTHING + `, contentID, folderID) + return err +} + +func insertEbookISBNProviderID(ctx context.Context, exec ebookSQLExecutor, contentID string, isbn string) error { + _, err := exec.Exec(ctx, ` + INSERT INTO media_item_provider_ids (content_id, provider, provider_id, item_type) + VALUES ($1, 'isbn', $2, 'ebook') + ON CONFLICT DO NOTHING + `, contentID, isbn) + return err +} + +type ebookCredit struct { + Name string + Kind models.PersonKind +} + +func ebookPeopleCreditsEqual(existing []models.ItemPerson, desired []ebookCredit) bool { + existingAuthors := make([]models.ItemPerson, 0, len(existing)) + for _, p := range existing { + if p.Kind == models.PersonKindNarrator { + return false + } + if p.Kind == models.PersonKindAuthor { + existingAuthors = append(existingAuthors, p) + } + } + if len(existingAuthors) != len(desired) { + return false + } + type key struct { + name string + kind models.PersonKind + } + have := make(map[key]struct{}, len(existingAuthors)) + for _, p := range existingAuthors { + have[key{strings.ToLower(strings.TrimSpace(p.Person.Name)), p.Kind}] = struct{}{} + } + for _, d := range desired { + k := key{strings.ToLower(strings.TrimSpace(d.Name)), d.Kind} + if _, ok := have[k]; !ok { + return false + } + } + return true +} + +type ebookResolvedAuthor struct { + ID int64 + Name string +} + +func mergeEbookPeople(existing []models.ItemPerson, authors []ebookResolvedAuthor) []models.ItemPerson { + people := make([]models.ItemPerson, 0, len(existing)+len(authors)) + for _, p := range existing { + if p.Kind == models.PersonKindAuthor || p.Kind == models.PersonKindNarrator { + continue + } + p.SortOrder = len(people) + people = append(people, p) + } + for _, author := range authors { + people = append(people, models.ItemPerson{ + Person: models.Person{ID: author.ID, Name: author.Name}, + Kind: models.PersonKindAuthor, + SortOrder: len(people), + }) + } + return people +} + +func ebookPeopleForReplace(existing []models.ItemPerson, getErr error, authors []ebookResolvedAuthor) ([]models.ItemPerson, error) { + if getErr != nil { + return nil, fmt.Errorf("get ebook people: %w", getErr) + } + return mergeEbookPeople(existing, authors), nil +} + +// ebookPeopleWriteAllowed reports whether file-embedded authors may be written +// for the item. Curated (provider-matched) items are fill-empty only: file +// metadata may supply authors when the item has none, but must never replace +// provider-enriched author credits. +func ebookPeopleWriteAllowed(curated bool, existing []models.ItemPerson) bool { + if !curated { + return true + } + for _, p := range existing { + if p.Kind == models.PersonKindAuthor { + return false + } + } + return true +} + +func (s *Scanner) upsertEbookPeople(ctx context.Context, contentID string, book *parsedEbook, curated bool) error { + if s.personRepo == nil { + return fmt.Errorf("personRepo not configured on Scanner") + } + if s.itemRepo == nil { + return fmt.Errorf("itemRepo not configured on Scanner") + } + + var desired []ebookCredit + for _, author := range book.Authors { + if name := strings.TrimSpace(author); name != "" { + desired = append(desired, ebookCredit{Name: name, Kind: models.PersonKindAuthor}) + } + } + if len(desired) == 0 { + return nil + } + + existing, err := s.itemRepo.GetPeople(ctx, contentID) + if err != nil { + return fmt.Errorf("get ebook people: %w", err) + } + if !ebookPeopleWriteAllowed(curated, existing) { + return nil + } + if ebookPeopleCreditsEqual(existing, desired) { + return nil + } + + authors := make([]ebookResolvedAuthor, 0, len(desired)) + for _, c := range desired { + personID, err := s.personRepo.FindOrCreate(ctx, models.Person{Name: c.Name}) + if err != nil { + return fmt.Errorf("find-or-create person %q: %w", c.Name, err) + } + authors = append(authors, ebookResolvedAuthor{ID: personID, Name: c.Name}) + } + people, err := ebookPeopleForReplace(existing, nil, authors) + if err != nil { + return err + } + return s.itemRepo.ReplacePeople(ctx, contentID, people) +} + +func (s *Scanner) upsertEbookSeries(ctx context.Context, contentID string, book *parsedEbook, curated bool) error { + if s == nil { + return fmt.Errorf("Scanner not configured") + } + if s.fileRepo == nil { + return fmt.Errorf("fileRepo not configured on Scanner") + } + + var currentName *string + var currentIdx *float64 + err := s.fileRepo.Pool().QueryRow(ctx, ` + SELECT series_name, series_index FROM ebook_series WHERE content_id = $1 + `, contentID).Scan(¤tName, ¤tIdx) + + plan, err := planEbookSeriesWrite(book, currentName, currentIdx, err, curated) + if err != nil { + return err + } + + switch plan.Kind { + case ebookSeriesWriteNone: + return nil + case ebookSeriesWriteDelete: + if _, delErr := s.fileRepo.Pool().Exec(ctx, + `DELETE FROM ebook_series WHERE content_id = $1`, contentID); delErr != nil { + return fmt.Errorf("delete ebook_series row: %w", delErr) + } + return nil + case ebookSeriesWriteUpsert: + var idx any + if plan.Index != nil { + idx = *plan.Index + } + if _, err := s.fileRepo.Pool().Exec(ctx, ` + INSERT INTO ebook_series (content_id, series_name, series_index, updated_at) + VALUES ($1, $2, $3, NOW()) + ON CONFLICT (content_id) DO UPDATE SET + series_name = EXCLUDED.series_name, + series_index = EXCLUDED.series_index, + updated_at = NOW() + `, contentID, plan.Name, idx); err != nil { + return fmt.Errorf("upsert ebook_series row: %w", err) + } + return nil + default: + return fmt.Errorf("unknown ebook_series write kind: %d", plan.Kind) + } +} + +func ebookSeriesDesired(book *parsedEbook) (string, *float64) { + if book == nil { + return "", nil + } + return strings.TrimSpace(book.Series), parseSeriesIndex(book.SeriesIndex) +} + +type ebookSeriesWriteKind int + +const ( + ebookSeriesWriteNone ebookSeriesWriteKind = iota + ebookSeriesWriteDelete + ebookSeriesWriteUpsert +) + +type ebookSeriesWritePlan struct { + Kind ebookSeriesWriteKind + Name string + Index *float64 +} + +// planEbookSeriesWrite decides how the ebook_series row should change for +// file-embedded series metadata. fillOnly applies to curated +// (provider-matched) items: the file may supply a series when the item has +// none, but must never replace or delete an existing (provider-enriched) row. +func planEbookSeriesWrite(book *parsedEbook, currentName *string, currentIdx *float64, queryErr error, fillOnly bool) (ebookSeriesWritePlan, error) { + if queryErr != nil { + if !errors.Is(queryErr, pgx.ErrNoRows) { + return ebookSeriesWritePlan{}, fmt.Errorf("query ebook_series: %w", queryErr) + } + currentName = nil + currentIdx = nil + } + + desiredName, desiredIdx := ebookSeriesDesired(book) + if desiredName == "" { + if currentName == nil || fillOnly { + return ebookSeriesWritePlan{Kind: ebookSeriesWriteNone}, nil + } + return ebookSeriesWritePlan{Kind: ebookSeriesWriteDelete}, nil + } + + if currentName != nil { + if fillOnly { + return ebookSeriesWritePlan{Kind: ebookSeriesWriteNone}, nil + } + if *currentName == desiredName && floatPtrEqual(currentIdx, desiredIdx) { + return ebookSeriesWritePlan{Kind: ebookSeriesWriteNone}, nil + } + } + + return ebookSeriesWritePlan{ + Kind: ebookSeriesWriteUpsert, + Name: desiredName, + Index: desiredIdx, + }, nil +} + +func ebookIdentityConfidence(book *parsedEbook) string { + if book == nil { + return "low" + } + + score := 0 + if book.Title != "" { + score++ + } + if len(book.Authors) > 0 { + score++ + } + if book.Year > 0 { + score++ + } + if book.ISBN != "" { + score++ + } + + switch { + case score >= 4: + return "high" + case score > 0: + return "medium" + default: + return "low" + } +} diff --git a/internal/scanner/ebook_test.go b/internal/scanner/ebook_test.go new file mode 100644 index 00000000..38a998a5 --- /dev/null +++ b/internal/scanner/ebook_test.go @@ -0,0 +1,1896 @@ +package scanner + +import ( + "archive/zip" + "context" + "errors" + "os" + "path/filepath" + "strings" + "testing" + "time" + + "github.com/Silo-Server/silo-server/internal/models" + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgconn" +) + +type recordingEbookExecutor struct { + queries []string + args [][]any +} + +func (r *recordingEbookExecutor) Exec(_ context.Context, query string, args ...any) (pgconn.CommandTag, error) { + r.queries = append(r.queries, query) + r.args = append(r.args, append([]any(nil), args...)) + return pgconn.CommandTag{}, nil +} + +type fakeFilesystemItemReader struct { + items []*models.MediaItem + err error +} + +func (f *fakeFilesystemItemReader) GetByIDs(_ context.Context, _ []string) ([]*models.MediaItem, error) { + return f.items, f.err +} + +type fakeEbookCoverCacher struct { + calls int + data []byte + contentID string + err error +} + +func (f *fakeEbookCoverCacher) CacheEbookCover(_ context.Context, data []byte, contentID string) (string, string, string, error) { + f.calls++ + f.data = append([]byte(nil), data...) + f.contentID = contentID + if f.err != nil { + return "", "", "", f.err + } + return "local/ebooks/" + contentID + "/poster", ".webp", "thumb", nil +} + +type fakeEbookMetadataUpdater struct { + posterPath string + posterThumbhash string + getErr error + contentID string + setPosterPath string + setThumbhash string + setPrefix string + setCalls int + err error +} + +func (f *fakeEbookMetadataUpdater) GetPoster(_ context.Context, _ string) (string, string, error) { + return f.posterPath, f.posterThumbhash, f.getErr +} + +func (f *fakeEbookMetadataUpdater) SetLocalPoster(_ context.Context, contentID, posterPath, thumbhash, localPrefix string) (bool, error) { + f.setCalls++ + f.contentID = contentID + f.setPosterPath = posterPath + f.setThumbhash = thumbhash + f.setPrefix = localPrefix + return f.err == nil, f.err +} + +func TestSupportsEbookFile(t *testing.T) { + cases := []struct { + path string + want bool + }{ + {"book.epub", true}, + {"book.PDF", true}, + {"book.mobi", true}, + {"book.azw", true}, + {"book.azw3", true}, + {"book.FB2", true}, + {"book.fb2.zip", true}, + {"book.fbz", true}, + {"book.cbz", true}, + {"book.cbr", true}, + {"book.txt", false}, + {"book.md", false}, + {"README.md", false}, + {"book.mp3", false}, + {"movie.mkv", false}, + } + for _, tc := range cases { + if got := SupportsEbookFile(tc.path); got != tc.want { + t.Errorf("SupportsEbookFile(%q) = %v, want %v", tc.path, got, tc.want) + } + } +} + +func TestParseEbookFileSupportsReaderFormatsWithoutEmbeddedMetadata(t *testing.T) { + for _, tc := range []struct { + name string + wantFormat string + }{ + {"book.pdf", "pdf"}, + {"book.mobi", "mobi"}, + {"book.azw", "azw"}, + {"book.azw3", "azw3"}, + {"book.cbr", "cbr"}, + } { + t.Run(tc.name, func(t *testing.T) { + path := filepath.Join(t.TempDir(), tc.name) + if err := os.WriteFile(path, []byte("placeholder"), 0o644); err != nil { + t.Fatalf("write file: %v", err) + } + got, err := parseEbookFile(path) + if err != nil { + t.Fatalf("parseEbookFile: %v", err) + } + if got.Format != tc.wantFormat { + t.Fatalf("Format = %q, want %q", got.Format, tc.wantFormat) + } + }) + } +} + +func TestNormalizeEbookISBNHandlesCommonLabels(t *testing.T) { + cases := []struct { + in string + want string + }{ + {"ISBN-13: 978-0-306-40615-7", "9780306406157"}, + {"ISBN-10: 0-9752298-0-x", "097522980X"}, + } + for _, tc := range cases { + if got := normalizeEbookISBN(tc.in); got != tc.want { + t.Errorf("normalizeEbookISBN(%q) = %q, want %q", tc.in, got, tc.want) + } + } +} + +func TestParseEbookEPUBMetadata(t *testing.T) { + path := writeTestEPUB(t, []string{"ISBN: 978-0-306-40615-7"}) + + got, err := parseEbookFile(path) + if err != nil { + t.Fatalf("parseEbookFile: %v", err) + } + + if got.Format != "epub" { + t.Fatalf("Format = %q, want epub", got.Format) + } + if got.Title != "The Test Ebook" { + t.Fatalf("Title = %q, want The Test Ebook", got.Title) + } + if strings.Join(got.Authors, ", ") != "Ada Writer, Ben Author" { + t.Fatalf("Authors = %v, want Ada Writer and Ben Author", got.Authors) + } + if got.ISBN != "9780306406157" { + t.Fatalf("ISBN = %q, want normalized ISBN", got.ISBN) + } + if got.Year != 2024 { + t.Fatalf("Year = %d, want 2024", got.Year) + } + if got.Publisher != "Silo Press" { + t.Fatalf("Publisher = %q, want Silo Press", got.Publisher) + } + wantPublishedAt := time.Date(2024, 3, 10, 0, 0, 0, 0, time.UTC) + if !got.PublishedAt.Equal(wantPublishedAt) { + t.Fatalf("PublishedAt = %v, want %v", got.PublishedAt, wantPublishedAt) + } + if got.Language != "en" { + t.Fatalf("Language = %q, want en", got.Language) + } + if strings.Join(got.Genres, ", ") != "Fiction, Adventure" { + t.Fatalf("Genres = %v, want Fiction and Adventure", got.Genres) + } + if got.Series != "Test Series" || got.SeriesIndex != "2" { + t.Fatalf("Series = %q/%q, want Test Series/2", got.Series, got.SeriesIndex) + } +} + +func TestParseEbookEPUBMetadataStripsHTMLDescription(t *testing.T) { + path := writeTestEPUBWithDescription(t, `<div><p><strong>Shanghai Dreams ...</strong> Magnus is stationed at Shanghai.<br>Published by The Electronic Book Company</p></div>`) + + got, err := parseEbookFile(path) + if err != nil { + t.Fatalf("parseEbookFile: %v", err) + } + + want := "Shanghai Dreams ... Magnus is stationed at Shanghai. Published by The Electronic Book Company" + if got.Description != want { + t.Fatalf("Description = %q, want %q", got.Description, want) + } +} + +func TestParseEbookEPUBSkipsUUIDIdentifierBeforeISBN(t *testing.T) { + path := writeTestEPUB(t, []string{ + "550e8400-e29b-41d4-a716-446655440000", + "ISBN: 978-0-306-40615-7", + }) + + got, err := parseEbookFile(path) + if err != nil { + t.Fatalf("parseEbookFile: %v", err) + } + if got.ISBN != "9780306406157" { + t.Fatalf("ISBN = %q, want normalized ISBN after skipping UUID", got.ISBN) + } +} + +func TestParseEbookEPUBMetadataReadsISBNFromMetaTags(t *testing.T) { + path := writeTestEPUBWithMeta(t, nil, ` `) + + got, err := parseEbookFile(path) + if err != nil { + t.Fatalf("parseEbookFile: %v", err) + } + if got.ISBN != "9780306406157" { + t.Fatalf("ISBN = %q, want normalized ISBN from calibre meta tag", got.ISBN) + } +} + +func TestParseEbookEPUBMetadataHandlesLatin1OPF(t *testing.T) { + path := writeTestEPUBWithOPFBytes(t, []byte("\n"+ + ""+ + "Caf\xe9 Society"+ + "Fran\xe7ois Author"+ + "Cr\xe8me Press"+ + "")) + + got, err := parseEbookFile(path) + if err != nil { + t.Fatalf("parseEbookFile: %v", err) + } + + if got.Title != "Café Society" || strings.Join(got.Authors, ", ") != "François Author" || got.Publisher != "Crème Press" { + t.Fatalf("parsed latin1 metadata = title %q authors %v publisher %q", got.Title, got.Authors, got.Publisher) + } +} + +func TestParseEbookEPUBMetadataHandlesWindows1251OPF(t *testing.T) { + path := writeTestEPUBWithOPFBytes(t, []byte("\n"+ + ""+ + "\xc2\xee\xe9\xed\xe0 \xe8 \xec\xe8\xf0"+ + "\xd2\xee\xeb\xf1\xf2\xee\xe9"+ + "")) + + got, err := parseEbookFile(path) + if err != nil { + t.Fatalf("parseEbookFile: %v", err) + } + + if got.Title != "Война и мир" || strings.Join(got.Authors, ", ") != "Толстой" { + t.Fatalf("parsed windows-1251 metadata = title %q authors %v", got.Title, got.Authors) + } +} + +func TestParseEbookEPUBMetadataAllowsXMLVersion11(t *testing.T) { + path := writeTestEPUBWithOPFBytes(t, []byte(` + + Versioned Ebook + Ada Writer +`)) + + got, err := parseEbookFile(path) + if err != nil { + t.Fatalf("parseEbookFile: %v", err) + } + + if got.Title != "Versioned Ebook" || strings.Join(got.Authors, ", ") != "Ada Writer" { + t.Fatalf("parsed XML 1.1 metadata = title %q authors %v", got.Title, got.Authors) + } +} + +func TestParseEbookEPUBExtractsManifestCover(t *testing.T) { + path := writeTestEPUBWithCover(t, "Images/cover.jpg", "image/jpeg", []byte("epub-cover-bytes")) + + got, err := parseEbookFile(path) + if err != nil { + t.Fatalf("parseEbookFile: %v", err) + } + + if got.Cover == nil { + t.Fatal("Cover = nil, want embedded EPUB cover") + } + if got.Cover.ContentType != "image/jpeg" { + t.Fatalf("Cover.ContentType = %q, want image/jpeg", got.Cover.ContentType) + } + if string(got.Cover.Bytes) != "epub-cover-bytes" { + t.Fatalf("Cover.Bytes = %q, want EPUB cover bytes", string(got.Cover.Bytes)) + } +} + +func TestParseEbookEPUBCoverSkipsXHTMLCoverPage(t *testing.T) { + path := filepath.Join(t.TempDir(), "book.epub") + file, err := os.Create(path) + if err != nil { + t.Fatalf("create epub: %v", err) + } + zw := zip.NewWriter(file) + add := func(name, body string) { + t.Helper() + w, err := zw.Create(name) + if err != nil { + t.Fatalf("create zip entry %s: %v", name, err) + } + if _, err := w.Write([]byte(body)); err != nil { + t.Fatalf("write zip entry %s: %v", name, err) + } + } + add("META-INF/container.xml", ` + + + + +`) + // EPUB2-style meta cover pointing at the XHTML cover *page*, with the + // real image declared later via properties="cover-image". + add("OPS/content.opf", ` + + + The Test Ebook + + + + + + +`) + add("OPS/cover.xhtml", "") + add("OPS/Images/cover.jpg", "real-cover-bytes") + if err := zw.Close(); err != nil { + t.Fatalf("close zip: %v", err) + } + if err := file.Close(); err != nil { + t.Fatalf("close epub: %v", err) + } + + got, err := parseEbookFile(path) + if err != nil { + t.Fatalf("parseEbookFile: %v", err) + } + + if got.Cover == nil { + t.Fatal("Cover = nil, want cover-image manifest item") + } + if got.Cover.ContentType != "image/jpeg" || string(got.Cover.Bytes) != "real-cover-bytes" { + t.Fatalf("Cover = %q/%q, want the image item, not the XHTML cover page", got.Cover.ContentType, string(got.Cover.Bytes)) + } +} + +func TestReadEPUBZipEntryRejectsOversizedEntry(t *testing.T) { + path := filepath.Join(t.TempDir(), "large.epub") + file, err := os.Create(path) + if err != nil { + t.Fatalf("create epub: %v", err) + } + zw := zip.NewWriter(file) + w, err := zw.Create("OPS/content.opf") + if err != nil { + t.Fatalf("create zip entry: %v", err) + } + if _, err := w.Write([]byte(strings.Repeat("x", maxEPUBMetadataEntrySize+1))); err != nil { + t.Fatalf("write zip entry: %v", err) + } + if err := zw.Close(); err != nil { + t.Fatalf("close zip: %v", err) + } + if err := file.Close(); err != nil { + t.Fatalf("close file: %v", err) + } + + reader, err := zip.OpenReader(path) + if err != nil { + t.Fatalf("open zip: %v", err) + } + defer reader.Close() + + _, err = readEPUBZipEntry(&reader.Reader, "OPS/content.opf") + if err == nil || !strings.Contains(err.Error(), "epub entry too large") { + t.Fatalf("readEPUBZipEntry error = %v, want size limit error", err) + } +} + +func TestParseEbookFB2Metadata(t *testing.T) { + path := filepath.Join(t.TempDir(), "book.fb2") + if err := os.WriteFile(path, []byte(` + + + + science fiction + AdaWriter + FB2 Test Ebook + 5 April 2022 + en + + + + FB2 Press + 2022 + 978-0-306-40615-7 + + +`), 0o644); err != nil { + t.Fatalf("write fb2: %v", err) + } + + got, err := parseEbookFile(path) + if err != nil { + t.Fatalf("parseEbookFile: %v", err) + } + + if got.Format != "fb2" || got.Title != "FB2 Test Ebook" { + t.Fatalf("Format/Title = %q/%q, want fb2/FB2 Test Ebook", got.Format, got.Title) + } + if strings.Join(got.Authors, ", ") != "Ada Writer" { + t.Fatalf("Authors = %v, want Ada Writer", got.Authors) + } + if got.ISBN != "9780306406157" { + t.Fatalf("ISBN = %q, want normalized ISBN", got.ISBN) + } + if got.Publisher != "FB2 Press" { + t.Fatalf("Publisher = %q, want FB2 Press", got.Publisher) + } + if got.Year != 2022 { + t.Fatalf("Year = %d, want 2022", got.Year) + } + wantPublishedAt := time.Date(2022, 4, 5, 0, 0, 0, 0, time.UTC) + if !got.PublishedAt.Equal(wantPublishedAt) { + t.Fatalf("PublishedAt = %v, want %v", got.PublishedAt, wantPublishedAt) + } + if got.Language != "en" { + t.Fatalf("Language = %q, want en", got.Language) + } + if strings.Join(got.Genres, ", ") != "science fiction" { + t.Fatalf("Genres = %v, want science fiction", got.Genres) + } + if got.Series != "FB2 Series" || got.SeriesIndex != "3" { + t.Fatalf("Series = %q/%q, want FB2 Series/3", got.Series, got.SeriesIndex) + } +} + +func TestParseEbookFBZMetadata(t *testing.T) { + path := filepath.Join(t.TempDir(), "book.fb2.zip") + file, err := os.Create(path) + if err != nil { + t.Fatalf("create fbz: %v", err) + } + zw := zip.NewWriter(file) + w, err := zw.Create("nested/book.fb2") + if err != nil { + t.Fatalf("create fb2 entry: %v", err) + } + if _, err := w.Write([]byte(` + + + + fantasy + AdaWriter + FBZ Test Ebook + + en + + + + Archive Press + 2021 + 978-0-306-40615-7 + + +`)); err != nil { + t.Fatalf("write fb2 entry: %v", err) + } + if err := zw.Close(); err != nil { + t.Fatalf("close zip: %v", err) + } + if err := file.Close(); err != nil { + t.Fatalf("close fbz: %v", err) + } + + got, err := parseEbookFile(path) + if err != nil { + t.Fatalf("parseEbookFile: %v", err) + } + + if got.Format != "fbz" || got.Title != "FBZ Test Ebook" { + t.Fatalf("Format/Title = %q/%q, want fbz/FBZ Test Ebook", got.Format, got.Title) + } + if strings.Join(got.Authors, ", ") != "Ada Writer" { + t.Fatalf("Authors = %v, want Ada Writer", got.Authors) + } + if got.ISBN != "9780306406157" { + t.Fatalf("ISBN = %q, want normalized ISBN", got.ISBN) + } + if got.Publisher != "Archive Press" || got.Year != 2021 { + t.Fatalf("Publisher/Year = %q/%d, want Archive Press/2021", got.Publisher, got.Year) + } + if got.Series != "Archive Series" || got.SeriesIndex != "4" { + t.Fatalf("Series = %q/%q, want Archive Series/4", got.Series, got.SeriesIndex) + } +} + +func TestParseEbookPDFInfoMetadata(t *testing.T) { + path := filepath.Join(t.TempDir(), "book.pdf") + if err := os.WriteFile(path, []byte(`%PDF-1.7 +1 0 obj +<< /Title (PDF Test Ebook) + /Author (Ada Writer; Ben Author) + /Subject (ISBN 978-0-306-40615-7) + /Keywords (science fiction, adventure) + /CreationDate (D:20240102030405Z) +>> +endobj +trailer +<< /Info 1 0 R >> +%%EOF`), 0o644); err != nil { + t.Fatalf("write pdf: %v", err) + } + + got, err := parseEbookFile(path) + if err != nil { + t.Fatalf("parseEbookFile: %v", err) + } + + if got.Format != "pdf" || got.Title != "PDF Test Ebook" { + t.Fatalf("Format/Title = %q/%q, want pdf/PDF Test Ebook", got.Format, got.Title) + } + if strings.Join(got.Authors, ", ") != "Ada Writer, Ben Author" { + t.Fatalf("Authors = %v, want Ada Writer and Ben Author", got.Authors) + } + if got.ISBN != "9780306406157" { + t.Fatalf("ISBN = %q, want normalized ISBN", got.ISBN) + } + if got.Year != 2024 { + t.Fatalf("Year = %d, want 2024", got.Year) + } + if strings.Join(got.Genres, ", ") != "science fiction, adventure" { + t.Fatalf("Genres = %v, want science fiction and adventure", got.Genres) + } +} + +func TestParseEbookPDFInfoMetadataDecodesUTF16BELiterals(t *testing.T) { + path := filepath.Join(t.TempDir(), "book.pdf") + if err := os.WriteFile(path, []byte("%PDF-1.7\n"+ + "1 0 obj\n"+ + "<< /Title (\xfe\xff\x00C\x00a\x00f\x00e)\n"+ + " /Author (\xfe\xff\x00T\x00h\x00o\x00m\x00a\x00s\x00 \x00D\x00 \x00S\x00e\x00e\x00l\x00e\x00y)\n"+ + ">>\nendobj\n%%EOF"), 0o644); err != nil { + t.Fatalf("write pdf: %v", err) + } + + got, err := parseEbookFile(path) + if err != nil { + t.Fatalf("parseEbookFile: %v", err) + } + + if got.Title != "Cafe" || strings.Join(got.Authors, ", ") != "Thomas D Seeley" { + t.Fatalf("PDF metadata = title %q authors %v, want decoded UTF-16BE", got.Title, got.Authors) + } +} + +func TestParseEbookPDFInfoMetadataPreservesUTF8Literals(t *testing.T) { + path := filepath.Join(t.TempDir(), "book.pdf") + if err := os.WriteFile(path, []byte("%PDF-1.7\n"+ + "1 0 obj\n"+ + "<< /Title (Caf\xc3\xa9 Society)\n"+ + " /Author (\xef\xbb\xbfFran\xc3\xa7ois Author)\n"+ + ">>\nendobj\n%%EOF"), 0o644); err != nil { + t.Fatalf("write pdf: %v", err) + } + + got, err := parseEbookFile(path) + if err != nil { + t.Fatalf("parseEbookFile: %v", err) + } + + if got.Title != "Café Society" { + t.Fatalf("Title = %q, want UTF-8 literal preserved without Windows-1252 mojibake", got.Title) + } + if strings.Join(got.Authors, ", ") != "François Author" { + t.Fatalf("Authors = %v, want UTF-8 BOM stripped and bytes preserved", got.Authors) + } +} + +func TestParseEbookPDFInfoMetadataFallsBackToWindows1252(t *testing.T) { + path := filepath.Join(t.TempDir(), "book.pdf") + if err := os.WriteFile(path, []byte("%PDF-1.7\n"+ + "1 0 obj\n"+ + "<< /Title (Caf\xe9 Society)\n"+ + ">>\nendobj\n%%EOF"), 0o644); err != nil { + t.Fatalf("write pdf: %v", err) + } + + got, err := parseEbookFile(path) + if err != nil { + t.Fatalf("parseEbookFile: %v", err) + } + + if got.Title != "Café Society" { + t.Fatalf("Title = %q, want Windows-1252 fallback for non-UTF-8 literal", got.Title) + } +} + +func TestParseEbookPDFInfoMetadataDecodesHexStrings(t *testing.T) { + path := filepath.Join(t.TempDir(), "book.pdf") + if err := os.WriteFile(path, []byte(`%PDF-1.7 +1 0 obj +<< /Title + /Author <41646120577269746572> + /CreationDate (0000-01-01) +>> +endobj +%%EOF`), 0o644); err != nil { + t.Fatalf("write pdf: %v", err) + } + + got, err := parseEbookFile(path) + if err != nil { + t.Fatalf("parseEbookFile: %v", err) + } + + if got.Title != "Hex Book" || strings.Join(got.Authors, ", ") != "Ada Writer" { + t.Fatalf("PDF hex metadata = title %q authors %v", got.Title, got.Authors) + } + if got.Year != 0 || !got.PublishedAt.IsZero() { + t.Fatalf("bad PDF date produced year/date = %d/%v, want ignored", got.Year, got.PublishedAt) + } +} + +func TestParseEbookCBZPageCount(t *testing.T) { + path := filepath.Join(t.TempDir(), "comic.cbz") + file, err := os.Create(path) + if err != nil { + t.Fatalf("create cbz: %v", err) + } + zw := zip.NewWriter(file) + for _, name := range []string{ + "Comic/001.jpg", + "Comic/002.PNG", + "Comic/003.webp", + "Comic/notes.txt", + "__MACOSX/._001.jpg", + } { + w, err := zw.Create(name) + if err != nil { + t.Fatalf("create zip entry %s: %v", name, err) + } + if _, err := w.Write([]byte("placeholder")); err != nil { + t.Fatalf("write zip entry %s: %v", name, err) + } + } + if err := zw.Close(); err != nil { + t.Fatalf("close zip: %v", err) + } + if err := file.Close(); err != nil { + t.Fatalf("close cbz: %v", err) + } + + got, err := parseEbookFile(path) + if err != nil { + t.Fatalf("parseEbookFile: %v", err) + } + + if got.Format != "cbz" { + t.Fatalf("Format = %q, want cbz", got.Format) + } + if got.PageCount != 3 { + t.Fatalf("PageCount = %d, want 3", got.PageCount) + } +} + +func TestParseEbookCBZExtractsFirstReadablePageAsCover(t *testing.T) { + path := filepath.Join(t.TempDir(), "comic.cbz") + file, err := os.Create(path) + if err != nil { + t.Fatalf("create cbz: %v", err) + } + zw := zip.NewWriter(file) + for _, entry := range []struct { + name string + body []byte + }{ + {name: "Comic/002.png", body: []byte("second-page")}, + {name: "Comic/001.jpg", body: []byte("first-page")}, + {name: "Comic/notes.txt", body: []byte("ignored")}, + } { + w, err := zw.Create(entry.name) + if err != nil { + t.Fatalf("create zip entry %s: %v", entry.name, err) + } + if _, err := w.Write(entry.body); err != nil { + t.Fatalf("write zip entry %s: %v", entry.name, err) + } + } + if err := zw.Close(); err != nil { + t.Fatalf("close zip: %v", err) + } + if err := file.Close(); err != nil { + t.Fatalf("close cbz: %v", err) + } + + got, err := parseEbookFile(path) + if err != nil { + t.Fatalf("parseEbookFile: %v", err) + } + + if got.Cover == nil { + t.Fatal("Cover = nil, want first CBZ page as cover") + } + if got.Cover.ContentType != "image/jpeg" { + t.Fatalf("Cover.ContentType = %q, want image/jpeg", got.Cover.ContentType) + } + if string(got.Cover.Bytes) != "first-page" { + t.Fatalf("Cover.Bytes = %q, want first page bytes", string(got.Cover.Bytes)) + } +} + +func TestParseEbookCBZPicksNaturallyFirstPageAsCover(t *testing.T) { + path := filepath.Join(t.TempDir(), "comic.cbz") + file, err := os.Create(path) + if err != nil { + t.Fatalf("create cbz: %v", err) + } + zw := zip.NewWriter(file) + for _, entry := range []struct { + name string + body string + }{ + {name: "ch10/page10.jpg", body: "chapter-ten"}, + {name: "ch2/page2.jpg", body: "chapter-two"}, + } { + w, err := zw.Create(entry.name) + if err != nil { + t.Fatalf("create zip entry %s: %v", entry.name, err) + } + if _, err := w.Write([]byte(entry.body)); err != nil { + t.Fatalf("write zip entry %s: %v", entry.name, err) + } + } + if err := zw.Close(); err != nil { + t.Fatalf("close zip: %v", err) + } + if err := file.Close(); err != nil { + t.Fatalf("close cbz: %v", err) + } + + got, err := parseEbookFile(path) + if err != nil { + t.Fatalf("parseEbookFile: %v", err) + } + + if got.Cover == nil { + t.Fatal("Cover = nil, want first page in natural order") + } + if string(got.Cover.Bytes) != "chapter-two" { + t.Fatalf("Cover.Bytes = %q, want ch2 before ch10 (natural order, not byte order)", string(got.Cover.Bytes)) + } +} + +func TestEbookTitleFromPathStripsCompoundFB2Extension(t *testing.T) { + cases := map[string]string{ + "/lib/Anna Karenina.fb2.zip": "Anna Karenina", + "/lib/Anna Karenina.FB2.ZIP": "Anna Karenina", + "/lib/Book.epub": "Book", + "/lib/vol.1.pdf": "vol.1", + } + for path, want := range cases { + if got := ebookTitleFromPath(path); got != want { + t.Errorf("ebookTitleFromPath(%q) = %q, want %q", path, got, want) + } + } +} + +func TestEbookSeriesDesiredParsesIndex(t *testing.T) { + name, idx := ebookSeriesDesired(&parsedEbook{ + Series: " The Expanse ", + SeriesIndex: "2 of 9", + }) + + if name != "The Expanse" { + t.Fatalf("series name = %q, want The Expanse", name) + } + if idx == nil || *idx != 2 { + t.Fatalf("series index = %v, want 2", idx) + } +} + +func TestUpsertEbookSeriesNilScannerReturnsError(t *testing.T) { + var s *Scanner + if err := s.upsertEbookSeries(context.Background(), "content-1", &parsedEbook{Series: "Series"}, false); err == nil { + t.Fatal("upsertEbookSeries nil scanner error = nil, want error") + } +} + +func TestUpsertEbookSeriesNilFileRepoReturnsError(t *testing.T) { + s := &Scanner{} + if err := s.upsertEbookSeries(context.Background(), "content-1", &parsedEbook{Series: "Series"}, false); err == nil { + t.Fatal("upsertEbookSeries nil fileRepo error = nil, want error") + } +} + +func TestPlanEbookSeriesWriteInsertsWhenRowAbsent(t *testing.T) { + plan, err := planEbookSeriesWrite(&parsedEbook{Series: " Series ", SeriesIndex: "2 of 9"}, nil, nil, pgx.ErrNoRows, false) + if err != nil { + t.Fatalf("planEbookSeriesWrite: %v", err) + } + if plan.Kind != ebookSeriesWriteUpsert || plan.Name != "Series" { + t.Fatalf("plan = %+v, want upsert Series", plan) + } + if plan.Index == nil || *plan.Index != 2 { + t.Fatalf("index = %v, want 2", plan.Index) + } +} + +func TestPlanEbookSeriesWriteBlankSeriesDeletesExistingAndSkipsAbsent(t *testing.T) { + currentName := "Series" + plan, err := planEbookSeriesWrite(&parsedEbook{Series: " "}, ¤tName, nil, nil, false) + if err != nil { + t.Fatalf("planEbookSeriesWrite delete: %v", err) + } + if plan.Kind != ebookSeriesWriteDelete { + t.Fatalf("blank existing plan = %+v, want delete", plan) + } + + plan, err = planEbookSeriesWrite(&parsedEbook{Series: ""}, nil, nil, pgx.ErrNoRows, false) + if err != nil { + t.Fatalf("planEbookSeriesWrite absent: %v", err) + } + if plan.Kind != ebookSeriesWriteNone { + t.Fatalf("blank absent plan = %+v, want none", plan) + } +} + +func TestPlanEbookSeriesWriteSkipsIdenticalRow(t *testing.T) { + currentName := "Series" + currentIdx := 3.5 + plan, err := planEbookSeriesWrite(&parsedEbook{Series: "Series", SeriesIndex: "3.5"}, ¤tName, ¤tIdx, nil, false) + if err != nil { + t.Fatalf("planEbookSeriesWrite: %v", err) + } + if plan.Kind != ebookSeriesWriteNone { + t.Fatalf("identical plan = %+v, want none", plan) + } +} + +func TestPlanEbookSeriesWriteAllowsNullNumericIndex(t *testing.T) { + plan, err := planEbookSeriesWrite(&parsedEbook{Series: "Series", SeriesIndex: "appendix"}, nil, nil, pgx.ErrNoRows, false) + if err != nil { + t.Fatalf("planEbookSeriesWrite: %v", err) + } + if plan.Kind != ebookSeriesWriteUpsert || plan.Index != nil { + t.Fatalf("plan = %+v, want upsert with nil index", plan) + } +} + +func TestPlanEbookSeriesWriteFillOnlyNeverReplacesOrDeletesExistingRow(t *testing.T) { + currentName := "Provider Series" + currentIdx := 1.0 + + // A curated item with an existing (provider-enriched) row must not be + // replaced by a differing file-embedded series... + plan, err := planEbookSeriesWrite(&parsedEbook{Series: "File Series", SeriesIndex: "4"}, ¤tName, ¤tIdx, nil, true) + if err != nil { + t.Fatalf("planEbookSeriesWrite replace: %v", err) + } + if plan.Kind != ebookSeriesWriteNone { + t.Fatalf("fill-only replace plan = %+v, want none", plan) + } + + // ...nor deleted when the file carries no series at all. + plan, err = planEbookSeriesWrite(&parsedEbook{Series: ""}, ¤tName, ¤tIdx, nil, true) + if err != nil { + t.Fatalf("planEbookSeriesWrite delete: %v", err) + } + if plan.Kind != ebookSeriesWriteNone { + t.Fatalf("fill-only delete plan = %+v, want none", plan) + } +} + +func TestPlanEbookSeriesWriteFillOnlyInsertsWhenRowAbsent(t *testing.T) { + plan, err := planEbookSeriesWrite(&parsedEbook{Series: "File Series", SeriesIndex: "2"}, nil, nil, pgx.ErrNoRows, true) + if err != nil { + t.Fatalf("planEbookSeriesWrite: %v", err) + } + if plan.Kind != ebookSeriesWriteUpsert || plan.Name != "File Series" { + t.Fatalf("plan = %+v, want fill-empty upsert", plan) + } +} + +func TestEbookPeopleWriteAllowedFillsEmptyOnlyWhenCurated(t *testing.T) { + providerAuthor := []models.ItemPerson{{Person: models.Person{Name: "Provider Author"}, Kind: models.PersonKindAuthor}} + nonAuthorOnly := []models.ItemPerson{{Person: models.Person{Name: "Manual Writer"}, Kind: models.PersonKindWriter}} + + cases := []struct { + name string + curated bool + existing []models.ItemPerson + want bool + }{ + {"pending item always writes", false, providerAuthor, true}, + {"curated with provider authors is read-only", true, providerAuthor, false}, + {"curated without author credits fills empty", true, nil, true}, + {"curated with only non-author credits fills empty", true, nonAuthorOnly, true}, + } + for _, tc := range cases { + if got := ebookPeopleWriteAllowed(tc.curated, tc.existing); got != tc.want { + t.Errorf("%s: ebookPeopleWriteAllowed = %v, want %v", tc.name, got, tc.want) + } + } +} + +func TestPlanEbookSeriesWriteReturnsQueryError(t *testing.T) { + queryErr := errors.New("query failed") + if _, err := planEbookSeriesWrite(&parsedEbook{Series: "Series"}, nil, nil, queryErr, false); !errors.Is(err, queryErr) { + t.Fatalf("planEbookSeriesWrite error = %v, want query error", err) + } +} + +func TestEbookIdentityConfidenceReflectsMetadataCompleteness(t *testing.T) { + book := &parsedEbook{Title: "Tagged Ebook", Authors: []string{"Author"}, Year: 2024, ISBN: "9780306406157"} + if got := ebookIdentityConfidence(book); got != "high" { + t.Fatalf("complete metadata confidence = %q, want high", got) + } + + book = &parsedEbook{Title: "Tagged Ebook", Authors: []string{"Author"}, Year: 2024} + if got := ebookIdentityConfidence(book); got != "medium" { + t.Fatalf("partial metadata confidence = %q, want medium", got) + } + + book = &parsedEbook{} + if got := ebookIdentityConfidence(book); got != "low" { + t.Fatalf("empty metadata confidence = %q, want low", got) + } +} + +func TestEbookContentGroupKeyGroupsMultipleSiblingFormatsByTitleAuthor(t *testing.T) { + want := ebookContentGroupKey(&parsedEbook{ + Format: "epub", + Title: " The Test Ebook ", + Authors: []string{"Ada Writer", "Ben Author"}, + }, "/library/Ada Writer/The Test Ebook.epub") + if want == "" { + t.Fatal("ebookContentGroupKey returned blank") + } + for _, tc := range []struct { + format string + path string + }{ + {format: "mobi", path: "/library/Ada Writer/The Test Ebook.mobi"}, + {format: "azw3", path: "/library/Ada Writer/The Test Ebook.azw3"}, + {format: "pdf", path: "/library/Ada Writer/The Test Ebook.pdf"}, + {format: "fb2", path: "/library/Ada Writer/The Test Ebook.fb2"}, + } { + got := ebookContentGroupKey(&parsedEbook{ + Format: tc.format, + Title: "The Test Ebook", + Authors: []string{"ben author", "ada writer"}, + }, tc.path) + if got != want { + t.Fatalf("%s content group key = %q, want %q", tc.format, got, want) + } + if strings.Contains(got, tc.format) { + t.Fatalf("content group key should not include file format %q: %q", tc.format, got) + } + } +} + +func TestEbookContentGroupKeyPrefersISBNAcrossTitleVariants(t *testing.T) { + first := ebookContentGroupKey(&parsedEbook{ + Format: "epub", + Title: "The Test Ebook", + Authors: []string{"Ada Writer"}, + ISBN: "9780306406157", + }, "/library/Ada Writer/The Test Ebook.epub") + second := ebookContentGroupKey(&parsedEbook{ + Format: "pdf", + Title: "The Test Ebook: Revised Edition", + Authors: []string{"Someone Else"}, + ISBN: "9780306406157", + }, "/library/Other Name.pdf") + + if first != second { + t.Fatalf("ISBN-backed content group keys differ: %q != %q", first, second) + } +} + +func TestResolveEbookMediaItemCreatesNewWhenRootHasNoClaim(t *testing.T) { + finder := &fakeRootContentFinder{} + writer := &fakeFilesystemItemWriter{} + + got, err := resolveEbookMediaItem(context.Background(), finder, writer, 7, "/library/Author/Book.epub", &parsedEbook{ + Title: "Book", Year: 2024, Authors: []string{"Author"}, Description: "Overview", Publisher: "Publisher", Genres: []string{"Fiction"}, Language: "en", + }) + if err != nil { + t.Fatalf("resolveEbookMediaItem: %v", err) + } + if got == "" || len(writer.upserts) != 1 { + t.Fatalf("contentID/upserts = %q/%d, want id and one upsert", got, len(writer.upserts)) + } + item := writer.upserts[0] + if item.Type != "ebook" || item.Title != "Book" || item.Year != 2024 || item.Overview != "Overview" || item.OriginalLanguage != "en" { + t.Fatalf("upserted ebook item = %+v", item) + } + if item.Status != "pending" { + t.Fatalf("Status = %q, want pending so enrichment can promote it to matched", item.Status) + } +} + +func TestResolveEbookMediaItemReusesRootScopedContentID(t *testing.T) { + finder := &fakeRootContentFinder{contentID: "ebook-root-id"} + writer := &fakeFilesystemItemWriter{} + + got, err := resolveEbookMediaItem(context.Background(), finder, writer, 7, "/library/Author/Book.epub", &parsedEbook{Title: "Book"}) + if err != nil { + t.Fatalf("resolveEbookMediaItem: %v", err) + } + if got != "ebook-root-id" { + t.Fatalf("contentID = %q, want root-scoped id", got) + } + if len(writer.upserts) != 0 { + t.Fatalf("unexpected item upsert for existing root: %d", len(writer.upserts)) + } +} + +func TestResolveEbookExistingRootAppliesParsedMetadata(t *testing.T) { + reader := &fakeFilesystemItemReader{items: []*models.MediaItem{{ + ContentID: "ebook-root-id", + Type: "ebook", + Title: "Old Title", + Year: 2020, + OriginalLanguage: "", + }}} + writer := &fakeFilesystemItemWriter{} + + curated, err := updateExistingEbookMediaItem(context.Background(), reader, writer, "ebook-root-id", &parsedEbook{ + Title: "New Title", + Year: 2026, + Publisher: "New Press", + Genres: []string{"Fiction"}, + Language: "en", + }) + if err != nil { + t.Fatalf("updateExistingEbookMediaItem: %v", err) + } + if curated { + t.Fatal("curated = true, want false for an unmatched item") + } + if len(writer.upserts) != 1 { + t.Fatalf("upserts = %d, want 1", len(writer.upserts)) + } + item := writer.upserts[0] + if item.ContentID != "ebook-root-id" || item.Type != "ebook" || item.Title != "New Title" || item.Year != 2026 || item.OriginalLanguage != "en" { + t.Fatalf("updated item = %+v", item) + } + if strings.Join(item.Studios, ",") != "New Press" || strings.Join(item.Genres, ",") != "Fiction" { + t.Fatalf("updated item studios/genres = %+v/%+v", item.Studios, item.Genres) + } +} + +func TestResolveEbookExistingRootReplacesHTMLOverview(t *testing.T) { + reader := &fakeFilesystemItemReader{items: []*models.MediaItem{{ + ContentID: "ebook-root-id", + Type: "ebook", + Title: "Old Title", + Overview: "

Raw overview

", + }}} + writer := &fakeFilesystemItemWriter{} + + _, err := updateExistingEbookMediaItem(context.Background(), reader, writer, "ebook-root-id", &parsedEbook{ + Title: "New Title", + Description: "Clean overview", + }) + if err != nil { + t.Fatalf("updateExistingEbookMediaItem: %v", err) + } + if len(writer.upserts) != 1 { + t.Fatalf("upserts = %d, want 1", len(writer.upserts)) + } + if writer.upserts[0].Overview != "Clean overview" { + t.Fatalf("Overview = %q, want Clean overview", writer.upserts[0].Overview) + } +} + +func TestApplyEbookToMediaItemPreservesExistingYearWhenParsedYearMissing(t *testing.T) { + item := &models.MediaItem{ + Type: "ebook", + Title: "Existing", + Year: 1999, + } + + applyEbookToMediaItem(item, &parsedEbook{Title: "Updated", Year: 0}) + + if item.Title != "Updated" { + t.Fatalf("Title = %q, want Updated", item.Title) + } + if item.Year != 1999 { + t.Fatalf("Year = %d, want preserved 1999", item.Year) + } +} + +func TestEbookPeopleCreditsEqualAuthorsOnly(t *testing.T) { + existing := []models.ItemPerson{ + {Person: models.Person{Name: "Ada Writer"}, Kind: models.PersonKindAuthor, SortOrder: 0}, + {Person: models.Person{Name: "Ben Author"}, Kind: models.PersonKindAuthor, SortOrder: 1}, + {Person: models.Person{Name: "Manual Contributor"}, Kind: models.PersonKindWriter, SortOrder: 2}, + } + desired := []ebookCredit{ + {Name: "Ada Writer", Kind: models.PersonKindAuthor}, + {Name: "Ben Author", Kind: models.PersonKindAuthor}, + } + if !ebookPeopleCreditsEqual(existing, desired) { + t.Fatal("expected matching author credits to compare equal while ignoring non-authors") + } + + existing[1] = models.ItemPerson{Person: models.Person{Name: "Other Author"}, Kind: models.PersonKindAuthor, SortOrder: 1} + if ebookPeopleCreditsEqual(existing, desired) { + t.Fatal("expected different author credit to make ebook author set differ") + } +} + +func TestEbookPeopleCreditsEqualRejectsStaleNarratorCredit(t *testing.T) { + existing := []models.ItemPerson{ + {Person: models.Person{Name: "Ada Writer"}, Kind: models.PersonKindAuthor, SortOrder: 0}, + {Person: models.Person{Name: "Legacy Narrator"}, Kind: models.PersonKindNarrator, SortOrder: 1}, + } + desired := []ebookCredit{ + {Name: "Ada Writer", Kind: models.PersonKindAuthor}, + } + + if ebookPeopleCreditsEqual(existing, desired) { + t.Fatal("expected stale narrator credit to force ebook people replacement") + } +} + +func TestEbookPeopleMergePreservesExistingNonAuthorCreditsExceptNarrators(t *testing.T) { + existing := []models.ItemPerson{ + {Person: models.Person{ID: 10, Name: "Old Author"}, Kind: models.PersonKindAuthor, SortOrder: 0}, + {Person: models.Person{ID: 20, Name: "Manual Writer"}, Kind: models.PersonKindWriter, SortOrder: 1, Character: "essay"}, + {Person: models.Person{ID: 30, Name: "Manual Narrator"}, Kind: models.PersonKindNarrator, SortOrder: 2}, + } + authors := []ebookResolvedAuthor{ + {ID: 40, Name: "New Author"}, + } + + got := mergeEbookPeople(existing, authors) + if len(got) != 2 { + t.Fatalf("merged people len = %d, want 2: %+v", len(got), got) + } + if got[0].Person.ID != 20 || got[0].Kind != models.PersonKindWriter || got[0].Character != "essay" { + t.Fatalf("first preserved non-author = %+v", got[0]) + } + if got[1].Person.ID != 40 || got[1].Kind != models.PersonKindAuthor || got[1].SortOrder != 1 { + t.Fatalf("new author credit = %+v", got[1]) + } +} + +func TestEbookPeopleReplacePlanReturnsGetPeopleError(t *testing.T) { + wantErr := errors.New("get people failed") + _, err := ebookPeopleForReplace(nil, wantErr, []ebookResolvedAuthor{{ID: 40, Name: "New Author"}}) + if !errors.Is(err, wantErr) { + t.Fatalf("error = %v, want %v", err, wantErr) + } +} + +func TestScanEbookBuildMediaFileSetsCorePersistenceFields(t *testing.T) { + modifiedAt := time.Date(2026, 6, 8, 12, 0, 0, 0, time.UTC) + book := &parsedEbook{Format: "epub", Title: "Book", Authors: []string{"Author"}, Year: 2024, ISBN: "9780306406157", PageCount: 321} + + got := buildEbookMediaFile(&models.MediaFolder{ID: 44}, "content-1", "/library/Book.epub", 1234, modifiedAt, book, ebookContentGroupKey(book, "/library/Book.epub")) + if got.ContentID != "content-1" || got.MediaFolderID != 44 { + t.Fatalf("ids = %q/%d, want content-1/44", got.ContentID, got.MediaFolderID) + } + if got.BaseType != "ebook" || got.BaseTitle != "Book" || got.BaseYear != 2024 || got.Container != "epub" || got.ProbeSource != "local" { + t.Fatalf("ebook file metadata = %+v", got) + } + if got.Duration != 321 { + t.Fatalf("Duration = %d, want ebook page count", got.Duration) + } + if got.CanonicalRootPath != "/library/Book.epub" || got.ObservedRootPath != "/library/Book.epub" || got.FilePath != "/library/Book.epub" { + t.Fatalf("ebook paths = canonical %q observed %q file %q", got.CanonicalRootPath, got.ObservedRootPath, got.FilePath) + } + if got.ContentGroupKey != "ebook:isbn:9780306406157" { + t.Fatalf("ContentGroupKey = %q, want ISBN-backed ebook group", got.ContentGroupKey) + } + if got.GroupKeyVersion != ebookGroupKeyVersion { + t.Fatalf("GroupKeyVersion = %d, want %d", got.GroupKeyVersion, ebookGroupKeyVersion) + } + if got.FileSize != 1234 || got.FileModifiedAt == nil || !got.FileModifiedAt.Equal(modifiedAt) { + t.Fatalf("file facts = size %d modified %v", got.FileSize, got.FileModifiedAt) + } +} + +func TestApplyEbookLocalCoverCachesEmbeddedAndSetsPoster(t *testing.T) { + cacher := &fakeEbookCoverCacher{} + updater := &fakeEbookMetadataUpdater{} + + err := applyEbookLocalCover(context.Background(), updater, cacher, "content-1", filepath.Join(t.TempDir(), "book.epub"), &parsedEbook{ + Cover: &parsedEbookCover{ + ContentType: "image/jpeg", + Bytes: []byte("cover-bytes"), + }, + }) + if err != nil { + t.Fatalf("applyEbookLocalCover: %v", err) + } + + if cacher.calls != 1 || cacher.contentID != "content-1" || string(cacher.data) != "cover-bytes" { + t.Fatalf("cache call = calls %d content %q data %q", cacher.calls, cacher.contentID, string(cacher.data)) + } + if updater.setCalls != 1 || updater.contentID != "content-1" { + t.Fatalf("set call = calls %d content %q", updater.setCalls, updater.contentID) + } + if updater.setPosterPath != "local/ebooks/content-1/poster/original.webp" { + t.Fatalf("poster path = %q", updater.setPosterPath) + } + if updater.setThumbhash != "thumb" { + t.Fatalf("poster thumbhash = %q", updater.setThumbhash) + } + if updater.setPrefix != localEbookPosterPrefix { + t.Fatalf("local prefix = %q, want %q", updater.setPrefix, localEbookPosterPrefix) + } +} + +func TestApplyEbookLocalCoverPreservesProviderPoster(t *testing.T) { + cacher := &fakeEbookCoverCacher{} + updater := &fakeEbookMetadataUpdater{posterPath: "ebook-metadata/ebooks/content-1/poster/original.webp"} + + err := applyEbookLocalCover(context.Background(), updater, cacher, "content-1", filepath.Join(t.TempDir(), "book.epub"), &parsedEbook{ + Cover: &parsedEbookCover{Bytes: []byte("cover-bytes")}, + }) + if err != nil { + t.Fatalf("applyEbookLocalCover: %v", err) + } + + if cacher.calls != 0 { + t.Fatalf("cache calls = %d, want 0", cacher.calls) + } + if updater.setCalls != 0 { + t.Fatalf("set calls = %d, want 0", updater.setCalls) + } +} + +func TestApplyEbookLocalCoverRefreshesStaleLocalPoster(t *testing.T) { + cacher := &fakeEbookCoverCacher{} + updater := &fakeEbookMetadataUpdater{ + posterPath: "local/ebooks/content-1/poster/original.webp", + posterThumbhash: "stale-thumb", + } + + err := applyEbookLocalCover(context.Background(), updater, cacher, "content-1", filepath.Join(t.TempDir(), "book.epub"), &parsedEbook{ + Cover: &parsedEbookCover{Bytes: []byte("replacement-cover-bytes")}, + }) + if err != nil { + t.Fatalf("applyEbookLocalCover: %v", err) + } + + if cacher.calls != 1 || string(cacher.data) != "replacement-cover-bytes" { + t.Fatalf("cache call = calls %d data %q, want refresh of locally owned poster", cacher.calls, string(cacher.data)) + } + if updater.setCalls != 1 { + t.Fatalf("set calls = %d, want 1", updater.setCalls) + } +} + +func TestApplyEbookLocalCoverPrefersSidecarOverEmbedded(t *testing.T) { + dir := t.TempDir() + if err := os.WriteFile(filepath.Join(dir, "book.epub"), []byte("book"), 0o644); err != nil { + t.Fatalf("write ebook: %v", err) + } + if err := os.WriteFile(filepath.Join(dir, "cover.jpg"), []byte("ebook-sidecar-cover"), 0o644); err != nil { + t.Fatalf("write cover: %v", err) + } + cacher := &fakeEbookCoverCacher{} + updater := &fakeEbookMetadataUpdater{} + + err := applyEbookLocalCover(context.Background(), updater, cacher, "content-1", filepath.Join(dir, "book.epub"), &parsedEbook{ + Cover: &parsedEbookCover{Bytes: []byte("embedded-cover")}, + }) + if err != nil { + t.Fatalf("applyEbookLocalCover: %v", err) + } + + if cacher.calls != 1 || string(cacher.data) != "ebook-sidecar-cover" { + t.Fatalf("cache call = calls %d data %q, want sidecar bytes", cacher.calls, string(cacher.data)) + } + if updater.setCalls != 1 || updater.setPosterPath != "local/ebooks/content-1/poster/original.webp" { + t.Fatalf("poster update = calls %d path %q", updater.setCalls, updater.setPosterPath) + } +} + +func TestFindSidecarBookCoverIgnoresGenericNamesInMultiBookDirectories(t *testing.T) { + dir := t.TempDir() + for _, name := range []string{"alpha.epub", "beta.epub"} { + if err := os.WriteFile(filepath.Join(dir, name), []byte("book"), 0o644); err != nil { + t.Fatalf("write ebook %s: %v", name, err) + } + } + if err := os.WriteFile(filepath.Join(dir, "cover.jpg"), []byte("shared-cover"), 0o644); err != nil { + t.Fatalf("write cover: %v", err) + } + if err := os.WriteFile(filepath.Join(dir, "beta.jpg"), []byte("beta-cover"), 0o644); err != nil { + t.Fatalf("write cover: %v", err) + } + + cover, _, err := findSidecarBookCover(filepath.Join(dir, "alpha.epub")) + if err != nil { + t.Fatalf("findSidecarBookCover(alpha): %v", err) + } + if cover != nil { + t.Fatalf("alpha cover = %q, want none (generic cover.jpg must not claim every book)", string(cover.Bytes)) + } + + cover, _, err = findSidecarBookCover(filepath.Join(dir, "beta.epub")) + if err != nil { + t.Fatalf("findSidecarBookCover(beta): %v", err) + } + if cover == nil || string(cover.Bytes) != "beta-cover" { + t.Fatalf("beta cover = %v, want filename-matched beta.jpg", cover) + } +} + +func TestScanEbookPersistenceSQLWritesLibraryMembershipAndISBNOnly(t *testing.T) { + ctx := context.Background() + exec := &recordingEbookExecutor{} + + if err := insertEbookLibraryMembership(ctx, exec, "content-1", 44); err != nil { + t.Fatalf("insertEbookLibraryMembership: %v", err) + } + if err := insertEbookISBNProviderID(ctx, exec, "content-1", "9780306406157"); err != nil { + t.Fatalf("insertEbookISBNProviderID: %v", err) + } + if len(exec.queries) != 2 { + t.Fatalf("queries = %d, want 2", len(exec.queries)) + } + if !strings.Contains(exec.queries[0], "media_item_libraries") || exec.args[0][0] != "content-1" || exec.args[0][1] != 44 { + t.Fatalf("library membership write = query %q args %+v", exec.queries[0], exec.args[0]) + } + if !strings.Contains(exec.queries[1], "media_item_provider_ids") || !strings.Contains(exec.queries[1], "'isbn'") || !strings.Contains(exec.queries[1], "'ebook'") { + t.Fatalf("ISBN provider write query = %q", exec.queries[1]) + } + if strings.Contains(exec.queries[1], "asin") || exec.args[1][0] != "content-1" || exec.args[1][1] != "9780306406157" { + t.Fatalf("ISBN provider write args/query = query %q args %+v", exec.queries[1], exec.args[1]) + } +} + +func TestCollectEbookRootScansExcludesUnmountedRootFromReconciliation(t *testing.T) { + healthy := t.TempDir() + if err := os.WriteFile(filepath.Join(healthy, "book.epub"), []byte("x"), 0o644); err != nil { + t.Fatalf("write ebook: %v", err) + } + unmounted := filepath.Join(t.TempDir(), "gone") + + scans, err := collectEbookRootScans(context.Background(), 44, []string{unmounted, healthy}) + if err != nil { + t.Fatalf("collectEbookRootScans: %v", err) + } + if len(scans) != 2 { + t.Fatalf("scans = %d, want 2", len(scans)) + } + if !scans[0].failed() || scans[0].rootErr == nil { + t.Fatalf("unmounted root scan = %+v, want failed with rootErr", scans[0]) + } + if scans[1].failed() || len(scans[1].files) != 1 { + t.Fatalf("healthy root scan = %+v, want 1 file and no failure", scans[1]) + } + + reconcileRoots, sawFiles := splitEbookReconcileRoots(scans) + if !sawFiles { + t.Fatal("sawFiles = false, want true for the healthy root") + } + if len(reconcileRoots) != 1 || reconcileRoots[0] != healthy { + t.Fatalf("reconcileRoots = %v, want only the healthy root: an unmounted root must never be reconciled (no mass-missing, no deletion)", reconcileRoots) + } +} + +func TestCollectEbookRootScansTreatsNonDirectoryRootAsFailed(t *testing.T) { + dir := t.TempDir() + fileRoot := filepath.Join(dir, "book.epub") + if err := os.WriteFile(fileRoot, []byte("x"), 0o644); err != nil { + t.Fatalf("write ebook: %v", err) + } + + scans, err := collectEbookRootScans(context.Background(), 44, []string{fileRoot}) + if err != nil { + t.Fatalf("collectEbookRootScans: %v", err) + } + if len(scans) != 1 || !scans[0].failed() { + t.Fatalf("scans = %+v, want one failed scan for a non-directory root", scans) + } + // The file itself is still indexed; only reconciliation is withheld. + if len(scans[0].files) != 1 { + t.Fatalf("files = %v, want the ebook file root indexed", scans[0].files) + } + if roots, _ := splitEbookReconcileRoots(scans); len(roots) != 0 { + t.Fatalf("reconcileRoots = %v, want none", roots) + } +} + +func TestCollectEbookRootScansMidWalkSubtreeErrorExcludesRoot(t *testing.T) { + if os.Getuid() == 0 { + t.Skip("permission-based subtree failure cannot be simulated as root") + } + root := t.TempDir() + if err := os.WriteFile(filepath.Join(root, "readable.epub"), []byte("x"), 0o644); err != nil { + t.Fatalf("write ebook: %v", err) + } + locked := filepath.Join(root, "zz-locked") + if err := os.Mkdir(locked, 0o755); err != nil { + t.Fatalf("mkdir: %v", err) + } + if err := os.WriteFile(filepath.Join(locked, "hidden.epub"), []byte("x"), 0o644); err != nil { + t.Fatalf("write hidden ebook: %v", err) + } + if err := os.Chmod(locked, 0o000); err != nil { + t.Fatalf("chmod: %v", err) + } + t.Cleanup(func() { _ = os.Chmod(locked, 0o755) }) + + scans, err := collectEbookRootScans(context.Background(), 44, []string{root}) + if err != nil { + t.Fatalf("collectEbookRootScans: %v", err) + } + if len(scans) != 1 { + t.Fatalf("scans = %d, want 1", len(scans)) + } + if scans[0].walkFailures == 0 || !scans[0].failed() { + t.Fatalf("scan = %+v, want walk failure recorded for unreadable subtree", scans[0]) + } + if len(scans[0].files) != 1 { + t.Fatalf("files = %v, want the readable ebook still indexed", scans[0].files) + } + // The unseen hidden.epub must not be marked missing: the whole root sits + // out of reconciliation. + if roots, _ := splitEbookReconcileRoots(scans); len(roots) != 0 { + t.Fatalf("reconcileRoots = %v, want none after a mid-walk subtree error", roots) + } +} + +func TestCollectEbookRootScansFollowsSymlinkedRoot(t *testing.T) { + target := t.TempDir() + if err := os.WriteFile(filepath.Join(target, "book.epub"), []byte("x"), 0o644); err != nil { + t.Fatalf("write ebook: %v", err) + } + link := filepath.Join(t.TempDir(), "library") + if err := os.Symlink(target, link); err != nil { + t.Fatalf("symlink: %v", err) + } + + scans, err := collectEbookRootScans(context.Background(), 44, []string{link}) + if err != nil { + t.Fatalf("collectEbookRootScans: %v", err) + } + if len(scans) != 1 || scans[0].failed() { + t.Fatalf("scans = %+v, want one clean scan through the symlinked root", scans) + } + want := filepath.Join(link, "book.epub") + if len(scans[0].files) != 1 || scans[0].files[0] != want { + t.Fatalf("files = %v, want %q recorded under the logical (symlink) path", scans[0].files, want) + } + if roots, sawFiles := splitEbookReconcileRoots(scans); !sawFiles || len(roots) != 1 { + t.Fatalf("reconcileRoots/sawFiles = %v/%v, want symlinked root reconcilable", roots, sawFiles) + } +} + +type fakeEbookCleanupGuard struct { + allowance bool + consumeCalls int + warnings []string + consumeErr error +} + +func (f *fakeEbookCleanupGuard) ConsumeEmptyCleanupAllowance(_ context.Context, _ int) (bool, error) { + f.consumeCalls++ + return f.allowance, f.consumeErr +} + +func (f *fakeEbookCleanupGuard) SetScanWarning(_ context.Context, _ int, code, _ string, _ time.Time) error { + f.warnings = append(f.warnings, code) + return nil +} + +func TestEbookEmptyCleanupAllowedRequiresExplicitAllowance(t *testing.T) { + guard := &fakeEbookCleanupGuard{allowance: false} + + allowed, err := ebookEmptyCleanupAllowed(context.Background(), guard, 44, true) + if err != nil { + t.Fatalf("ebookEmptyCleanupAllowed: %v", err) + } + if allowed { + t.Fatal("allowed = true, want false: an empty root must not reconcile without confirmation") + } + if guard.consumeCalls != 1 { + t.Fatalf("consumeCalls = %d, want 1", guard.consumeCalls) + } + if len(guard.warnings) != 1 || guard.warnings[0] != "empty_root" { + t.Fatalf("warnings = %v, want one empty_root scan warning", guard.warnings) + } +} + +func TestEbookEmptyCleanupAllowedConsumesAllowanceOnce(t *testing.T) { + guard := &fakeEbookCleanupGuard{allowance: true} + + allowed, err := ebookEmptyCleanupAllowed(context.Background(), guard, 44, true) + if err != nil { + t.Fatalf("ebookEmptyCleanupAllowed: %v", err) + } + if !allowed { + t.Fatal("allowed = false, want true once the operator confirmed cleanup") + } + if len(guard.warnings) != 0 { + t.Fatalf("warnings = %v, want none for a confirmed cleanup", guard.warnings) + } +} + +func TestEbookEmptyCleanupAllowedNeverConsumesForSubtreeScans(t *testing.T) { + guard := &fakeEbookCleanupGuard{allowance: true} + + allowed, err := ebookEmptyCleanupAllowed(context.Background(), guard, 44, false) + if err != nil { + t.Fatalf("ebookEmptyCleanupAllowed: %v", err) + } + if allowed || guard.consumeCalls != 0 { + t.Fatalf("allowed/consumeCalls = %v/%d, want subtree scans to skip cleanup without touching the folder allowance", allowed, guard.consumeCalls) + } +} + +func TestEbookEmptyCleanupAllowedNilGuardDeniesCleanup(t *testing.T) { + allowed, err := ebookEmptyCleanupAllowed(context.Background(), nil, 44, true) + if err != nil || allowed { + t.Fatalf("allowed/err = %v/%v, want denial when no guard repository is wired", allowed, err) + } +} + +func TestScanEbookFolderReturnsErrorWhenEveryReconcileFails(t *testing.T) { + root := t.TempDir() + if err := os.WriteFile(filepath.Join(root, "bad.epub"), []byte("not a real epub"), 0o644); err != nil { + t.Fatalf("write fake ebook: %v", err) + } + + s := &Scanner{} + err := s.ScanEbookFolder(context.Background(), &models.MediaFolder{ID: 44, Paths: []string{root}}) + if err == nil { + t.Fatal("ScanEbookFolder returned nil, want aggregate failure") + } + if !strings.Contains(err.Error(), "folder_id=44") { + t.Fatalf("error = %q, want folder id", err) + } +} + +func TestScanEbookFolderReturnsCanceledContext(t *testing.T) { + root := t.TempDir() + ctx, cancel := context.WithCancel(context.Background()) + cancel() + + s := &Scanner{} + err := s.ScanEbookFolder(ctx, &models.MediaFolder{ID: 44, Paths: []string{root}}) + if !errors.Is(err, context.Canceled) { + t.Fatalf("ScanEbookFolder error = %v, want context.Canceled", err) + } +} + +func TestScanEbookFolderReportsProgress(t *testing.T) { + root := t.TempDir() + if err := os.WriteFile(filepath.Join(root, "broken.epub"), []byte("not a zip"), 0o644); err != nil { + t.Fatalf("write ebook fixture: %v", err) + } + var updates []ProgressUpdate + ctx := WithProgressReporter(context.Background(), func(update ProgressUpdate) { + updates = append(updates, update) + }) + + err := (&Scanner{}).ScanEbookFolder(ctx, &models.MediaFolder{ID: 44, Paths: []string{root}}) + if err == nil { + t.Fatal("ScanEbookFolder returned nil, want aggregate failure") + } + + if len(updates) == 0 { + t.Fatal("expected ebook scanner progress updates") + } + if updates[0].Phase != "ebook_scan" || updates[0].TotalFiles != 1 { + t.Fatalf("first progress update = %+v, want ebook_scan discovery total", updates[0]) + } + last := updates[len(updates)-1] + if last.FilesProcessed != 1 || last.Errors != 1 { + t.Fatalf("last progress update = %+v, want processed/error counts", last) + } +} + +func writeTestEPUB(t *testing.T, identifiers []string) string { + return writeTestEPUBWithMeta(t, identifiers, "") +} + +func writeTestEPUBWithMeta(t *testing.T, identifiers []string, extraMetaXML string) string { + return writeTestEPUBWithDescriptionAndMeta(t, identifiers, "Back cover copy", extraMetaXML) +} + +func writeTestEPUBWithDescription(t *testing.T, description string) string { + return writeTestEPUBWithDescriptionAndMeta(t, []string{"ISBN: 978-0-306-40615-7"}, description, "") +} + +func writeTestEPUBWithDescriptionAndMeta(t *testing.T, identifiers []string, description string, extraMetaXML string) string { + t.Helper() + + path := filepath.Join(t.TempDir(), "book.epub") + file, err := os.Create(path) + if err != nil { + t.Fatalf("create epub: %v", err) + } + zw := zip.NewWriter(file) + add := func(name, body string) { + t.Helper() + w, err := zw.Create(name) + if err != nil { + t.Fatalf("create zip entry %s: %v", name, err) + } + if _, err := w.Write([]byte(body)); err != nil { + t.Fatalf("write zip entry %s: %v", name, err) + } + } + add("META-INF/container.xml", ` + + + + +`) + var identifierXML strings.Builder + for _, identifier := range identifiers { + identifierXML.WriteString(" ") + identifierXML.WriteString(identifier) + identifierXML.WriteString("\n") + } + add("OPS/content.opf", ` + + + The Test Ebook + Ada Writer + Ben Author +`+identifierXML.String()+` + Silo Press + 2024-03-10 + en + Fiction + Adventure + `+description+` + + +`+extraMetaXML+` + +`) + if err := zw.Close(); err != nil { + t.Fatalf("close zip: %v", err) + } + if err := file.Close(); err != nil { + t.Fatalf("close epub: %v", err) + } + return path +} + +func writeTestEPUBWithOPFBytes(t *testing.T, opf []byte) string { + t.Helper() + + path := filepath.Join(t.TempDir(), "book.epub") + file, err := os.Create(path) + if err != nil { + t.Fatalf("create epub: %v", err) + } + zw := zip.NewWriter(file) + add := func(name string, body []byte) { + t.Helper() + w, err := zw.Create(name) + if err != nil { + t.Fatalf("create zip entry %s: %v", name, err) + } + if _, err := w.Write(body); err != nil { + t.Fatalf("write zip entry %s: %v", name, err) + } + } + add("META-INF/container.xml", []byte(` + + + + +`)) + add("OPS/content.opf", opf) + if err := zw.Close(); err != nil { + t.Fatalf("close zip: %v", err) + } + if err := file.Close(); err != nil { + t.Fatalf("close epub: %v", err) + } + return path +} + +func writeTestEPUBWithCover(t *testing.T, coverHref string, coverType string, coverBytes []byte) string { + t.Helper() + + path := filepath.Join(t.TempDir(), "book.epub") + file, err := os.Create(path) + if err != nil { + t.Fatalf("create epub: %v", err) + } + zw := zip.NewWriter(file) + add := func(name string, body []byte) { + t.Helper() + w, err := zw.Create(name) + if err != nil { + t.Fatalf("create zip entry %s: %v", name, err) + } + if _, err := w.Write(body); err != nil { + t.Fatalf("write zip entry %s: %v", name, err) + } + } + add("META-INF/container.xml", []byte(` + + + + +`)) + add("OPS/content.opf", []byte(` + + + The Test Ebook + + + + + +`)) + add("OPS/"+coverHref, coverBytes) + if err := zw.Close(); err != nil { + t.Fatalf("close zip: %v", err) + } + if err := file.Close(); err != nil { + t.Fatalf("close epub: %v", err) + } + return path +} + +func TestUpdateExistingEbookMediaItemPreservesCuratedMatchedItem(t *testing.T) { + reader := &fakeFilesystemItemReader{items: []*models.MediaItem{{ + ContentID: "ebook-root-id", + Type: "ebook", + Title: "Curated Title", + Year: 2020, + Status: "matched", + }}} + writer := &fakeFilesystemItemWriter{} + + curated, err := updateExistingEbookMediaItem(context.Background(), reader, writer, "ebook-root-id", &parsedEbook{ + Title: "Embedded File Title", + Year: 2026, + }) + if err != nil { + t.Fatalf("updateExistingEbookMediaItem: %v", err) + } + if !curated { + t.Fatal("curated = false, want true for a matched item") + } + if len(writer.upserts) != 0 { + t.Fatalf("upserts = %d, want 0: matched items must not be overwritten by file metadata", len(writer.upserts)) + } +} + +func TestEbookItemHasCuratedMetadata(t *testing.T) { + cases := []struct { + item *models.MediaItem + want bool + }{ + {nil, false}, + {&models.MediaItem{Status: "matched"}, true}, + {&models.MediaItem{Status: " Matched "}, true}, + {&models.MediaItem{Status: "pending"}, false}, + {&models.MediaItem{Status: "unmatched"}, false}, + {&models.MediaItem{}, false}, + } + for _, tc := range cases { + if got := ebookItemHasCuratedMetadata(tc.item); got != tc.want { + t.Errorf("ebookItemHasCuratedMetadata(%+v) = %v, want %v", tc.item, got, tc.want) + } + } +} + +func TestParseEbookPDFReadsInfoDictionaryFromFileTail(t *testing.T) { + // Non-linearized PDFs put the Info dictionary at the end of the file and + // typically have no Info keys in the head at all; the parser must find the + // trailing dictionary past the head scan window and use it to fill every + // key the head did not produce. + head := "%PDF-1.7\n" + + "1 0 obj\n" + + "<< /Author (Ada Writer)\n" + + ">>\nendobj\n" + tail := "2 0 obj\n" + + "<< /Title (Tail Info Title)\n" + + " /CreationDate (D:20240102030405Z)\n" + + ">>\nendobj\n" + + "trailer\n<< /Info 2 0 R >>\n%%EOF" + padding := strings.Repeat("x", maxPDFMetadataScanSize) + + path := filepath.Join(t.TempDir(), "book.pdf") + if err := os.WriteFile(path, []byte(head+padding+tail), 0o644); err != nil { + t.Fatalf("write pdf: %v", err) + } + + got, err := parseEbookFile(path) + if err != nil { + t.Fatalf("parseEbookFile: %v", err) + } + if got.Title != "Tail Info Title" { + t.Fatalf("Title = %q, want tail Info dictionary value", got.Title) + } + if strings.Join(got.Authors, ", ") != "Ada Writer" { + t.Fatalf("Authors = %v, want head-only value preserved", got.Authors) + } + if got.Year != 2024 { + t.Fatalf("Year = %d, want 2024 from tail CreationDate", got.Year) + } +} + +func TestParseEbookPDFHeadInfoWinsOverTailStreamGarbage(t *testing.T) { + // Linearized PDFs carry the real Info dictionary in the head; a + // "/Title"-shaped byte pattern inside a trailing compressed stream must + // not replace it. + head := "%PDF-1.7\n" + + "1 0 obj\n" + + "<< /Linearized 1 >>\nendobj\n" + + "2 0 obj\n" + + "<< /Title (Real Head Title)\n" + + " /Author (Ada Writer)\n" + + ">>\nendobj\n" + tail := "3 0 obj\n" + + "<< /Length 64 >>\nstream\n" + + "\x01\x02/Title (\xfe\xba\xad binary stream garbage)\x03\x04\n" + + "endstream\nendobj\n" + + "trailer\n<< /Info 2 0 R >>\n%%EOF" + padding := strings.Repeat("x", maxPDFMetadataScanSize) + + path := filepath.Join(t.TempDir(), "book.pdf") + if err := os.WriteFile(path, []byte(head+padding+tail), 0o644); err != nil { + t.Fatalf("write pdf: %v", err) + } + + got, err := parseEbookFile(path) + if err != nil { + t.Fatalf("parseEbookFile: %v", err) + } + if got.Title != "Real Head Title" { + t.Fatalf("Title = %q, want authoritative head value over tail stream garbage", got.Title) + } +} + +func TestFindPDFInfoValueRequiresProperKeyDelimiter(t *testing.T) { + // "/TitleSort" must not satisfy a "/Title" lookup, and the real key later + // in the window must still be found. + data := []byte(`<< /TitleSort (Adventures of, The) /Title (The Adventures) >>`) + value, ok := findPDFInfoValue(data, "Title") + if !ok || value != "The Adventures" { + t.Fatalf("findPDFInfoValue = %q, %v; want the properly delimited /Title", value, ok) + } +} + +func TestFindPDFInfoValueRetriesPastUnparseableOccurrences(t *testing.T) { + // A key match inside binary stream data whose "value" is not a PDF string + // must not end the search; the real key/value pair later in the window + // still wins. + data := []byte("stream\x00/Title \x07\x08garbage\x00endstream\n" + + "<< /Title (Recovered Title) >>") + value, ok := findPDFInfoValue(data, "Title") + if !ok || value != "Recovered Title" { + t.Fatalf("findPDFInfoValue = %q, %v; want recovery past unparseable match", value, ok) + } + + if _, ok := findPDFInfoValue([]byte("/Titled (Nope)"), "Title"); ok { + t.Fatal("findPDFInfoValue matched a longer key sharing the prefix") + } +} + +func TestParseEbookFB2RejectsOversizedFile(t *testing.T) { + path := filepath.Join(t.TempDir(), "book.fb2") + if err := os.WriteFile(path, make([]byte, maxEPUBMetadataEntrySize+1), 0o644); err != nil { + t.Fatalf("write fb2: %v", err) + } + + _, err := parseEbookFile(path) + if err == nil || !strings.Contains(err.Error(), "too large") { + t.Fatalf("parseEbookFile error = %v, want size-cap rejection", err) + } +} diff --git a/internal/scanner/scanner.go b/internal/scanner/scanner.go index 89040001..a084268c 100644 --- a/internal/scanner/scanner.go +++ b/internal/scanner/scanner.go @@ -97,9 +97,12 @@ func shouldSkipMovieSupplementalFile(path string) bool { } // Scanner discovers and indexes media files in media folders. -// scannerImageCacher is the slice of imagecache.Cacher the audiobook -// branch uses. Wired via SetImageCacher; nil-safe when absent. -type scannerImageCacher = audiobookCoverCacher +// scannerImageCacher is the slice of imagecache.Cacher the book scanners use. +// Wired via SetImageCacher; nil-safe when absent. +type scannerImageCacher interface { + audiobookCoverCacher + ebookCoverCacher +} type Scanner struct { fileRepo *FileRepository @@ -126,9 +129,9 @@ type Scanner struct { seriesQueueSyncer SeriesQueueSyncer } -// SetImageCacher installs the imagecache.Cacher used by the audiobook -// branch to push embedded M4B cover art into the public assets bucket. -// Optional; if unset, audiobook covers are not extracted. +// SetImageCacher installs the imagecache.Cacher used by book scanners to push +// embedded cover art into the public assets bucket. Optional; if unset, local +// covers are not extracted. func (s *Scanner) SetImageCacher(cacher scannerImageCacher) { if s == nil { return @@ -241,6 +244,13 @@ func (s *Scanner) ScanFolder(ctx context.Context, folder *models.MediaFolder) (* return &ScanResult{}, nil } + if isEbookLibraryType(folder.Type) { + if err := s.ScanEbookFolder(watchCtx, folder); err != nil { + return nil, err + } + return &ScanResult{}, nil + } + return s.scanPaths(watchCtx, folder, folder.Paths, folder.Paths, true) } @@ -263,6 +273,12 @@ func (s *Scanner) ScanSubtree(ctx context.Context, folder *models.MediaFolder, s } return &ScanResult{}, nil } + if isEbookLibraryType(folder.Type) { + if err := s.scanEbookPaths(watchCtx, folder, []string{cleanSubtree}, false); err != nil { + return nil, err + } + return &ScanResult{}, nil + } return s.scanPaths(watchCtx, folder, []string{cleanSubtree}, []string{cleanSubtree}, false) } @@ -309,6 +325,15 @@ func isPodcastLibraryType(libraryType string) bool { } } +func isEbookLibraryType(libraryType string) bool { + switch strings.ToLower(strings.TrimSpace(libraryType)) { + case "ebook", "ebooks": + return true + default: + return false + } +} + // walkMode tells walkLogicalTree which file extensions to surface and // which library-specific filename heuristics (sample/extra skipping) // to apply. @@ -319,6 +344,7 @@ const ( walkModeMovie // movie library: video extensions + sample/extra skipping walkModeAudiobook // audiobook library: audio extensions, no skipping walkModePodcast // podcast library: audio extensions, no skipping + walkModeEbook // ebook library: ebook extensions, no skipping ) // walkModeFor derives a walkMode from a media_folders.type string. @@ -332,6 +358,8 @@ func walkModeFor(folderType string) walkMode { return walkModeAudiobook case isPodcastLibraryType(folderType): return walkModePodcast + case isEbookLibraryType(folderType): + return walkModeEbook default: return walkModeVideo } @@ -343,6 +371,8 @@ func (m walkMode) acceptsExt(ext string) bool { switch m { case walkModeAudiobook, walkModePodcast: return audioExtensions[ext] + case walkModeEbook: + return ebookExtensions[ext] default: return videoExtensions[ext] } @@ -364,6 +394,16 @@ func isIgnoredDirectoryPath(path string) bool { return ignoredDirNames[strings.ToLower(filepath.Base(path))] } +// recordWalkFailure counts an entry the walk could not read or resolve. +// Callers that pass a non-nil counter (the book scanners) use it to exclude +// incompletely walked roots from missing-file reconciliation; the video walk +// passes nil and keeps its historical log-and-continue behavior. +func recordWalkFailure(failures *int) { + if failures != nil { + *failures++ + } +} + func walkLogicalTree( ctx context.Context, logicalPath string, @@ -371,6 +411,7 @@ func walkLogicalTree( mode walkMode, visitedPhysicalDirs map[string]struct{}, filePaths *[]string, + walkFailures *int, ) error { if ctx != nil { if err := ctx.Err(); err != nil { @@ -381,6 +422,7 @@ func walkLogicalTree( info, err := os.Lstat(physicalPath) if err != nil { slog.Warn("scanner: walk lstat failed", "path", logicalPath, "physical_path", physicalPath, "error", err) + recordWalkFailure(walkFailures) return nil } @@ -388,15 +430,17 @@ func walkLogicalTree( resolved, err := filepath.EvalSymlinks(physicalPath) if err != nil { slog.Warn("scanner: symlink resolve failed", "path", logicalPath, "physical_path", physicalPath, "error", err) + recordWalkFailure(walkFailures) return nil } targetInfo, err := os.Stat(resolved) if err != nil { slog.Warn("scanner: symlink stat failed", "path", logicalPath, "resolved_path", resolved, "error", err) + recordWalkFailure(walkFailures) return nil } if targetInfo.IsDir() { - return walkLogicalTree(ctx, logicalPath, resolved, mode, visitedPhysicalDirs, filePaths) + return walkLogicalTree(ctx, logicalPath, resolved, mode, visitedPhysicalDirs, filePaths, walkFailures) } if mode == walkModeMovie && shouldSkipMovieSupplementalFile(logicalPath) { return nil @@ -420,6 +464,7 @@ func walkLogicalTree( canonicalDir, err := canonicalWalkPath(physicalPath) if err != nil { slog.Warn("scanner: canonical path resolution failed", "path", logicalPath, "physical_path", physicalPath, "error", err) + recordWalkFailure(walkFailures) return nil } if _, seen := visitedPhysicalDirs[canonicalDir]; seen { @@ -437,6 +482,7 @@ func walkLogicalTree( entries, err := os.ReadDir(physicalPath) if err != nil { slog.Warn("scanner: directory read failed", "path", logicalPath, "physical_path", physicalPath, "error", err) + recordWalkFailure(walkFailures) return nil } for _, entry := range entries { @@ -453,15 +499,17 @@ func walkLogicalTree( resolved, err := filepath.EvalSymlinks(physicalChild) if err != nil { slog.Warn("scanner: symlink resolve failed", "path", logicalChild, "physical_path", physicalChild, "error", err) + recordWalkFailure(walkFailures) continue } targetInfo, err := os.Stat(resolved) if err != nil { slog.Warn("scanner: symlink stat failed", "path", logicalChild, "resolved_path", resolved, "error", err) + recordWalkFailure(walkFailures) continue } if targetInfo.IsDir() { - if err := walkLogicalTree(ctx, logicalChild, resolved, mode, visitedPhysicalDirs, filePaths); err != nil { + if err := walkLogicalTree(ctx, logicalChild, resolved, mode, visitedPhysicalDirs, filePaths, walkFailures); err != nil { return err } continue @@ -476,7 +524,7 @@ func walkLogicalTree( } if entry.IsDir() { - if err := walkLogicalTree(ctx, logicalChild, physicalChild, mode, visitedPhysicalDirs, filePaths); err != nil { + if err := walkLogicalTree(ctx, logicalChild, physicalChild, mode, visitedPhysicalDirs, filePaths, walkFailures); err != nil { return err } continue @@ -508,7 +556,7 @@ func collectLogicalFilePaths(ctx context.Context, walkRoots []string, libraryTyp if cleanRoot == "" || cleanRoot == "." { continue } - if err := walkLogicalTree(ctx, cleanRoot, cleanRoot, mode, visitedPhysicalDirs, &filePaths); err != nil { + if err := walkLogicalTree(ctx, cleanRoot, cleanRoot, mode, visitedPhysicalDirs, &filePaths, nil); err != nil { return nil, err } } diff --git a/internal/scanner/scanner_test.go b/internal/scanner/scanner_test.go index edde4348..d548c264 100644 --- a/internal/scanner/scanner_test.go +++ b/internal/scanner/scanner_test.go @@ -200,3 +200,76 @@ func TestIsPodcastLibraryType(t *testing.T) { } } } + +func TestIsEbookLibraryType(t *testing.T) { + cases := []struct { + in string + want bool + }{ + {"ebooks", true}, + {"ebook", true}, + {"Ebook", true}, + {" EBOOKS ", true}, + {"audiobooks", false}, + {"movies", false}, + {"", false}, + } + for _, tc := range cases { + if got := isEbookLibraryType(tc.in); got != tc.want { + t.Errorf("isEbookLibraryType(%q) = %v, want %v", tc.in, got, tc.want) + } + } +} + +func TestWalkModeForEbookLibraryTypes(t *testing.T) { + for _, libraryType := range []string{"ebook", "ebooks", " EBOOKS "} { + if got := walkModeFor(libraryType); got != walkModeEbook { + t.Fatalf("walkModeFor(%q) = %v, want %v", libraryType, got, walkModeEbook) + } + } +} + +func TestWalkModeEbookAcceptsEbookExtensionsOnly(t *testing.T) { + for _, ext := range []string{".epub", ".pdf", ".mobi", ".azw", ".azw3", ".fb2", ".fbz", ".cbz", ".cbr"} { + if !walkModeEbook.acceptsExt(ext) { + t.Fatalf("walkModeEbook should accept %s", ext) + } + } + for _, ext := range []string{".txt", ".md", ".mp4", ".mp3", ".mkv"} { + if walkModeEbook.acceptsExt(ext) { + t.Fatalf("walkModeEbook should reject %s", ext) + } + } +} + +func TestScanFolderEbookLibraryRoutesToEbookScanner(t *testing.T) { + scanner := &Scanner{} + result, err := scanner.ScanFolder(context.Background(), &models.MediaFolder{ + ID: 0, + Type: " ebooks ", + Paths: []string{t.TempDir()}, + }) + if err != nil { + t.Fatalf("ScanFolder ebook error = %v, want nil", err) + } + if result == nil { + t.Fatal("ScanFolder ebook result = nil, want empty result") + } +} + +func TestScanSubtreeEbookLibraryRoutesToEbookScanner(t *testing.T) { + subtree := t.TempDir() + + scanner := &Scanner{} + result, err := scanner.ScanSubtree(context.Background(), &models.MediaFolder{ + ID: 0, + Type: "ebook", + Paths: []string{subtree}, + }, subtree) + if err != nil { + t.Fatalf("ScanSubtree ebook error = %v, want nil", err) + } + if result == nil { + t.Fatal("ScanSubtree ebook result = nil, want empty result") + } +} diff --git a/internal/sections/defaults.go b/internal/sections/defaults.go index 1d27ec80..409546bc 100644 --- a/internal/sections/defaults.go +++ b/internal/sections/defaults.go @@ -199,6 +199,14 @@ func DefaultLibrarySectionsForType(libraryID *int, libraryType string) []*PageSe {ID: "default-recommended-for-you", Scope: "library", LibraryID: libraryID, Position: 4, SectionType: SectionRecommendedForYou, Title: "Recommended for You", ItemLimit: 20, Config: emptyCfg, Enabled: true}, {ID: "default-random-audiobooks", Scope: "library", LibraryID: libraryID, Position: 5, SectionType: SectionRandom, Title: "Random Picks", ItemLimit: 20, Config: defaultMediaScopeConfig("audiobook"), Enabled: true}, } + case "ebooks", "ebook": + return []*PageSection{ + {ID: "default-continue-reading", Scope: "library", LibraryID: libraryID, Position: 0, SectionType: SectionContinueWatching, Title: "Continue Reading", ItemLimit: 20, Config: ContinueTypeConfig(ContinueTypeReading), Enabled: true}, + {ID: "default-recently-added-ebooks", Scope: "library", LibraryID: libraryID, Position: 1, SectionType: SectionRecentlyAdded, Title: "Recently Added Ebooks", ItemLimit: 20, Config: defaultMediaScopeConfig("ebook"), Enabled: true}, + {ID: "default-recently-released-ebooks", Scope: "library", LibraryID: libraryID, Position: 2, SectionType: SectionRecentlyReleased, Title: "Recently Released Ebooks", ItemLimit: 20, Config: defaultMediaScopeConfig("ebook"), Enabled: true}, + {ID: "default-recommended-for-you", Scope: "library", LibraryID: libraryID, Position: 3, SectionType: SectionRecommendedForYou, Title: "Recommended for You", ItemLimit: 20, Config: emptyCfg, Enabled: true}, + {ID: "default-random-ebooks", Scope: "library", LibraryID: libraryID, Position: 4, SectionType: SectionRandom, Title: "Random Picks", ItemLimit: 20, Config: defaultMediaScopeConfig("ebook"), Enabled: true}, + } default: return DefaultLibrarySections(libraryID) } diff --git a/internal/sections/defaults_test.go b/internal/sections/defaults_test.go index af874e54..7b1b898e 100644 --- a/internal/sections/defaults_test.go +++ b/internal/sections/defaults_test.go @@ -323,6 +323,69 @@ func TestDefaultLibrarySectionsForTypeAudiobooks(t *testing.T) { }) } +func TestDefaultLibrarySectionsForTypeEbooks(t *testing.T) { + libraryID := 11 + got := DefaultLibrarySectionsForType(&libraryID, "ebooks") + + if len(got) != 5 { + t.Fatalf("expected 5 ebook default sections, got %d", len(got)) + } + + tests := []struct { + index int + id string + sectionType SectionType + title string + position int + }{ + {index: 0, id: "default-continue-reading", sectionType: SectionContinueWatching, title: "Continue Reading", position: 0}, + {index: 1, id: "default-recently-added-ebooks", sectionType: SectionRecentlyAdded, title: "Recently Added Ebooks", position: 1}, + {index: 2, id: "default-recently-released-ebooks", sectionType: SectionRecentlyReleased, title: "Recently Released Ebooks", position: 2}, + {index: 3, id: "default-recommended-for-you", sectionType: SectionRecommendedForYou, title: "Recommended for You", position: 3}, + {index: 4, id: "default-random-ebooks", sectionType: SectionRandom, title: "Random Picks", position: 4}, + } + + for _, tt := range tests { + section := got[tt.index] + if section.ID != tt.id { + t.Fatalf("section %d id = %q, want %q", tt.index, section.ID, tt.id) + } + if section.SectionType != tt.sectionType { + t.Fatalf("section %d type = %q, want %q", tt.index, section.SectionType, tt.sectionType) + } + if section.Title != tt.title { + t.Fatalf("section %d title = %q, want %q", tt.index, section.Title, tt.title) + } + if section.Position != tt.position { + t.Fatalf("section %d position = %d, want %d", tt.index, section.Position, tt.position) + } + if section.Featured { + t.Fatalf("section %d featured = true, want false", tt.index) + } + } + + assertContinueType(t, got[0].Config, ContinueTypeReading) + assertQueryDefinition(t, got[1].Config, catalog.QueryDefinition{ + MediaScope: "ebook", + Match: "all", + Groups: []catalog.QueryGroup{}, + Sort: catalog.QuerySort{Field: "added_at", Order: "desc"}, + }) + assertQueryDefinition(t, got[2].Config, catalog.QueryDefinition{ + MediaScope: "ebook", + Match: "all", + Groups: []catalog.QueryGroup{}, + Sort: catalog.QuerySort{Field: "added_at", Order: "desc"}, + }) + assertEmptyJSON(t, got[3].Config) + assertQueryDefinition(t, got[4].Config, catalog.QueryDefinition{ + MediaScope: "ebook", + Match: "all", + Groups: []catalog.QueryGroup{}, + Sort: catalog.QuerySort{Field: "added_at", Order: "desc"}, + }) +} + func TestDefaultLibrarySectionsForTypeMixed(t *testing.T) { libraryID := 99 got := DefaultLibrarySectionsForType(&libraryID, "mixed") diff --git a/internal/sections/fetcher.go b/internal/sections/fetcher.go index a220ee85..c28ee8dc 100644 --- a/internal/sections/fetcher.go +++ b/internal/sections/fetcher.go @@ -382,35 +382,39 @@ func (f *Fetcher) fetchContinueWatchingSection(ctx context.Context, resolved Res orderedItems := make([]*models.MediaItem, 0, limit) itemMeta := make(map[string]SectionItemMeta) - for offset := 0; len(orderedItems) < limit && offset < continueProgressMaxScanned; offset += continueProgressPageSize { - pageLimit := continueProgressPageSize - if remaining := continueProgressMaxScanned - offset; remaining < pageLimit { - pageLimit = remaining - } - progressEntries, err := store.ListProgress(ctx, profileID, "in_progress", pageLimit, offset) - if err != nil { - return SectionWithItems{}, fmt.Errorf("listing progress: %w", err) - } - if len(progressEntries) == 0 { - break - } - rawProgressCount := len(progressEntries) - progressEntries = dismissals.FilterProgress(progressEntries) - - pageItems, pageMeta, err := f.fetchContinueProgressItems(ctx, store, profileID, progressEntries, continueType, effectiveLibID, effectiveLibraryIDs, filter) + // Ebook resume points live in ebook_reader_progress rather than the + // watch-progress store, so reading sections pull from that table and skip + // the next-up handling below. + if continueType == ContinueTypeReading { + orderedItems, err = f.collectContinueProgressItems(ctx, store, profileID, dismissals, continueType, effectiveLibID, effectiveLibraryIDs, filter, limit, orderedItems, itemMeta, + func(pageLimit, offset int) ([]userstore.WatchProgress, error) { + entries, err := f.listEbookContinueWatchingProgress(ctx, userID, profileID, pageLimit, offset) + if err != nil { + return nil, fmt.Errorf("listing ebook reader progress: %w", err) + } + return entries, nil + }) if err != nil { return SectionWithItems{}, err } - for _, item := range pageItems { - if len(orderedItems) >= limit { - break + return SectionWithItems{ + ResolvedSection: resolved, + Items: orderedItems, + TotalCount: len(orderedItems), + ItemMeta: itemMeta, + }, nil + } + + orderedItems, err = f.collectContinueProgressItems(ctx, store, profileID, dismissals, continueType, effectiveLibID, effectiveLibraryIDs, filter, limit, orderedItems, itemMeta, + func(pageLimit, offset int) ([]userstore.WatchProgress, error) { + entries, err := store.ListProgress(ctx, profileID, "in_progress", pageLimit, offset) + if err != nil { + return nil, fmt.Errorf("listing progress: %w", err) } - orderedItems = append(orderedItems, item) - } - maps.Copy(itemMeta, pageMeta) - if rawProgressCount < pageLimit { - break - } + return entries, nil + }) + if err != nil { + return SectionWithItems{}, err } nextUpMode := "" @@ -454,6 +458,137 @@ func (f *Fetcher) fetchContinueWatchingSection(ctx context.Context, resolved Res }, nil } +// collectContinueProgressItems pages through in-progress entries from +// listPage, drops dismissed entries, resolves the remainder to media items, +// and appends them to orderedItems until limit is reached, the source is +// exhausted, or continueProgressMaxScanned entries have been scanned. Paging +// past the requested limit matters because dismissal filtering and +// library/access scoping happen after the source query: trimming at the +// source LIMIT would under-fill the section whenever dismissals exist. +func (f *Fetcher) collectContinueProgressItems( + ctx context.Context, + store userstore.UserStore, + profileID string, + dismissals catalog.HomeDismissalIndex, + continueType ContinueType, + libraryID *int, + libraryIDs []int, + filter catalog.AccessFilter, + limit int, + orderedItems []*models.MediaItem, + itemMeta map[string]SectionItemMeta, + listPage func(pageLimit, offset int) ([]userstore.WatchProgress, error), +) ([]*models.MediaItem, error) { + // LIMIT/OFFSET pages over a live, updated_at-ordered source: a progress + // write between page fetches shifts rows across page boundaries, so the + // same content_id can be returned on two consecutive pages. Track what has + // already been appended so a card never appears twice in one section. + seen := make(map[string]struct{}, limit) + for _, item := range orderedItems { + seen[item.ContentID] = struct{}{} + } + for offset := 0; len(orderedItems) < limit && offset < continueProgressMaxScanned; offset += continueProgressPageSize { + pageLimit := continueProgressPageSize + if remaining := continueProgressMaxScanned - offset; remaining < pageLimit { + pageLimit = remaining + } + progressEntries, err := listPage(pageLimit, offset) + if err != nil { + return nil, err + } + if len(progressEntries) == 0 { + break + } + rawProgressCount := len(progressEntries) + progressEntries = dismissals.FilterProgress(progressEntries) + + pageItems, pageMeta, err := f.fetchContinueProgressItems(ctx, store, profileID, progressEntries, continueType, libraryID, libraryIDs, filter) + if err != nil { + return nil, err + } + orderedItems = appendUniqueContinueItems(orderedItems, pageItems, seen, limit) + maps.Copy(itemMeta, pageMeta) + if rawProgressCount < pageLimit { + break + } + } + return orderedItems, nil +} + +// appendUniqueContinueItems appends pageItems to orderedItems up to limit, +// skipping content IDs already recorded in seen and recording every appended +// ID. Pages come from a live LIMIT/OFFSET scan, so without this guard an item +// shifted across a page boundary by a concurrent progress write would surface +// twice in the same section. +func appendUniqueContinueItems(orderedItems, pageItems []*models.MediaItem, seen map[string]struct{}, limit int) []*models.MediaItem { + for _, item := range pageItems { + if len(orderedItems) >= limit { + break + } + if _, dup := seen[item.ContentID]; dup { + continue + } + seen[item.ContentID] = struct{}{} + orderedItems = append(orderedItems, item) + } + return orderedItems +} + +// ebookContinueWatchingProgressQuery lists in-progress ebook resume points for +// Continue Reading. Like the video path (pgstore ListProgress "in_progress"), +// rows the user hid via user_history_hidden_items are excluded while reading +// activity after hidden_before counts again. +var ebookContinueWatchingProgressQuery = fmt.Sprintf(` + SELECT content_id, progress::double precision, updated_at + FROM ebook_reader_progress + WHERE user_id = $1 + AND profile_id = $2 + AND progress > 0 + AND progress < %s + AND NOT EXISTS ( + SELECT 1 + FROM user_history_hidden_items hhi + WHERE hhi.user_id = ebook_reader_progress.user_id + AND hhi.profile_id = ebook_reader_progress.profile_id + AND hhi.media_item_id = ebook_reader_progress.content_id + AND ebook_reader_progress.updated_at <= hhi.hidden_before + ) + ORDER BY updated_at DESC, content_id ASC + LIMIT $3 OFFSET $4 +`, catalog.EbookFinishedProgressThresholdSQL) + +func (f *Fetcher) listEbookContinueWatchingProgress(ctx context.Context, userID int, profileID string, limit, offset int) ([]userstore.WatchProgress, error) { + if f.pool == nil || userID <= 0 || profileID == "" || limit <= 0 { + return nil, nil + } + + rows, err := f.pool.Query(ctx, ebookContinueWatchingProgressQuery, userID, profileID, limit, offset) + if err != nil { + return nil, err + } + defer rows.Close() + + entries := make([]userstore.WatchProgress, 0) + for rows.Next() { + var contentID string + var progress float64 + var updatedAt time.Time + if err := rows.Scan(&contentID, &progress, &updatedAt); err != nil { + return nil, err + } + entries = append(entries, userstore.WatchProgress{ + MediaItemID: contentID, + PositionSeconds: progress, + DurationSeconds: 1, + UpdatedAt: updatedAt.UTC().Format(time.RFC3339), + }) + } + if err := rows.Err(); err != nil { + return nil, err + } + return entries, nil +} + const ( continueProgressPageSize = 100 continueProgressMaxScanned = 1000 diff --git a/internal/sections/fetcher_continue_watching_test.go b/internal/sections/fetcher_continue_watching_test.go index cd8a217d..6efaeaf6 100644 --- a/internal/sections/fetcher_continue_watching_test.go +++ b/internal/sections/fetcher_continue_watching_test.go @@ -1,6 +1,7 @@ package sections import ( + "strings" "testing" "time" @@ -148,6 +149,76 @@ func TestParseContinueTypeRejectsUnknownExplicitType(t *testing.T) { } } +func TestEbookContinueWatchingProgressQueryGatesHiddenHistory(t *testing.T) { + t.Parallel() + + query := strings.Join(strings.Fields(ebookContinueWatchingProgressQuery), " ") + + // Continue Reading must honor user_history_hidden_items with the same + // semantics as the video path (pgstore ListProgress "in_progress"): a row + // is hidden while updated_at <= hidden_before and counts again once new + // reading activity lands after hidden_before. + for _, term := range []string{ + "FROM ebook_reader_progress", + "NOT EXISTS", + "FROM user_history_hidden_items hhi", + "hhi.media_item_id = ebook_reader_progress.content_id", + "ebook_reader_progress.updated_at <= hhi.hidden_before", + } { + if !strings.Contains(query, term) { + t.Fatalf("ebook continue watching query missing %q:\n%s", term, query) + } + } +} + +func TestAppendUniqueContinueItemsDedupesAcrossPages(t *testing.T) { + t.Parallel() + + seen := make(map[string]struct{}) + limit := 4 + + pageOne := []*models.MediaItem{ + {ContentID: "book-a"}, + {ContentID: "book-b"}, + } + ordered := appendUniqueContinueItems(nil, pageOne, seen, limit) + + // A progress write between pages shifts rows across the LIMIT/OFFSET + // boundary, so page two re-serves book-b. + pageTwo := []*models.MediaItem{ + {ContentID: "book-b"}, + {ContentID: "book-c"}, + } + ordered = appendUniqueContinueItems(ordered, pageTwo, seen, limit) + + got := contentIDs(ordered) + want := []string{"book-a", "book-b", "book-c"} + if len(got) != len(want) { + t.Fatalf("ordered IDs = %v, want %v", got, want) + } + for i := range want { + if got[i] != want[i] { + t.Fatalf("ordered IDs = %v, want %v", got, want) + } + } +} + +func TestAppendUniqueContinueItemsRespectsLimit(t *testing.T) { + t.Parallel() + + seen := make(map[string]struct{}) + page := []*models.MediaItem{ + {ContentID: "a"}, + {ContentID: "b"}, + {ContentID: "c"}, + } + + ordered := appendUniqueContinueItems(nil, page, seen, 2) + if got := contentIDs(ordered); len(got) != 2 || got[0] != "a" || got[1] != "b" { + t.Fatalf("ordered IDs = %v, want [a b]", got) + } +} + func contentIDs(items []*models.MediaItem) []string { ids := make([]string, 0, len(items)) for _, item := range items { diff --git a/internal/server/frontend.go b/internal/server/frontend.go index 21df49bb..0e937f1e 100644 --- a/internal/server/frontend.go +++ b/internal/server/frontend.go @@ -10,6 +10,43 @@ import ( // When nil, FrontendHandler returns a placeholder response. var WebDistFS fs.FS +// frontendContentSecurityPolicy is served with every SPA HTML response. +// +// SECURITY: this policy is the primary mitigation for malicious ebook content. +// The in-app reader (foliate-js) renders book chapters in same-origin blob: +// iframes with allow-scripts (required to work around a WebKit bug), and +// blob:/srcdoc documents inherit the embedding document's CSP. With +// script-src 'self', scripts embedded in an EPUB (blob:/inline/data: sources) +// cannot execute, so a hostile book cannot read localStorage tokens or call +// the API. Do not add 'unsafe-inline', 'unsafe-eval', blob:, or data: to +// script-src without revisiting that threat model. +// +// Allowances beyond 'self' exist for concrete app needs: +// - script-src 'wasm-unsafe-eval': JASSUB (libass) subtitle rendering and +// node-unrar-js CBR extraction compile WebAssembly. +// - style-src blob: and 'unsafe-inline': foliate-js loads EPUB stylesheets +// via blob: URLs; the app uses inline style attributes. Google Fonts CSS +// is linked from index.html. +// - img-src/media-src http(s): artwork can come from TMDB/TVDB/S3 public +// URLs, and stream URLs may point at standalone proxy/transcode workers +// on another origin (proxy public_url, plain http on LANs). +// - connect-src http(s)/ws(s): realtime session hub WebSockets, browser-side +// Plex auth (plex.tv), and HLS fetches against standalone worker origins. +// - font-src blob: data: plus fonts.gstatic.com for Google Fonts; reader +// book fonts load from blob: URLs. +const frontendContentSecurityPolicy = "default-src 'self'; " + + "script-src 'self' 'wasm-unsafe-eval'; " + + "style-src 'self' 'unsafe-inline' blob: https://fonts.googleapis.com; " + + "img-src 'self' blob: data: http: https:; " + + "font-src 'self' blob: data: https://fonts.gstatic.com; " + + "media-src 'self' blob: http: https:; " + + "connect-src 'self' ws: wss: http: https:; " + + "worker-src 'self' blob:; " + + "frame-src 'self' blob:; " + + "object-src 'none'; " + + "base-uri 'self'; " + + "form-action 'self'" + // FrontendHandler returns an http.Handler that serves the embedded SPA. // It serves static files from WebDistFS and falls back to index.html for // SPA routing (any path that doesn't match a file). @@ -17,6 +54,7 @@ func FrontendHandler() http.Handler { if WebDistFS == nil { return http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.Header().Set("Content-Type", "text/plain") + w.Header().Set("X-Content-Type-Options", "nosniff") w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte("Frontend not built. Run: cd web && bun run build")) }) @@ -25,10 +63,12 @@ func FrontendHandler() http.Handler { fileServer := http.FileServer(http.FS(WebDistFS)) return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("X-Content-Type-Options", "nosniff") path := r.URL.Path - // Try to serve the file directly - if path != "/" && !strings.HasSuffix(path, "/") { + // Try to serve the file directly. index.html is excluded so the SPA + // HTML always goes through the fallback below and carries the CSP. + if path != "/" && path != "/index.html" && !strings.HasSuffix(path, "/") { if f, err := WebDistFS.Open(strings.TrimPrefix(path, "/")); err == nil { f.Close() fileServer.ServeHTTP(w, r) @@ -43,6 +83,7 @@ func FrontendHandler() http.Handler { return } w.Header().Set("Content-Type", "text/html; charset=utf-8") + w.Header().Set("Content-Security-Policy", frontendContentSecurityPolicy) w.WriteHeader(http.StatusOK) _, _ = w.Write(indexBytes) }) diff --git a/internal/server/frontend_test.go b/internal/server/frontend_test.go new file mode 100644 index 00000000..fbb3570f --- /dev/null +++ b/internal/server/frontend_test.go @@ -0,0 +1,80 @@ +package server + +import ( + "net/http" + "net/http/httptest" + "strings" + "testing" + "testing/fstest" +) + +func TestFrontendHandlerSetsSecurityHeadersOnSPAHTML(t *testing.T) { + prev := WebDistFS + WebDistFS = fstest.MapFS{ + "index.html": &fstest.MapFile{Data: []byte("
")}, + "assets/app.js": &fstest.MapFile{ + Data: []byte("console.log(1)"), + }, + } + t.Cleanup(func() { WebDistFS = prev }) + + handler := FrontendHandler() + + for _, path := range []string{"/", "/index.html", "/library/ebooks"} { + t.Run(path, func(t *testing.T) { + rr := httptest.NewRecorder() + handler.ServeHTTP(rr, httptest.NewRequest(http.MethodGet, path, nil)) + + if rr.Code != http.StatusOK { + t.Fatalf("status = %d", rr.Code) + } + if got := rr.Header().Get("Content-Type"); got != "text/html; charset=utf-8" { + t.Fatalf("content-type = %q", got) + } + csp := rr.Header().Get("Content-Security-Policy") + if csp != frontendContentSecurityPolicy { + t.Fatalf("csp = %q", csp) + } + // The ebook-reader threat model depends on these directives; fail + // loudly if they are weakened. + for _, directive := range []string{ + "script-src 'self' 'wasm-unsafe-eval'", + "object-src 'none'", + "base-uri 'self'", + } { + if !strings.Contains(csp, directive) { + t.Fatalf("csp missing %q: %q", directive, csp) + } + } + if strings.Contains(csp, "script-src 'self' 'wasm-unsafe-eval' ") { + t.Fatalf("script-src must not carry extra sources: %q", csp) + } + if got := rr.Header().Get("X-Content-Type-Options"); got != "nosniff" { + t.Fatalf("x-content-type-options = %q", got) + } + }) + } +} + +func TestFrontendHandlerServesStaticAssetsWithoutCSP(t *testing.T) { + prev := WebDistFS + WebDistFS = fstest.MapFS{ + "index.html": &fstest.MapFile{Data: []byte("")}, + "assets/app.js": &fstest.MapFile{Data: []byte("console.log(1)")}, + } + t.Cleanup(func() { WebDistFS = prev }) + + handler := FrontendHandler() + rr := httptest.NewRecorder() + handler.ServeHTTP(rr, httptest.NewRequest(http.MethodGet, "/assets/app.js", nil)) + + if rr.Code != http.StatusOK { + t.Fatalf("status = %d", rr.Code) + } + if got := rr.Header().Get("Content-Security-Policy"); got != "" { + t.Fatalf("static asset should not carry the SPA CSP, got %q", got) + } + if got := rr.Header().Get("X-Content-Type-Options"); got != "nosniff" { + t.Fatalf("x-content-type-options = %q", got) + } +} diff --git a/internal/taskmanager/tasks/sync_ebook_metadata.go b/internal/taskmanager/tasks/sync_ebook_metadata.go new file mode 100644 index 00000000..8656ab7d --- /dev/null +++ b/internal/taskmanager/tasks/sync_ebook_metadata.go @@ -0,0 +1,56 @@ +package tasks + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/Silo-Server/silo-server/internal/taskmanager" +) + +type ebookMetadataEnricher interface { + Run(ctx context.Context) (int, error) +} + +// SyncEbookMetadataTask runs the periodic ebook enrichment sweep. +// It calls ebooks.Enricher.Run() which selects unenriched ebook media_items, +// resolves the per-folder metadata-provider chain at content_level='ebook', +// and writes results back to the database. +type SyncEbookMetadataTask struct { + enricher ebookMetadataEnricher +} + +// NewSyncEbookMetadataTask constructs the task. +func NewSyncEbookMetadataTask(enricher ebookMetadataEnricher) *SyncEbookMetadataTask { + return &SyncEbookMetadataTask{enricher: enricher} +} + +func (t *SyncEbookMetadataTask) Key() string { return "sync_ebook_metadata" } +func (t *SyncEbookMetadataTask) Name() string { return "Sync Ebook Metadata" } +func (t *SyncEbookMetadataTask) Description() string { + return "Fetches metadata (cover art, overview, authors) for ebooks that have not yet been enriched" +} +func (t *SyncEbookMetadataTask) Category() taskmanager.TaskCategory { + return taskmanager.TaskCategoryMetadata +} +func (t *SyncEbookMetadataTask) IsHidden() bool { return false } + +func (t *SyncEbookMetadataTask) DefaultTriggers() []taskmanager.TriggerConfig { + return []taskmanager.TriggerConfig{ + {Type: taskmanager.TriggerTypeInterval, IntervalMs: 5 * 60 * 1000}, + } +} + +func (t *SyncEbookMetadataTask) Execute(ctx context.Context, progress taskmanager.ProgressReporter) error { + progress.Report(0, "Scanning for unenriched ebooks") + + enriched, err := t.enricher.Run(ctx) + if err != nil { + return fmt.Errorf("ebook metadata sync: %w", err) + } + + result, _ := json.Marshal(map[string]int{"items_enriched": enriched}) + progress.SetResultData(result) + progress.Report(100, fmt.Sprintf("Ebook metadata sync complete (%d items enriched)", enriched)) + return nil +} diff --git a/internal/taskmanager/tasks/sync_ebook_metadata_test.go b/internal/taskmanager/tasks/sync_ebook_metadata_test.go new file mode 100644 index 00000000..0adb8c9a --- /dev/null +++ b/internal/taskmanager/tasks/sync_ebook_metadata_test.go @@ -0,0 +1,92 @@ +package tasks + +import ( + "context" + "encoding/json" + "errors" + "strings" + "testing" + + "github.com/Silo-Server/silo-server/internal/taskmanager" +) + +type fakeEbookMetadataEnricher struct { + enriched int + err error + called bool +} + +func (f *fakeEbookMetadataEnricher) Run(context.Context) (int, error) { + f.called = true + return f.enriched, f.err +} + +type ebookMetadataProgressReporter struct { + messages []string + result json.RawMessage +} + +func (p *ebookMetadataProgressReporter) Report(_ float64, message string) { + p.messages = append(p.messages, message) +} + +func (p *ebookMetadataProgressReporter) SetResultData(data json.RawMessage) { + p.result = data +} + +func TestSyncEbookMetadataTaskProperties(t *testing.T) { + task := NewSyncEbookMetadataTask(&fakeEbookMetadataEnricher{}) + + if task.Key() != "sync_ebook_metadata" { + t.Fatalf("Key() = %q, want sync_ebook_metadata", task.Key()) + } + if task.Name() != "Sync Ebook Metadata" { + t.Fatalf("Name() = %q, want Sync Ebook Metadata", task.Name()) + } + if task.Category() != taskmanager.TaskCategoryMetadata { + t.Fatalf("Category() = %q, want %q", task.Category(), taskmanager.TaskCategoryMetadata) + } + if task.IsHidden() { + t.Fatal("IsHidden() = true, want false") + } + triggers := task.DefaultTriggers() + if len(triggers) != 1 || triggers[0].Type != taskmanager.TriggerTypeInterval || triggers[0].IntervalMs != 5*60*1000 { + t.Fatalf("DefaultTriggers() = %#v, want one 5 minute interval", triggers) + } + if strings.Contains(strings.ToLower(task.Description()), "narrator") { + t.Fatalf("Description() mentions narrator: %q", task.Description()) + } +} + +func TestSyncEbookMetadataTaskExecuteReportsResult(t *testing.T) { + enricher := &fakeEbookMetadataEnricher{enriched: 3} + task := NewSyncEbookMetadataTask(enricher) + progress := &ebookMetadataProgressReporter{} + + if err := task.Execute(context.Background(), progress); err != nil { + t.Fatalf("Execute() error = %v", err) + } + if !enricher.called { + t.Fatal("enricher was not called") + } + var result map[string]int + if err := json.Unmarshal(progress.result, &result); err != nil { + t.Fatalf("result data JSON error: %v", err) + } + if result["items_enriched"] != 3 { + t.Fatalf("items_enriched = %d, want 3", result["items_enriched"]) + } + if len(progress.messages) == 0 || !strings.Contains(progress.messages[len(progress.messages)-1], "3 items enriched") { + t.Fatalf("progress messages = %#v, want completion count", progress.messages) + } +} + +func TestSyncEbookMetadataTaskExecuteWrapsError(t *testing.T) { + enricher := &fakeEbookMetadataEnricher{err: errors.New("boom")} + task := NewSyncEbookMetadataTask(enricher) + + err := task.Execute(context.Background(), &ebookMetadataProgressReporter{}) + if err == nil || !strings.Contains(err.Error(), "ebook metadata sync") { + t.Fatalf("Execute() error = %v, want wrapped ebook metadata sync error", err) + } +} diff --git a/migrations/sql/20260608000100_ebook_series.sql b/migrations/sql/20260608000100_ebook_series.sql new file mode 100644 index 00000000..461b680e --- /dev/null +++ b/migrations/sql/20260608000100_ebook_series.sql @@ -0,0 +1,17 @@ +-- +goose Up +-- +goose StatementBegin +CREATE TABLE ebook_series ( + content_id TEXT PRIMARY KEY REFERENCES media_items(content_id) ON DELETE CASCADE, + series_name TEXT NOT NULL, + series_index NUMERIC, + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +CREATE INDEX ebook_series_name_lower + ON ebook_series (LOWER(series_name), series_index NULLS LAST); +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +DROP TABLE IF EXISTS ebook_series; +-- +goose StatementEnd diff --git a/migrations/sql/20260608000200_ebook_reader_progress.sql b/migrations/sql/20260608000200_ebook_reader_progress.sql new file mode 100644 index 00000000..d89b68e5 --- /dev/null +++ b/migrations/sql/20260608000200_ebook_reader_progress.sql @@ -0,0 +1,22 @@ +-- +goose Up +-- +goose StatementBegin +CREATE TABLE ebook_reader_progress ( + user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, + profile_id TEXT NOT NULL, + content_id TEXT NOT NULL REFERENCES media_items(content_id) ON DELETE CASCADE, + file_id INTEGER NOT NULL REFERENCES media_files(id) ON DELETE CASCADE, + location TEXT NOT NULL, + progress DOUBLE PRECISION NOT NULL DEFAULT 0, + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + PRIMARY KEY (user_id, profile_id, content_id), + CHECK (progress >= 0 AND progress <= 1) +); + +CREATE INDEX ebook_reader_progress_profile_updated + ON ebook_reader_progress (user_id, profile_id, updated_at DESC); +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +DROP TABLE IF EXISTS ebook_reader_progress; +-- +goose StatementEnd diff --git a/migrations/sql/20260608000300_ebook_reader_state.sql b/migrations/sql/20260608000300_ebook_reader_state.sql new file mode 100644 index 00000000..a44ff0b2 --- /dev/null +++ b/migrations/sql/20260608000300_ebook_reader_state.sql @@ -0,0 +1,41 @@ +-- +goose Up +-- +goose StatementBegin +CREATE TABLE ebook_reader_config ( + user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, + profile_id TEXT NOT NULL, + content_id TEXT NOT NULL REFERENCES media_items(content_id) ON DELETE CASCADE, + config JSONB NOT NULL DEFAULT '{}'::jsonb, + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + PRIMARY KEY (user_id, profile_id, content_id) +); + +CREATE INDEX ebook_reader_config_profile_updated + ON ebook_reader_config (user_id, profile_id, updated_at DESC); + +CREATE TABLE ebook_reader_annotations ( + id TEXT PRIMARY KEY, + user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, + profile_id TEXT NOT NULL, + content_id TEXT NOT NULL REFERENCES media_items(content_id) ON DELETE CASCADE, + kind TEXT NOT NULL CHECK (kind IN ('highlight', 'note', 'bookmark')), + cfi_range TEXT, + location TEXT, + selected_text TEXT NOT NULL DEFAULT '', + note TEXT NOT NULL DEFAULT '', + style TEXT NOT NULL DEFAULT 'highlight', + color TEXT NOT NULL DEFAULT '#facc15', + metadata JSONB NOT NULL DEFAULT '{}'::jsonb, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + CHECK ((kind = 'bookmark' AND location IS NOT NULL) OR (kind <> 'bookmark' AND cfi_range IS NOT NULL)) +); + +CREATE INDEX ebook_reader_annotations_book + ON ebook_reader_annotations (user_id, profile_id, content_id, updated_at DESC); +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +DROP TABLE IF EXISTS ebook_reader_annotations; +DROP TABLE IF EXISTS ebook_reader_config; +-- +goose StatementEnd diff --git a/migrations/sql/20260610071424_add_ebook_reader_fk_indexes.sql b/migrations/sql/20260610071424_add_ebook_reader_fk_indexes.sql new file mode 100644 index 00000000..23a17218 --- /dev/null +++ b/migrations/sql/20260610071424_add_ebook_reader_fk_indexes.sql @@ -0,0 +1,26 @@ +-- +goose Up +-- +goose StatementBegin +-- The ebook reader tables carry ON DELETE CASCADE foreign keys on content_id +-- (and file_id for progress), but every existing index leads with user_id, so +-- media_items/media_files deletes had to seq-scan them. Plain btree indexes on +-- the FK columns let those cascades use index scans. +CREATE INDEX IF NOT EXISTS ebook_reader_progress_content_id + ON ebook_reader_progress (content_id); + +CREATE INDEX IF NOT EXISTS ebook_reader_progress_file_id + ON ebook_reader_progress (file_id); + +CREATE INDEX IF NOT EXISTS ebook_reader_config_content_id + ON ebook_reader_config (content_id); + +CREATE INDEX IF NOT EXISTS ebook_reader_annotations_content_id + ON ebook_reader_annotations (content_id); +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +DROP INDEX IF EXISTS ebook_reader_annotations_content_id; +DROP INDEX IF EXISTS ebook_reader_config_content_id; +DROP INDEX IF EXISTS ebook_reader_progress_file_id; +DROP INDEX IF EXISTS ebook_reader_progress_content_id; +-- +goose StatementEnd diff --git a/migrations/sql/20260610080326_backfill_ebook_item_status.sql b/migrations/sql/20260610080326_backfill_ebook_item_status.sql new file mode 100644 index 00000000..9fd3ef18 --- /dev/null +++ b/migrations/sql/20260610080326_backfill_ebook_item_status.sql @@ -0,0 +1,18 @@ +-- +goose Up +-- Ebook items created by the scanner before status handling was fixed were +-- written with status = '' (the upsert bypasses the column default), which +-- disabled the pending -> matched promotion in enrichment and the curated- +-- metadata guard. Rows enrichment already processed become 'matched'; the +-- rest become 'pending' so the next enrichment sweep promotes them normally. +UPDATE media_items +SET status = CASE + WHEN matched_at IS NOT NULL OR last_refreshed IS NOT NULL THEN 'matched' + ELSE 'pending' + END +WHERE type = 'ebook' + AND status = ''; + +-- +goose Down +-- Irreversible data backfill: the pre-migration status ('') carried no +-- information, so there is nothing meaningful to restore. +SELECT 1; diff --git a/migrations/sql/20260610080327_ebook_enrichment_state.sql b/migrations/sql/20260610080327_ebook_enrichment_state.sql new file mode 100644 index 00000000..8f8b1aa3 --- /dev/null +++ b/migrations/sql/20260610080327_ebook_enrichment_state.sql @@ -0,0 +1,13 @@ +-- +goose Up +-- Dedicated failure accounting for the ebook enrichment sweep. Enrichment +-- previously incremented media_items.refresh_failures, which the metadata +-- refresh-debt system also owns (increments on refresh failure, resets on +-- success, reads for debt scoring); the two systems fought over one counter. +CREATE TABLE ebook_enrichment_state ( + content_id text PRIMARY KEY REFERENCES media_items(content_id) ON DELETE CASCADE, + failures integer NOT NULL DEFAULT 0, + updated_at timestamptz NOT NULL DEFAULT now() +); + +-- +goose Down +DROP TABLE ebook_enrichment_state; diff --git a/web/package.json b/web/package.json index d450f60c..c811d9e0 100644 --- a/web/package.json +++ b/web/package.json @@ -23,15 +23,19 @@ "@fontsource/noto-sans-thai": "^5.2.8", "@tanstack/react-query": "^5.90.21", "@tanstack/react-virtual": "^3.13.19", + "@zip.js/zip.js": "^2.8.26", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "embla-carousel": "^8.6.0", "embla-carousel-react": "^8.6.0", "embla-carousel-wheel-gestures": "^8.1.0", + "foliate-js": "file:vendor/foliate-js", "framer-motion": "^12.38.0", "hls.js": "^1.6.15", "jassub": "^2.4.2", "lucide-react": "^0.576.0", + "node-unrar-js": "^2.0.2", + "pdfjs-dist": "^5.7.284", "radix-ui": "^1.4.3", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index a6af9a64..563158f1 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -37,6 +37,9 @@ importers: '@tanstack/react-virtual': specifier: ^3.13.19 version: 3.13.22(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@zip.js/zip.js': + specifier: ^2.8.26 + version: 2.8.26 class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -52,6 +55,9 @@ importers: embla-carousel-wheel-gestures: specifier: ^8.1.0 version: 8.1.0(embla-carousel@8.6.0) + foliate-js: + specifier: file:vendor/foliate-js + version: file:vendor/foliate-js framer-motion: specifier: ^12.38.0 version: 12.38.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -64,6 +70,12 @@ importers: lucide-react: specifier: ^0.576.0 version: 0.576.0(react@19.2.4) + node-unrar-js: + specifier: ^2.0.2 + version: 2.0.2 + pdfjs-dist: + specifier: ^5.7.284 + version: 5.7.284 radix-ui: specifier: ^1.4.3 version: 1.4.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -586,6 +598,81 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@napi-rs/canvas-android-arm64@0.1.100': + resolution: {integrity: sha512-hjhCKhntPv9+t4ckHymdx0phYNcVW+GKQR6Lzw2zE+pOVjOplSmtx9nNNknTjbEDLcuLZqA1y8ufKg1XfgftzQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@napi-rs/canvas-darwin-arm64@0.1.100': + resolution: {integrity: sha512-2PcswRaC7Ly645DGt88///zuFDhJxJYdKAs1uU3mfk1atYkXufgcgLfBpk6Tm12nCQBaNt1wpybuPZ4qOhTo8A==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@napi-rs/canvas-darwin-x64@0.1.100': + resolution: {integrity: sha512-ePNZtj7pNIva/siZMg+HmbeozkIjqUIYdoymH8HaA3qK7LfzFN4WMBM8G6HQ9ZC+H3+Dnn5pqtiXpgLykaPOhw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@napi-rs/canvas-linux-arm-gnueabihf@0.1.100': + resolution: {integrity: sha512-d5cDB48oWFGU8/XPhUOFAlySgb/VAu7D+s8fi55K1Pcfg8aPplHWqMgibhVLU8ky7Pyg/fuiVLz4Nf3JrSTuUA==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@napi-rs/canvas-linux-arm64-gnu@0.1.100': + resolution: {integrity: sha512-rDxgxRu69RvDlX/bh9o22DxLsGr8EqsNgotL9+RwQE1S0b0cqeatqsw6aW45mukm0B42DIAaAacKaYQ8cqS1nw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@napi-rs/canvas-linux-arm64-musl@0.1.100': + resolution: {integrity: sha512-K3mDW66N+xT2/V439u1alFANiBUjdEx2gLiNYnCmUsva5jZMxWTjafBYwTzYK+EMFMHrUoabuU+T1BIP5CgbYQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@napi-rs/canvas-linux-riscv64-gnu@0.1.100': + resolution: {integrity: sha512-mooqUBTIsccZpnoQC4NgrC1v6C1vof39etLNMnBwCY+p0gajWJvAHLGQ6g/gGyS5YrpDW+GefSN4+Cvcr08UWw==} + engines: {node: '>= 10'} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@napi-rs/canvas-linux-x64-gnu@0.1.100': + resolution: {integrity: sha512-1eCvkDCazm7FFhsT7DfGOdSaHgZVK3bt/dSBl5EWHOWmnz+I7j8tPseJqqD81NF+MH21jKUK4wQSDjN0mdhnTg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@napi-rs/canvas-linux-x64-musl@0.1.100': + resolution: {integrity: sha512-20arT6lnI19S68qNlii73TSEDbECNgzMz2EpldC1V3mZFuRkeujXkcebRk0LRJe9SEUAooYiLokfMViY8IX7yA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@napi-rs/canvas-win32-arm64-msvc@0.1.100': + resolution: {integrity: sha512-DZFFT1wIAg37LJw37yhMRFfjATd3vTQzjZ1Yki8u2vhO6Hi5VE6BVaGQ1aaDu7xb4iMErz+9EOwjpS7xcxFeBw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@napi-rs/canvas-win32-x64-msvc@0.1.100': + resolution: {integrity: sha512-MyT1j3mHC2+Lu4pBi9mKyMJhtP6U7k7EldY7sj/uS5gJA65gTXt8MefJQXLJo5d/vZbuWmfxzkEUNc/urV3pHA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@napi-rs/canvas@0.1.100': + resolution: {integrity: sha512-xglYA6q3XO5P3BNJYxVZ1IV7DLVjp1Py6nwag88YntrS+3vKHyYcMqXVS4ZztJmwz2uGvz1FWhI/4LgbR5uQDA==} + engines: {node: '>= 10'} + '@radix-ui/number@1.1.1': resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==} @@ -1695,6 +1782,10 @@ packages: '@vitest/utils@4.1.0': resolution: {integrity: sha512-XfPXT6a8TZY3dcGY8EdwsBulFCIw+BeeX0RZn2x/BtiY/75YGh8FeWGG8QISN/WhaqSrE2OrlDgtF8q5uhOTmw==} + '@zip.js/zip.js@2.8.26': + resolution: {integrity: sha512-RQ4h9F6DOiHxpdocUDrOl6xBM+yOtz+LkUol47AVWcfebGBDpZ7w7Xvz9PS24JgXvLGiXXzSAfdCdVy1tPlaFA==} + engines: {bun: '>=0.7.0', deno: '>=1.0.0', node: '>=18.0.0'} + abslink@1.1.6: resolution: {integrity: sha512-8fQgnUoVSgc1IhOrYzdDY+wTDPktbuYjds2LKf9kWYWKwDnHgXU168gdV3sPei7vevFf5m2fPZ6IQSKZkSjHVg==} @@ -1800,6 +1891,9 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + construct-style-sheets-polyfill@3.1.0: + resolution: {integrity: sha512-HBLKP0chz8BAY6rBdzda11c3wAZeCZ+kIG4weVC2NM3AXzxx09nhe8t0SQNdloAvg5GLuHwq/0SPOOSPvtCcKw==} + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -2006,6 +2100,9 @@ packages: flatted@3.4.2: resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} + foliate-js@file:vendor/foliate-js: + resolution: {directory: vendor/foliate-js, type: directory} + framer-motion@12.38.0: resolution: {integrity: sha512-rFYkY/pigbcswl1XQSb7q424kSTQ8q6eAC+YUsSKooHQYuLdzdHjrt6uxUC+PRAO++q5IS7+TamgIw1AphxR+g==} peerDependencies: @@ -2359,6 +2456,10 @@ packages: node-releases@2.0.36: resolution: {integrity: sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==} + node-unrar-js@2.0.2: + resolution: {integrity: sha512-hLNmoJzqaKJnod8yiTVGe9hnlNRHotUi0CreSv/8HtfRi/3JnRC8DvsmKfeGGguRjTEulhZK6zXX5PXoVuDZ2w==} + engines: {node: '>=10.0.0'} + object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -2396,6 +2497,10 @@ packages: pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + pdfjs-dist@5.7.284: + resolution: {integrity: sha512-h4EdYQczmGhbOlqc3PPZwxevn7ApdWPbovAuWXOB/DjIyigSnwfy2oze7c6mRcSr9XgLp3eN3EeL4DyySTPMFw==} + engines: {node: '>=22.13.0 || >=24'} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -3271,6 +3376,54 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@napi-rs/canvas-android-arm64@0.1.100': + optional: true + + '@napi-rs/canvas-darwin-arm64@0.1.100': + optional: true + + '@napi-rs/canvas-darwin-x64@0.1.100': + optional: true + + '@napi-rs/canvas-linux-arm-gnueabihf@0.1.100': + optional: true + + '@napi-rs/canvas-linux-arm64-gnu@0.1.100': + optional: true + + '@napi-rs/canvas-linux-arm64-musl@0.1.100': + optional: true + + '@napi-rs/canvas-linux-riscv64-gnu@0.1.100': + optional: true + + '@napi-rs/canvas-linux-x64-gnu@0.1.100': + optional: true + + '@napi-rs/canvas-linux-x64-musl@0.1.100': + optional: true + + '@napi-rs/canvas-win32-arm64-msvc@0.1.100': + optional: true + + '@napi-rs/canvas-win32-x64-msvc@0.1.100': + optional: true + + '@napi-rs/canvas@0.1.100': + optionalDependencies: + '@napi-rs/canvas-android-arm64': 0.1.100 + '@napi-rs/canvas-darwin-arm64': 0.1.100 + '@napi-rs/canvas-darwin-x64': 0.1.100 + '@napi-rs/canvas-linux-arm-gnueabihf': 0.1.100 + '@napi-rs/canvas-linux-arm64-gnu': 0.1.100 + '@napi-rs/canvas-linux-arm64-musl': 0.1.100 + '@napi-rs/canvas-linux-riscv64-gnu': 0.1.100 + '@napi-rs/canvas-linux-x64-gnu': 0.1.100 + '@napi-rs/canvas-linux-x64-musl': 0.1.100 + '@napi-rs/canvas-win32-arm64-msvc': 0.1.100 + '@napi-rs/canvas-win32-x64-msvc': 0.1.100 + optional: true + '@radix-ui/number@1.1.1': {} '@radix-ui/primitive@1.1.3': {} @@ -4406,6 +4559,8 @@ snapshots: convert-source-map: 2.0.0 tinyrainbow: 3.1.0 + '@zip.js/zip.js@2.8.26': {} + abslink@1.1.6: {} acorn-jsx@5.3.2(acorn@8.16.0): @@ -4495,6 +4650,8 @@ snapshots: concat-map@0.0.1: {} + construct-style-sheets-polyfill@3.1.0: {} + convert-source-map@2.0.0: {} cookie@1.1.1: {} @@ -4721,6 +4878,10 @@ snapshots: flatted@3.4.2: {} + foliate-js@file:vendor/foliate-js: + dependencies: + construct-style-sheets-polyfill: 3.1.0 + framer-motion@12.38.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4): dependencies: motion-dom: 12.38.0 @@ -4997,6 +5158,8 @@ snapshots: node-releases@2.0.36: {} + node-unrar-js@2.0.2: {} + object-assign@4.1.1: {} obug@2.1.1: {} @@ -5032,6 +5195,10 @@ snapshots: pathe@2.0.3: {} + pdfjs-dist@5.7.284: + optionalDependencies: + '@napi-rs/canvas': 0.1.100 + picocolors@1.1.1: {} picomatch@4.0.4: {} diff --git a/web/public/vendor/pdfjs/annotation_layer_builder.css b/web/public/vendor/pdfjs/annotation_layer_builder.css new file mode 100644 index 00000000..c1c2a8d5 --- /dev/null +++ b/web/public/vendor/pdfjs/annotation_layer_builder.css @@ -0,0 +1,407 @@ +/* Copyright 2014 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +.annotationLayer { + color-scheme: only light; + + --annotation-unfocused-field-background: url("data:image/svg+xml;charset=UTF-8,"); + --input-focus-border-color: Highlight; + --input-focus-outline: 1px solid Canvas; + --input-unfocused-border-color: transparent; + --input-disabled-border-color: transparent; + --input-hover-border-color: black; + --link-outline: none; + + @media screen and (forced-colors: active) { + --input-focus-border-color: CanvasText; + --input-unfocused-border-color: ActiveText; + --input-disabled-border-color: GrayText; + --input-hover-border-color: Highlight; + --link-outline: 1.5px solid LinkText; + + .textWidgetAnnotation :is(input, textarea):required, + .choiceWidgetAnnotation select:required, + .buttonWidgetAnnotation:is(.checkBox, .radioButton) input:required { + outline: 1.5px solid selectedItem; + } + + .linkAnnotation { + outline: var(--link-outline); + + &:hover { + backdrop-filter: var(--hcm-highlight-filter); + } + + & > a:hover { + opacity: 0 !important; + background: none !important; + box-shadow: none; + } + } + + .popupAnnotation .popup { + outline: calc(1.5px * var(--total-scale-factor)) solid CanvasText !important; + background-color: ButtonFace !important; + color: ButtonText !important; + } + + .highlightArea:hover::after { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + backdrop-filter: var(--hcm-highlight-filter); + content: ""; + pointer-events: none; + } + + .popupAnnotation.focused .popup { + outline: calc(3px * var(--total-scale-factor)) solid Highlight !important; + } + } + + position: absolute; + top: 0; + left: 0; + pointer-events: none; + transform-origin: 0 0; + + &[data-main-rotation="90"] .norotate { + transform: rotate(270deg) translateX(-100%); + } + &[data-main-rotation="180"] .norotate { + transform: rotate(180deg) translate(-100%, -100%); + } + &[data-main-rotation="270"] .norotate { + transform: rotate(90deg) translateY(-100%); + } + + &.disabled { + section, + .popup { + pointer-events: none; + } + } + + .annotationContent { + position: absolute; + width: 100%; + height: 100%; + pointer-events: none; + + &.freetext { + background: transparent; + border: none; + inset: 0; + overflow: visible; + white-space: nowrap; + font: 10px sans-serif; + line-height: 1.35; + } + } + + section { + position: absolute; + text-align: initial; + pointer-events: auto; + box-sizing: border-box; + transform-origin: 0 0; + user-select: none; + + &:has(div.annotationContent) { + canvas.annotationContent { + display: none; + } + } + + .overlaidText { + position: absolute; + top: 0; + left: 0; + width: 0; + height: 0; + display: inline-block; + overflow: hidden; + } + } + + .textLayer.selecting ~ & section { + pointer-events: none; + } + + :is(.linkAnnotation, .buttonWidgetAnnotation.pushButton) > a { + position: absolute; + font-size: 1em; + top: 0; + left: 0; + width: 100%; + height: 100%; + } + + :is(.linkAnnotation, .buttonWidgetAnnotation.pushButton):not(.hasBorder) + > a:hover { + opacity: 0.2; + background-color: rgb(255 255 0); + } + + .linkAnnotation.hasBorder:hover { + background-color: rgb(255 255 0 / 0.2); + } + + .hasBorder { + background-size: 100% 100%; + } + + .textAnnotation img { + position: absolute; + cursor: pointer; + width: 100%; + height: 100%; + top: 0; + left: 0; + } + + .textWidgetAnnotation :is(input, textarea), + .choiceWidgetAnnotation select, + .buttonWidgetAnnotation:is(.checkBox, .radioButton) input { + background-image: var(--annotation-unfocused-field-background); + border: 2px solid var(--input-unfocused-border-color); + box-sizing: border-box; + font: calc(9px * var(--total-scale-factor)) sans-serif; + height: 100%; + margin: 0; + vertical-align: top; + width: 100%; + } + + .textWidgetAnnotation :is(input, textarea):required, + .choiceWidgetAnnotation select:required, + .buttonWidgetAnnotation:is(.checkBox, .radioButton) input:required { + outline: 1.5px solid red; + } + + .choiceWidgetAnnotation select option { + padding: 0; + } + + .buttonWidgetAnnotation.radioButton input { + border-radius: 50%; + } + + .textWidgetAnnotation textarea { + resize: none; + } + + .textWidgetAnnotation :is(input, textarea)[disabled], + .choiceWidgetAnnotation select[disabled], + .buttonWidgetAnnotation:is(.checkBox, .radioButton) input[disabled] { + background: none; + border: 2px solid var(--input-disabled-border-color); + cursor: not-allowed; + } + + .textWidgetAnnotation :is(input, textarea):hover, + .choiceWidgetAnnotation select:hover, + .buttonWidgetAnnotation:is(.checkBox, .radioButton) input:hover { + border: 2px solid var(--input-hover-border-color); + } + .textWidgetAnnotation :is(input, textarea):hover, + .choiceWidgetAnnotation select:hover, + .buttonWidgetAnnotation.checkBox input:hover { + border-radius: 2px; + } + + .textWidgetAnnotation :is(input, textarea):focus, + .choiceWidgetAnnotation select:focus { + background: none; + border: 2px solid var(--input-focus-border-color); + border-radius: 2px; + outline: var(--input-focus-outline); + } + + .buttonWidgetAnnotation:is(.checkBox, .radioButton) :focus { + background-image: none; + background-color: transparent; + } + + .buttonWidgetAnnotation.checkBox :focus { + border: 2px solid var(--input-focus-border-color); + border-radius: 2px; + outline: var(--input-focus-outline); + } + + .buttonWidgetAnnotation.radioButton :focus { + border: 2px solid var(--input-focus-border-color); + outline: var(--input-focus-outline); + } + + .buttonWidgetAnnotation.checkBox input:checked::before, + .buttonWidgetAnnotation.checkBox input:checked::after, + .buttonWidgetAnnotation.radioButton input:checked::before { + background-color: CanvasText; + content: ""; + display: block; + position: absolute; + } + + .buttonWidgetAnnotation.checkBox input:checked::before, + .buttonWidgetAnnotation.checkBox input:checked::after { + height: 80%; + left: 45%; + width: 1px; + } + + .buttonWidgetAnnotation.checkBox input:checked::before { + transform: rotate(45deg); + } + + .buttonWidgetAnnotation.checkBox input:checked::after { + transform: rotate(-45deg); + } + + .buttonWidgetAnnotation.radioButton input:checked::before { + border-radius: 50%; + height: 50%; + left: 25%; + top: 25%; + width: 50%; + } + + .textWidgetAnnotation input.comb { + font-family: monospace; + padding-left: 2px; + padding-right: 0; + } + + .textWidgetAnnotation input.comb:focus { + /* + * Letter spacing is placed on the right side of each character. Hence, the + * letter spacing of the last character may be placed outside the visible + * area, causing horizontal scrolling. We avoid this by extending the width + * when the element has focus and revert this when it loses focus. + */ + width: 103%; + } + + .buttonWidgetAnnotation:is(.checkBox, .radioButton) input { + appearance: none; + } + + .fileAttachmentAnnotation .popupTriggerArea { + height: 100%; + width: 100%; + } + + .popupAnnotation { + position: absolute; + font-size: calc(9px * var(--total-scale-factor)); + pointer-events: none; + width: max-content; + max-width: 45%; + height: auto; + } + + .popup { + background-color: rgb(255 255 153); + color: black; + box-shadow: 0 calc(2px * var(--total-scale-factor)) + calc(5px * var(--total-scale-factor)) rgb(136 136 136); + border-radius: calc(2px * var(--total-scale-factor)); + outline: 1.5px solid rgb(255 255 74); + padding: calc(6px * var(--total-scale-factor)); + cursor: pointer; + font: message-box; + white-space: normal; + word-wrap: break-word; + pointer-events: auto; + user-select: text; + } + + .popupAnnotation.focused .popup { + outline-width: 3px; + } + + .popup * { + font-size: calc(9px * var(--total-scale-factor)); + } + + .popup > .header { + display: inline-block; + } + + .popup > .header > .title { + display: inline; + font-weight: bold; + } + + .popup > .header .popupDate { + display: inline-block; + margin-left: calc(5px * var(--total-scale-factor)); + width: fit-content; + } + + .popupContent { + border-top: 1px solid rgb(51 51 51); + margin-top: calc(2px * var(--total-scale-factor)); + padding-top: calc(2px * var(--total-scale-factor)); + } + + .richText > * { + white-space: pre-wrap; + font-size: calc(9px * var(--total-scale-factor)); + } + + .popupTriggerArea { + cursor: pointer; + + &:hover { + backdrop-filter: var(--hcm-highlight-filter); + } + } + + section svg { + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; + } + + .annotationTextContent { + position: absolute; + width: 100%; + height: 100%; + opacity: 0; + color: transparent; + user-select: none; + pointer-events: none; + + span { + width: 100%; + display: inline-block; + } + } + + svg.quadrilateralsContainer { + contain: strict; + width: 0; + height: 0; + position: absolute; + top: 0; + left: 0; + z-index: -1; + } +} diff --git a/web/public/vendor/pdfjs/cmaps/78-EUC-H.bcmap b/web/public/vendor/pdfjs/cmaps/78-EUC-H.bcmap new file mode 100644 index 00000000..2655fc70 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/78-EUC-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/78-EUC-V.bcmap b/web/public/vendor/pdfjs/cmaps/78-EUC-V.bcmap new file mode 100644 index 00000000..f1ed8538 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/78-EUC-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/78-H.bcmap b/web/public/vendor/pdfjs/cmaps/78-H.bcmap new file mode 100644 index 00000000..39e89d33 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/78-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/78-RKSJ-H.bcmap b/web/public/vendor/pdfjs/cmaps/78-RKSJ-H.bcmap new file mode 100644 index 00000000..e4167cb5 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/78-RKSJ-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/78-RKSJ-V.bcmap b/web/public/vendor/pdfjs/cmaps/78-RKSJ-V.bcmap new file mode 100644 index 00000000..50b1646e Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/78-RKSJ-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/78-V.bcmap b/web/public/vendor/pdfjs/cmaps/78-V.bcmap new file mode 100644 index 00000000..d7af99b5 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/78-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/78ms-RKSJ-H.bcmap b/web/public/vendor/pdfjs/cmaps/78ms-RKSJ-H.bcmap new file mode 100644 index 00000000..37077d01 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/78ms-RKSJ-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/78ms-RKSJ-V.bcmap b/web/public/vendor/pdfjs/cmaps/78ms-RKSJ-V.bcmap new file mode 100644 index 00000000..acf23231 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/78ms-RKSJ-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/83pv-RKSJ-H.bcmap b/web/public/vendor/pdfjs/cmaps/83pv-RKSJ-H.bcmap new file mode 100644 index 00000000..2359bc52 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/83pv-RKSJ-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/90ms-RKSJ-H.bcmap b/web/public/vendor/pdfjs/cmaps/90ms-RKSJ-H.bcmap new file mode 100644 index 00000000..af829382 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/90ms-RKSJ-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/90ms-RKSJ-V.bcmap b/web/public/vendor/pdfjs/cmaps/90ms-RKSJ-V.bcmap new file mode 100644 index 00000000..780549de Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/90ms-RKSJ-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/90msp-RKSJ-H.bcmap b/web/public/vendor/pdfjs/cmaps/90msp-RKSJ-H.bcmap new file mode 100644 index 00000000..bfd3119c Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/90msp-RKSJ-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/90msp-RKSJ-V.bcmap b/web/public/vendor/pdfjs/cmaps/90msp-RKSJ-V.bcmap new file mode 100644 index 00000000..25ef14ab Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/90msp-RKSJ-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/90pv-RKSJ-H.bcmap b/web/public/vendor/pdfjs/cmaps/90pv-RKSJ-H.bcmap new file mode 100644 index 00000000..02f713bb Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/90pv-RKSJ-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/90pv-RKSJ-V.bcmap b/web/public/vendor/pdfjs/cmaps/90pv-RKSJ-V.bcmap new file mode 100644 index 00000000..d08e0cc5 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/90pv-RKSJ-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/Add-H.bcmap b/web/public/vendor/pdfjs/cmaps/Add-H.bcmap new file mode 100644 index 00000000..59442aca Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/Add-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/Add-RKSJ-H.bcmap b/web/public/vendor/pdfjs/cmaps/Add-RKSJ-H.bcmap new file mode 100644 index 00000000..a3065e44 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/Add-RKSJ-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/Add-RKSJ-V.bcmap b/web/public/vendor/pdfjs/cmaps/Add-RKSJ-V.bcmap new file mode 100644 index 00000000..040014cf Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/Add-RKSJ-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/Add-V.bcmap b/web/public/vendor/pdfjs/cmaps/Add-V.bcmap new file mode 100644 index 00000000..2f816d32 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/Add-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/Adobe-CNS1-0.bcmap b/web/public/vendor/pdfjs/cmaps/Adobe-CNS1-0.bcmap new file mode 100644 index 00000000..88ec04af Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/Adobe-CNS1-0.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/Adobe-CNS1-1.bcmap b/web/public/vendor/pdfjs/cmaps/Adobe-CNS1-1.bcmap new file mode 100644 index 00000000..03a50147 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/Adobe-CNS1-1.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/Adobe-CNS1-2.bcmap b/web/public/vendor/pdfjs/cmaps/Adobe-CNS1-2.bcmap new file mode 100644 index 00000000..2aa95141 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/Adobe-CNS1-2.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/Adobe-CNS1-3.bcmap b/web/public/vendor/pdfjs/cmaps/Adobe-CNS1-3.bcmap new file mode 100644 index 00000000..86d8b8c7 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/Adobe-CNS1-3.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/Adobe-CNS1-4.bcmap b/web/public/vendor/pdfjs/cmaps/Adobe-CNS1-4.bcmap new file mode 100644 index 00000000..f50fc6c1 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/Adobe-CNS1-4.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/Adobe-CNS1-5.bcmap b/web/public/vendor/pdfjs/cmaps/Adobe-CNS1-5.bcmap new file mode 100644 index 00000000..6caf4a83 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/Adobe-CNS1-5.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/Adobe-CNS1-6.bcmap b/web/public/vendor/pdfjs/cmaps/Adobe-CNS1-6.bcmap new file mode 100644 index 00000000..b77fb070 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/Adobe-CNS1-6.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/Adobe-CNS1-UCS2.bcmap b/web/public/vendor/pdfjs/cmaps/Adobe-CNS1-UCS2.bcmap new file mode 100644 index 00000000..69d79a2c Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/Adobe-CNS1-UCS2.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/Adobe-GB1-0.bcmap b/web/public/vendor/pdfjs/cmaps/Adobe-GB1-0.bcmap new file mode 100644 index 00000000..36101083 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/Adobe-GB1-0.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/Adobe-GB1-1.bcmap b/web/public/vendor/pdfjs/cmaps/Adobe-GB1-1.bcmap new file mode 100644 index 00000000..707bb106 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/Adobe-GB1-1.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/Adobe-GB1-2.bcmap b/web/public/vendor/pdfjs/cmaps/Adobe-GB1-2.bcmap new file mode 100644 index 00000000..f7648cc3 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/Adobe-GB1-2.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/Adobe-GB1-3.bcmap b/web/public/vendor/pdfjs/cmaps/Adobe-GB1-3.bcmap new file mode 100644 index 00000000..85214589 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/Adobe-GB1-3.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/Adobe-GB1-4.bcmap b/web/public/vendor/pdfjs/cmaps/Adobe-GB1-4.bcmap new file mode 100644 index 00000000..e40c63ab Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/Adobe-GB1-4.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/Adobe-GB1-5.bcmap b/web/public/vendor/pdfjs/cmaps/Adobe-GB1-5.bcmap new file mode 100644 index 00000000..d7623b50 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/Adobe-GB1-5.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/Adobe-GB1-UCS2.bcmap b/web/public/vendor/pdfjs/cmaps/Adobe-GB1-UCS2.bcmap new file mode 100644 index 00000000..75865259 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/Adobe-GB1-UCS2.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/Adobe-Japan1-0.bcmap b/web/public/vendor/pdfjs/cmaps/Adobe-Japan1-0.bcmap new file mode 100644 index 00000000..f0e94ec1 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/Adobe-Japan1-0.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/Adobe-Japan1-1.bcmap b/web/public/vendor/pdfjs/cmaps/Adobe-Japan1-1.bcmap new file mode 100644 index 00000000..dad42c5a Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/Adobe-Japan1-1.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/Adobe-Japan1-2.bcmap b/web/public/vendor/pdfjs/cmaps/Adobe-Japan1-2.bcmap new file mode 100644 index 00000000..090819a0 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/Adobe-Japan1-2.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/Adobe-Japan1-3.bcmap b/web/public/vendor/pdfjs/cmaps/Adobe-Japan1-3.bcmap new file mode 100644 index 00000000..087dfc15 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/Adobe-Japan1-3.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/Adobe-Japan1-4.bcmap b/web/public/vendor/pdfjs/cmaps/Adobe-Japan1-4.bcmap new file mode 100644 index 00000000..46aa9bff Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/Adobe-Japan1-4.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/Adobe-Japan1-5.bcmap b/web/public/vendor/pdfjs/cmaps/Adobe-Japan1-5.bcmap new file mode 100644 index 00000000..5b4b65cc Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/Adobe-Japan1-5.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/Adobe-Japan1-6.bcmap b/web/public/vendor/pdfjs/cmaps/Adobe-Japan1-6.bcmap new file mode 100644 index 00000000..e77d699a Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/Adobe-Japan1-6.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/Adobe-Japan1-UCS2.bcmap b/web/public/vendor/pdfjs/cmaps/Adobe-Japan1-UCS2.bcmap new file mode 100644 index 00000000..128a1410 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/Adobe-Japan1-UCS2.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/Adobe-Korea1-0.bcmap b/web/public/vendor/pdfjs/cmaps/Adobe-Korea1-0.bcmap new file mode 100644 index 00000000..cef1a998 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/Adobe-Korea1-0.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/Adobe-Korea1-1.bcmap b/web/public/vendor/pdfjs/cmaps/Adobe-Korea1-1.bcmap new file mode 100644 index 00000000..11ffa36d Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/Adobe-Korea1-1.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/Adobe-Korea1-2.bcmap b/web/public/vendor/pdfjs/cmaps/Adobe-Korea1-2.bcmap new file mode 100644 index 00000000..3172308c Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/Adobe-Korea1-2.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/Adobe-Korea1-UCS2.bcmap b/web/public/vendor/pdfjs/cmaps/Adobe-Korea1-UCS2.bcmap new file mode 100644 index 00000000..f3371c0c Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/Adobe-Korea1-UCS2.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/B5-H.bcmap b/web/public/vendor/pdfjs/cmaps/B5-H.bcmap new file mode 100644 index 00000000..beb4d228 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/B5-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/B5-V.bcmap b/web/public/vendor/pdfjs/cmaps/B5-V.bcmap new file mode 100644 index 00000000..2d4f87d5 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/B5-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/B5pc-H.bcmap b/web/public/vendor/pdfjs/cmaps/B5pc-H.bcmap new file mode 100644 index 00000000..ce001316 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/B5pc-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/B5pc-V.bcmap b/web/public/vendor/pdfjs/cmaps/B5pc-V.bcmap new file mode 100644 index 00000000..73b99ff2 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/B5pc-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/CNS-EUC-H.bcmap b/web/public/vendor/pdfjs/cmaps/CNS-EUC-H.bcmap new file mode 100644 index 00000000..61d1d0cb Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/CNS-EUC-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/CNS-EUC-V.bcmap b/web/public/vendor/pdfjs/cmaps/CNS-EUC-V.bcmap new file mode 100644 index 00000000..1a393a51 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/CNS-EUC-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/CNS1-H.bcmap b/web/public/vendor/pdfjs/cmaps/CNS1-H.bcmap new file mode 100644 index 00000000..f738e218 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/CNS1-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/CNS1-V.bcmap b/web/public/vendor/pdfjs/cmaps/CNS1-V.bcmap new file mode 100644 index 00000000..9c3169f0 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/CNS1-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/CNS2-H.bcmap b/web/public/vendor/pdfjs/cmaps/CNS2-H.bcmap new file mode 100644 index 00000000..c89b3527 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/CNS2-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/CNS2-V.bcmap b/web/public/vendor/pdfjs/cmaps/CNS2-V.bcmap new file mode 100644 index 00000000..7588cec8 --- /dev/null +++ b/web/public/vendor/pdfjs/cmaps/CNS2-V.bcmap @@ -0,0 +1,3 @@ +àRCopyright 1990-2009 Adobe Systems Incorporated. +All rights reserved. +See ./LICENSEáCNS2-H \ No newline at end of file diff --git a/web/public/vendor/pdfjs/cmaps/ETHK-B5-H.bcmap b/web/public/vendor/pdfjs/cmaps/ETHK-B5-H.bcmap new file mode 100644 index 00000000..cb29415d Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/ETHK-B5-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/ETHK-B5-V.bcmap b/web/public/vendor/pdfjs/cmaps/ETHK-B5-V.bcmap new file mode 100644 index 00000000..f09aec63 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/ETHK-B5-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/ETen-B5-H.bcmap b/web/public/vendor/pdfjs/cmaps/ETen-B5-H.bcmap new file mode 100644 index 00000000..c2d77462 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/ETen-B5-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/ETen-B5-V.bcmap b/web/public/vendor/pdfjs/cmaps/ETen-B5-V.bcmap new file mode 100644 index 00000000..89bff159 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/ETen-B5-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/ETenms-B5-H.bcmap b/web/public/vendor/pdfjs/cmaps/ETenms-B5-H.bcmap new file mode 100644 index 00000000..a7d69db5 --- /dev/null +++ b/web/public/vendor/pdfjs/cmaps/ETenms-B5-H.bcmap @@ -0,0 +1,3 @@ +àRCopyright 1990-2009 Adobe Systems Incorporated. +All rights reserved. +See ./LICENSEá ETen-B5-H` ^ \ No newline at end of file diff --git a/web/public/vendor/pdfjs/cmaps/ETenms-B5-V.bcmap b/web/public/vendor/pdfjs/cmaps/ETenms-B5-V.bcmap new file mode 100644 index 00000000..adc5d618 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/ETenms-B5-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/EUC-H.bcmap b/web/public/vendor/pdfjs/cmaps/EUC-H.bcmap new file mode 100644 index 00000000..e92ea5b3 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/EUC-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/EUC-V.bcmap b/web/public/vendor/pdfjs/cmaps/EUC-V.bcmap new file mode 100644 index 00000000..7a7c1832 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/EUC-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/Ext-H.bcmap b/web/public/vendor/pdfjs/cmaps/Ext-H.bcmap new file mode 100644 index 00000000..3b5cde44 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/Ext-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/Ext-RKSJ-H.bcmap b/web/public/vendor/pdfjs/cmaps/Ext-RKSJ-H.bcmap new file mode 100644 index 00000000..ea4d2d97 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/Ext-RKSJ-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/Ext-RKSJ-V.bcmap b/web/public/vendor/pdfjs/cmaps/Ext-RKSJ-V.bcmap new file mode 100644 index 00000000..3457c277 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/Ext-RKSJ-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/Ext-V.bcmap b/web/public/vendor/pdfjs/cmaps/Ext-V.bcmap new file mode 100644 index 00000000..4999ca40 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/Ext-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/GB-EUC-H.bcmap b/web/public/vendor/pdfjs/cmaps/GB-EUC-H.bcmap new file mode 100644 index 00000000..e39908b9 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/GB-EUC-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/GB-EUC-V.bcmap b/web/public/vendor/pdfjs/cmaps/GB-EUC-V.bcmap new file mode 100644 index 00000000..d5be5446 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/GB-EUC-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/GB-H.bcmap b/web/public/vendor/pdfjs/cmaps/GB-H.bcmap new file mode 100644 index 00000000..39189c54 --- /dev/null +++ b/web/public/vendor/pdfjs/cmaps/GB-H.bcmap @@ -0,0 +1,4 @@ +àRCopyright 1990-2009 Adobe Systems Incorporated. +All rights reserved. +See ./LICENSE!!�º]aX!!]`�21�> �p �z�$]‚�"R‚d�-Uƒ7�*„ 4„%�+ „Z „{�/…%…<�9K…b�1]†.�"‡ ‰`]‡,�"]ˆ +�"]ˆh�"]‰F�"]Š$�"]‹�"]‹`�"]Œ>�"]��"]�z�"]ŽX�"]�6�"]��"]�r�"]‘P�"]’.�"]“ �"]“j�"]”H�"]•&�"]–�"]–b�"]—@�"]˜�"]˜|�"]™Z�"]š8�"]›�"]›t�"]œR�"]�0�"]ž�"]žl�"]ŸJ�"] (�"]¡�"]¡d�"]¢B�"]£ �"X£~�']¤W�"]¥5�"]¦�"]¦q�"]§O�"]¨-�"]© �"]©i�"]ªG�"]«%�"]¬�"]¬a�"]­?�"]®�"]®{�"]¯Y�"]°7�"]±�"]±s�"]²Q�"]³/�"]´ �"]´k�"]µI�"]¶'�"]·�"]·c�"]¸A�"]¹�"]¹}�"]º[�"]»9 \ No newline at end of file diff --git a/web/public/vendor/pdfjs/cmaps/GB-V.bcmap b/web/public/vendor/pdfjs/cmaps/GB-V.bcmap new file mode 100644 index 00000000..31083451 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/GB-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/GBK-EUC-H.bcmap b/web/public/vendor/pdfjs/cmaps/GBK-EUC-H.bcmap new file mode 100644 index 00000000..05fff7e8 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/GBK-EUC-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/GBK-EUC-V.bcmap b/web/public/vendor/pdfjs/cmaps/GBK-EUC-V.bcmap new file mode 100644 index 00000000..0cdf6bed Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/GBK-EUC-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/GBK2K-H.bcmap b/web/public/vendor/pdfjs/cmaps/GBK2K-H.bcmap new file mode 100644 index 00000000..46f6ba59 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/GBK2K-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/GBK2K-V.bcmap b/web/public/vendor/pdfjs/cmaps/GBK2K-V.bcmap new file mode 100644 index 00000000..d9a94798 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/GBK2K-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/GBKp-EUC-H.bcmap b/web/public/vendor/pdfjs/cmaps/GBKp-EUC-H.bcmap new file mode 100644 index 00000000..5cb0af68 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/GBKp-EUC-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/GBKp-EUC-V.bcmap b/web/public/vendor/pdfjs/cmaps/GBKp-EUC-V.bcmap new file mode 100644 index 00000000..bca93b8e Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/GBKp-EUC-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/GBT-EUC-H.bcmap b/web/public/vendor/pdfjs/cmaps/GBT-EUC-H.bcmap new file mode 100644 index 00000000..4b4e2d32 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/GBT-EUC-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/GBT-EUC-V.bcmap b/web/public/vendor/pdfjs/cmaps/GBT-EUC-V.bcmap new file mode 100644 index 00000000..38f70669 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/GBT-EUC-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/GBT-H.bcmap b/web/public/vendor/pdfjs/cmaps/GBT-H.bcmap new file mode 100644 index 00000000..8437ac33 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/GBT-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/GBT-V.bcmap b/web/public/vendor/pdfjs/cmaps/GBT-V.bcmap new file mode 100644 index 00000000..697ab4a8 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/GBT-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/GBTpc-EUC-H.bcmap b/web/public/vendor/pdfjs/cmaps/GBTpc-EUC-H.bcmap new file mode 100644 index 00000000..f6e50e89 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/GBTpc-EUC-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/GBTpc-EUC-V.bcmap b/web/public/vendor/pdfjs/cmaps/GBTpc-EUC-V.bcmap new file mode 100644 index 00000000..6c0d71a2 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/GBTpc-EUC-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/GBpc-EUC-H.bcmap b/web/public/vendor/pdfjs/cmaps/GBpc-EUC-H.bcmap new file mode 100644 index 00000000..c9edf67c Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/GBpc-EUC-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/GBpc-EUC-V.bcmap b/web/public/vendor/pdfjs/cmaps/GBpc-EUC-V.bcmap new file mode 100644 index 00000000..31450c97 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/GBpc-EUC-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/H.bcmap b/web/public/vendor/pdfjs/cmaps/H.bcmap new file mode 100644 index 00000000..7b24ea46 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/HKdla-B5-H.bcmap b/web/public/vendor/pdfjs/cmaps/HKdla-B5-H.bcmap new file mode 100644 index 00000000..7d30c050 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/HKdla-B5-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/HKdla-B5-V.bcmap b/web/public/vendor/pdfjs/cmaps/HKdla-B5-V.bcmap new file mode 100644 index 00000000..78946940 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/HKdla-B5-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/HKdlb-B5-H.bcmap b/web/public/vendor/pdfjs/cmaps/HKdlb-B5-H.bcmap new file mode 100644 index 00000000..d829a231 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/HKdlb-B5-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/HKdlb-B5-V.bcmap b/web/public/vendor/pdfjs/cmaps/HKdlb-B5-V.bcmap new file mode 100644 index 00000000..2b572b50 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/HKdlb-B5-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/HKgccs-B5-H.bcmap b/web/public/vendor/pdfjs/cmaps/HKgccs-B5-H.bcmap new file mode 100644 index 00000000..971a4f23 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/HKgccs-B5-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/HKgccs-B5-V.bcmap b/web/public/vendor/pdfjs/cmaps/HKgccs-B5-V.bcmap new file mode 100644 index 00000000..d353ca25 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/HKgccs-B5-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/HKm314-B5-H.bcmap b/web/public/vendor/pdfjs/cmaps/HKm314-B5-H.bcmap new file mode 100644 index 00000000..576dc011 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/HKm314-B5-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/HKm314-B5-V.bcmap b/web/public/vendor/pdfjs/cmaps/HKm314-B5-V.bcmap new file mode 100644 index 00000000..0e96d0e2 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/HKm314-B5-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/HKm471-B5-H.bcmap b/web/public/vendor/pdfjs/cmaps/HKm471-B5-H.bcmap new file mode 100644 index 00000000..11d170c7 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/HKm471-B5-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/HKm471-B5-V.bcmap b/web/public/vendor/pdfjs/cmaps/HKm471-B5-V.bcmap new file mode 100644 index 00000000..54959bf9 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/HKm471-B5-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/HKscs-B5-H.bcmap b/web/public/vendor/pdfjs/cmaps/HKscs-B5-H.bcmap new file mode 100644 index 00000000..6ef7857a Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/HKscs-B5-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/HKscs-B5-V.bcmap b/web/public/vendor/pdfjs/cmaps/HKscs-B5-V.bcmap new file mode 100644 index 00000000..1fb2fa2a Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/HKscs-B5-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/Hankaku.bcmap b/web/public/vendor/pdfjs/cmaps/Hankaku.bcmap new file mode 100644 index 00000000..4b8ec7fc Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/Hankaku.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/Hiragana.bcmap b/web/public/vendor/pdfjs/cmaps/Hiragana.bcmap new file mode 100644 index 00000000..17e983e7 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/Hiragana.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/KSC-EUC-H.bcmap b/web/public/vendor/pdfjs/cmaps/KSC-EUC-H.bcmap new file mode 100644 index 00000000..a45c65f0 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/KSC-EUC-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/KSC-EUC-V.bcmap b/web/public/vendor/pdfjs/cmaps/KSC-EUC-V.bcmap new file mode 100644 index 00000000..0e7b21f0 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/KSC-EUC-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/KSC-H.bcmap b/web/public/vendor/pdfjs/cmaps/KSC-H.bcmap new file mode 100644 index 00000000..b9b22b67 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/KSC-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/KSC-Johab-H.bcmap b/web/public/vendor/pdfjs/cmaps/KSC-Johab-H.bcmap new file mode 100644 index 00000000..2531ffcf Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/KSC-Johab-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/KSC-Johab-V.bcmap b/web/public/vendor/pdfjs/cmaps/KSC-Johab-V.bcmap new file mode 100644 index 00000000..367ceb22 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/KSC-Johab-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/KSC-V.bcmap b/web/public/vendor/pdfjs/cmaps/KSC-V.bcmap new file mode 100644 index 00000000..6ae2f0b6 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/KSC-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/KSCms-UHC-H.bcmap b/web/public/vendor/pdfjs/cmaps/KSCms-UHC-H.bcmap new file mode 100644 index 00000000..a8d4240e Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/KSCms-UHC-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/KSCms-UHC-HW-H.bcmap b/web/public/vendor/pdfjs/cmaps/KSCms-UHC-HW-H.bcmap new file mode 100644 index 00000000..8b4ae18f Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/KSCms-UHC-HW-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/KSCms-UHC-HW-V.bcmap b/web/public/vendor/pdfjs/cmaps/KSCms-UHC-HW-V.bcmap new file mode 100644 index 00000000..b655dbcf Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/KSCms-UHC-HW-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/KSCms-UHC-V.bcmap b/web/public/vendor/pdfjs/cmaps/KSCms-UHC-V.bcmap new file mode 100644 index 00000000..21f97f65 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/KSCms-UHC-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/KSCpc-EUC-H.bcmap b/web/public/vendor/pdfjs/cmaps/KSCpc-EUC-H.bcmap new file mode 100644 index 00000000..e06f361e Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/KSCpc-EUC-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/KSCpc-EUC-V.bcmap b/web/public/vendor/pdfjs/cmaps/KSCpc-EUC-V.bcmap new file mode 100644 index 00000000..f3c9113f Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/KSCpc-EUC-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/Katakana.bcmap b/web/public/vendor/pdfjs/cmaps/Katakana.bcmap new file mode 100644 index 00000000..524303c4 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/Katakana.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/LICENSE b/web/public/vendor/pdfjs/cmaps/LICENSE new file mode 100644 index 00000000..b1ad168a --- /dev/null +++ b/web/public/vendor/pdfjs/cmaps/LICENSE @@ -0,0 +1,36 @@ +%%Copyright: ----------------------------------------------------------- +%%Copyright: Copyright 1990-2009 Adobe Systems Incorporated. +%%Copyright: All rights reserved. +%%Copyright: +%%Copyright: Redistribution and use in source and binary forms, with or +%%Copyright: without modification, are permitted provided that the +%%Copyright: following conditions are met: +%%Copyright: +%%Copyright: Redistributions of source code must retain the above +%%Copyright: copyright notice, this list of conditions and the following +%%Copyright: disclaimer. +%%Copyright: +%%Copyright: Redistributions in binary form must reproduce the above +%%Copyright: copyright notice, this list of conditions and the following +%%Copyright: disclaimer in the documentation and/or other materials +%%Copyright: provided with the distribution. +%%Copyright: +%%Copyright: Neither the name of Adobe Systems Incorporated nor the names +%%Copyright: of its contributors may be used to endorse or promote +%%Copyright: products derived from this software without specific prior +%%Copyright: written permission. +%%Copyright: +%%Copyright: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +%%Copyright: CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +%%Copyright: INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +%%Copyright: MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +%%Copyright: DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +%%Copyright: CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +%%Copyright: SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +%%Copyright: NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +%%Copyright: LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +%%Copyright: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +%%Copyright: CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +%%Copyright: OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +%%Copyright: SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +%%Copyright: ----------------------------------------------------------- diff --git a/web/public/vendor/pdfjs/cmaps/NWP-H.bcmap b/web/public/vendor/pdfjs/cmaps/NWP-H.bcmap new file mode 100644 index 00000000..afc5e4b0 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/NWP-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/NWP-V.bcmap b/web/public/vendor/pdfjs/cmaps/NWP-V.bcmap new file mode 100644 index 00000000..bb5785e3 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/NWP-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/RKSJ-H.bcmap b/web/public/vendor/pdfjs/cmaps/RKSJ-H.bcmap new file mode 100644 index 00000000..fb8d298e Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/RKSJ-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/RKSJ-V.bcmap b/web/public/vendor/pdfjs/cmaps/RKSJ-V.bcmap new file mode 100644 index 00000000..a2555a6c Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/RKSJ-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/Roman.bcmap b/web/public/vendor/pdfjs/cmaps/Roman.bcmap new file mode 100644 index 00000000..f896dcf1 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/Roman.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniCNS-UCS2-H.bcmap b/web/public/vendor/pdfjs/cmaps/UniCNS-UCS2-H.bcmap new file mode 100644 index 00000000..d5db27c5 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniCNS-UCS2-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniCNS-UCS2-V.bcmap b/web/public/vendor/pdfjs/cmaps/UniCNS-UCS2-V.bcmap new file mode 100644 index 00000000..1dc9b7a2 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniCNS-UCS2-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniCNS-UTF16-H.bcmap b/web/public/vendor/pdfjs/cmaps/UniCNS-UTF16-H.bcmap new file mode 100644 index 00000000..961afefb Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniCNS-UTF16-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniCNS-UTF16-V.bcmap b/web/public/vendor/pdfjs/cmaps/UniCNS-UTF16-V.bcmap new file mode 100644 index 00000000..df0cffe8 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniCNS-UTF16-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniCNS-UTF32-H.bcmap b/web/public/vendor/pdfjs/cmaps/UniCNS-UTF32-H.bcmap new file mode 100644 index 00000000..1ab18a14 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniCNS-UTF32-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniCNS-UTF32-V.bcmap b/web/public/vendor/pdfjs/cmaps/UniCNS-UTF32-V.bcmap new file mode 100644 index 00000000..ad14662e Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniCNS-UTF32-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniCNS-UTF8-H.bcmap b/web/public/vendor/pdfjs/cmaps/UniCNS-UTF8-H.bcmap new file mode 100644 index 00000000..83c6bd7c Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniCNS-UTF8-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniCNS-UTF8-V.bcmap b/web/public/vendor/pdfjs/cmaps/UniCNS-UTF8-V.bcmap new file mode 100644 index 00000000..22a27e4d Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniCNS-UTF8-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniGB-UCS2-H.bcmap b/web/public/vendor/pdfjs/cmaps/UniGB-UCS2-H.bcmap new file mode 100644 index 00000000..5bd6228c Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniGB-UCS2-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniGB-UCS2-V.bcmap b/web/public/vendor/pdfjs/cmaps/UniGB-UCS2-V.bcmap new file mode 100644 index 00000000..53c534b7 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniGB-UCS2-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniGB-UTF16-H.bcmap b/web/public/vendor/pdfjs/cmaps/UniGB-UTF16-H.bcmap new file mode 100644 index 00000000..b95045b4 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniGB-UTF16-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniGB-UTF16-V.bcmap b/web/public/vendor/pdfjs/cmaps/UniGB-UTF16-V.bcmap new file mode 100644 index 00000000..51f023e0 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniGB-UTF16-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniGB-UTF32-H.bcmap b/web/public/vendor/pdfjs/cmaps/UniGB-UTF32-H.bcmap new file mode 100644 index 00000000..f0dbd14f Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniGB-UTF32-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniGB-UTF32-V.bcmap b/web/public/vendor/pdfjs/cmaps/UniGB-UTF32-V.bcmap new file mode 100644 index 00000000..ce9c30a9 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniGB-UTF32-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniGB-UTF8-H.bcmap b/web/public/vendor/pdfjs/cmaps/UniGB-UTF8-H.bcmap new file mode 100644 index 00000000..982ca462 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniGB-UTF8-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniGB-UTF8-V.bcmap b/web/public/vendor/pdfjs/cmaps/UniGB-UTF8-V.bcmap new file mode 100644 index 00000000..f78020dd Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniGB-UTF8-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniJIS-UCS2-H.bcmap b/web/public/vendor/pdfjs/cmaps/UniJIS-UCS2-H.bcmap new file mode 100644 index 00000000..7daf56af Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniJIS-UCS2-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniJIS-UCS2-HW-H.bcmap b/web/public/vendor/pdfjs/cmaps/UniJIS-UCS2-HW-H.bcmap new file mode 100644 index 00000000..ac9975c5 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniJIS-UCS2-HW-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniJIS-UCS2-HW-V.bcmap b/web/public/vendor/pdfjs/cmaps/UniJIS-UCS2-HW-V.bcmap new file mode 100644 index 00000000..3da0a1c6 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniJIS-UCS2-HW-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniJIS-UCS2-V.bcmap b/web/public/vendor/pdfjs/cmaps/UniJIS-UCS2-V.bcmap new file mode 100644 index 00000000..c50b9ddf Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniJIS-UCS2-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniJIS-UTF16-H.bcmap b/web/public/vendor/pdfjs/cmaps/UniJIS-UTF16-H.bcmap new file mode 100644 index 00000000..67613446 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniJIS-UTF16-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniJIS-UTF16-V.bcmap b/web/public/vendor/pdfjs/cmaps/UniJIS-UTF16-V.bcmap new file mode 100644 index 00000000..70bf90c0 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniJIS-UTF16-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniJIS-UTF32-H.bcmap b/web/public/vendor/pdfjs/cmaps/UniJIS-UTF32-H.bcmap new file mode 100644 index 00000000..7a83d53a Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniJIS-UTF32-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniJIS-UTF32-V.bcmap b/web/public/vendor/pdfjs/cmaps/UniJIS-UTF32-V.bcmap new file mode 100644 index 00000000..7a871353 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniJIS-UTF32-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniJIS-UTF8-H.bcmap b/web/public/vendor/pdfjs/cmaps/UniJIS-UTF8-H.bcmap new file mode 100644 index 00000000..9f0334ca Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniJIS-UTF8-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniJIS-UTF8-V.bcmap b/web/public/vendor/pdfjs/cmaps/UniJIS-UTF8-V.bcmap new file mode 100644 index 00000000..808a94f0 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniJIS-UTF8-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniJIS2004-UTF16-H.bcmap b/web/public/vendor/pdfjs/cmaps/UniJIS2004-UTF16-H.bcmap new file mode 100644 index 00000000..d768bf81 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniJIS2004-UTF16-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniJIS2004-UTF16-V.bcmap b/web/public/vendor/pdfjs/cmaps/UniJIS2004-UTF16-V.bcmap new file mode 100644 index 00000000..3d5bf6fb Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniJIS2004-UTF16-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniJIS2004-UTF32-H.bcmap b/web/public/vendor/pdfjs/cmaps/UniJIS2004-UTF32-H.bcmap new file mode 100644 index 00000000..09eee10d Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniJIS2004-UTF32-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniJIS2004-UTF32-V.bcmap b/web/public/vendor/pdfjs/cmaps/UniJIS2004-UTF32-V.bcmap new file mode 100644 index 00000000..6c546001 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniJIS2004-UTF32-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniJIS2004-UTF8-H.bcmap b/web/public/vendor/pdfjs/cmaps/UniJIS2004-UTF8-H.bcmap new file mode 100644 index 00000000..1b1a64f5 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniJIS2004-UTF8-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniJIS2004-UTF8-V.bcmap b/web/public/vendor/pdfjs/cmaps/UniJIS2004-UTF8-V.bcmap new file mode 100644 index 00000000..994aa9ef Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniJIS2004-UTF8-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniJISPro-UCS2-HW-V.bcmap b/web/public/vendor/pdfjs/cmaps/UniJISPro-UCS2-HW-V.bcmap new file mode 100644 index 00000000..643f921b Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniJISPro-UCS2-HW-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniJISPro-UCS2-V.bcmap b/web/public/vendor/pdfjs/cmaps/UniJISPro-UCS2-V.bcmap new file mode 100644 index 00000000..c148f67f Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniJISPro-UCS2-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniJISPro-UTF8-V.bcmap b/web/public/vendor/pdfjs/cmaps/UniJISPro-UTF8-V.bcmap new file mode 100644 index 00000000..1849d809 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniJISPro-UTF8-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniJISX0213-UTF32-H.bcmap b/web/public/vendor/pdfjs/cmaps/UniJISX0213-UTF32-H.bcmap new file mode 100644 index 00000000..a83a677c Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniJISX0213-UTF32-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniJISX0213-UTF32-V.bcmap b/web/public/vendor/pdfjs/cmaps/UniJISX0213-UTF32-V.bcmap new file mode 100644 index 00000000..f527248a Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniJISX0213-UTF32-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniJISX02132004-UTF32-H.bcmap b/web/public/vendor/pdfjs/cmaps/UniJISX02132004-UTF32-H.bcmap new file mode 100644 index 00000000..e1a988dc Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniJISX02132004-UTF32-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniJISX02132004-UTF32-V.bcmap b/web/public/vendor/pdfjs/cmaps/UniJISX02132004-UTF32-V.bcmap new file mode 100644 index 00000000..47e054a9 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniJISX02132004-UTF32-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniKS-UCS2-H.bcmap b/web/public/vendor/pdfjs/cmaps/UniKS-UCS2-H.bcmap new file mode 100644 index 00000000..b5b94852 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniKS-UCS2-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniKS-UCS2-V.bcmap b/web/public/vendor/pdfjs/cmaps/UniKS-UCS2-V.bcmap new file mode 100644 index 00000000..026adcaa Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniKS-UCS2-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniKS-UTF16-H.bcmap b/web/public/vendor/pdfjs/cmaps/UniKS-UTF16-H.bcmap new file mode 100644 index 00000000..fd4e66e8 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniKS-UTF16-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniKS-UTF16-V.bcmap b/web/public/vendor/pdfjs/cmaps/UniKS-UTF16-V.bcmap new file mode 100644 index 00000000..075efb70 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniKS-UTF16-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniKS-UTF32-H.bcmap b/web/public/vendor/pdfjs/cmaps/UniKS-UTF32-H.bcmap new file mode 100644 index 00000000..769d2142 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniKS-UTF32-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniKS-UTF32-V.bcmap b/web/public/vendor/pdfjs/cmaps/UniKS-UTF32-V.bcmap new file mode 100644 index 00000000..bdab208b Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniKS-UTF32-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniKS-UTF8-H.bcmap b/web/public/vendor/pdfjs/cmaps/UniKS-UTF8-H.bcmap new file mode 100644 index 00000000..6ff8674a Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniKS-UTF8-H.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/UniKS-UTF8-V.bcmap b/web/public/vendor/pdfjs/cmaps/UniKS-UTF8-V.bcmap new file mode 100644 index 00000000..8dfa76a5 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/UniKS-UTF8-V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/V.bcmap b/web/public/vendor/pdfjs/cmaps/V.bcmap new file mode 100644 index 00000000..fdec9906 Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/V.bcmap differ diff --git a/web/public/vendor/pdfjs/cmaps/WP-Symbol.bcmap b/web/public/vendor/pdfjs/cmaps/WP-Symbol.bcmap new file mode 100644 index 00000000..46729bbf Binary files /dev/null and b/web/public/vendor/pdfjs/cmaps/WP-Symbol.bcmap differ diff --git a/web/public/vendor/pdfjs/pdf.min.mjs b/web/public/vendor/pdfjs/pdf.min.mjs new file mode 100644 index 00000000..5822350b --- /dev/null +++ b/web/public/vendor/pdfjs/pdf.min.mjs @@ -0,0 +1,28 @@ +/** + * @licstart The following is the entire license notice for the + * JavaScript code in this page + * + * Copyright 2024 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @licend The above is the entire license notice for the + * JavaScript code in this page + */ +/** + * pdfjsVersion = 5.7.284 + * pdfjsBuild = 7e5b36c2d + */const t=!("object"!=typeof process||process+""!="[object process]"||process.versions.nw||process.versions.electron&&process.type&&"browser"!==process.type),e=[1/0,1/0,-1/0,-1/0],i=new Float32Array(e),s=[.001,0,0,.001,0,0],n=1.35,r=1,a=2,o=4,l=16,h=32,d=64,c=128,u=256,p={DISABLE:0,ENABLE:1,ENABLE_FORMS:2,ENABLE_STORAGE:3},g="pdfjs_internal_editor_",m={DISABLE:-1,NONE:0,FREETEXT:3,HIGHLIGHT:9,STAMP:13,INK:15,POPUP:16,SIGNATURE:101,COMMENT:102},f={RESIZE:1,CREATE:2,FREETEXT_SIZE:11,FREETEXT_COLOR:12,FREETEXT_OPACITY:13,INK_COLOR:21,INK_THICKNESS:22,INK_OPACITY:23,INK_COLOR_AND_OPACITY:24,HIGHLIGHT_COLOR:31,HIGHLIGHT_THICKNESS:32,HIGHLIGHT_FREE:33,HIGHLIGHT_SHOW_ALL:34,DRAW_STEP:41},b={PRINT:4,MODIFY_CONTENTS:8,COPY:16,MODIFY_ANNOTATIONS:32,FILL_INTERACTIVE_FORMS:256,COPY_FOR_ACCESSIBILITY:512,ASSEMBLE:1024,PRINT_HIGH_QUALITY:2048},y=0,A=1,v=2,w=3,x=3,C=4,E={GRAYSCALE_1BPP:1,RGB_24BPP:2,RGBA_32BPP:3},T={TEXT:1,LINK:2,FREETEXT:3,LINE:4,SQUARE:5,CIRCLE:6,POLYGON:7,POLYLINE:8,HIGHLIGHT:9,UNDERLINE:10,SQUIGGLY:11,STRIKEOUT:12,STAMP:13,CARET:14,INK:15,POPUP:16,FILEATTACHMENT:17,SOUND:18,MOVIE:19,WIDGET:20,SCREEN:21,PRINTERMARK:22,TRAPNET:23,WATERMARK:24,THREED:25,REDACT:26},S=1,_=2,P=3,D=4,M=5,k={ERRORS:0,WARNINGS:1,INFOS:5},I={dependency:1,setLineWidth:2,setLineCap:3,setLineJoin:4,setMiterLimit:5,setDash:6,setRenderingIntent:7,setFlatness:8,setGState:9,save:10,restore:11,transform:12,moveTo:13,lineTo:14,curveTo:15,curveTo2:16,curveTo3:17,closePath:18,rectangle:19,stroke:20,closeStroke:21,fill:22,eoFill:23,fillStroke:24,eoFillStroke:25,closeFillStroke:26,closeEOFillStroke:27,endPath:28,clip:29,eoClip:30,beginText:31,endText:32,setCharSpacing:33,setWordSpacing:34,setHScale:35,setLeading:36,setFont:37,setTextRenderingMode:38,setTextRise:39,moveText:40,setLeadingMoveText:41,setTextMatrix:42,nextLine:43,showText:44,showSpacedText:45,nextLineShowText:46,nextLineSetSpacingShowText:47,setCharWidth:48,setCharWidthAndBounds:49,setStrokeColorSpace:50,setFillColorSpace:51,setStrokeColor:52,setStrokeColorN:53,setFillColor:54,setFillColorN:55,setStrokeGray:56,setFillGray:57,setStrokeRGBColor:58,setFillRGBColor:59,setStrokeCMYKColor:60,setFillCMYKColor:61,shadingFill:62,beginInlineImage:63,beginImageData:64,endInlineImage:65,paintXObject:66,markPoint:67,markPointProps:68,beginMarkedContent:69,beginMarkedContentProps:70,endMarkedContent:71,beginCompat:72,endCompat:73,paintFormXObjectBegin:74,paintFormXObjectEnd:75,beginGroup:76,endGroup:77,beginAnnotation:80,endAnnotation:81,paintImageMaskXObject:83,paintImageMaskXObjectGroup:84,paintImageXObject:85,paintInlineImageXObject:86,paintInlineImageXObjectGroup:87,paintImageXObjectRepeat:88,paintImageMaskXObjectRepeat:89,paintSolidColorImageMask:90,constructPath:91,setStrokeTransparent:92,setFillTransparent:93,rawFillPath:94},F=0,B=1,R=2,O=3,L=4,N={NEED_PASSWORD:1,INCORRECT_PASSWORD:2};let U=k.WARNINGS;function setVerbosityLevel(t){Number.isInteger(t)&&(U=t)}function getVerbosityLevel(){return U}function info(t){U>=k.INFOS&&console.info(`Info: ${t}`)}function warn(t){U>=k.WARNINGS&&console.warn(`Warning: ${t}`)}function unreachable(t){throw new Error(t)}function assert(t,e){t||unreachable(e)}function createValidAbsoluteUrl(t,e=null,i=null){if(!t)return null;if(i&&"string"==typeof t){if(i.addDefaultProtocol&&t.startsWith("www.")){const e=t.match(/\./g);e?.length>=2&&(t=`http://${t}`)}if(i.tryConvertEncoding)try{t=function stringToUTF8String(t){return decodeURIComponent(escape(t))}(t)}catch{}}const s=e?URL.parse(t,e):URL.parse(t);return function _isValidProtocol(t){switch(t?.protocol){case"http:":case"https:":case"ftp:":case"mailto:":case"tel:":return!0;default:return!1}}(s)?s:null}function updateUrlHash(t,e,i=!1){const s=URL.parse(t);if(s){s.hash=e;return s.href}return i&&createValidAbsoluteUrl(t,"http://example.com")?t.split("#",1)[0]+""+(e?`#${e}`:""):""}function stripPath(t){return t.substring(t.lastIndexOf("/")+1)}function shadow(t,e,i,s=!1){Object.defineProperty(t,e,{value:i,enumerable:!s,configurable:!0,writable:!1});return i}const H=function BaseExceptionClosure(){function BaseException(t,e){this.message=t;this.name=e}BaseException.prototype=new Error;BaseException.constructor=BaseException;return BaseException}();class PasswordException extends H{constructor(t,e){super(t,"PasswordException");this.code=e}}class UnknownErrorException extends H{constructor(t,e){super(t,"UnknownErrorException");this.details=e}}class InvalidPDFException extends H{constructor(t){super(t,"InvalidPDFException")}}class ResponseException extends H{constructor(t,e,i){super(t,"ResponseException");this.status=e;this.missing=i}}class FormatError extends H{constructor(t){super(t,"FormatError")}}class AbortException extends H{constructor(t){super(t,"AbortException")}}function stringToBytes(t){"string"!=typeof t&&unreachable("Invalid argument for stringToBytes");const e=t.length,i=new Uint8Array(e);for(let s=0;s{if("undefined"==typeof document)return!1;const t=document.createElement("input");t.type="color";t.setAttribute("alpha","");t.value="#ff000080";return"#ff0000"!==t.value})())}}class Util{static get hexNums(){return shadow(this,"hexNums",Array.from(Array(256).keys(),t=>t.toString(16).padStart(2,"0")))}static makeHexColor(t,e,i){return`#${this.hexNums[t]}${this.hexNums[e]}${this.hexNums[i]}`}static domMatrixToTransform(t){return[t.a,t.b,t.c,t.d,t.e,t.f]}static scaleMinMax(t,e){let i;if(t[0]){if(t[0]<0){i=e[0];e[0]=e[2];e[2]=i}e[0]*=t[0];e[2]*=t[0];if(t[3]<0){i=e[1];e[1]=e[3];e[3]=i}e[1]*=t[3];e[3]*=t[3]}else{i=e[0];e[0]=e[1];e[1]=i;i=e[2];e[2]=e[3];e[3]=i;if(t[1]<0){i=e[1];e[1]=e[3];e[3]=i}e[1]*=t[1];e[3]*=t[1];if(t[2]<0){i=e[0];e[0]=e[2];e[2]=i}e[0]*=t[2];e[2]*=t[2]}e[0]+=t[4];e[1]+=t[5];e[2]+=t[4];e[3]+=t[5]}static transform(t,e){return[t[0]*e[0]+t[2]*e[1],t[1]*e[0]+t[3]*e[1],t[0]*e[2]+t[2]*e[3],t[1]*e[2]+t[3]*e[3],t[0]*e[4]+t[2]*e[5]+t[4],t[1]*e[4]+t[3]*e[5]+t[5]]}static multiplyByDOMMatrix(t,e){return[t[0]*e.a+t[2]*e.b,t[1]*e.a+t[3]*e.b,t[0]*e.c+t[2]*e.d,t[1]*e.c+t[3]*e.d,t[0]*e.e+t[2]*e.f+t[4],t[1]*e.e+t[3]*e.f+t[5]]}static applyTransform(t,e,i=0){const s=t[i],n=t[i+1];t[i]=s*e[0]+n*e[2]+e[4];t[i+1]=s*e[1]+n*e[3]+e[5]}static applyTransformToBezier(t,e,i=0){const s=e[0],n=e[1],r=e[2],a=e[3],o=e[4],l=e[5];for(let e=0;e<6;e+=2){const h=t[i+e],d=t[i+e+1];t[i+e]=h*s+d*r+o;t[i+e+1]=h*n+d*a+l}}static applyInverseTransform(t,e){const i=t[0],s=t[1],n=e[0]*e[3]-e[1]*e[2];t[0]=(i*e[3]-s*e[2]+e[2]*e[5]-e[4]*e[3])/n;t[1]=(-i*e[1]+s*e[0]+e[4]*e[1]-e[5]*e[0])/n}static axialAlignedBoundingBox(t,e,i){const s=e[0],n=e[1],r=e[2],a=e[3],o=e[4],l=e[5],h=t[0],d=t[1],c=t[2],u=t[3];let p=s*h+o,g=p,m=s*c+o,f=m,b=a*d+l,y=b,A=a*u+l,v=A;if(0!==n||0!==r){const t=n*h,e=n*c,i=r*d,s=r*u;p+=i;f+=i;m+=s;g+=s;b+=t;v+=t;A+=e;y+=e}i[0]=Math.min(i[0],p,m,g,f);i[1]=Math.min(i[1],b,A,y,v);i[2]=Math.max(i[2],p,m,g,f);i[3]=Math.max(i[3],b,A,y,v)}static inverseTransform(t){const e=t[0]*t[3]-t[1]*t[2];return[t[3]/e,-t[1]/e,-t[2]/e,t[0]/e,(t[2]*t[5]-t[4]*t[3])/e,(t[4]*t[1]-t[5]*t[0])/e]}static singularValueDecompose2dScale(t,e){const i=t[0],s=t[1],n=t[2],r=t[3],a=i**2+s**2,o=i*n+s*r,l=n**2+r**2,h=(a+l)/2,d=Math.sqrt(h**2-(a*l-o**2));e[0]=Math.sqrt(h+d||1);e[1]=Math.sqrt(h-d||1)}static normalizeRect(t){const e=t.slice(0);if(t[0]>t[2]){e[0]=t[2];e[2]=t[0]}if(t[1]>t[3]){e[1]=t[3];e[3]=t[1]}return e}static intersect(t,e){const i=Math.max(Math.min(t[0],t[2]),Math.min(e[0],e[2])),s=Math.min(Math.max(t[0],t[2]),Math.max(e[0],e[2]));if(i>s)return null;const n=Math.max(Math.min(t[1],t[3]),Math.min(e[1],e[3])),r=Math.min(Math.max(t[1],t[3]),Math.max(e[1],e[3]));return n>r?null:[i,n,s,r]}static pointBoundingBox(t,e,i){i[0]=Math.min(i[0],t);i[1]=Math.min(i[1],e);i[2]=Math.max(i[2],t);i[3]=Math.max(i[3],e)}static rectBoundingBox(t,e,i,s,n){n[0]=Math.min(n[0],t,i);n[1]=Math.min(n[1],e,s);n[2]=Math.max(n[2],t,i);n[3]=Math.max(n[3],e,s)}static#t(t,e,i,s,n,r,a,o,l,h){if(l<=0||l>=1)return;const d=1-l,c=l*l,u=c*l,p=d*(d*(d*t+3*l*e)+3*c*i)+u*s,g=d*(d*(d*n+3*l*r)+3*c*a)+u*o;h[0]=Math.min(h[0],p);h[1]=Math.min(h[1],g);h[2]=Math.max(h[2],p);h[3]=Math.max(h[3],g)}static#e(t,e,i,s,n,r,a,o,l,h,d,c){if(Math.abs(l)<1e-12){Math.abs(h)>=1e-12&&this.#t(t,e,i,s,n,r,a,o,-d/h,c);return}const u=h**2-4*d*l;if(u<0)return;const p=Math.sqrt(u),g=2*l;this.#t(t,e,i,s,n,r,a,o,(-h+p)/g,c);this.#t(t,e,i,s,n,r,a,o,(-h-p)/g,c)}static bezierBoundingBox(t,e,i,s,n,r,a,o,l){l[0]=Math.min(l[0],t,a);l[1]=Math.min(l[1],e,o);l[2]=Math.max(l[2],t,a);l[3]=Math.max(l[3],e,o);this.#e(t,i,n,a,e,s,r,o,3*(3*(i-n)-t+a),6*(t-2*i+n),3*(i-t),l);this.#e(t,i,n,a,e,s,r,o,3*(3*(s-r)-e+o),6*(e-2*s+r),3*(s-e),l)}}let z=null,G=null;function normalizeUnicode(t){if(!z){z=/([\u00a0\u00b5\u037e\u0eb3\u2000-\u200a\u202f\u2126\ufb00-\ufb04\ufb06\ufb20-\ufb36\ufb38-\ufb3c\ufb3e\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufba1\ufba4-\ufba9\ufbae-\ufbb1\ufbd3-\ufbdc\ufbde-\ufbe7\ufbea-\ufbf8\ufbfc-\ufbfd\ufc00-\ufc5d\ufc64-\ufcf1\ufcf5-\ufd3d\ufd88\ufdf4\ufdfa-\ufdfb\ufe71\ufe77\ufe79\ufe7b\ufe7d]+)|(\ufb05+)/gu;G=new Map([["ſt","ſt"]])}return t.replaceAll(z,(t,e,i)=>e?e.normalize("NFKC"):G.get(i))}function getUuid(){if("function"==typeof crypto.randomUUID)return crypto.randomUUID();const t=new Uint8Array(32);crypto.getRandomValues(t);return function bytesToString(t){"object"==typeof t&&void 0!==t?.length||unreachable("Invalid argument for bytesToString");const e=t.length,i=8192;if(e[],makeMap=()=>new Map,makeObj=()=>Object.create(null);function MathClamp(t,e,i){return Math.min(Math.max(t,e),i)}class XfaText{static textContent(t){const e=[],i={items:e,styles:Object.create(null)};!function walk(t){if(!t)return;let i=null;const s=t.name;if("#text"===s)i=t.value;else{if(!XfaText.shouldBuildText(s))return;t?.attributes?.textContent?i=t.attributes.textContent:t.value&&(i=t.value)}null!==i&&e.push({str:i});if(t.children)for(const e of t.children)walk(e)}(t);return i}static shouldBuildText(t){return!("textarea"===t||"input"===t||"option"===t||"select"===t)}}class XfaLayer{static setupStorage(t,e,i,s,n){const r=s.getValue(e,{value:null});switch(i.name){case"textarea":null!==r.value&&(t.textContent=r.value);if("print"===n)break;t.addEventListener("input",t=>{s.setValue(e,{value:t.target.value})});break;case"input":if("radio"===i.attributes.type||"checkbox"===i.attributes.type){r.value===i.attributes.xfaOn?t.setAttribute("checked",!0):r.value===i.attributes.xfaOff&&t.removeAttribute("checked");if("print"===n)break;t.addEventListener("change",t=>{s.setValue(e,{value:t.target.checked?t.target.getAttribute("xfaOn"):t.target.getAttribute("xfaOff")})})}else{null!==r.value&&t.setAttribute("value",r.value);if("print"===n)break;t.addEventListener("input",t=>{s.setValue(e,{value:t.target.value})})}break;case"select":if(null!==r.value){t.setAttribute("value",r.value);for(const t of i.children)t.attributes.value===r.value?t.attributes.selected=!0:Object.hasOwn(t.attributes,"selected")&&delete t.attributes.selected}t.addEventListener("input",t=>{const i=t.target.options,n=-1===i.selectedIndex?"":i[i.selectedIndex].value;s.setValue(e,{value:n})})}}static setAttributes({html:t,element:e,storage:i=null,intent:s,linkService:n}){const{attributes:r}=e,a=t instanceof HTMLAnchorElement;"radio"===r.type&&(r.name=`${r.name}-${s}`);for(const[e,i]of Object.entries(r))if(null!=i)switch(e){case"class":i.length&&t.setAttribute(e,i.join(" "));break;case"dataId":break;case"id":t.setAttribute("data-element-id",i);break;case"style":Object.assign(t.style,i);break;case"textContent":t.textContent=i;break;default:(!a||"href"!==e&&"newWindow"!==e)&&t.setAttribute(e,i)}a&&n.addLinkAttributes(t,r.href,r.newWindow);i&&r.dataId&&this.setupStorage(t,r.dataId,e,i)}static render(t){const e=t.annotationStorage,i=t.linkService,s=t.xfaHtml,n=t.intent||"display",r=document.createElement(s.name);s.attributes&&this.setAttributes({html:r,element:s,intent:n,linkService:i});const a="richText"!==n,o=t.div;o.append(r);if(t.viewport){const e=`matrix(${t.viewport.transform.join(",")})`;o.style.transform=e}a&&o.setAttribute("class","xfaLayer xfaFont");const l=[];if(0===s.children.length){if(s.value){const t=document.createTextNode(s.value);r.append(t);a&&XfaText.shouldBuildText(s.name)&&l.push(t)}return{textDivs:l}}const h=[[s,-1,r]];for(;h.length>0;){const[t,s,r]=h.at(-1);if(s+1===t.children.length){h.pop();continue}const o=t.children[++h.at(-1)[1]];if(null===o)continue;const{name:d}=o;if("#text"===d){const t=document.createTextNode(o.value);l.push(t);r.append(t);continue}const c=o?.attributes?.xmlns?document.createElementNS(o.attributes.xmlns,d):document.createElement(d);r.append(c);o.attributes&&this.setAttributes({html:c,element:o,storage:e,intent:n,linkService:i});if(o.children?.length>0)h.push([o,-1,c]);else if(o.value){const t=document.createTextNode(o.value);a&&XfaText.shouldBuildText(d)&&l.push(t);c.append(t)}}for(const t of o.querySelectorAll(".xfaNonInteractive input, .xfaNonInteractive textarea"))t.setAttribute("readOnly",!0);return{textDivs:l}}static update(t){const e=`matrix(${t.viewport.transform.join(",")})`;t.div.style.transform=e;t.div.hidden=!1}}const V="http://www.w3.org/2000/svg";class PixelsPerInch{static CSS=96;static PDF=72;static PDF_TO_CSS_UNITS=this.CSS/this.PDF}async function fetchData(t,e="text"){if(isValidFetchUrl(t,document.baseURI)){const i=await fetch(t);if(!i.ok)throw new Error(i.statusText);switch(e){case"blob":return i.blob();case"bytes":return i.bytes();case"json":return i.json()}return i.text()}return new Promise((i,s)=>{const n=new XMLHttpRequest;n.open("GET",t,!0);n.responseType="bytes"===e?"arraybuffer":e;n.onreadystatechange=()=>{if(n.readyState===XMLHttpRequest.DONE)if(200!==n.status&&0!==n.status)s(new Error(n.statusText));else{switch(e){case"bytes":i(new Uint8Array(n.response));return;case"blob":case"json":i(n.response);return}i(n.responseText)}};n.send(null)})}class PageViewport{constructor({viewBox:t,userUnit:e,scale:i,rotation:s,offsetX:n=0,offsetY:r=0,dontFlip:a=!1}){this.viewBox=t;this.userUnit=e;this.scale=i;this.rotation=s;this.offsetX=n;this.offsetY=r;i*=e;const o=(t[2]+t[0])/2,l=(t[3]+t[1])/2;let h,d,c,u,p,g,m,f;(s%=360)<0&&(s+=360);switch(s){case 180:h=-1;d=0;c=0;u=1;break;case 90:h=0;d=1;c=1;u=0;break;case 270:h=0;d=-1;c=-1;u=0;break;case 0:h=1;d=0;c=0;u=-1;break;default:throw new Error("PageViewport: Invalid rotation, must be a multiple of 90 degrees.")}if(a){c=-c;u=-u}if(0===h){p=Math.abs(l-t[1])*i+n;g=Math.abs(o-t[0])*i+r;m=(t[3]-t[1])*i;f=(t[2]-t[0])*i}else{p=Math.abs(o-t[0])*i+n;g=Math.abs(l-t[1])*i+r;m=(t[2]-t[0])*i;f=(t[3]-t[1])*i}this.transform=[h*i,d*i,c*i,u*i,p-h*i*o-c*i*l,g-d*i*o-u*i*l];this.width=m;this.height=f}get rawDims(){const t=this.viewBox;return shadow(this,"rawDims",{pageWidth:t[2]-t[0],pageHeight:t[3]-t[1],pageX:t[0],pageY:t[1]})}clone({scale:t=this.scale,rotation:e=this.rotation,offsetX:i=this.offsetX,offsetY:s=this.offsetY,dontFlip:n=!1}={}){return new PageViewport({viewBox:this.viewBox.slice(),userUnit:this.userUnit,scale:t,rotation:e,offsetX:i,offsetY:s,dontFlip:n})}convertToViewportPoint(t,e){const i=[t,e];Util.applyTransform(i,this.transform);return i}convertToViewportRectangle(t){const e=[t[0],t[1]];Util.applyTransform(e,this.transform);const i=[t[2],t[3]];Util.applyTransform(i,this.transform);return[e[0],e[1],i[0],i[1]]}convertToPdfPoint(t,e){const i=[t,e];Util.applyInverseTransform(i,this.transform);return i}}class RenderingCancelledException extends H{constructor(t,e=0){super(t,"RenderingCancelledException");this.extraDelay=e}}function isDataScheme(t){const e=t.length;let i=0;for(;i{try{return new URL(t)}catch{try{return new URL(decodeURIComponent(t))}catch{try{return new URL(t,"https://foo.bar")}catch{try{return new URL(decodeURIComponent(t),"https://foo.bar")}catch{return null}}}}})(t);if(!i)return e;const decode=t=>{try{let e=decodeURIComponent(t);if(e.includes("/")){e=stripPath(e);if(/^\.pdf$/i.test(e))return t}return e}catch{return t}},s=/\.pdf$/i,n=stripPath(i.pathname);if(s.test(n))return decode(n);if(i.searchParams.size>0){const getLast=t=>[...t].findLast(t=>s.test(t)),t=getLast(i.searchParams.values())??getLast(i.searchParams.keys());if(t)return decode(t)}if(i.hash){const t=/[^/?#=]+\.pdf\b(?!.*\.pdf\b)/i.exec(i.hash);if(t)return decode(t[0])}return e}class StatTimer{#i=new Map;times=[];time(t){this.#i.has(t)&&warn(`Timer is already running for ${t}`);this.#i.set(t,Date.now())}timeEnd(t){this.#i.has(t)||warn(`Timer has not been started for ${t}`);this.times.push({name:t,start:this.#i.get(t),end:Date.now()});this.#i.delete(t)}toString(){const t=Math.max(...this.times.map(t=>t.name.length));return this.times.map(e=>`${e.name.padEnd(t)} ${e.end-e.start}ms\n`).join("")}}function isValidFetchUrl(t,e){const i=e?URL.parse(t,e):URL.parse(t);return/https?:/.test(i?.protocol??"")}function noContextMenu(t){t.preventDefault()}function stopEvent(t){t.preventDefault();t.stopPropagation()}class PDFDateString{static#s;static toDateObject(t){if(t instanceof Date)return t;if(!t||"string"!=typeof t)return null;this.#s||=new RegExp("^D:(\\d{4})(\\d{2})?(\\d{2})?(\\d{2})?(\\d{2})?(\\d{2})?([Z|+|-])?(\\d{2})?'?(\\d{2})?'?");const e=this.#s.exec(t);if(!e)return null;const i=parseInt(e[1],10);let s=parseInt(e[2],10);s=s>=1&&s<=12?s-1:0;let n=parseInt(e[3],10);n=n>=1&&n<=31?n:1;let r=parseInt(e[4],10);r=r>=0&&r<=23?r:0;let a=parseInt(e[5],10);a=a>=0&&a<=59?a:0;let o=parseInt(e[6],10);o=o>=0&&o<=59?o:0;const l=e[7]||"Z";let h=parseInt(e[8],10);h=h>=0&&h<=23?h:0;let d=parseInt(e[9],10)||0;d=d>=0&&d<=59?d:0;if("-"===l){r+=h;a+=d}else if("+"===l){r-=h;a-=d}return new Date(Date.UTC(i,s,n,r,a,o))}}function getXfaPageViewport(t,{scale:e=1,rotation:i=0}){const{width:s,height:n}=t.attributes.style,r=[0,0,parseInt(s,10),parseInt(n,10)];return new PageViewport({viewBox:r,userUnit:1,scale:e,rotation:i})}function getRGBA(t){if(t.startsWith("#")){const e=t.slice(1);return[parseInt(e.slice(0,2),16),parseInt(e.slice(2,4),16),parseInt(e.slice(4,6),16),e.length>=8?parseInt(e.slice(6,8),16)/255:1]}if(t.startsWith("rgb(")){const[e,i,s]=t.slice(4,-1).split(",").map(t=>parseInt(t,10));return[e,i,s,1]}if(t.startsWith("rgba(")){const e=t.slice(5,-1).split(",");return[parseInt(e[0],10),parseInt(e[1],10),parseInt(e[2],10),parseFloat(e[3])]}const e=t.match(/^color\(srgb\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)(?:\s*\/\s*([\d.]+|none))?\)$/);return e?[Math.round(255*parseFloat(e[1])),Math.round(255*parseFloat(e[2])),Math.round(255*parseFloat(e[3])),void 0!==e[4]&&"none"!==e[4]?parseFloat(e[4]):1]:null}function getRGB(t){const e=getRGBA(t);if(!e){warn(`Not a valid color format: "${t}"`);return[0,0,0]}return e.slice(0,3)}function getCurrentTransform(t){const{a:e,b:i,c:s,d:n,e:r,f:a}=t.getTransform();return[e,i,s,n,r,a]}function getCurrentTransformInverse(t){const{a:e,b:i,c:s,d:n,e:r,f:a}=t.getTransform().invertSelf();return[e,i,s,n,r,a]}function setLayerDimensions(t,e,i=!1,s=!0){if(e instanceof PageViewport){const{pageWidth:s,pageHeight:n}=e.rawDims,{style:r}=t,a=FeatureTest.isCSSRoundSupported,o=`var(--total-scale-factor) * ${s}px`,l=`var(--total-scale-factor) * ${n}px`,h=a?`round(down, ${o}, var(--scale-round-x))`:`calc(${o})`,d=a?`round(down, ${l}, var(--scale-round-y))`:`calc(${l})`;if(i&&e.rotation%180!=0){r.width=d;r.height=h}else{r.width=h;r.height=d}}s&&t.setAttribute("data-main-rotation",e.rotation)}class OutputScale{constructor(){const{pixelRatio:t}=OutputScale;this.sx=t;this.sy=t}get scaled(){return 1!==this.sx||1!==this.sy}get symmetric(){return this.sx===this.sy}limitCanvas(t,e,i,s,n=-1){let r=1/0,a=1/0,o=1/0;(i=OutputScale.capPixels(i,n))>0&&(r=Math.sqrt(i/(t*e)));if(-1!==s){a=s/t;o=s/e}const l=Math.min(r,a,o);if(this.sx>l||this.sy>l){this.sx=l;this.sy=l;return!0}return!1}static get pixelRatio(){return globalThis.devicePixelRatio||1}static capPixels(t,e){if(e>=0){const i=Math.ceil(window.screen.availWidth*window.screen.availHeight*this.pixelRatio**2*(1+e/100));return t>0?Math.min(t,i):i}return t}}const j=["image/apng","image/avif","image/bmp","image/gif","image/jpeg","image/png","image/svg+xml","image/webp","image/x-icon"];class ColorScheme{static get isDarkMode(){return shadow(this,"isDarkMode",!!window?.matchMedia?.("(prefers-color-scheme: dark)").matches)}}class CSSConstants{static get commentForegroundColor(){const t=document.createElement("span");t.classList.add("comment","sidebar");const{style:e}=t;e.width=e.height="0";e.display="none";e.color="var(--comment-fg-color)";document.body.append(t);const{color:i}=window.getComputedStyle(t);t.remove();return shadow(this,"commentForegroundColor",getRGB(i))}}function applyOpacity(t,e){const i=255*(1-(e=MathClamp(e??1,0,1)));return t.map(t=>Math.round(t*e+i))}function RGBToHSL(t,e){const i=t[0]/255,s=t[1]/255,n=t[2]/255,r=Math.max(i,s,n),a=Math.min(i,s,n),o=(r+a)/2;if(r===a)e[0]=e[1]=0;else{const t=r-a;e[1]=o<.5?t/(r+a):t/(2-r-a);switch(r){case i:e[0]=60*((s-n)/t+(sn?(s+.05)/(n+.05):(n+.05)/(s+.05)}const $=new Map;function findContrastColor(t,e){const i=t[0]+256*t[1]+65536*t[2]+16777216*e[0]+4294967296*e[1]+1099511627776*e[2];let s=$.get(i);if(s)return s;const n=new Float32Array(9),r=n.subarray(0,3),a=n.subarray(3,6);RGBToHSL(t,a);const o=n.subarray(6,9);RGBToHSL(e,o);const l=o[2]<.5,h=l?12:4.5;a[2]=l?Math.sqrt(a[2]):1-Math.sqrt(1-a[2]);if(contrastRatio(a,o,r)i;){const i=a[2]=(t+e)/2;l===contrastRatio(a,o,r){e.delete()},{signal:e._signal});this.#o.append(i)}get#b(){const t=document.createElement("div");t.className="divider";return t}async addAltText(t){const e=await t.render();this.#f(e);this.#o.append(e,this.#b);this.#l=t}addComment(t,e=null){if(this.#h)return;const i=t.renderForToolbar();if(!i)return;this.#f(i);const s=this.#d=this.#b;if(e){this.#o.insertBefore(i,e);this.#o.insertBefore(s,e)}else this.#o.append(i,s);this.#h=t;t.toolbar=this}addColorPicker(t){if(this.#r)return;this.#r=t;const e=t.renderButton();this.#f(e);this.#o.append(e,this.#b)}async addEditSignatureButton(t){const e=this.#c=await t.renderEditButton(this.#a);this.#f(e);this.#o.append(e,this.#b)}removeButton(t){if("comment"===t){this.#h?.removeToolbarCommentButton();this.#h=null;this.#d?.remove();this.#d=null}}async addButton(t,e){switch(t){case"colorPicker":e&&this.addColorPicker(e);break;case"altText":e&&await this.addAltText(e);break;case"editSignature":e&&await this.addEditSignatureButton(e);break;case"delete":this.addDeleteButton();break;case"comment":e&&this.addComment(e)}}async addButtonBefore(t,e,i){if(!e&&"comment"===t)return;const s=this.#o.querySelector(i);s&&"comment"===t&&this.addComment(e,s)}updateEditSignatureButton(t){this.#c&&(this.#c.title=t)}remove(){this.#n.remove();this.#r?.destroy();this.#r=null}}class FloatingToolbar{#o=null;#n=null;#y;constructor(t){this.#y=t}#A(){const t=this.#n=document.createElement("div");t.className="editToolbar";t.setAttribute("role","toolbar");const e=this.#y._signal;e instanceof AbortSignal&&!e.aborted&&t.addEventListener("contextmenu",noContextMenu,{signal:e});const i=this.#o=document.createElement("div");i.className="buttons";t.append(i);this.#y.hasCommentManager()&&this.#v("commentButton","pdfjs-comment-floating-button","pdfjs-comment-floating-button-label",()=>{this.#y.commentSelection("floating_button")});this.#v("highlightButton","pdfjs-highlight-floating-button1","pdfjs-highlight-floating-button-label",()=>{this.#y.highlightSelection("floating_button")});return t}#w(t,e){let i=0,s=0;for(const n of t){const t=n.y+n.height;if(ti){s=r;i=t}else e?r>s&&(s=r):r=1}static clearPointerType(){CurrentPointers.#T=null}static clearPointerIds(){CurrentPointers.#x=NaN;CurrentPointers.#C=null}static clearTimeStamp(){CurrentPointers.#E=NaN}}class IdManager{#S=0;get id(){return`${g}${this.#S++}`}}class ImageManager{#_=getUuid();#S=0;#P=null;static get _isSVGFittingCanvas(){const t=new OffscreenCanvas(1,3).getContext("2d",{willReadFrequently:!0}),e=new Image;e.src='data:image/svg+xml;charset=UTF-8,';return shadow(this,"_isSVGFittingCanvas",e.decode().then(()=>{t.drawImage(e,0,0,1,1,0,0,1,3);return 0===new Uint32Array(t.getImageData(0,0,1,1).data.buffer)[0]}))}async#D(t,e){this.#P||=new Map;let i=this.#P.get(t);if(null===i)return null;if(i?.bitmap){i.refCounter+=1;return i}try{i||={bitmap:null,id:`image_${this.#_}_${this.#S++}`,refCounter:0,isSvg:!1};let t;if("string"==typeof e){i.url=e;t=await fetchData(e,"blob")}else e instanceof File?t=i.file=e:e instanceof Blob&&(t=e);if("image/svg+xml"===t.type){const e=ImageManager._isSVGFittingCanvas,s=new FileReader,n=new Image,r=new Promise((t,r)=>{n.onload=()=>{i.bitmap=n;i.isSvg=!0;t()};s.onload=async()=>{const t=i.svgUrl=s.result;n.src=await e?`${t}#svgView(preserveAspectRatio(none))`:t};n.onerror=s.onerror=r});s.readAsDataURL(t);await r}else i.bitmap=await createImageBitmap(t);i.refCounter=1}catch(t){warn(t);i=null}this.#P.set(t,i);i&&this.#P.set(i.id,i);return i}async getFromFile(t){const{lastModified:e,name:i,size:s,type:n}=t;return this.#D(`${e}_${i}_${s}_${n}`,t)}async getFromUrl(t){return this.#D(t,t)}async getFromBlob(t,e){const i=await e;return this.#D(t,i)}async getFromId(t){this.#P||=new Map;const e=this.#P.get(t);if(!e)return null;if(e.bitmap){e.refCounter+=1;return e}if(e.file)return this.getFromFile(e.file);if(e.blobPromise){const{blobPromise:t}=e;delete e.blobPromise;return this.getFromBlob(e.id,t)}return this.getFromUrl(e.url)}getFromCanvas(t,e){this.#P||=new Map;let i=this.#P.get(t);if(i?.bitmap){i.refCounter+=1;return i}const s=new OffscreenCanvas(e.width,e.height);s.getContext("2d").drawImage(e,0,0);i={bitmap:s.transferToImageBitmap(),id:`image_${this.#_}_${this.#S++}`,refCounter:1,isSvg:!1};this.#P.set(t,i);this.#P.set(i.id,i);return i}getSvgUrl(t){const e=this.#P.get(t);return e?.isSvg?e.svgUrl:null}deleteId(t){this.#P||=new Map;const e=this.#P.get(t);if(!e)return;e.refCounter-=1;if(0!==e.refCounter)return;const{bitmap:i}=e;if(!e.url&&!e.file){const t=new OffscreenCanvas(i.width,i.height);t.getContext("bitmaprenderer").transferFromImageBitmap(i);e.blobPromise=t.convertToBlob()}i.close?.();e.bitmap=null}isValidId(t){return t.startsWith(`image_${this.#_}_`)}}class CommandManager{#M=[];#k=!1;#I;#F=-1;constructor(t=128){this.#I=t}add({cmd:t,undo:e,post:i,mustExec:s,type:n=NaN,overwriteIfSameType:r=!1,keepUndo:a=!1}){s&&t();if(this.#k)return;const o={cmd:t,undo:e,post:i,type:n};if(-1===this.#F){this.#M.length>0&&(this.#M.length=0);this.#F=0;this.#M.push(o);return}if(r&&this.#M[this.#F].type===n){a&&(o.undo=this.#M[this.#F].undo);this.#M[this.#F]=o;return}const l=this.#F+1;if(l===this.#I)this.#M.splice(0,1);else{this.#F=l;l=0;e--)if(this.#M[e].type!==t){this.#M.splice(e+1,this.#F-e);this.#F=e;return}this.#M.length=0;this.#F=-1}}destroy(){this.#M=null}}class KeyboardManager{constructor(t){this.buffer=[];this.callbacks=new Map;this.allKeys=new Set;const{isMac:e}=FeatureTest.platform;for(const[i,s,n={}]of t)for(const t of i){const i=t.startsWith("mac+");if(e&&i){this.callbacks.set(t.slice(4),{callback:s,options:n});this.allKeys.add(t.split("+").at(-1))}else if(!e&&!i){this.callbacks.set(t,{callback:s,options:n});this.allKeys.add(t.split("+").at(-1))}}}#B(t){t.altKey&&this.buffer.push("alt");t.ctrlKey&&this.buffer.push("ctrl");t.metaKey&&this.buffer.push("meta");t.shiftKey&&this.buffer.push("shift");this.buffer.push(t.key);const e=this.buffer.join("+");this.buffer.length=0;return e}exec(t,e){if(!this.allKeys.has(e.key))return;const i=this.callbacks.get(this.#B(e));if(!i)return;const{callback:s,options:{bubbles:n=!1,args:r=[],checker:a=null}}=i;if(!a||a(t,e)){s.bind(t,...r,e)();n||stopEvent(e)}}}class ColorManager{static _colorsMapping=new Map([["CanvasText",[0,0,0]],["Canvas",[255,255,255]]]);get _colors(){const t=new Map([["CanvasText",null],["Canvas",null]]);!function getColorValues(t){const e=document.createElement("span");e.style.visibility="hidden";e.style.colorScheme="only light";document.body.append(e);for(const i of t.keys()){e.style.color=i;const s=window.getComputedStyle(e).color;t.set(i,getRGB(s))}e.remove()}(t);return shadow(this,"_colors",t)}convert(t){const e=getRGB(t);if(!window.matchMedia("(forced-colors: active)").matches)return e;for(const[t,i]of this._colors)if(i.every((t,i)=>t===e[i]))return ColorManager._colorsMapping.get(t);return e}getHexCode(t){const e=this._colors.get(t);return e?Util.makeHexColor(...e):t}}class AnnotationEditorUIManager{#R=new AbortController;#O=null;#L=null;#N=new Map;#U=new Map;#H=null;#z=null;#G=null;#W=null;#V=new CommandManager;#j=null;#$=null;#X=null;#q=0;#K=new Set;#Y=null;#Q=null;#J=new Set;_editorUndoBar=null;#Z=!1;#tt=!1;#et=!1;#it=null;#st=null;#nt=null;#rt=null;#at=!1;#ot=null;#lt=new IdManager;#ht=!1;#dt=!1;#ct=!1;#ut=null;#pt=null;#gt=null;#mt=null;#ft=null;#bt=m.NONE;#yt=new Set;#At=null;#vt=null;#wt=null;#xt=null;#Ct=null;#Et={isEditing:!1,isEmpty:!0,hasSomethingToUndo:!1,hasSomethingToRedo:!1,hasSelectedEditor:!1,hasSelectedText:!1};#Tt=[0,0];#St=null;#_t=null;#Pt=null;#Dt=null;#Mt=null;static TRANSLATE_SMALL=1;static TRANSLATE_BIG=10;static get _keyboardManager(){const t=AnnotationEditorUIManager.prototype,arrowChecker=t=>t.#_t.contains(document.activeElement)&&"BUTTON"!==document.activeElement.tagName&&t.hasSomethingToControl(),textInputChecker=(t,{target:e})=>{if(e instanceof HTMLInputElement){const{type:t}=e;return"text"!==t&&"number"!==t}return!0},e=this.TRANSLATE_SMALL,i=this.TRANSLATE_BIG;return shadow(this,"_keyboardManager",new KeyboardManager([[["ctrl+a","mac+meta+a"],t.selectAll,{checker:textInputChecker}],[["ctrl+z","mac+meta+z"],t.undo,{checker:textInputChecker}],[["ctrl+y","ctrl+shift+z","mac+meta+shift+z","ctrl+shift+Z","mac+meta+shift+Z"],t.redo,{checker:textInputChecker}],[["Backspace","alt+Backspace","ctrl+Backspace","shift+Backspace","mac+Backspace","mac+alt+Backspace","mac+ctrl+Backspace","Delete","ctrl+Delete","shift+Delete","mac+Delete"],t.delete,{checker:textInputChecker}],[["Enter","mac+Enter"],t.addNewEditorFromKeyboard,{checker:(t,{target:e})=>!(e instanceof HTMLButtonElement)&&t.#_t.contains(e)&&!t.isEnterHandled}],[[" ","mac+ "],t.addNewEditorFromKeyboard,{checker:(t,{target:e})=>!(e instanceof HTMLButtonElement)&&t.#_t.contains(document.activeElement)}],[["Escape","mac+Escape"],t.unselectAll],[["ArrowLeft","mac+ArrowLeft"],t.translateSelectedEditors,{args:[-e,0],checker:arrowChecker}],[["ctrl+ArrowLeft","mac+shift+ArrowLeft"],t.translateSelectedEditors,{args:[-i,0],checker:arrowChecker}],[["ArrowRight","mac+ArrowRight"],t.translateSelectedEditors,{args:[e,0],checker:arrowChecker}],[["ctrl+ArrowRight","mac+shift+ArrowRight"],t.translateSelectedEditors,{args:[i,0],checker:arrowChecker}],[["ArrowUp","mac+ArrowUp"],t.translateSelectedEditors,{args:[0,-e],checker:arrowChecker}],[["ctrl+ArrowUp","mac+shift+ArrowUp"],t.translateSelectedEditors,{args:[0,-i],checker:arrowChecker}],[["ArrowDown","mac+ArrowDown"],t.translateSelectedEditors,{args:[0,e],checker:arrowChecker}],[["ctrl+ArrowDown","mac+shift+ArrowDown"],t.translateSelectedEditors,{args:[0,i],checker:arrowChecker}]]))}constructor(t,e,i,s,n,r,a,o,l,h,d,c,u,p,g,m){const f=this._signal=this.#R.signal;this.#_t=t;this.#Pt=e;this.#Dt=i;this.#z=s;this.#j=n;this.#vt=r;this.#Ct=o;this._eventBus=a;a._on("editingaction",this.onEditingAction.bind(this),{signal:f});a._on("pagechanging",this.onPageChanging.bind(this),{signal:f});a._on("scalechanging",this.onScaleChanging.bind(this),{signal:f});a._on("rotationchanging",this.onRotationChanging.bind(this),{signal:f});a._on("setpreference",this.onSetPreference.bind(this),{signal:f});a._on("switchannotationeditorparams",t=>this.updateParams(t.type,t.value),{signal:f});window.addEventListener("pointerdown",()=>{this.#dt=!0},{capture:!0,signal:f});window.addEventListener("pointerup",()=>{this.#dt=!1},{capture:!0,signal:f});window.addEventListener("beforeunload",this.#kt.bind(this),{capture:!0,signal:f});this.#It();this.#Ft();this.#Bt();this.#G=o.annotationStorage;this.#it=o.filterFactory;this.#wt=l;this.#rt=h||null;this.#Z=d;this.#tt=c;this.#et=u;this.#ft=p||null;this.viewParameters={realScale:PixelsPerInch.PDF_TO_CSS_UNITS,rotation:0};this.isShiftKeyDown=!1;this._editorUndoBar=g||null;this._supportsPinchToZoom=!1!==m;n?.setSidebarUiManager(this)}destroy(){this.#Mt?.resolve();this.#Mt=null;this.#R?.abort();this.#R=null;this._signal=null;for(const t of this.#U.values())t.destroy();this.#U.clear();this.#N.clear();this.#J.clear();this.#mt?.clear();this.#O=null;this.#yt.clear();this.#V.destroy();this.#z?.destroy();this.#j?.destroy();this.#vt?.destroy();this.#ot?.hide();this.#ot=null;this.#gt?.destroy();this.#gt=null;this.#L=null;if(this.#st){clearTimeout(this.#st);this.#st=null}if(this.#St){clearTimeout(this.#St);this.#St=null}this._editorUndoBar?.destroy();this.#Ct=null}combinedSignal(t){return AbortSignal.any([this._signal,t.signal])}get mlManager(){return this.#ft}get useNewAltTextFlow(){return this.#tt}get useNewAltTextWhenAddingImage(){return this.#et}get hcmFilter(){return shadow(this,"hcmFilter",this.#wt?this.#it.addHCMFilter(this.#wt.foreground,this.#wt.background):"none")}get direction(){return shadow(this,"direction",getComputedStyle(this.#_t).direction)}get _highlightColors(){return shadow(this,"_highlightColors",this.#rt?new Map(this.#rt.split(",").map(t=>{(t=t.split("=").map(t=>t.trim()))[1]=t[1].toUpperCase();return t})):null)}get highlightColors(){const{_highlightColors:t}=this;if(!t)return shadow(this,"highlightColors",null);const e=new Map,i=!!this.#wt;for(const[s,n]of t){const t=s.endsWith("_HCM");i&&t?e.set(s.replace("_HCM",""),n):i||t||e.set(s,n)}return shadow(this,"highlightColors",e)}get highlightColorNames(){return shadow(this,"highlightColorNames",this.highlightColors?new Map(Array.from(this.highlightColors,t=>t.reverse())):null)}getNonHCMColor(t){if(!this._highlightColors)return t;const e=this.highlightColorNames.get(t);return this._highlightColors.get(e)||t}getNonHCMColorName(t){return this.highlightColorNames.get(t)||t}setCurrentDrawingSession(t){if(t){this.unselectAll();this.disableUserSelect(!0)}else this.disableUserSelect(!1);this.#X=t}setMainHighlightColorPicker(t){this.#gt=t}editAltText(t,e=!1){this.#z?.editAltText(this,t,e)}hasCommentManager(){return!!this.#j}editComment(t,e,i,s){this.#j?.showDialog(this,t,e,i,s)}selectComment(t,e){const i=this.#U.get(t),s=i?.getEditorByUID(e);s?.toggleComment(!0,!0)}updateComment(t){this.#j?.updateComment(t.getData())}updatePopupColor(t){this.#j?.updatePopupColor(t)}removeComment(t){this.#j?.removeComments([t.uid])}deleteComment(t,e){const undo=()=>{t.comment=e};this.addCommands({cmd:()=>{this._editorUndoBar?.show(undo,"comment");this.toggleComment(null);t.comment=null},undo,mustExec:!0})}toggleComment(t,e,i=void 0){this.#j?.toggleCommentPopup(t,e,i)}makeCommentColor(t,e){return t&&this.#j?.makeCommentColor(t,e)||null}getCommentDialogElement(){return this.#j?.dialogElement||null}async waitForEditorsRendered(t){if(this.#U.has(t-1))return;const{resolve:e,promise:i}=Promise.withResolvers(),onEditorsRendered=i=>{if(i.pageNumber===t){this._eventBus._off("editorsrendered",onEditorsRendered);e()}};this._eventBus.on("editorsrendered",onEditorsRendered);await i}getSignature(t){this.#vt?.getSignature({uiManager:this,editor:t})}get signatureManager(){return this.#vt}switchToMode(t,e){this._eventBus.on("annotationeditormodechanged",e,{once:!0,signal:this._signal});this._eventBus.dispatch("showannotationeditorui",{source:this,mode:t})}setPreference(t,e){this._eventBus.dispatch("setpreference",{source:this,name:t,value:e})}onSetPreference({name:t,value:e}){if("enableNewAltTextWhenAddingImage"===t)this.#et=e}onPageChanging({pageNumber:t}){this.#q=t-1}deletePage(t){for(const e of this.getEditors(t))e.remove();this.#U.delete(t);this.#q===t&&(this.#q=0)}focusMainContainer(){this.#_t.focus()}findParent(t,e){for(const i of this.#U.values()){const{x:s,y:n,width:r,height:a}=i.div.getBoundingClientRect();if(t>=s&&t<=s+r&&e>=n&&e<=n+a)return i}return null}disableUserSelect(t=!1){this.#Pt.classList.toggle("noUserSelect",t)}addShouldRescale(t){this.#J.add(t)}removeShouldRescale(t){this.#J.delete(t)}onScaleChanging({scale:t}){this.commitOrRemove();this.viewParameters.realScale=t*PixelsPerInch.PDF_TO_CSS_UNITS;for(const t of this.#J)t.onScaleChanging();this.#X?.onScaleChanging()}onRotationChanging({pagesRotation:t}){this.commitOrRemove();this.viewParameters.rotation=t}#Rt({anchorNode:t}){return t.nodeType===Node.TEXT_NODE?t.parentElement:t}#Ot(t){const{currentLayer:e}=this;if(e.hasTextLayer(t))return e;for(const e of this.#U.values())if(e.hasTextLayer(t))return e;return null}highlightSelection(t="",e=!1){const i=document.getSelection();if(!i||i.isCollapsed)return;const{anchorNode:s,anchorOffset:n,focusNode:r,focusOffset:a}=i,o=i.toString(),l=this.#Rt(i).closest(".textLayer"),h=this.getSelectionBoxes(l);if(!h)return;i.empty();const d=this.#Ot(l),c=this.#bt===m.NONE,callback=()=>{const i=d?.createAndAddNewEditor({x:0,y:0},!1,{methodOfCreation:t,boxes:h,anchorNode:s,anchorOffset:n,focusNode:r,focusOffset:a,text:o});c&&this.showAllEditors("highlight",!0,!0);e&&i?.editComment()};c?this.switchToMode(m.HIGHLIGHT,callback):callback()}commentSelection(t=""){this.highlightSelection(t,!0)}#kt(t){this.commitOrRemove();this.currentLayer?.endDrawingSession(!1)}#Lt(){const t=document.getSelection();if(!t||t.isCollapsed)return;const e=this.#Rt(t).closest(".textLayer"),i=this.getSelectionBoxes(e);if(i){this.#ot||=new FloatingToolbar(this);this.#ot.show(e,i,"ltr"===this.direction)}}getAndRemoveDataFromAnnotationStorage(t){if(!this.#G)return null;const e=`${g}${t}`,i=this.#G.getRawValue(e);i&&this.#G.remove(e);return i}addToAnnotationStorage(t){t.isEmpty()||!this.#G||this.#G.has(t.id)||this.#G.setValue(t.id,t)}a11yAlert(t,e=null){const i=this.#Dt;if(i){i.setAttribute("data-l10n-id",t);e?i.setAttribute("data-l10n-args",JSON.stringify(e)):i.removeAttribute("data-l10n-args")}}#Nt(){const t=document.getSelection();if(!t||t.isCollapsed){if(this.#At){this.#ot?.hide();this.#At=null;this.#Ut({hasSelectedText:!1})}return}const{anchorNode:e}=t;if(e===this.#At)return;const i=this.#Rt(t).closest(".textLayer");if(i){this.#ot?.hide();this.#At=e;this.#Ut({hasSelectedText:!0});if(this.#bt===m.HIGHLIGHT||this.#bt===m.NONE){this.#bt===m.HIGHLIGHT&&this.showAllEditors("highlight",!0,!0);this.#at=this.isShiftKeyDown;if(!this.isShiftKeyDown){const t=this.#bt===m.HIGHLIGHT?this.#Ot(i):null;t?.toggleDrawing();if(this.#dt){const e=new AbortController,i=this.combinedSignal(e),pointerup=i=>{if("pointerup"!==i.type||0===i.button){e.abort();t?.toggleDrawing(!0);"pointerup"===i.type&&this.#Ht("main_toolbar")}};window.addEventListener("pointerup",pointerup,{signal:i});window.addEventListener("blur",pointerup,{signal:i})}else{t?.toggleDrawing(!0);this.#Ht("main_toolbar")}}}}else if(this.#At){this.#ot?.hide();this.#At=null;this.#Ut({hasSelectedText:!1})}}#Ht(t=""){this.#bt===m.HIGHLIGHT?this.highlightSelection(t):this.#Z&&this.#Lt()}#It(){document.addEventListener("selectionchange",this.#Nt.bind(this),{signal:this._signal})}#zt(){if(this.#nt)return;this.#nt=new AbortController;const t=this.combinedSignal(this.#nt);window.addEventListener("focus",this.focus.bind(this),{signal:t});window.addEventListener("blur",this.blur.bind(this),{signal:t})}#Gt(){this.#nt?.abort();this.#nt=null}blur(){this.isShiftKeyDown=!1;if(this.#at){this.#at=!1;this.#Ht("main_toolbar")}if(!this.hasSelection)return;const{activeElement:t}=document;for(const e of this.#yt)if(e.div.contains(t)){this.#pt=[e,t];e._focusEventsAllowed=!1;break}}focus(){if(!this.#pt)return;const[t,e]=this.#pt;this.#pt=null;e.addEventListener("focusin",()=>{t._focusEventsAllowed=!0},{once:!0,signal:this._signal});e.focus()}#Bt(){if(this.#ut)return;this.#ut=new AbortController;const t=this.combinedSignal(this.#ut);window.addEventListener("keydown",this.keydown.bind(this),{signal:t});window.addEventListener("keyup",this.keyup.bind(this),{signal:t})}#Wt(){this.#ut?.abort();this.#ut=null}#Vt(){if(this.#$)return;this.#$=new AbortController;const t=this.combinedSignal(this.#$);document.addEventListener("copy",this.copy.bind(this),{signal:t});document.addEventListener("cut",this.cut.bind(this),{signal:t});document.addEventListener("paste",this.paste.bind(this),{signal:t})}#jt(){this.#$?.abort();this.#$=null}#Ft(){const t=this._signal;document.addEventListener("dragover",this.dragOver.bind(this),{signal:t});document.addEventListener("drop",this.drop.bind(this),{signal:t})}addEditListeners(){this.#Bt();this.setEditingState(!0)}removeEditListeners(){this.#Wt();this.setEditingState(!1)}dragOver(t){for(const{type:e}of t.dataTransfer.items)for(const i of this.#Q)if(i.isHandlingMimeForPasting(e)){t.dataTransfer.dropEffect="copy";t.preventDefault();return}}drop(t){for(const e of t.dataTransfer.items)for(const i of this.#Q)if(i.isHandlingMimeForPasting(e.type)){i.paste(e,this.currentLayer);t.preventDefault();return}}copy(t){t.preventDefault();this.#O?.commitOrRemove();if(!this.hasSelection)return;const e=[];for(const t of this.#yt){const i=t.serialize(!0);i&&e.push(i)}0!==e.length&&t.clipboardData.setData("application/pdfjs",JSON.stringify(e))}cut(t){this.copy(t);this.delete()}async paste(t){t.preventDefault();const{clipboardData:e}=t;for(const t of e.items)for(const e of this.#Q)if(e.isHandlingMimeForPasting(t.type)){e.paste(t,this.currentLayer);return}let i=e.getData("application/pdfjs");if(!i)return;try{i=JSON.parse(i)}catch(t){warn(`paste: "${t.message}".`);return}if(!Array.isArray(i))return;this.unselectAll();const s=this.currentLayer;try{const t=[];for(const e of i){const i=await s.deserialize(e);if(!i)return;t.push(i)}const cmd=()=>{for(const e of t)this.#$t(e);this.#Xt(t)},undo=()=>{for(const e of t)e.remove()};this.addCommands({cmd,undo,mustExec:!0})}catch(t){warn(`paste: "${t.message}".`)}}keydown(t){this.isShiftKeyDown||"Shift"!==t.key||(this.isShiftKeyDown=!0);this.#bt===m.NONE||this.isEditorHandlingKeyboard||AnnotationEditorUIManager._keyboardManager.exec(this,t)}keyup(t){if(this.isShiftKeyDown&&"Shift"===t.key){this.isShiftKeyDown=!1;if(this.#at){this.#at=!1;this.#Ht("main_toolbar")}}}onEditingAction({name:t}){switch(t){case"undo":case"redo":case"delete":case"selectAll":this[t]();break;case"highlightSelection":this.highlightSelection("context_menu");break;case"commentSelection":this.commentSelection("context_menu")}}updatePageIndex(t,e){for(const i of this.getEditors(t))i.pageIndex=e;const i=this.#H.get(t);if(i){i.pageIndex=e;this.#U.set(e,i);this.#ht?i.enable():i.disable()}}startUpdatePages(){this.#H=new Map(this.#U);this.#U.clear()}endUpdatePages(){this.#H=null}clonePage(t,e){for(const i of this.getEditors(t)){const t=i.serialize(i.mode!==m.HIGHLIGHT);if(t){t.pageIndex=e;t.id=this.getId();t.isClone=!0;delete t.popupRef;this.#G.setValue(t.id,t)}}}findClonesForPage(t){const e=[],{pageIndex:i}=t;for(const[s,n]of this.#G)if(n.pageIndex===i&&n.isClone){this.#G.remove(s);e.push(t.deserialize(n).then(e=>{if(e){e.isClone=!0;t.addOrRebuild(e)}}))}return Promise.all(e)}#Ut(t){if(Object.entries(t).some(([t,e])=>this.#Et[t]!==e)){this._eventBus.dispatch("editingstateschanged",{source:this,details:Object.assign(this.#Et,t)});this.#bt===m.HIGHLIGHT&&!1===t.hasSelectedEditor&&this.#qt([[f.HIGHLIGHT_FREE,!0]])}}#qt(t){this._eventBus.dispatch("annotationeditorparamschanged",{source:this,details:t})}setEditingState(t){if(t){this.#zt();this.#Vt();this.#Ut({isEditing:this.#bt!==m.NONE,isEmpty:this.#Kt(),hasSomethingToUndo:this.#V.hasSomethingToUndo(),hasSomethingToRedo:this.#V.hasSomethingToRedo(),hasSelectedEditor:!1})}else{this.#Gt();this.#jt();this.#Ut({isEditing:!1});this.disableUserSelect(!1)}}registerEditorTypes(t){if(!this.#Q){this.#Q=t;for(const t of this.#Q)this.#qt(t.defaultPropertiesToUpdate)}}getId(){return this.#lt.id}get currentLayer(){return this.#U.get(this.#q)}getLayer(t){return this.#U.get(t)}get currentPageIndex(){return this.#q}addLayer(t){this.#U.set(t.pageIndex,t);this.#ht?t.enable():t.disable()}removeLayer(t){this.#U.delete(t.pageIndex)}async updateMode(t,e=null,i=!1,s=!1,n=!1,r=!1){if(this.#bt!==t){if(this.#Mt){await this.#Mt.promise;if(!this.#Mt)return}this.#Mt=Promise.withResolvers();this.#X?.commitOrRemove();this.#bt===m.POPUP&&this.#j?.hideSidebar();this.#j?.destroyPopup();this.#bt=t;if(t!==m.NONE){for(const t of this.#N.values())t.addStandaloneCommentButton();t===m.SIGNATURE&&await(this.#vt?.loadSignatures());i&&CurrentPointers.clearPointerType();this.setEditingState(!0);await this.#Yt();this.unselectAll();for(const e of this.#U.values())e.updateMode(t);if(t===m.POPUP){this.#L||=await this.#Ct.getAnnotationsByType(new Set(this.#Q.map(t=>t._editorType)));const t=new Set,e=[];for(const i of this.#N.values()){const{annotationElementId:s,hasComment:n,deleted:r}=i;s&&t.add(s);n&&!r&&e.push(i.getData())}for(const i of this.#L){const{id:s,popupRef:n,contentsObj:r}=i;n&&r?.str&&!t.has(s)&&!this.#K.has(s)&&e.push(i)}this.#j?.showSidebar(e)}if(e){for(const t of this.#N.values())if(t.uid===e){this.setSelected(t);r?t.editComment():n?t.enterInEditMode():t.focus()}else t.unselect();this.#Mt.resolve()}else{s&&this.addNewEditorFromKeyboard();this.#Mt.resolve()}}else{this.setEditingState(!1);this.#Qt();for(const t of this.#N.values())t.hideStandaloneCommentButton();this._editorUndoBar?.hide();this.toggleComment(null);this.#Mt.resolve()}}}addNewEditorFromKeyboard(){this.currentLayer.canCreateNewEmptyEditor()&&this.currentLayer.addNewEditor()}updateToolbar(t){t.mode!==this.#bt&&this._eventBus.dispatch("switchannotationeditormode",{source:this,...t})}updateParams(t,e){if(this.#Q){switch(t){case f.CREATE:this.currentLayer.addNewEditor(e);return;case f.HIGHLIGHT_SHOW_ALL:this._eventBus.dispatch("reporttelemetry",{source:this,details:{type:"editing",data:{type:"highlight",action:"toggle_visibility"}}});(this.#xt||=new Map).set(t,e);this.showAllEditors("highlight",e)}if(this.hasSelection)for(const i of this.#yt)i.updateParams(t,e);else for(const i of this.#Q)i.updateDefaultParams(t,e)}}showAllEditors(t,e,i=!1){for(const i of this.#N.values())i.editorType===t&&i.show(e);(this.#xt?.get(f.HIGHLIGHT_SHOW_ALL)??!0)!==e&&this.#qt([[f.HIGHLIGHT_SHOW_ALL,e]])}enableWaiting(t=!1){if(this.#ct!==t){this.#ct=t;for(const e of this.#U.values()){t?e.disableClick():e.enableClick();e.div.classList.toggle("waiting",t)}}}async#Yt(){if(!this.#ht){this.#ht=!0;const t=[];for(const e of this.#U.values())t.push(e.enable());await Promise.all(t);for(const t of this.#N.values())t.enable()}}#Qt(){this.unselectAll();if(this.#ht){this.#ht=!1;for(const t of this.#U.values())t.disable();for(const t of this.#N.values())t.disable()}}*getEditors(t){for(const e of this.#N.values())e.pageIndex===t&&(yield e)}getEditor(t){return this.#N.get(t)}addEditor(t){this.#N.set(t.id,t)}removeEditor(t){if(t.div.contains(document.activeElement)){this.#st&&clearTimeout(this.#st);this.#st=setTimeout(()=>{this.focusMainContainer();this.#st=null},0)}this.#N.delete(t.id);t.annotationElementId&&this.#mt?.delete(t.annotationElementId);this.unselect(t);t.annotationElementId&&this.#K.has(t.annotationElementId)||this.#G?.remove(t.id)}addDeletedAnnotationElement(t){this.#K.add(t.annotationElementId);this.addChangedExistingAnnotation(t);t.deleted=!0}isDeletedAnnotationElement(t){return this.#K.has(t)}removeDeletedAnnotationElement(t){this.#K.delete(t.annotationElementId);this.removeChangedExistingAnnotation(t);t.deleted=!1}#$t(t){const e=this.#U.get(t.pageIndex);if(e)e.addOrRebuild(t);else{this.addEditor(t);this.addToAnnotationStorage(t)}}setActiveEditor(t){if(this.#O!==t){this.#O=t;t&&this.#qt(t.propertiesToUpdate)}}get#Jt(){let t=null;for(t of this.#yt);return t}updateUI(t){this.#Jt===t&&this.#qt(t.propertiesToUpdate)}updateUIForDefaultProperties(t){this.#qt(t.defaultPropertiesToUpdate)}toggleSelected(t){if(this.#yt.has(t)){this.#yt.delete(t);t.unselect();this.#Ut({hasSelectedEditor:this.hasSelection})}else{this.#yt.add(t);t.select();this.#qt(t.propertiesToUpdate);this.#Ut({hasSelectedEditor:!0})}}setSelected(t){this.updateToolbar({mode:t.mode,editId:t.uid});this.#X?.commitOrRemove();for(const e of this.#yt)e!==t&&e.unselect();this.#j?.destroyPopup();this.#yt.clear();this.#yt.add(t);t.select();this.#qt(t.propertiesToUpdate);this.#Ut({hasSelectedEditor:!0})}isSelected(t){return this.#yt.has(t)}get firstSelectedEditor(){return this.#yt.values().next().value}unselect(t){t.unselect();this.#yt.delete(t);this.#Ut({hasSelectedEditor:this.hasSelection})}get hasSelection(){return 0!==this.#yt.size}get isEnterHandled(){return 1===this.#yt.size&&this.firstSelectedEditor.isEnterHandled}undo(){this.#V.undo();this.#Ut({hasSomethingToUndo:this.#V.hasSomethingToUndo(),hasSomethingToRedo:!0,isEmpty:this.#Kt()});this._editorUndoBar?.hide()}redo(){this.#V.redo();this.#Ut({hasSomethingToUndo:!0,hasSomethingToRedo:this.#V.hasSomethingToRedo(),isEmpty:this.#Kt()})}addCommands(t){this.#V.add(t);this.#Ut({hasSomethingToUndo:!0,hasSomethingToRedo:!1,isEmpty:this.#Kt()})}cleanUndoStack(t){this.#V.cleanType(t)}#Kt(){if(0===this.#N.size)return!0;if(1===this.#N.size)for(const t of this.#N.values())return t.isEmpty();return!1}delete(){this.commitOrRemove();const t=this.currentLayer?.endDrawingSession(!0);if(!this.hasSelection&&!t)return;const e=t?[t]:[...this.#yt],undo=()=>{for(const t of e)this.#$t(t)};this.addCommands({cmd:()=>{this._editorUndoBar?.show(undo,1===e.length?e[0].editorType:e.length);for(const t of e)t.remove()},undo,mustExec:!0})}commitOrRemove(){this.#O?.commitOrRemove()}hasSomethingToControl(){return this.#O||this.hasSelection}#Xt(t){for(const t of this.#yt)t.unselect();this.#yt.clear();for(const e of t)if(!e.isEmpty()){this.#yt.add(e);e.select()}this.#Ut({hasSelectedEditor:this.hasSelection})}selectAll(){for(const t of this.#yt)t.commit();this.#Xt(this.#N.values())}unselectAll(){if(this.#O){this.#O.commitOrRemove();if(this.#bt!==m.NONE)return}if(!this.#X?.commitOrRemove()){this.#j?.destroyPopup();if(this.hasSelection){for(const t of this.#yt)t.unselect();this.#yt.clear();this.#Ut({hasSelectedEditor:!1})}}}translateSelectedEditors(t,e,i=!1){i||this.commitOrRemove();if(!this.hasSelection)return;this.#Tt[0]+=t;this.#Tt[1]+=e;const[s,n]=this.#Tt,r=[...this.#yt];this.#St&&clearTimeout(this.#St);this.#St=setTimeout(()=>{this.#St=null;this.#Tt[0]=this.#Tt[1]=0;this.addCommands({cmd:()=>{for(const t of r)if(this.#N.has(t.id)){t.translateInPage(s,n);t.translationDone()}},undo:()=>{for(const t of r)if(this.#N.has(t.id)){t.translateInPage(-s,-n);t.translationDone()}},mustExec:!1})},1e3);for(const i of r){i.translateInPage(t,e);i.translationDone()}}setUpDragSession(){if(this.hasSelection){this.disableUserSelect(!0);this.#Y=new Map;for(const t of this.#yt)this.#Y.set(t,{savedX:t.x,savedY:t.y,savedPageIndex:t.pageIndex,newX:0,newY:0,newPageIndex:-1})}}endDragSession(){if(!this.#Y)return!1;this.disableUserSelect(!1);const t=this.#Y;this.#Y=null;let e=!1;for(const[{x:i,y:s,pageIndex:n},r]of t){r.newX=i;r.newY=s;r.newPageIndex=n;e||=i!==r.savedX||s!==r.savedY||n!==r.savedPageIndex}if(!e)return!1;const move=(t,e,i,s)=>{if(this.#N.has(t.id)){const n=this.#U.get(s);if(n)t._setParentAndPosition(n,e,i);else{t.pageIndex=s;t.x=e;t.y=i}}};this.addCommands({cmd:()=>{for(const[e,{newX:i,newY:s,newPageIndex:n}]of t)move(e,i,s,n)},undo:()=>{for(const[e,{savedX:i,savedY:s,savedPageIndex:n}]of t)move(e,i,s,n)},mustExec:!0});return!0}dragSelectedEditors(t,e){if(this.#Y)for(const i of this.#Y.keys())i.drag(t,e)}rebuild(t){if(null===t.parent){const e=this.getLayer(t.pageIndex);if(e){e.changeParent(t);e.addOrRebuild(t)}else{this.addEditor(t);this.addToAnnotationStorage(t);t.rebuild()}}else t.parent.addOrRebuild(t)}get isEditorHandlingKeyboard(){return this.getActive()?.shouldGetKeyboardEvents()||1===this.#yt.size&&this.firstSelectedEditor.shouldGetKeyboardEvents()}isActive(t){return this.#O===t}getActive(){return this.#O}getMode(){return this.#bt}isEditingMode(){return this.#bt!==m.NONE}get imageManager(){return shadow(this,"imageManager",new ImageManager)}getSelectionBoxes(t){if(!t)return null;const e=document.getSelection();for(let i=0,s=e.rangeCount;i({x:(e-s)/r,y:1-(t+a-i)/n,width:o/r,height:a/n});break;case"180":a=(t,e,a,o)=>({x:1-(t+a-i)/n,y:1-(e+o-s)/r,width:a/n,height:o/r});break;case"270":a=(t,e,a,o)=>({x:1-(e+o-s)/r,y:(t-i)/n,width:o/r,height:a/n});break;default:a=(t,e,a,o)=>({x:(t-i)/n,y:(e-s)/r,width:a/n,height:o/r})}const o=[];for(let t=0,i=e.rangeCount;tt.stopPropagation(),{signal:i});const onClick=t=>{t.preventDefault();this.#a._uiManager.editAltText(this.#a);this.#le&&this.#a._reportTelemetry({action:"pdfjs.image.alt_text.image_status_label_clicked",data:{label:this.#de}})};t.addEventListener("click",onClick,{capture:!0,signal:i});t.addEventListener("keydown",e=>{if(e.target===t&&"Enter"===e.key){this.#ne=!0;onClick(e)}},{signal:i});await this.#ce();return t}get#de(){return(this.#l?"added":null===this.#l&&this.guessedText&&"review")||"missing"}finish(){if(this.#te){this.#te.focus({focusVisible:this.#ne});this.#ne=!1}}isEmpty(){return this.#le?null===this.#l:!this.#l&&!this.#Zt}hasData(){return this.#le?null!==this.#l||!!this.#ae:this.isEmpty()}get guessedText(){return this.#ae}async setGuessedText(t){if(null===this.#l){this.#ae=t;this.#oe=await AltText._l10n.get("pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer",{generatedAltText:t});this.#ce()}}toggleAltTextBadge(t=!1){if(this.#le&&!this.#l){if(!this.#re){const t=this.#re=document.createElement("div");t.className="noAltTextBadge";this.#a.div.append(t)}this.#re.classList.toggle("hidden",!t)}else{this.#re?.remove();this.#re=null}}serialize(t){let e=this.#l;t||this.#ae!==e||(e=this.#oe);return{altText:e,decorative:this.#Zt,guessedText:this.#ae,textWithDisclaimer:this.#oe}}get data(){return{altText:this.#l,decorative:this.#Zt}}set data({altText:t,decorative:e,guessedText:i,textWithDisclaimer:s,cancel:n=!1}){if(i){this.#ae=i;this.#oe=s}if(this.#l!==t||this.#Zt!==e){if(!n){this.#l=t;this.#Zt=e}this.#ce()}}toggle(t=!1){if(this.#te){if(!t&&this.#se){clearTimeout(this.#se);this.#se=null}this.#te.disabled=!t}}shown(){this.#a._reportTelemetry({action:"pdfjs.image.alt_text.image_status_label_displayed",data:{label:this.#de}})}destroy(){this.#te?.remove();this.#te=null;this.#ee=null;this.#ie=null;this.#re?.remove();this.#re=null}async#ce(){const t=this.#te;if(!t)return;if(this.#le){t.classList.toggle("done",!!this.#l);t.setAttribute("data-l10n-id",AltText.#he[this.#de]);this.#ee?.setAttribute("data-l10n-id",AltText.#he[`${this.#de}-label`]);if(!this.#l){this.#ie?.remove();return}}else{if(!this.#l&&!this.#Zt){t.classList.remove("done");this.#ie?.remove();return}t.classList.add("done");t.setAttribute("data-l10n-id","pdfjs-editor-alt-text-edit-button")}let e=this.#ie;if(!e){this.#ie=e=document.createElement("span");e.className="tooltip";e.setAttribute("role","tooltip");e.id=`alt-text-tooltip-${this.#a.id}`;const i=100,s=this.#a._uiManager._signal;s.addEventListener("abort",()=>{clearTimeout(this.#se);this.#se=null},{once:!0});t.addEventListener("mouseenter",()=>{this.#se=setTimeout(()=>{this.#se=null;this.#ie.classList.add("show");this.#a._reportTelemetry({action:"alt_text_tooltip"})},i)},{signal:s});t.addEventListener("mouseleave",()=>{if(this.#se){clearTimeout(this.#se);this.#se=null}this.#ie?.classList.remove("show")},{signal:s})}if(this.#Zt)e.setAttribute("data-l10n-id","pdfjs-editor-alt-text-decorative-tooltip");else{e.removeAttribute("data-l10n-id");e.textContent=this.#l}e.parentNode||t.append(e);const i=this.#a.getElementForAltText();i?.setAttribute("aria-describedby",e.id)}}class Comment{#ue=null;#pe=null;#ge=!1;#a=null;#me=null;#fe=null;#be=null;#ye=null;#Ae=!1;#ve=null;constructor(t){this.#a=t}renderForToolbar(){const t=this.#pe=document.createElement("button");t.className="comment";return this.#A(t,!1)}renderForStandalone(){const t=this.#ue=document.createElement("button");t.className="annotationCommentButton";const e=this.#a.commentButtonPosition;if(e){const{style:i}=t;i.insetInlineEnd=`calc(${100*("ltr"===this.#a._uiManager.direction?1-e[0]:e[0])}% - var(--comment-button-dim))`;i.top=`calc(${100*e[1]}% - var(--comment-button-dim))`;const s=this.#a.commentButtonColor;s&&(i.backgroundColor=s)}return this.#A(t,!0)}focusButton(){setTimeout(()=>{(this.#ue??this.#pe)?.focus()},0)}onUpdatedColor(){if(!this.#ue)return;const t=this.#a.commentButtonColor;t&&(this.#ue.style.backgroundColor=t);this.#a._uiManager.updatePopupColor(this.#a)}get commentButtonWidth(){return(this.#ue?.getBoundingClientRect().width??0)/this.#a.parent.boundingClientRect.width}get commentPopupPositionInLayer(){if(this.#ve)return this.#ve;if(!this.#ue)return null;const{x:t,y:e,height:i}=this.#ue.getBoundingClientRect(),{x:s,y:n,width:r,height:a}=this.#a.parent.boundingClientRect;return[(t-s)/r,(e+i-n)/a]}set commentPopupPositionInLayer(t){this.#ve=t}hasDefaultPopupPosition(){return null===this.#ve}removeStandaloneCommentButton(){this.#ue?.remove();this.#ue=null}removeToolbarCommentButton(){this.#pe?.remove();this.#pe=null}setCommentButtonStates({selected:t,hasPopup:e}){if(this.#ue){this.#ue.classList.toggle("selected",t);this.#ue.ariaExpanded=e}}#A(t,e){if(!this.#a._uiManager.hasCommentManager())return null;t.tabIndex="0";t.ariaHasPopup="dialog";if(e){t.ariaControls="commentPopup";t.setAttribute("data-l10n-id","pdfjs-show-comment-button")}else{t.ariaControlsElements=[this.#a._uiManager.getCommentDialogElement()];t.setAttribute("data-l10n-id","pdfjs-editor-add-comment-button")}const i=this.#a._uiManager._signal;if(!(i instanceof AbortSignal)||i.aborted)return t;t.addEventListener("contextmenu",noContextMenu,{signal:i});if(e){t.addEventListener("focusin",t=>{this.#a._focusEventsAllowed=!1;stopEvent(t)},{capture:!0,signal:i});t.addEventListener("focusout",t=>{this.#a._focusEventsAllowed=!0;stopEvent(t)},{capture:!0,signal:i})}t.addEventListener("pointerdown",t=>t.stopPropagation(),{signal:i});const onClick=e=>{e.preventDefault();t===this.#pe?this.edit():this.#a.toggleComment(!0)};t.addEventListener("click",onClick,{capture:!0,signal:i});t.addEventListener("keydown",e=>{if(e.target===t&&"Enter"===e.key){this.#ge=!0;onClick(e)}},{signal:i});t.addEventListener("pointerenter",()=>{this.#a.toggleComment(!1,!0)},{signal:i});t.addEventListener("pointerleave",()=>{this.#a.toggleComment(!1,!1)},{signal:i});return t}edit(t){const e=this.commentPopupPositionInLayer;let i,s;if(e)[i,s]=e;else{[i,s]=this.#a.commentButtonPosition;const{width:t,height:e,x:n,y:r}=this.#a;i=n+i*t;s=r+s*e}const n=this.#a.parent.boundingClientRect,{x:r,y:a,width:o,height:l}=n;this.#a._uiManager.editComment(this.#a,r+i*o,a+s*l,{...t,parentDimensions:n})}finish(){if(this.#pe){this.#pe.focus({focusVisible:this.#ge});this.#ge=!1}}isDeleted(){return this.#Ae||""===this.#be}isEmpty(){return null===this.#be}hasBeenEdited(){return this.isDeleted()||this.#be!==this.#me}serialize(){return this.data}get data(){return{text:this.#be,richText:this.#fe,date:this.#ye,deleted:this.isDeleted()}}set data(t){t!==this.#be&&(this.#fe=null);if(null!==t){this.#be=t;this.#ye=new Date;this.#Ae=!1}else{this.#be="";this.#Ae=!0}}restoreData({text:t,richText:e,date:i}){this.#be=t;this.#fe=e;this.#ye=i;this.#Ae=!1}setInitialText(t,e=null){this.#me=t;this.data=t;this.#ye=null;this.#fe=e}shown(){}destroy(){this.#pe?.remove();this.#pe=null;this.#ue?.remove();this.#ue=null;this.#be="";this.#fe=null;this.#ye=null;this.#a=null;this.#ge=!1;this.#Ae=!1}}class TouchManager{#_t;#we=!1;#xe=null;#Ce;#Ee;#Te;#Se;#_e=null;#Pe;#De=null;#Me;#ke=null;constructor({container:t,isPinchingDisabled:e=null,isPinchingStopped:i=null,onPinchStart:s=null,onPinching:n=null,onPinchEnd:r=null,signal:a}){this.#_t=t;this.#xe=i;this.#Ce=e;this.#Ee=s;this.#Te=n;this.#Se=r;this.#Me=new AbortController;this.#Pe=AbortSignal.any([a,this.#Me.signal]);t.addEventListener("touchstart",this.#Ie.bind(this),{passive:!1,signal:this.#Pe})}get MIN_TOUCH_DISTANCE_TO_PINCH(){return 35/OutputScale.pixelRatio}#Ie(t){if(this.#Ce?.())return;if(1===t.touches.length){if(this.#_e)return;const t=this.#_e=new AbortController,e=AbortSignal.any([this.#Pe,t.signal]),i=this.#_t,s={capture:!0,signal:e,passive:!1},cancelPointerDown=t=>{if("touch"===t.pointerType){this.#_e?.abort();this.#_e=null}};i.addEventListener("pointerdown",t=>{if("touch"===t.pointerType){stopEvent(t);cancelPointerDown(t)}},s);i.addEventListener("pointerup",cancelPointerDown,s);i.addEventListener("pointercancel",cancelPointerDown,s);return}if(!this.#ke){this.#ke=new AbortController;const t=AbortSignal.any([this.#Pe,this.#ke.signal]),e=this.#_t,i={signal:t,capture:!1,passive:!1};e.addEventListener("touchmove",this.#Fe.bind(this),i);const s=this.#Be.bind(this);e.addEventListener("touchend",s,i);e.addEventListener("touchcancel",s,i);i.capture=!0;e.addEventListener("pointerdown",stopEvent,i);e.addEventListener("pointermove",stopEvent,i);e.addEventListener("pointercancel",stopEvent,i);e.addEventListener("pointerup",stopEvent,i);this.#Ee?.()}stopEvent(t);if(2!==t.touches.length||this.#xe?.()){this.#De=null;return}let[e,i]=t.touches;e.identifier>i.identifier&&([e,i]=[i,e]);this.#De={touch0X:e.screenX,touch0Y:e.screenY,touch1X:i.screenX,touch1Y:i.screenY}}#Fe(t){if(!this.#De||2!==t.touches.length)return;stopEvent(t);let[e,i]=t.touches;e.identifier>i.identifier&&([e,i]=[i,e]);const{screenX:s,screenY:n}=e,{screenX:r,screenY:a}=i,o=this.#De,{touch0X:l,touch0Y:h,touch1X:d,touch1Y:c}=o,u=d-l,p=c-h,g=r-s,m=a-n,f=Math.hypot(g,m)||1,b=Math.hypot(u,p)||1;if(!this.#we&&Math.abs(b-f)<=TouchManager.MIN_TOUCH_DISTANCE_TO_PINCH)return;o.touch0X=s;o.touch0Y=n;o.touch1X=r;o.touch1Y=a;if(!this.#we){this.#we=!0;return}const y=[(s+r)/2,(n+a)/2];this.#Te?.(y,b,f)}#Be(t){if(!(t.touches.length>=2)){if(this.#ke){this.#ke.abort();this.#ke=null;this.#Se?.()}if(this.#De){stopEvent(t);this.#De=null;this.#we=!1}}}destroy(){this.#Me?.abort();this.#Me=null;this.#_e?.abort();this.#_e=null}}class AnnotationEditor{#Re=null;#Oe=null;#l=null;#h=null;#ue=null;#Le=!1;#Ne=null;#Ue="";#He=null;#ze=null;#Ge=null;#We=null;#Ve=null;#je="";#$e=!1;#Xe=null;#qe=!1;#Ke=!1;#Ye=!1;#Qe=null;#Je=0;#Ze=0;#ti=null;#ei=null;isSelected=!1;_isCopy=!1;_editToolbar=null;_initialOptions=Object.create(null);_initialData=null;_isVisible=!0;_uiManager=null;_focusEventsAllowed=!0;static _l10n=null;static _l10nResizer=null;#ii=!1;#si=AnnotationEditor._zIndex++;static _borderLineWidth=-1;static _colorManager=new ColorManager;static _zIndex=1;static _telemetryTimeout=1e3;static get _resizerKeyboardManager(){const t=AnnotationEditor.prototype._resizeWithKeyboard,e=AnnotationEditorUIManager.TRANSLATE_SMALL,i=AnnotationEditorUIManager.TRANSLATE_BIG;return shadow(this,"_resizerKeyboardManager",new KeyboardManager([[["ArrowLeft","mac+ArrowLeft"],t,{args:[-e,0]}],[["ctrl+ArrowLeft","mac+shift+ArrowLeft"],t,{args:[-i,0]}],[["ArrowRight","mac+ArrowRight"],t,{args:[e,0]}],[["ctrl+ArrowRight","mac+shift+ArrowRight"],t,{args:[i,0]}],[["ArrowUp","mac+ArrowUp"],t,{args:[0,-e]}],[["ctrl+ArrowUp","mac+shift+ArrowUp"],t,{args:[0,-i]}],[["ArrowDown","mac+ArrowDown"],t,{args:[0,e]}],[["ctrl+ArrowDown","mac+shift+ArrowDown"],t,{args:[0,i]}],[["Escape","mac+Escape"],AnnotationEditor.prototype._stopResizingWithKeyboard]]))}constructor(t){this.parent=t.parent;this.id=t.id;this.width=this.height=null;this.pageIndex=t.parent.pageIndex;this.name=t.name;this.div=null;this._uiManager=t.uiManager;this.annotationElementId=null;this._willKeepAspectRatio=!1;this._initialOptions.isCentered=t.isCentered;this._structTreeParentId=null;this.annotationElementId=t.annotationElementId||null;this.creationDate=t.creationDate||new Date;this.modificationDate=t.modificationDate||null;this.canAddComment=!0;const{rotation:e,rawDims:{pageWidth:i,pageHeight:s,pageX:n,pageY:r}}=this.parent.viewport;this.rotation=e;this.pageRotation=(360+e-this._uiManager.viewParameters.rotation)%360;this.pageDimensions=[i,s];this.pageTranslation=[n,r];const[a,o]=this.parentDimensions;this.x=t.x/a;this.y=t.y/o;this.isAttachedToDOM=!1;this.deleted=!1}updatePageIndex(t){this.pageIndex=t}get editorType(){return Object.getPrototypeOf(this).constructor._type}get mode(){return Object.getPrototypeOf(this).constructor._editorType}static get isDrawer(){return!1}static get _defaultLineColor(){return shadow(this,"_defaultLineColor",this._colorManager.getHexCode("CanvasText"))}static deleteAnnotationElement(t){const e=new FakeEditor({id:t._uiManager.getId(),parent:t.parent,uiManager:t._uiManager});e.annotationElementId=t.annotationElementId;e.deleted=!0;e._uiManager.addToAnnotationStorage(e)}static initialize(t,e){AnnotationEditor._l10n??=t;AnnotationEditor._l10nResizer||=Object.freeze({topLeft:"pdfjs-editor-resizer-top-left",topMiddle:"pdfjs-editor-resizer-top-middle",topRight:"pdfjs-editor-resizer-top-right",middleRight:"pdfjs-editor-resizer-middle-right",bottomRight:"pdfjs-editor-resizer-bottom-right",bottomMiddle:"pdfjs-editor-resizer-bottom-middle",bottomLeft:"pdfjs-editor-resizer-bottom-left",middleLeft:"pdfjs-editor-resizer-middle-left"});if(-1!==AnnotationEditor._borderLineWidth)return;const i=getComputedStyle(document.documentElement);AnnotationEditor._borderLineWidth=parseFloat(i.getPropertyValue("--outline-width"))||0}static updateDefaultParams(t,e){}static get defaultPropertiesToUpdate(){return[]}static isHandlingMimeForPasting(t){return!1}static paste(t,e){unreachable("Not implemented")}get propertiesToUpdate(){return[]}get _isDraggable(){return this.#ii}set _isDraggable(t){this.#ii=t;this.div?.classList.toggle("draggable",t)}get uid(){return this.annotationElementId||this.id}get isEnterHandled(){return!0}center(){const[t,e]=this.pageDimensions;switch(this.parentRotation){case 90:this.x-=this.height*e/(2*t);this.y+=this.width*t/(2*e);break;case 180:this.x+=this.width/2;this.y+=this.height/2;break;case 270:this.x+=this.height*e/(2*t);this.y-=this.width*t/(2*e);break;default:this.x-=this.width/2;this.y-=this.height/2}this.fixAndSetPosition()}addCommands(t){this._uiManager.addCommands(t)}get currentLayer(){return this._uiManager.currentLayer}setInBackground(){this.div.style.zIndex=0}setInForeground(){this.div.style.zIndex=this.#si}setParent(t){if(null!==t){this.pageIndex=t.pageIndex;this.pageDimensions=t.pageDimensions}else{this.#ni();this.#We?.remove();this.#We=null}this.parent=t}focusin(t){this._focusEventsAllowed&&(this.#$e?this.#$e=!1:this.parent.setSelected(this))}focusout(t){if(!this._focusEventsAllowed)return;if(!this.isAttachedToDOM)return;const e=t.relatedTarget;if(!e?.closest(`#${this.id}`)){t.preventDefault();this.parent?.isMultipleSelection||this.commitOrRemove()}}commitOrRemove(){this.isEmpty()?this.remove():this.commit()}commit(){this.isInEditMode()&&this.addToAnnotationStorage()}addToAnnotationStorage(){this._uiManager.addToAnnotationStorage(this)}setAt(t,e,i,s){const[n,r]=this.parentDimensions;[i,s]=this.screenToPageTranslation(i,s);this.x=(t+i)/n;this.y=(e+s)/r;this.fixAndSetPosition()}_moveAfterPaste(t,e){if(this.isClone){delete this.isClone;return}const[i,s]=this.parentDimensions;this.setAt(t*i,e*s,this.width*i,this.height*s);this._onTranslated()}#ri([t,e],i,s){[i,s]=this.screenToPageTranslation(i,s);this.x+=i/t;this.y+=s/e;this._onTranslating(this.x,this.y);this.fixAndSetPosition()}translate(t,e){this.#ri(this.parentDimensions,t,e)}translateInPage(t,e){this.#Xe||=[this.x,this.y,this.width,this.height];this.#ri(this.pageDimensions,t,e);this.div.scrollIntoView({block:"nearest"})}translationDone(){this._onTranslated(this.x,this.y)}drag(t,e){this.#Xe||=[this.x,this.y,this.width,this.height];const{div:i,parentDimensions:[s,n]}=this;this.x+=t/s;this.y+=e/n;if(this.parent&&(this.x<0||this.x>1||this.y<0||this.y>1)){const{x:t,y:e}=this.div.getBoundingClientRect();if(this.parent.findNewParent(this,t,e)){this.x-=Math.floor(this.x);this.y-=Math.floor(this.y)}}let{x:r,y:a}=this;const[o,l]=this.getBaseTranslation();r+=o;a+=l;const{style:h}=i;h.left=`${(100*r).toFixed(2)}%`;h.top=`${(100*a).toFixed(2)}%`;this._onTranslating(r,a);i.scrollIntoView({block:"nearest"})}_onTranslating(t,e){}_onTranslated(t,e){}get _hasBeenMoved(){return!!this.#Xe&&(this.#Xe[0]!==this.x||this.#Xe[1]!==this.y)}get _hasBeenResized(){return!!this.#Xe&&(this.#Xe[2]!==this.width||this.#Xe[3]!==this.height)}getBaseTranslation(){const[t,e]=this.parentDimensions,{_borderLineWidth:i}=AnnotationEditor,s=i/t,n=i/e;switch(this.rotation){case 90:return[-s,n];case 180:return[s,n];case 270:return[s,-n];default:return[-s,-n]}}get _mustFixPosition(){return!0}fixAndSetPosition(t=this.rotation){const{div:{style:e},pageDimensions:[i,s]}=this;let{x:n,y:r,width:a,height:o}=this;a*=i;o*=s;n*=i;r*=s;if(this._mustFixPosition)switch(t){case 0:n=MathClamp(n,0,i-a);r=MathClamp(r,0,s-o);break;case 90:n=MathClamp(n,0,i-o);r=MathClamp(r,a,s);break;case 180:n=MathClamp(n,a,i);r=MathClamp(r,o,s);break;case 270:n=MathClamp(n,o,i);r=MathClamp(r,0,s-a)}this.x=n/=i;this.y=r/=s;const[l,h]=this.getBaseTranslation();n+=l;r+=h;e.left=`${(100*n).toFixed(2)}%`;e.top=`${(100*r).toFixed(2)}%`;this.moveInDOM()}static#ai(t,e,i){switch(i){case 90:return[e,-t];case 180:return[-t,-e];case 270:return[-e,t];default:return[t,e]}}screenToPageTranslation(t,e){return AnnotationEditor.#ai(t,e,this.parentRotation)}pageTranslationToScreen(t,e){return AnnotationEditor.#ai(t,e,360-this.parentRotation)}#oi(t){switch(t){case 90:{const[t,e]=this.pageDimensions;return[0,-t/e,e/t,0]}case 180:return[-1,0,0,-1];case 270:{const[t,e]=this.pageDimensions;return[0,t/e,-e/t,0]}default:return[1,0,0,1]}}get parentScale(){return this._uiManager.viewParameters.realScale}get parentRotation(){return(this._uiManager.viewParameters.rotation+this.pageRotation)%360}get parentDimensions(){const{parentScale:t,pageDimensions:[e,i]}=this;return[e*t,i*t]}setDims(){const{div:{style:t},width:e,height:i}=this;t.width=`${(100*e).toFixed(2)}%`;t.height=`${(100*i).toFixed(2)}%`}getInitialTranslation(){return[0,0]}#li(){if(this.#He)return;this.#He=document.createElement("div");this.#He.classList.add("resizers");const t=this._willKeepAspectRatio?["topLeft","topRight","bottomRight","bottomLeft"]:["topLeft","topMiddle","topRight","middleRight","bottomRight","bottomMiddle","bottomLeft","middleLeft"],e=this._uiManager._signal;for(const i of t){const t=document.createElement("div");this.#He.append(t);t.classList.add("resizer",i);t.setAttribute("data-resizer-name",i);t.addEventListener("pointerdown",this.#hi.bind(this,i),{signal:e});t.addEventListener("contextmenu",noContextMenu,{signal:e});t.tabIndex=-1}this.div.prepend(this.#He)}#hi(t,e){e.preventDefault();const{isMac:i}=FeatureTest.platform;if(0!==e.button||e.ctrlKey&&i)return;this.#l?.toggle(!1);const s=this._isDraggable;this._isDraggable=!1;this.#ze=[e.screenX,e.screenY];const n=new AbortController,r=this._uiManager.combinedSignal(n);this.parent.togglePointerEvents(!1);window.addEventListener("pointermove",this.#di.bind(this,t),{passive:!0,capture:!0,signal:r});window.addEventListener("touchmove",stopEvent,{passive:!1,signal:r});window.addEventListener("contextmenu",noContextMenu,{signal:r});this.#Ge={savedX:this.x,savedY:this.y,savedWidth:this.width,savedHeight:this.height};const a=this.parent.div.style.cursor,o=this.div.style.cursor;this.div.style.cursor=this.parent.div.style.cursor=window.getComputedStyle(e.target).cursor;const pointerUpCallback=()=>{n.abort();this.parent.togglePointerEvents(!0);this.#l?.toggle(!0);this._isDraggable=s;this.parent.div.style.cursor=a;this.div.style.cursor=o;this.#ci()};window.addEventListener("pointerup",pointerUpCallback,{signal:r});window.addEventListener("blur",pointerUpCallback,{signal:r})}#ui(t,e,i,s){this.width=i;this.height=s;this.x=t;this.y=e;this.setDims();this.fixAndSetPosition();this._onResized()}_onResized(){}#ci(){if(!this.#Ge)return;const{savedX:t,savedY:e,savedWidth:i,savedHeight:s}=this.#Ge;this.#Ge=null;const n=this.x,r=this.y,a=this.width,o=this.height;n===t&&r===e&&a===i&&o===s||this.addCommands({cmd:this.#ui.bind(this,n,r,a,o),undo:this.#ui.bind(this,t,e,i,s),mustExec:!0})}static _round(t){return Math.round(1e4*t)/1e4}#di(t,e){const[i,s]=this.parentDimensions,n=this.x,r=this.y,a=this.width,o=this.height,l=AnnotationEditor.MIN_SIZE/i,h=AnnotationEditor.MIN_SIZE/s,d=this.#oi(this.rotation),transf=(t,e)=>[d[0]*t+d[2]*e,d[1]*t+d[3]*e],c=this.#oi(360-this.rotation);let u,p,g=!1,m=!1;switch(t){case"topLeft":g=!0;u=(t,e)=>[0,0];p=(t,e)=>[t,e];break;case"topMiddle":u=(t,e)=>[t/2,0];p=(t,e)=>[t/2,e];break;case"topRight":g=!0;u=(t,e)=>[t,0];p=(t,e)=>[0,e];break;case"middleRight":m=!0;u=(t,e)=>[t,e/2];p=(t,e)=>[0,e/2];break;case"bottomRight":g=!0;u=(t,e)=>[t,e];p=(t,e)=>[0,0];break;case"bottomMiddle":u=(t,e)=>[t/2,e];p=(t,e)=>[t/2,0];break;case"bottomLeft":g=!0;u=(t,e)=>[0,e];p=(t,e)=>[t,0];break;case"middleLeft":m=!0;u=(t,e)=>[0,e/2];p=(t,e)=>[t,e/2]}const f=u(a,o),b=p(a,o);let y=transf(...b);const A=AnnotationEditor._round(n+y[0]),v=AnnotationEditor._round(r+y[1]);let w,x,C=1,E=1;if(e.fromKeyboard)({deltaX:w,deltaY:x}=e);else{const{screenX:t,screenY:i}=e,[s,n]=this.#ze;[w,x]=this.screenToPageTranslation(t-s,i-n);this.#ze[0]=t;this.#ze[1]=i}[w,x]=(T=w/i,S=x/s,[c[0]*T+c[2]*S,c[1]*T+c[3]*S]);var T,S;if(g){const t=Math.hypot(a,o);C=E=Math.max(Math.min(Math.hypot(b[0]-f[0]-w,b[1]-f[1]-x)/t,1/a,1/o),l/a,h/o)}else m?C=MathClamp(Math.abs(b[0]-f[0]-w),l,1)/a:E=MathClamp(Math.abs(b[1]-f[1]-x),h,1)/o;const _=AnnotationEditor._round(a*C),P=AnnotationEditor._round(o*E);y=transf(...p(_,P));const D=A-y[0],M=v-y[1];this.#Xe||=[this.x,this.y,this.width,this.height];this.width=_;this.height=P;this.x=D;this.y=M;this.setDims();this.fixAndSetPosition();this._onResizing()}_onResizing(){}altTextFinish(){this.#l?.finish()}get toolbarButtons(){return null}async addEditToolbar(){if(this._editToolbar||this.#Ke)return this._editToolbar;this._editToolbar=new EditorToolbar(this);this.div.append(this._editToolbar.render());const{toolbarButtons:t}=this;if(t)for(const[e,i]of t)await this._editToolbar.addButton(e,i);this.hasComment||this._editToolbar.addButton("comment",this.addCommentButton());this._editToolbar.addButton("delete");return this._editToolbar}addCommentButtonInToolbar(){this._editToolbar?.addButtonBefore("comment",this.addCommentButton(),".deleteButton")}removeCommentButtonFromToolbar(){this._editToolbar?.removeButton("comment")}removeEditToolbar(){this._editToolbar?.remove();this._editToolbar=null;this.#l?.destroy()}addContainer(t){const e=this._editToolbar?.div;e?e.before(t):this.div.append(t)}getClientDimensions(){return this.div.getBoundingClientRect()}createAltText(){if(!this.#l){AltText.initialize(AnnotationEditor._l10n);this.#l=new AltText(this);if(this.#Re){this.#l.data=this.#Re;this.#Re=null}}return this.#l}get altTextData(){return this.#l?.data}set altTextData(t){this.#l&&(this.#l.data=t)}get guessedAltText(){return this.#l?.guessedText}async setGuessedAltText(t){await(this.#l?.setGuessedText(t))}serializeAltText(t){return this.#l?.serialize(t)}hasAltText(){return!!this.#l&&!this.#l.isEmpty()}hasAltTextData(){return this.#l?.hasData()??!1}focusCommentButton(){this.#h?.focusButton()}addCommentButton(){return this.canAddComment?this.#h||=new Comment(this):null}addStandaloneCommentButton(){if(this._uiManager.hasCommentManager())if(this.#ue)this._uiManager.isEditingMode()&&this.#ue.classList.remove("hidden");else if(this.hasComment){this.#ue=this.#h.renderForStandalone();this.div.append(this.#ue)}}removeStandaloneCommentButton(){this.#h.removeStandaloneCommentButton();this.#ue=null}hideStandaloneCommentButton(){this.#ue?.classList.add("hidden")}get comment(){if(!this.#h)return null;const{data:{richText:t,text:e,date:i,deleted:s}}=this.#h;return{text:e,richText:t,date:i,deleted:s,color:this.getNonHCMColor(),opacity:this.opacity??1}}set comment(t){this.#h||=new Comment(this);"object"==typeof t&&null!==t?this.#h.restoreData(t):this.#h.data=t;if(this.hasComment){this.removeCommentButtonFromToolbar();this.addStandaloneCommentButton();this._uiManager.updateComment(this)}else{this.addCommentButtonInToolbar();this.removeStandaloneCommentButton();this._uiManager.removeComment(this)}}setCommentData({comment:t,popupRef:e,richText:i}){if(!e)return;this.#h||=new Comment(this);this.#h.setInitialText(t,i);if(!this.annotationElementId)return;const s=this._uiManager.getAndRemoveDataFromAnnotationStorage(this.annotationElementId);s&&this.updateFromAnnotationLayer(s)}get hasEditedComment(){return this.#h?.hasBeenEdited()}get hasDeletedComment(){return this.#h?.isDeleted()}get hasComment(){return!!this.#h&&!this.#h.isEmpty()&&!this.#h.isDeleted()}async editComment(t){this.#h||=new Comment(this);this.#h.edit(t)}toggleComment(t,e=void 0){this.hasComment&&this._uiManager.toggleComment(this,t,e)}setSelectedCommentButton(t){this.#h.setSelectedButton(t)}addComment(t){if(this.hasEditedComment){const e=180,i=100,[,,,s]=t.rect,[n]=this.pageDimensions,[r]=this.pageTranslation,a=r+n+1,o=s-i,l=a+e;t.popup={contents:this.comment.text,deleted:this.comment.deleted,rect:[a,o,l,s]}}}updateFromAnnotationLayer({popup:{contents:t,deleted:e}}){this.#h.data=e?null:t}get parentBoundingClientRect(){return this.parent.boundingClientRect}render(){const t=this.div=document.createElement("div");t.setAttribute("data-editor-rotation",(360-this.rotation)%360);t.className=this.name;t.setAttribute("id",this.id);t.tabIndex=this.#Le?-1:0;t.setAttribute("role","application");this.defaultL10nId&&t.setAttribute("data-l10n-id",this.defaultL10nId);this._isVisible||t.classList.add("hidden");this.setInForeground();this.#pi();const[e,i]=this.parentDimensions;if(this.parentRotation%180!=0){t.style.maxWidth=`${(100*i/e).toFixed(2)}%`;t.style.maxHeight=`${(100*e/i).toFixed(2)}%`}const[s,n]=this.getInitialTranslation();this.translate(s,n);bindEvents(this,t,["keydown","pointerdown","dblclick"]);this.isResizable&&this._uiManager._supportsPinchToZoom&&(this.#ei||=new TouchManager({container:t,isPinchingDisabled:()=>!this.isSelected,onPinchStart:this.#gi.bind(this),onPinching:this.#mi.bind(this),onPinchEnd:this.#fi.bind(this),signal:this._uiManager._signal}));this.addStandaloneCommentButton();this._uiManager._editorUndoBar?.hide();return t}#gi(){this.#Ge={savedX:this.x,savedY:this.y,savedWidth:this.width,savedHeight:this.height};this.#l?.toggle(!1);this.parent.togglePointerEvents(!1)}#mi(t,e,i){let s=i/e*.7+1-.7;if(1===s)return;const n=this.#oi(this.rotation),transf=(t,e)=>[n[0]*t+n[2]*e,n[1]*t+n[3]*e],[r,a]=this.parentDimensions,o=this.x,l=this.y,h=this.width,d=this.height,c=AnnotationEditor.MIN_SIZE/r,u=AnnotationEditor.MIN_SIZE/a;s=Math.max(Math.min(s,1/h,1/d),c/h,u/d);const p=AnnotationEditor._round(h*s),g=AnnotationEditor._round(d*s);if(p===h&&g===d)return;this.#Xe||=[o,l,h,d];const m=transf(h/2,d/2),f=AnnotationEditor._round(o+m[0]),b=AnnotationEditor._round(l+m[1]),y=transf(p/2,g/2);this.x=f-y[0];this.y=b-y[1];this.width=p;this.height=g;this.setDims();this.fixAndSetPosition();this._onResizing()}#fi(){this.#l?.toggle(!0);this.parent.togglePointerEvents(!0);this.#ci()}pointerdown(t){const{isMac:e}=FeatureTest.platform;if(0!==t.button||t.ctrlKey&&e)t.preventDefault();else{this.#$e=!0;this._isDraggable?this.#bi(t):this.#yi(t)}}#yi(t){const{isMac:e}=FeatureTest.platform;t.ctrlKey&&!e||t.shiftKey||t.metaKey&&e?this.parent.toggleSelected(this):this.parent.setSelected(this)}#bi(t){const{isSelected:e}=this;this._uiManager.setUpDragSession();let i=!1;const s=new AbortController,n=this._uiManager.combinedSignal(s),r={capture:!0,passive:!1,signal:n},cancelDrag=t=>{s.abort();this.#Ne=null;this.#$e=!1;this._uiManager.endDragSession()||this.#yi(t);i&&this._onStopDragging()};if(e){this.#Je=t.clientX;this.#Ze=t.clientY;this.#Ne=t.pointerId;this.#Ue=t.pointerType;window.addEventListener("pointermove",t=>{if(!i){i=!0;this._uiManager.toggleComment(this,!0,!1);this._onStartDragging()}const{clientX:e,clientY:s,pointerId:n}=t;if(n!==this.#Ne){stopEvent(t);return}const[r,a]=this.screenToPageTranslation(e-this.#Je,s-this.#Ze);this.#Je=e;this.#Ze=s;this._uiManager.dragSelectedEditors(r,a)},r);window.addEventListener("touchmove",stopEvent,r);window.addEventListener("pointerdown",t=>{t.pointerType===this.#Ue&&(this.#ei||t.isPrimary)&&cancelDrag(t);stopEvent(t)},r)}const pointerUpCallback=t=>{this.#Ne&&this.#Ne!==t.pointerId?stopEvent(t):cancelDrag(t)};window.addEventListener("pointerup",pointerUpCallback,{signal:n});window.addEventListener("blur",pointerUpCallback,{signal:n})}_onStartDragging(){}_onStopDragging(){}moveInDOM(){this.#Qe&&clearTimeout(this.#Qe);this.#Qe=setTimeout(()=>{this.#Qe=null;this.parent?.moveEditorInDOM(this)},0)}_setParentAndPosition(t,e,i){t.changeParent(this);this.x=e;this.y=i;this.fixAndSetPosition();this._onTranslated()}getRect(t,e,i=this.rotation){const s=this.parentScale,[n,r]=this.pageDimensions,[a,o]=this.pageTranslation,l=t/s,h=e/s,d=this.x*n,c=this.y*r,u=this.width*n,p=this.height*r;switch(i){case 0:return[d+l+a,r-c-h-p+o,d+l+u+a,r-c-h+o];case 90:return[d+h+a,r-c+l+o,d+h+p+a,r-c+l+u+o];case 180:return[d-l-u+a,r-c+h+o,d-l+a,r-c+h+p+o];case 270:return[d-h-p+a,r-c-l-u+o,d-h+a,r-c-l+o];default:throw new Error("Invalid rotation")}}getRectInCurrentCoords(t,e){const[i,s,n,r]=t,a=n-i,o=r-s;switch(this.rotation){case 0:return[i,e-r,a,o];case 90:return[i,e-s,o,a];case 180:return[n,e-s,a,o];case 270:return[n,e-r,o,a];default:throw new Error("Invalid rotation")}}getPDFRect(){return this.getRect(0,0)}getNonHCMColor(){return this.color&&AnnotationEditor._colorManager.convert(this._uiManager.getNonHCMColor(this.color))}onUpdatedColor(){this.#h?.onUpdatedColor()}getData(){const{comment:{text:t,color:e,date:i,opacity:s,deleted:n,richText:r},uid:a,pageIndex:o,creationDate:l,modificationDate:h}=this;return{id:a,pageIndex:o,rect:this.getPDFRect(),richText:r,contentsObj:{str:t},creationDate:l,modificationDate:i||h,popupRef:!n,color:e,opacity:s}}onceAdded(t){}isEmpty(){return!1}enableEditMode(){if(this.isInEditMode())return!1;this.parent.setEditingState(!1);this.#Ke=!0;return!0}disableEditMode(){if(!this.isInEditMode())return!1;this.parent.setEditingState(!0);this.#Ke=!1;return!0}isInEditMode(){return this.#Ke}shouldGetKeyboardEvents(){return this.#Ye}needsToBeRebuilt(){return this.div&&!this.isAttachedToDOM}get isOnScreen(){const{top:t,left:e,bottom:i,right:s}=this.getClientDimensions(),{innerHeight:n,innerWidth:r}=window;return e0&&t0}#pi(){if(this.#Ve||!this.div)return;this.#Ve=new AbortController;const t=this._uiManager.combinedSignal(this.#Ve);this.div.addEventListener("focusin",this.focusin.bind(this),{signal:t});this.div.addEventListener("focusout",this.focusout.bind(this),{signal:t})}rebuild(){this.#pi()}rotate(t){}resize(){}serializeDeleted(){return{id:this.annotationElementId,deleted:!0,pageIndex:this.pageIndex,popupRef:this._initialData?.popupRef||""}}serialize(t=!1,e=null){return{annotationType:this.mode,pageIndex:this.pageIndex,rect:this.getPDFRect(),rotation:this.rotation,structTreeParentId:this._structTreeParentId,popupRef:this._initialData?.popupRef||""}}static async deserialize(t,e,i){const s=new this.prototype.constructor({parent:e,id:i.getId(),uiManager:i,annotationElementId:t.annotationElementId,creationDate:t.creationDate,modificationDate:t.modificationDate});s.rotation=t.rotation;s.#Re=t.accessibilityData;s._isCopy=t.isCopy||!1;const[n,r]=s.pageDimensions,[a,o,l,h]=s.getRectInCurrentCoords(t.rect,r);s.x=a/n;s.y=o/r;s.width=l/n;s.height=h/r;return s}get hasBeenModified(){return!!this.annotationElementId&&(this.deleted||null!==this.serialize())}remove(){this.#Ve?.abort();this.#Ve=null;this.isEmpty()||this.commit();this.parent?this.parent.remove(this):this._uiManager.removeEditor(this);this.hideCommentPopup();if(this.#Qe){clearTimeout(this.#Qe);this.#Qe=null}this.#ni();this.removeEditToolbar();if(this.#ti){for(const t of this.#ti.values())clearTimeout(t);this.#ti=null}this.parent=null;this.#ei?.destroy();this.#ei=null;this.#We?.remove();this.#We=null}get isResizable(){return!1}makeResizable(){if(this.isResizable){this.#li();this.#He.classList.remove("hidden")}}get toolbarPosition(){return null}get commentButtonPosition(){return"ltr"===this._uiManager.direction?[1,0]:[0,0]}get commentButtonPositionInPage(){const{commentButtonPosition:[t,e]}=this,[i,s,n,r]=this.getPDFRect();return[AnnotationEditor._round(i+(n-i)*t),AnnotationEditor._round(s+(r-s)*(1-e))]}get commentButtonColor(){return this._uiManager.makeCommentColor(this.getNonHCMColor(),this.opacity)}get commentPopupPosition(){return this.#h.commentPopupPositionInLayer}set commentPopupPosition(t){this.#h.commentPopupPositionInLayer=t}hasDefaultPopupPosition(){return this.#h.hasDefaultPopupPosition()}get commentButtonWidth(){return this.#h.commentButtonWidth}get elementBeforePopup(){return this.div}setCommentButtonStates(t){this.#h?.setCommentButtonStates(t)}keydown(t){if(!this.isResizable||t.target!==this.div||"Enter"!==t.key)return;this._uiManager.setSelected(this);this.#Ge={savedX:this.x,savedY:this.y,savedWidth:this.width,savedHeight:this.height};const e=this.#He.children;if(!this.#Oe){this.#Oe=Array.from(e);const t=this.#Ai.bind(this),i=this.#vi.bind(this),s=this._uiManager._signal;for(const e of this.#Oe){const n=e.getAttribute("data-resizer-name");e.setAttribute("role","spinbutton");e.addEventListener("keydown",t,{signal:s});e.addEventListener("blur",i,{signal:s});e.addEventListener("focus",this.#wi.bind(this,n),{signal:s});e.setAttribute("data-l10n-id",AnnotationEditor._l10nResizer[n])}}const i=this.#Oe[0];let s=0;for(const t of e){if(t===i)break;s++}const n=(360-this.rotation+this.parentRotation)%360/90*(this.#Oe.length/4);if(n!==s){if(ns)for(let t=0;t{this.div?.classList.contains("selectedEditor")&&this._editToolbar?.show()})}}focus(){this.div&&!this.div.contains(document.activeElement)&&setTimeout(()=>this.div?.focus({preventScroll:!0}),0)}unselect(){if(this.isSelected){this.isSelected=!1;this.#He?.classList.add("hidden");this.div?.classList.remove("selectedEditor");this.div?.contains(document.activeElement)&&this._uiManager.currentLayer.div.focus({preventScroll:!0});this._editToolbar?.hide();this.#l?.toggleAltTextBadge(!0);this.hideCommentPopup()}}hideCommentPopup(){this.hasComment&&this._uiManager.toggleComment(null)}updateParams(t,e){}disableEditing(){}enableEditing(){}get canChangeContent(){return!1}enterInEditMode(){if(this.canChangeContent){this.enableEditMode();this.div.focus()}}dblclick(t){if("BUTTON"!==t.target.nodeName){this.enterInEditMode();this.parent.updateToolbar({mode:this.constructor._editorType,editId:this.uid})}}getElementForAltText(){return this.div}get contentDiv(){return this.div}get isEditing(){return this.#qe}set isEditing(t){this.#qe=t;if(this.parent)if(t){this.parent.setSelected(this);this.parent.setActiveEditor(this)}else this.parent.setActiveEditor(null)}static get MIN_SIZE(){return 16}static canCreateNewEmptyEditor(){return!0}get telemetryInitialData(){return{action:"added"}}get telemetryFinalData(){return null}_reportTelemetry(t,e=!1){if(e){this.#ti||=new Map;const{action:e}=t;let i=this.#ti.get(e);i&&clearTimeout(i);i=setTimeout(()=>{this._reportTelemetry(t);this.#ti.delete(e);0===this.#ti.size&&(this.#ti=null)},AnnotationEditor._telemetryTimeout);this.#ti.set(e,i);return}t.type||=this.editorType;this._uiManager._eventBus.dispatch("reporttelemetry",{source:this,details:{type:"editing",data:t}})}show(t=this._isVisible){this.div.classList.toggle("hidden",!t);this._isVisible=t}enable(){this.div&&(this.div.tabIndex=0);this.#Le=!1}disable(){this.div&&(this.div.tabIndex=-1);this.#Le=!0}updateFakeAnnotationElement(t){if(this.#We||this.deleted)if(this.deleted){this.#We.remove();this.#We=null}else(this.hasEditedComment||this._hasBeenMoved||this._hasBeenResized)&&this.#We.updateEdited({rect:this.getPDFRect(),popup:this.comment});else this.#We=t.addFakeAnnotation(this)}renderAnnotationElement(t){if(this.deleted){t.hide();return null}let e=t.container.querySelector(".annotationContent");if(e){if("CANVAS"===e.nodeName){const t=e;e=document.createElement("div");e.classList.add("annotationContent",this.editorType);t.before(e)}}else{e=document.createElement("div");e.classList.add("annotationContent",this.editorType);t.container.prepend(e)}return e}resetAnnotationElement(t){const{firstElementChild:e}=t.container;"DIV"===e?.nodeName&&e.classList.contains("annotationContent")&&e.remove()}}class FakeEditor extends AnnotationEditor{constructor(t){super(t);this.annotationElementId=t.annotationElementId;this.deleted=!0}serialize(){return this.serializeDeleted()}}const X=3285377520,q=4294901760,K=65535;class MurmurHash3_64{constructor(t){this.h1=t?4294967295&t:X;this.h2=t?4294967295&t:X}update(t){let e,i;if("string"==typeof t){e=new Uint8Array(2*t.length);i=0;for(let s=0,n=t.length;s>>8;e[i++]=255&n}}}else{if(!ArrayBuffer.isView(t))throw new Error("Invalid data format, must be a string or TypedArray.");e=t.slice();i=e.byteLength}const s=i>>2,n=i-4*s,r=new Uint32Array(e.buffer,0,s);let a=0,o=0,l=this.h1,h=this.h2;const d=3432918353,c=461845907,u=11601,p=13715;for(let t=0;t>>17;a=a*c&q|a*p&K;l^=a;l=l<<13|l>>>19;l=5*l+3864292196}else{o=r[t];o=o*d&q|o*u&K;o=o<<15|o>>>17;o=o*c&q|o*p&K;h^=o;h=h<<13|h>>>19;h=5*h+3864292196}a=0;switch(n){case 3:a^=e[4*s+2]<<16;case 2:a^=e[4*s+1]<<8;case 1:a^=e[4*s];a=a*d&q|a*u&K;a=a<<15|a>>>17;a=a*c&q|a*p&K;1&s?l^=a:h^=a}this.h1=l;this.h2=h}hexdigest(){let t=this.h1,e=this.h2;t^=e>>>1;t=3981806797*t&q|36045*t&K;e=4283543511*e&q|(2950163797*(e<<16|t>>>16)&q)>>>16;t^=e>>>1;t=444984403*t&q|60499*t&K;e=3301882366*e&q|(3120437893*(e<<16|t>>>16)&q)>>>16;t^=e>>>1;return(t>>>0).toString(16).padStart(8,"0")+(e>>>0).toString(16).padStart(8,"0")}}const Y=Object.freeze({map:null,hash:"",transfer:void 0});class AnnotationStorage{#Ci=!1;#Ei=null;#Ti=null;#Si=new Map;onSetModified=null;onResetModified=null;onAnnotationEditor=null;getValue(t,e){const i=this.#Si.get(t);return void 0===i?e:Object.assign(e,i)}getRawValue(t){return this.#Si.get(t)}remove(t){const e=this.#Si.get(t);if(void 0!==e){e instanceof AnnotationEditor&&this.#Ti.delete(e.annotationElementId);this.#Si.delete(t);0===this.#Si.size&&this.resetModified();this.#Si.values().some(t=>t instanceof AnnotationEditor)||this.onAnnotationEditor?.(null)}}setValue(t,e){const i=this.#Si.get(t);let s=!1;if(void 0!==i){for(const[t,n]of Object.entries(e))if(i[t]!==n){s=!0;i[t]=n}}else{s=!0;this.#Si.set(t,e)}s&&this.#_i();if(e instanceof AnnotationEditor){(this.#Ti||=new Map).set(e.annotationElementId,e);this.onAnnotationEditor?.(e.constructor._type)}}has(t){return this.#Si.has(t)}get size(){return this.#Si.size}#_i(){if(!this.#Ci){this.#Ci=!0;this.onSetModified?.()}}resetModified(){if(this.#Ci){this.#Ci=!1;this.onResetModified?.()}}get print(){return new PrintAnnotationStorage(this)}get serializable(){if(0===this.#Si.size)return Y;const t=new Map,e=new MurmurHash3_64,i=[],s=Object.create(null);let n=!1;for(const[i,r]of this.#Si){const a=r instanceof AnnotationEditor?r.serialize(!1,s):r;if(r.page){r.pageIndex=r.page._pageIndex;delete r.page}if(a){t.set(i,a);e.update(`${i}:${JSON.stringify(a)}`);n||=!!a.bitmap}}if(n)for(const e of t.values())e.bitmap&&i.push(e.bitmap);return t.size>0?{map:t,hash:e.hexdigest(),transfer:i}:Y}get editorStats(){let t=null;const e=new Map;let i=0,s=0;for(const n of this.#Si.values()){if(!(n instanceof AnnotationEditor)){n.popup&&(n.popup.deleted?s+=1:i+=1);continue}n.isCommentDeleted?s+=1:n.hasEditedComment&&(i+=1);const r=n.telemetryFinalData;if(!r)continue;const{type:a}=r;e.has(a)||e.set(a,Object.getPrototypeOf(n).constructor);t||=Object.create(null);const o=t[a]||=new Map;for(const[t,e]of Object.entries(r)){if("type"===t)continue;const i=o.getOrInsertComputed(t,makeMap);i.set(e,(i.get(e)??0)+1)}}if(s>0||i>0){t||=Object.create(null);t.comments={deleted:s,edited:i}}if(!t)return null;for(const[i,s]of e)t[i]=s.computeTelemetryFinalData(t[i]);return t}resetModifiedIds(){this.#Ei=null}updateEditor(t,e){const i=this.#Ti?.get(t);if(i){i.updateFromAnnotationLayer(e);return!0}return!1}getEditor(t){return this.#Ti?.get(t)||null}get modifiedIds(){if(this.#Ei)return this.#Ei;const t=[];if(this.#Ti)for(const e of this.#Ti.values())e.serialize()&&t.push(e.annotationElementId);let e="";if(t.length){const i=new MurmurHash3_64;i.update(t.join(","));e=i.hexdigest()}return this.#Ei={ids:new Set(t),hash:e}}[Symbol.iterator](){return this.#Si.entries()}}class PrintAnnotationStorage extends AnnotationStorage{#Pi=Y;constructor(t){super();const{serializable:e}=t;if(e===Y)return;const{map:i,hash:s,transfer:n}=e,r=structuredClone(i,n?{transfer:n}:null);this.#Pi={map:r,hash:s,transfer:[]}}get print(){unreachable("Should not call PrintAnnotationStorage.print")}get serializable(){return this.#Pi}get modifiedIds(){return shadow(this,"modifiedIds",{ids:new Set,hash:""})}}const Q="__forcedDependency",{floor:J,ceil:Z}=Math;function expandBBox(t,e,i,s,n,r){t[4*e+0]=Math.min(t[4*e+0],i);t[4*e+1]=Math.min(t[4*e+1],s);t[4*e+2]=Math.max(t[4*e+2],n);t[4*e+3]=Math.max(t[4*e+3],r)}const tt=new Uint32Array(new Uint8Array([255,255,0,0]).buffer)[0];class BBoxReader{#Di;#Mi;constructor(t,e){this.#Di=t;this.#Mi=e}get length(){return this.#Di.length}isEmpty(t){return this.#Di[t]===tt}minX(t){return this.#Mi[4*t+0]/256}minY(t){return this.#Mi[4*t+1]/256}maxX(t){return(this.#Mi[4*t+2]+1)/256}maxY(t){return(this.#Mi[4*t+3]+1)/256}}const ensureDebugMetadata=(t,e)=>t?.getOrInsertComputed(e,()=>({dependencies:new Set,isRenderingOperation:!1}));class CanvasBBoxTracker{#ki=[[1,0,0,1,0,0]];#Ii=[-1/0,-1/0,1/0,1/0];#Fi=new Float64Array(e);_pendingBBoxIdx=-1;#Bi;#Ri;#Oi;#Di;_savesStack=[];_markedContentStack=[];constructor(t,e){this.#Bi=t.width;this.#Ri=t.height;this.#Li(e)}growOperationsCount(t){t>=this.#Di.length&&this.#Li(t,this.#Di)}#Li(t,e){const i=new ArrayBuffer(4*t);this.#Oi=new Uint8ClampedArray(i);this.#Di=new Uint32Array(i);if(e&&e.length>0){this.#Di.set(e);this.#Di.fill(tt,e.length)}else this.#Di.fill(tt)}get clipBox(){return this.#Ii}save(t){this.#Ii={__proto__:this.#Ii};this._savesStack.push(t);return this}restore(t,e){const i=Object.getPrototypeOf(this.#Ii);if(null===i)return this;this.#Ii=i;const s=this._savesStack.pop();if(void 0!==s){e?.(s,t);this.#Di[t]=this.#Di[s]}return this}recordOpenMarker(t){this._savesStack.push(t);return this}getOpenMarker(){return 0===this._savesStack.length?null:this._savesStack.at(-1)}recordCloseMarker(t,e){const i=this._savesStack.pop();if(void 0!==i){e?.(i,t);this.#Di[t]=this.#Di[i]}return this}beginMarkedContent(t){this._markedContentStack.push(t);return this}endMarkedContent(t,e){const i=this._markedContentStack.pop();if(void 0!==i){e?.(i,t);this.#Di[t]=this.#Di[i]}return this}pushBaseTransform(t){this.#ki.push(Util.multiplyByDOMMatrix(this.#ki.at(-1),t.getTransform()));return this}popBaseTransform(){this.#ki.length>1&&this.#ki.pop();return this}resetBBox(t){if(this._pendingBBoxIdx!==t){this._pendingBBoxIdx=t;this.#Fi.set(e,0)}return this}recordClipBox(t,i,s,n,r,a){const o=Util.multiplyByDOMMatrix(this.#ki.at(-1),i.getTransform()),l=e.slice();Util.axialAlignedBoundingBox([s,r,n,a],o,l);const h=Util.intersect(this.#Ii,l);if(h){this.#Ii[0]=h[0];this.#Ii[1]=h[1];this.#Ii[2]=h[2];this.#Ii[3]=h[3]}else{this.#Ii[0]=this.#Ii[1]=1/0;this.#Ii[2]=this.#Ii[3]=-1/0}return this}recordBBox(t,i,s,n,r,a){const o=this.#Ii;if(o[0]===1/0)return this;const l=Util.multiplyByDOMMatrix(this.#ki.at(-1),i.getTransform());if(o[0]===-1/0){Util.axialAlignedBoundingBox([s,r,n,a],l,this.#Fi);return this}const h=e.slice();Util.axialAlignedBoundingBox([s,r,n,a],l,h);this.#Fi[0]=MathClamp(h[0],o[0],this.#Fi[0]);this.#Fi[1]=MathClamp(h[1],o[1],this.#Fi[1]);this.#Fi[2]=MathClamp(h[2],this.#Fi[2],o[2]);this.#Fi[3]=MathClamp(h[3],this.#Fi[3],o[3]);return this}recordFullPageBBox(t){this.#Fi[0]=Math.max(0,this.#Ii[0]);this.#Fi[1]=Math.max(0,this.#Ii[1]);this.#Fi[2]=Math.min(this.#Bi,this.#Ii[2]);this.#Fi[3]=Math.min(this.#Ri,this.#Ii[3]);return this}recordOperation(t,e=!1,i){if(this._pendingBBoxIdx!==t)return this;const s=J(256*this.#Fi[0]/this.#Bi),n=J(256*this.#Fi[1]/this.#Ri),r=Z(256*this.#Fi[2]/this.#Bi),a=Z(256*this.#Fi[3]/this.#Ri);expandBBox(this.#Oi,t,s,n,r,a);if(i)for(const e of i)for(const i of e)i!==t&&expandBBox(this.#Oi,i,s,n,r,a);e||(this._pendingBBoxIdx=-1);return this}bboxToClipBoxDropOperation(t){if(this._pendingBBoxIdx===t){this._pendingBBoxIdx=-1;this.#Ii[0]=Math.max(this.#Ii[0],this.#Fi[0]);this.#Ii[1]=Math.max(this.#Ii[1],this.#Fi[1]);this.#Ii[2]=Math.min(this.#Ii[2],this.#Fi[2]);this.#Ii[3]=Math.min(this.#Ii[3],this.#Fi[3])}return this}take(){return new BBoxReader(this.#Di,this.#Oi)}takeDebugMetadata(){throw new Error("Unreachable")}recordSimpleData(t,e){return this}recordIncrementalData(t,e){return this}resetIncrementalData(t,e){return this}recordNamedData(t,e){return this}recordSimpleDataFromNamed(t,e,i){return this}recordFutureForcedDependency(t,e){return this}inheritSimpleDataAsFutureForcedDependencies(t){return this}inheritPendingDependenciesAsFutureForcedDependencies(){return this}recordCharacterBBox(t,e,i,s=1,n=0,r=0,a){return this}getSimpleIndex(t){}recordDependencies(t,e){return this}recordNamedDependency(t,e){return this}recordShowTextOperation(t,e=!1){return this}}class CanvasDependencyTracker{#Ni={__proto__:null};#Ui={__proto__:null,transform:[],moveText:[],sameLineText:[],[Q]:[]};#Hi=new Map;#zi=new Set;#Gi=new Map;#Wi;#Vi;#ji;constructor(t,e=!1){this.#ji=t;if(e){this.#Wi=new Map;this.#Vi=(t,e)=>{ensureDebugMetadata(this.#Wi,e).dependencies.add(t)}}}get clipBox(){return this.#ji.clipBox}growOperationsCount(t){this.#ji.growOperationsCount(t)}save(t){this.#Ni={__proto__:this.#Ni};this.#Ui={__proto__:this.#Ui,transform:{__proto__:this.#Ui.transform},moveText:{__proto__:this.#Ui.moveText},sameLineText:{__proto__:this.#Ui.sameLineText},[Q]:{__proto__:this.#Ui[Q]}};this.#ji.save(t);return this}restore(t){this.#ji.restore(t,this.#Vi);const e=Object.getPrototypeOf(this.#Ni);if(null===e)return this;this.#Ni=e;this.#Ui=Object.getPrototypeOf(this.#Ui);return this}recordOpenMarker(t){this.#ji.recordOpenMarker(t,this.#Vi);return this}getOpenMarker(){return this.#ji.getOpenMarker()}recordCloseMarker(t){this.#ji.recordCloseMarker(t,this.#Vi);return this}beginMarkedContent(t){this.#ji.beginMarkedContent(t);return this}endMarkedContent(t){this.#ji.endMarkedContent(t,this.#Vi);return this}pushBaseTransform(t){this.#ji.pushBaseTransform(t);return this}popBaseTransform(){this.#ji.popBaseTransform();return this}recordSimpleData(t,e){this.#Ni[t]=e;return this}recordIncrementalData(t,e){this.#Ui[t].push(e);return this}resetIncrementalData(t,e){this.#Ui[t].length=0;return this}recordNamedData(t,e){this.#Hi.set(t,e);return this}recordSimpleDataFromNamed(t,e,i){this.#Ni[t]=this.#Hi.get(e)??i}recordFutureForcedDependency(t,e){this.recordIncrementalData(Q,e);return this}inheritSimpleDataAsFutureForcedDependencies(t){for(const e of t)e in this.#Ni&&this.recordFutureForcedDependency(e,this.#Ni[e]);return this}inheritPendingDependenciesAsFutureForcedDependencies(){for(const t of this.#zi)this.recordFutureForcedDependency(Q,t);return this}resetBBox(t){this.#ji.resetBBox(t);return this}recordClipBox(t,e,i,s,n,r){this.#ji.recordClipBox(t,e,i,s,n,r);return this}recordBBox(t,e,i,s,n,r){this.#ji.recordBBox(t,e,i,s,n,r);return this}recordCharacterBBox(t,e,i,s=1,n=0,r=0,a){const o=i.bbox;let l,h;if(o){l=o[2]!==o[0]&&o[3]!==o[1]&&this.#Gi.get(i);if(!1!==l){h=[0,0,0,0];Util.axialAlignedBoundingBox(o,i.fontMatrix,h);1===s&&0===n&&0===r||Util.scaleMinMax([s,0,0,-s,n,r],h);if(l)return this.recordBBox(t,e,h[0],h[2],h[1],h[3])}}if(!a)return this.recordFullPageBBox(t);const d=a();if(o&&h&&void 0===l){l=h[0]<=n-d.actualBoundingBoxLeft&&h[2]>=n+d.actualBoundingBoxRight&&h[1]<=r-d.actualBoundingBoxAscent&&h[3]>=r+d.actualBoundingBoxDescent;this.#Gi.set(i,l);if(l)return this.recordBBox(t,e,h[0],h[2],h[1],h[3])}return this.recordBBox(t,e,n-d.actualBoundingBoxLeft,n+d.actualBoundingBoxRight,r-d.actualBoundingBoxAscent,r+d.actualBoundingBoxDescent)}recordFullPageBBox(t){this.#ji.recordFullPageBBox(t);return this}getSimpleIndex(t){return this.#Ni[t]}recordDependencies(t,e){const i=this.#zi,s=this.#Ni,n=this.#Ui;for(const t of e)t in this.#Ni?i.add(s[t]):t in n&&n[t].forEach(i.add,i);return this}recordNamedDependency(t,e){this.#Hi.has(e)&&this.#zi.add(this.#Hi.get(e));return this}recordOperation(t,e=!1){this.recordDependencies(t,[Q]);if(this.#Wi){const e=ensureDebugMetadata(this.#Wi,t),{dependencies:i}=e;this.#zi.forEach(i.add,i);this.#ji._savesStack.forEach(i.add,i);this.#ji._markedContentStack.forEach(i.add,i);i.delete(t);e.isRenderingOperation=!0}const i=!e&&t===this.#ji._pendingBBoxIdx;this.#ji.recordOperation(t,e,[this.#zi,this.#ji._savesStack,this.#ji._markedContentStack]);i&&this.#zi.clear();return this}recordShowTextOperation(t,e=!1){const i=Array.from(this.#zi);this.recordOperation(t,e);this.recordIncrementalData("sameLineText",t);for(const t of i)this.recordIncrementalData("sameLineText",t);return this}bboxToClipBoxDropOperation(t,e=!1){const i=!e&&t===this.#ji._pendingBBoxIdx;this.#ji.bboxToClipBoxDropOperation(t);i&&this.#zi.clear();return this}take(){this.#Gi.clear();return this.#ji.take()}takeDebugMetadata(){return this.#Wi}}class CanvasNestedDependencyTracker{#$i;#Xi;#qi;#Ki=0;#Yi=0;constructor(t,e,i){if(t instanceof CanvasNestedDependencyTracker&&t.#qi===!!i)return t;this.#$i=t;this.#Xi=e;this.#qi=!!i}get clipBox(){return this.#$i.clipBox}growOperationsCount(){throw new Error("Unreachable")}save(t){this.#Yi++;this.#$i.save(this.#Xi);return this}restore(t){if(this.#Yi>0){this.#$i.restore(this.#Xi);this.#Yi--}return this}recordOpenMarker(t){this.#Ki++;return this}getOpenMarker(){return this.#Ki>0?this.#Xi:this.#$i.getOpenMarker()}recordCloseMarker(t){this.#Ki--;return this}beginMarkedContent(t){return this}endMarkedContent(t){return this}pushBaseTransform(t){this.#$i.pushBaseTransform(t);return this}popBaseTransform(){this.#$i.popBaseTransform();return this}recordSimpleData(t,e){this.#$i.recordSimpleData(t,this.#Xi);return this}recordIncrementalData(t,e){this.#$i.recordIncrementalData(t,this.#Xi);return this}resetIncrementalData(t,e){this.#$i.resetIncrementalData(t,this.#Xi);return this}recordNamedData(t,e){return this}recordSimpleDataFromNamed(t,e,i){this.#$i.recordSimpleDataFromNamed(t,e,this.#Xi);return this}recordFutureForcedDependency(t,e){this.#$i.recordFutureForcedDependency(t,this.#Xi);return this}inheritSimpleDataAsFutureForcedDependencies(t){this.#$i.inheritSimpleDataAsFutureForcedDependencies(t);return this}inheritPendingDependenciesAsFutureForcedDependencies(){this.#$i.inheritPendingDependenciesAsFutureForcedDependencies();return this}resetBBox(t){this.#qi||this.#$i.resetBBox(this.#Xi);return this}recordClipBox(t,e,i,s,n,r){this.#qi||this.#$i.recordClipBox(this.#Xi,e,i,s,n,r);return this}recordBBox(t,e,i,s,n,r){this.#qi||this.#$i.recordBBox(this.#Xi,e,i,s,n,r);return this}recordCharacterBBox(t,e,i,s,n,r,a){this.#qi||this.#$i.recordCharacterBBox(this.#Xi,e,i,s,n,r,a);return this}recordFullPageBBox(t){this.#qi||this.#$i.recordFullPageBBox(this.#Xi);return this}getSimpleIndex(t){return this.#$i.getSimpleIndex(t)}recordDependencies(t,e){this.#$i.recordDependencies(this.#Xi,e);return this}recordNamedDependency(t,e){this.#$i.recordNamedDependency(this.#Xi,e);return this}recordOperation(t){this.#$i.recordOperation(this.#Xi,!0);return this}recordShowTextOperation(t){this.#$i.recordShowTextOperation(this.#Xi,!0);return this}bboxToClipBoxDropOperation(t){this.#qi||this.#$i.bboxToClipBoxDropOperation(this.#Xi,!0);return this}take(){throw new Error("Unreachable")}takeDebugMetadata(){throw new Error("Unreachable")}}const et=["path","transform","filter","strokeColor","strokeAlpha","lineWidth","lineCap","lineJoin","miterLimit","dash"],it=["path","transform","filter","fillColor","fillAlpha","globalCompositeOperation","SMask"],st=["transform","SMask","filter","fillAlpha","strokeAlpha","globalCompositeOperation"],nt=["filter","fillColor","fillAlpha"],rt=["transform","leading","charSpacing","wordSpacing","hScale","textRise","moveText","textMatrix","font","fontObj","filter","fillColor","textRenderingMode","SMask","fillAlpha","strokeAlpha","globalCompositeOperation","sameLineText"],at=["transform"],ot=["transform","fillColor"];class CanvasImagesTracker{#Bi;#Ri;#Qi=4;#Ji=0;#Mi=new CanvasImagesTracker.#Zi(6*this.#Qi);static#Zi=FeatureTest.isFloat16ArraySupported?Float16Array:Float32Array;constructor(t){this.#Bi=t.width;this.#Ri=t.height}record(t,i,s,n){if(this.#Ji===this.#Qi){this.#Qi*=2;const t=new CanvasImagesTracker.#Zi(6*this.#Qi);t.set(this.#Mi);this.#Mi=t}const r=Util.domMatrixToTransform(t.getTransform());let a;if(n[0]!==1/0){const t=e.slice();Util.axialAlignedBoundingBox([0,-s,i,0],r,t);const o=Util.intersect(n,t);if(!o)return;const[l,h,d,c]=o;if(l!==t[0]||h!==t[1]||d!==t[2]||c!==t[3]){const t=Math.atan2(r[1],r[0]),e=Math.abs(Math.sin(t)),i=Math.abs(Math.cos(t));if(e<1e-6||i<1e-6||Math.abs(e-i)<1e-6)a=[l,h,l,c,d,h];else{const t=d-l,s=c-h,n=e*e,r=i*i,o=i*e,u=r-n,p=(s*r-t*o)/u;a=[l+(s*o-t*n)/u,h,l,h+p,d,c-p]}}}if(!a){a=[0,-s,0,0,i,-s];Util.applyTransform(a,r,0);Util.applyTransform(a,r,2);Util.applyTransform(a,r,4)}a[0]/=this.#Bi;a[1]/=this.#Ri;a[2]/=this.#Bi;a[3]/=this.#Ri;a[4]/=this.#Bi;a[5]/=this.#Ri;this.#Mi.set(a,6*this.#Ji);this.#Ji++}take(){return this.#Mi.subarray(0,6*this.#Ji)}}class FontLoader{#ts=new Set;constructor({ownerDocument:t=globalThis.document,styleElement:e=null}){this._document=t;this.nativeFontFaces=new Set;this.styleElement=null;this.loadingRequests=[];this.loadTestFontId=0}addNativeFontFace(t){this.nativeFontFaces.add(t);this._document.fonts.add(t)}removeNativeFontFace(t){this.nativeFontFaces.delete(t);this._document.fonts.delete(t)}insertRule(t){if(!this.styleElement){this.styleElement=this._document.createElement("style");this._document.documentElement.getElementsByTagName("head")[0].append(this.styleElement)}const e=this.styleElement.sheet;e.insertRule(t,e.cssRules.length)}clear(){for(const t of this.nativeFontFaces)this._document.fonts.delete(t);this.nativeFontFaces.clear();this.#ts.clear();if(this.styleElement){this.styleElement.remove();this.styleElement=null}}async loadSystemFont({systemFontInfo:t,disableFontFace:e,_inspectFont:i}){if(t&&!this.#ts.has(t.loadedName)){assert(!e,"loadSystemFont shouldn't be called when `disableFontFace` is set.");if(this.isFontLoadingAPISupported){const{loadedName:e,src:s,style:n}=t,r=new FontFace(e,s,n);this.addNativeFontFace(r);try{await r.load();this.#ts.add(e);i?.(t)}catch{warn(`Cannot load system font: ${t.baseFontName}, installing it could help to improve PDF rendering.`);this.removeNativeFontFace(r)}return}unreachable("Not implemented: loadSystemFont without the Font Loading API.")}}async bind(t){if(t.attached||t.missingFile&&!t.systemFontInfo)return;t.attached=!0;if(t.systemFontInfo){await this.loadSystemFont(t);return}if(this.isFontLoadingAPISupported){const e=t.createNativeFontFace();if(e){this.addNativeFontFace(e);try{await e.loaded}catch(i){warn(`Failed to load font '${e.family}': '${i}'.`);t.disableFontFace=!0;throw i}}return}const e=t.createFontFaceRule();if(e){this.insertRule(e);if(this.isSyncFontLoadingSupported)return;await new Promise(e=>{const i=this._queueLoadingCallback(e);this._prepareFontLoadEvent(t,i)})}}get isFontLoadingAPISupported(){return shadow(this,"isFontLoadingAPISupported",!!this._document?.fonts)}get isSyncFontLoadingSupported(){return shadow(this,"isSyncFontLoadingSupported",t||FeatureTest.platform.isFirefox)}_queueLoadingCallback(t){const{loadingRequests:e}=this,i={done:!1,complete:function completeRequest(){assert(!i.done,"completeRequest() cannot be called twice.");i.done=!0;for(;e.length>0&&e[0].done;){const t=e.shift();setTimeout(t.callback,0)}},callback:t};e.push(i);return i}get _loadTestFont(){return shadow(this,"_loadTestFont",atob("T1RUTwALAIAAAwAwQ0ZGIDHtZg4AAAOYAAAAgUZGVE1lkzZwAAAEHAAAABxHREVGABQAFQAABDgAAAAeT1MvMlYNYwkAAAEgAAAAYGNtYXABDQLUAAACNAAAAUJoZWFk/xVFDQAAALwAAAA2aGhlYQdkA+oAAAD0AAAAJGhtdHgD6AAAAAAEWAAAAAZtYXhwAAJQAAAAARgAAAAGbmFtZVjmdH4AAAGAAAAAsXBvc3T/hgAzAAADeAAAACAAAQAAAAEAALZRFsRfDzz1AAsD6AAAAADOBOTLAAAAAM4KHDwAAAAAA+gDIQAAAAgAAgAAAAAAAAABAAADIQAAAFoD6AAAAAAD6AABAAAAAAAAAAAAAAAAAAAAAQAAUAAAAgAAAAQD6AH0AAUAAAKKArwAAACMAooCvAAAAeAAMQECAAACAAYJAAAAAAAAAAAAAQAAAAAAAAAAAAAAAFBmRWQAwAAuAC4DIP84AFoDIQAAAAAAAQAAAAAAAAAAACAAIAABAAAADgCuAAEAAAAAAAAAAQAAAAEAAAAAAAEAAQAAAAEAAAAAAAIAAQAAAAEAAAAAAAMAAQAAAAEAAAAAAAQAAQAAAAEAAAAAAAUAAQAAAAEAAAAAAAYAAQAAAAMAAQQJAAAAAgABAAMAAQQJAAEAAgABAAMAAQQJAAIAAgABAAMAAQQJAAMAAgABAAMAAQQJAAQAAgABAAMAAQQJAAUAAgABAAMAAQQJAAYAAgABWABYAAAAAAAAAwAAAAMAAAAcAAEAAAAAADwAAwABAAAAHAAEACAAAAAEAAQAAQAAAC7//wAAAC7////TAAEAAAAAAAABBgAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAD/gwAyAAAAAQAAAAAAAAAAAAAAAAAAAAABAAQEAAEBAQJYAAEBASH4DwD4GwHEAvgcA/gXBIwMAYuL+nz5tQXkD5j3CBLnEQACAQEBIVhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYAAABAQAADwACAQEEE/t3Dov6fAH6fAT+fPp8+nwHDosMCvm1Cvm1DAz6fBQAAAAAAAABAAAAAMmJbzEAAAAAzgTjFQAAAADOBOQpAAEAAAAAAAAADAAUAAQAAAABAAAAAgABAAAAAAAAAAAD6AAAAAAAAA=="))}_prepareFontLoadEvent(t,e){function int32(t,e){return t.charCodeAt(e)<<24|t.charCodeAt(e+1)<<16|t.charCodeAt(e+2)<<8|255&t.charCodeAt(e+3)}function spliceString(t,e,i,s){return t.substring(0,e)+s+t.substring(e+i)}let i,s;const n=this._document.createElement("canvas");n.width=1;n.height=1;const r=n.getContext("2d");let a=0;const o=`lt${Date.now()}${this.loadTestFontId++}`;let l=this._loadTestFont;l=spliceString(l,976,o.length,o);const h=1482184792;let d=int32(l,16);for(i=0,s=o.length-3;i>24&255,t>>16&255,t>>8&255,255&t)}(d));const c=`@font-face {font-family:"${o}";src:${`url(data:font/opentype;base64,${btoa(l)});`}}`;this.insertRule(c);const u=this._document.createElement("div");u.style.visibility="hidden";u.style.width=u.style.height="10px";u.style.position="absolute";u.style.top=u.style.left="0px";for(const e of[t.loadedName,o]){const t=this._document.createElement("span");t.textContent="Hi";t.style.fontFamily=e;u.append(t)}this._document.body.append(u);!function isFontReady(t,e){if(++a>30){warn("Load test font never loaded.");e();return}r.font="30px "+t;r.fillText(".",0,20);r.getImageData(0,0,1,1).data[3]>0?e():setTimeout(isFontReady.bind(null,t,e))}(o,()=>{u.remove();e.complete()})}}class FontFaceObject{compiledGlyphs=Object.create(null);#es;constructor(t,e=null,i,s){this.#es=t;this._inspectFont=e;i&&(this.charProcOperatorList=i);s&&Object.assign(this,s)}createNativeFontFace(){if(!this.data||this.disableFontFace)return null;let t;if(this.cssFontInfo){const e={weight:this.cssFontInfo.fontWeight};this.cssFontInfo.italicAngle&&(e.style=`oblique ${this.cssFontInfo.italicAngle}deg`);t=new FontFace(this.cssFontInfo.fontFamily,this.data,e)}else t=new FontFace(this.loadedName,this.data,{});this._inspectFont?.(this);return t}createFontFaceRule(){if(!this.data||this.disableFontFace)return null;const t=`url(data:${this.mimetype};base64,${this.data.toBase64()});`;let e;if(this.cssFontInfo){let i=`font-weight: ${this.cssFontInfo.fontWeight};`;this.cssFontInfo.italicAngle&&(i+=`font-style: oblique ${this.cssFontInfo.italicAngle}deg;`);e=`@font-face {font-family:"${this.cssFontInfo.fontFamily}";${i}src:${t}}`}else e=`@font-face {font-family:"${this.loadedName}";src:${t}}`;this._inspectFont?.(this,t);return e}getPathGenerator(t,e){if(void 0!==this.compiledGlyphs[e])return this.compiledGlyphs[e];const i=this.loadedName+"_path_"+e;let s;try{s=t.get(i)}catch(t){warn(`getPathGenerator - ignoring character: "${t}".`)}const n=makePathFromDrawOPS(s?.path);this.fontExtraProperties||t.delete(i);return this.compiledGlyphs[e]=n}get black(){return this.#es.black}get bold(){return this.#es.bold}get disableFontFace(){return this.#es.disableFontFace}set disableFontFace(t){shadow(this,"disableFontFace",!!t)}get fontExtraProperties(){return this.#es.fontExtraProperties}get isInvalidPDFjsFont(){return this.#es.isInvalidPDFjsFont}get isType3Font(){return this.#es.isType3Font}get italic(){return this.#es.italic}get missingFile(){return this.#es.missingFile}get remeasure(){return this.#es.remeasure}get vertical(){return this.#es.vertical}get ascent(){return this.#es.ascent}get defaultWidth(){return this.#es.defaultWidth}get descent(){return this.#es.descent}get bbox(){return this.#es.bbox}get fontMatrix(){return this.#es.fontMatrix}get fallbackName(){return this.#es.fallbackName}get loadedName(){return this.#es.loadedName}get mimetype(){return this.#es.mimetype}get name(){return this.#es.name}get data(){return this.#es.data}clearData(){this.#es.clearData()}get cssFontInfo(){return this.#es.cssFontInfo}get systemFontInfo(){return this.#es.systemFontInfo}get defaultVMetrics(){return this.#es.defaultVMetrics}}class CSS_FONT_INFO{static strings=["fontFamily","fontWeight","italicAngle"]}class SYSTEM_FONT_INFO{static strings=["css","loadedName","baseFontName","src"]}class FONT_INFO{static bools=["black","bold","disableFontFace","fontExtraProperties","isInvalidPDFjsFont","isType3Font","italic","missingFile","remeasure","vertical"];static numbers=["ascent","defaultWidth","descent"];static strings=["fallbackName","loadedName","mimetype","name"];static OFFSET_NUMBERS=Math.ceil(2*this.bools.length/8);static OFFSET_BBOX=this.OFFSET_NUMBERS+8*this.numbers.length;static OFFSET_FONT_MATRIX=this.OFFSET_BBOX+1+8;static OFFSET_DEFAULT_VMETRICS=this.OFFSET_FONT_MATRIX+1+48;static OFFSET_STRINGS=this.OFFSET_DEFAULT_VMETRICS+1+6}class PATTERN_INFO{static KIND=0;static HAS_BBOX=1;static HAS_BACKGROUND=2;static SHADING_TYPE=3;static N_COORD=4;static N_COLOR=8;static N_STOP=12;static N_FIGURES=16}class CssFontInfo{#is;#ss=new TextDecoder;#ns;constructor(t){this.#is=t;this.#ns=new DataView(t)}#rs(t){assert(t>i&3;return 0===s?void 0:2===s}get black(){return this.#as(0)}get bold(){return this.#as(1)}get disableFontFace(){return this.#as(2)}get fontExtraProperties(){return this.#as(3)}get isInvalidPDFjsFont(){return this.#as(4)}get isType3Font(){return this.#as(5)}get italic(){return this.#as(6)}get missingFile(){return this.#as(7)}get remeasure(){return this.#as(8)}get vertical(){return this.#as(9)}#os(t){assert(t0){i=e.slice();for(let t=0,e=h.length;t"object"==typeof t&&Number.isInteger(t?.num)&&t.num>=0&&Number.isInteger(t?.gen)&&t.gen>=0,lt=function _isValidExplicitDest(t,e,i){if(!Array.isArray(i)||i.length<2)return!1;const[s,n,...r]=i;if(!t(s)&&!Number.isInteger(s))return!1;if(!e(n))return!1;const a=r.length;let o=!0;switch(n.name){case"XYZ":if(a<2||a>3)return!1;break;case"Fit":case"FitB":return 0===a;case"FitH":case"FitBH":case"FitV":case"FitBV":if(a>1)return!1;break;case"FitR":if(4!==a)return!1;o=!1;break;default:return!1}for(const t of r)if(!("number"==typeof t||o&&null===t))return!1;return!0}.bind(null,isRefProxy,t=>"object"==typeof t&&"string"==typeof t?.name);class LoopbackPort{#ds=new Map;#cs=Promise.resolve();postMessage(t,e){const i={data:structuredClone(t,e?{transfer:e}:null)};this.#cs.then(()=>{for(const[t]of this.#ds)t.call(this,i)})}addEventListener(t,e,i=null){let s=null;if(i?.signal instanceof AbortSignal){const{signal:n}=i;if(n.aborted){warn("LoopbackPort - cannot use an `aborted` signal.");return}const onAbort=()=>this.removeEventListener(t,e);s=()=>n.removeEventListener("abort",onAbort);n.addEventListener("abort",onAbort)}this.#ds.set(e,s)}removeEventListener(t,e){const i=this.#ds.get(e);i?.();this.#ds.delete(e)}terminate(){for(const[,t]of this.#ds)t?.();this.#ds.clear()}}const ht=1,dt=2,ct=1,ut=2,pt=3,gt=4,mt=5,ft=6,bt=7,yt=8;function onFn(){}function wrapReason(t){if(t instanceof AbortException||t instanceof InvalidPDFException||t instanceof PasswordException||t instanceof ResponseException||t instanceof UnknownErrorException)return t;t instanceof Error||"object"==typeof t&&null!==t||unreachable('wrapReason: Expected "reason" to be a (possibly cloned) Error.');switch(t.name){case"AbortException":return new AbortException(t.message);case"InvalidPDFException":return new InvalidPDFException(t.message);case"PasswordException":return new PasswordException(t.message,t.code);case"ResponseException":return new ResponseException(t.message,t.status,t.missing);case"UnknownErrorException":return new UnknownErrorException(t.message,t.details)}return new UnknownErrorException(t.message,t.toString())}class MessageHandler{#us=new AbortController;constructor(t,e,i){this.sourceName=t;this.targetName=e;this.comObj=i;this.callbackId=1;this.streamId=1;this.streamSinks=Object.create(null);this.streamControllers=Object.create(null);this.callbackCapabilities=Object.create(null);this.actionHandler=Object.create(null);i.addEventListener("message",this.#ps.bind(this),{signal:this.#us.signal})}#ps({data:t}){if(t.targetName!==this.sourceName)return;if(t.stream){this.#gs(t);return}if(t.callback){const e=t.callbackId,i=this.callbackCapabilities[e];if(!i)throw new Error(`Cannot resolve callback ${e}`);delete this.callbackCapabilities[e];if(t.callback===ht)i.resolve(t.data);else{if(t.callback!==dt)throw new Error("Unexpected callback case");i.reject(wrapReason(t.reason))}return}const e=this.actionHandler[t.action];if(!e)throw new Error(`Unknown action from worker: ${t.action}`);if(t.callbackId){const i=this.sourceName,s=t.sourceName,n=this.comObj;Promise.try(e,t.data).then(function(e){n.postMessage({sourceName:i,targetName:s,callback:ht,callbackId:t.callbackId,data:e})},function(e){n.postMessage({sourceName:i,targetName:s,callback:dt,callbackId:t.callbackId,reason:wrapReason(e)})});return}t.streamId?this.#ms(t):e(t.data)}on(t,e){const i=this.actionHandler;if(i[t])throw new Error(`There is already an actionName called "${t}"`);i[t]=e}send(t,e,i){this.comObj.postMessage({sourceName:this.sourceName,targetName:this.targetName,action:t,data:e},i)}sendWithPromise(t,e,i){const s=this.callbackId++,n=Promise.withResolvers();this.callbackCapabilities[s]=n;try{this.comObj.postMessage({sourceName:this.sourceName,targetName:this.targetName,action:t,callbackId:s,data:e},i)}catch(t){n.reject(t)}return n.promise}sendWithStream(t,e,i,s){const n=this.streamId++,r=this.sourceName,a=this.targetName,o=this.comObj;return new ReadableStream({start:i=>{const l=Promise.withResolvers();this.streamControllers[n]={controller:i,startCall:l,pullCall:null,cancelCall:null,isClosed:!1};o.postMessage({sourceName:r,targetName:a,action:t,streamId:n,data:e,desiredSize:i.desiredSize},s);return l.promise},pull:t=>{const e=Promise.withResolvers();this.streamControllers[n].pullCall=e;o.postMessage({sourceName:r,targetName:a,stream:ft,streamId:n,desiredSize:t.desiredSize});return e.promise},cancel:t=>{assert(t instanceof Error,"cancel must have a valid reason");const e=Promise.withResolvers();this.streamControllers[n].cancelCall=e;this.streamControllers[n].isClosed=!0;o.postMessage({sourceName:r,targetName:a,stream:ct,streamId:n,reason:wrapReason(t)});return e.promise}},i)}#ms(t){const e=t.streamId,i=this.sourceName,s=t.sourceName,n=this.comObj,r=this,a=this.actionHandler[t.action],o={enqueue(t,r=1,a){if(this.isCancelled)return;const o=this.desiredSize;this.desiredSize-=r;if(o>0&&this.desiredSize<=0){this.sinkCapability=Promise.withResolvers();this.ready=this.sinkCapability.promise}n.postMessage({sourceName:i,targetName:s,stream:gt,streamId:e,chunk:t},a)},close(){if(!this.isCancelled){this.isCancelled=!0;n.postMessage({sourceName:i,targetName:s,stream:pt,streamId:e});delete r.streamSinks[e]}},error(t){assert(t instanceof Error,"error must have a valid reason");if(!this.isCancelled){this.isCancelled=!0;n.postMessage({sourceName:i,targetName:s,stream:mt,streamId:e,reason:wrapReason(t)})}},sinkCapability:Promise.withResolvers(),onPull:null,onCancel:null,isCancelled:!1,desiredSize:t.desiredSize,ready:null};o.sinkCapability.resolve();o.ready=o.sinkCapability.promise;this.streamSinks[e]=o;Promise.try(a,t.data,o).then(function(){n.postMessage({sourceName:i,targetName:s,stream:yt,streamId:e,success:!0})},function(t){n.postMessage({sourceName:i,targetName:s,stream:yt,streamId:e,reason:wrapReason(t)})})}#gs(t){const e=t.streamId,i=this.sourceName,s=t.sourceName,n=this.comObj,r=this.streamControllers[e],a=this.streamSinks[e];switch(t.stream){case yt:t.success?r.startCall.resolve():r.startCall.reject(wrapReason(t.reason));break;case bt:t.success?r.pullCall.resolve():r.pullCall.reject(wrapReason(t.reason));break;case ft:if(!a){n.postMessage({sourceName:i,targetName:s,stream:bt,streamId:e,success:!0});break}a.desiredSize<=0&&t.desiredSize>0&&a.sinkCapability.resolve();a.desiredSize=t.desiredSize;Promise.try(a.onPull||onFn).then(function(){n.postMessage({sourceName:i,targetName:s,stream:bt,streamId:e,success:!0})},function(t){n.postMessage({sourceName:i,targetName:s,stream:bt,streamId:e,reason:wrapReason(t)})});break;case gt:assert(r,"enqueue should have stream controller");if(r.isClosed)break;r.controller.enqueue(t.chunk);break;case pt:assert(r,"close should have stream controller");if(r.isClosed)break;r.isClosed=!0;r.controller.close();this.#fs(r,e);break;case mt:assert(r,"error should have stream controller");r.controller.error(wrapReason(t.reason));this.#fs(r,e);break;case ut:t.success?r.cancelCall.resolve():r.cancelCall.reject(wrapReason(t.reason));this.#fs(r,e);break;case ct:if(!a)break;const o=wrapReason(t.reason);Promise.try(a.onCancel||onFn,o).then(function(){n.postMessage({sourceName:i,targetName:s,stream:ut,streamId:e,success:!0})},function(t){n.postMessage({sourceName:i,targetName:s,stream:ut,streamId:e,reason:wrapReason(t)})});a.sinkCapability.reject(o);a.isCancelled=!0;delete this.streamSinks[e];break;default:throw new Error("Unexpected stream case")}}async#fs(t,e){await Promise.allSettled([t.startCall?.promise,t.pullCall?.promise,t.cancelCall?.promise]);delete this.streamControllers[e]}destroy(){this.#us?.abort();this.#us=null}}class BaseBinaryDataFactory{#bs=Object.freeze({cMapUrl:"CMap",standardFontDataUrl:"font",wasmUrl:"wasm"});constructor({cMapUrl:t=null,standardFontDataUrl:e=null,wasmUrl:i=null}){this.cMapUrl=t;this.standardFontDataUrl=e;this.wasmUrl=i}async fetch({kind:t,filename:e}){switch(t){case"cMapUrl":case"standardFontDataUrl":case"wasmUrl":break;default:unreachable(`Not implemented: ${t}`)}const i=this[t];if(!i)throw new Error(`Ensure that the \`${t}\` API parameter is provided.`);const s=`${i}${e}`;return this._fetch(s,t).catch(e=>{throw new Error(`Unable to load ${this.#bs[t]} data at: ${s}`)})}async _fetch(t,e){unreachable("Abstract method `_fetch` called.")}}class DOMBinaryDataFactory extends BaseBinaryDataFactory{async _fetch(t,e){const i="cMapUrl"!==e||t.endsWith(".bcmap")?"bytes":"text",s=await fetchData(t,i);return s instanceof Uint8Array?s:stringToBytes(s)}}class BaseCanvasFactory{#ys=!1;constructor({enableHWA:t=!1}){this.#ys=t}create(t,e){if(t<=0||e<=0)throw new Error("Invalid canvas size");const i=this._createCanvas(t,e);return{canvas:i,context:i.getContext("2d",{willReadFrequently:!this.#ys})}}reset({canvas:t},e,i){if(!t)throw new Error("Canvas is not specified");if(e<=0||i<=0)throw new Error("Invalid canvas size");t.width=e;t.height=i}destroy(t){const{canvas:e}=t;if(!e)throw new Error("Canvas is not specified");e.width=e.height=0;t.canvas=null;t.context=null}_createCanvas(t,e){unreachable("Abstract method `_createCanvas` called.")}}class DOMCanvasFactory extends BaseCanvasFactory{constructor({ownerDocument:t=globalThis.document,enableHWA:e=!1}){super({enableHWA:e});this._document=t}_createCanvas(t,e){const i=this._document.createElement("canvas");i.width=t;i.height=e;return i}}class BaseFilterFactory{addFilter(t){return"none"}addHCMFilter(t,e){return"none"}addAlphaFilter(t){return"none"}addLuminosityFilter(t){return"none"}addHighlightHCMFilter(t,e,i,s,n){return"none"}destroy(t=!1){}}class DOMFilterFactory extends BaseFilterFactory{#As;#vs;#ws;#xs;#Cs;#Es;#S=0;constructor({docId:t,ownerDocument:e=globalThis.document}){super();this.#xs=t;this.#Cs=e}get#P(){return this.#vs||=new Map}get#Ts(){return this.#Es||=new Map}get#Ss(){if(!this.#ws){const t=this.#Cs.createElement("div"),{style:e}=t;e.visibility="hidden";e.contain="strict";e.width=e.height=0;e.position="absolute";e.top=e.left=0;e.zIndex=-1;const i=this.#Cs.createElementNS(V,"svg");i.setAttribute("width",0);i.setAttribute("height",0);this.#ws=this.#Cs.createElementNS(V,"defs");t.append(i);i.append(this.#ws);this.#Cs.body.append(t)}return this.#ws}#_s(t){if(1===t.length){const e=t[0],i=new Array(256);for(let t=0;t<256;t++)i[t]=e[t]/255;const s=i.join(",");return[s,s,s]}const[e,i,s]=t,n=new Array(256),r=new Array(256),a=new Array(256);for(let t=0;t<256;t++){n[t]=e[t]/255;r[t]=i[t]/255;a[t]=s[t]/255}return[n.join(","),r.join(","),a.join(",")]}#Ps(t){if(void 0===this.#As){this.#As="";const t=this.#Cs.URL;t!==this.#Cs.baseURI&&(isDataScheme(t)?warn('#createUrl: ignore "data:"-URL for performance reasons.'):this.#As=updateUrlHash(t,""))}return`url(${this.#As}#${t})`}addFilter(t){if(!t)return"none";let e=this.#P.get(t);if(e)return e;const[i,s,n]=this.#_s(t),r=1===t.length?i:`${i}${s}${n}`;e=this.#P.get(r);if(e){this.#P.set(t,e);return e}const a=`g_${this.#xs}_transfer_map_${this.#S++}`,o=this.#Ps(a);this.#P.set(t,o);this.#P.set(r,o);const l=this.#Ds(a);this.#Ms(i,s,n,l);return o}addHCMFilter(t,e){const i=`${t}-${e}`,s="base";let n=this.#Ts.get(s);if(n?.key===i)return n.url;if(n){n.filter?.remove();n.key=i;n.url="none";n.filter=null}else{n={key:i,url:"none",filter:null};this.#Ts.set(s,n)}if(!t||!e)return n.url;const r=this.#ks(t);t=Util.makeHexColor(...r);const a=this.#ks(e);e=Util.makeHexColor(...a);this.#Ss.style.color="";if("#000000"===t&&"#ffffff"===e||t===e)return n.url;const o=new Array(256);for(let t=0;t<=255;t++){const e=t/255;o[t]=e<=.03928?e/12.92:((e+.055)/1.055)**2.4}const l=o.join(","),h=`g_${this.#xs}_hcm_filter`,d=n.filter=this.#Ds(h);this.#Ms(l,l,l,d);this.#Is(d);const getSteps=(t,e)=>{const i=r[t]/255,s=a[t]/255,n=new Array(e+1);for(let t=0;t<=e;t++)n[t]=i+t/e*(s-i);return n.join(",")};this.#Ms(getSteps(0,5),getSteps(1,5),getSteps(2,5),d);n.url=this.#Ps(h);return n.url}addAlphaFilter(t){let e=this.#P.get(t);if(e)return e;const[i]=this.#_s([t]),s=`alpha_${i}`;e=this.#P.get(s);if(e){this.#P.set(t,e);return e}const n=`g_${this.#xs}_alpha_map_${this.#S++}`,r=this.#Ps(n);this.#P.set(t,r);this.#P.set(s,r);const a=this.#Ds(n);this.#Fs(i,a);return r}addLuminosityFilter(t){let e,i,s=this.#P.get(t||"luminosity");if(s)return s;if(t){[e]=this.#_s([t]);i=`luminosity_${e}`}else i="luminosity";s=this.#P.get(i);if(s){this.#P.set(t,s);return s}const n=`g_${this.#xs}_luminosity_map_${this.#S++}`,r=this.#Ps(n);this.#P.set(t,r);this.#P.set(i,r);const a=this.#Ds(n);this.#Bs(a);t&&this.#Fs(e,a);return r}addHighlightHCMFilter(t,e,i,s,n){const r=`${e}-${i}-${s}-${n}`;let a=this.#Ts.get(t);if(a?.key===r)return a.url;if(a){a.filter?.remove();a.key=r;a.url="none";a.filter=null}else{a={key:r,url:"none",filter:null};this.#Ts.set(t,a)}if(!e||!i)return a.url;const[o,l]=[e,i].map(this.#ks.bind(this));let h=Math.round(.2126*o[0]+.7152*o[1]+.0722*o[2]),d=Math.round(.2126*l[0]+.7152*l[1]+.0722*l[2]),[c,u]=[s,n].map(this.#ks.bind(this));d{const s=new Array(256),n=(d-h)/i,r=t/255,a=(e-t)/(255*i);let o=0;for(let t=0;t<=i;t++){const e=Math.round(h+t*n),i=r+t*a;for(let t=o;t<=e;t++)s[t]=i;o=e+1}for(let t=o;t<256;t++)s[t]=s[o-1];return s.join(",")},p=`g_${this.#xs}_hcm_${t}_filter`,g=a.filter=this.#Ds(p);this.#Is(g);this.#Ms(getSteps(c[0],u[0],5),getSteps(c[1],u[1],5),getSteps(c[2],u[2],5),g);a.url=this.#Ps(p);return a.url}destroy(t=!1){if(!t||!this.#Es?.size){this.#ws?.parentNode.parentNode.remove();this.#ws=null;this.#vs?.clear();this.#vs=null;this.#Es?.clear();this.#Es=null;this.#S=0}}#Bs(t){const e=this.#Cs.createElementNS(V,"feColorMatrix");e.setAttribute("type","matrix");e.setAttribute("values","0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.3 0.59 0.11 0 0");t.append(e)}#Is(t){const e=this.#Cs.createElementNS(V,"feColorMatrix");e.setAttribute("type","matrix");e.setAttribute("values","0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0 0 0 1 0");t.append(e)}#Ds(t){const e=this.#Cs.createElementNS(V,"filter");e.setAttribute("color-interpolation-filters","sRGB");e.setAttribute("id",t);this.#Ss.append(e);return e}#Rs(t,e,i){const s=this.#Cs.createElementNS(V,e);s.setAttribute("type","discrete");s.setAttribute("tableValues",i);t.append(s)}#Ms(t,e,i,s){const n=this.#Cs.createElementNS(V,"feComponentTransfer");s.append(n);this.#Rs(n,"feFuncR",t);this.#Rs(n,"feFuncG",e);this.#Rs(n,"feFuncB",i)}#Fs(t,e){const i=this.#Cs.createElementNS(V,"feComponentTransfer");e.append(i);this.#Rs(i,"feFuncA",t)}#ks(t){this.#Ss.style.color=t;return getRGB(getComputedStyle(this.#Ss).getPropertyValue("color"))}}t&&warn("Please use the `legacy` build in Node.js environments.");class NodeFilterFactory extends BaseFilterFactory{}class NodeCanvasFactory extends BaseCanvasFactory{_createCanvas(t,e){return process.getBuiltinModule("module").createRequire(import.meta.url)("@napi-rs/canvas").createCanvas(t,e)}}class NodeBinaryDataFactory extends BaseBinaryDataFactory{async _fetch(t,e){return async function node_utils_fetchData(t){const e=process.getBuiltinModule("fs/promises"),i=await e.readFile(t);return new Uint8Array(i)}(t)}}const At=new class WebGPU{#Os=null;#Ls=null;#Ns=null;#Us=null;async#Hs(){if(!globalThis.navigator?.gpu)return!1;try{const t=await navigator.gpu.requestAdapter();if(!t)return!1;this.#Us=navigator.gpu.getPreferredCanvasFormat();this.#Ls=await t.requestDevice();return!0}catch{return!1}}init(){return this.#Os||=this.#Hs()}get isReady(){return null!==this.#Ls}loadMeshShader(){if(!this.#Ls||this.#Ns)return;const t=this.#Ls.createShaderModule({code:"\nstruct Uniforms {\n offsetX : f32,\n offsetY : f32,\n scaleX : f32,\n scaleY : f32,\n paddedWidth : f32,\n paddedHeight : f32,\n borderSize : f32,\n _pad : f32,\n};\n\n@group(0) @binding(0) var u : Uniforms;\n\nstruct VertexInput {\n @location(0) position : vec2,\n @location(1) color : vec4,\n};\n\nstruct VertexOutput {\n @builtin(position) position : vec4,\n @location(0) color : vec3,\n};\n\n@vertex\nfn vs_main(in : VertexInput) -> VertexOutput {\n var out : VertexOutput;\n let cx = (in.position.x + u.offsetX) * u.scaleX;\n let cy = (in.position.y + u.offsetY) * u.scaleY;\n out.position = vec4(\n ((cx + u.borderSize) / u.paddedWidth) * 2.0 - 1.0,\n 1.0 - ((cy + u.borderSize) / u.paddedHeight) * 2.0,\n 0.0,\n 1.0\n );\n out.color = in.color.rgb;\n return out;\n}\n\n@fragment\nfn fs_main(in : VertexOutput) -> @location(0) vec4 {\n return vec4(in.color, 1.0);\n}\n"});this.#Ns=this.#Ls.createRenderPipeline({layout:"auto",vertex:{module:t,entryPoint:"vs_main",buffers:[{arrayStride:8,attributes:[{shaderLocation:0,offset:0,format:"float32x2"}]},{arrayStride:4,attributes:[{shaderLocation:1,offset:0,format:"unorm8x4"}]}]},fragment:{module:t,entryPoint:"fs_main",targets:[{format:this.#Us}]},primitive:{topology:"triangle-list"}})}draw(t,e,i,s,n,r,a,o){this.loadMeshShader();const l=this.#Ls,{offsetX:h,offsetY:d,scaleX:c,scaleY:u}=s,p=l.createBuffer({size:Math.max(t.byteLength,4),usage:GPUBufferUsage.VERTEX|GPUBufferUsage.COPY_DST});t.byteLength>0&&l.queue.writeBuffer(p,0,t);const g=l.createBuffer({size:Math.max(e.byteLength,4),usage:GPUBufferUsage.VERTEX|GPUBufferUsage.COPY_DST});e.byteLength>0&&l.queue.writeBuffer(g,0,e);const m=l.createBuffer({size:32,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST});l.queue.writeBuffer(m,0,new Float32Array([h,d,c,u,r,a,o,0]));const f=l.createBindGroup({layout:this.#Ns.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:m}}]}),b=new OffscreenCanvas(r,a),y=b.getContext("webgpu");y.configure({device:l,format:this.#Us,alphaMode:n?"opaque":"premultiplied"});const A=n?{r:n[0]/255,g:n[1]/255,b:n[2]/255,a:1}:{r:0,g:0,b:0,a:0},v=l.createCommandEncoder(),w=v.beginRenderPass({colorAttachments:[{view:y.getCurrentTexture().createView(),clearValue:A,loadOp:"clear",storeOp:"store"}]});if(i>0){w.setPipeline(this.#Ns);w.setBindGroup(0,f);w.setVertexBuffer(0,p);w.setVertexBuffer(1,g);w.draw(i)}w.end();l.queue.submit([v.finish()]);p.destroy();g.destroy();m.destroy();return b.transferToImageBitmap()}};const vt="Fill",wt="Stroke",xt="Shading";function applyBoundingBox(t,e){if(!e)return;const i=e[2]-e[0],s=e[3]-e[1],n=new Path2D;n.rect(e[0],e[1],i,s);t.clip(n)}class BaseShadingPattern{isModifyingCurrentTransform(){return!1}getPattern(){unreachable("Abstract method `getPattern` called.")}}class RadialAxialShadingPattern extends BaseShadingPattern{constructor(t){super();this._type=t[1];this._bbox=t[2];this._colorStops=t[3];this._p0=t[4];this._p1=t[5];this._r0=t[6];this._r1=t[7];this.matrix=null}isOriginBased(){return 0===this._p0[0]&&0===this._p0[1]&&(!this.isRadial()||0===this._p1[0]&&0===this._p1[1])}isRadial(){return"radial"===this._type}areConic(){if(!this.isRadial())return!1;const t=Math.hypot(this._p0[0]-this._p1[0],this._p0[1]-this._p1[1]);return t+this._r1>this._r0&&t+this._r0>this._r1}_createGradient(t,e=null){let i,s=this._p0,n=this._p1;if(e){s=s.slice();n=n.slice();Util.applyTransform(s,e);Util.applyTransform(n,e)}if("axial"===this._type)i=t.createLinearGradient(s[0],s[1],n[0],n[1]);else if("radial"===this._type){let r=this._r0,a=this._r1;if(e){const t=new Float32Array(2);Util.singularValueDecompose2dScale(e,t);r*=t[0];a*=t[0]}i=t.createRadialGradient(s[0],s[1],r,n[0],n[1],a)}for(const t of this._colorStops)i.addColorStop(t[0],t[1]);return i}_createReversedGradient(t,e=null){let i=this._p1,s=this._p0;if(e){i=i.slice();s=s.slice();Util.applyTransform(i,e);Util.applyTransform(s,e)}let n=this._r1,r=this._r0;if(e){const t=new Float32Array(2);Util.singularValueDecompose2dScale(e,t);n*=t[0];r*=t[0]}const a=t.createRadialGradient(i[0],i[1],n,s[0],s[1],r),o=this._colorStops.map(([t,e])=>[1-t,e]).reverse();for(const[t,e]of o)a.addColorStop(t,e);return a}getPattern(t,e,i,s){let n;if(s===wt||s===vt){if(this.isOriginBased()){let s=Util.transform(i,e.baseTransform);this.matrix&&(s=Util.transform(s,this.matrix));const n=.001,r=Math.hypot(s[0],s[1]),a=Math.hypot(s[2],s[3]),o=(s[0]*s[2]+s[1]*s[3])/(r*a);if(Math.abs(o)l[2*s+1]){u=i;i=s;s=u;u=r;r=a;a=u}if(l[2*s+1]>l[2*n+1]){u=s;s=n;n=u;u=a;a=o;o=u}if(l[2*i+1]>l[2*s+1]){u=i;i=s;s=u;u=r;r=a;a=u}const p=(l[2*i]+e.offsetX)*e.scaleX,g=(l[2*i+1]+e.offsetY)*e.scaleY,m=(l[2*s]+e.offsetX)*e.scaleX,f=(l[2*s+1]+e.offsetY)*e.scaleY,b=(l[2*n]+e.offsetX)*e.scaleX,y=(l[2*n+1]+e.offsetY)*e.scaleY;if(g>=y)return;const A=h[4*r],v=h[4*r+1],w=h[4*r+2],x=h[4*a],C=h[4*a+1],E=h[4*a+2],T=h[4*o],S=h[4*o+1],_=h[4*o+2],P=Math.round(g),D=Math.round(y);let M,k,I,F,B,R,O,L;for(let t=P;t<=D;t++){if(ty?1:f===y?0:(f-t)/(f-y);M=m-(m-b)*e;k=x-(x-T)*e;I=C-(C-S)*e;F=E-(E-_)*e}let e;e=ty?1:(g-t)/(g-y);B=p-(p-b)*e;R=A-(A-T)*e;O=v-(v-S)*e;L=w-(w-_)*e;const i=Math.round(Math.min(M,B)),s=Math.round(Math.max(M,B));let n=c*t+4*i;for(let t=i;t<=s;t++){e=(M-t)/(M-B);e<0?e=0:e>1&&(e=1);d[n++]=k-(k-R)*e|0;d[n++]=I-(I-O)*e|0;d[n++]=F-(F-L)*e|0;d[n++]=255}}}class MeshShadingPattern extends BaseShadingPattern{constructor(t){super();this._posData=t[2];this._colData=t[3];this._vertexCount=t[4];this._bounds=t[5];this._bbox=t[6];this._background=t[7];this.matrix=null;!function loadMeshShader(){At.loadMeshShader()}()}_createMeshCanvas(t,e,i){const s=Math.floor(this._bounds[0]),n=Math.floor(this._bounds[1]),r=Math.ceil(this._bounds[2])-s,a=Math.ceil(this._bounds[3])-n,o=Math.min(Math.ceil(Math.abs(r*t[0]*1.1)),3e3)||1,l=Math.min(Math.ceil(Math.abs(a*t[1]*1.1)),3e3)||1,h=r?r/o:1,d=a?a/l:1,c={coords:this._posData,colors:this._colData,offsetX:-s,offsetY:-n,scaleX:1/h,scaleY:1/d},u=o+4,p=l+4,g=i.create(u,p);if(function isGPUReady(){return At.isReady}()&&this._vertexCount>48)g.context.drawImage(function drawMeshWithGPU(t,e,i,s,n,r,a,o){return At.draw(t,e,i,s,n,r,a,o)}(this._posData,this._colData,this._vertexCount,c,e,u,p,2),0,0);else{const t=g.context.createImageData(o,l);if(e){const i=t.data;for(let t=0,s=i.length;tl+1e-6||e>h+1e-6)return null;const d=Math.floor((i-a)/l)+1,c=Math.ceil((i+t-n)/l)-1,u=Math.floor((s-o)/h)+1,p=Math.ceil((s+e-r)/h)-1;return c<=d&&p<=u?[d,u]:null}updatePatternDims(t,e){const i=Util.inverseTransform(this.patternBaseMatrix),s=[t[0],t[1]],n=[t[2],t[3]];Util.applyTransform(s,i);Util.applyTransform(n,i);e[0]=Math.abs(n[0]-s[0]);e[1]=Math.abs(n[1]-s[1]);e[2]=Math.min(s[0],n[0]);e[3]=Math.min(s[1],n[1])}_renderTileCanvas(t,e,i,s){const[n,r,a,o]=this.bbox,l=t.canvasFactory.create(i.size,s.size),h=l.context,d=this.canvasGraphicsFactory.createCanvasGraphics(h,e);d.groupLevel=t.groupLevel;this.setFillAndStrokeStyleToContext(d,this.paintType,this.color);h.translate(-i.scale*n,-s.scale*r);d.transform(0,i.scale,0,0,s.scale,0,0);h.save();d.dependencyTracker?.save();this.clipBbox(d,n,r,a,o);d.baseTransform=getCurrentTransform(d.ctx);d.executeOperatorList(this.operatorList);d.endDrawing();d.dependencyTracker?.restore();h.restore();return l}_getCombinedScales(){const t=new Float32Array(2);Util.singularValueDecompose2dScale(this.matrix,t);const[e,i]=t;Util.singularValueDecompose2dScale(this.baseTransform,t);return[e*t[0],i*t[1]]}drawPattern(t,e,i=!1,[s,n],r){const[a,o,l,h]=this.bbox,d=t.dependencyTracker;d&&(t.dependencyTracker=new CanvasNestedDependencyTracker(d,r));t.save();i?t.ctx.clip(e,"evenodd"):t.ctx.clip(e);t.ctx.setTransform(...this.patternBaseMatrix);t.ctx.translate(s*this.xstep,n*this.ystep);if(this.needsIsolation||1!==t.ctx.globalAlpha||"source-over"!==t.ctx.globalCompositeOperation||t.inSMaskMode){const e=l-a,i=h-o,[s,n]=this._getCombinedScales(),d=this.getSizeAndScale(e,this.ctx.canvas.width,s),c=this.getSizeAndScale(i,this.ctx.canvas.height,n),u=this._renderTileCanvas(t,r,d,c);t.ctx.drawImage(u.canvas,a,o,e,i);t.canvasFactory.destroy(u)}else{this.setFillAndStrokeStyleToContext(t,this.paintType,this.color);this.clipBbox(t,a,o,l,h);t.baseTransformStack.push(t.baseTransform);t.baseTransform=getCurrentTransform(t.ctx);t.executeOperatorList(this.operatorList);t.baseTransform=t.baseTransformStack.pop()}t.restore();d&&(t.dependencyTracker=d)}createPatternCanvas(t,e){const[i,s,n,r]=this.bbox,a=n-i,o=r-s;let{xstep:l,ystep:h}=this;l=Math.abs(l);h=Math.abs(h);info("TilingType: "+this.tilingType);const[d,c]=this._getCombinedScales();let u=a,p=o,g=!1,m=!1;Math.ceil(l*d)>=Math.ceil(a*d)?u=l:g=!0;Math.ceil(h*c)>=Math.ceil(o*c)?p=h:m=!0;const f=this.getSizeAndScale(u,this.ctx.canvas.width,d),b=this.getSizeAndScale(p,this.ctx.canvas.height,c),y=this._renderTileCanvas(t,e,f,b);if(g||m){const e=y.canvas;g&&(u=l);m&&(p=h);const n=this.getSizeAndScale(u,this.ctx.canvas.width,d),r=this.getSizeAndScale(p,this.ctx.canvas.height,c),f=n.size,b=r.size,A=t.canvasFactory.create(f,b),v=A.context,w=g?Math.floor(a/l):0,x=m?Math.floor(o/h):0;for(let t=0;t<=w;t++)for(let i=0;i<=x;i++)v.drawImage(e,f*t,b*i,f,b,0,0,f,b);t.canvasFactory.destroy(y);return{canvas:A.canvas,canvasEntry:A,scaleX:n.scale,scaleY:r.scale,offsetX:i,offsetY:s}}return{canvas:y.canvas,canvasEntry:y,scaleX:f.scale,scaleY:b.scale,offsetX:i,offsetY:s}}getSizeAndScale(t,e,i){const s=Math.max(TilingPattern.MAX_PATTERN_SIZE,e);let n=Math.ceil(t*i);n>=s?n=s:i=n/t;return{scale:i,size:n}}clipBbox(t,e,i,s,n){const r=s-e,a=n-i,o=new Path2D;o.rect(e,i,r,a);Util.axialAlignedBoundingBox([e,i,s,n],getCurrentTransform(t.ctx),t.current.minMax);t.ctx.clip(o);t.current.updateClipFromPath()}setFillAndStrokeStyleToContext(t,e,i){const s=t.ctx,n=t.current;n.patternFill=n.patternStroke=!1;switch(e){case Ct:const{fillStyle:t,strokeStyle:r}=this.ctx;s.fillStyle=n.fillColor=t;s.strokeStyle=n.strokeColor=r;break;case Et:s.fillStyle=s.strokeStyle=i;n.fillColor=n.strokeColor=i;break;default:throw new FormatError(`Unsupported paint type: ${e}`)}}isModifyingCurrentTransform(){return!1}getPattern(t,e,i,s,n){const r=s!==xt?Util.transform(i,this.patternBaseMatrix):i,a=this.createPatternCanvas(e,n);let o=new DOMMatrix(r);o=o.translate(a.offsetX,a.offsetY);o=o.scale(1/a.scaleX,1/a.scaleY);const l=t.createPattern(a.canvas,"repeat");e.canvasFactory.destroy(a.canvasEntry);l.setTransform(o);return l}}function convertBlackAndWhiteToRGBA({src:t,srcPos:e=0,dest:i,width:s,height:n,nonBlackColor:r=4294967295,inverseDecode:a=!1}){const o=FeatureTest.isLittleEndian?4278190080:255,[l,h]=a?[r,o]:[o,r],d=s>>3,c=7&s,u=l^h,p=t.length;i=new Uint32Array(i.buffer);let g=0;for(let s=0;s>7&1)&u;i[g+1]=l^-(s>>6&1)&u;i[g+2]=l^-(s>>5&1)&u;i[g+3]=l^-(s>>4&1)&u;i[g+4]=l^-(s>>3&1)&u;i[g+5]=l^-(s>>2&1)&u;i[g+6]=l^-(s>>1&1)&u;i[g+7]=l^-(1&s)&u}if(0===c)continue;const s=e>7-t&1)&u}return{srcPos:e,destPos:g}}const Tt=16,St=new DOMMatrix,_t=new Float32Array(2);function drawImageAtIntegerCoords(t,e,i,s,n,r,a,o,l,h){const[d,c,u,p,g,m]=getCurrentTransform(t);if(0===c&&0===u){const f=a*d+g,b=Math.round(f),y=o*p+m,A=Math.round(y),v=(a+l)*d+g,w=Math.abs(Math.round(v)-b)||1,x=(o+h)*p+m,C=Math.abs(Math.round(x)-A)||1;t.setTransform(Math.sign(d),0,0,Math.sign(p),b,A);t.drawImage(e,i,s,n,r,0,0,w,C);t.setTransform(d,c,u,p,g,m);return[w,C]}if(0===d&&0===p){const f=o*u+g,b=Math.round(f),y=a*c+m,A=Math.round(y),v=(o+h)*u+g,w=Math.abs(Math.round(v)-b)||1,x=(a+l)*c+m,C=Math.abs(Math.round(x)-A)||1;t.setTransform(0,Math.sign(c),Math.sign(u),0,b,A);t.drawImage(e,i,s,n,r,0,0,C,w);t.setTransform(d,c,u,p,g,m);return[C,w]}t.drawImage(e,i,s,n,r,a,o,l,h);return[Math.hypot(d,c)*l,Math.hypot(u,p)*h]}class CanvasExtraState{alphaIsShape=!1;fontSize=0;fontSizeScale=1;textMatrix=null;textMatrixScale=1;fontMatrix=s;leading=0;x=0;y=0;lineX=0;lineY=0;charSpacing=0;wordSpacing=0;textHScale=1;textRenderingMode=y;textRise=0;fillColor="#000000";strokeColor="#000000";tilingPatternDims=null;patternFill=!1;patternStroke=!1;fillAlpha=1;strokeAlpha=1;lineWidth=1;activeSMask=null;transferMaps="none";minMax=i.slice();constructor(t,e){this.clipBox=new Float32Array([0,0,t,e])}clone(){const t=Object.create(this);t.clipBox=this.clipBox.slice();t.minMax=this.minMax.slice();t.tilingPatternDims=this.tilingPatternDims?.slice();return t}getPathBoundingBox(t=vt,e=null){const i=this.minMax.slice();if(t===wt){e||unreachable("Stroke bounding box must include transform.");Util.singularValueDecompose2dScale(e,_t);const t=_t[0]*this.lineWidth/2,s=_t[1]*this.lineWidth/2;i[0]-=t;i[1]-=s;i[2]+=t;i[3]+=s}return i}updateClipFromPath(){const t=Util.intersect(this.clipBox,this.getPathBoundingBox());this.startNewPathAndClipBox(t||[0,0,0,0])}isEmptyClip(){return this.minMax[0]===1/0}startNewPathAndClipBox(t){this.clipBox.set(t,0);this.minMax.set(i,0)}getClippedPathBoundingBox(t=vt,e=null){return Util.intersect(this.clipBox,this.getPathBoundingBox(t,e))}}function putBinaryImageData(t,e){if(e instanceof ImageData){t.putImageData(e,0,0);return}const i=e.height,s=e.width,n=i%Tt,r=(i-n)/Tt,a=0===n?r:r+1,o=t.createImageData(s,Tt);let l,h=0;const d=e.data,c=o.data;let u,p,g,m;if(e.kind===E.GRAYSCALE_1BPP){const e=d.byteLength,i=new Uint32Array(c.buffer,0,c.byteLength>>2),m=i.length,f=s+7>>3,b=4294967295,y=FeatureTest.isLittleEndian?4278190080:255;for(u=0;uf?s:8*t-7,a=-8&r;let o=0,c=0;for(;n>=1}}for(;l=r){g=n;m=s*g}l=0;for(p=m;p--;){c[l++]=d[h++];c[l++]=d[h++];c[l++]=d[h++];c[l++]=255}t.putImageData(o,0,u*Tt)}}}function putBinaryImageMask(t,e){if(e.bitmap){t.drawImage(e.bitmap,0,0);return}const i=e.height,s=e.width,n=i%Tt,r=(i-n)/Tt,a=0===n?r:r+1,o=t.createImageData(s,Tt);let l=0;const h=e.data,d=o.data;for(let e=0;e10&&"function"==typeof i,d=h?Date.now()+15:0;let c=0;const u=this.commonObjs,p=this.objs;let g,m;for(;;){if(void 0!==s){if(o===s.nextBreakPoint){s.breakIt(o,i);return o}if(s.shouldSkip(o)){if(++o===l)return o;continue}}if(!n||n(o)){g=a[o];m=r[o]??null;if(g!==I.dependency)null===m?this[g](o):this[g](o,...m);else for(const t of m){this.dependencyTracker?.recordNamedData(t,o);const e=t.startsWith("g_")?u:p;if(!e.has(t)){e.get(t,i);return o}}}o++;if(o===l)return o;if(h&&++c>10){if(Date.now()>d){i();return o}c=0}}}#zs(){for(;this.stateStack.length||this.inSMaskMode;)this.restore();this.current.activeSMask=null;this.ctx.restore();if(this.transparentCanvas){this.ctx=this.compositeCtx;this.ctx.save();this.ctx.setTransform(1,0,0,1,0,0);this.ctx.drawImage(this.transparentCanvas,0,0);this.ctx.restore();this.canvasFactory.destroy(this.transparentCanvasEntry);this.transparentCanvas=null;this.transparentCanvasEntry=null}}endDrawing(){this.#zs();for(const t of this.smaskGroupCanvases)this.canvasFactory.destroy(t);this.smaskGroupCanvases.length=0;this._clearPreparedSMask();this.tempSMask=null;this.smaskStack.length=0;this.cachedPatterns.clear();for(const t of this._cachedBitmapsMap.values()){for(const e of t.values())"undefined"!=typeof HTMLCanvasElement&&e instanceof HTMLCanvasElement&&(e.width=e.height=0);t.clear()}this._cachedBitmapsMap.clear();this.#Gs()}#Gs(){if(this.pageColors){const t=this.filterFactory.addHCMFilter(this.pageColors.foreground,this.pageColors.background);if("none"!==t){const e=this.ctx.filter;this.ctx.filter=t;this.ctx.drawImage(this.ctx.canvas,0,0);this.ctx.filter=e}}}_scaleImage(t,e){const i=t.width??t.displayWidth,s=t.height??t.displayHeight,n=[];let r=Math.max(Math.hypot(e[0],e[1]),1),a=Math.max(Math.hypot(e[2],e[3]),1),o=i,l=s;for(;r>2&&o>1||a>2&&l>1;){let t=o,e=l;if(r>2&&o>1){t=Math.ceil(o/2);r/=o/t}if(a>2&&l>1){e=Math.ceil(l/2);a/=l/e}n.push({newWidth:t,newHeight:e});o=t;l=e}if(0===n.length)return{img:t,paintWidth:i,paintHeight:s,tmpCanvas:null};if(1===n.length){const{newWidth:e,newHeight:r}=n[0],a=this.canvasFactory.create(e,r);a.context.drawImage(t,0,0,i,s,0,0,e,r);return{img:a.canvas,paintWidth:e,paintHeight:r,tmpCanvas:a}}let h=this.canvasFactory.create(1,1),d=this.canvasFactory.create(1,1),c=i,u=s,p=t;for(const{newWidth:t,newHeight:e}of n){this.canvasFactory.reset(d,t,e);d.context.drawImage(p,0,0,c,u,0,0,t,e);[h,d]=[d,h];p=h.canvas;c=t;u=e}this.canvasFactory.destroy(d);return{img:h.canvas,paintWidth:c,paintHeight:u,tmpCanvas:h}}_createMaskCanvas(t,e){const s=this.ctx,{width:n,height:r}=e,a=this.current.fillColor,o=this.current.patternFill,l=getCurrentTransform(s);let h,d,c,u;if((e.bitmap||e.data)&&e.count>1){const i=e.bitmap||e.data.buffer;d=JSON.stringify(o?l:[l.slice(0,4),a]);h=this._cachedBitmapsMap.getOrInsertComputed(i,makeMap);const s=h.get(d);if(s&&!o){const e=Math.round(Math.min(l[0],l[2])+l[4]),i=Math.round(Math.min(l[1],l[3])+l[5]);this.dependencyTracker?.recordDependencies(t,ot);return{canvas:s,offsetX:e,offsetY:i}}c=s}if(!c){u=this.canvasFactory.create(n,r);putBinaryImageMask(u.context,e)}let p=Util.transform(l,[1/n,0,0,-1/r,0,0]);p=Util.transform(p,[1,0,0,1,0,-r]);const g=i.slice();Util.axialAlignedBoundingBox([0,0,n,r],p,g);const[m,f,b,y]=g,A=Math.round(b-m)||1,v=Math.round(y-f)||1,w=this.canvasFactory.create(A,v),x=w.context,C=m,E=f;x.translate(-C,-E);x.transform(...p);let T=null;if(!c){const t=this._scaleImage(u.canvas,getCurrentTransformInverse(x));c=t.img;T=t.tmpCanvas;if(c!==u.canvas){this.canvasFactory.destroy(u);u=null}if(h&&o){h.set(d,c);T=null;u=null}}x.imageSmoothingEnabled=getImageSmoothingEnabled(getCurrentTransform(x),e.interpolate);drawImageAtIntegerCoords(x,c,0,0,c.width,c.height,0,0,n,r);T&&this.canvasFactory.destroy(T);u&&this.canvasFactory.destroy(u);x.globalCompositeOperation="source-in";const S=Util.transform(getCurrentTransformInverse(x),[1,0,0,1,-C,-E]);x.fillStyle=o?a.getPattern(s,this,S,vt,t):a;x.fillRect(0,0,n,r);h&&!o&&h.set(d,w.canvas);this.dependencyTracker?.recordDependencies(t,ot);return{canvas:w.canvas,canvasEntry:h&&!o?null:w,offsetX:Math.round(C),offsetY:Math.round(E)}}setLineWidth(t,e){this.dependencyTracker?.recordSimpleData("lineWidth",t);e!==this.current.lineWidth&&(this._cachedScaleForStroking[0]=-1);this.current.lineWidth=e;this.ctx.lineWidth=e}setLineCap(t,e){this.dependencyTracker?.recordSimpleData("lineCap",t);this.ctx.lineCap=Pt[e]}setLineJoin(t,e){this.dependencyTracker?.recordSimpleData("lineJoin",t);this.ctx.lineJoin=Dt[e]}setMiterLimit(t,e){this.dependencyTracker?.recordSimpleData("miterLimit",t);this.ctx.miterLimit=e}setDash(t,e,i){this.dependencyTracker?.recordSimpleData("dash",t);const s=this.ctx;if(void 0!==s.setLineDash){s.setLineDash(e);s.lineDashOffset=i}}setRenderingIntent(t,e){}setFlatness(t,e){}setGState(t,e){for(const[i,s]of e)switch(i){case"LW":this.setLineWidth(t,s);break;case"LC":this.setLineCap(t,s);break;case"LJ":this.setLineJoin(t,s);break;case"ML":this.setMiterLimit(t,s);break;case"D":this.setDash(t,s[0],s[1]);break;case"RI":this.setRenderingIntent(t,s);break;case"FL":this.setFlatness(t,s);break;case"Font":this.setFont(t,s[0],s[1]);break;case"CA":this.dependencyTracker?.recordSimpleData("strokeAlpha",t);this.current.strokeAlpha=s;break;case"ca":this.dependencyTracker?.recordSimpleData("fillAlpha",t);this.ctx.globalAlpha=this.current.fillAlpha=s;break;case"BM":this.dependencyTracker?.recordSimpleData("globalCompositeOperation",t);this.ctx.globalCompositeOperation=s;break;case"SMask":this.dependencyTracker?.recordSimpleData("SMask",t);this.current.activeSMask=s?this.tempSMask:null;this.current.activeSMask&&(this.current.activeSMask.blendMode=this.ctx.globalCompositeOperation);this.tempSMask=null;this.checkSMaskState(t);break;case"TR":this.dependencyTracker?.recordSimpleData("filter",t);this.ctx.filter=this.current.transferMaps=this.filterFactory.addFilter(s)}}get inSMaskMode(){return!!this.suspendedCtx}_clearPreparedSMask(){if(this.smaskPreparedEntry){this.canvasFactory.destroy(this.smaskPreparedEntry);this.smaskPreparedEntry=null}this.smaskPreparedFor=null;this.smaskPreparedOffsetX=0;this.smaskPreparedOffsetY=0}_ensurePreparedSMask(t,e,i){if(t!==this.smaskPreparedFor){this._clearPreparedSMask();this._prepareSMaskCanvas(t,e,i)}}checkSMaskState(t){const e=this.inSMaskMode;this.current.activeSMask&&!e?this.beginSMaskMode(t):!this.current.activeSMask&&e?this.endSMaskMode():this.current.activeSMask&&e&&this._ensurePreparedSMask(this.current.activeSMask,this.ctx.canvas.width,this.ctx.canvas.height)}_prepareSMaskCanvas(t,e,i){const{canvas:s,subtype:n,backdrop:r,transferMap:a}=t,o="Luminosity"===n||"Alpha"===n&&a;if(!r&&!o){this.smaskPreparedFor=t;return}let l,h,d;if(r&&o){const o=this.canvasFactory.create(e,i),c=o.context;c.drawImage(s,t.offsetX,t.offsetY);c.globalCompositeOperation="destination-atop";c.fillStyle=r;c.fillRect(0,0,e,i);c.globalCompositeOperation="source-over";l=this.canvasFactory.create(e,i);const u=l.context;u.filter="Alpha"===n?this.filterFactory.addAlphaFilter(a):this.filterFactory.addLuminosityFilter(a);u.drawImage(o.canvas,0,0);u.filter="none";this.canvasFactory.destroy(o);h=d=0}else if(o){l=this.canvasFactory.create(s.width,s.height);const e=l.context;e.filter="Alpha"===n?this.filterFactory.addAlphaFilter(a):this.filterFactory.addLuminosityFilter(a);e.drawImage(s,0,0);e.filter="none";({offsetX:h,offsetY:d}=t)}else{l=this.canvasFactory.create(e,i);const n=l.context;n.drawImage(s,t.offsetX,t.offsetY);n.globalCompositeOperation="destination-atop";n.fillStyle=r;n.fillRect(0,0,e,i);n.globalCompositeOperation="source-over";h=d=0}this.smaskPreparedEntry=l;this.smaskPreparedFor=t;this.smaskPreparedOffsetX=h;this.smaskPreparedOffsetY=d}beginSMaskMode(t){if(this.inSMaskMode)throw new Error("beginSMaskMode called while already in smask mode");const{width:e,height:i}=this.ctx.canvas,s=this.canvasFactory.create(e,i);this.smaskScratchCanvas=s;this.suspendedCtx=this.ctx;const n=this.ctx=s.context;n.setTransform(this.suspendedCtx.getTransform());copyCtxState(this.suspendedCtx,n);!function mirrorContextOperations(t,e){if(t._removeMirroring)throw new Error("Context is already forwarding operations.");t.__originalSave=t.save;t.__originalRestore=t.restore;t.__originalRotate=t.rotate;t.__originalScale=t.scale;t.__originalTranslate=t.translate;t.__originalTransform=t.transform;t.__originalSetTransform=t.setTransform;t.__originalResetTransform=t.resetTransform;t.__originalClip=t.clip;t.__originalMoveTo=t.moveTo;t.__originalLineTo=t.lineTo;t.__originalBezierCurveTo=t.bezierCurveTo;t.__originalRect=t.rect;t.__originalClosePath=t.closePath;t.__originalBeginPath=t.beginPath;t._removeMirroring=()=>{t.save=t.__originalSave;t.restore=t.__originalRestore;t.rotate=t.__originalRotate;t.scale=t.__originalScale;t.translate=t.__originalTranslate;t.transform=t.__originalTransform;t.setTransform=t.__originalSetTransform;t.resetTransform=t.__originalResetTransform;t.clip=t.__originalClip;t.moveTo=t.__originalMoveTo;t.lineTo=t.__originalLineTo;t.bezierCurveTo=t.__originalBezierCurveTo;t.rect=t.__originalRect;t.closePath=t.__originalClosePath;t.beginPath=t.__originalBeginPath;delete t._removeMirroring};t.save=function(){e.save();this.__originalSave()};t.restore=function(){e.restore();this.__originalRestore()};t.translate=function(t,i){e.translate(t,i);this.__originalTranslate(t,i)};t.scale=function(t,i){e.scale(t,i);this.__originalScale(t,i)};t.transform=function(t,i,s,n,r,a){e.transform(t,i,s,n,r,a);this.__originalTransform(t,i,s,n,r,a)};t.setTransform=function(t,i,s,n,r,a){if(void 0===i){e.setTransform(t);this.__originalSetTransform(t)}else{e.setTransform(t,i,s,n,r,a);this.__originalSetTransform(t,i,s,n,r,a)}};t.resetTransform=function(){e.resetTransform();this.__originalResetTransform()};t.rotate=function(t){e.rotate(t);this.__originalRotate(t)};t.clip=function(t){e.clip(t);this.__originalClip(t)};t.moveTo=function(t,i){e.moveTo(t,i);this.__originalMoveTo(t,i)};t.lineTo=function(t,i){e.lineTo(t,i);this.__originalLineTo(t,i)};t.bezierCurveTo=function(t,i,s,n,r,a){e.bezierCurveTo(t,i,s,n,r,a);this.__originalBezierCurveTo(t,i,s,n,r,a)};t.rect=function(t,i,s,n){e.rect(t,i,s,n);this.__originalRect(t,i,s,n)};t.closePath=function(){e.closePath();this.__originalClosePath()};t.beginPath=function(){e.beginPath();this.__originalBeginPath()}}(n,this.suspendedCtx);this._ensurePreparedSMask(this.current.activeSMask,e,i);this.setGState(t,[["BM","source-over"]])}endSMaskMode(){if(!this.inSMaskMode)throw new Error("endSMaskMode called while not in smask mode");this.ctx._removeMirroring();copyCtxState(this.ctx,this.suspendedCtx);this.ctx=this.suspendedCtx;this.suspendedCtx=null;this.canvasFactory.destroy(this.smaskScratchCanvas);this.smaskScratchCanvas=null;this._clearPreparedSMask()}compose(t){if(!this.current.activeSMask)return;if(t){t[0]=Math.floor(t[0]);t[1]=Math.floor(t[1]);t[2]=Math.ceil(t[2]);t[3]=Math.ceil(t[3])}else t=[0,0,this.ctx.canvas.width,this.ctx.canvas.height];const e=this.current.activeSMask,i=this.suspendedCtx;this.composeSMask(i,e,this.ctx,t);this.ctx.save();this.ctx.setTransform(1,0,0,1,0,0);this.ctx.clearRect(t[0],t[1],t[2]-t[0],t[3]-t[1]);this.ctx.restore()}composeSMask(t,e,i,s){const n=s[0],r=s[1],a=s[2]-n,o=s[3]-r;if(0===a||0===o)return;const l=this.smaskPreparedEntry;if(l){const t=n-this.smaskPreparedOffsetX,e=r-this.smaskPreparedOffsetY;i.save();i.globalAlpha=1;i.setTransform(1,0,0,1,0,0);const s=new Path2D;s.rect(n,r,a,o);i.clip(s);i.globalCompositeOperation="destination-in";i.drawImage(l.canvas,t,e,a,o,n,r,a,o);i.restore()}else this.genericComposeSMask(e.context,i,a,o,n,r,e.offsetX,e.offsetY);t.save();t.globalAlpha=1;t.globalCompositeOperation=e.blendMode||"source-over";t.setTransform(1,0,0,1,0,0);t.drawImage(i.canvas,n,r,a,o,n,r,a,o);t.restore()}genericComposeSMask(t,e,i,s,n,r,a,o){const l=t.canvas,h=n-a,d=r-o;e.save();e.globalAlpha=1;e.setTransform(1,0,0,1,0,0);const c=new Path2D;c.rect(n,r,i,s);e.clip(c);e.globalCompositeOperation="destination-in";e.drawImage(l,h,d,i,s,n,r,i,s);e.restore()}save(t){this.inSMaskMode&©CtxState(this.ctx,this.suspendedCtx);this.ctx.save();const e=this.current;this.stateStack.push(e);this.current=e.clone();this.dependencyTracker?.save(t)}restore(t){this.dependencyTracker?.restore(t);if(0!==this.stateStack.length){this.current=this.stateStack.pop();this.ctx.restore();if(this.inSMaskMode){copyCtxState(this.suspendedCtx,this.ctx);this.ctx.setTransform(this.suspendedCtx.getTransform())}this.checkSMaskState(t);this.pendingClip=null;this._cachedScaleForStroking[0]=-1;this._cachedGetSinglePixelWidth=null}else this.inSMaskMode&&this.endSMaskMode()}transform(t,e,i,s,n,r,a){this.dependencyTracker?.recordIncrementalData("transform",t);this.ctx.transform(e,i,s,n,r,a);this._cachedScaleForStroking[0]=-1;this._cachedGetSinglePixelWidth=null}constructPath(t,e,i,s){let[n]=i;if(!s){n||=i[0]=new Path2D;e!==I.stroke&&e!==I.closeStroke&&(this.current.tilingPatternDims=null);this[e](t,n);return}if(null!==this.dependencyTracker){const i=e===I.stroke?this.current.lineWidth/2:0;this.dependencyTracker.resetBBox(t).recordBBox(t,this.ctx,s[0]-i,s[2]+i,s[1]-i,s[3]+i).recordDependencies(t,["transform"])}n instanceof Path2D||(n=i[0]=makePathFromDrawOPS(n));Util.axialAlignedBoundingBox(s,getCurrentTransform(this.ctx),this.current.minMax);const r=this.current.tilingPatternDims;if(r&&e!==I.stroke&&e!==I.closeStroke&&this.current.fillColor instanceof TilingPattern){const t=Util.intersect(this.current.clipBox,this.current.minMax);t?this.current.fillColor.updatePatternDims(t,r):this.current.tilingPatternDims=null}this[e](t,n);this._pathStartIdx=t}closePath(t){this.ctx.closePath()}stroke(t,e,i=!0){const s=this.ctx,n=this.current.strokeColor;s.globalAlpha=this.current.strokeAlpha;if(this.contentVisible)if("object"==typeof n&&n?.getPattern){const i=n.isModifyingCurrentTransform()?s.getTransform():null;s.save();s.strokeStyle=n.getPattern(s,this,getCurrentTransformInverse(s),wt,t);if(i){const t=new Path2D;t.addPath(e,s.getTransform().invertSelf().multiplySelf(i));e=t}this.rescaleAndStroke(e,!1);s.restore()}else this.rescaleAndStroke(e,!0);this.dependencyTracker?.recordDependencies(t,et);i&&this.consumePath(t,e,this.current.getClippedPathBoundingBox(wt,getCurrentTransform(this.ctx)));s.globalAlpha=this.current.fillAlpha}closeStroke(t,e){this.stroke(t,e)}fill(t,e,i=!0){const s=this.ctx,n=this.current.fillColor,r=this.current.patternFill;let a=!1;const o=this.current.getClippedPathBoundingBox();this.dependencyTracker?.recordDependencies(t,it);if(r){const r=this.current.tilingPatternDims,l=r&&n.canSkipPatternCanvas(r);if(l){n.drawPattern(this,e,this.pendingEOFill,l,t);this.pendingEOFill=!1;i&&this.consumePath(t,e,o);this.current.tilingPatternDims=null;return}const h=n.isModifyingCurrentTransform()?s.getTransform():null;this.dependencyTracker?.save(t);s.save();s.fillStyle=n.getPattern(s,this,getCurrentTransformInverse(s),vt,t);if(h){const t=new Path2D;t.addPath(e,s.getTransform().invertSelf().multiplySelf(h));e=t}a=!0}if(this.contentVisible&&null!==o)if(this.pendingEOFill){s.fill(e,"evenodd");this.pendingEOFill=!1}else s.fill(e);if(a){s.restore();this.dependencyTracker?.restore(t)}i&&this.consumePath(t,e,o)}eoFill(t,e){this.pendingEOFill=!0;this.fill(t,e)}fillStroke(t,e){this.fill(t,e,!1);this.stroke(t,e,!1);this.consumePath(t,e)}eoFillStroke(t,e){this.pendingEOFill=!0;this.fillStroke(t,e)}closeFillStroke(t,e){this.fillStroke(t,e)}closeEOFillStroke(t,e){this.pendingEOFill=!0;this.fillStroke(t,e)}endPath(t,e){this.consumePath(t,e)}rawFillPath(t,e){this.ctx.fill(e);this.dependencyTracker?.recordDependencies(t,nt).recordOperation(t)}clip(t){this.dependencyTracker?.recordFutureForcedDependency("clipMode",t);this.pendingClip=Mt}eoClip(t){this.dependencyTracker?.recordFutureForcedDependency("clipMode",t);this.pendingClip=kt}beginText(t){this.current.textMatrix=null;this.current.textMatrixScale=1;this.current.x=this.current.lineX=0;this.current.y=this.current.lineY=0;this.dependencyTracker?.recordOpenMarker(t).resetIncrementalData("sameLineText").resetIncrementalData("moveText",t)}endText(t){const e=this.pendingTextPaths,i=this.ctx;if(this.dependencyTracker){const{dependencyTracker:i}=this;void 0!==e&&i.recordFutureForcedDependency("textClip",i.getOpenMarker()).recordFutureForcedDependency("textClip",t);i.recordCloseMarker(t)}if(void 0!==e){const t=new Path2D,s=i.getTransform().invertSelf();for(const{transform:i,x:n,y:r,fontSize:a,path:o}of e)o&&t.addPath(o,new DOMMatrix(i).preMultiplySelf(s).translate(n,r).scale(a,-a));i.clip(t)}delete this.pendingTextPaths}setCharSpacing(t,e){this.dependencyTracker?.recordSimpleData("charSpacing",t);this.current.charSpacing=e}setWordSpacing(t,e){this.dependencyTracker?.recordSimpleData("wordSpacing",t);this.current.wordSpacing=e}setHScale(t,e){this.dependencyTracker?.recordSimpleData("hScale",t);this.current.textHScale=e/100}setLeading(t,e){this.dependencyTracker?.recordSimpleData("leading",t);this.current.leading=-e}setFont(t,e,i){this.dependencyTracker?.recordSimpleData("font",t).recordSimpleDataFromNamed("fontObj",e,t);const n=this.commonObjs.get(e),r=this.current;if(!n)throw new Error(`Can't find font for ${e}`);r.fontMatrix=n.fontMatrix||s;0!==r.fontMatrix[0]&&0!==r.fontMatrix[3]||warn("Invalid font matrix for font "+e);if(i<0){i=-i;r.fontDirection=-1}else r.fontDirection=1;this.current.font=n;this.current.fontSize=i;if(n.isType3Font)return;const a=n.loadedName||"sans-serif",o=n.systemFontInfo?.css||`"${a}", ${n.fallbackName}`;let l="normal";n.black?l="900":n.bold&&(l="bold");const h=n.italic?"italic":"normal";let d=i;i<16?d=16:i>100&&(d=100);this.current.fontSizeScale=i/d;this.ctx.font=`${h} ${l} ${d}px ${o}`}setTextRenderingMode(t,e){this.dependencyTracker?.recordSimpleData("textRenderingMode",t);this.current.textRenderingMode=e}setTextRise(t,e){this.dependencyTracker?.recordSimpleData("textRise",t);this.current.textRise=e}moveText(t,e,i){this.dependencyTracker?.resetIncrementalData("sameLineText").recordIncrementalData("moveText",t);this.current.x=this.current.lineX+=e;this.current.y=this.current.lineY+=i}setLeadingMoveText(t,e,i){this.setLeading(t,-i);this.moveText(t,e,i)}setTextMatrix(t,e){this.dependencyTracker?.resetIncrementalData("sameLineText").recordSimpleData("textMatrix",t);const{current:i}=this;i.textMatrix=e;i.textMatrixScale=Math.hypot(e[0],e[1]);i.x=i.lineX=0;i.y=i.lineY=0}nextLine(t){this.moveText(t,0,this.current.leading);this.dependencyTracker?.recordIncrementalData("moveText",this.dependencyTracker.getSimpleIndex("leading")??t)}#Ws(t,e,i){const s=new Path2D;s.addPath(t,new DOMMatrix(i).invertSelf().multiplySelf(e));return s}paintChar(t,e,i,s,n,r){const a=this.ctx,o=this.current,l=o.font,h=o.textRenderingMode,d=o.fontSize/o.fontSizeScale,c=h&x,u=!!(h&C),p=o.patternFill&&!l.missingFile,g=o.patternStroke&&!l.missingFile;let m;(l.disableFontFace||u||p||g)&&!l.missingFile&&(m=l.getPathGenerator(this.commonObjs,e));if(m&&(l.disableFontFace||p||g)){a.save();a.translate(i,s);a.scale(d,-d);this.dependencyTracker?.recordCharacterBBox(t,a,l);let e;if(c===y||c===v)if(n){e=a.getTransform();a.setTransform(...n);const t=this.#Ws(m,e,n);a.fill(t)}else a.fill(m);if(c===A||c===v)if(r){e||=a.getTransform();a.setTransform(...r);const{a:t,b:i,c:s,d:n}=e,o=Util.inverseTransform(r),l=Util.transform([t,i,s,n,0,0],o);Util.singularValueDecompose2dScale(l,_t);a.lineWidth*=Math.max(_t[0],_t[1])/d;a.stroke(this.#Ws(m,e,r))}else{a.lineWidth/=d;a.stroke(m)}a.restore()}else{if(c===y||c===v){a.fillText(e,i,s);this.dependencyTracker?.recordCharacterBBox(t,a,l,d,i,s,()=>a.measureText(e))}if(c===A||c===v){this.dependencyTracker&&this.dependencyTracker?.recordCharacterBBox(t,a,l,d,i,s,()=>a.measureText(e)).recordDependencies(t,et);a.strokeText(e,i,s)}}if(u){(this.pendingTextPaths||=[]).push({transform:getCurrentTransform(a),x:i,y:s,fontSize:d,path:m});this.dependencyTracker?.recordCharacterBBox(t,a,l,d,i,s)}}get isFontSubpixelAAEnabled(){const t=this.canvasFactory.create(10,10),e=t.context;e.scale(1.5,1);e.fillText("I",0,10);const i=e.getImageData(0,0,10,10).data;this.canvasFactory.destroy(t);let s=!1;for(let t=3;t0&&i[t]<255){s=!0;break}return shadow(this,"isFontSubpixelAAEnabled",s)}showText(t,e){if(this.dependencyTracker){this.dependencyTracker.recordDependencies(t,rt).resetBBox(t);this.current.textRenderingMode&C&&this.dependencyTracker.recordFutureForcedDependency("textClip",t).inheritPendingDependenciesAsFutureForcedDependencies()}const i=this.current,s=i.font;if(s.isType3Font){this.showType3Text(t,e);this.dependencyTracker?.recordShowTextOperation(t);return}const n=i.fontSize;if(0===n){this.dependencyTracker?.recordOperation(t);return}const r=this.ctx,a=i.fontSizeScale,o=i.charSpacing,l=i.wordSpacing,h=i.fontDirection,d=i.textHScale*h,c=e.length,u=s.vertical,p=u?1:-1,g=s.defaultVMetrics,m=n*i.fontMatrix[0],f=i.textRenderingMode===y&&!s.disableFontFace&&!i.patternFill;r.save();i.textMatrix&&r.transform(...i.textMatrix);r.translate(i.x,i.y+i.textRise);h>0?r.scale(d,-1):r.scale(d,1);let b,w;const E=i.textRenderingMode&x,T=E===y||E===v,S=E===A||E===v;let _=i.lineWidth;const P=i.textMatrixScale;0===P||0===_?S&&(_=this.getSinglePixelWidth()):_/=P;if(1!==a){r.scale(a,a);_/=a}r.lineWidth=_;if(T&&i.patternFill){r.save();const e=i.fillColor.getPattern(r,this,getCurrentTransformInverse(r),vt,t);b=getCurrentTransform(r);r.restore();r.fillStyle=e}if(S&&i.patternStroke){r.save();const e=i.strokeColor.getPattern(r,this,getCurrentTransformInverse(r),wt,t);w=getCurrentTransform(r);r.restore();r.strokeStyle=e}if(s.isInvalidPDFjsFont){const s=[];let n=0;for(const t of e){s.push(t.unicode);n+=t.width}const a=s.join("");r.fillText(a,0,0);if(null!==this.dependencyTracker){const e=r.measureText(a);this.dependencyTracker.recordBBox(t,this.ctx,-e.actualBoundingBoxLeft,e.actualBoundingBoxRight,-e.actualBoundingBoxAscent,e.actualBoundingBoxDescent).recordShowTextOperation(t)}i.x+=n*m*d;r.restore();this.compose();return}let D,M=0;for(D=0;D0){C=r.measureText(y);const t=1e3*C.width/n*a;if(EC??r.measureText(y))}else{this.paintChar(t,y,v,x,b,w);if(A){const e=v+n*A.offset.x/a,i=x-n*A.offset.y/a;this.paintChar(t,A.fontChar,e,i,b,w)}}M+=u?E*m-c*h:E*m+c*h;d&&r.restore()}u?i.y-=M:i.x+=M*d;r.restore();this.compose();this.dependencyTracker?.recordShowTextOperation(t)}showType3Text(t,e){const i=this.ctx,n=this.current,r=n.font,a=n.fontSize,o=n.fontDirection,l=r.vertical?1:-1,h=n.charSpacing,d=n.wordSpacing,c=n.textHScale*o,u=n.fontMatrix||s,p=e.length;let g,m,f,b;if(n.textRenderingMode===w||0===a)return;this._cachedScaleForStroking[0]=-1;this._cachedGetSinglePixelWidth=null;i.save();n.textMatrix&&i.transform(...n.textMatrix);i.translate(n.x,n.y+n.textRise);i.scale(c,o);const y=this.dependencyTracker;this.dependencyTracker=y?new CanvasNestedDependencyTracker(y,t):null;for(g=0;gnew CanvasGraphics(t,this.commonObjs,this.objs,this.canvasFactory,this.filterFactory,{optionalContentConfig:this.optionalContentConfig,markedContentStack:this.markedContentStack},void 0,void 0,this.dependencyTracker?new CanvasNestedDependencyTracker(this.dependencyTracker,e,!0):null)};i=new TilingPattern(e,this.ctx,s,t)}else i=this._getPattern(t,e[1],e[2]);return i}setStrokeColorN(t,...e){this.dependencyTracker?.recordSimpleData("strokeColor",t);this.current.strokeColor=this.getColorN_Pattern(t,e);this.current.patternStroke=!0}setFillColorN(t,...e){this.dependencyTracker?.recordSimpleData("fillColor",t);const i=this.current.fillColor=this.getColorN_Pattern(t,e);this.current.patternFill=!0;this.current.tilingPatternDims=i instanceof TilingPattern?[0,0,0,0]:null}setStrokeRGBColor(t,e){this.dependencyTracker?.recordSimpleData("strokeColor",t);this.ctx.strokeStyle=this.current.strokeColor=e;this.current.patternStroke=!1}setStrokeTransparent(t){this.dependencyTracker?.recordSimpleData("strokeColor",t);this.ctx.strokeStyle=this.current.strokeColor="transparent";this.current.patternStroke=!1}setFillRGBColor(t,e){this.dependencyTracker?.recordSimpleData("fillColor",t);this.ctx.fillStyle=this.current.fillColor=e;this.current.patternFill=!1;this.current.tilingPatternDims=null}setFillTransparent(t){this.dependencyTracker?.recordSimpleData("fillColor",t);this.ctx.fillStyle=this.current.fillColor="transparent";this.current.patternFill=!1;this.current.tilingPatternDims=null}_getPattern(t,e,i=null){let s;if(this.cachedPatterns.has(e))s=this.cachedPatterns.get(e);else{s=function getShadingPattern(t){switch(t[0]){case"RadialAxial":return new RadialAxialShadingPattern(t);case"Mesh":return new MeshShadingPattern(t);case"Dummy":return new DummyShadingPattern}throw new Error(`Unknown IR type: ${t[0]}`)}(this.getObject(t,e));this.cachedPatterns.set(e,s)}i&&(s.matrix=i);return s}shadingFill(t,e){if(!this.contentVisible)return;const s=this.ctx;this.save(t);const n=this._getPattern(t,e);s.fillStyle=n.getPattern(s,this,getCurrentTransformInverse(s),xt,t);const r=getCurrentTransformInverse(s);if(r){const{width:t,height:e}=s.canvas,n=i.slice();Util.axialAlignedBoundingBox([0,0,t,e],r,n);const[a,o,l,h]=n;this.ctx.fillRect(a,o,l-a,h-o)}else this.ctx.fillRect(-1e10,-1e10,2e10,2e10);this.dependencyTracker?.resetBBox(t).recordFullPageBBox(t).recordDependencies(t,at).recordDependencies(t,it).recordOperation(t);this.compose(this.current.getClippedPathBoundingBox());this.restore(t)}beginInlineImage(){unreachable("Should not call beginInlineImage")}beginImageData(){unreachable("Should not call beginImageData")}paintFormXObjectBegin(t,e,i){if(this.contentVisible){this.save(t);this.baseTransformStack.push(this.baseTransform);e&&this.transform(t,...e);this.baseTransform=getCurrentTransform(this.ctx);if(i){Util.axialAlignedBoundingBox(i,this.baseTransform,this.current.minMax);const[e,s,n,r]=i,a=new Path2D;a.rect(e,s,n-e,r-s);this.ctx.clip(a);this.dependencyTracker?.recordClipBox(t,this.ctx,e,n,s,r);this.endPath(t)}}}paintFormXObjectEnd(t){if(this.contentVisible){this.restore(t);this.baseTransform=this.baseTransformStack.pop()}}beginGroup(t,e){if(!this.contentVisible)return;this.save(t);const{inSMaskMode:s}=this;if(s){this.endSMaskMode();this.current.activeSMask=null}const n=this.ctx;e.isolated||info("TODO: Support non-isolated groups.");e.knockout&&warn("Knockout groups not supported.");if(!e.needsIsolation&&1===n.globalAlpha&&"source-over"===n.globalCompositeOperation&&!s){if(e.bbox){let t=new Path2D;const[i,s,r,a]=e.bbox;t.rect(i,s,r-i,a-s);if(e.matrix){const i=new Path2D;i.addPath(t,new DOMMatrix(e.matrix));t=i}n.clip(t)}this.groupStack.push(null);this.groupLevel++;return}const r=getCurrentTransform(n);e.matrix&&n.transform(...e.matrix);const a=[0,0,n.canvas.width,n.canvas.height];let o;if(e.bbox){o=i.slice();Util.axialAlignedBoundingBox(e.bbox,getCurrentTransform(n),o);o=Util.intersect(o,a)||[0,0,0,0]}else o=a;const l=Math.floor(o[0]),h=Math.floor(o[1]),d=Math.max(Math.ceil(o[2])-l,1),c=Math.max(Math.ceil(o[3])-h,1);this.current.startNewPathAndClipBox([0,0,d,c]);const u=this.canvasFactory.create(d,c);e.smask&&this.smaskGroupCanvases.push(u);const p=u.context;p.translate(-l,-h);p.transform(...r);if(!e.isolated&&!e.smask&&s&&e.needsIsolation){p.save();p.setTransform(1,0,0,1,0,0);p.drawImage(n.canvas,-l,-h);p.restore()}if(e.bbox){let t=new Path2D;const[i,s,n,r]=e.bbox;t.rect(i,s,n-i,r-s);if(e.matrix){const i=new Path2D;i.addPath(t,new DOMMatrix(e.matrix));t=i}p.clip(t)}e.smask&&this.smaskStack.push({canvas:u.canvas,context:p,offsetX:l,offsetY:h,subtype:e.smask.subtype,backdrop:e.smask.backdrop,transferMap:e.smask.transferMap||null});if(!e.smask||this.dependencyTracker){n.setTransform(1,0,0,1,0,0);n.translate(l,h);n.save()}copyCtxState(n,p);this.ctx=p;this.dependencyTracker?.inheritSimpleDataAsFutureForcedDependencies(["fillAlpha","strokeAlpha","globalCompositeOperation"]).pushBaseTransform(n);this.setGState(t,[["BM","source-over"],["ca",1],["CA",1],["TR",null]]);this.groupStack.push(n);this.groupLevel++}endGroup(t,e){if(!this.contentVisible)return;this.groupLevel--;const s=this.ctx,n=this.groupStack.pop();if(null!==n){this.ctx=n;this.ctx.imageSmoothingEnabled=!1;this.dependencyTracker?.popBaseTransform();if(e.smask){this.tempSMask=this.smaskStack.pop();this.restore(t);if(this.dependencyTracker){this.ctx.restore();this.inSMaskMode&&this.ctx.setTransform(this.suspendedCtx.getTransform())}}else{this.ctx.restore();const e=getCurrentTransform(this.ctx);this.restore(t);this.ctx.save();this.ctx.setTransform(...e);const n=i.slice();Util.axialAlignedBoundingBox([0,0,s.canvas.width,s.canvas.height],e,n);this.ctx.drawImage(s.canvas,0,0);this.ctx.restore();this.canvasFactory.destroy({canvas:s.canvas,context:s});this.compose(n)}}else this.restore(t)}beginAnnotation(t,e,i,s,n,r){this.#zs();resetCtxToDefault(this.ctx);this.ctx.save();this.save(t);this.baseTransform&&this.ctx.setTransform(...this.baseTransform);if(i){const n=i[2]-i[0],a=i[3]-i[1];if(r&&this.annotationCanvasMap){(s=s.slice())[4]-=i[0];s[5]-=i[1];(i=i.slice())[0]=i[1]=0;i[2]=n;i[3]=a;Util.singularValueDecompose2dScale(getCurrentTransform(this.ctx),_t);const{viewportScale:t}=this,r=Math.ceil(n*this.outputScaleX*t),o=Math.ceil(a*this.outputScaleY*t);this.annotationCanvas=this.canvasFactory.create(r,o);const{canvas:l,context:h}=this.annotationCanvas;this.annotationCanvasMap.set(e,l);this.annotationCanvas.savedCtx=this.ctx;this.ctx=h;this.ctx.save();this.ctx.setTransform(_t[0],0,0,-_t[1],0,a*_t[1]);resetCtxToDefault(this.ctx)}else{resetCtxToDefault(this.ctx);this.endPath(t);const e=new Path2D;e.rect(i[0],i[1],n,a);this.ctx.clip(e)}}this.current=new CanvasExtraState(this.ctx.canvas.width,this.ctx.canvas.height);this.baseTransformStack.push(this.baseTransform);this.transform(t,...s);this.transform(t,...n);this.baseTransform=getCurrentTransform(this.ctx)}endAnnotation(t){if(this.annotationCanvas){this.ctx.restore();this.#Gs();this.ctx=this.annotationCanvas.savedCtx;delete this.annotationCanvas.savedCtx;delete this.annotationCanvas}this.baseTransform=this.baseTransformStack.pop()}paintImageMaskXObject(t,e){if(!this.contentVisible)return;const i=e.count;(e=this.getObject(t,e.data,e)).count=i;const s=this.ctx,n=this._createMaskCanvas(t,e),r=n.canvas;s.save();s.setTransform(1,0,0,1,0,0);s.drawImage(r,n.offsetX,n.offsetY);this.dependencyTracker?.resetBBox(t).recordBBox(t,this.ctx,n.offsetX,n.offsetX+r.width,n.offsetY,n.offsetY+r.height).recordOperation(t);s.restore();n.canvasEntry&&this.canvasFactory.destroy(n.canvasEntry);this.compose()}paintImageMaskXObjectRepeat(t,e,i,s=0,n=0,r,a){if(!this.contentVisible)return;e=this.getObject(t,e.data,e);const o=this.ctx;o.save();const l=getCurrentTransform(o);o.transform(i,s,n,r,0,0);const h=this._createMaskCanvas(t,e);o.setTransform(1,0,0,1,h.offsetX-l[4],h.offsetY-l[5]);this.dependencyTracker?.resetBBox(t);for(let e=0,d=a.length;ee?h/e:1;a=l>e?l/e:1}}this._cachedScaleForStroking[0]=r;this._cachedScaleForStroking[1]=a}return this._cachedScaleForStroking}rescaleAndStroke(t,e){const{ctx:i,current:{lineWidth:s}}=this,[n,r]=this.getScaleForStroking();if(n===r){i.lineWidth=(s||1)*n;i.stroke(t);return}const a=i.getLineDash();e&&i.save();i.scale(n,r);St.a=1/n;St.d=1/r;const o=new Path2D;o.addPath(t,St);if(a.length>0){const t=Math.max(n,r);i.setLineDash(a.map(e=>e/t));i.lineDashOffset/=t}i.lineWidth=s||1;i.stroke(o);e&&i.restore()}isContentVisible(){for(let t=this.markedContentStack.length-1;t>=0;t--)if(!this.markedContentStack[t].visible)return!1;return!0}}for(const t in I)void 0!==CanvasGraphics.prototype[t]&&(CanvasGraphics.prototype[I[t]]=CanvasGraphics.prototype[t]);class BasePDFStream{#Vs=null;#js=null;_fullReader=null;_rangeReaders=new Set;_source=null;constructor(t,e,i){this._source=t;this.#Vs=e;this.#js=i}get _progressiveDataLength(){return this._fullReader?._loaded??0}getFullReader(){assert(!this._fullReader,"BasePDFStream.getFullReader can only be called once.");return this._fullReader=new this.#Vs(this)}getRangeReader(t,e){if(e<=this._progressiveDataLength)return null;const i=new this.#js(this,t,e);this._rangeReaders.add(i);return i}cancelAllRequests(t){this._fullReader?.cancel(t);for(const e of new Set(this._rangeReaders))e.cancel(t)}}class BasePDFStreamReader{onProgress=null;_contentLength=0;_filename=null;_headersCapability=Promise.withResolvers();_isRangeSupported=!1;_isStreamingSupported=!1;_loaded=0;_stream=null;constructor(t){this._stream=t}_callOnProgress(){this.onProgress?.({loaded:this._loaded,total:this._contentLength})}get headersReady(){return this._headersCapability.promise}get filename(){return this._filename}get contentLength(){return this._contentLength}get isRangeSupported(){return this._isRangeSupported}get isStreamingSupported(){return this._isStreamingSupported}async read(){unreachable("Abstract method `read` called")}cancel(t){unreachable("Abstract method `cancel` called")}}class BasePDFStreamRangeReader{_stream=null;constructor(t,e,i){this._stream=t}async read(){unreachable("Abstract method `read` called")}cancel(t){unreachable("Abstract method `cancel` called")}}function createHeaders(t,e){const i=new Headers;if(!t||!e||"object"!=typeof e)return i;for(const t in e){const s=e[t];void 0!==s&&i.append(t,s)}return i}function getResponseOrigin(t){return URL.parse(t)?.origin??null}function validateRangeRequestCapabilities({responseHeaders:t,isHttp:e,rangeChunkSize:i,disableRange:s}){const n={contentLength:0,isRangeSupported:!1},r=parseInt(t.get("Content-Length"),10);if(!Number.isInteger(r))return n;n.contentLength=r;if(r<=2*i)return n;if(s||!e)return n;if("bytes"!==t.get("Accept-Ranges"))return n;"identity"===(t.get("Content-Encoding")||"identity")&&(n.isRangeSupported=!0);return n}function extractFilenameFromHeader(t){const e=t.get("Content-Disposition");if(e){let t=function getFilenameFromContentDispositionHeader(t){let e=!0,i=toParamRegExp("filename\\*","i").exec(t);if(i){i=i[1];let t=rfc2616unquote(i);t=unescape(t);t=rfc5987decode(t);t=rfc2047decode(t);return fixupEncoding(t)}i=function rfc2231getparam(t){const e=[];let i;const s=toParamRegExp("filename\\*((?!0\\d)\\d+)(\\*?)","ig");for(;null!==(i=s.exec(t));){let[,t,s,n]=i;t=parseInt(t,10);if(t in e){if(0===t)break}else e[t]=[s,n]}const n=[];for(let t=0;t{t._responseOrigin=getResponseOrigin(i.url);ensureResponseStatus(i.status,n);this._reader=i.body.getReader();const r=i.headers,{contentLength:a,isRangeSupported:o}=validateRangeRequestCapabilities({responseHeaders:r,isHttp:!0,rangeChunkSize:s,disableRange:e});this._contentLength=a;this._isRangeSupported=o;this._filename=extractFilenameFromHeader(r);!this._isStreamingSupported&&this._isRangeSupported&&this.cancel(new AbortException("Streaming is disabled."));this._headersCapability.resolve()}).catch(this._headersCapability.reject)}async read(){await this._headersCapability.promise;const{value:t,done:e}=await this._reader.read();if(e)return{value:t,done:e};this._loaded+=t.byteLength;this._callOnProgress();return{value:getArrayBuffer(t),done:!1}}cancel(t){this._reader?.cancel(t);this._abortController.abort()}}class PDFFetchStreamRangeReader extends BasePDFStreamRangeReader{_abortController=new AbortController;_readCapability=Promise.withResolvers();_reader=null;constructor(t,e,i){super(t,e,i);const{url:s,withCredentials:n}=t._source,r=new Headers(t.headers);r.append("Range",`bytes=${e}-${i-1}`);fetchUrl(s,r,n,this._abortController).then(e=>{ensureResponseOrigin(getResponseOrigin(e.url),t._responseOrigin);ensureResponseStatus(e.status,s);this._reader=e.body.getReader();this._readCapability.resolve()}).catch(this._readCapability.reject)}async read(){await this._readCapability.promise;const{value:t,done:e}=await this._reader.read();return e?{value:t,done:e}:{value:getArrayBuffer(t),done:!1}}cancel(t){this._reader?.cancel(t);this._abortController.abort()}}function transport_stream_getArrayBuffer(t){return t instanceof Uint8Array&&t.byteLength===t.buffer.byteLength?t.buffer:new Uint8Array(t).buffer}function endRequests(){for(const t of this._requests)t.resolve({value:void 0,done:!0});this._requests.length=0}class PDFDataTransportStream extends BasePDFStream{_progressiveDone=!1;_queuedChunks=[];constructor(t){super(t,PDFDataTransportStreamReader,PDFDataTransportStreamRangeReader);const{pdfDataRangeTransport:e}=t,{initialData:i,progressiveDone:s}=e;if(i?.length>0){const t=transport_stream_getArrayBuffer(i);this._queuedChunks.push(t)}this._progressiveDone=s;e.transportReady(t=>{switch(t.type){case"range":case"progressiveRead":this.#$s(t.begin,t.chunk);break;case"progressiveDone":this._fullReader?.progressiveDone();this._progressiveDone=!0}})}#$s(t,e){const i=transport_stream_getArrayBuffer(e);if(void 0===t)this._fullReader?this._fullReader._enqueue(i):this._queuedChunks.push(i);else{const e=this._rangeReaders.keys().find(e=>e._begin===t);assert(e,"#onReceiveData - no `PDFDataTransportStreamRangeReader` instance found.");e._enqueue(i)}}getFullReader(){const t=super.getFullReader();this._queuedChunks=null;return t}getRangeReader(t,e){const i=super.getRangeReader(t,e);if(i){i.onDone=()=>this._rangeReaders.delete(i);this._source.pdfDataRangeTransport.requestDataRange(t,e)}return i}cancelAllRequests(t){super.cancelAllRequests(t);this._source.pdfDataRangeTransport.abort()}}class PDFDataTransportStreamReader extends BasePDFStreamReader{#Xs=endRequests.bind(this);_done=!1;_queuedChunks=null;_requests=[];constructor(t){super(t);const{pdfDataRangeTransport:e,disableRange:i,disableStream:s}=t._source,{length:n,contentDispositionFilename:r}=e;this._queuedChunks=t._queuedChunks||[];for(const t of this._queuedChunks)this._loaded+=t.byteLength;this._done=t._progressiveDone;this._contentLength=n;this._isStreamingSupported=!s;this._isRangeSupported=!i;isPdfFile(r)&&(this._filename=r);this._headersCapability.resolve();const a=this._loaded;Promise.resolve().then(()=>{a>0&&this._loaded===a&&this._callOnProgress()})}_enqueue(t){if(!this._done){if(this._requests.length>0){this._requests.shift().resolve({value:t,done:!1})}else this._queuedChunks.push(t);this._loaded+=t.byteLength;this._callOnProgress()}}async read(){if(this._queuedChunks.length>0){return{value:this._queuedChunks.shift(),done:!1}}if(this._done)return{value:void 0,done:!0};const t=Promise.withResolvers();this._requests.push(t);return t.promise}cancel(t){this._done=!0;this.#Xs()}progressiveDone(){this._done||=!0;0===this._queuedChunks.length&&this.#Xs()}}class PDFDataTransportStreamRangeReader extends BasePDFStreamRangeReader{#Xs=endRequests.bind(this);onDone=null;_begin=-1;_done=!1;_queuedChunk=null;_requests=[];constructor(t,e,i){super(t,e,i);this._begin=e}_enqueue(t){if(!this._done){if(0===this._requests.length)this._queuedChunk=t;else{this._requests.shift().resolve({value:t,done:!1});this.#Xs()}this._done=!0;this.onDone?.()}}async read(){if(this._queuedChunk){const t=this._queuedChunk;this._queuedChunk=null;return{value:t,done:!1}}if(this._done)return{value:void 0,done:!0};const t=Promise.withResolvers();this._requests.push(t);return t.promise}cancel(t){this._done=!0;this.#Xs();this.onDone?.()}}class PDFNetworkStream extends BasePDFStream{#qs=new WeakMap;_responseOrigin=null;constructor(t){super(t,PDFNetworkStreamReader,PDFNetworkStreamRangeReader);const{httpHeaders:e,url:i}=t;this.url=i;this.isHttp=/https?:/.test(i.protocol);this.headers=createHeaders(this.isHttp,e)}_request(t){const e=new XMLHttpRequest,i={validateStatus:null,onHeadersReceived:t.onHeadersReceived,onDone:t.onDone,onError:t.onError,onProgress:t.onProgress};this.#qs.set(e,i);e.open("GET",this.url);e.withCredentials=this._source.withCredentials;for(const[t,i]of this.headers)e.setRequestHeader(t,i);if(this.isHttp&&"begin"in t&&"end"in t){e.setRequestHeader("Range",`bytes=${t.begin}-${t.end-1}`);i.validateStatus=t=>206===t||200===t}else i.validateStatus=t=>200===t;e.responseType="arraybuffer";assert(t.onError,"Expected `onError` callback to be provided.");e.onerror=()=>t.onError(e.status);e.onreadystatechange=this.#Ks.bind(this,e);e.onprogress=this.#Ys.bind(this,e);e.send(null);return e}#Ys(t,e){const i=this.#qs.get(t);i?.onProgress?.(e)}#Ks(t,e){const i=this.#qs.get(t);if(!i)return;if(t.readyState>=2&&i.onHeadersReceived){i.onHeadersReceived();delete i.onHeadersReceived}if(4!==t.readyState)return;if(!this.#qs.has(t))return;this.#qs.delete(t);if(0===t.status&&this.isHttp){i.onError(t.status);return}const s=t.status||200;if(!i.validateStatus(s)){i.onError(t.status);return}const n=function network_getArrayBuffer(t){return"string"!=typeof t?t:stringToBytes(t).buffer}(t.response);if(206===s){const e=t.getResponseHeader("Content-Range");if(/bytes (\d+)-(\d+)\/(\d+)/.test(e))i.onDone(n);else{warn('Missing or invalid "Content-Range" header.');i.onError(0)}}else n?i.onDone(n):i.onError(t.status)}_abortRequest(t){if(this.#qs.has(t)){this.#qs.delete(t);t.abort()}}getRangeReader(t,e){const i=super.getRangeReader(t,e);i&&(i.onClosed=()=>this._rangeReaders.delete(i));return i}}class PDFNetworkStreamReader extends BasePDFStreamReader{#Xs=endRequests.bind(this);_cachedChunks=[];_done=!1;_requests=[];_storedError=null;constructor(t){super(t);this._fullRequestXhr=t._request({onHeadersReceived:this.#Qs.bind(this),onDone:this.#Js.bind(this),onError:this.#Zs.bind(this),onProgress:this.#Ys.bind(this)})}#Qs(){const t=this._stream,{disableRange:e,rangeChunkSize:i}=t._source,s=this._fullRequestXhr;t._responseOrigin=getResponseOrigin(s.responseURL);const n=s.getAllResponseHeaders(),r=new Headers(n?n.trimStart().replace(/[^\S ]+$/,"").split(/[\r\n]+/).map(t=>{const[e,...i]=t.split(": ");return[e,i.join(": ")]}):[]),{contentLength:a,isRangeSupported:o}=validateRangeRequestCapabilities({responseHeaders:r,isHttp:t.isHttp,rangeChunkSize:i,disableRange:e});this._contentLength=a;this._isRangeSupported=o;this._filename=extractFilenameFromHeader(r);this._isRangeSupported&&t._abortRequest(s);this._headersCapability.resolve()}#Js(t){if(this._requests.length>0){this._requests.shift().resolve({value:t,done:!1})}else this._cachedChunks.push(t);this._done=!0;0===this._cachedChunks.length&&this.#Xs()}#Zs(t){this._storedError=createResponseError(t,this._stream.url);this._headersCapability.reject(this._storedError);for(const t of this._requests)t.reject(this._storedError);this._requests.length=0;this._cachedChunks.length=0}#Ys(t){this.onProgress?.({loaded:t.loaded,total:t.lengthComputable?t.total:this._contentLength})}async read(){await this._headersCapability.promise;if(this._storedError)throw this._storedError;if(this._cachedChunks.length>0){return{value:this._cachedChunks.shift(),done:!1}}if(this._done)return{value:void 0,done:!0};const t=Promise.withResolvers();this._requests.push(t);return t.promise}cancel(t){this._done=!0;this._headersCapability.reject(t);this.#Xs();this._stream._abortRequest(this._fullRequestXhr);this._fullRequestXhr=null}}class PDFNetworkStreamRangeReader extends BasePDFStreamRangeReader{#Xs=endRequests.bind(this);onClosed=null;_done=!1;_queuedChunk=null;_requests=[];_storedError=null;constructor(t,e,i){super(t,e,i);this._requestXhr=t._request({begin:e,end:i,onHeadersReceived:this.#Qs.bind(this),onDone:this.#Js.bind(this),onError:this.#Zs.bind(this),onProgress:null})}#Qs(){const t=getResponseOrigin(this._requestXhr?.responseURL);try{ensureResponseOrigin(t,this._stream._responseOrigin)}catch(t){this._storedError=t;this.#Zs(0)}}#Js(t){if(this._requests.length>0){this._requests.shift().resolve({value:t,done:!1})}else this._queuedChunk=t;this._done=!0;this.#Xs();this.onClosed?.()}#Zs(t){this._storedError??=createResponseError(t,this._stream.url);for(const t of this._requests)t.reject(this._storedError);this._requests.length=0;this._queuedChunk=null}async read(){if(this._storedError)throw this._storedError;if(null!==this._queuedChunk){const t=this._queuedChunk;this._queuedChunk=null;return{value:t,done:!1}}if(this._done)return{value:void 0,done:!0};const t=Promise.withResolvers();this._requests.push(t);return t.promise}cancel(t){this._done=!0;this.#Xs();this._stream._abortRequest(this._requestXhr);this.onClosed?.()}}function getReadableStream(t,e=null){const i=process.getBuiltinModule("fs"),{Readable:s}=process.getBuiltinModule("stream"),n=i.createReadStream(t,e);return s.toWeb(n)}class PDFNodeStream extends BasePDFStream{constructor(t){super(t,PDFNodeStreamReader,PDFNodeStreamRangeReader);const{url:e}=t;assert("file:"===e.protocol,"PDFNodeStream only supports file:// URLs.")}}class PDFNodeStreamReader extends BasePDFStreamReader{_reader=null;constructor(t){super(t);const{disableRange:e,disableStream:i,rangeChunkSize:s,url:n}=t._source;this._isStreamingSupported=!i;process.getBuiltinModule("fs/promises").lstat(n).then(t=>{const i=getReadableStream(n);this._reader=i.getReader();const{size:r}=t;this._contentLength=r;this._isRangeSupported=!e&&r>2*s;!this._isStreamingSupported&&this._isRangeSupported&&this.cancel(new AbortException("Streaming is disabled."));this._headersCapability.resolve()}).catch(t=>{"ENOENT"===t.code&&(t=createResponseError(0,n));this._headersCapability.reject(t)})}async read(){await this._headersCapability.promise;const{value:t,done:e}=await this._reader.read();if(e)return{value:t,done:e};this._loaded+=t.byteLength;this._callOnProgress();return{value:getArrayBuffer(t),done:!1}}cancel(t){this._reader?.cancel(t)}}class PDFNodeStreamRangeReader extends BasePDFStreamRangeReader{_readCapability=Promise.withResolvers();_reader=null;constructor(t,e,i){super(t,e,i);const{url:s}=t._source;try{const t=getReadableStream(s,{start:e,end:i-1});this._reader=t.getReader();this._readCapability.resolve()}catch(t){this._readCapability.reject(t)}}async read(){await this._readCapability.promise;const{value:t,done:e}=await this._reader.read();return e?{value:t,done:e}:{value:getArrayBuffer(t),done:!1}}cancel(t){this._reader?.cancel(t)}}class GlobalWorkerOptions{static#tn=null;static#en="";static get workerPort(){return this.#tn}static set workerPort(t){if(!("undefined"!=typeof Worker&&t instanceof Worker)&&null!==t)throw new Error("Invalid `workerPort` type.");this.#tn=t}static get workerSrc(){return this.#en}static set workerSrc(t){if("string"!=typeof t)throw new Error("Invalid `workerSrc` type.");this.#en=t}}class Metadata{#in;#sn;constructor({parsedData:t,rawData:e}){this.#in=t;this.#sn=e}getRaw(){return this.#sn}get(t){return this.#in.get(t)??null}[Symbol.iterator](){return this.#in.entries()}}const It=Symbol("INTERNAL");class OptionalContentGroup{#nn=!1;#rn=!1;#an=!1;#on=!0;constructor(t,{name:e,intent:i,usage:s,rbGroups:n}){this.#nn=!!(t&a);this.#rn=!!(t&o);this.name=e;this.intent=i;this.usage=s;this.rbGroups=n}get visible(){if(this.#an)return this.#on;if(!this.#on)return!1;const{print:t,view:e}=this.usage;return this.#nn?"OFF"!==e?.viewState:!this.#rn||"OFF"!==t?.printState}_setVisible(t,e,i=!1){t!==It&&unreachable("Internal method `_setVisible` called.");this.#an=i;this.#on=e}}class OptionalContentConfig{#ln=null;#hn=new Map;#dn=null;#cn=null;constructor(t,e=a){this.renderingIntent=e;this.name=null;this.creator=null;if(null!==t){this.name=t.name;this.creator=t.creator;this.#cn=t.order;for(const i of t.groups)this.#hn.set(i.id,new OptionalContentGroup(e,i));if("OFF"===t.baseState)for(const t of this.#hn.values())t._setVisible(It,!1);for(const e of t.on)this.#hn.get(e)._setVisible(It,!0);for(const e of t.off)this.#hn.get(e)._setVisible(It,!1);this.#dn=this.getHash()}}#un(t){const e=t.length;if(e<2)return!0;const i=t[0];for(let s=1;st===e+1)&&(this.#pn=null)}deletePages(t){this.#yn();const e=this.#pn,i=this.#An();this.#bn={pageNumberToId:e.slice(),pagesNumber:this.#mn,prevPageNumbers:this.#gn.slice()};const s=this.#mn-t.length;this.#mn=s;const n=this.#pn=new Uint32Array(s);this.#gn=new Int32Array(s);let r=0,a=0;for(const i of t){const t=i-1;if(t!==r){n.set(e.subarray(r,t),a);a+=t-r}r=t+1}rthis.#pn[t-1])}}cancelCopy(){this.#fn=null}pastePages(t){this.#yn();const e=this.#pn,i=this.#An(),{pageNumbers:s,pageIds:n}=this.#fn,r=this.#mn+s.length;this.#mn=r;const a=this.#pn=new Uint32Array(r);this.#gn=new Int32Array(r);a.set(e.subarray(0,t),0);a.set(n,t);a.set(e.subarray(t),t+s.length);this.#vn(i,null,t,s);this.#fn=null}#vn(t,e=null,i=-1,s=null){const n=this.#gn,r=this.#pn,a=i+(s?.length??0),o=new Map;for(let l=0,h=this.#mn;l=i&&lt[0]-e[0]);for(let i=0,s=t.length;it-e);const e=new Map;for(let i=0,s=t.length;i({...Promise.withResolvers(),data:Ft});class PDFObjects{#wn=new Map;get(t,e=null){if(e){const i=this.#wn.getOrInsertComputed(t,dataObj);i.promise.then(()=>e(i.data));return null}const i=this.#wn.get(t);if(!i||i.data===Ft)throw new Error(`Requesting object that isn't resolved yet ${t}.`);return i.data}has(t){const e=this.#wn.get(t);return!!e&&e.data!==Ft}delete(t){const e=this.#wn.get(t);if(!e||e.data===Ft)return!1;this.#wn.delete(t);return!0}resolve(t,e=null){const i=this.#wn.getOrInsertComputed(t,dataObj);if(i.data!==Ft)throw new Error(`Object already resolved ${t}.`);i.data=e;i.resolve()}clear(){for(const{data:t}of this.#wn.values())t?.bitmap?.close();this.#wn.clear()}*[Symbol.iterator](){for(const[t,{data:e}]of this.#wn)e!==Ft&&(yield[t,e])}}class TextLayer{#xn=Promise.withResolvers();#_t=null;#Cn=!1;#En=!!globalThis.FontInspector?.enabled;#Tn=null;#Sn=null;#_n=null;#Pn=0;#Dn=0;#Mn=null;#kn=null;#In=0;#Fn=0;#Bn=Object.create(null);#Rn=[];#On=null;#Ln=[];#Nn=new WeakMap;#Un=null;static#Hn=new Map;static#zn=new Map;static#Gn=new WeakMap;static#Wn=null;static#Vn=new Set;constructor({textContentSource:t,images:e,container:i,viewport:s}){if(t instanceof ReadableStream)this.#On=t;else{if("object"!=typeof t)throw new Error('No "textContentSource" parameter specified.');this.#On=new ReadableStream({start(e){e.enqueue(t);e.close()}})}this.#_t=this.#kn=i;this.#Tn=e;this.#Fn=s.scale*OutputScale.pixelRatio;this.#In=s.rotation;this.#_n={div:null,properties:null,ctx:null};const{pageWidth:n,pageHeight:r,pageX:a,pageY:o}=s.rawDims;this.#Un=[1,0,0,-1,-a,o+r];this.#Dn=n;this.#Pn=r;TextLayer.#jn();i.style.setProperty("--min-font-size",TextLayer.#Wn);setLayerDimensions(i,s);this.#xn.promise.finally(()=>{TextLayer.#Vn.delete(this);this.#_n=null;this.#Bn=null}).catch(()=>{})}static get fontFamilyMap(){const{isWindows:t,isFirefox:e}=FeatureTest.platform;return shadow(this,"fontFamilyMap",new Map([["sans-serif",(t&&e?"Calibri, ":"")+"sans-serif"],["monospace",(t&&e?"Lucida Console, ":"")+"monospace"]]))}render(){this.#Tn&&this.#_t.append(this.#Tn.render());const pump=()=>{this.#Mn.read().then(({value:t,done:e})=>{if(e)this.#xn.resolve();else{this.#Sn??=t.lang;Object.assign(this.#Bn,t.styles);this.#$n(t.items);pump()}},this.#xn.reject)};this.#Mn=this.#On.getReader();TextLayer.#Vn.add(this);pump();return this.#xn.promise}update({viewport:t,onBefore:e=null}){const i=t.scale*OutputScale.pixelRatio,s=t.rotation;if(s!==this.#In){e?.();this.#In=s;setLayerDimensions(this.#kn,{rotation:s})}if(i!==this.#Fn){e?.();this.#Fn=i;const t={div:null,properties:null,ctx:TextLayer.#Xn(this.#Sn)};for(const e of this.#Ln){t.properties=this.#Nn.get(e);t.div=e;this.#qn(t)}}}cancel(){const t=new AbortException("TextLayer task cancelled.");this.#Mn?.cancel(t).catch(()=>{});this.#Mn=null;this.#xn.reject(t)}get textDivs(){return this.#Ln}get textContentItemsStr(){return this.#Rn}#$n(t){if(this.#Cn)return;this.#_n.ctx??=TextLayer.#Xn(this.#Sn);const e=this.#Ln,i=this.#Rn;for(const s of t){if(e.length>1e5){warn("Ignoring additional textDivs for performance reasons.");this.#Cn=!0;return}if(void 0!==s.str){i.push(s.str);this.#Kn(s)}else if("beginMarkedContentProps"===s.type||"beginMarkedContent"===s.type){const t=this.#_t;this.#_t=document.createElement("span");this.#_t.classList.add("markedContent");s.id&&this.#_t.setAttribute("id",`${s.id}`);"Artifact"===s.tag&&(this.#_t.ariaHidden=!0);t.append(this.#_t)}else"endMarkedContent"===s.type&&(this.#_t=this.#_t.parentNode)}}#Kn(t){const e=document.createElement("span"),i={angle:0,canvasWidth:0,hasText:""!==t.str,hasEOL:t.hasEOL,fontSize:0};this.#Ln.push(e);const s=Util.transform(this.#Un,t.transform);let n=Math.atan2(s[1],s[0]);const r=this.#Bn[t.fontName];r.vertical&&(n+=Math.PI/2);let a=this.#En&&r.fontSubstitution||r.fontFamily;a=TextLayer.fontFamilyMap.get(a)||a;const o=Math.hypot(s[2],s[3]),l=o*TextLayer.#Yn(a,r,this.#Sn);let h,d;if(0===n){h=s[4];d=s[5]-l}else{h=s[4]+l*Math.sin(n);d=s[5]-l*Math.cos(n)}const c=e.style;c.left=`${(100*h/this.#Dn).toFixed(2)}%`;c.top=`${(100*d/this.#Pn).toFixed(2)}%`;c.setProperty("--font-height",`${o.toFixed(2)}px`);c.fontFamily=a;i.fontSize=o;e.setAttribute("role","presentation");e.textContent=t.str;e.dir=t.dir;this.#En&&(e.dataset.fontName=r.fontSubstitutionLoadedName||t.fontName);0!==n&&(i.angle=n*(180/Math.PI));let u=!1;if(t.str.length>1)u=!0;else if(" "!==t.str&&t.transform[0]!==t.transform[3]){const e=Math.abs(t.transform[0]),i=Math.abs(t.transform[3]);e!==i&&Math.max(e,i)/Math.min(e,i)>1.5&&(u=!0)}u&&(i.canvasWidth=r.vertical?t.height:t.width);this.#Nn.set(e,i);this.#_n.div=e;this.#_n.properties=i;this.#qn(this.#_n);i.hasText&&this.#_t.append(e);if(i.hasEOL){const t=document.createElement("br");t.setAttribute("role","presentation");this.#_t.append(t)}}#qn(t){const{div:e,properties:i,ctx:s}=t,{style:n}=e;if(0!==i.canvasWidth&&i.hasText){const{fontFamily:t}=n,{canvasWidth:r,fontSize:a}=i;TextLayer.#Qn(s,a*this.#Fn,t);const{width:o}=s.measureText(e.textContent);o>0&&n.setProperty("--scale-x",r*this.#Fn/o)}0!==i.angle&&n.setProperty("--rotate",`${i.angle}deg`)}static cleanup(){if(!(this.#Vn.size>0)){this.#Hn.clear();for(const{canvas:t}of this.#zn.values())t.remove();this.#zn.clear()}}static#Xn(t=null){let e=this.#zn.get(t||="");if(!e){const i=document.createElement("canvas");i.className="hiddenCanvasElement";i.lang=t;document.body.append(i);e=i.getContext("2d",{alpha:!1,willReadFrequently:!0});this.#zn.set(t,e);this.#Gn.set(e,{size:0,family:""})}return e}static#Qn(t,e,i){const s=this.#Gn.get(t);if(e!==s.size||i!==s.family){t.font=`${e}px ${i}`;s.size=e;s.family=i}}static#jn(){if(null!==this.#Wn)return;const t=document.createElement("div");t.style.opacity=0;t.style.lineHeight=1;t.style.fontSize="1px";t.style.position="absolute";t.textContent="X";document.body.append(t);this.#Wn=t.getBoundingClientRect().height;t.remove()}static#Yn(t,e,i){const s=this.#Hn.get(t);if(s)return s;const n=this.#Xn(i);n.canvas.width=n.canvas.height=30;this.#Qn(n,30,t);const r=n.measureText(""),a=r.fontBoundingBoxAscent,o=Math.abs(r.fontBoundingBoxDescent);n.canvas.width=n.canvas.height=0;let l=.8;if(a)l=a/(a+o);else{FeatureTest.platform.isFirefox&&warn("Enable the `dom.textMetrics.fontBoundingBox.enabled` preference in `about:config` to improve TextLayer rendering.");e.ascent?l=e.ascent:e.descent&&(l=1+e.descent)}this.#Hn.set(t,l);return l}}function getDocument(e={}){"string"==typeof e||e instanceof URL?e={url:e}:(e instanceof ArrayBuffer||ArrayBuffer.isView(e))&&(e={data:e});const i=new PDFDocumentLoadingTask,{docId:s}=i,n=e.url?function getUrlProp(e){if(e instanceof URL)return e;if("string"==typeof e){if(t){if(/^[a-z][a-z0-9\-+.]+:/i.test(e))return new URL(e);const t=process.getBuiltinModule("url");return new URL(t.pathToFileURL(e))}const i=URL.parse(e,window.location);if(i)return i}throw new Error("Invalid PDF url data: either string or URL-object is expected in the url property.")}(e.url):null,r=e.data?function getDataProp(e){if(t&&"undefined"!=typeof Buffer&&e instanceof Buffer)throw new Error("Please provide binary data as `Uint8Array`, rather than `Buffer`.");if(e instanceof Uint8Array&&e.byteLength===e.buffer.byteLength)return e;if("string"==typeof e)return stringToBytes(e);if(e instanceof ArrayBuffer||ArrayBuffer.isView(e)||"object"==typeof e&&!isNaN(e?.length))return new Uint8Array(e);throw new Error("Invalid PDF binary data: either TypedArray, string, or array-like object is expected in the data property.")}(e.data):null,a=e.httpHeaders||null,o=!0===e.withCredentials,l=e.password??null,h=e.range instanceof PDFDataRangeTransport?e.range:null,d=Number.isInteger(e.rangeChunkSize)&&e.rangeChunkSize>0?e.rangeChunkSize:65536;let c=e.worker instanceof PDFWorker?e.worker:null;const u=e.verbosity,p="string"!=typeof e.docBaseUrl||isDataScheme(e.docBaseUrl)?null:e.docBaseUrl,g=getFactoryUrlProp(e.cMapUrl),m=!1!==e.cMapPacked,f=getFactoryUrlProp(e.iccUrl),b=getFactoryUrlProp(e.standardFontDataUrl),y=getFactoryUrlProp(e.wasmUrl),A=!0!==e.stopAtErrors,v=Number.isInteger(e.maxImageSize)&&e.maxImageSize>-1?e.maxImageSize:-1,w="boolean"==typeof e.isOffscreenCanvasSupported?e.isOffscreenCanvasSupported:!t,x="boolean"==typeof e.isImageDecoderSupported?e.isImageDecoderSupported:!t&&(FeatureTest.platform.isFirefox||!globalThis.chrome),C=Number.isInteger(e.canvasMaxAreaInBytes)?e.canvasMaxAreaInBytes:-1,E="boolean"==typeof e.disableFontFace?e.disableFontFace:t,T=!0===e.fontExtraProperties,S=!0===e.enableXfa,_=e.ownerDocument||globalThis.document,P=!0===e.disableRange,D=!0===e.disableStream,M=!0===e.disableAutoFetch,k=!0===e.pdfBug,I=e.CanvasFactory||(t?NodeCanvasFactory:DOMCanvasFactory),F=e.FilterFactory||(t?NodeFilterFactory:DOMFilterFactory),B=e.BinaryDataFactory||(t?NodeBinaryDataFactory:DOMBinaryDataFactory),R=!0===e.enableHWA,O=!0===e.enableWebGPU?function initGPU(){return At.init()}():Promise.resolve(!1),L=!1!==e.useWasm,N=e.pagesMapper||new PagesMapper,U="boolean"==typeof e.useSystemFonts?e.useSystemFonts:!t&&!E,H="boolean"==typeof e.useWorkerFetch?e.useWorkerFetch:!!(B===DOMBinaryDataFactory&&g&&m&&b&&y&&isValidFetchUrl(g,document.baseURI)&&isValidFetchUrl(b,document.baseURI)&&isValidFetchUrl(y,document.baseURI));setVerbosityLevel(u);const z={canvasFactory:new I({ownerDocument:_,enableHWA:R}),filterFactory:new F({docId:s,ownerDocument:_}),binaryDataFactory:H?null:new B({cMapUrl:g,standardFontDataUrl:b,wasmUrl:y})};if(!c){c=PDFWorker.create({verbosity:u,port:GlobalWorkerOptions.workerPort});i._worker=c}const G={docId:s,apiVersion:"5.7.284",data:r,password:l,disableAutoFetch:M,rangeChunkSize:d,docBaseUrl:p,enableXfa:S,evaluatorOptions:{maxImageSize:v,disableFontFace:E,ignoreErrors:A,isOffscreenCanvasSupported:w,isImageDecoderSupported:x,canvasMaxAreaInBytes:C,fontExtraProperties:T,useSystemFonts:U,useWasm:L,useWorkerFetch:H,cMapUrl:g,cMapPacked:m,iccUrl:f,standardFontDataUrl:b,wasmUrl:y,hasGPU:!1}},W={ownerDocument:_,pdfBug:k,styleElement:null,enableHWA:R,loadingParams:{disableAutoFetch:M,enableXfa:S}};Promise.all([c.promise,O]).then(function([,e]){if(i.destroyed)throw new Error("Loading aborted");if(c.destroyed)throw new Error("Worker was destroyed");G.evaluatorOptions.hasGPU=e;const l=c.messageHandler.sendWithPromise("GetDocRequest",G,r?[r.buffer]:null);let u;if(r);else if(h)u=new PDFDataTransportStream({pdfDataRangeTransport:h,disableRange:P,disableStream:D});else{if(!n)throw new Error("getDocument - expected either `data`, `range`, or `url` parameter.");{const e=function getNetworkStream(e){return isValidFetchUrl(e)?PDFFetchStream:t?PDFNodeStream:PDFNetworkStream}(n);u=new e({url:n,httpHeaders:a,withCredentials:o,rangeChunkSize:d,disableRange:P,disableStream:D})}}return l.then(t=>{if(i.destroyed)throw new Error("Loading aborted");if(c.destroyed)throw new Error("Worker was destroyed");const e=new MessageHandler(s,t,c.port),n=new WorkerTransport(e,i,u,W,z,N);i._transport=n;e.send("Ready",null)})}).catch(i._capability.reject);return i}class PDFDocumentLoadingTask{static#xs=0;_capability=Promise.withResolvers();_transport=null;_worker=null;docId="d"+PDFDocumentLoadingTask.#xs++;destroyed=!1;onPassword=null;onProgress=null;get promise(){return this._capability.promise}async destroy(){this.destroyed=!0;try{this._worker?.port&&(this._worker._pendingDestroy=!0);await(this._transport?.destroy())}catch(t){this._worker?.port&&delete this._worker._pendingDestroy;throw t}this._transport=null;this._worker?.destroy();this._worker=null}async getData(){return this._transport.getData()}}class PDFDataRangeTransport{#xn=Promise.withResolvers();#Jn=null;constructor(t,e,i=!1,s=null){this.length=t;this.initialData=e;this.progressiveDone=i;this.contentDispositionFilename=s;Object.defineProperty(this,"onDataProgress",{value:()=>{!function deprecated(t){console.log("Deprecated API usage: "+t)}("`PDFDataRangeTransport.prototype.onDataProgress` - method was removed, since loading progress is now reported automatically through the `PDFDataTransportStream` class (and related code).")}})}onDataRange(t,e){this.#Jn({type:"range",begin:t,chunk:e})}onDataProgressiveRead(t){this.#xn.promise.then(()=>{this.#Jn({type:"progressiveRead",chunk:t})})}onDataProgressiveDone(){this.#xn.promise.then(()=>{this.#Jn({type:"progressiveDone"})})}transportReady(t){this.#Jn=t;this.#xn.resolve()}requestDataRange(t,e){unreachable("Abstract method PDFDataRangeTransport.requestDataRange")}abort(){}}class PDFDocumentProxy{constructor(t,e){this._pdfInfo=t;this._transport=e}get pagesMapper(){return this._transport.pagesMapper}get annotationStorage(){return this._transport.annotationStorage}get canvasFactory(){return this._transport.canvasFactory}get filterFactory(){return this._transport.filterFactory}get numPages(){return this._pdfInfo.numPages}get fingerprints(){return this._pdfInfo.fingerprints}get isPureXfa(){return shadow(this,"isPureXfa",!!this._transport._htmlForXfa)}get allXfaHtml(){return this._transport._htmlForXfa}getPage(t){return this._transport.getPage(t)}getPageIndex(t){return this._transport.getPageIndex(t)}getDestinations(){return this._transport.getDestinations()}getDestination(t){return this._transport.getDestination(t)}getPageLabels(){return this._transport.getPageLabels()}getPageLayout(){return this._transport.getPageLayout()}getPageMode(){return this._transport.getPageMode()}getViewerPreferences(){return this._transport.getViewerPreferences()}getOpenAction(){return this._transport.getOpenAction()}getAttachments(){return this._transport.getAttachments()}getAnnotationsByType(t,e){return this._transport.getAnnotationsByType(t,e)}getJSActions(){return this._transport.getDocJSActions()}getOutline(){return this._transport.getOutline()}getOptionalContentConfig({intent:t="display"}={}){const{renderingIntent:e}=this._transport.getRenderingIntent(t);return this._transport.getOptionalContentConfig(e)}getPermissions(){return this._transport.getPermissions()}getMetadata(){return this._transport.getMetadata()}getMarkInfo(){return this._transport.getMarkInfo()}getData(){return this._transport.getData()}saveDocument(){return this._transport.saveDocument()}extractPages(t){return this._transport.extractPages(t)}getDownloadInfo(){return this._transport.downloadInfoCapability.promise}getRawData(t){return this._transport.getRawData(t)}cleanup(t=!1){return this._transport.startCleanup(t||this.isPureXfa)}destroy(){return this.loadingTask.destroy()}cachedPageNumber(t){return this._transport.cachedPageNumber(t)}get loadingParams(){return this._transport.loadingParams}get loadingTask(){return this._transport.loadingTask}getFieldObjects(){return this._transport.getFieldObjects()}hasJSActions(){return this._transport.hasJSActions()}getCalculationOrderIds(){return this._transport.getCalculationOrderIds()}}class PDFPageProxy{#Zn=!1;#tr=null;constructor(t,e,i,s,n=!1){this._pageIndex=t;this._pageInfo=e;this._transport=i;this._stats=n?new StatTimer:null;this._pdfBug=n;this.commonObjs=i.commonObjs;this.objs=new PDFObjects;this._intentStates=new Map;this.destroyed=!1;this.recordedBBoxes=null;this.#tr=s;this.imageCoordinates=null}clone(t){const e=new PDFPageProxy(t,this._pageInfo,this._transport,this.#tr,this._pdfBug);e.clonedFromIndex=this.clonedFromIndex??this._pageIndex;this._transport.updatePage(e);return e}get pageNumber(){return this._pageIndex+1}set pageNumber(t){this._pageIndex=t-1;this._transport.updatePage(this)}get rotate(){return this._pageInfo.rotate}get ref(){return this._pageInfo.ref}get userUnit(){return this._pageInfo.userUnit}get view(){return this._pageInfo.view}getViewport({scale:t,rotation:e=this.rotate,offsetX:i=0,offsetY:s=0,dontFlip:n=!1}={}){return new PageViewport({viewBox:this.view,userUnit:this.userUnit,scale:t,rotation:e,offsetX:i,offsetY:s,dontFlip:n})}getAnnotations({intent:t="display"}={}){const{renderingIntent:e}=this._transport.getRenderingIntent(t);return this._transport.getAnnotations(this._pageIndex,e)}getJSActions(){return this._transport.getPageJSActions(this._pageIndex)}get filterFactory(){return this._transport.filterFactory}get isPureXfa(){return shadow(this,"isPureXfa",!!this._transport._htmlForXfa)}async getXfa(){return this._transport._htmlForXfa?.children[this._pageIndex]||null}render({canvasContext:t,canvas:e=t.canvas,viewport:i,intent:s="display",annotationMode:n=p.ENABLE,transform:r=null,background:a=null,optionalContentConfigPromise:l=null,annotationCanvasMap:h=null,pageColors:d=null,printAnnotationStorage:c=null,isEditing:u=!1,recordImages:g=!1,recordOperations:m=!1,operationsFilter:f=null}){this._stats?.time("Overall");const b=this._transport.getRenderingIntent(s,n,c,u),{renderingIntent:y,cacheKey:A}=b;this.#Zn=!1;l||=this._transport.getOptionalContentConfig(y);const v=this._intentStates.getOrInsertComputed(A,makeObj);if(v.streamReaderCancelTimeout){clearTimeout(v.streamReaderCancelTimeout);v.streamReaderCancelTimeout=null}const w=!!(y&o);if(!v.displayReadyCapability){v.displayReadyCapability=Promise.withResolvers();v.operatorList={fnArray:[],argsArray:[],lastChunk:!1,separateAnnots:null};this._stats?.time("Page Request");this._pumpOperatorList(b)}const x=!(!this._pdfBug||!globalThis.StepperManager?.enabled),C=!!e&&!this.recordedBBoxes&&(m||x),E=!!e&&!this.imageCoordinates&&g,complete=t=>{v.renderTasks.delete(_);if(C){const t=_.gfx?.dependencyTracker.take();if(t){_.stepper?.setOperatorBBoxes(t,_.gfx.dependencyTracker.takeDebugMetadata());m&&(this.recordedBBoxes=t)}}E&&!t&&(this.imageCoordinates=_.gfx?.imagesTracker.take());w&&(this.#Zn=!0);this.#er();if(t){_.capability.reject(t);this._abortOperatorList({intentState:v,reason:t instanceof Error?t:new Error(t)})}else _.capability.resolve();if(this._stats){this._stats.timeEnd("Rendering");this._stats.timeEnd("Overall");globalThis.Stats?.enabled&&globalThis.Stats.add(this.pageNumber,this._stats)}};let T=null,S=null;(C||E)&&(S=new CanvasBBoxTracker(e,v.operatorList.length));C&&(T=new CanvasDependencyTracker(S,x));const _=new InternalRenderTask({callback:complete,params:{canvas:e,canvasContext:t,dependencyTracker:T??S,imagesTracker:E?new CanvasImagesTracker(e):null,viewport:i,transform:r,background:a},objs:this.objs,commonObjs:this.commonObjs,annotationCanvasMap:h,operatorList:v.operatorList,pageIndex:this._pageIndex,canvasFactory:this._transport.canvasFactory,filterFactory:this._transport.filterFactory,useRequestAnimationFrame:!w,pdfBug:this._pdfBug,pageColors:d,enableHWA:this._transport.enableHWA,operationsFilter:f});(v.renderTasks||=new Set).add(_);const P=_.task;Promise.all([v.displayReadyCapability.promise,l]).then(([t,e])=>{if(this.destroyed)complete();else{this._stats?.time("Rendering");if(!(e.renderingIntent&y))throw new Error("Must use the same `intent`-argument when calling the `PDFPageProxy.render` and `PDFDocumentProxy.getOptionalContentConfig` methods.");_.initializeGraphics({transparency:t,optionalContentConfig:e});_.operatorListChanged()}}).catch(complete);return P}getOperatorList({intent:t="display",annotationMode:e=p.ENABLE,printAnnotationStorage:i=null,isEditing:s=!1}={}){const n=this._transport.getRenderingIntent(t,e,i,s,!0),r=this._intentStates.getOrInsertComputed(n.cacheKey,makeObj);let a;if(!r.opListReadCapability){a=Object.create(null);a.operatorListChanged=function operatorListChanged(){if(r.operatorList.lastChunk){r.opListReadCapability.resolve(r.operatorList);r.renderTasks.delete(a)}};r.opListReadCapability=Promise.withResolvers();(r.renderTasks||=new Set).add(a);r.operatorList={fnArray:[],argsArray:[],lastChunk:!1,separateAnnots:null};this._stats?.time("Page Request");this._pumpOperatorList(n)}return r.opListReadCapability.promise}streamTextContent({includeMarkedContent:t=!1,disableNormalization:e=!1}={}){return this._transport.messageHandler.sendWithStream("GetTextContent",{pageId:this.#tr.getPageId(this._pageIndex+1)-1,pageIndex:this._pageIndex,includeMarkedContent:!0===t,disableNormalization:!0===e},{highWaterMark:100,size:t=>t.items.length})}async getTextContent(t={}){if(this._transport._htmlForXfa)return this.getXfa().then(t=>XfaText.textContent(t));const e=this.streamTextContent(t),i={items:[],styles:Object.create(null),lang:null};for await(const t of e){i.lang??=t.lang;Object.assign(i.styles,t.styles);i.items.push(...t.items)}return i}getStructTree(){return this._transport.getStructTree(this._pageIndex)}_destroy(){this.destroyed=!0;const t=[];for(const e of this._intentStates.values()){this._abortOperatorList({intentState:e,reason:new Error("Page was destroyed."),force:!0});if(!e.opListReadCapability)for(const i of e.renderTasks){t.push(i.completed);i.cancel()}}this.objs.clear();this.#Zn=!1;return Promise.all(t)}cleanup(t=!1){this.#Zn=!0;const e=this.#er();t&&e&&(this._stats&&=new StatTimer);return e}#er(){if(!this.#Zn||this.destroyed)return!1;for(const{renderTasks:t,operatorList:e}of this._intentStates.values())if(t.size>0||!e.lastChunk)return!1;this._intentStates.clear();this.objs.clear();this.#Zn=!1;return!0}_startRenderPage(t,e){const i=this._intentStates.get(e);if(i){this._stats?.timeEnd("Page Request");i.displayReadyCapability?.resolve(t)}}_renderPageChunk(t,e){for(let i=0,s=t.length;i{a.read().then(({value:t,done:e})=>{if(e)o.streamReader=null;else if(!this._transport.destroyed){this._renderPageChunk(t,o);pump()}},t=>{o.streamReader=null;if(!this._transport.destroyed){if(o.operatorList){o.operatorList.lastChunk=!0;for(const t of o.renderTasks)t.operatorListChanged();this.#er()}if(o.displayReadyCapability)o.displayReadyCapability.reject(t);else{if(!o.opListReadCapability)throw t;o.opListReadCapability.reject(t)}}})};pump()}_abortOperatorList({intentState:t,reason:e,force:i=!1}){if(t.streamReader){if(t.streamReaderCancelTimeout){clearTimeout(t.streamReaderCancelTimeout);t.streamReaderCancelTimeout=null}if(!i){if(t.renderTasks.size>0)return;if(e instanceof RenderingCancelledException){let i=100;e.extraDelay>0&&e.extraDelay<1e3&&(i+=e.extraDelay);t.streamReaderCancelTimeout=setTimeout(()=>{t.streamReaderCancelTimeout=null;this._abortOperatorList({intentState:t,reason:e,force:!0})},i);return}}t.streamReader.cancel(new AbortException(e.message)).catch(()=>{});t.streamReader=null;if(!this._transport.destroyed){for(const[e,i]of this._intentStates)if(i===t){this._intentStates.delete(e);break}this.cleanup()}}}get stats(){return this._stats}}class PDFWorker{#xn=Promise.withResolvers();#ir=null;#tn=null;#sr=null;static#nr=0;static#rr=!1;static#ar=new WeakMap;static{if(t){this.#rr=!0;GlobalWorkerOptions.workerSrc||="./pdf.worker.mjs"}this._isSameOrigin=(t,e)=>{const i=URL.parse(t);if(!i?.origin||"null"===i.origin)return!1;const s=new URL(e,i);return i.origin===s.origin};this._createCDNWrapper=t=>{const e=`await import("${t}");`;return URL.createObjectURL(new Blob([e],{type:"text/javascript"}))}}constructor({name:t=null,port:e=null,verbosity:i=getVerbosityLevel()}={}){this.name=t;this.destroyed=!1;this.verbosity=i;if(e){if(PDFWorker.#ar.has(e))throw new Error("Cannot use more than one PDFWorker per port.");PDFWorker.#ar.set(e,this);this.#or(e)}else this.#lr()}get promise(){return this.#xn.promise}#hr(){this.#xn.resolve();this.#ir.send("configure",{verbosity:this.verbosity})}get port(){return this.#tn}get messageHandler(){return this.#ir}#or(t){this.#tn=t;this.#ir=new MessageHandler("main","worker",t);this.#ir.on("ready",()=>{});this.#hr()}#lr(){if(PDFWorker.#rr||PDFWorker.#dr){this.#cr();return}let{workerSrc:t}=PDFWorker;try{PDFWorker._isSameOrigin(window.location,t)||(t=PDFWorker._createCDNWrapper(new URL(t,window.location).href));const e=new Worker(t,{type:"module"}),i=new MessageHandler("main","worker",e),terminateEarly=()=>{s.abort();i.destroy();e.terminate();this.destroyed?this.#xn.reject(new Error("Worker was destroyed")):this.#cr()},s=new AbortController;e.addEventListener("error",()=>{this.#sr||terminateEarly()},{signal:s.signal});i.on("test",t=>{s.abort();if(!this.destroyed&&t){this.#ir=i;this.#tn=e;this.#sr=e;this.#hr()}else terminateEarly()});i.on("ready",t=>{s.abort();if(this.destroyed)terminateEarly();else try{sendTest()}catch{this.#cr()}});const sendTest=()=>{const t=new Uint8Array;i.send("test",t,[t.buffer])};sendTest();return}catch{info("The worker has been disabled.")}this.#cr()}#cr(){if(!PDFWorker.#rr){warn("Setting up fake worker.");PDFWorker.#rr=!0}PDFWorker._setupFakeWorkerGlobal.then(t=>{if(this.destroyed){this.#xn.reject(new Error("Worker was destroyed"));return}const e=new LoopbackPort;this.#tn=e;const i="fake"+PDFWorker.#nr++,s=new MessageHandler(i+"_worker",i,e);t.setup(s,e);this.#ir=new MessageHandler(i,i+"_worker",e);this.#hr()}).catch(t=>{this.#xn.reject(new Error(`Setting up fake worker failed: "${t.message}".`))})}destroy(){this.destroyed=!0;this.#sr?.terminate();this.#sr=null;PDFWorker.#ar.delete(this.#tn);this.#tn=null;this.#ir?.destroy();this.#ir=null}static create(t){const e=this.#ar.get(t?.port);if(e){if(e._pendingDestroy)throw new Error("PDFWorker.create - the worker is being destroyed.\nPlease remember to await `PDFDocumentLoadingTask.destroy()`-calls.");return e}return new PDFWorker(t)}static get workerSrc(){if(GlobalWorkerOptions.workerSrc)return GlobalWorkerOptions.workerSrc;throw new Error('No "GlobalWorkerOptions.workerSrc" specified.')}static get#dr(){try{return globalThis.pdfjsWorker?.WorkerMessageHandler||null}catch{return null}}static get _setupFakeWorkerGlobal(){return shadow(this,"_setupFakeWorkerGlobal",(async()=>{if(this.#dr)return this.#dr;return(await import( +/*webpackIgnore: true*/ +/*@vite-ignore*/ +this.workerSrc)).WorkerMessageHandler})())}}class WorkerTransport{downloadInfoCapability=Promise.withResolvers();#ur=null;#pr=new Map;#gr=null;#mr=new Map;#fr=new Map;#br=new Map;#yr=null;constructor(t,e,i,s,n,r){this.messageHandler=t;this.loadingTask=e;this.#gr=i;this.commonObjs=new PDFObjects;this.fontLoader=new FontLoader({ownerDocument:s.ownerDocument,styleElement:s.styleElement});this.enableHWA=s.enableHWA;this.loadingParams=s.loadingParams;this._params=s;this.canvasFactory=n.canvasFactory;this.filterFactory=n.filterFactory;this.binaryDataFactory=n.binaryDataFactory;this.pagesMapper=r;this.destroyed=!1;this.destroyCapability=null;this.setupMessageHandler()}updatePage(t){const{_pageIndex:e}=t;this.#mr.set(e,t);this.#fr.set(e,Promise.resolve(t))}#Ar(t,e=null){return this.#pr.getOrInsertComputed(t,()=>this.messageHandler.sendWithPromise(t,e))}#Ys({loaded:t,total:e}){this.loadingTask.onProgress?.({loaded:t,total:e,percent:e?MathClamp(Math.round(t/e*100),0,100):NaN})}get annotationStorage(){return shadow(this,"annotationStorage",new AnnotationStorage)}getRenderingIntent(t,e=p.ENABLE,i=null,s=!1,n=!1){let g=a,m=Y;switch(t){case"any":g=r;break;case"display":break;case"print":g=o;break;default:warn(`getRenderingIntent - invalid intent: ${t}`)}const f=g&o&&i instanceof PrintAnnotationStorage?i:this.annotationStorage;switch(e){case p.DISABLE:g+=d;break;case p.ENABLE:break;case p.ENABLE_FORMS:g+=l;break;case p.ENABLE_STORAGE:g+=h;m=f.serializable;break;default:warn(`getRenderingIntent - invalid annotationMode: ${e}`)}s&&(g+=c);n&&(g+=u);const{ids:b,hash:y}=f.modifiedIds;return{renderingIntent:g,cacheKey:[g,m.hash,y].join("_"),annotationStorageSerializable:m,modifiedIds:b}}destroy(){if(this.destroyCapability)return this.destroyCapability.promise;this.destroyed=!0;this.destroyCapability=Promise.withResolvers();this.#yr?.reject(new Error("Worker was destroyed during onPassword callback"));const t=[];for(const e of this.#mr.values())t.push(e._destroy());this.#mr.clear();this.#fr.clear();this.#br.clear();Object.hasOwn(this,"annotationStorage")&&this.annotationStorage.resetModified();const e=this.messageHandler.sendWithPromise("Terminate",null);t.push(e);Promise.all(t).then(()=>{this.commonObjs.clear();this.fontLoader.clear();this.#pr.clear();this.filterFactory.destroy();TextLayer.cleanup();this.#gr?.cancelAllRequests(new AbortException("Worker was terminated."));this.messageHandler?.destroy();this.messageHandler=null;this.destroyCapability.resolve()},this.destroyCapability.reject);return this.destroyCapability.promise}setupMessageHandler(){const{messageHandler:t,loadingTask:e}=this;t.on("GetReader",(t,e)=>{assert(this.#gr,"GetReader - no `BasePDFStream` instance available.");this.#ur=this.#gr.getFullReader();this.#ur.onProgress=t=>this.#Ys(t);e.onPull=()=>{this.#ur.read().then(function({value:t,done:i}){if(i)e.close();else{assert(t instanceof ArrayBuffer,"GetReader - expected an ArrayBuffer.");e.enqueue(new Uint8Array(t),1,[t])}}).catch(t=>{e.error(t)})};e.onCancel=t=>{this.#ur.cancel(t);e.ready.catch(t=>{if(!this.destroyed)throw t})}});t.on("ReaderHeadersReady",async t=>{await this.#ur.headersReady;const{isStreamingSupported:e,isRangeSupported:i,contentLength:s}=this.#ur;e&&i&&(this.#ur.onProgress=null);return{isStreamingSupported:e,isRangeSupported:i,contentLength:s}});t.on("GetRangeReader",(t,e)=>{assert(this.#gr,"GetRangeReader - no `BasePDFStream` instance available.");const i=this.#gr.getRangeReader(t.begin,t.end);if(i){e.onPull=()=>{i.read().then(function({value:t,done:i}){if(i)e.close();else{assert(t instanceof ArrayBuffer,"GetRangeReader - expected an ArrayBuffer.");e.enqueue(new Uint8Array(t),1,[t])}}).catch(t=>{e.error(t)})};e.onCancel=t=>{i.cancel(t);e.ready.catch(t=>{if(!this.destroyed)throw t})}}else e.close()});t.on("GetDoc",({pdfInfo:t})=>{this.pagesMapper.pagesNumber=t.numPages;this._numPages=t.numPages;this._htmlForXfa=t.htmlForXfa;delete t.htmlForXfa;e._capability.resolve(new PDFDocumentProxy(t,this))});t.on("DocException",t=>{e._capability.reject(wrapReason(t))});t.on("PasswordRequest",t=>{this.#yr=Promise.withResolvers();try{if(!e.onPassword)throw wrapReason(t);const updatePassword=t=>{t instanceof Error?this.#yr.reject(t):this.#yr.resolve({password:t})};e.onPassword(updatePassword,t.code)}catch(t){this.#yr.reject(t)}return this.#yr.promise});t.on("DataLoaded",t=>{this.#Ys({loaded:t.length,total:t.length});this.downloadInfoCapability.resolve(t)});t.on("StartRenderPage",t=>{if(this.destroyed)return;this.#mr.get(t.pageIndex)._startRenderPage(t.transparency,t.cacheKey)});t.on("commonobj",([e,i,s])=>{if(this.destroyed)return null;if(this.commonObjs.has(e))return null;switch(i){case"Font":if("error"in s){const t=s.error;warn(`Error during font loading: ${t}`);this.commonObjs.resolve(e,t);break}const n=new FontInfo(s),r=this._params.pdfBug&&globalThis.FontInspector?.enabled?(t,e)=>globalThis.FontInspector.fontAdded(t,e):null,a=new FontFaceObject(n,r,s.charProcOperatorList,s.extra);this.fontLoader.bind(a).catch(()=>t.sendWithPromise("FontFallback",{id:e})).finally(()=>{a.fontExtraProperties||a.clearData();this.commonObjs.resolve(e,a)});break;case"CopyLocalImage":const{imageRef:o}=s;assert(o,"The imageRef must be defined.");for(const t of this.#mr.values())for(const[,i]of t.objs){if(i?.ref!==o)continue;if(!i.dataLen)return null;const t=structuredClone(i);this.commonObjs.resolve(e,t);return i.dataLen}break;case"FontPath":this.commonObjs.resolve(e,new FontPathInfo(s));break;case"Image":this.commonObjs.resolve(e,s);break;case"Pattern":const l=new PatternInfo(s);this.commonObjs.resolve(e,l.getIR());break;default:throw new Error(`Got unknown common object type ${i}`)}return null});t.on("obj",([t,e,i,s])=>{if(this.destroyed)return;const n=this.#mr.get(e);if(!n.objs.has(t))if(0!==n._intentStates.size)switch(i){case"Image":case"Pattern":n.objs.resolve(t,s);break;default:throw new Error(`Got unknown object type ${i}`)}else s?.bitmap?.close()});t.on("DocProgress",t=>{this.destroyed||this.#Ys(t)});t.on("FetchBinaryData",async t=>{if(this.destroyed)throw new Error("Worker was destroyed.");if(!this.binaryDataFactory)throw new Error("`BinaryDataFactory` not initialized, see the `useWorkerFetch` parameter.");return this.binaryDataFactory.fetch(t)})}getData(){return this.messageHandler.sendWithPromise("GetData",null)}saveDocument(){this.annotationStorage.size<=0&&warn("saveDocument called while `annotationStorage` is empty, please use the getData-method instead.");const{map:t,transfer:e}=this.annotationStorage.serializable;return this.messageHandler.sendWithPromise("SaveDocument",{isPureXfa:!!this._htmlForXfa,numPages:this._numPages,annotationStorage:t,filename:this.#ur?.filename??null},e).finally(()=>{this.annotationStorage.resetModified()})}extractPages(t){const e={pageInfos:t};let i;if(this.annotationStorage.size>0){const{map:t,transfer:s}=this.annotationStorage.serializable;e.annotationStorage=t;i=s}return this.messageHandler.sendWithPromise("ExtractPages",e,i).finally(()=>{this.annotationStorage.resetModified()})}getPage(t){if(!Number.isInteger(t)||t<=0||t>this.pagesMapper.pagesNumber)return Promise.reject(new Error("Invalid page request."));const e=t-1,i=this.pagesMapper.getPageId(t)-1,s=this.#fr.get(e);if(s)return s;const n=this.messageHandler.sendWithPromise("GetPage",{pageIndex:i}).then(t=>{if(this.destroyed)throw new Error("Transport destroyed");t.refStr&&this.#br.set(t.refStr,i);const s=new PDFPageProxy(e,t,this,this.pagesMapper,this._params.pdfBug);this.#mr.set(e,s);return s});this.#fr.set(e,n);return n}async getPageIndex(t){if(!isRefProxy(t))throw new Error("Invalid pageIndex request.");const e=await this.messageHandler.sendWithPromise("GetPageIndex",{num:t.num,gen:t.gen}),i=this.pagesMapper.getPageNumber(e+1);if(0===i)throw new Error("GetPageIndex: page has been removed.");return i-1}getAnnotations(t,e){return this.messageHandler.sendWithPromise("GetAnnotations",{pageIndex:this.pagesMapper.getPageId(t+1)-1,intent:e})}getFieldObjects(){return this.#Ar("GetFieldObjects")}hasJSActions(){return this.#Ar("HasJSActions")}getCalculationOrderIds(){return this.messageHandler.sendWithPromise("GetCalculationOrderIds",null)}getDestinations(){return this.messageHandler.sendWithPromise("GetDestinations",null)}getDestination(t){return"string"!=typeof t?Promise.reject(new Error("Invalid destination request.")):this.messageHandler.sendWithPromise("GetDestination",{id:t})}getPageLabels(){return this.messageHandler.sendWithPromise("GetPageLabels",null)}getPageLayout(){return this.messageHandler.sendWithPromise("GetPageLayout",null)}getPageMode(){return this.messageHandler.sendWithPromise("GetPageMode",null)}getViewerPreferences(){return this.messageHandler.sendWithPromise("GetViewerPreferences",null)}getOpenAction(){return this.messageHandler.sendWithPromise("GetOpenAction",null)}getAttachments(){return this.messageHandler.sendWithPromise("GetAttachments",null)}getAnnotationsByType(t,e){return this.messageHandler.sendWithPromise("GetAnnotationsByType",{types:t,pageIndexesToSkip:e})}getDocJSActions(){return this.#Ar("GetDocJSActions")}getPageJSActions(t){return this.messageHandler.sendWithPromise("GetPageJSActions",{pageIndex:this.pagesMapper.getPageId(t+1)-1})}getStructTree(t){return this.messageHandler.sendWithPromise("GetStructTree",{pageIndex:this.pagesMapper.getPageId(t+1)-1})}getOutline(){return this.messageHandler.sendWithPromise("GetOutline",null)}getOptionalContentConfig(t){return this.#Ar("GetOptionalContentConfig").then(e=>new OptionalContentConfig(e,t))}getPermissions(){return this.messageHandler.sendWithPromise("GetPermissions",null)}getMetadata(){const t="GetMetadata";return this.#pr.getOrInsertComputed(t,()=>this.messageHandler.sendWithPromise(t,null).then(t=>({info:t[0],metadata:t[1]?new Metadata(t[1]):null,contentDispositionFilename:this.#ur?.filename??null,contentLength:this.#ur?.contentLength??null,hasStructTree:t[2]})))}getMarkInfo(){return this.messageHandler.sendWithPromise("GetMarkInfo",null)}getRawData(t){return this.messageHandler.sendWithPromise("GetRawData",t)}async startCleanup(t=!1){if(!this.destroyed){await this.messageHandler.sendWithPromise("Cleanup",null);for(const t of this.#mr.values()){if(!t.cleanup())throw new Error(`startCleanup: Page ${t.pageNumber} is currently rendering.`)}this.commonObjs.clear();t||this.fontLoader.clear();this.#pr.clear();this.filterFactory.destroy(!0);TextLayer.cleanup()}}cachedPageNumber(t){if(!isRefProxy(t))return null;const e=0===t.gen?`${t.num}R`:`${t.num}R${t.gen}`,i=this.#br.get(e);if(i>=0){const t=this.pagesMapper.getPageNumber(i+1);if(0!==t)return t}return null}}class RenderTask{_internalRenderTask=null;onContinue=null;onError=null;constructor(t){this._internalRenderTask=t}get promise(){return this._internalRenderTask.capability.promise}cancel(t=0){this._internalRenderTask.cancel(null,t)}get separateAnnots(){const{separateAnnots:t}=this._internalRenderTask.operatorList;if(!t)return!1;const{annotationCanvasMap:e}=this._internalRenderTask;return t.form||t.canvas&&e?.size>0}get imageCoordinates(){return this._internalRenderTask.imageCoordinates||null}}class InternalRenderTask{#vr=null;static#wr=new WeakSet;constructor({callback:t,params:e,objs:i,commonObjs:s,annotationCanvasMap:n,operatorList:r,pageIndex:a,canvasFactory:o,filterFactory:l,useRequestAnimationFrame:h=!1,pdfBug:d=!1,pageColors:c=null,enableHWA:u=!1,operationsFilter:p=null}){this.callback=t;this.params=e;this.objs=i;this.commonObjs=s;this.annotationCanvasMap=n;this.operatorListIdx=null;this.operatorList=r;this._pageIndex=a;this.canvasFactory=o;this.filterFactory=l;this._pdfBug=d;this.pageColors=c;this.running=!1;this.graphicsReadyCallback=null;this.graphicsReady=!1;this._useRequestAnimationFrame=!0===h&&"undefined"!=typeof window;this.cancelled=!1;this.capability=Promise.withResolvers();this.task=new RenderTask(this);this._cancelBound=this.cancel.bind(this);this._continueBound=this._continue.bind(this);this._scheduleNextBound=this._scheduleNext.bind(this);this._nextBound=this._next.bind(this);this._canvas=e.canvas;this._canvasContext=e.canvas?null:e.canvasContext;this._enableHWA=u;this._dependencyTracker=e.dependencyTracker;this._imagesTracker=e.imagesTracker;this._operationsFilter=p}get completed(){return this.capability.promise.catch(function(){})}initializeGraphics({transparency:t=!1,optionalContentConfig:e}){if(this.cancelled)return;if(this._canvas){if(InternalRenderTask.#wr.has(this._canvas))throw new Error("Cannot use the same canvas during multiple render() operations. Use different canvas or ensure previous operations were cancelled or completed.");InternalRenderTask.#wr.add(this._canvas)}if(this._pdfBug&&globalThis.StepperManager?.enabled){this.stepper=globalThis.StepperManager.create(this._pageIndex);this.stepper.init(this.operatorList);this.stepper.nextBreakPoint=this.stepper.getNextBreakPoint()}const{viewport:i,transform:s,background:n,dependencyTracker:r,imagesTracker:a}=this.params,o=this._canvasContext||this._canvas.getContext("2d",{alpha:!1,willReadFrequently:!this._enableHWA});this.gfx=new CanvasGraphics(o,this.commonObjs,this.objs,this.canvasFactory,this.filterFactory,{optionalContentConfig:e},this.annotationCanvasMap,this.pageColors,r,a);this.gfx.beginDrawing({transform:s,viewport:i,transparency:t,background:n});this.operatorListIdx=0;this.graphicsReady=!0;this.graphicsReadyCallback?.()}cancel(t=null,e=0){this.running=!1;this.cancelled=!0;this.gfx?.endDrawing();if(this.#vr){window.cancelAnimationFrame(this.#vr);this.#vr=null}InternalRenderTask.#wr.delete(this._canvas);t||=new RenderingCancelledException(`Rendering cancelled, page ${this._pageIndex+1}`,e);this.callback(t);this.task.onError?.(t)}operatorListChanged(){if(this.graphicsReady){this.gfx.dependencyTracker?.growOperationsCount(this.operatorList.fnArray.length);this.stepper?.updateOperatorList(this.operatorList);this.running||this._continue()}else this.graphicsReadyCallback||=this._continueBound}_continue(){this.running=!0;this.cancelled||(this.task.onContinue?this.task.onContinue(this._scheduleNextBound):this._scheduleNext())}_scheduleNext(){this._useRequestAnimationFrame?this.#vr=window.requestAnimationFrame(()=>{this.#vr=null;this._nextBound().catch(this._cancelBound)}):Promise.resolve().then(this._nextBound).catch(this._cancelBound)}async _next(){if(!this.cancelled){this.operatorListIdx=this.gfx.executeOperatorList(this.operatorList,this.operatorListIdx,this._continueBound,this.stepper,this._operationsFilter);if(this.operatorListIdx===this.operatorList.argsArray.length){this.running=!1;if(this.operatorList.lastChunk){this.gfx.endDrawing();InternalRenderTask.#wr.delete(this._canvas);this.callback()}}}}}const Bt="5.7.284",Rt="7e5b36c2d";class ColorPicker{#xr=null;#Cr=null;#Er;#Tr=null;#Sr=!1;#_r=!1;#a=null;#Pr;#Dr=null;#y=null;static#Mr=null;static get _keyboardManager(){return shadow(this,"_keyboardManager",new KeyboardManager([[["Escape","mac+Escape"],ColorPicker.prototype._hideDropdownFromKeyboard],[[" ","mac+ "],ColorPicker.prototype._colorSelectFromKeyboard],[["ArrowDown","ArrowRight","mac+ArrowDown","mac+ArrowRight"],ColorPicker.prototype._moveToNext],[["ArrowUp","ArrowLeft","mac+ArrowUp","mac+ArrowLeft"],ColorPicker.prototype._moveToPrevious],[["Home","mac+Home"],ColorPicker.prototype._moveToBeginning],[["End","mac+End"],ColorPicker.prototype._moveToEnd]]))}constructor({editor:t=null,uiManager:e=null}){if(t){this.#_r=!1;this.#a=t}else this.#_r=!0;this.#y=t?._uiManager||e;this.#Pr=this.#y._eventBus;this.#Er=t?.color?.toUpperCase()||this.#y?.highlightColors.values().next().value||"#FFFF98";ColorPicker.#Mr||=Object.freeze({blue:"pdfjs-editor-colorpicker-blue",green:"pdfjs-editor-colorpicker-green",pink:"pdfjs-editor-colorpicker-pink",red:"pdfjs-editor-colorpicker-red",yellow:"pdfjs-editor-colorpicker-yellow"})}renderButton(){const t=this.#xr=document.createElement("button");t.className="colorPicker";t.tabIndex="0";t.setAttribute("data-l10n-id","pdfjs-editor-colorpicker-button");t.ariaHasPopup="true";this.#a&&(t.ariaControls=`${this.#a.id}_colorpicker_dropdown`);const e=this.#y._signal;t.addEventListener("click",this.#kr.bind(this),{signal:e});t.addEventListener("keydown",this.#Ir.bind(this),{signal:e});const i=this.#Cr=document.createElement("span");i.className="swatch";i.ariaHidden="true";i.style.backgroundColor=this.#Er;t.append(i);return t}renderMainDropdown(){const t=this.#Tr=this.#Fr();t.ariaOrientation="horizontal";t.ariaLabelledBy="highlightColorPickerLabel";return t}#Fr(){const t=document.createElement("div"),e=this.#y._signal;t.addEventListener("contextmenu",noContextMenu,{signal:e});t.className="dropdown";t.role="listbox";t.ariaMultiSelectable="false";t.ariaOrientation="vertical";t.setAttribute("data-l10n-id","pdfjs-editor-colorpicker-dropdown");this.#a&&(t.id=`${this.#a.id}_colorpicker_dropdown`);for(const[i,s]of this.#y.highlightColors){const n=document.createElement("button");n.tabIndex="0";n.role="option";n.setAttribute("data-color",s);n.title=i;n.setAttribute("data-l10n-id",ColorPicker.#Mr[i]);const r=document.createElement("span");n.append(r);r.className="swatch";r.style.backgroundColor=s;n.ariaSelected=s===this.#Er;n.addEventListener("click",this.#Br.bind(this,s),{signal:e});t.append(n)}t.addEventListener("keydown",this.#Ir.bind(this),{signal:e});return t}#Br(t,e){e.stopPropagation();this.#Pr.dispatch("switchannotationeditorparams",{source:this,type:f.HIGHLIGHT_COLOR,value:t});this.updateColor(t)}_colorSelectFromKeyboard(t){if(t.target===this.#xr){this.#kr(t);return}const e=t.target.getAttribute("data-color");e&&this.#Br(e,t)}_moveToNext(t){this.#Rr?t.target!==this.#xr?t.target.nextSibling?.focus():this.#Tr.firstElementChild?.focus():this.#kr(t)}_moveToPrevious(t){if(t.target!==this.#Tr?.firstElementChild&&t.target!==this.#xr){this.#Rr||this.#kr(t);t.target.previousSibling?.focus()}else this.#Rr&&this._hideDropdownFromKeyboard()}_moveToBeginning(t){this.#Rr?this.#Tr.firstElementChild?.focus():this.#kr(t)}_moveToEnd(t){this.#Rr?this.#Tr.lastElementChild?.focus():this.#kr(t)}#Ir(t){ColorPicker._keyboardManager.exec(this,t)}#kr(t){if(this.#Rr){this.hideDropdown();return}this.#Sr=0===t.detail;if(!this.#Dr){this.#Dr=new AbortController;window.addEventListener("pointerdown",this.#p.bind(this),{signal:this.#y.combinedSignal(this.#Dr)})}this.#xr.ariaExpanded="true";if(this.#Tr){this.#Tr.classList.remove("hidden");return}const e=this.#Tr=this.#Fr();this.#xr.append(e)}#p(t){this.#Tr?.contains(t.target)||this.hideDropdown()}hideDropdown(){this.#Tr?.classList.add("hidden");this.#xr.ariaExpanded="false";this.#Dr?.abort();this.#Dr=null}get#Rr(){return this.#Tr&&!this.#Tr.classList.contains("hidden")}_hideDropdownFromKeyboard(){if(!this.#_r)if(this.#Rr){this.hideDropdown();this.#xr.focus({preventScroll:!0,focusVisible:this.#Sr})}else this.#a?.unselect()}updateColor(t){this.#Cr&&(this.#Cr.style.backgroundColor=t);if(!this.#Tr)return;const e=this.#y.highlightColors.values();for(const i of this.#Tr.children)i.ariaSelected=e.next().value===t.toUpperCase()}destroy(){this.#xr?.remove();this.#xr=null;this.#Cr=null;this.#Tr?.remove();this.#Tr=null}}class BasicColorPicker{#Or=null;#Lr=!1;#a=null;#y=null;static#Mr=null;constructor(t){this.#a=t;this.#y=t._uiManager;BasicColorPicker.#Mr||=Object.freeze({freetext:"pdfjs-editor-color-picker-free-text-input",ink:"pdfjs-editor-color-picker-ink-input"})}renderButton(){if(this.#Or)return this.#Or;const{editorType:t,colorType:e,colorAndOpacityType:i,opacityType:s,color:n,opacity:r}=this.#a,a=this.#Lr=FeatureTest.isAlphaColorInputSupported&&void 0!==s,o=this.#Or=document.createElement("input");o.type="color";if(a){o.setAttribute("alpha","");const t=Util.hexNums[Math.round(255*(r??1))];o.value=(n||"#000000")+t}else o.value=n||"#000000";o.className="basicColorPicker";o.tabIndex=0;o.setAttribute("data-l10n-id",BasicColorPicker.#Mr[t]);o.addEventListener("input",()=>{if(a){const t=getRGBA(o.value);if(!t)return;const[n,r,a,l]=t,h=Util.makeHexColor(n,r,a);if(void 0!==i)this.#y.updateParams(i,{color:h,opacity:l});else{this.#y.updateParams(e,h);this.#y.updateParams(s,l)}}else this.#y.updateParams(e,o.value)},{signal:this.#y._signal});return o}update(t){if(this.#Or)if(this.#Lr){const e=Util.hexNums[Math.round(255*this.#a.opacity)];this.#Or.value=t+e}else this.#Or.value=t}updateOpacity(t){if(!this.#Or||!this.#Lr)return;const e=Util.hexNums[Math.round(255*t)];this.#Or.value=this.#a.color+e}destroy(){this.#Or?.remove();this.#Or=null}hideDropdown(){}}function makeColorComp(t){return Math.floor(255*MathClamp(t,0,1)).toString(16).padStart(2,"0")}function scaleAndClamp(t){return 255*MathClamp(t,0,1)}class ColorConverters{static CMYK_G([t,e,i,s]){return["G",1-Math.min(1,.3*t+.59*i+.11*e+s)]}static G_CMYK([t]){return["CMYK",0,0,0,1-t]}static G_RGB([t]){return["RGB",t,t,t]}static G_rgb([t]){return[t=scaleAndClamp(t),t,t]}static G_HTML([t]){const e=makeColorComp(t);return`#${e}${e}${e}`}static RGB_G([t,e,i]){return["G",.3*t+.59*e+.11*i]}static RGB_rgb(t){return t.map(scaleAndClamp)}static RGB_HTML(t){return`#${t.map(makeColorComp).join("")}`}static T_HTML(){return"#00000000"}static T_rgb(){return[null]}static CMYK_RGB([t,e,i,s]){return["RGB",1-Math.min(1,t+s),1-Math.min(1,i+s),1-Math.min(1,e+s)]}static CMYK_rgb([t,e,i,s]){return[scaleAndClamp(1-Math.min(1,t+s)),scaleAndClamp(1-Math.min(1,i+s)),scaleAndClamp(1-Math.min(1,e+s))]}static CMYK_HTML(t){const e=this.CMYK_RGB(t).slice(1);return this.RGB_HTML(e)}static RGB_CMYK([t,e,i]){const s=1-t,n=1-e,r=1-i;return["CMYK",s,n,r,Math.min(s,n,r)]}}class BaseSVGFactory{create(t,e,i=!1){if(t<=0||e<=0)throw new Error("Invalid SVG dimensions");const s=this._createSVG("svg:svg");s.setAttribute("version","1.1");if(!i){s.setAttribute("width",`${t}px`);s.setAttribute("height",`${e}px`)}s.setAttribute("preserveAspectRatio","none");s.setAttribute("viewBox",`0 0 ${t} ${e}`);return s}createElement(t){if("string"!=typeof t)throw new Error("Invalid SVG element type");return this._createSVG(t)}_createSVG(t){unreachable("Abstract method `_createSVG` called.")}}class DOMSVGFactory extends BaseSVGFactory{_createSVG(t){return document.createElementNS(V,t)}}const Ot=new WeakSet,Lt=60*(new Date).getTimezoneOffset()*1e3;class AnnotationElementFactory{static create(t){switch(t.data.annotationType){case T.LINK:return new LinkAnnotationElement(t);case T.TEXT:return new TextAnnotationElement(t);case T.WIDGET:switch(t.data.fieldType){case"Tx":return new TextWidgetAnnotationElement(t);case"Btn":return t.data.radioButton?new RadioButtonWidgetAnnotationElement(t):t.data.checkBox?new CheckboxWidgetAnnotationElement(t):new PushButtonWidgetAnnotationElement(t);case"Ch":return new ChoiceWidgetAnnotationElement(t);case"Sig":return new SignatureWidgetAnnotationElement(t)}return new WidgetAnnotationElement(t);case T.POPUP:return new PopupAnnotationElement(t);case T.FREETEXT:return new FreeTextAnnotationElement(t);case T.LINE:return new LineAnnotationElement(t);case T.SQUARE:return new SquareAnnotationElement(t);case T.CIRCLE:return new CircleAnnotationElement(t);case T.POLYLINE:return new PolylineAnnotationElement(t);case T.CARET:return new CaretAnnotationElement(t);case T.INK:return new InkAnnotationElement(t);case T.POLYGON:return new PolygonAnnotationElement(t);case T.HIGHLIGHT:return new HighlightAnnotationElement(t);case T.UNDERLINE:return new UnderlineAnnotationElement(t);case T.SQUIGGLY:return new SquigglyAnnotationElement(t);case T.STRIKEOUT:return new StrikeOutAnnotationElement(t);case T.STAMP:return new StampAnnotationElement(t);case T.FILEATTACHMENT:return new FileAttachmentAnnotationElement(t);default:return new AnnotationElement(t)}}}class AnnotationElement{#Nr=null;#Ur=!1;#Hr=null;constructor(t,{isRenderable:e=!1,ignoreBorder:i=!1,createQuadrilaterals:s=!1}={}){this.isRenderable=e;this.data=t.data;this.layer=t.layer;this.linkService=t.linkService;this.downloadManager=t.downloadManager;this.imageResourcesPath=t.imageResourcesPath;this.renderForms=t.renderForms;this.svgFactory=t.svgFactory;this.annotationStorage=t.annotationStorage;this.enableComment=t.enableComment;this.enableScripting=t.enableScripting;this.hasJSActions=t.hasJSActions;this._fieldObjects=t.fieldObjects;this.parent=t.parent;this.hasOwnCommentButton=!1;e&&(this.contentElement=this.container=this._createContainer(i));s&&this._createQuadrilaterals()}static _hasPopupData({contentsObj:t,richText:e}){return!(!t?.str&&!e?.str)}get _isEditable(){return this.data.isEditable}get hasPopupData(){return AnnotationElement._hasPopupData(this.data)||this.enableComment&&!!this.commentText}get commentData(){const{data:t}=this,e=this.annotationStorage?.getEditor(t.id);return e?e.getData():t}get hasCommentButton(){return this.enableComment&&this.hasPopupElement}get commentButtonPosition(){const t=this.annotationStorage?.getEditor(this.data.id);if(t)return t.commentButtonPositionInPage;const{quadPoints:e,inkLists:i,rect:s}=this.data;let n=-1/0,r=-1/0;if(e?.length>=8){for(let t=0;tr){r=e[t+1];n=e[t+2]}else e[t+1]===r&&(n=Math.max(n,e[t+2]));return[n,r]}if(i?.length>=1){for(const t of i)for(let e=0,i=t.length;er){r=t[e+1];n=t[e]}else t[e+1]===r&&(n=Math.max(n,t[e]));if(n!==1/0)return[n,r]}return s?[s[2],s[3]]:null}_normalizePoint(t){const{page:{view:e},viewport:{rawDims:{pageWidth:i,pageHeight:s,pageX:n,pageY:r}}}=this.parent;t[1]=e[3]-t[1]+e[1];t[0]=100*(t[0]-n)/i;t[1]=100*(t[1]-r)/s;return t}get commentText(){const{data:t}=this;return this.annotationStorage.getRawValue(`${g}${t.id}`)?.popup?.contents||t.contentsObj?.str||""}set commentText(t){const{data:e}=this,i={deleted:!t,contents:t||""};this.annotationStorage.updateEditor(e.id,{popup:i})||this.annotationStorage.setValue(`${g}${e.id}`,{id:e.id,annotationType:e.annotationType,page:this.parent.page,popup:i,popupRef:e.popupRef,modificationDate:new Date});t||this.removePopup()}removePopup(){(this.#Hr?.popup||this.popup)?.remove();this.#Hr=this.popup=null}updateEdited(t){if(!this.container)return;t.rect&&(this.#Nr||={rect:this.data.rect.slice(0)});const{rect:e,popup:i}=t;e&&this.#zr(e);let s=this.#Hr?.popup||this.popup;if(!s&&i?.text){this._createPopup(i);s=this.#Hr.popup}if(s){s.updateEdited(t);if(i?.deleted){s.remove();this.#Hr=null;this.popup=null}}}resetEdited(){if(this.#Nr){this.#zr(this.#Nr.rect);this.#Hr?.popup.resetEdited();this.#Nr=null}}#zr(t){const{container:{style:e},data:{rect:i,rotation:s},parent:{viewport:{rawDims:{pageWidth:n,pageHeight:r,pageX:a,pageY:o}}}}=this;i?.splice(0,4,...t);e.left=100*(t[0]-a)/n+"%";e.top=100*(r-t[3]+o)/r+"%";if(0===s){e.width=100*(t[2]-t[0])/n+"%";e.height=100*(t[3]-t[1])/r+"%"}else this.setRotation(s)}_createContainer(t){const{data:e,parent:{page:i,viewport:s}}=this,n=document.createElement("section");n.setAttribute("data-annotation-id",e.id);this instanceof WidgetAnnotationElement||this instanceof LinkAnnotationElement||(n.tabIndex=0);const{style:r}=n;r.zIndex=this.parent.zIndex;this.parent.zIndex+=2;e.alternativeText&&(n.title=e.alternativeText);e.noRotate&&n.classList.add("norotate");if(!e.rect||this instanceof PopupAnnotationElement){const{rotation:t}=e;e.hasOwnCanvas||0===t||this.setRotation(t,n);return n}const{width:a,height:o}=this;if(!t&&e.borderStyle.width>0){r.borderWidth=`${e.borderStyle.width}px`;const t=e.borderStyle.horizontalCornerRadius,i=e.borderStyle.verticalCornerRadius;if(t>0||i>0){const e=`calc(${t}px * var(--total-scale-factor)) / calc(${i}px * var(--total-scale-factor))`;r.borderRadius=e}else if(this instanceof RadioButtonWidgetAnnotationElement){const t=`calc(${a}px * var(--total-scale-factor)) / calc(${o}px * var(--total-scale-factor))`;r.borderRadius=t}switch(e.borderStyle.style){case S:r.borderStyle="solid";break;case _:r.borderStyle="dashed";break;case P:warn("Unimplemented border style: beveled");break;case D:warn("Unimplemented border style: inset");break;case M:r.borderBottomStyle="solid"}const s=e.borderColor||null;if(s){this.#Ur=!0;r.borderColor=Util.makeHexColor(...s)}else r.borderWidth=0}const l=Util.normalizeRect([e.rect[0],i.view[3]-e.rect[1]+i.view[1],e.rect[2],i.view[3]-e.rect[3]+i.view[1]]),{pageWidth:h,pageHeight:d,pageX:c,pageY:u}=s.rawDims;r.left=100*(l[0]-c)/h+"%";r.top=100*(l[1]-u)/d+"%";const{rotation:p}=e;if(e.hasOwnCanvas||0===p){r.width=100*a/h+"%";r.height=100*o/d+"%"}else this.setRotation(p,n);return n}setRotation(t,e=this.container){if(!this.data.rect)return;const{pageWidth:i,pageHeight:s}=this.parent.viewport.rawDims;let{width:n,height:r}=this;t%180!=0&&([n,r]=[r,n]);e.style.width=100*n/i+"%";e.style.height=100*r/s+"%";e.setAttribute("data-main-rotation",(360-t)%360)}get _commonActions(){const setColor=(t,e,i)=>{const s=i.detail[t],n=s[0],r=s.slice(1);i.target.style[e]=ColorConverters[`${n}_HTML`](r);this.annotationStorage.setValue(this.data.id,{[e]:ColorConverters[`${n}_rgb`](r)})};return shadow(this,"_commonActions",{display:t=>{const{display:e}=t.detail,i=e%2==1;this.container.style.visibility=i?"hidden":"visible";this.annotationStorage.setValue(this.data.id,{noView:i,noPrint:1===e||2===e})},print:t=>{this.annotationStorage.setValue(this.data.id,{noPrint:!t.detail.print})},hidden:t=>{const{hidden:e}=t.detail;this.container.style.visibility=e?"hidden":"visible";this.annotationStorage.setValue(this.data.id,{noPrint:e,noView:e})},focus:t=>{setTimeout(()=>t.target.focus({preventScroll:!1}),0)},userName:t=>{t.target.title=t.detail.userName},readonly:t=>{t.target.disabled=t.detail.readonly},required:t=>{this._setRequired(t.target,t.detail.required)},bgColor:t=>{setColor("bgColor","backgroundColor",t)},fillColor:t=>{setColor("fillColor","backgroundColor",t)},fgColor:t=>{setColor("fgColor","color",t)},textColor:t=>{setColor("textColor","color",t)},borderColor:t=>{setColor("borderColor","borderColor",t)},strokeColor:t=>{setColor("strokeColor","borderColor",t)},rotation:t=>{const e=t.detail.rotation;this.setRotation(e);this.annotationStorage.setValue(this.data.id,{rotation:e})}})}_dispatchEventFromSandbox(t,e){const i=this._commonActions;for(const s of Object.keys(e.detail)){const n=t[s]||i[s];n?.(e)}}_setDefaultPropertiesFromJS(t){if(!this.enableScripting)return;const e=this.annotationStorage.getRawValue(this.data.id);if(!e)return;const i=this._commonActions;for(const[s,n]of Object.entries(e)){const r=i[s];if(r){r({detail:{[s]:n},target:t});delete e[s]}}}_createQuadrilaterals(){if(!this.container)return;const{quadPoints:t}=this.data;if(!t)return;const[e,i,s,n]=this.data.rect.map(t=>Math.fround(t));if(8===t.length){const[r,a,o,l]=t.subarray(2,6);if(s===r&&n===a&&e===o&&i===l)return}const{style:r}=this.container;let a;if(this.#Ur){const{borderColor:t,borderWidth:e}=r;r.borderWidth=0;a=["url('data:image/svg+xml;utf8,",'',``];this.container.classList.add("hasBorder")}const o=s-e,l=n-i,{svgFactory:h}=this,d=h.createElement("svg");d.classList.add("quadrilateralsContainer");d.setAttribute("width",0);d.setAttribute("height",0);d.role="none";const c=h.createElement("defs");d.append(c);const u=h.createElement("clipPath"),p=`clippath_${this.data.id}`;u.setAttribute("id",p);u.setAttribute("clipPathUnits","objectBoundingBox");c.append(u);for(let i=2,s=t.length;i`)}if(this.#Ur){a.push("')");r.backgroundImage=a.join("")}this.container.append(d);this.container.style.clipPath=`url(#${p})`}_createPopup(t=null){const{data:e}=this;let i,s;if(t){i={str:t.text};s=t.date}else{i=e.contentsObj;s=e.modificationDate}this.#Hr=new PopupAnnotationElement({data:{color:e.color,titleObj:e.titleObj,modificationDate:s,contentsObj:i,richText:e.richText,parentRect:e.rect,borderStyle:0,id:`popup_${e.id}`,rotation:e.rotation,noRotate:!0},linkService:this.linkService,parent:this.parent,elements:[this]})}get hasPopupElement(){return!!(this.#Hr||this.popup||this.data.popupRef)}get extraPopupElement(){return this.#Hr}render(){unreachable("Abstract method `AnnotationElement.render` called")}_getElementsByName(t,e=null){const i=[];if(this._fieldObjects){const s=this._fieldObjects[t];if(s)for(const{page:t,id:n,exportValues:r}of s){if(-1===t)continue;if(n===e)continue;const s="string"==typeof r?r:null,a=document.querySelector(`[data-element-id="${n}"]`);!a||Ot.has(a)?i.push({id:n,exportValue:s,domElement:a}):warn(`_getElementsByName - element not allowed: ${n}`)}return i}for(const s of document.getElementsByName(t)){const{exportValue:t}=s,n=s.getAttribute("data-element-id");n!==e&&(Ot.has(s)&&i.push({id:n,exportValue:t,domElement:s}))}return i}show(){this.container&&(this.container.hidden=!1);this.popup?.maybeShow()}hide(){this.container&&(this.container.hidden=!0);this.popup?.forceHide()}getElementsToTriggerPopup(){return this.container}addHighlightArea(){const t=this.getElementsToTriggerPopup();if(Array.isArray(t))for(const e of t)e.classList.add("highlightArea");else t.classList.add("highlightArea")}_editOnDoubleClick(){if(!this._isEditable)return;const{annotationEditorType:t,data:{id:e}}=this;this.container.addEventListener("dblclick",()=>{this.linkService.eventBus?.dispatch("switchannotationeditormode",{source:this,mode:t,editId:e,mustEnterInEditMode:!0})})}get width(){return this.data.rect[2]-this.data.rect[0]}get height(){return this.data.rect[3]-this.data.rect[1]}}class EditorAnnotationElement extends AnnotationElement{constructor(t){super(t,{isRenderable:!0,ignoreBorder:!0});this.editor=t.editor}render(){this.container.className="editorAnnotation";return this.container}createOrUpdatePopup(){const{editor:t}=this;t.hasComment&&this._createPopup(t.comment)}get hasCommentButton(){return this.enableComment&&this.editor.hasComment}get commentButtonPosition(){return this.editor.commentButtonPositionInPage}get commentText(){return this.editor.comment.text}set commentText(t){this.editor.comment=t;t||this.removePopup()}get commentData(){return this.editor.getData()}remove(){this.parent.removeAnnotation(this.data.id);this.container.remove();this.container=null;this.removePopup()}}class LinkAnnotationElement extends AnnotationElement{constructor(t,e=null){super(t,{isRenderable:!0,ignoreBorder:!!e?.ignoreBorder,createQuadrilaterals:!0});this.isTooltipOnly=t.data.isTooltipOnly}render(){const{data:t,linkService:e}=this,i=document.createElement("a");i.setAttribute("data-element-id",t.id);let s=!1;if(t.url){e.addLinkAttributes(i,t.url,t.newWindow);s=!0}else if(t.action){this._bindNamedAction(i,t.action,t.overlaidText);s=!0}else if(t.attachment){this.#Gr(i,t.attachment,t.overlaidText,t.attachmentDest);s=!0}else if(t.setOCGState){this.#Wr(i,t.setOCGState,t.overlaidText);s=!0}else if(t.dest){this._bindLink(i,t.dest,t.overlaidText);s=!0}else{if(t.actions&&(t.actions.Action||t.actions["Mouse Up"]||t.actions["Mouse Down"])&&this.enableScripting&&this.hasJSActions){this._bindJSAction(i,t);s=!0}if(t.resetForm){this._bindResetFormAction(i,t.resetForm);s=!0}else if(this.isTooltipOnly&&!s){this._bindLink(i,"");s=!0}}this.container.classList.add("linkAnnotation");if(s){this.contentElement=i;this.container.append(i)}return this.container}#Vr(){this.container.setAttribute("data-internal-link","")}_bindLink(t,e,i=""){t.href=this.linkService.getDestinationHash(e);t.onclick=()=>{e&&this.linkService.goToDestination(e);return!1};(e||""===e)&&this.#Vr();i&&(t.title=i)}_bindNamedAction(t,e,i=""){t.href=this.linkService.getAnchorUrl("");t.onclick=()=>{this.linkService.executeNamedAction(e);return!1};i&&(t.title=i);this.#Vr()}#Gr(t,e,i="",s=null){t.href=this.linkService.getAnchorUrl("");e.description?t.title=e.description:i&&(t.title=i);t.onclick=()=>{this.downloadManager?.openOrDownloadData(e.content,e.filename,s);return!1};this.#Vr()}#Wr(t,e,i=""){t.href=this.linkService.getAnchorUrl("");t.onclick=()=>{this.linkService.executeSetOCGState(e);return!1};i&&(t.title=i);this.#Vr()}_bindJSAction(t,e){t.href=this.linkService.getAnchorUrl("");const i=new Map([["Action","onclick"],["Mouse Up","onmouseup"],["Mouse Down","onmousedown"]]);for(const s of Object.keys(e.actions)){const n=i.get(s);n&&(t[n]=()=>{this.linkService.eventBus?.dispatch("dispatcheventinsandbox",{source:this,detail:{id:e.id,name:s}});return!1})}e.overlaidText&&(t.title=e.overlaidText);t.onclick||(t.onclick=()=>!1);this.#Vr()}_bindResetFormAction(t,e){const i=t.onclick;i||(t.href=this.linkService.getAnchorUrl(""));this.#Vr();if(this._fieldObjects)t.onclick=()=>{i?.();const{fields:t,refs:s,include:n}=e,r=[];if(0!==t.length||0!==s.length){const e=new Set(s);for(const i of t){const t=this._fieldObjects[i]||[];for(const{id:i}of t)e.add(i)}for(const t of Object.values(this._fieldObjects))for(const i of t)e.has(i.id)===n&&r.push(i)}else for(const t of Object.values(this._fieldObjects))r.push(...t);const a=this.annotationStorage,o=[];for(const t of r){const{id:e}=t;o.push(e);switch(t.type){case"text":{const i=t.defaultValue||"";a.setValue(e,{value:i});break}case"checkbox":case"radiobutton":{const i=t.defaultValue===t.exportValues;a.setValue(e,{value:i});break}case"combobox":case"listbox":{const i=t.defaultValue||"";a.setValue(e,{value:i});break}default:continue}const i=document.querySelector(`[data-element-id="${e}"]`);i&&(Ot.has(i)?i.dispatchEvent(new Event("resetform")):warn(`_bindResetFormAction - element not allowed: ${e}`))}this.enableScripting&&this.linkService.eventBus?.dispatch("dispatcheventinsandbox",{source:this,detail:{id:"app",ids:o,name:"ResetForm"}});return!1};else{warn('_bindResetFormAction - "resetForm" action not supported, ensure that the `fieldObjects` parameter is provided.');i||(t.onclick=()=>!1)}}}class TextAnnotationElement extends AnnotationElement{constructor(t){super(t,{isRenderable:!0})}render(){this.container.classList.add("textAnnotation");const t=document.createElement("img");t.src=this.imageResourcesPath+"annotation-"+this.data.name.toLowerCase()+".svg";t.setAttribute("data-l10n-id","pdfjs-text-annotation-type");t.setAttribute("data-l10n-args",JSON.stringify({type:this.data.name}));if(!this.data.popupRef&&this.hasPopupData){this.hasOwnCommentButton=!0;this._createPopup()}this.container.append(t);return this.container}}class WidgetAnnotationElement extends AnnotationElement{render(){return this.container}showElementAndHideCanvas(t){if(this.data.hasOwnCanvas){"CANVAS"===t.previousSibling?.nodeName&&(t.previousSibling.hidden=!0);t.hidden=!1}}_getKeyModifier(t){return FeatureTest.platform.isMac?t.metaKey:t.ctrlKey}_setEventListener(t,e,i,s,n){i.includes("mouse")?t.addEventListener(i,t=>{this.linkService.eventBus?.dispatch("dispatcheventinsandbox",{source:this,detail:{id:this.data.id,name:s,value:n(t),shift:t.shiftKey,modifier:this._getKeyModifier(t)}})}):t.addEventListener(i,t=>{if("blur"===i){if(!e.focused||!t.relatedTarget)return;e.focused=!1}else if("focus"===i){if(e.focused)return;e.focused=!0}n&&this.linkService.eventBus?.dispatch("dispatcheventinsandbox",{source:this,detail:{id:this.data.id,name:s,value:n(t)}})})}_setEventListeners(t,e,i,s){for(const[n,r]of i)if("Action"===r||this.data.actions?.[r]){"Focus"!==r&&"Blur"!==r||(e||={focused:!1});this._setEventListener(t,e,n,r,s);"Focus"!==r||this.data.actions?.Blur?"Blur"!==r||this.data.actions?.Focus||this._setEventListener(t,e,"focus","Focus",null):this._setEventListener(t,e,"blur","Blur",null)}}_setBackgroundColor(t){const e=this.data.backgroundColor||null;t.style.backgroundColor=null===e?"transparent":Util.makeHexColor(...e)}_setTextStyle(t){const e=["left","center","right"],{fontColor:i}=this.data.defaultAppearanceData,s=this.data.defaultAppearanceData.fontSize||9,r=t.style;let a;const roundToOneDecimal=t=>Math.round(10*t)/10;if(this.data.multiLine){const t=Math.abs(this.data.rect[3]-this.data.rect[1]-2),e=t/(Math.round(t/(n*s))||1);a=Math.min(s,roundToOneDecimal(e/n))}else{const t=Math.abs(this.data.rect[3]-this.data.rect[1]-2);a=Math.min(s,roundToOneDecimal(t/n))}r.fontSize=`calc(${a}px * var(--total-scale-factor))`;r.color=Util.makeHexColor(...i);null!==this.data.textAlignment&&(r.textAlign=e[this.data.textAlignment])}_setRequired(t,e){e?t.setAttribute("required",!0):t.removeAttribute("required");t.setAttribute("aria-required",e)}}class TextWidgetAnnotationElement extends WidgetAnnotationElement{constructor(t){super(t,{isRenderable:t.renderForms||t.data.hasOwnCanvas||!t.data.hasAppearance&&!!t.data.fieldValue})}setPropertyOnSiblings(t,e,i,s){const n=this.annotationStorage;for(const r of this._getElementsByName(t.name,t.id)){r.domElement&&(r.domElement[e]=i);n.setValue(r.id,{[s]:i})}}render(){const t=this.annotationStorage,e=this.data.id;this.container.classList.add("textWidgetAnnotation");let i=null;if(this.renderForms){const s=t.getValue(e,{value:this.data.fieldValue});let n=s.value||"";const r=t.getValue(e,{charLimit:this.data.maxLen}).charLimit;r&&n.length>r&&(n=n.slice(0,r));let a=s.formattedValue||this.data.textContent?.join("\n")||null;a&&this.data.comb&&(a=a.replaceAll(/\s+/g,""));const o={userValue:n,formattedValue:a,lastCommittedValue:null,commitKey:1,focused:!1};if(this.data.multiLine){i=document.createElement("textarea");i.textContent=a??n;this.data.doNotScroll&&(i.style.overflowY="hidden")}else{i=document.createElement("input");i.type=this.data.password?"password":"text";i.setAttribute("value",a??n);this.data.doNotScroll&&(i.style.overflowX="hidden")}this.data.hasOwnCanvas&&(i.hidden=!0);Ot.add(i);this.contentElement=i;i.setAttribute("data-element-id",e);i.disabled=this.data.readOnly;i.name=this.data.fieldName;i.tabIndex=0;const{datetimeFormat:l,datetimeType:h,timeStep:d}=this.data,c=!!h&&this.enableScripting;l&&(i.title=l);this._setRequired(i,this.data.required);r&&(i.maxLength=r);i.addEventListener("input",s=>{t.setValue(e,{value:s.target.value});this.setPropertyOnSiblings(i,"value",s.target.value,"value");o.formattedValue=null});i.addEventListener("resetform",t=>{const e=this.data.defaultFieldValue??"";i.value=o.userValue=e;o.formattedValue=null});let blurListener=t=>{const{formattedValue:e}=o;null!=e&&(t.target.value=e);t.target.scrollLeft=0};if(this.enableScripting&&this.hasJSActions){i.addEventListener("focus",t=>{if(o.focused)return;const{target:e}=t;if(c){e.type=h;d&&(e.step=d)}if(o.userValue){const t=o.userValue;if(c)if("time"===h){const i=new Date(t),s=[i.getHours(),i.getMinutes(),i.getSeconds()];e.value=s.map(t=>t.toString().padStart(2,"0")).join(":")}else e.value=new Date(t-Lt).toISOString().split("date"===h?"T":".",1)[0];else e.value=t}o.lastCommittedValue=e.value;o.commitKey=1;this.data.actions?.Focus||(o.focused=!0)});i.addEventListener("updatefromsandbox",i=>{this.showElementAndHideCanvas(i.target);const s={value(i){o.userValue=i.detail.value??"";c||t.setValue(e,{value:o.userValue.toString()});i.target.value=o.userValue},formattedValue(i){const{formattedValue:s}=i.detail;o.formattedValue=s;null!=s&&i.target!==document.activeElement&&(i.target.value=s);const n={formattedValue:s};c&&(n.value=s);t.setValue(e,n)},selRange(t){t.target.setSelectionRange(...t.detail.selRange)},charLimit:i=>{const{charLimit:s}=i.detail,{target:n}=i;if(0===s){n.removeAttribute("maxLength");return}n.setAttribute("maxLength",s);let r=o.userValue;if(r&&!(r.length<=s)){r=r.slice(0,s);n.value=o.userValue=r;t.setValue(e,{value:r});this.linkService.eventBus?.dispatch("dispatcheventinsandbox",{source:this,detail:{id:e,name:"Keystroke",value:r,willCommit:!0,commitKey:1,selStart:n.selectionStart,selEnd:n.selectionEnd}})}}};this._dispatchEventFromSandbox(s,i)});i.addEventListener("keydown",t=>{o.commitKey=1;let i=-1;"Escape"===t.key?i=0:"Enter"!==t.key||this.data.multiLine?"Tab"===t.key&&(o.commitKey=3):i=2;if(-1===i)return;const{value:s}=t.target;if(o.lastCommittedValue!==s){o.lastCommittedValue=s;o.userValue=s;this.linkService.eventBus?.dispatch("dispatcheventinsandbox",{source:this,detail:{id:e,name:"Keystroke",value:s,willCommit:!0,commitKey:i,selStart:t.target.selectionStart,selEnd:t.target.selectionEnd}})}});const s=blurListener;blurListener=null;i.addEventListener("blur",t=>{if(!o.focused||!t.relatedTarget)return;this.data.actions?.Blur||(o.focused=!1);const{target:i}=t;let{value:n}=i;if(c){if(n&&"time"===h){const t=n.split(":").map(t=>parseInt(t,10));n=new Date(2e3,0,1,t[0],t[1],t[2]||0).valueOf();i.step=""}else{n.includes("T")||(n=`${n}T00:00`);n=new Date(n).valueOf()}i.type="text"}o.userValue=n;o.lastCommittedValue!==n&&this.linkService.eventBus?.dispatch("dispatcheventinsandbox",{source:this,detail:{id:e,name:"Keystroke",value:n,willCommit:!0,commitKey:o.commitKey,selStart:t.target.selectionStart,selEnd:t.target.selectionEnd}});s(t)});this.data.actions?.Keystroke&&i.addEventListener("beforeinput",t=>{o.lastCommittedValue=null;const{data:i,target:s}=t,{value:n,selectionStart:r,selectionEnd:a}=s;let l=r,h=a;switch(t.inputType){case"deleteWordBackward":{const t=n.substring(0,r).match(/\w*[^\w]*$/);t&&(l-=t[0].length);break}case"deleteWordForward":{const t=n.substring(r).match(/^[^\w]*\w*/);t&&(h+=t[0].length);break}case"deleteContentBackward":r===a&&(l-=1);break;case"deleteContentForward":r===a&&(h+=1)}t.preventDefault();this.linkService.eventBus?.dispatch("dispatcheventinsandbox",{source:this,detail:{id:e,name:"Keystroke",value:n,change:i||"",willCommit:!1,selStart:l,selEnd:h}})});this._setEventListeners(i,o,[["focus","Focus"],["blur","Blur"],["mousedown","Mouse Down"],["mouseenter","Mouse Enter"],["mouseleave","Mouse Exit"],["mouseup","Mouse Up"]],t=>t.target.value)}blurListener&&i.addEventListener("blur",blurListener);if(this.data.comb){const t=(this.data.rect[2]-this.data.rect[0])/r;i.classList.add("comb");i.style.letterSpacing=`calc(${t}px * var(--total-scale-factor) - 1ch)`}}else{i=document.createElement("div");i.textContent=this.data.fieldValue;i.style.verticalAlign="middle";i.style.display="table-cell";this.data.hasOwnCanvas&&(i.hidden=!0)}this._setTextStyle(i);this._setBackgroundColor(i);this._setDefaultPropertiesFromJS(i);this.container.append(i);return this.container}}class SignatureWidgetAnnotationElement extends WidgetAnnotationElement{constructor(t){super(t,{isRenderable:!!t.data.hasOwnCanvas})}}class CheckboxWidgetAnnotationElement extends WidgetAnnotationElement{constructor(t){super(t,{isRenderable:t.renderForms})}render(){const t=this.annotationStorage,e=this.data,i=e.id;let s=t.getValue(i,{value:e.exportValue===e.fieldValue}).value;if("string"==typeof s){s="Off"!==s;t.setValue(i,{value:s})}this.container.classList.add("buttonWidgetAnnotation","checkBox");const n=document.createElement("input");Ot.add(n);n.setAttribute("data-element-id",i);n.disabled=e.readOnly;this._setRequired(n,this.data.required);n.type="checkbox";n.name=e.fieldName;s&&n.setAttribute("checked",!0);n.setAttribute("exportValue",e.exportValue);n.tabIndex=0;n.addEventListener("change",s=>{const{name:n,checked:r}=s.target;for(const s of this._getElementsByName(n,i)){const i=r&&s.exportValue===e.exportValue;s.domElement&&(s.domElement.checked=i);t.setValue(s.id,{value:i})}t.setValue(i,{value:r})});n.addEventListener("resetform",t=>{const i=e.defaultFieldValue||"Off";t.target.checked=i===e.exportValue});if(this.enableScripting&&this.hasJSActions){n.addEventListener("updatefromsandbox",e=>{const s={value(e){e.target.checked="Off"!==e.detail.value;t.setValue(i,{value:e.target.checked})}};this._dispatchEventFromSandbox(s,e)});this._setEventListeners(n,null,[["change","Validate"],["change","Action"],["focus","Focus"],["blur","Blur"],["mousedown","Mouse Down"],["mouseenter","Mouse Enter"],["mouseleave","Mouse Exit"],["mouseup","Mouse Up"]],t=>t.target.checked)}this._setBackgroundColor(n);this._setDefaultPropertiesFromJS(n);this.container.append(n);return this.container}}class RadioButtonWidgetAnnotationElement extends WidgetAnnotationElement{constructor(t){super(t,{isRenderable:t.renderForms})}render(){this.container.classList.add("buttonWidgetAnnotation","radioButton");const t=this.annotationStorage,e=this.data,i=e.id;let s=t.getValue(i,{value:e.fieldValue===e.buttonValue}).value;if("string"==typeof s){s=s!==e.buttonValue;t.setValue(i,{value:s})}if(s)for(const s of this._getElementsByName(e.fieldName,i))t.setValue(s.id,{value:!1});const n=document.createElement("input");Ot.add(n);n.setAttribute("data-element-id",i);n.disabled=e.readOnly;this._setRequired(n,this.data.required);n.type="radio";n.name=e.fieldName;s&&n.setAttribute("checked",!0);n.tabIndex=0;n.addEventListener("change",e=>{const{name:s,checked:n}=e.target;for(const e of this._getElementsByName(s,i))t.setValue(e.id,{value:!1});t.setValue(i,{value:n})});n.addEventListener("resetform",t=>{const i=e.defaultFieldValue;t.target.checked=null!=i&&i===e.buttonValue});if(this.enableScripting&&this.hasJSActions){const s=e.buttonValue;n.addEventListener("updatefromsandbox",e=>{const n={value:e=>{const n=s===e.detail.value;for(const s of this._getElementsByName(e.target.name)){const e=n&&s.id===i;s.domElement&&(s.domElement.checked=e);t.setValue(s.id,{value:e})}}};this._dispatchEventFromSandbox(n,e)});this._setEventListeners(n,null,[["change","Validate"],["change","Action"],["focus","Focus"],["blur","Blur"],["mousedown","Mouse Down"],["mouseenter","Mouse Enter"],["mouseleave","Mouse Exit"],["mouseup","Mouse Up"]],t=>t.target.checked)}this._setBackgroundColor(n);this._setDefaultPropertiesFromJS(n);this.container.append(n);return this.container}}class PushButtonWidgetAnnotationElement extends LinkAnnotationElement{constructor(t){super(t,{ignoreBorder:t.data.hasAppearance})}render(){const t=super.render();t.classList.add("buttonWidgetAnnotation","pushButton");const e=t.lastChild;if(this.enableScripting&&this.hasJSActions&&e){this._setDefaultPropertiesFromJS(e);e.addEventListener("updatefromsandbox",t=>{this._dispatchEventFromSandbox({},t)})}return t}}class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement{constructor(t){super(t,{isRenderable:t.renderForms})}render(){this.container.classList.add("choiceWidgetAnnotation");const t=this.annotationStorage,e=this.data.id,i=t.getValue(e,{value:this.data.fieldValue}),s=document.createElement("select");Ot.add(s);s.setAttribute("data-element-id",e);s.disabled=this.data.readOnly;this._setRequired(s,this.data.required);s.name=this.data.fieldName;s.tabIndex=0;let n=this.data.combo&&this.data.options.length>0;if(!this.data.combo){s.size=this.data.options.length;this.data.multiSelect&&(s.multiple=!0)}s.addEventListener("resetform",t=>{const e=this.data.defaultFieldValue;for(const t of s.options)t.selected=t.value===e});const fixDisplayValue=(t,e)=>{const i=e.replaceAll(" "," ");t.textContent=i;i!==e&&t.setAttribute("display-value",e)};for(const t of this.data.options){const e=document.createElement("option");fixDisplayValue(e,t.displayValue);e.value=t.exportValue;if(i.value.includes(t.exportValue)){e.setAttribute("selected",!0);n=!1}s.append(e)}let r=null;if(n){const t=document.createElement("option");t.value=" ";t.setAttribute("hidden",!0);t.setAttribute("selected",!0);s.prepend(t);r=()=>{t.remove();s.removeEventListener("input",r);r=null};s.addEventListener("input",r)}const getValue=t=>{const e=t?"value":"textContent",{options:i,multiple:n}=s;return n?Array.prototype.filter.call(i,t=>t.selected).map(t=>t[e]):-1===i.selectedIndex?null:i[i.selectedIndex][e]};let a=getValue(!1);const getItems=t=>{const e=t.target.options;return Array.prototype.map.call(e,t=>({displayValue:t.getAttribute("display-value")||t.textContent,exportValue:t.value}))};if(this.enableScripting&&this.hasJSActions){s.addEventListener("updatefromsandbox",i=>{const n={value(i){r?.();const n=i.detail.value,o=new Set(Array.isArray(n)?n:[n]);for(const t of s.options)t.selected=o.has(t.value);t.setValue(e,{value:getValue(!0)});a=getValue(!1)},multipleSelection(t){s.multiple=!0},remove(i){const n=s.options,r=i.detail.remove;n[r].selected=!1;s.remove(r);if(n.length>0){-1===Array.prototype.findIndex.call(n,t=>t.selected)&&(n[0].selected=!0)}t.setValue(e,{value:getValue(!0),items:getItems(i)});a=getValue(!1)},clear(i){for(;0!==s.length;)s.remove(0);t.setValue(e,{value:null,items:[]});a=getValue(!1)},insert(i){const{index:n,displayValue:r,exportValue:o}=i.detail.insert,l=s.children[n],h=document.createElement("option");fixDisplayValue(h,r);h.value=o;l?l.before(h):s.append(h);t.setValue(e,{value:getValue(!0),items:getItems(i)});a=getValue(!1)},items(i){const{items:n}=i.detail;for(;0!==s.length;)s.remove(0);for(const t of n){const{displayValue:e,exportValue:i}=t,n=document.createElement("option");fixDisplayValue(n,e);n.value=i;s.append(n)}s.options.length>0&&(s.options[0].selected=!0);t.setValue(e,{value:getValue(!0),items:getItems(i)});a=getValue(!1)},indices(i){const s=new Set(i.detail.indices);for(const t of i.target.options)t.selected=s.has(t.index);t.setValue(e,{value:getValue(!0)});a=getValue(!1)},editable(t){t.target.disabled=!t.detail.editable}};this._dispatchEventFromSandbox(n,i)});s.addEventListener("input",i=>{const s=getValue(!0),n=getValue(!1);t.setValue(e,{value:s});i.preventDefault();this.linkService.eventBus?.dispatch("dispatcheventinsandbox",{source:this,detail:{id:e,name:"Keystroke",value:a,change:n,changeEx:s,willCommit:!1,commitKey:1,keyDown:!1}})});this._setEventListeners(s,null,[["focus","Focus"],["blur","Blur"],["mousedown","Mouse Down"],["mouseenter","Mouse Enter"],["mouseleave","Mouse Exit"],["mouseup","Mouse Up"],["input","Action"],["input","Validate"]],t=>t.target.value)}else s.addEventListener("input",function(i){t.setValue(e,{value:getValue(!0)})});this.data.combo&&this._setTextStyle(s);this._setBackgroundColor(s);this._setDefaultPropertiesFromJS(s);this.container.append(s);return this.container}}class PopupAnnotationElement extends AnnotationElement{constructor(t){const{data:e,elements:i,parent:s}=t,n=!!s._commentManager;super(t,{isRenderable:!n&&AnnotationElement._hasPopupData(e)});this.elements=i;if(n&&AnnotationElement._hasPopupData(e)){const t=this.popup=this.#jr();for(const e of i)e.popup=t}else this.popup=null}#jr(){return new PopupElement({container:this.container,color:this.data.color,titleObj:this.data.titleObj,modificationDate:this.data.modificationDate||this.data.creationDate,contentsObj:this.data.contentsObj,richText:this.data.richText,rect:this.data.rect,parentRect:this.data.parentRect||null,parent:this.parent,elements:this.elements,open:this.data.open,commentManager:this.parent._commentManager})}render(){const{container:t}=this;t.classList.add("popupAnnotation");t.role="comment";const e=this.popup=this.#jr(),i=[];for(const t of this.elements){t.popup=e;t.container.ariaHasPopup="dialog";i.push(t.data.id);t.addHighlightArea()}this.container.setAttribute("aria-controls",i.map(t=>`${W}${t}`).join(","));return this.container}}class PopupElement{#j=null;#$r=this.#Ir.bind(this);#Xr=this.#qr.bind(this);#Kr=this.#Yr.bind(this);#Qr=this.#Jr.bind(this);#Zr=null;#_t=null;#ta=null;#ea=null;#ia=null;#sa=null;#na=null;#ra=!1;#aa=null;#oa=null;#F=null;#la=null;#ha=null;#ve=null;#da=null;#fe=null;#ca=null;#Nr=null;#ua=!1;#pa=null;#ga=null;constructor({container:t,color:e,elements:i,titleObj:s,modificationDate:n,contentsObj:r,richText:a,parent:o,rect:l,parentRect:h,open:d,commentManager:c=null}){this.#_t=t;this.#ca=s;this.#ta=r;this.#fe=a;this.#sa=o;this.#Zr=e;this.#da=l;this.#na=h;this.#ia=i;this.#j=c;this.#pa=i[0];this.#ea=PDFDateString.toDateObject(n);this.trigger=i.flatMap(t=>t.getElementsToTriggerPopup());if(!c){this.#ma();this.#_t.hidden=!0;d&&this.#Jr()}}#ma(){if(this.#oa)return;this.#oa=new AbortController;const{signal:t}=this.#oa;for(const e of this.trigger){e.addEventListener("click",this.#Qr,{signal:t});e.addEventListener("pointerenter",this.#Kr,{signal:t});e.addEventListener("pointerleave",this.#Xr,{signal:t});e.classList.add("popupTriggerArea")}for(const e of this.#ia)e.container?.addEventListener("keydown",this.#$r,{signal:t})}#fa(){const t=this.#ia.find(t=>t.hasCommentButton);t&&(this.#ha=t._normalizePoint(t.commentButtonPosition))}renderCommentButton(){if(this.#la){this.#la.parentNode||this.#pa.container.after(this.#la);return}this.#ha||this.#fa();if(!this.#ha)return;const{signal:t}=this.#oa=new AbortController,e=this.#pa.hasOwnCommentButton,togglePopup=()=>{this.#j.toggleCommentPopup(this,!0,void 0,!e)},showPopup=()=>{this.#j.toggleCommentPopup(this,!1,!0,!e)},hidePopup=()=>{this.#j.toggleCommentPopup(this,!1,!1)};if(e){this.#la=this.#pa.container;for(const e of this.trigger){e.ariaHasPopup="dialog";e.ariaControls="commentPopup";e.addEventListener("keydown",this.#$r,{signal:t});e.addEventListener("click",togglePopup,{signal:t});e.addEventListener("pointerenter",showPopup,{signal:t});e.addEventListener("pointerleave",hidePopup,{signal:t});e.classList.add("popupTriggerArea")}}else{const e=this.#la=document.createElement("button");e.className="annotationCommentButton";const i=this.#pa.container;e.style.zIndex=parseInt(i.style.zIndex,10)+1;e.tabIndex=0;e.ariaHasPopup="dialog";e.ariaControls="commentPopup";e.setAttribute("data-l10n-id","pdfjs-show-comment-button");this.#ba();this.#ya();e.addEventListener("keydown",this.#$r,{signal:t});e.addEventListener("click",togglePopup,{signal:t});e.addEventListener("pointerenter",showPopup,{signal:t});e.addEventListener("pointerleave",hidePopup,{signal:t});i.after(e)}}#ya(){if(this.#pa.extraPopupElement&&!this.#pa.editor)return;this.#la||this.renderCommentButton();const[t,e]=this.#ha,{style:i}=this.#la;i.left=`calc(${t}%)`;i.top=`calc(${e}% - var(--comment-button-dim))`}#ba(){if(!this.#pa.extraPopupElement){this.#la||this.renderCommentButton();this.#la.style.backgroundColor=this.commentButtonColor||""}}get commentButtonColor(){const{color:t,opacity:e}=this.#pa.commentData;return t?this.#sa._commentManager.makeCommentColor(t,e):null}focusCommentButton(){setTimeout(()=>{this.#la?.focus()},0)}getData(){const{richText:t,color:e,opacity:i,creationDate:s,modificationDate:n}=this.#pa.commentData;return{contentsObj:{str:this.comment},richText:t,color:e,opacity:i,creationDate:s,modificationDate:n}}get elementBeforePopup(){return this.#la}get comment(){this.#ga||=this.#pa.commentText;return this.#ga}set comment(t){t!==this.comment&&(this.#pa.commentText=this.#ga=t)}focus(){this.#pa.container?.focus()}get parentBoundingClientRect(){return this.#pa.layer.getBoundingClientRect()}setCommentButtonStates({selected:t,hasPopup:e}){if(this.#la){this.#la.classList.toggle("selected",t);this.#la.ariaExpanded=e}}setSelectedCommentButton(t){this.#la.classList.toggle("selected",t)}get commentPopupPosition(){if(this.#ve)return this.#ve;const{x:t,y:e,height:i}=this.#la.getBoundingClientRect(),{x:s,y:n,width:r,height:a}=this.#pa.layer.getBoundingClientRect();return[(t-s)/r,(e+i-n)/a]}set commentPopupPosition(t){this.#ve=t}hasDefaultPopupPosition(){return null===this.#ve}get commentButtonPosition(){return this.#ha}get commentButtonWidth(){return this.#la.getBoundingClientRect().width/this.parentBoundingClientRect.width}editComment(t){const[e,i]=this.#ve||this.commentButtonPosition.map(t=>t/100),s=this.parentBoundingClientRect,{x:n,y:r,width:a,height:o}=s;this.#j.showDialog(null,this,n+e*a,r+i*o,{...t,parentDimensions:s})}render(){if(this.#aa)return;const t=this.#aa=document.createElement("div");t.className="popup";if(this.#Zr){const e=t.style.outlineColor=Util.makeHexColor(...this.#Zr);t.style.backgroundColor=`color-mix(in srgb, ${e} 30%, white)`}const e=document.createElement("span");e.className="header";if(this.#ca?.str){const t=document.createElement("span");t.className="title";e.append(t);({dir:t.dir,str:t.textContent}=this.#ca)}t.append(e);if(this.#ea){const t=document.createElement("time");t.className="popupDate";t.setAttribute("data-l10n-id","pdfjs-annotation-date-time-string");t.setAttribute("data-l10n-args",JSON.stringify({dateObj:this.#ea.valueOf()}));t.dateTime=this.#ea.toISOString();e.append(t)}renderRichText({html:this.#Aa||this.#ta.str,dir:this.#ta?.dir,className:"popupContent"},t);this.#_t.append(t)}get#Aa(){const t=this.#fe,e=this.#ta;return!t?.str||e?.str&&e.str!==t.str?null:this.#fe.html||null}get#va(){return this.#Aa?.attributes?.style?.fontSize||0}get#wa(){return this.#Aa?.attributes?.style?.color||null}#xa(t){const e=[],i={str:t,html:{name:"div",attributes:{dir:"auto"},children:[{name:"p",children:e}]}},s={style:{color:this.#wa,fontSize:this.#va?`calc(${this.#va}px * var(--total-scale-factor))`:""}};for(const i of t.split("\n"))e.push({name:"span",value:i,attributes:s});return i}#Ir(t){t.altKey||t.shiftKey||t.ctrlKey||t.metaKey||("Enter"===t.key||"Escape"===t.key&&this.#ra)&&this.#Jr()}updateEdited({rect:t,popup:e,deleted:i}){if(this.#j){if(i){this.remove();this.#ga=null}else if(e)if(e.deleted)this.remove();else{this.#ba();this.#ga=e.text}if(t){this.#ha=null;this.#fa();this.#ya()}}else if(i||e?.deleted)this.remove();else{this.#ma();this.#Nr||={contentsObj:this.#ta,richText:this.#fe};t&&(this.#F=null);if(e&&e.text){this.#fe=this.#xa(e.text);this.#ea=PDFDateString.toDateObject(e.date);this.#ta=null}this.#aa?.remove();this.#aa=null}}resetEdited(){if(this.#Nr){({contentsObj:this.#ta,richText:this.#fe}=this.#Nr);this.#Nr=null;this.#aa?.remove();this.#aa=null;this.#F=null}}remove(){this.#oa?.abort();this.#oa=null;this.#aa?.remove();this.#aa=null;this.#ua=!1;this.#ra=!1;this.#la?.remove();this.#la=null;if(this.trigger)for(const t of this.trigger)t.classList.remove("popupTriggerArea")}#Ca(){if(null!==this.#F)return;const{page:{view:t},viewport:{rawDims:{pageWidth:e,pageHeight:i,pageX:s,pageY:n}}}=this.#sa;let r=!!this.#na,a=r?this.#na:this.#da;for(const t of this.#ia)if(!a||null!==Util.intersect(t.data.rect,a)){a=t.data.rect;r=!0;break}const o=Util.normalizeRect([a[0],t[3]-a[1]+t[1],a[2],t[3]-a[3]+t[1]]),l=r?a[2]-a[0]+5:0,h=o[0]+l,d=o[1];this.#F=[100*(h-s)/e,100*(d-n)/i];const{style:c}=this.#_t;c.left=`${this.#F[0]}%`;c.top=`${this.#F[1]}%`}#Jr(){if(this.#j)this.#j.toggleCommentPopup(this,!1);else{this.#ra=!this.#ra;if(this.#ra){this.#Yr();this.#_t.addEventListener("click",this.#Qr);this.#_t.addEventListener("keydown",this.#$r)}else{this.#qr();this.#_t.removeEventListener("click",this.#Qr);this.#_t.removeEventListener("keydown",this.#$r)}}}#Yr(){this.#aa||this.render();if(this.isVisible)this.#ra&&this.#_t.classList.add("focused");else{this.#Ca();this.#_t.hidden=!1;this.#_t.style.zIndex=parseInt(this.#_t.style.zIndex,10)+1e3}}#qr(){this.#_t.classList.remove("focused");if(!this.#ra&&this.isVisible){this.#_t.hidden=!0;this.#_t.style.zIndex=parseInt(this.#_t.style.zIndex,10)-1e3}}forceHide(){this.#ua=this.isVisible;this.#ua&&(this.#_t.hidden=!0)}maybeShow(){if(!this.#j){this.#ma();if(this.#ua){this.#aa||this.#Yr();this.#ua=!1;this.#_t.hidden=!1}}}get isVisible(){return!this.#j&&!1===this.#_t.hidden}}class FreeTextAnnotationElement extends AnnotationElement{constructor(t){super(t,{isRenderable:!0,ignoreBorder:!0});this.textContent=t.data.textContent;this.textPosition=t.data.textPosition;this.annotationEditorType=m.FREETEXT}render(){this.container.classList.add("freeTextAnnotation");if(this.textContent){const t=this.contentElement=document.createElement("div");t.classList.add("annotationTextContent");t.setAttribute("role","comment");for(const e of this.textContent){const i=document.createElement("span");i.textContent=e;t.append(i)}this.container.append(t)}if(!this.data.popupRef&&this.hasPopupData){this.hasOwnCommentButton=!0;this._createPopup()}this._editOnDoubleClick();return this.container}}class LineAnnotationElement extends AnnotationElement{#Ea=null;constructor(t){super(t,{isRenderable:!0,ignoreBorder:!0})}render(){this.container.classList.add("lineAnnotation");const{data:t,width:e,height:i}=this,s=this.svgFactory.create(e,i,!0),n=this.#Ea=this.svgFactory.createElement("svg:line");n.setAttribute("x1",t.rect[2]-t.lineCoordinates[0]);n.setAttribute("y1",t.rect[3]-t.lineCoordinates[1]);n.setAttribute("x2",t.rect[2]-t.lineCoordinates[2]);n.setAttribute("y2",t.rect[3]-t.lineCoordinates[3]);n.setAttribute("stroke-width",t.borderStyle.width||1);n.setAttribute("stroke","transparent");n.setAttribute("fill","transparent");s.append(n);this.container.append(s);if(!t.popupRef&&this.hasPopupData){this.hasOwnCommentButton=!0;this._createPopup()}return this.container}getElementsToTriggerPopup(){return this.#Ea}addHighlightArea(){this.container.classList.add("highlightArea")}}class SquareAnnotationElement extends AnnotationElement{#Ta=null;constructor(t){super(t,{isRenderable:!0,ignoreBorder:!0})}render(){this.container.classList.add("squareAnnotation");const{data:t,width:e,height:i}=this,s=this.svgFactory.create(e,i,!0),n=t.borderStyle.width,r=this.#Ta=this.svgFactory.createElement("svg:rect");r.setAttribute("x",n/2);r.setAttribute("y",n/2);r.setAttribute("width",e-n);r.setAttribute("height",i-n);r.setAttribute("stroke-width",n||1);r.setAttribute("stroke","transparent");r.setAttribute("fill","transparent");s.append(r);this.container.append(s);if(!t.popupRef&&this.hasPopupData){this.hasOwnCommentButton=!0;this._createPopup()}return this.container}getElementsToTriggerPopup(){return this.#Ta}addHighlightArea(){this.container.classList.add("highlightArea")}}class CircleAnnotationElement extends AnnotationElement{#Sa=null;constructor(t){super(t,{isRenderable:!0,ignoreBorder:!0})}render(){this.container.classList.add("circleAnnotation");const{data:t,width:e,height:i}=this,s=this.svgFactory.create(e,i,!0),n=t.borderStyle.width,r=this.#Sa=this.svgFactory.createElement("svg:ellipse");r.setAttribute("cx",e/2);r.setAttribute("cy",i/2);r.setAttribute("rx",e/2-n/2);r.setAttribute("ry",i/2-n/2);r.setAttribute("stroke-width",n||1);r.setAttribute("stroke","transparent");r.setAttribute("fill","transparent");s.append(r);this.container.append(s);if(!t.popupRef&&this.hasPopupData){this.hasOwnCommentButton=!0;this._createPopup()}return this.container}getElementsToTriggerPopup(){return this.#Sa}addHighlightArea(){this.container.classList.add("highlightArea")}}class PolylineAnnotationElement extends AnnotationElement{#_a=null;constructor(t){super(t,{isRenderable:!0,ignoreBorder:!0});this.containerClassName="polylineAnnotation";this.svgElementName="svg:polyline"}render(){this.container.classList.add(this.containerClassName);const{data:{rect:t,vertices:e,borderStyle:i,popupRef:s},width:n,height:r}=this;if(!e)return this.container;const a=this.svgFactory.create(n,r,!0);let o=[];for(let i=0,s=e.length;i=0&&n.setAttribute("stroke-width",e||1);if(i)for(let t=0,e=this.#Da.length;t{"Enter"===t.key&&(s?t.metaKey:t.ctrlKey)&&this.#Ia()});if(!e.popupRef&&this.hasPopupData){this.hasOwnCommentButton=!0;this._createPopup()}else i.classList.add("popupTriggerArea");t.append(i);return t}getElementsToTriggerPopup(){return this.#ka}addHighlightArea(){this.container.classList.add("highlightArea")}#Ia(){this.downloadManager?.openOrDownloadData(this.content,this.filename)}}class AnnotationLayer{#Fa=null;#Ba=null;#G=null;#Ra=new Map;#Oa=null;#La=null;#ia=[];#Na=!1;zIndex=0;constructor({div:t,accessibilityManager:e,annotationCanvasMap:i,annotationEditorUIManager:s,page:n,viewport:r,structTreeLayer:a,commentManager:o,linkService:l,annotationStorage:h}){this.div=t;this.#Fa=e;this.#Ba=i;this.#Oa=a||null;this.#La=l||null;this.#G=h||new AnnotationStorage;this.page=n;this.viewport=r;this._annotationEditorUIManager=s;this._commentManager=o||null}hasEditableAnnotations(){return this.#Ra.size>0}async render(t){const{annotations:e}=t,i=this.div;setLayerDimensions(i,this.viewport);const s=new Map,n=[],r={data:null,layer:i,linkService:this.#La,downloadManager:t.downloadManager,imageResourcesPath:t.imageResourcesPath||"",renderForms:!1!==t.renderForms,svgFactory:new DOMSVGFactory,annotationStorage:this.#G,enableComment:!0===t.enableComment,enableScripting:!0===t.enableScripting,hasJSActions:t.hasJSActions,fieldObjects:t.fieldObjects,parent:this,elements:null};for(const t of e){if(t.noHTML)continue;const e=t.annotationType===T.POPUP;if(e){const e=s.get(t.id);if(!e)continue;if(!this._commentManager){n.push(t);continue}r.elements=e}else if(t.rect[2]===t.rect[0]||t.rect[3]===t.rect[1])continue;r.data=t;const i=AnnotationElementFactory.create(r);if(!i.isRenderable)continue;if(!e){this.#ia.push(i);t.popupRef&&s.getOrInsertComputed(t.popupRef,makeArr).push(i)}const a=i.render();t.hidden&&(a.style.visibility="hidden");if(i._isEditable){this.#Ra.set(i.data.id,i);this._annotationEditorUIManager?.renderAnnotationElement(i)}}await this.#Ua();for(const t of n){const e=r.elements=s.get(t.id);r.data=t;const i=AnnotationElementFactory.create(r);if(!i.isRenderable)continue;const n=i.render();i.contentElement.id=`${W}${t.id}`;t.hidden&&(n.style.visibility="hidden");e.at(-1).container.after(n)}this.#Ha()}async#Ua(){if(0===this.#ia.length)return;this.div.replaceChildren();const t=[];if(!this.#Na){this.#Na=!0;for(const{contentElement:e,data:{id:i}}of this.#ia){const s=e.id=`${W}${i}`;t.push(this.#Oa?.getAriaAttributes(s).then(t=>{if(t)for(const[i,s]of t)e.setAttribute(i,s)}))}}this.#ia.sort(({data:{rect:[t,e,i,s]}},{data:{rect:[n,r,a,o]}})=>{if(t===i&&e===s)return 1;if(n===a&&r===o)return-1;const l=(e+s)/2,h=(r+o)/2;if(l>=o&&h<=e)return-1;if(h>=s&&l<=r)return 1;return(t+i)/2-(n+a)/2});const e=document.createDocumentFragment();for(const t of this.#ia){e.append(t.container);this._commentManager?(t.extraPopupElement?.popup||t.popup)?.renderCommentButton():t.extraPopupElement&&e.append(t.extraPopupElement.render())}this.div.append(e);await Promise.all(t);if(this.#Fa)for(const t of this.#ia)this.#Fa.addPointerInTextLayer(t.contentElement,!1)}async addLinkAnnotations(t){const e={data:null,layer:this.div,linkService:this.#La,svgFactory:new DOMSVGFactory,parent:this};for(const i of t){i.borderStyle||=AnnotationLayer._defaultBorderStyle;e.data=i;const t=AnnotationElementFactory.create(e);if(t.isRenderable){t.render();t.contentElement.id=`${W}${i.id}`;this.#ia.push(t)}}await this.#Ua()}update({viewport:t}){const e=this.div;this.viewport=t;setLayerDimensions(e,{rotation:t.rotation});this.#Ha();e.hidden=!1}#Ha(){if(!this.#Ba)return;const t=this.div;for(const[e,i]of this.#Ba){const s=t.querySelector(`[data-annotation-id="${e}"]`);if(!s)continue;i.className="annotationContent";const{firstChild:n}=s;n?"CANVAS"===n.nodeName?n.replaceWith(i):n.classList.contains("annotationContent")?n.after(i):n.before(i):s.append(i);const r=this.#Ra.get(e);if(r)if(r._hasNoCanvas){this._annotationEditorUIManager?.setMissingCanvas(e,s.id,i);r._hasNoCanvas=!1}else r.canvas=i}this.#Ba.clear()}getEditableAnnotations(){return this.#Ra.values()}getEditableAnnotation(t){return this.#Ra.get(t)}addFakeAnnotation(t){const{div:e}=this,{id:i,rotation:s}=t,n=new EditorAnnotationElement({data:{id:i,rect:t.getPDFRect(),rotation:s},editor:t,layer:e,parent:this,enableComment:!!this._commentManager,linkService:this.#La,annotationStorage:this.#G});n.render();n.contentElement.id=`${W}${i}`;n.createOrUpdatePopup();this.#ia.push(n);return n}removeAnnotation(t){const e=this.#ia.findIndex(e=>e.data.id===t);if(e<0)return;const[i]=this.#ia.splice(e,1);this.#Fa?.removePointerInTextLayer(i.contentElement)}updateFakeAnnotations(t){if(0!==t.length){for(const e of t)e.updateFakeAnnotationElement(this);this.#Ua()}}togglePointerEvents(t=!1){this.div.classList.toggle("disabled",!t)}static get _defaultBorderStyle(){return shadow(this,"_defaultBorderStyle",Object.freeze({width:1,rawWidth:1,style:S,dashArray:[3],horizontalCornerRadius:0,verticalCornerRadius:0}))}}const Nt=/\r\n?|\n/g;class FreeTextEditor extends AnnotationEditor{#za="";#Ga=`${this.id}-editor`;#Wa=null;#va;_colorPicker=null;static _freeTextDefaultContent="";static _internalPadding=0;static _defaultColor=null;static _defaultFontSize=10;static get _keyboardManager(){const t=FreeTextEditor.prototype,arrowChecker=t=>t.isEmpty(),e=AnnotationEditorUIManager.TRANSLATE_SMALL,i=AnnotationEditorUIManager.TRANSLATE_BIG;return shadow(this,"_keyboardManager",new KeyboardManager([[["ctrl+s","mac+meta+s","ctrl+p","mac+meta+p"],t.commitOrRemove,{bubbles:!0}],[["ctrl+Enter","mac+meta+Enter","Escape","mac+Escape"],t.commitOrRemove],[["ArrowLeft","mac+ArrowLeft"],t._translateEmpty,{args:[-e,0],checker:arrowChecker}],[["ctrl+ArrowLeft","mac+shift+ArrowLeft"],t._translateEmpty,{args:[-i,0],checker:arrowChecker}],[["ArrowRight","mac+ArrowRight"],t._translateEmpty,{args:[e,0],checker:arrowChecker}],[["ctrl+ArrowRight","mac+shift+ArrowRight"],t._translateEmpty,{args:[i,0],checker:arrowChecker}],[["ArrowUp","mac+ArrowUp"],t._translateEmpty,{args:[0,-e],checker:arrowChecker}],[["ctrl+ArrowUp","mac+shift+ArrowUp"],t._translateEmpty,{args:[0,-i],checker:arrowChecker}],[["ArrowDown","mac+ArrowDown"],t._translateEmpty,{args:[0,e],checker:arrowChecker}],[["ctrl+ArrowDown","mac+shift+ArrowDown"],t._translateEmpty,{args:[0,i],checker:arrowChecker}]]))}static _type="freetext";static _editorType=m.FREETEXT;constructor(t){super({...t,name:"freeTextEditor"});this.color=t.color||FreeTextEditor._defaultColor||AnnotationEditor._defaultLineColor;this.#va=t.fontSize||FreeTextEditor._defaultFontSize;this.annotationElementId||this._uiManager.a11yAlert("pdfjs-editor-freetext-added-alert");this.canAddComment=!1}static initialize(t,e){AnnotationEditor.initialize(t,e);const i=getComputedStyle(document.documentElement);this._internalPadding=parseFloat(i.getPropertyValue("--freetext-padding"))}static updateDefaultParams(t,e){switch(t){case f.FREETEXT_SIZE:FreeTextEditor._defaultFontSize=e;break;case f.FREETEXT_COLOR:FreeTextEditor._defaultColor=e}}updateParams(t,e){switch(t){case f.FREETEXT_SIZE:this.#Va(e);break;case f.FREETEXT_COLOR:this.#ba(e)}}static get defaultPropertiesToUpdate(){return[[f.FREETEXT_SIZE,FreeTextEditor._defaultFontSize],[f.FREETEXT_COLOR,FreeTextEditor._defaultColor||AnnotationEditor._defaultLineColor]]}get propertiesToUpdate(){return[[f.FREETEXT_SIZE,this.#va],[f.FREETEXT_COLOR,this.color]]}get toolbarButtons(){this._colorPicker||=new BasicColorPicker(this);return[["colorPicker",this._colorPicker]]}get colorType(){return f.FREETEXT_COLOR}#Va(t){const setFontsize=t=>{this.editorDiv.style.fontSize=`calc(${t}px * var(--total-scale-factor))`;this.translate(0,-(t-this.#va)*this.parentScale);this.#va=t;this.#ja()},e=this.#va;this.addCommands({cmd:setFontsize.bind(this,t),undo:setFontsize.bind(this,e),post:this._uiManager.updateUI.bind(this._uiManager,this),mustExec:!0,type:f.FREETEXT_SIZE,overwriteIfSameType:!0,keepUndo:!0})}onUpdatedColor(){this.editorDiv.style.color=this.color;this._colorPicker?.update(this.color);super.onUpdatedColor()}#ba(t){const setColor=t=>{this.color=t;this.onUpdatedColor()},e=this.color;this.addCommands({cmd:setColor.bind(this,t),undo:setColor.bind(this,e),post:this._uiManager.updateUI.bind(this._uiManager,this),mustExec:!0,type:f.FREETEXT_COLOR,overwriteIfSameType:!0,keepUndo:!0})}_translateEmpty(t,e){this._uiManager.translateSelectedEditors(t,e,!0)}getInitialTranslation(){const t=this.parentScale;return[-FreeTextEditor._internalPadding*t,-(FreeTextEditor._internalPadding+this.#va)*t]}rebuild(){if(this.parent){super.rebuild();null!==this.div&&(this.isAttachedToDOM||this.parent.add(this))}}enableEditMode(){if(!super.enableEditMode())return!1;this.overlayDiv.classList.remove("enabled");this.editorDiv.contentEditable=!0;this._isDraggable=!1;this.div.removeAttribute("aria-activedescendant");this.#Wa=new AbortController;const t=this._uiManager.combinedSignal(this.#Wa);this.editorDiv.addEventListener("keydown",this.editorDivKeydown.bind(this),{signal:t});this.editorDiv.addEventListener("focus",this.editorDivFocus.bind(this),{signal:t});this.editorDiv.addEventListener("blur",this.editorDivBlur.bind(this),{signal:t});this.editorDiv.addEventListener("input",this.editorDivInput.bind(this),{signal:t});this.editorDiv.addEventListener("paste",this.editorDivPaste.bind(this),{signal:t});return!0}disableEditMode(){if(!super.disableEditMode())return!1;this.overlayDiv.classList.add("enabled");this.editorDiv.contentEditable=!1;this.div.setAttribute("aria-activedescendant",this.#Ga);this._isDraggable=!0;this.#Wa?.abort();this.#Wa=null;this.div.focus({preventScroll:!0});this.isEditing=!1;this.parent.div.classList.add("freetextEditing");return!0}focusin(t){if(this._focusEventsAllowed){super.focusin(t);t.target!==this.editorDiv&&this.editorDiv.focus()}}onceAdded(t){if(!this.width){this.enableEditMode();t&&this.editorDiv.focus();this._initialOptions?.isCentered&&this.center();this._initialOptions=null}}isEmpty(){return!this.editorDiv||""===this.editorDiv.innerText.trim()}remove(){this.isEditing=!1;if(this.parent){this.parent.setEditingState(!0);this.parent.div.classList.add("freetextEditing")}super.remove()}#$a(){const t=[];this.editorDiv.normalize();let e=null;for(const i of this.editorDiv.childNodes)if(e?.nodeType!==Node.TEXT_NODE||"BR"!==i.nodeName){t.push(FreeTextEditor.#Xa(i));e=i}return t.join("\n")}#ja(){const[t,e]=this.parentDimensions;let i;if(this.isAttachedToDOM)i=this.div.getBoundingClientRect();else{const{currentLayer:t,div:e}=this,s=e.style.display,n=e.classList.contains("hidden");e.classList.remove("hidden");e.style.display="hidden";t.div.append(this.div);i=e.getBoundingClientRect();e.remove();e.style.display=s;e.classList.toggle("hidden",n)}if(this.rotation%180==this.parentRotation%180){this.width=i.width/t;this.height=i.height/e}else{this.width=i.height/t;this.height=i.width/e}this.fixAndSetPosition()}commit(){if(!this.isInEditMode())return;super.commit();this.disableEditMode();const t=this.#za,e=this.#za=this.#$a().trimEnd();if(t===e)return;const setText=t=>{this.#za=t;if(t){this.#qa();this._uiManager.rebuild(this);this.#ja()}else this.remove()};this.addCommands({cmd:()=>{setText(e)},undo:()=>{setText(t)},mustExec:!1});this.#ja()}shouldGetKeyboardEvents(){return this.isInEditMode()}enterInEditMode(){this.enableEditMode();this.editorDiv.focus()}keydown(t){if(t.target===this.div&&"Enter"===t.key){this.enterInEditMode();t.preventDefault()}}editorDivKeydown(t){FreeTextEditor._keyboardManager.exec(this,t)}editorDivFocus(t){this.isEditing=!0}editorDivBlur(t){this.isEditing=!1}editorDivInput(t){this.parent.div.classList.toggle("freetextEditing",this.isEmpty())}disableEditing(){this.editorDiv.setAttribute("role","comment");this.editorDiv.removeAttribute("aria-multiline")}enableEditing(){this.editorDiv.setAttribute("role","textbox");this.editorDiv.setAttribute("aria-multiline",!0)}get canChangeContent(){return!0}render(){if(this.div)return this.div;let t,e;if(this._isCopy||this.annotationElementId){t=this.x;e=this.y}super.render();this.editorDiv=document.createElement("div");this.editorDiv.className="internal";this.editorDiv.setAttribute("id",this.#Ga);this.editorDiv.setAttribute("data-l10n-id","pdfjs-free-text2");this.editorDiv.setAttribute("data-l10n-attrs","default-content");this.enableEditing();this.editorDiv.contentEditable=!0;const{style:i}=this.editorDiv;i.fontSize=`calc(${this.#va}px * var(--total-scale-factor))`;i.color=this.color;this.div.append(this.editorDiv);this.overlayDiv=document.createElement("div");this.overlayDiv.classList.add("overlay","enabled");this.div.append(this.overlayDiv);if(this._isCopy||this.annotationElementId){const[i,s]=this.parentDimensions;if(this.annotationElementId){const{position:n}=this._initialData;let[r,a]=this.getInitialTranslation();[r,a]=this.pageTranslationToScreen(r,a);const[o,l]=this.pageDimensions,[h,d]=this.pageTranslation;let c,u;switch(this.rotation){case 0:c=t+(n[0]-h)/o;u=e+this.height-(n[1]-d)/l;break;case 90:c=t+(n[0]-h)/o;u=e-(n[1]-d)/l;[r,a]=[a,-r];break;case 180:c=t-this.width+(n[0]-h)/o;u=e-(n[1]-d)/l;[r,a]=[-r,-a];break;case 270:c=t+(n[0]-h-this.height*l)/o;u=e+(n[1]-d-this.width*o)/l;[r,a]=[-a,r]}this.setAt(c*i,u*s,r,a)}else this._moveAfterPaste(t,e);this.#qa();this._isDraggable=!0;this.editorDiv.contentEditable=!1}else{this._isDraggable=!1;this.editorDiv.contentEditable=!0}return this.div}static#Xa(t){return(t.nodeType===Node.TEXT_NODE?t.nodeValue:t.innerText).replaceAll(Nt,"")}editorDivPaste(t){const e=t.clipboardData||window.clipboardData,{types:i}=e;if(1===i.length&&"text/plain"===i[0])return;t.preventDefault();const s=FreeTextEditor.#Ka(e.getData("text")||"").replaceAll(Nt,"\n");if(!s)return;const n=window.getSelection();if(!n.rangeCount)return;this.editorDiv.normalize();n.deleteFromDocument();const r=n.getRangeAt(0);if(!s.includes("\n")){r.insertNode(document.createTextNode(s));this.editorDiv.normalize();n.collapseToStart();return}const{startContainer:a,startOffset:o}=r,l=[],h=[];if(a.nodeType===Node.TEXT_NODE){const t=a.parentElement;h.push(a.nodeValue.slice(o).replaceAll(Nt,""));if(t!==this.editorDiv){let e=l;for(const i of this.editorDiv.childNodes)i!==t?e.push(FreeTextEditor.#Xa(i)):e=h}l.push(a.nodeValue.slice(0,o).replaceAll(Nt,""))}else if(a===this.editorDiv){let t=l,e=0;for(const i of this.editorDiv.childNodes){e++===o&&(t=h);t.push(FreeTextEditor.#Xa(i))}}this.#za=`${l.join("\n")}${s}${h.join("\n")}`;this.#qa();const d=new Range;let c=Math.sumPrecise(l.map(t=>t.length));for(const{firstChild:t}of this.editorDiv.childNodes)if(t.nodeType===Node.TEXT_NODE){const e=t.nodeValue.length;if(c<=e){d.setStart(t,c);d.setEnd(t,c);break}c-=e}n.removeAllRanges();n.addRange(d)}#qa(){this.editorDiv.replaceChildren();if(this.#za)for(const t of this.#za.split("\n")){const e=document.createElement("div");e.append(t?document.createTextNode(t):document.createElement("br"));this.editorDiv.append(e)}}#Ya(){return this.#za.replaceAll(" "," ")}static#Ka(t){return t.replaceAll(" "," ")}get contentDiv(){return this.editorDiv}getPDFRect(){const t=FreeTextEditor._internalPadding*this.parentScale;return this.getRect(t,t)}static async deserialize(t,e,i){let s=null;if(t instanceof FreeTextAnnotationElement){const{data:{defaultAppearanceData:{fontSize:e,fontColor:i},rect:n,rotation:r,id:a,popupRef:o,richText:l,contentsObj:h,creationDate:d,modificationDate:c},textContent:u,textPosition:p,parent:{page:{pageNumber:g}}}=t;if(!u||0===u.length)return null;s=t={annotationType:m.FREETEXT,color:Array.from(i),fontSize:e,value:u.join("\n"),position:p,pageIndex:g-1,rect:n.slice(0),rotation:r,annotationElementId:a,id:a,deleted:!1,popupRef:o,comment:h?.str||null,richText:l,creationDate:d,modificationDate:c}}const n=await super.deserialize(t,e,i);n.#va=t.fontSize;n.color=Util.makeHexColor(...t.color);n.#za=FreeTextEditor.#Ka(t.value);n._initialData=s;t.comment&&n.setCommentData(t);return n}serialize(t=!1){if(this.isEmpty())return null;if(this.deleted)return this.serializeDeleted();const e=AnnotationEditor._colorManager.convert(this.isAttachedToDOM?getComputedStyle(this.editorDiv).color:this.color),i=Object.assign(super.serialize(t),{color:e,fontSize:this.#va,value:this.#Ya()});this.addComment(i);if(t){i.isCopy=!0;return i}if(this.annotationElementId&&!this.#Qa(i))return null;i.id=this.annotationElementId;return i}#Qa(t){const{value:e,fontSize:i,color:s,pageIndex:n}=this._initialData;return this.hasEditedComment||this._hasBeenMoved||t.value!==e||t.fontSize!==i||t.color.some((t,e)=>t!==s[e])||t.pageIndex!==n}renderAnnotationElement(t){const e=super.renderAnnotationElement(t);if(!e)return null;const{style:i}=e;i.fontSize=`calc(${this.#va}px * var(--total-scale-factor))`;i.color=this.color;e.replaceChildren();for(const t of this.#za.split("\n")){const i=document.createElement("div");i.append(t?document.createTextNode(t):document.createElement("br"));e.append(i)}t.updateEdited({rect:this.getPDFRect(),popup:this._uiManager.hasCommentManager()||this.hasEditedComment?this.comment:{text:this.#za}});return e}resetAnnotationElement(t){super.resetAnnotationElement(t);t.resetEdited()}}class Outline{static PRECISION=1e-4;toSVGPath(){unreachable("Abstract method `toSVGPath` must be implemented.")}get box(){unreachable("Abstract getter `box` must be implemented.")}serialize(t,e){unreachable("Abstract method `serialize` must be implemented.")}static _rescale(t,e,i,s,n,r){r||=new Float32Array(t.length);for(let a=0,o=t.length;a=6;t-=6)isNaN(e[t])?i.push(`L${e[t+4]} ${e[t+5]}`):i.push(`C${e[t]} ${e[t+1]} ${e[t+2]} ${e[t+3]} ${e[t+4]} ${e[t+5]}`);this.#bo(i);return i.join(" ")}#mo(){const[t,e,i,s]=this.#Ja,[n,r,a,o]=this.#go();return`M${(this.#so[2]-t)/i} ${(this.#so[3]-e)/s} L${(this.#so[4]-t)/i} ${(this.#so[5]-e)/s} L${n} ${r} L${a} ${o} L${(this.#so[16]-t)/i} ${(this.#so[17]-e)/s} L${(this.#so[14]-t)/i} ${(this.#so[15]-e)/s} Z`}#bo(t){const e=this.#Za;t.push(`L${e[4]} ${e[5]} Z`)}#fo(t){const[e,i,s,n]=this.#Ja,r=this.#so.subarray(4,6),a=this.#so.subarray(16,18),[o,l,h,d]=this.#go();t.push(`L${(r[0]-e)/s} ${(r[1]-i)/n} L${o} ${l} L${h} ${d} L${(a[0]-e)/s} ${(a[1]-i)/n}`)}newFreeDrawOutline(t,e,i,s,n,r){return new FreeDrawOutline(t,e,i,s,n,r)}getOutlines(){const t=this.#io,e=this.#Za,i=this.#so,[s,n,r,a]=this.#Ja,o=new Float32Array((this.#do?.length??0)+2);for(let t=0,e=o.length-2;t=6;t-=6)for(let i=0;i<6;i+=2)if(isNaN(e[t+i])){l[h]=l[h+1]=NaN;h+=2}else{l[h]=e[t+i];l[h+1]=e[t+i+1];h+=2}this.#vo(l,h);return this.newFreeDrawOutline(l,o,this.#Ja,this.#lo,this.#to,this.#eo)}#yo(t){const e=this.#so,[i,s,n,r]=this.#Ja,[a,o,l,h]=this.#go(),d=new Float32Array(36);d.set([NaN,NaN,NaN,NaN,(e[2]-i)/n,(e[3]-s)/r,NaN,NaN,NaN,NaN,(e[4]-i)/n,(e[5]-s)/r,NaN,NaN,NaN,NaN,a,o,NaN,NaN,NaN,NaN,l,h,NaN,NaN,NaN,NaN,(e[16]-i)/n,(e[17]-s)/r,NaN,NaN,NaN,NaN,(e[14]-i)/n,(e[15]-s)/r],0);return this.newFreeDrawOutline(d,t,this.#Ja,this.#lo,this.#to,this.#eo)}#vo(t,e){const i=this.#Za;t.set([NaN,NaN,NaN,NaN,i[4],i[5]],e);return e+6}#Ao(t,e){const i=this.#so.subarray(4,6),s=this.#so.subarray(16,18),[n,r,a,o]=this.#Ja,[l,h,d,c]=this.#go();t.set([NaN,NaN,NaN,NaN,(i[0]-n)/a,(i[1]-r)/o,NaN,NaN,NaN,NaN,l,h,NaN,NaN,NaN,NaN,d,c,NaN,NaN,NaN,NaN,(s[0]-n)/a,(s[1]-r)/o],e);return e+24}}class FreeDrawOutline extends Outline{#Ja;#wo=new Float32Array(4);#to;#eo;#do;#lo;#xo;constructor(t,e,i,s,n,r){super();this.#xo=t;this.#do=e;this.#Ja=i;this.#lo=s;this.#to=n;this.#eo=r;this.firstPoint=[NaN,NaN];this.lastPoint=[NaN,NaN];this.#Co(r);const[a,o,l,h]=this.#wo;for(let e=0,i=t.length;ep){a=u;o=p}else o===p&&(a=d(a,u));if(hc[1]){a=c[0];o=c[1]}else o===c[1]&&(a=d(a,c[0]));if(ht[0]-e[0]||t[1]-e[1]||t[2]-e[2]);const t=[];for(const e of this.#So)if(e[3]){t.push(...this.#Po(e));this.#Do(e)}else{this.#Mo(e);t.push(...this.#Po(e))}return this.#ko(t)}#ko(t){const e=[],i=new Set;for(const i of t){const[t,s,n]=i;e.push([t,s,i],[t,n,i])}e.sort((t,e)=>t[1]-e[1]||t[0]-e[0]);for(let t=0,s=e.length;t0;){const t=i.values().next().value;let[e,r,a,o,l]=t;i.delete(t);let h=e,d=r;n=[e,a];s.push(n);for(;;){let t;if(i.has(o))t=o;else{if(!i.has(l))break;t=l}i.delete(t);[e,r,a,o,l]=t;if(h!==e){n.push(h,d,e,d===r?r:a);h=e}d=d===r?a:r}n.push(h,d)}return new HighlightOutline(s,this.#Ja,this.#Eo,this.#To)}#Io(t){const e=this.#_o;let i=0,s=e.length-1;for(;i<=s;){const n=i+s>>1,r=e[n][0];if(r===t)return n;r=0;s--){const[i,n]=this.#_o[s];if(i!==t)break;if(i===t&&n===e){this.#_o.splice(s,1);return}}}#Po(t){const[e,i,s]=t,n=[[e,i,s]],r=this.#Io(s);for(let t=0;t=i)if(o>s)n[t][1]=s;else{if(1===r)return[];n.splice(t,1);t--;r--}else{n[t][2]=i;o>s&&n.push([e,s,o])}}}return n}}class HighlightOutline extends Outline{#Ja;#Fo;constructor(t,e,i,s){super();this.#Fo=t;this.#Ja=e;this.firstPoint=i;this.lastPoint=s}toSVGPath(){const t=[];for(const e of this.#Fo){let[i,s]=e;t.push(`M${i} ${s}`);for(let n=2;n-1){this.#Wo=!0;this.#$o(t);this.#Xo()}else if(this.#Oo){this.#Bo=t.anchorNode;this.#Ro=t.anchorOffset;this.#Uo=t.focusNode;this.#Ho=t.focusOffset;this.#qo();this.#Xo();this.rotate(this.rotation)}this.annotationElementId||this._uiManager.a11yAlert("pdfjs-editor-highlight-added-alert")}get telemetryInitialData(){return{action:"added",type:this.#Wo?"free_highlight":"highlight",color:this._uiManager.getNonHCMColorName(this.color),thickness:this.#ho,methodOfCreation:this.#jo}}get telemetryFinalData(){return{type:"highlight",color:this._uiManager.getNonHCMColorName(this.color)}}static computeTelemetryFinalData(t){return{numberOfColors:t.get("color").size}}#qo(){const t=new HighlightOutliner(this.#Oo,.001);this.#Go=t.getOutlines();[this.x,this.y,this.width,this.height]=this.#Go.box;const e=new HighlightOutliner(this.#Oo,.0025,.001,"ltr"===this._uiManager.direction);this.#No=e.getOutlines();const{firstPoint:i}=this.#Go;this.#Eo=[(i[0]-this.x)/this.width,(i[1]-this.y)/this.height];const{lastPoint:s}=this.#No;this.#To=[(s[0]-this.x)/this.width,(s[1]-this.y)/this.height]}#$o({highlightOutlines:t,highlightId:e,clipPathId:i}){this.#Go=t;this.#No=t.getNewOutline(this.#ho/2+1.5,.0025);if(e>=0){this.#S=e;this.#Lo=i;this.parent.drawLayer.finalizeDraw(e,{bbox:t.box,path:{d:t.toSVGPath()}});this.#Vo=this.parent.drawLayer.drawOutline({rootClass:{highlightOutline:!0,free:!0},bbox:this.#No.box,path:{d:this.#No.toSVGPath()}},!0)}else if(this.parent){const e=this.parent.viewport.rotation;this.parent.drawLayer.updateProperties(this.#S,{bbox:HighlightEditor.#Ko(this.#Go.box,(e-this.rotation+360)%360),path:{d:t.toSVGPath()}});this.parent.drawLayer.updateProperties(this.#Vo,{bbox:HighlightEditor.#Ko(this.#No.box,e),path:{d:this.#No.toSVGPath()}})}const[s,n,r,a]=t.box;switch(this.rotation){case 0:this.x=s;this.y=n;this.width=r;this.height=a;break;case 90:{const[t,e]=this.parentDimensions;this.x=n;this.y=1-s;this.width=r*e/t;this.height=a*t/e;break}case 180:this.x=1-s;this.y=1-n;this.width=r;this.height=a;break;case 270:{const[t,e]=this.parentDimensions;this.x=1-n;this.y=s;this.width=r*e/t;this.height=a*t/e;break}}const{firstPoint:o}=t;this.#Eo=[(o[0]-s)/r,(o[1]-n)/a];const{lastPoint:l}=this.#No;this.#To=[(l[0]-s)/r,(l[1]-n)/a]}static initialize(t,e){AnnotationEditor.initialize(t,e);HighlightEditor._defaultColor||=e.highlightColors?.values().next().value||"#fff066"}static updateDefaultParams(t,e){switch(t){case f.HIGHLIGHT_COLOR:HighlightEditor._defaultColor=e;break;case f.HIGHLIGHT_THICKNESS:HighlightEditor._defaultThickness=e}}translateInPage(t,e){}get toolbarPosition(){return this.#To}get commentButtonPosition(){return this.#Eo}updateParams(t,e){switch(t){case f.HIGHLIGHT_COLOR:this.#ba(e);break;case f.HIGHLIGHT_THICKNESS:this.#Yo(e)}}static get defaultPropertiesToUpdate(){return[[f.HIGHLIGHT_COLOR,HighlightEditor._defaultColor],[f.HIGHLIGHT_THICKNESS,HighlightEditor._defaultThickness]]}get propertiesToUpdate(){return[[f.HIGHLIGHT_COLOR,this.color||HighlightEditor._defaultColor],[f.HIGHLIGHT_THICKNESS,this.#ho||HighlightEditor._defaultThickness],[f.HIGHLIGHT_FREE,this.#Wo]]}onUpdatedColor(){this.parent?.drawLayer.updateProperties(this.#S,{root:{fill:this.color,"fill-opacity":this.opacity}});this.#r?.updateColor(this.color);super.onUpdatedColor()}#ba(t){const setColorAndOpacity=(t,e)=>{this.color=t;this.opacity=e;this.onUpdatedColor()},e=this.color,i=this.opacity;this.addCommands({cmd:setColorAndOpacity.bind(this,t,HighlightEditor._defaultOpacity),undo:setColorAndOpacity.bind(this,e,i),post:this._uiManager.updateUI.bind(this._uiManager,this),mustExec:!0,type:f.HIGHLIGHT_COLOR,overwriteIfSameType:!0,keepUndo:!0});this._reportTelemetry({action:"color_changed",color:this._uiManager.getNonHCMColorName(t)},!0)}#Yo(t){const e=this.#ho,setThickness=t=>{this.#ho=t;this.#Qo(t)};this.addCommands({cmd:setThickness.bind(this,t),undo:setThickness.bind(this,e),post:this._uiManager.updateUI.bind(this._uiManager,this),mustExec:!0,type:f.INK_THICKNESS,overwriteIfSameType:!0,keepUndo:!0});this._reportTelemetry({action:"thickness_changed",thickness:t},!0)}get toolbarButtons(){if(this._uiManager.highlightColors){return[["colorPicker",this.#r=new ColorPicker({editor:this})]]}return super.toolbarButtons}disableEditing(){super.disableEditing();this.div.classList.toggle("disabled",!0)}enableEditing(){super.enableEditing();this.div.classList.toggle("disabled",!1)}fixAndSetPosition(){return super.fixAndSetPosition(this.#Jo())}getBaseTranslation(){return[0,0]}getRect(t,e){return super.getRect(t,e,this.#Jo())}onceAdded(t){this.annotationElementId||this.parent.addUndoableEditor(this);t&&this.div.focus()}remove(){this.#Zo();this._reportTelemetry({action:"deleted"});super.remove()}rebuild(){if(this.parent){super.rebuild();if(null!==this.div){this.#Xo();this.isAttachedToDOM||this.parent.add(this)}}}setParent(t){let e=!1;if(this.parent&&!t)this.#Zo();else if(t){this.#Xo(t);e=!this.parent&&this.div?.classList.contains("selectedEditor")}super.setParent(t);this.show(this._isVisible);e&&this.select()}#Qo(t){if(this.#Wo){this.#$o({highlightOutlines:this.#Go.getNewOutline(t/2)});this.fixAndSetPosition();this.setDims()}}#Zo(){if(null!==this.#S&&this.parent){this.parent.drawLayer.remove(this.#S);this.#S=null;this.parent.drawLayer.remove(this.#Vo);this.#Vo=null}}#Xo(t=this.parent){if(null===this.#S){({id:this.#S,clipPathId:this.#Lo}=t.drawLayer.draw({bbox:this.#Go.box,root:{viewBox:"0 0 1 1",fill:this.color,"fill-opacity":this.opacity},rootClass:{highlight:!0,free:this.#Wo},path:{d:this.#Go.toSVGPath()}},!1,!0));this.#Vo=t.drawLayer.drawOutline({rootClass:{highlightOutline:!0,free:this.#Wo},bbox:this.#No.box,path:{d:this.#No.toSVGPath()}},this.#Wo);this.#zo&&(this.#zo.style.clipPath=this.#Lo)}}static#Ko([t,e,i,s],n){switch(n){case 90:return[1-e-s,t,s,i];case 180:return[1-t-i,1-e-s,i,s];case 270:return[e,1-t-i,s,i]}return[t,e,i,s]}rotate(t){const{drawLayer:e}=this.parent;let i;if(this.#Wo){t=(t-this.rotation+360)%360;i=HighlightEditor.#Ko(this.#Go.box,t)}else i=HighlightEditor.#Ko([this.x,this.y,this.width,this.height],t);e.updateProperties(this.#S,{bbox:i,root:{"data-main-rotation":t}});e.updateProperties(this.#Vo,{bbox:HighlightEditor.#Ko(this.#No.box,t),root:{"data-main-rotation":t}})}render(){if(this.div)return this.div;const t=super.render();if(this.#be){t.setAttribute("aria-label",this.#be);t.setAttribute("role","mark")}this.#Wo?t.classList.add("free"):this.div.addEventListener("keydown",this.#tl.bind(this),{signal:this._uiManager._signal});const e=this.#zo=document.createElement("div");t.append(e);e.setAttribute("aria-hidden","true");e.className="internal";e.style.clipPath=this.#Lo;this.setDims();bindEvents(this,this.#zo,["pointerover","pointerleave"]);this.enableEditing();return t}pointerover(){this.isSelected||this.parent?.drawLayer.updateProperties(this.#Vo,{rootClass:{hovered:!0}})}pointerleave(){this.isSelected||this.parent?.drawLayer.updateProperties(this.#Vo,{rootClass:{hovered:!1}})}#tl(t){HighlightEditor._keyboardManager.exec(this,t)}_moveCaret(t){this.parent.unselect(this);switch(t){case 0:case 2:this.#el(!0);break;case 1:case 3:this.#el(!1)}}#el(t){if(!this.#Bo)return;const e=window.getSelection();t?e.setPosition(this.#Bo,this.#Ro):e.setPosition(this.#Uo,this.#Ho)}select(){super.select();this.#Vo&&this.parent?.drawLayer.updateProperties(this.#Vo,{rootClass:{hovered:!1,selected:!0}})}unselect(){super.unselect();if(this.#Vo){this.parent?.drawLayer.updateProperties(this.#Vo,{rootClass:{selected:!1}});this.#Wo||this.#el(!1)}}get _mustFixPosition(){return!this.#Wo}show(t=this._isVisible){super.show(t);if(this.parent){this.parent.drawLayer.updateProperties(this.#S,{rootClass:{hidden:!t}});this.parent.drawLayer.updateProperties(this.#Vo,{rootClass:{hidden:!t}})}}#Jo(){return this.#Wo?this.rotation:0}#il(){if(this.#Wo)return null;const[t,e]=this.pageDimensions,[i,s]=this.pageTranslation,n=this.#Oo,r=new Float32Array(8*n.length);let a=0;for(const{x:o,y:l,width:h,height:d}of n){const n=o*t+i,c=(1-l)*e+s;r[a]=r[a+4]=n;r[a+1]=r[a+3]=c;r[a+2]=r[a+6]=n+h*t;r[a+5]=r[a+7]=c-d*e;a+=8}return r}#sl(t){return this.#Go.serialize(t,this.#Jo())}static startHighlighting(t,e,{target:i,x:s,y:n}){const{x:r,y:a,width:o,height:l}=i.getBoundingClientRect(),h=new AbortController,d=t.combinedSignal(h),pointerUpCallback=e=>{h.abort();this.#nl(t,e)};window.addEventListener("blur",pointerUpCallback,{signal:d});window.addEventListener("pointerup",pointerUpCallback,{signal:d});window.addEventListener("pointerdown",stopEvent,{capture:!0,passive:!1,signal:d});window.addEventListener("contextmenu",noContextMenu,{signal:d});i.addEventListener("pointermove",this.#rl.bind(this,t),{signal:d});this._freeHighlight=new FreeHighlightOutliner({x:s,y:n},[r,a,o,l],t.scale,this._defaultThickness/2,e,.001);({id:this._freeHighlightId,clipPathId:this._freeHighlightClipId}=t.drawLayer.draw({bbox:[0,0,1,1],root:{viewBox:"0 0 1 1",fill:this._defaultColor,"fill-opacity":this._defaultOpacity},rootClass:{highlight:!0,free:!0},path:{d:this._freeHighlight.toSVGPath()}},!0,!0))}static#rl(t,e){this._freeHighlight.add(e)&&t.drawLayer.updateProperties(this._freeHighlightId,{path:{d:this._freeHighlight.toSVGPath()}})}static#nl(t,e){this._freeHighlight.isEmpty()?t.drawLayer.remove(this._freeHighlightId):t.createAndAddNewEditor(e,!1,{highlightId:this._freeHighlightId,highlightOutlines:this._freeHighlight.getOutlines(),clipPathId:this._freeHighlightClipId,methodOfCreation:"main_toolbar"});this._freeHighlightId=-1;this._freeHighlight=null;this._freeHighlightClipId=""}static async deserialize(t,e,i){let s=null;if(t instanceof HighlightAnnotationElement){const{data:{quadPoints:e,rect:i,rotation:n,id:r,color:a,opacity:o,popupRef:l,richText:h,contentsObj:d,creationDate:c,modificationDate:u},parent:{page:{pageNumber:p}}}=t;s=t={annotationType:m.HIGHLIGHT,color:Array.from(a),opacity:o,quadPoints:e,boxes:null,pageIndex:p-1,rect:i.slice(0),rotation:n,annotationElementId:r,id:r,deleted:!1,popupRef:l,richText:h,comment:d?.str||null,creationDate:c,modificationDate:u}}else if(t instanceof InkAnnotationElement){const{data:{inkLists:e,rect:i,rotation:n,id:r,color:a,borderStyle:{rawWidth:o},popupRef:l,richText:h,contentsObj:d,creationDate:c,modificationDate:u},parent:{page:{pageNumber:p}}}=t;s=t={annotationType:m.HIGHLIGHT,color:Array.from(a),thickness:o,inkLists:e,boxes:null,pageIndex:p-1,rect:i.slice(0),rotation:n,annotationElementId:r,id:r,deleted:!1,popupRef:l,richText:h,comment:d?.str||null,creationDate:c,modificationDate:u}}const{color:n,quadPoints:r,inkLists:a,outlines:o,opacity:l}=t,h=await super.deserialize(t,e,i);h.color=Util.makeHexColor(...n);h.opacity=l||1;a&&(h.#ho=t.thickness);h._initialData=s;t.comment&&h.setCommentData(t);const[d,c]=h.pageDimensions,[u,p]=h.pageTranslation;if(r){const t=h.#Oo=[];for(let e=0;et!==e[i])}renderAnnotationElement(t){if(this.deleted){t.hide();return null}t.updateEdited({rect:this.getPDFRect(),popup:this.comment});return null}static canCreateNewEmptyEditor(){return!1}}class DrawingOptions{#al=Object.create(null);updateProperty(t,e){this[t]=e;this.updateSVGProperty(t,e)}updateProperties(t){if(t)for(const[e,i]of Object.entries(t))e.startsWith("_")||this.updateProperty(e,i)}updateSVGProperty(t,e){this.#al[t]=e}toSVGProperties(){const t=this.#al;this.#al=Object.create(null);return{root:t}}reset(){this.#al=Object.create(null)}updateAll(t=this){this.updateProperties(t)}clone(){unreachable("Not implemented")}}class DrawingEditor extends AnnotationEditor{#ol=null;#ll;_colorPicker=null;_drawId=null;static _currentDrawId=-1;static _currentParent=null;static#hl=null;static#dl=null;static#cl=null;static _INNER_MARGIN=3;constructor(t){super(t);this.#ll=t.mustBeCommitted||!1;this._addOutlines(t)}onUpdatedColor(){this._colorPicker?.update(this.color);super.onUpdatedColor()}onUpdatedOpacity(){this._colorPicker?.updateOpacity?.(this.opacity)}_addOutlines(t){if(t.drawOutlines){this.#ul(t);this.#Xo()}}#ul({drawOutlines:t,drawId:e,drawingOptions:i}){this.#ol=t;this._drawingOptions||=i;this.annotationElementId||this._uiManager.a11yAlert(`pdfjs-editor-${this.editorType}-added-alert`);if(e>=0){this._drawId=e;this.parent.drawLayer.finalizeDraw(e,t.defaultProperties)}else this._drawId=this.#pl(t,this.parent);this.#gl(t.box)}#pl(t,e){const{id:i}=e.drawLayer.draw(DrawingEditor._mergeSVGProperties(this._drawingOptions.toSVGProperties(),t.defaultSVGProperties),!1,!1);return i}static _mergeSVGProperties(t,e){const i=new Set(Object.keys(t));for(const[s,n]of Object.entries(e))i.has(s)?Object.assign(t[s],n):t[s]=n;return t}static getDefaultDrawingOptions(t){unreachable("Not implemented")}static get typesMap(){unreachable("Not implemented")}static get isDrawer(){return!0}static get supportMultipleDrawings(){return!1}static updateDefaultParams(t,e){const i=this.typesMap.get(t);i&&this._defaultDrawingOptions.updateProperty(i,e);if(this._currentParent){DrawingEditor.#hl.updateProperty(i,e);this._currentParent.drawLayer.updateProperties(this._currentDrawId,this._defaultDrawingOptions.toSVGProperties())}}updateParams(t,e){const i=this.constructor.typesMap.get(t);i&&this._updateProperty(t,i,e)}static get defaultPropertiesToUpdate(){const t=[],e=this._defaultDrawingOptions;for(const[i,s]of this.typesMap)t.push([i,e[s]]);return t}get propertiesToUpdate(){const t=[],{_drawingOptions:e}=this;for(const[i,s]of this.constructor.typesMap)t.push([i,e[s]]);return t}_updateProperty(t,e,i){const s=this._drawingOptions,n=s[e],setter=i=>{s.updateProperty(e,i);const n=this.#ol.updateProperty(e,i);n&&this.#gl(n);this.parent?.drawLayer.updateProperties(this._drawId,s.toSVGProperties());t===this.colorType?this.onUpdatedColor():t===this.opacityType&&this.onUpdatedOpacity()};this.addCommands({cmd:setter.bind(this,i),undo:setter.bind(this,n),post:this._uiManager.updateUI.bind(this._uiManager,this),mustExec:!0,type:t,overwriteIfSameType:!0,keepUndo:!0})}_updateColorAndOpacity(t,e){const i=this.constructor.typesMap.get(this.colorType),s=this.constructor.typesMap.get(this.opacityType),n=this._drawingOptions,r=n[i],a=n[s],setter=(t,e)=>{n.updateProperty(i,t);n.updateProperty(s,e);this.#ol.updateProperty(i,t);this.#ol.updateProperty(s,e);this.parent?.drawLayer.updateProperties(this._drawId,n.toSVGProperties());this.onUpdatedColor();this.onUpdatedOpacity()};this.addCommands({cmd:setter.bind(this,t,e),undo:setter.bind(this,r,a),post:this._uiManager.updateUI.bind(this._uiManager,this),mustExec:!0,type:f.INK_COLOR_AND_OPACITY,overwriteIfSameType:!0,keepUndo:!0})}_onResizing(){this.parent?.drawLayer.updateProperties(this._drawId,DrawingEditor._mergeSVGProperties(this.#ol.getPathResizingSVGProperties(this.#ml()),{bbox:this.#fl()}))}_onResized(){this.parent?.drawLayer.updateProperties(this._drawId,DrawingEditor._mergeSVGProperties(this.#ol.getPathResizedSVGProperties(this.#ml()),{bbox:this.#fl()}))}_onTranslating(t,e){this.parent?.drawLayer.updateProperties(this._drawId,{bbox:this.#fl()})}_onTranslated(){this.parent?.drawLayer.updateProperties(this._drawId,DrawingEditor._mergeSVGProperties(this.#ol.getPathTranslatedSVGProperties(this.#ml(),this.parentDimensions),{bbox:this.#fl()}))}_onStartDragging(){this.parent?.drawLayer.updateProperties(this._drawId,{rootClass:{moving:!0}})}_onStopDragging(){this.parent?.drawLayer.updateProperties(this._drawId,{rootClass:{moving:!1}})}commit(){super.commit();this.disableEditMode();this.disableEditing()}disableEditing(){super.disableEditing();this.div.classList.toggle("disabled",!0)}enableEditing(){super.enableEditing();this.div.classList.toggle("disabled",!1)}getBaseTranslation(){return[0,0]}get isResizable(){return!0}onceAdded(t){this.annotationElementId||this.parent.addUndoableEditor(this);this._isDraggable=!0;if(this.#ll){this.#ll=!1;this.commit();this.parent.setSelected(this);t&&this.isOnScreen&&this.div.focus()}}remove(){this.#Zo();super.remove()}rebuild(){if(this.parent){super.rebuild();if(null!==this.div){this.#Xo();this.#gl(this.#ol.box);this.isAttachedToDOM||this.parent.add(this)}}}setParent(t){let e=!1;if(this.parent&&!t){this._uiManager.removeShouldRescale(this);this.#Zo()}else if(t){this._uiManager.addShouldRescale(this);this.#Xo(t);e=!this.parent&&this.div?.classList.contains("selectedEditor")}super.setParent(t);e&&this.select()}#Zo(){if(null!==this._drawId&&this.parent){this.parent.drawLayer.remove(this._drawId);this._drawId=null;this._drawingOptions.reset()}}#Xo(t=this.parent){if(null===this._drawId||this.parent!==t)if(null===this._drawId){this._drawingOptions.updateAll();this._drawId=this.#pl(this.#ol,t)}else this.parent.drawLayer.updateParent(this._drawId,t.drawLayer)}#bl([t,e,i,s]){const{parentDimensions:[n,r],rotation:a}=this;switch(a){case 90:return[e,1-t,i*(r/n),s*(n/r)];case 180:return[1-t,1-e,i,s];case 270:return[1-e,t,i*(r/n),s*(n/r)];default:return[t,e,i,s]}}#ml(){const{x:t,y:e,width:i,height:s,parentDimensions:[n,r],rotation:a}=this;switch(a){case 90:return[1-e,t,i*(n/r),s*(r/n)];case 180:return[1-t,1-e,i,s];case 270:return[e,1-t,i*(n/r),s*(r/n)];default:return[t,e,i,s]}}#gl(t){[this.x,this.y,this.width,this.height]=this.#bl(t);if(this.div){this.fixAndSetPosition();this.setDims()}this._onResized()}#fl(){const{x:t,y:e,width:i,height:s,rotation:n,parentRotation:r,parentDimensions:[a,o]}=this;switch((4*n+r)/90){case 1:return[1-e-s,t,s,i];case 2:return[1-t-i,1-e-s,i,s];case 3:return[e,1-t-i,s,i];case 4:return[t,e-i*(a/o),s*(o/a),i*(a/o)];case 5:return[1-e,t,i*(a/o),s*(o/a)];case 6:return[1-t-s*(o/a),1-e,s*(o/a),i*(a/o)];case 7:return[e-i*(a/o),1-t-s*(o/a),i*(a/o),s*(o/a)];case 8:return[t-i,e-s,i,s];case 9:return[1-e,t-i,s,i];case 10:return[1-t,1-e,i,s];case 11:return[e-s,1-t,s,i];case 12:return[t-s*(o/a),e,s*(o/a),i*(a/o)];case 13:return[1-e-i*(a/o),t-s*(o/a),i*(a/o),s*(o/a)];case 14:return[1-t,1-e-i*(a/o),s*(o/a),i*(a/o)];case 15:return[e,1-t,i*(a/o),s*(o/a)];default:return[t,e,i,s]}}rotate(){this.parent&&this.parent.drawLayer.updateProperties(this._drawId,DrawingEditor._mergeSVGProperties({bbox:this.#fl()},this.#ol.updateRotation((this.parentRotation-this.rotation+360)%360)))}onScaleChanging(){this.parent&&this.#gl(this.#ol.updateParentDimensions(this.parentDimensions,this.parent.scale))}static onScaleChangingWhenDrawing(){}render(){if(this.div)return this.div;let t,e;if(this._isCopy){t=this.x;e=this.y}const i=super.render();i.classList.add("draw");const s=document.createElement("div");i.append(s);s.setAttribute("aria-hidden","true");s.className="internal";this.setDims();this._uiManager.addShouldRescale(this);this.disableEditing();this._isCopy&&this._moveAfterPaste(t,e);return i}static createDrawerInstance(t,e,i,s,n){unreachable("Not implemented")}static startDrawing(t,e,i,s){const{target:n,offsetX:r,offsetY:a,pointerId:o,pointerType:l}=s;if(CurrentPointers.isInitializedAndDifferentPointerType(l))return;const{viewport:{rotation:h}}=t,{width:d,height:c}=n.getBoundingClientRect(),u=DrawingEditor.#dl=new AbortController,p=t.combinedSignal(u);CurrentPointers.setPointer(l,o);window.addEventListener("pointerup",t=>{CurrentPointers.isSamePointerIdOrRemove(t.pointerId)&&this._endDraw(t)},{signal:p});window.addEventListener("pointercancel",t=>{CurrentPointers.isSamePointerIdOrRemove(t.pointerId)&&this._currentParent.endDrawingSession()},{signal:p});window.addEventListener("pointerdown",t=>{if(CurrentPointers.isSamePointerType(t.pointerType)){CurrentPointers.initializeAndAddPointerId(t.pointerId);if(DrawingEditor.#hl.isCancellable()){DrawingEditor.#hl.removeLastElement();DrawingEditor.#hl.isEmpty()?this._currentParent.endDrawingSession(!0):this._endDraw(null)}}},{capture:!0,passive:!1,signal:p});window.addEventListener("contextmenu",noContextMenu,{signal:p});n.addEventListener("pointermove",this._drawMove.bind(this),{signal:p});n.addEventListener("touchmove",t=>{CurrentPointers.isSameTimeStamp(t.timeStamp)&&stopEvent(t)},{signal:p});t.toggleDrawing();e._editorUndoBar?.hide();if(DrawingEditor.#hl)t.drawLayer.updateProperties(this._currentDrawId,DrawingEditor.#hl.startNew(r,a,d,c,h));else{e.updateUIForDefaultProperties(this);DrawingEditor.#hl=this.createDrawerInstance(r,a,d,c,h);DrawingEditor.#cl=this.getDefaultDrawingOptions();this._currentParent=t;({id:this._currentDrawId}=t.drawLayer.draw(this._mergeSVGProperties(DrawingEditor.#cl.toSVGProperties(),DrawingEditor.#hl.defaultSVGProperties),!0,!1))}}static _drawMove(t){CurrentPointers.isSameTimeStamp(t.timeStamp);if(!DrawingEditor.#hl)return;const{offsetX:e,offsetY:i,pointerId:s}=t;if(CurrentPointers.isSamePointerId(s))if(CurrentPointers.isUsingMultiplePointers())this._endDraw(t);else{this._currentParent.drawLayer.updateProperties(this._currentDrawId,DrawingEditor.#hl.add(e,i));CurrentPointers.setTimeStamp(t.timeStamp);stopEvent(t)}}static _cleanup(t){if(t){this._currentDrawId=-1;this._currentParent=null;DrawingEditor.#hl=null;DrawingEditor.#cl=null;CurrentPointers.clearTimeStamp()}if(DrawingEditor.#dl){DrawingEditor.#dl.abort();DrawingEditor.#dl=null;CurrentPointers.clearPointerIds()}}static _endDraw(t){const e=this._currentParent;if(e){e.toggleDrawing(!0);this._cleanup(!1);t?.target===e.div&&e.drawLayer.updateProperties(this._currentDrawId,DrawingEditor.#hl.end(t.offsetX,t.offsetY));if(this.supportMultipleDrawings){const t=DrawingEditor.#hl,i=this._currentDrawId,s=t.getLastElement();e.addCommands({cmd:()=>{e.drawLayer.updateProperties(i,t.setLastElement(s))},undo:()=>{e.drawLayer.updateProperties(i,t.removeLastElement())},mustExec:!1,type:f.DRAW_STEP});return}this.endDrawing(!1)}}static endDrawing(t){const e=this._currentParent;if(!e)return null;e.toggleDrawing(!0);e.cleanUndoStack(f.DRAW_STEP);if(!DrawingEditor.#hl.isEmpty()){const{pageDimensions:[i,s],scale:n}=e,r=e.createAndAddNewEditor({offsetX:0,offsetY:0},!1,{drawId:this._currentDrawId,drawOutlines:DrawingEditor.#hl.getOutlines(i*n,s*n,n,this._INNER_MARGIN),drawingOptions:DrawingEditor.#cl,mustBeCommitted:!t});this._cleanup(!0);return r}e.drawLayer.remove(this._currentDrawId);this._cleanup(!0);return null}createDrawingOptions(t){}static deserializeDraw(t,e,i,s,n,r){unreachable("Not implemented")}static async deserialize(t,e,i){const{rawDims:{pageWidth:s,pageHeight:n,pageX:r,pageY:a}}=e.viewport,o=this.deserializeDraw(r,a,s,n,this._INNER_MARGIN,t),l=await super.deserialize(t,e,i);l.createDrawingOptions(t);l.#ul({drawOutlines:o});l.#Xo();l.onScaleChanging();l.rotate();return l}serializeDraw(t){const[e,i]=this.pageTranslation,[s,n]=this.pageDimensions;return this.#ol.serialize([e,i,s,n],t)}renderAnnotationElement(t){t.updateEdited({rect:this.getPDFRect()});return null}static canCreateNewEmptyEditor(){return!1}}class InkDrawOutliner{#so=new Float64Array(6);#Ea;#yl;#In;#ho;#do;#Al="";#vl=0;#Fo=new InkDrawOutline;#wl;#xl;constructor(t,e,i,s,n,r){this.#wl=i;this.#xl=s;this.#In=n;this.#ho=r;[t,e]=this.#Cl(t,e);const a=this.#Ea=[NaN,NaN,NaN,NaN,t,e];this.#do=[t,e];this.#yl=[{line:a,points:this.#do}];this.#so.set(a,0)}updateProperty(t,e){"stroke-width"===t&&(this.#ho=e)}#Cl(t,e){return Outline._normalizePoint(t,e,this.#wl,this.#xl,this.#In)}isEmpty(){return!this.#yl||0===this.#yl.length}isCancellable(){return this.#do.length<=10}add(t,e){[t,e]=this.#Cl(t,e);const[i,s,n,r]=this.#so.subarray(2,6),a=t-n,o=e-r;if(Math.hypot(this.#wl*a,this.#xl*o)<=2)return null;this.#do.push(t,e);if(isNaN(i)){this.#so.set([n,r,t,e],2);this.#Ea.push(NaN,NaN,NaN,NaN,t,e);return{path:{d:this.toSVGPath()}}}isNaN(this.#so[0])&&this.#Ea.splice(6,6);this.#so.set([i,s,n,r,t,e],0);this.#Ea.push(...Outline.createBezierPoints(i,s,n,r,t,e));return{path:{d:this.toSVGPath()}}}end(t,e){const i=this.add(t,e);return i||(2===this.#do.length?{path:{d:this.toSVGPath()}}:null)}startNew(t,e,i,s,n){this.#wl=i;this.#xl=s;this.#In=n;[t,e]=this.#Cl(t,e);const r=this.#Ea=[NaN,NaN,NaN,NaN,t,e];this.#do=[t,e];const a=this.#yl.at(-1);if(a){a.line=new Float32Array(a.line);a.points=new Float32Array(a.points)}this.#yl.push({line:r,points:this.#do});this.#so.set(r,0);this.#vl=0;this.toSVGPath();return null}getLastElement(){return this.#yl.at(-1)}setLastElement(t){if(!this.#yl)return this.#Fo.setLastElement(t);this.#yl.push(t);this.#Ea=t.line;this.#do=t.points;this.#vl=0;return{path:{d:this.toSVGPath()}}}removeLastElement(){if(!this.#yl)return this.#Fo.removeLastElement();this.#yl.pop();this.#Al="";for(let t=0,e=this.#yl.length;tt??NaN),d,c,u,p),points:g(a[t].map(t=>t??NaN),d,c,u,p)});const m=new this.prototype.constructor;m.build(h,i,s,1,o,l,n);return m}#Pl(t=this.#ho){const e=this.#to+t/2*this.#Tl;return this.#In%180==0?[e/this.#wl,e/this.#xl]:[e/this.#xl,e/this.#wl]}#_l(){const[t,e,i,s]=this.#wo,[n,r]=this.#Pl(0);return[t+n,e+r,i-2*n,s-2*r]}#Sl(){const t=this.#wo=i.slice();for(const{line:e}of this.#yl){if(e.length<=12){for(let i=4,s=e.length;it!==e[i])||t.thickness!==i||t.opacity!==s||t.pageIndex!==n}renderAnnotationElement(t){if(this.deleted){t.hide();return null}const{points:e,rect:i}=this.serializeDraw(!1);t.updateEdited({rect:i,thickness:this._drawingOptions["stroke-width"],points:e,popup:this.comment});return null}}class ContourDrawOutline extends InkDrawOutline{toSVGPath(){let t=super.toSVGPath();t.endsWith("Z")||(t+="Z");return t}}class SignatureExtractor{static#Dl={maxDim:512,sigmaSFactor:.02,sigmaR:25,kernelSize:16};static#Ml(t,e,i,s){s-=e;return 0===(i-=t)?s>0?0:4:1===i?s+6:2-s}static#kl=new Int32Array([0,1,-1,1,-1,0,-1,-1,0,-1,1,-1,1,0,1,1]);static#Il(t,e,i,s,n,r,a){const o=this.#Ml(i,s,n,r);for(let n=0;n<8;n++){const r=(-n+o-a+16)%8;if(0!==t[(i+this.#kl[2*r])*e+(s+this.#kl[2*r+1])])return r}return-1}static#Fl(t,e,i,s,n,r,a){const o=this.#Ml(i,s,n,r);for(let n=0;n<8;n++){const r=(n+o+a+16)%8;if(0!==t[(i+this.#kl[2*r])*e+(s+this.#kl[2*r+1])])return r}return-1}static#Bl(t,e,i,s){const n=t.length,r=new Int32Array(n);for(let e=0;e=1&&0===r[s+1])){1!==n&&(a=Math.abs(n));continue}o+=1;d+=1;n>1&&(a=n)}const c=[i,t],u=d===i+1,p={isHole:u,points:c,id:o,parent:0};l.push(p);let g;for(const t of l)if(t.id===a){g=t;break}g?g.isHole?p.parent=u?g.parent:a:p.parent=u?a:g.parent:p.parent=u?a:0;const m=this.#Il(r,e,t,i,h,d,0);if(-1===m){r[s]=-o;1!==r[s]&&(a=Math.abs(r[s]));continue}let f=this.#kl[2*m],b=this.#kl[2*m+1];const y=t+f,A=i+b;h=y;d=A;let v=t,w=i;for(;;){const n=this.#Fl(r,e,v,w,h,d,1);f=this.#kl[2*n];b=this.#kl[2*n+1];const l=v+f,u=w+b;c.push(u,l);const p=v*e+w;0===r[p+1]?r[p]=-o:1===r[p]&&(r[p]=o);if(l===t&&u===i&&v===y&&w===A){1!==r[s]&&(a=Math.abs(r[s]));break}h=v;d=w;v=l;w=u}}}return l}static#Rl(t,e,i,s){if(i-e<=4){for(let n=e;nv){w=s;v=e}}if(v>(l*A)**2){this.#Rl(t,e,w+2,s);this.#Rl(t,w,i,s)}else s.push(n,r)}static#Ol(t){const e=[],i=t.length;this.#Rl(t,0,i,e);e.push(t[i-2],t[i-1]);return e.length<=4?null:e}static#Ll(t,e,i,s,n,r){const a=new Float32Array(r**2),o=-2*s**2,l=r>>1;for(let t=0;t=i))for(let i=0;i=e)continue;const p=t[u*e+s],m=a[o*r+i]*h[Math.abs(p-d)];c+=p*m;g+=m}}p[u[o]=Math.round(c/g)]++}return[u,p]}static#Nl(t){const e=new Uint32Array(256);for(const i of t)e[i]++;return e}static#Ul(t){const e=t.length,i=new Uint8ClampedArray(e>>2);let s=-1/0,n=1/0;for(let e=0,r=i.length;e0!==t);let r=n,a=n;for(e=n;e<256;e++){const n=t[e];if(n>i){if(e-r>s){s=e-r;a=e-1}i=n;r=e}}for(e=a-1;e>=0&&!(t[e]>t[e+1]);e--);return e}static#zl(t){const e=t,{width:i,height:s}=t,{maxDim:n}=this.#Dl;let r=i,a=s;if(i>n||s>n){let o=i,l=s,h=Math.log2(Math.max(i,s)/n);const d=Math.floor(h);h=h===d?d-1:d;for(let i=0;i=-128&&o<=127?Int8Array:a>=-32768&&o<=32767?Int16Array:Int32Array;const h=t.length,d=8+3*h,c=new Uint32Array(d);let u=0;c[u++]=d*Uint32Array.BYTES_PER_ELEMENT+(l-2*h)*r.BYTES_PER_ELEMENT;c[u++]=0;c[u++]=s;c[u++]=n;c[u++]=e?0:1;c[u++]=Math.max(0,Math.floor(i??0));c[u++]=h;c[u++]=r.BYTES_PER_ELEMENT;for(const e of t){c[u++]=e.length-2;c[u++]=e[0];c[u++]=e[1]}const p=new CompressionStream("deflate-raw"),g=p.writable.getWriter();await g.ready;g.write(c);const m=r.prototype.constructor;for(const e of t){const t=new m(e.length-2);for(let i=2,s=e.length;i{await n.ready;await n.close()}).catch(()=>{});let r=null,a=0;for await(const t of i){r||=new Uint8Array(new Uint32Array(t.buffer,0,4)[0]);r.set(t,a);a+=t.length}const o=new Uint32Array(r.buffer,0,r.length>>2),l=o[1];if(0!==l)throw new Error(`Invalid version: ${l}`);const h=o[2],d=o[3],c=0===o[4],u=o[5],p=o[6],g=o[7],m=[],f=(8+3*p)*Uint32Array.BYTES_PER_ELEMENT;let b;switch(g){case Int8Array.BYTES_PER_ELEMENT:b=new Int8Array(r.buffer,f);break;case Int16Array.BYTES_PER_ELEMENT:b=new Int16Array(r.buffer,f);break;case Int32Array.BYTES_PER_ELEMENT:b=new Int32Array(r.buffer,f)}a=0;for(let t=0;t{e?.updateEditSignatureButton(t)})}}getSignaturePreview(){const{newCurves:t,areContours:e,thickness:i,width:s,height:n}=this.#Vl,r=Math.max(s,n);return{areContours:e,outline:SignatureExtractor.processDrawnLines({lines:{curves:t.map(t=>({points:t})),thickness:i,width:s,height:n},pageWidth:r,pageHeight:r,rotation:0,innerMargin:0,mustSmooth:!1,areContours:e}).outline}}get toolbarButtons(){return this._uiManager.signatureManager?[["editSignature",this._uiManager.signatureManager]]:super.toolbarButtons}addSignature(t,e,i,s){const{x:n,y:r}=this,{outline:a}=this.#Vl=t;this.#Gl=a instanceof ContourDrawOutline;this.description=i;let o;if(this.#Gl)o=SignatureEditor.getDefaultDrawingOptions();else{o=SignatureEditor._defaultDrawnSignatureOptions.clone();o.updateProperties({"stroke-width":a.thickness})}this._addOutlines({drawOutlines:a,drawingOptions:o});const[,l]=this.pageDimensions;let h=e/l;h=h>=1?.5:h;this.width*=h/this.height;if(this.width>=1){h*=.9/this.width;this.width=.9}this.height=h;this.setDims();this.x=n;this.y=r;this.center();this._onResized();this.onScaleChanging();this.rotate();this._uiManager.addToAnnotationStorage(this);this.setUuid(s);this._reportTelemetry({action:"pdfjs.signature.inserted",data:{hasBeenSaved:!!s,hasDescription:!!i}});this.div.hidden=!1}getFromImage(t){const{rawDims:{pageWidth:e,pageHeight:i},rotation:s}=this.parent.viewport;return SignatureExtractor.process(t,e,i,s,SignatureEditor._INNER_MARGIN)}getFromText(t,e){const{rawDims:{pageWidth:i,pageHeight:s},rotation:n}=this.parent.viewport;return SignatureExtractor.extractContoursFromText(t,e,i,s,n,SignatureEditor._INNER_MARGIN)}getDrawnSignature(t){const{rawDims:{pageWidth:e,pageHeight:i},rotation:s}=this.parent.viewport;return SignatureExtractor.processDrawnLines({lines:t,pageWidth:e,pageHeight:i,rotation:s,innerMargin:SignatureEditor._INNER_MARGIN,mustSmooth:!1,areContours:!1})}createDrawingOptions({areContours:t,thickness:e}){if(t)this._drawingOptions=SignatureEditor.getDefaultDrawingOptions();else{this._drawingOptions=SignatureEditor._defaultDrawnSignatureOptions.clone();this._drawingOptions.updateProperties({"stroke-width":e})}}serialize(t=!1){if(this.isEmpty())return null;const{lines:e,points:i}=this.serializeDraw(t),{_drawingOptions:{"stroke-width":s}}=this,n=Object.assign(super.serialize(t),{isSignature:!0,areContours:this.#Gl,color:[0,0,0],thickness:this.#Gl?0:s});this.addComment(n);if(t){n.paths={lines:e,points:i};n.uuid=this.#jl;n.isCopy=!0}else n.lines=e;this.#Wl&&(n.accessibilityData={type:"Figure",alt:this.#Wl});return n}static deserializeDraw(t,e,i,s,n,r){return r.areContours?ContourDrawOutline.deserialize(t,e,i,s,n,r):InkDrawOutline.deserialize(t,e,i,s,n,r)}static async deserialize(t,e,i){const s=await super.deserialize(t,e,i);s.#Gl=t.areContours;s.description=t.accessibilityData?.alt||"";s.#jl=t.uuid;return s}}class StampEditor extends AnnotationEditor{#$l=null;#Xl=null;#ql=null;#Kl=null;#Yl=null;#Ql="";#Jl=null;#Zl=!1;#th=null;#eh=!1;#ih=!1;static _type="stamp";static _editorType=m.STAMP;constructor(t){super({...t,name:"stampEditor"});this.#Kl=t.bitmapUrl;this.#Yl=t.bitmapFile;this.defaultL10nId="pdfjs-editor-stamp-editor"}static initialize(t,e){AnnotationEditor.initialize(t,e)}static isHandlingMimeForPasting(t){return j.includes(t)}static paste(t,e){e.pasteEditor({mode:m.STAMP},{bitmapFile:t.getAsFile()})}altTextFinish(){this._uiManager.useNewAltTextFlow&&(this.div.hidden=!1);super.altTextFinish()}get telemetryFinalData(){return{type:"stamp",hasAltText:!!this.altTextData?.altText}}static computeTelemetryFinalData(t){const e=t.get("hasAltText");return{hasAltText:e.get(!0)??0,hasNoAltText:e.get(!1)??0}}#sh(t,e=!1){if(t){this.#$l=t.bitmap;if(!e){this.#Xl=t.id;this.#eh=t.isSvg}t.file&&(this.#Ql=t.file.name);this.#nh()}else this.remove()}#rh(){this.#ql=null;this._uiManager.enableWaiting(!1);if(this.#Jl)if(this._uiManager.useNewAltTextWhenAddingImage&&this._uiManager.useNewAltTextFlow&&this.#$l)this.addEditToolbar().then(()=>{this._editToolbar.hide();this._uiManager.editAltText(this,!0)});else{if(!this._uiManager.useNewAltTextWhenAddingImage&&this._uiManager.useNewAltTextFlow&&this.#$l){this._reportTelemetry({action:"pdfjs.image.image_added",data:{alt_text_modal:!1,alt_text_type:"empty"}});try{this.mlGuessAltText()}catch{}}this.div.focus()}}async mlGuessAltText(t=null,e=!0){if(this.hasAltTextData())return null;const{mlManager:i}=this._uiManager;if(!i)throw new Error("No ML.");if(!await i.isEnabledFor("altText"))throw new Error("ML isn't enabled for alt text.");const{data:s,width:n,height:r}=t||this.copyCanvas(null,null,!0).imageData,a=await i.guess({name:"altText",request:{data:s,width:n,height:r,channels:s.length/(n*r)}});if(!a)throw new Error("No response from the AI service.");if(a.error)throw new Error("Error from the AI service.");if(a.cancel)return null;if(!a.output)throw new Error("No valid response from the AI service.");const o=a.output;await this.setGuessedAltText(o);e&&!this.hasAltTextData()&&(this.altTextData={alt:o,decorative:!1});return o}#ah(){if(this.#Xl){this._uiManager.enableWaiting(!0);this._uiManager.imageManager.getFromId(this.#Xl).then(t=>this.#sh(t,!0)).finally(()=>this.#rh());return}if(this.#Kl){const t=this.#Kl;this.#Kl=null;this._uiManager.enableWaiting(!0);this.#ql=this._uiManager.imageManager.getFromUrl(t).then(t=>this.#sh(t)).finally(()=>this.#rh());return}if(this.#Yl){const t=this.#Yl;this.#Yl=null;this._uiManager.enableWaiting(!0);this.#ql=this._uiManager.imageManager.getFromFile(t).then(t=>this.#sh(t)).finally(()=>this.#rh());return}const t=document.createElement("input");t.type="file";t.accept=j.join(",");const e=this._uiManager._signal;this.#ql=new Promise(i=>{t.addEventListener("change",async()=>{if(t.files&&0!==t.files.length){this._uiManager.enableWaiting(!0);const e=await this._uiManager.imageManager.getFromFile(t.files[0]);this._reportTelemetry({action:"pdfjs.image.image_selected",data:{alt_text_modal:this._uiManager.useNewAltTextFlow}});this.#sh(e)}else this.remove();i()},{signal:e});t.addEventListener("cancel",()=>{this.remove();i()},{signal:e})}).finally(()=>this.#rh());t.click()}remove(){if(this.#Xl){this.#$l=null;this._uiManager.imageManager.deleteId(this.#Xl);this.#Jl?.remove();this.#Jl=null;if(this.#th){clearTimeout(this.#th);this.#th=null}}super.remove()}rebuild(){if(this.parent){super.rebuild();if(null!==this.div){this.#Xl&&null===this.#Jl&&this.#ah();this.isAttachedToDOM||this.parent.add(this)}}else this.#Xl&&this.#ah()}onceAdded(t){this._isDraggable=!0;t&&this.div.focus()}isEmpty(){return!(this.#ql||this.#$l||this.#Kl||this.#Yl||this.#Xl||this.#Zl)}get toolbarButtons(){return[["altText",this.createAltText()]]}get isResizable(){return!0}render(){if(this.div)return this.div;let t,e;if(this._isCopy){t=this.x;e=this.y}super.render();this.div.hidden=!0;this.createAltText();this.#Zl||(this.#$l?this.#nh():this.#ah());this._isCopy&&this._moveAfterPaste(t,e);this._uiManager.addShouldRescale(this);return this.div}setCanvas(t,e){const{id:i,bitmap:s}=this._uiManager.imageManager.getFromCanvas(t,e);e.remove();if(i&&this._uiManager.imageManager.isValidId(i)){this.#Xl=i;s&&(this.#$l=s);this.#Zl=!1;this.#nh()}}_onResized(){this.onScaleChanging()}onScaleChanging(){if(!this.parent)return;null!==this.#th&&clearTimeout(this.#th);this.#th=setTimeout(()=>{this.#th=null;this.#oh()},200)}#nh(){const{div:t}=this;let{width:e,height:i}=this.#$l;const[s,n]=this.pageDimensions,r=.75;if(this.width){e=this.width*s;i=this.height*n}else if(e>r*s||i>r*n){const t=Math.min(r*s/e,r*n/i);e*=t;i*=t}this._uiManager.enableWaiting(!1);const a=this.#Jl=document.createElement("canvas");a.setAttribute("role","img");this.addContainer(a);this.width=e/s;this.height=i/n;this.setDims();this._initialOptions?.isCentered?this.center():this.fixAndSetPosition();this._initialOptions=null;this._uiManager.useNewAltTextWhenAddingImage&&this._uiManager.useNewAltTextFlow&&!this.annotationElementId||(t.hidden=!1);this.#oh();if(!this.#ih){this.parent.addUndoableEditor(this);this.#ih=!0}this._reportTelemetry({action:"inserted_image"});this.#Ql&&this.div.setAttribute("aria-description",this.#Ql);this.annotationElementId||this._uiManager.a11yAlert("pdfjs-editor-stamp-added-alert")}copyCanvas(t,e,i=!1){t||(t=224);const{width:s,height:n}=this.#$l,r=new OutputScale;let a=this.#$l,o=s,l=n,h=null;if(e){if(s>e||n>e){const t=Math.min(e/s,e/n);o=Math.floor(s*t);l=Math.floor(n*t)}h=document.createElement("canvas");const t=h.width=Math.ceil(o*r.sx),i=h.height=Math.ceil(l*r.sy);this.#eh||(a=this.#lh(t,i));const d=h.getContext("2d");d.filter=this._uiManager.hcmFilter;let c="white",u="#cfcfd8";if("none"!==this._uiManager.hcmFilter)u="black";else if(ColorScheme.isDarkMode){c="#8f8f9d";u="#42414d"}const p=15,g=p*r.sx,m=p*r.sy,f=new OffscreenCanvas(2*g,2*m),b=f.getContext("2d");b.fillStyle=c;b.fillRect(0,0,2*g,2*m);b.fillStyle=u;b.fillRect(0,0,g,m);b.fillRect(g,m,g,m);d.fillStyle=d.createPattern(f,"repeat");d.fillRect(0,0,t,i);d.drawImage(a,0,0,a.width,a.height,0,0,t,i)}let d=null;if(i){let e,i;if(r.symmetric&&a.widtht||n>t){const r=Math.min(t/s,t/n);e=Math.floor(s*r);i=Math.floor(n*r);this.#eh||(a=this.#lh(e,i))}}const o=new OffscreenCanvas(e,i).getContext("2d",{willReadFrequently:!0});o.drawImage(a,0,0,a.width,a.height,0,0,e,i);d={width:e,height:i,data:o.getImageData(0,0,e,i).data}}return{canvas:h,width:o,height:l,imageData:d}}#lh(t,e){const{width:i,height:s}=this.#$l;let n=i,r=s,a=this.#$l;for(;n>2*t||r>2*e;){const i=n,s=r;n>2*t&&(n=Math.ceil(n/2));r>2*e&&(r=Math.ceil(r/2));const o=new OffscreenCanvas(n,r);o.getContext("2d").drawImage(a,0,0,i,s,0,0,n,r);a=o.transferToImageBitmap()}return a}#oh(){const[t,e]=this.parentDimensions,{width:i,height:s}=this,n=new OutputScale,r=Math.ceil(i*t*n.sx),a=Math.ceil(s*e*n.sy),o=this.#Jl;if(!o||o.width===r&&o.height===a)return;o.width=r;o.height=a;const l=this.#eh?this.#$l:this.#lh(r,a),h=o.getContext("2d");h.filter=this._uiManager.hcmFilter;h.drawImage(l,0,0,l.width,l.height,0,0,r,a)}#hh(t){if(t){if(this.#eh){const t=this._uiManager.imageManager.getSvgUrl(this.#Xl);if(t)return t}const t=document.createElement("canvas");({width:t.width,height:t.height}=this.#$l);t.getContext("2d").drawImage(this.#$l,0,0);return t.toDataURL()}if(this.#eh){const[t,e]=this.pageDimensions,i=Math.round(this.width*t*PixelsPerInch.PDF_TO_CSS_UNITS),s=Math.round(this.height*e*PixelsPerInch.PDF_TO_CSS_UNITS),n=new OffscreenCanvas(i,s);n.getContext("2d").drawImage(this.#$l,0,0,this.#$l.width,this.#$l.height,0,0,i,s);return n.transferToImageBitmap()}return structuredClone(this.#$l)}static async deserialize(t,e,i){let s=null,n=!1;if(t instanceof StampAnnotationElement){const{data:{rect:r,rotation:a,id:o,structParent:l,popupRef:h,richText:d,contentsObj:c,creationDate:u,modificationDate:p},container:g,parent:{page:{pageNumber:f}},canvas:b}=t;let y,A;if(b){delete t.canvas;({id:y,bitmap:A}=i.imageManager.getFromCanvas(g.id,b));b.remove()}else{n=!0;t._hasNoCanvas=!0}const v=(await e._structTree.getAriaAttributes(`${W}${o}`))?.get("aria-label")||"";s=t={annotationType:m.STAMP,bitmapId:y,bitmap:A,pageIndex:f-1,rect:r.slice(0),rotation:a,annotationElementId:o,id:o,deleted:!1,accessibilityData:{decorative:!1,altText:v},isSvg:!1,structParent:l,popupRef:h,richText:d,comment:c?.str||null,creationDate:u,modificationDate:p}}const r=await super.deserialize(t,e,i),{rect:a,bitmap:o,bitmapUrl:l,bitmapId:h,isSvg:d,accessibilityData:c}=t;if(n){i.addMissingCanvas(t.id,r);r.#Zl=!0}else if(h&&i.imageManager.isValidId(h)){r.#Xl=h;o&&(r.#$l=o)}else r.#Kl=l;r.#eh=d;const[u,p]=r.pageDimensions;r.width=(a[2]-a[0])/u;r.height=(a[3]-a[1])/p;c&&(r.altTextData=c);r._initialData=s;t.comment&&r.setCommentData(t);r.#ih=!!s;return r}serialize(t=!1,e=null){if(this.isEmpty())return null;if(this.deleted)return this.serializeDeleted();const i=Object.assign(super.serialize(t),{bitmapId:this.#Xl,isSvg:this.#eh});this.addComment(i);if(t){i.bitmapUrl=this.#hh(!0);i.accessibilityData=this.serializeAltText(!0);i.isCopy=!0;return i}const{decorative:s,altText:n}=this.serializeAltText(!1);!s&&n&&(i.accessibilityData={type:"Figure",alt:n});if(this.annotationElementId){const t=this.#Qa(i);if(t.isSame)return null;t.isSameAltText?delete i.accessibilityData:i.accessibilityData.structParent=this._initialData.structParent??-1;i.id=this.annotationElementId;delete i.bitmapId;return i}if(null===e)return i;e.stamps||=new Map;const r=this.#eh?(i.rect[2]-i.rect[0])*(i.rect[3]-i.rect[1]):null;if(e.stamps.has(this.#Xl)){if(this.#eh){const t=e.stamps.get(this.#Xl);if(r>t.area){t.area=r;t.serialized.bitmap.close();t.serialized.bitmap=this.#hh(!1)}}}else{e.stamps.set(this.#Xl,{area:r,serialized:i});i.bitmap=this.#hh(!1)}return i}#Qa(t){const{pageIndex:e,accessibilityData:{altText:i}}=this._initialData,s=t.pageIndex===e,n=(t.accessibilityData?.alt||"")===i;return{isSame:!this.hasEditedComment&&!this._hasBeenMoved&&!this._hasBeenResized&&s&&n,isSameAltText:n}}renderAnnotationElement(t){if(this.deleted){t.hide();return null}t.updateEdited({rect:this.getPDFRect(),popup:this.comment});return null}}class AnnotationEditorLayer{#Fa;#dh=!1;#ch=null;#uh=null;#ph=null;#gh=new Map;#mh=!1;#fh=!1;#bh=!1;#yh=null;#Ah=null;#vh=null;#wh=null;#xh=null;#Ch=-1;#y;static _initialized=!1;static#Q=new Map([FreeTextEditor,InkEditor,StampEditor,HighlightEditor,SignatureEditor].map(t=>[t._editorType,t]));constructor({uiManager:t,pageIndex:e,div:i,structTreeLayer:s,accessibilityManager:n,annotationLayer:r,drawLayer:a,textLayer:o,viewport:l,l10n:h}){const d=[...AnnotationEditorLayer.#Q.values()];if(!AnnotationEditorLayer._initialized){AnnotationEditorLayer._initialized=!0;for(const e of d)e.initialize(h,t)}t.registerEditorTypes(d);this.#y=t;this.pageIndex=e;this.div=i;this.#Fa=n;this.#ch=r;this.viewport=l;this.#vh=o;this.drawLayer=a;this._structTree=s;this.#y.addLayer(this)}get isEmpty(){return 0===this.#gh.size}get isInvisible(){return this.isEmpty&&this.#y.getMode()===m.NONE}updateToolbar(t){this.#y.updateToolbar(t)}updateMode(t=this.#y.getMode()){this.#Eh();switch(t){case m.NONE:this.div.classList.toggle("nonEditing",!0);this.disableTextSelection();this.togglePointerEvents(!1);this.toggleAnnotationLayerPointerEvents(!0);this.disableClick();return;case m.INK:this.disableTextSelection();this.togglePointerEvents(!0);this.enableClick();break;case m.HIGHLIGHT:this.enableTextSelection();this.togglePointerEvents(!1);this.disableClick();break;default:this.disableTextSelection();this.togglePointerEvents(!0);this.enableClick()}this.toggleAnnotationLayerPointerEvents(!1);const{classList:e}=this.div;e.toggle("nonEditing",!1);if(t===m.POPUP)e.toggle("commentEditing",!0);else{e.toggle("commentEditing",!1);for(const i of AnnotationEditorLayer.#Q.values())e.toggle(`${i._type}Editing`,t===i._editorType)}this.div.hidden=!1}hasTextLayer(t){return t===this.#vh?.div}setEditingState(t){this.#y.setEditingState(t)}addCommands(t){this.#y.addCommands(t)}cleanUndoStack(t){this.#y.cleanUndoStack(t)}toggleDrawing(t=!1){this.div.classList.toggle("drawing",!t)}togglePointerEvents(t=!1){this.div.classList.toggle("disabled",!t)}toggleAnnotationLayerPointerEvents(t=!1){this.#ch?.togglePointerEvents(t)}get#Th(){return 0!==this.#gh.size?this.#gh.values():this.#y.getEditors(this.pageIndex)}async enable(){this.#bh=!0;this.div.tabIndex=0;this.togglePointerEvents(!0);this.div.classList.toggle("nonEditing",!1);this.#xh?.abort();this.#xh=null;const t=new Set;for(const e of this.#Th){e.enableEditing();e.show(!0);if(e.annotationElementId){this.#y.removeChangedExistingAnnotation(e);t.add(e.annotationElementId)}}const e=this.#ch;if(e)for(const i of e.getEditableAnnotations()){i.hide();if(this.#y.isDeletedAnnotationElement(i.data.id))continue;if(t.has(i.data.id))continue;const e=await this.deserialize(i);if(e){this.addOrRebuild(e);e.enableEditing()}}this.#bh=!1;this.#y._eventBus.dispatch("editorsrendered",{source:this,pageNumber:this.pageIndex+1})}disable(){this.#fh=!0;this.div.tabIndex=-1;this.togglePointerEvents(!1);this.div.classList.toggle("nonEditing",!0);if(this.#vh&&!this.#xh){this.#xh=new AbortController;const t=this.#y.combinedSignal(this.#xh);this.#vh.div.addEventListener("pointerdown",t=>{const{clientX:e,clientY:i,timeStamp:s}=t;if(s-this.#Ch>500){this.#Ch=s;return}this.#Ch=-1;const{classList:n}=this.div;n.toggle("getElements",!0);const r=document.elementsFromPoint(e,i);n.toggle("getElements",!1);if(!this.div.contains(r[0]))return;let a;const o=new RegExp(`^${g}[0-9]+$`);for(const t of r)if(o.test(t.id)){a=t.id;break}if(!a)return;const l=this.#gh.get(a);if(null===l?.annotationElementId){stopEvent(t);l.dblclick(t)}},{signal:t,capture:!0})}const t=this.#ch,e=[];if(t){const i=new Map,s=new Map;for(const t of this.#Th){t.disableEditing();if(t.annotationElementId)if(null===t.serialize()){s.set(t.annotationElementId,t);this.getEditableAnnotation(t.annotationElementId)?.show();t.remove()}else i.set(t.annotationElementId,t);else e.push(t)}for(const e of t.getEditableAnnotations()){const{id:t}=e.data;if(this.#y.isDeletedAnnotationElement(t)){e.updateEdited({deleted:!0});continue}let n=s.get(t);if(n){n.resetAnnotationElement(e);n.show(!1);e.show()}else{n=i.get(t);if(n){this.#y.addChangedExistingAnnotation(n);n.renderAnnotationElement(e)&&n.show(!1)}e.show()}}}this.#Eh();this.isEmpty&&(this.div.hidden=!0);const{classList:i}=this.div;for(const t of AnnotationEditorLayer.#Q.values())i.remove(`${t._type}Editing`);this.disableTextSelection();this.toggleAnnotationLayerPointerEvents(!0);t?.updateFakeAnnotations(e);this.#fh=!1}getEditableAnnotation(t){return this.#ch?.getEditableAnnotation(t)||null}setActiveEditor(t){this.#y.getActive()!==t&&this.#y.setActiveEditor(t)}enableTextSelection(){this.div.tabIndex=-1;if(this.#vh?.div&&!this.#wh){this.#wh=new AbortController;const t=this.#y.combinedSignal(this.#wh);this.#vh.div.addEventListener("pointerdown",this.#Sh.bind(this),{signal:t});this.#vh.div.classList.add("highlighting")}}disableTextSelection(){this.div.tabIndex=0;if(this.#vh?.div&&this.#wh){this.#wh.abort();this.#wh=null;this.#vh.div.classList.remove("highlighting")}}#Sh(t){this.#y.unselectAll();const{target:e}=t;if(e===this.#vh.div||("img"===e.getAttribute("role")||e.classList.contains("endOfContent")||e.classList.contains("textLayerImagePlaceholder"))&&this.#vh.div.contains(e)){const{isMac:e}=FeatureTest.platform;if(0!==t.button||t.ctrlKey&&e)return;this.#y.showAllEditors("highlight",!0,!0);this.#vh.div.classList.add("free");this.toggleDrawing();HighlightEditor.startHighlighting(this,"ltr"===this.#y.direction,{target:this.#vh.div,x:t.x,y:t.y});this.#vh.div.addEventListener("pointerup",()=>{this.#vh.div.classList.remove("free");this.toggleDrawing(!0)},{once:!0,signal:this.#y._signal});t.preventDefault()}}enableClick(){if(this.#uh)return;this.#uh=new AbortController;const t=this.#y.combinedSignal(this.#uh);this.div.addEventListener("pointerdown",this.pointerdown.bind(this),{signal:t});const e=this.pointerup.bind(this);this.div.addEventListener("pointerup",e,{signal:t});this.div.addEventListener("pointercancel",e,{signal:t})}disableClick(){this.#uh?.abort();this.#uh=null}attach(t){this.#gh.set(t.id,t);const{annotationElementId:e}=t;e&&this.#y.isDeletedAnnotationElement(e)&&this.#y.removeDeletedAnnotationElement(t)}detach(t){this.#gh.delete(t.id);this.#Fa?.removePointerInTextLayer(t.contentDiv);!this.#fh&&t.annotationElementId&&this.#y.addDeletedAnnotationElement(t)}remove(t){this.detach(t);this.#y.removeEditor(t);t.div.remove();t.isAttachedToDOM=!1}changeParent(t){if(t.parent!==this){if(t.parent&&t.annotationElementId){this.#y.addDeletedAnnotationElement(t);AnnotationEditor.deleteAnnotationElement(t);t.annotationElementId=null}this.attach(t);t.parent?.detach(t);t.setParent(this);if(t.div&&t.isAttachedToDOM){t.div.remove();this.div.append(t.div)}}}add(t){if(t.parent!==this||!t.isAttachedToDOM){this.changeParent(t);this.#y.addEditor(t);this.attach(t);if(!t.isAttachedToDOM){const e=t.render();this.div.append(e);t.isAttachedToDOM=!0}t.fixAndSetPosition();t.onceAdded(!this.#bh);this.#y.addToAnnotationStorage(t);t._reportTelemetry(t.telemetryInitialData)}}moveEditorInDOM(t){if(!t.isAttachedToDOM)return;const{activeElement:e}=document;if(t.div.contains(e)&&!this.#ph){t._focusEventsAllowed=!1;this.#ph=setTimeout(()=>{this.#ph=null;if(t.div.contains(document.activeElement))t._focusEventsAllowed=!0;else{t.div.addEventListener("focusin",()=>{t._focusEventsAllowed=!0},{once:!0,signal:this.#y._signal});e.focus()}},0)}t._structTreeParentId=this.#Fa?.moveElementInDOM(this.div,t.div,t.contentDiv,!0)}addOrRebuild(t){if(t.needsToBeRebuilt()){t.parent||=this;t.rebuild();t.show()}else this.add(t)}addUndoableEditor(t){this.addCommands({cmd:()=>t._uiManager.rebuild(t),undo:()=>{t.remove()},mustExec:!1})}getEditorByUID(t){for(const e of this.#gh.values())if(e.uid===t)return e;return null}get#_h(){return AnnotationEditorLayer.#Q.get(this.#y.getMode())}combinedSignal(t){return this.#y.combinedSignal(t)}#Ph(t){const e=this.#_h;return e?new e.prototype.constructor(t):null}canCreateNewEmptyEditor(){return this.#_h?.canCreateNewEmptyEditor()}async pasteEditor(t,e){this.updateToolbar(t);await this.#y.updateMode(t.mode);const{offsetX:i,offsetY:s}=this.#Dh(),n=this.#y.getId(),r=this.#Ph({parent:this,id:n,x:i,y:s,uiManager:this.#y,isCentered:!0,...e});r&&this.add(r)}async deserialize(t){return await(AnnotationEditorLayer.#Q.get(t.annotationType??t.annotationEditorType)?.deserialize(t,this,this.#y))||null}createAndAddNewEditor(t,e,i={}){const s=this.#y.getId(),n=this.#Ph({parent:this,id:s,x:t.offsetX,y:t.offsetY,uiManager:this.#y,isCentered:e,...i});n&&this.add(n);return n}get boundingClientRect(){return this.div.getBoundingClientRect()}#Dh(){const{x:t,y:e,width:i,height:s}=this.boundingClientRect,n=Math.max(0,t),r=Math.max(0,e),a=(n+Math.min(window.innerWidth,t+i))/2-t,o=(r+Math.min(window.innerHeight,e+s))/2-e,[l,h]=this.viewport.rotation%180==0?[a,o]:[o,a];return{offsetX:l,offsetY:h}}addNewEditor(t={}){this.createAndAddNewEditor(this.#Dh(),!0,t)}setSelected(t){this.#y.setSelected(t)}toggleSelected(t){this.#y.toggleSelected(t)}unselect(t){this.#y.unselect(t)}pointerup(t){const{isMac:e}=FeatureTest.platform;if(0!==t.button||t.ctrlKey&&e)return;if(t.target!==this.div)return;if(!this.#mh)return;this.#mh=!1;if(this.#_h?.isDrawer&&this.#_h.supportMultipleDrawings)return;if(!this.#dh){this.#dh=!0;return}const i=this.#y.getMode();i!==m.STAMP&&i!==m.POPUP&&i!==m.SIGNATURE?this.createAndAddNewEditor(t,!1):this.#y.unselectAll()}pointerdown(t){this.#y.getMode()===m.HIGHLIGHT&&this.enableTextSelection();if(this.#mh){this.#mh=!1;return}const{isMac:e}=FeatureTest.platform;if(0!==t.button||t.ctrlKey&&e)return;if(t.target!==this.div)return;this.#mh=!0;if(this.#_h?.isDrawer){this.startDrawingSession(t);return}const i=this.#y.getActive();this.#dh=!i||i.isEmpty()}startDrawingSession(t){this.div.focus({preventScroll:!0});if(this.#yh){this.#_h.startDrawing(this,this.#y,!1,t);return}this.#y.setCurrentDrawingSession(this);this.#yh=new AbortController;const e=this.#y.combinedSignal(this.#yh);this.div.addEventListener("blur",({relatedTarget:t})=>{if(t&&!this.div.contains(t)){this.#Ah=null;this.commitOrRemove()}},{signal:e});this.#_h.startDrawing(this,this.#y,!1,t)}pause(t){if(t){const{activeElement:t}=document;this.div.contains(t)&&(this.#Ah=t);return}this.#Ah&&setTimeout(()=>{this.#Ah?.focus();this.#Ah=null},0)}endDrawingSession(t=!1){if(!this.#yh)return null;this.#y.setCurrentDrawingSession(null);this.#yh.abort();this.#yh=null;this.#Ah=null;return this.#_h.endDrawing(t)}findNewParent(t,e,i){const s=this.#y.findParent(e,i);if(null===s||s===this)return!1;s.changeParent(t);return!0}commitOrRemove(){if(this.#yh){this.endDrawingSession();return!0}return!1}onScaleChanging(){this.#yh&&this.#_h.onScaleChangingWhenDrawing(this)}destroy(){this.commitOrRemove();if(this.#y.getActive()?.parent===this){this.#y.commitOrRemove();this.#y.setActiveEditor(null)}if(this.#ph){clearTimeout(this.#ph);this.#ph=null}for(const t of this.#gh.values()){this.#Fa?.removePointerInTextLayer(t.contentDiv);t.setParent(null);t.isAttachedToDOM=!1;t.div.remove()}this.div=null;this.#gh.clear();this.#y.removeLayer(this)}#Eh(){for(const t of this.#gh.values())t.isEmpty()&&t.remove()}async render({viewport:t}){this.viewport=t;setLayerDimensions(this.div,t);for(const t of this.#y.getEditors(this.pageIndex)){this.add(t);t.rebuild()}await this.#y.findClonesForPage(this);this.div.hidden=this.isEmpty;this.updateMode()}update({viewport:t}){this.#y.commitOrRemove();this.#Eh();const e=this.viewport.rotation,i=t.rotation;this.viewport=t;setLayerDimensions(this.div,{rotation:i});if(e!==i)for(const t of this.#gh.values())t.rotate(i)}get pageDimensions(){const{pageWidth:t,pageHeight:e}=this.viewport.rawDims;return[t,e]}get scale(){return this.#y.viewParameters.realScale}}class DrawLayer{#sa=null;#Mh=new Map;#kh=new Map;static#S=0;setParent(t){if(this.#sa){if(this.#sa!==t){if(this.#Mh.size>0)for(const e of this.#Mh.values()){e.remove();t.append(e)}this.#sa=t}}else this.#sa=t}static get _svgFactory(){return shadow(this,"_svgFactory",new DOMSVGFactory)}static#Ih(t,[e,i,s,n]){const{style:r}=t;r.top=100*i+"%";r.left=100*e+"%";r.width=100*s+"%";r.height=100*n+"%"}#Fh(){const t=DrawLayer._svgFactory.create(1,1,!0);this.#sa.append(t);t.setAttribute("aria-hidden",!0);return t}#Bh(t,e){const i=DrawLayer._svgFactory.createElement("clipPath");t.append(i);const s=`clip_${e}`;i.setAttribute("id",s);i.setAttribute("clipPathUnits","objectBoundingBox");const n=DrawLayer._svgFactory.createElement("use");i.append(n);n.setAttribute("href",`#${e}`);n.classList.add("clip");return s}#Rh(t,e){for(const[i,s]of Object.entries(e))null===s?t.removeAttribute(i):t.setAttribute(i,s)}draw(t,e=!1,i=!1){const s=DrawLayer.#S++,n=this.#Fh(),r=DrawLayer._svgFactory.createElement("defs");n.append(r);const a=DrawLayer._svgFactory.createElement("path");r.append(a);const o=`path_${s}`;a.setAttribute("id",o);a.setAttribute("vector-effect","non-scaling-stroke");e&&this.#kh.set(s,a);const l=i?this.#Bh(r,o):null,h=DrawLayer._svgFactory.createElement("use");n.append(h);h.setAttribute("href",`#${o}`);this.updateProperties(n,t);this.#Mh.set(s,n);return{id:s,clipPathId:`url(#${l})`}}drawOutline(t,e){const i=DrawLayer.#S++,s=this.#Fh(),n=DrawLayer._svgFactory.createElement("defs");s.append(n);const r=DrawLayer._svgFactory.createElement("path");n.append(r);const a=`path_${i}`;r.setAttribute("id",a);r.setAttribute("vector-effect","non-scaling-stroke");let o;if(e){const t=DrawLayer._svgFactory.createElement("mask");n.append(t);o=`mask_${i}`;t.setAttribute("id",o);t.setAttribute("maskUnits","objectBoundingBox");const e=DrawLayer._svgFactory.createElement("rect");t.append(e);e.setAttribute("width","1");e.setAttribute("height","1");e.setAttribute("fill","white");const s=DrawLayer._svgFactory.createElement("use");t.append(s);s.setAttribute("href",`#${a}`);s.setAttribute("stroke","none");s.setAttribute("fill","black");s.setAttribute("fill-rule","nonzero");s.classList.add("mask")}const l=DrawLayer._svgFactory.createElement("use");s.append(l);l.setAttribute("href",`#${a}`);o&&l.setAttribute("mask",`url(#${o})`);const h=l.cloneNode();s.append(h);l.classList.add("mainOutline");h.classList.add("secondaryOutline");this.updateProperties(s,t);this.#Mh.set(i,s);return i}finalizeDraw(t,e){this.#kh.delete(t);this.updateProperties(t,e)}updateProperties(t,e){if(!e)return;const{root:i,bbox:s,rootClass:n,path:r}=e,a="number"==typeof t?this.#Mh.get(t):t;if(a){i&&this.#Rh(a,i);s&&DrawLayer.#Ih(a,s);if(n){const{classList:t}=a;for(const[e,i]of Object.entries(n))t.toggle(e,i)}if(r){const t=a.firstElementChild.firstElementChild;this.#Rh(t,r)}}}updateParent(t,e){if(e===this)return;const i=this.#Mh.get(t);if(i){e.#sa.append(i);this.#Mh.delete(t);e.#Mh.set(t,i)}}remove(t){this.#kh.delete(t);if(null!==this.#sa){this.#Mh.get(t).remove();this.#Mh.delete(t)}}destroy(){this.#sa=null;for(const t of this.#Mh.values())t.remove();this.#Mh.clear();this.#kh.clear()}}function percentage(t){return`${(100*t).toFixed(2)}%`}class TextLayerImages{#Oh=[];#Lh=new Map;#Nh=null;#Uh=0;#Dn=0;#Pn=0;static#Hh=null;constructor(t,e,i,s){this.#Uh=t;this.#Oh=e;this.#Dn=i.rawDims.pageWidth;this.#Pn=i.rawDims.pageHeight;this.#Nh=s}render(){const t=document.createElement("div");t.className="textLayerImages";for(let e=0;e{if(!(t.target instanceof HTMLCanvasElement))return;const e=t.target,i=this.#Lh.get(e);if(!i)return;const s=TextLayerImages.#Hh?.deref();if(s===e)return;if(s){s.width=0;s.height=0}TextLayerImages.#Hh=new WeakRef(e);const{inverseTransform:n,x1:r,y1:a,width:o,height:l}=i,h=this.#Nh(),d=Math.ceil(r*h.width),c=Math.ceil(a*h.height),u=Math.floor((r+o/this.#Dn)*h.width),p=Math.floor((a+l/this.#Pn)*h.height);e.width=u-d;e.height=p-c;const g=e.getContext("2d");g.setTransform(...n);g.translate(-d,-c);g.drawImage(h,0,0)});return t}#zh([t,e,i,s,n,r]){const a=Math.hypot((n-t)*this.#Dn,(r-e)*this.#Pn),o=Math.hypot((i-t)*this.#Dn,(s-e)*this.#Pn);if(a=ce&&console.info(`Info: ${e}`)}function warn(e){ta>=fe&&console.warn(`Warning: ${e}`)}function unreachable(e){throw new Error(e)}function assert(e,t){e||unreachable(t)}function createValidAbsoluteUrl(e,t=null,a=null){if(!e)return null;if(a&&"string"==typeof e){if(a.addDefaultProtocol&&e.startsWith("www.")){const t=e.match(/\./g);t?.length>=2&&(e=`http://${e}`)}if(a.tryConvertEncoding)try{e=stringToUTF8String(e)}catch{}}const n=t?URL.parse(e,t):URL.parse(e);return function _isValidProtocol(e){switch(e?.protocol){case"http:":case"https:":case"ftp:":case"mailto:":case"tel:":return!0;default:return!1}}(n)?n:null}function shadow(e,t,a,n=!1){Object.defineProperty(e,t,{value:a,enumerable:!n,configurable:!0,writable:!1});return a}const aa=function BaseExceptionClosure(){function BaseException(e,t){this.message=e;this.name=t}BaseException.prototype=new Error;BaseException.constructor=BaseException;return BaseException}();class PasswordException extends aa{constructor(e,t){super(e,"PasswordException");this.code=t}}class UnknownErrorException extends aa{constructor(e,t){super(e,"UnknownErrorException");this.details=t}}class InvalidPDFException extends aa{constructor(e){super(e,"InvalidPDFException")}}class ResponseException extends aa{constructor(e,t,a){super(e,"ResponseException");this.status=t;this.missing=a}}class FormatError extends aa{constructor(e){super(e,"FormatError")}}class AbortException extends aa{constructor(e){super(e,"AbortException")}}function bytesToString(e){"object"==typeof e&&void 0!==e?.length||unreachable("Invalid argument for bytesToString");const t=e.length,a=8192;if(t{if("undefined"==typeof document)return!1;const e=document.createElement("input");e.type="color";e.setAttribute("alpha","");e.value="#ff000080";return"#ff0000"!==e.value})())}}class Util{static get hexNums(){return shadow(this,"hexNums",Array.from(Array(256).keys(),e=>e.toString(16).padStart(2,"0")))}static makeHexColor(e,t,a){return`#${this.hexNums[e]}${this.hexNums[t]}${this.hexNums[a]}`}static domMatrixToTransform(e){return[e.a,e.b,e.c,e.d,e.e,e.f]}static scaleMinMax(e,t){let a;if(e[0]){if(e[0]<0){a=t[0];t[0]=t[2];t[2]=a}t[0]*=e[0];t[2]*=e[0];if(e[3]<0){a=t[1];t[1]=t[3];t[3]=a}t[1]*=e[3];t[3]*=e[3]}else{a=t[0];t[0]=t[1];t[1]=a;a=t[2];t[2]=t[3];t[3]=a;if(e[1]<0){a=t[1];t[1]=t[3];t[3]=a}t[1]*=e[1];t[3]*=e[1];if(e[2]<0){a=t[0];t[0]=t[2];t[2]=a}t[0]*=e[2];t[2]*=e[2]}t[0]+=e[4];t[1]+=e[5];t[2]+=e[4];t[3]+=e[5]}static transform(e,t){return[e[0]*t[0]+e[2]*t[1],e[1]*t[0]+e[3]*t[1],e[0]*t[2]+e[2]*t[3],e[1]*t[2]+e[3]*t[3],e[0]*t[4]+e[2]*t[5]+e[4],e[1]*t[4]+e[3]*t[5]+e[5]]}static multiplyByDOMMatrix(e,t){return[e[0]*t.a+e[2]*t.b,e[1]*t.a+e[3]*t.b,e[0]*t.c+e[2]*t.d,e[1]*t.c+e[3]*t.d,e[0]*t.e+e[2]*t.f+e[4],e[1]*t.e+e[3]*t.f+e[5]]}static applyTransform(e,t,a=0){const n=e[a],s=e[a+1];e[a]=n*t[0]+s*t[2]+t[4];e[a+1]=n*t[1]+s*t[3]+t[5]}static applyTransformToBezier(e,t,a=0){const n=t[0],s=t[1],r=t[2],i=t[3],o=t[4],l=t[5];for(let t=0;t<6;t+=2){const f=e[a+t],c=e[a+t+1];e[a+t]=f*n+c*r+o;e[a+t+1]=f*s+c*i+l}}static applyInverseTransform(e,t){const a=e[0],n=e[1],s=t[0]*t[3]-t[1]*t[2];e[0]=(a*t[3]-n*t[2]+t[2]*t[5]-t[4]*t[3])/s;e[1]=(-a*t[1]+n*t[0]+t[4]*t[1]-t[5]*t[0])/s}static axialAlignedBoundingBox(e,t,a){const n=t[0],s=t[1],r=t[2],i=t[3],o=t[4],l=t[5],f=e[0],c=e[1],h=e[2],u=e[3];let m=n*f+o,p=m,d=n*h+o,g=d,b=i*c+l,w=b,j=i*u+l,k=j;if(0!==s||0!==r){const e=s*f,t=s*h,a=r*c,n=r*u;m+=a;g+=a;d+=n;p+=n;b+=e;k+=e;j+=t;w+=t}a[0]=Math.min(a[0],m,d,p,g);a[1]=Math.min(a[1],b,j,w,k);a[2]=Math.max(a[2],m,d,p,g);a[3]=Math.max(a[3],b,j,w,k)}static inverseTransform(e){const t=e[0]*e[3]-e[1]*e[2];return[e[3]/t,-e[1]/t,-e[2]/t,e[0]/t,(e[2]*e[5]-e[4]*e[3])/t,(e[4]*e[1]-e[5]*e[0])/t]}static singularValueDecompose2dScale(e,t){const a=e[0],n=e[1],s=e[2],r=e[3],i=a**2+n**2,o=a*s+n*r,l=s**2+r**2,f=(i+l)/2,c=Math.sqrt(f**2-(i*l-o**2));t[0]=Math.sqrt(f+c||1);t[1]=Math.sqrt(f-c||1)}static normalizeRect(e){const t=e.slice(0);if(e[0]>e[2]){t[0]=e[2];t[2]=e[0]}if(e[1]>e[3]){t[1]=e[3];t[3]=e[1]}return t}static intersect(e,t){const a=Math.max(Math.min(e[0],e[2]),Math.min(t[0],t[2])),n=Math.min(Math.max(e[0],e[2]),Math.max(t[0],t[2]));if(a>n)return null;const s=Math.max(Math.min(e[1],e[3]),Math.min(t[1],t[3])),r=Math.min(Math.max(e[1],e[3]),Math.max(t[1],t[3]));return s>r?null:[a,s,n,r]}static pointBoundingBox(e,t,a){a[0]=Math.min(a[0],e);a[1]=Math.min(a[1],t);a[2]=Math.max(a[2],e);a[3]=Math.max(a[3],t)}static rectBoundingBox(e,t,a,n,s){s[0]=Math.min(s[0],e,a);s[1]=Math.min(s[1],t,n);s[2]=Math.max(s[2],e,a);s[3]=Math.max(s[3],t,n)}static#e(e,t,a,n,s,r,i,o,l,f){if(l<=0||l>=1)return;const c=1-l,h=l*l,u=h*l,m=c*(c*(c*e+3*l*t)+3*h*a)+u*n,p=c*(c*(c*s+3*l*r)+3*h*i)+u*o;f[0]=Math.min(f[0],m);f[1]=Math.min(f[1],p);f[2]=Math.max(f[2],m);f[3]=Math.max(f[3],p)}static#t(e,t,a,n,s,r,i,o,l,f,c,h){if(Math.abs(l)<1e-12){Math.abs(f)>=1e-12&&this.#e(e,t,a,n,s,r,i,o,-c/f,h);return}const u=f**2-4*c*l;if(u<0)return;const m=Math.sqrt(u),p=2*l;this.#e(e,t,a,n,s,r,i,o,(-f+m)/p,h);this.#e(e,t,a,n,s,r,i,o,(-f-m)/p,h)}static bezierBoundingBox(e,t,a,n,s,r,i,o,l){l[0]=Math.min(l[0],e,i);l[1]=Math.min(l[1],t,o);l[2]=Math.max(l[2],e,i);l[3]=Math.max(l[3],t,o);this.#t(e,a,s,i,t,n,r,o,3*(3*(a-s)-e+i),6*(e-2*a+s),3*(a-e),l);this.#t(e,a,s,i,t,n,r,o,3*(3*(n-r)-t+o),6*(t-2*n+r),3*(n-t),l)}}const na=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,728,711,710,729,733,731,730,732,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8226,8224,8225,8230,8212,8211,402,8260,8249,8250,8722,8240,8222,8220,8221,8216,8217,8218,8482,64257,64258,321,338,352,376,381,305,322,339,353,382,0,8364];function stringToPDFString(e,t=!1){if(e[0]>="ï"){let a;if("þ"===e[0]&&"ÿ"===e[1]){a="utf-16be";e.length%2==1&&(e=e.slice(0,-1))}else if("ÿ"===e[0]&&"þ"===e[1]){a="utf-16le";e.length%2==1&&(e=e.slice(0,-1))}else"ï"===e[0]&&"»"===e[1]&&"¿"===e[2]&&(a="utf-8");if(a)try{const n=new TextDecoder(a,{fatal:!0}),s=stringToBytes(e),r=n.decode(s);return t||!r.includes("")?r:r.replaceAll(/\x1b[^\x1b]*(?:\x1b|$)/g,"")}catch(e){warn(`stringToPDFString: "${e}".`)}}const a=[];for(let n=0,s=e.length;n[],makeMap=()=>new Map,makeObj=()=>Object.create(null),ia=Symbol("CIRCULAR_REF"),oa=Symbol("EOF");let la=Object.create(null),fa=Object.create(null),ca=Object.create(null);class Name{constructor(e){this.name=e}static get(e){return fa[e]||=new Name(e)}}class Cmd{constructor(e){this.cmd=e}static get(e){return la[e]||=new Cmd(e)}}const ha=function nonSerializableClosure(){return ha};class Dict{__nonSerializable__=ha;#a=new Map;objId=null;suppressEncryption=!1;xref;constructor(e=null){this.xref=e}assignXref(e){this.xref=e}get size(){return this.#a.size}#n(e,t,a,n){let s=this.#a.get(t);if(void 0===s&&void 0!==a){s=this.#a.get(a);void 0===s&&void 0!==n&&(s=this.#a.get(n))}return s instanceof Ref&&this.xref?e?this.xref.fetchAsync(s,this.suppressEncryption):this.xref.fetch(s,this.suppressEncryption):s}get(e,t,a){return this.#n(!1,e,t,a)}async getAsync(e,t,a){return this.#n(!0,e,t,a)}getArray(e,t,a){let n=this.#n(!1,e,t,a);if(Array.isArray(n)){n=n.slice();for(let e=0,t=n.length;e{unreachable("Should not call `set` on the empty dictionary.")};return shadow(this,"empty",e)}static merge({xref:e,dictArray:t,mergeSubDicts:a=!1}){const n=new Dict(e),s=new Map;for(const e of t)if(e instanceof Dict)for(const[t,n]of e.getRawEntries()){let e=s.get(t);if(void 0===e){e=[];s.set(t,e)}else if(!(a&&n instanceof Dict))continue;e.push(n)}for(const[t,a]of s){if(1===a.length||!(a[0]instanceof Dict)){n.set(t,a[0]);continue}const s=new Dict(e);for(const e of a)for(const[t,a]of e.getRawEntries())s.setIfNotExists(t,a);s.size>0&&n.set(t,s)}s.clear();return n.size>0?n:Dict.empty}clone(){const e=new Dict(this.xref);for(const[t,a]of this.#a)e.set(t,a);return e}delete(e){this.#a.delete(e)}}class Ref{constructor(e,t){this.num=e;this.gen=t}toString(){return 0===this.gen?`${this.num}R`:`${this.num}R${this.gen}`}static fromString(e){const t=ca[e];if(t)return t;const a=/^(\d+)R(\d*)$/.exec(e);return a&&"0"!==a[1]?ca[e]=new Ref(parseInt(a[1],10),a[2]?parseInt(a[2],10):0):null}static get(e,t){const a=0===t?`${e}R`:`${e}R${t}`;return ca[a]||=new Ref(e,t)}}class RefSet{constructor(e=null){this._set=new Set(e?._set)}has(e){return this._set.has(e.toString())}put(e){this._set.add(e.toString())}remove(e){this._set.delete(e.toString())}[Symbol.iterator](){return this._set.values()}clear(){this._set.clear()}}class RefSetCache{_map=new Map;get size(){return this._map.size}get(e){return this._map.get(e.toString())}has(e){return this._map.has(e.toString())}put(e,t){this._map.set(e.toString(),t)}putAlias(e,t){this._map.set(e.toString(),this.get(t))}[Symbol.iterator](){return this._map.values()}clear(){this._map.clear()}*values(){yield*this._map.values()}*items(){for(const[e,t]of this._map)yield[Ref.fromString(e),t]}*keys(){for(const e of this._map.keys())yield Ref.fromString(e)}}function isName(e,t){return e instanceof Name&&(void 0===t||e.name===t)}function isCmd(e,t){return e instanceof Cmd&&(void 0===t||e.cmd===t)}function isDict(e,t){return e instanceof Dict&&(void 0===t||isName(e.get("Type"),t))}function isRefsEqual(e,t){return e.num===t.num&&e.gen===t.gen}class BaseStream{get length(){unreachable("Abstract getter `length` accessed")}get isEmpty(){unreachable("Abstract getter `isEmpty` accessed")}get isDataLoaded(){return shadow(this,"isDataLoaded",!0)}getByte(){unreachable("Abstract method `getByte` called")}getBytes(e){unreachable("Abstract method `getBytes` called")}async getImageData(e,t){return this.getBytes(e,t)}async asyncGetBytes(){unreachable("Abstract method `asyncGetBytes` called")}get isAsync(){return!1}get isAsyncDecoder(){return!1}get isImageStream(){return!1}get canAsyncDecodeImageFromBuffer(){return!1}async getTransferableImage(){return null}peekByte(){const e=this.getByte();-1!==e&&this.pos--;return e}peekBytes(e){const t=this.getBytes(e);this.pos-=t.length;return t}getUint16(){const e=this.getByte(),t=this.getByte();return-1===e||-1===t?-1:(e<<8)+t}getInt32(){return(this.getByte()<<24)+(this.getByte()<<16)+(this.getByte()<<8)+this.getByte()}getByteRange(e,t){unreachable("Abstract method `getByteRange` called")}getString(e){return bytesToString(this.getBytes(e))}skip(e){this.pos+=e||1}reset(){unreachable("Abstract method `reset` called")}moveStart(){unreachable("Abstract method `moveStart` called")}makeSubStream(e,t,a=null){unreachable("Abstract method `makeSubStream` called")}getBaseStreams(){return null}getOriginalStream(){return this.stream?.getOriginalStream()||this}}const ua=/^[1-9]\.\d$/,ma=2**31-1,pa=[1,0,0,1,0,0],da=["ColorSpace","ExtGState","Font","Pattern","Properties","Shading","XObject"],ga=["ExtGState","Font","Properties","XObject"];function getLookupTableFactory(e){let t;return function(){if(e){t=Object.create(null);e(t);e=null}return t}}class MissingDataException extends aa{constructor(e,t){super(`Missing data [${e}, ${t})`,"MissingDataException");this.begin=e;this.end=t}}class ParserEOFException extends aa{constructor(e){super(e,"ParserEOFException")}}class XRefEntryException extends aa{constructor(e){super(e,"XRefEntryException")}}class XRefParseException extends aa{constructor(e){super(e,"XRefParseException")}}function arrayBuffersToBytes(e){const t=e.length;if(0===t)return new Uint8Array(0);if(1===t)return new Uint8Array(e[0]);let a=0;for(let n=0;n0,"The number should be a positive integer.");const a="M".repeat(e/1e3|0)+ba[e%1e3/100|0]+ba[10+(e%100/10|0)]+ba[20+e%10];return t?a.toLowerCase():a}function isWhiteSpace(e){return 32===e||9===e||13===e||10===e}function isNumberArray(e,t){return Array.isArray(e)?(null===t||e.length===t)&&e.every(e=>"number"==typeof e):ArrayBuffer.isView(e)&&!(e instanceof BigInt64Array||e instanceof BigUint64Array)&&(null===t||e.length===t)}function lookupMatrix(e,t){return isNumberArray(e,6)?e:t}function lookupRect(e,t){return isNumberArray(e,4)?e:t}function lookupNormalRect(e,t){return isNumberArray(e,4)?Util.normalizeRect(e):t}function parseXFAPath(e){const t=/(.+)\[(\d+)\]$/;return e.split(".").map(e=>{const a=e.match(t);return a?{name:a[1],pos:parseInt(a[2],10)}:{name:e,pos:0}})}function escapePDFName(e){const t=[];let a=0;for(let n=0,s=e.length;n126||35===s||40===s||41===s||60===s||62===s||91===s||93===s||123===s||125===s||47===s||37===s){a"\n"===e?"\\n":"\r"===e?"\\r":`\\${e}`)}function _collectJS(e,t,a,n){if(!e)return;let s=null;if(e instanceof Ref){if(n.has(e))return;s=e;n.put(s);e=t.fetch(e)}if(Array.isArray(e))for(const s of e)_collectJS(s,t,a,n);else if(e instanceof Dict){if(isName(e.get("S"),"JavaScript")){const t=e.get("JS");let n;t instanceof BaseStream?n=t.getString():"string"==typeof t&&(n=t);n&&=stringToPDFString(n,!0).replaceAll("\0","");n&&a.push(n.trim())}_collectJS(e.getRaw("Next"),t,a,n)}s&&n.remove(s)}function collectActions(e,t,a){const n=Object.create(null),s=getInheritableProperty({dict:t,key:"AA",stopWhenFound:!1});if(s)for(let t=s.length-1;t>=0;t--){const r=s[t];if(r instanceof Dict)for(const[t,s]of r.getRawEntries()){const r=a[t];if(!r)continue;const i=[];_collectJS(s,e,i,new RefSet);i.length>0&&(n[r]=i)}}if(t.has("A")){const a=[];_collectJS(t.get("A"),e,a,new RefSet);a.length>0&&(n.Action=a)}return objectSize(n)>0?n:null}const wa={60:"<",62:">",38:"&",34:""",39:"'"};function*codePointIter(e){for(let t=0,a=e.length;t55295&&(a<57344||a>65533)&&t++;yield a}}function encodeToXmlString(e){const t=[];let a=0;for(let n=0,s=e.length;n55295&&(s<57344||s>65533)&&n++;a=n+1}}if(0===t.length)return e;a: ${e}.`);return!1}return!0}function validateCSSFont(e){const t=new Set(["100","200","300","400","500","600","700","800","900","1000","normal","bold","bolder","lighter"]),{fontFamily:a,fontWeight:n,italicAngle:s}=e;if(!validateFontName(a,!0))return!1;const r=n?n.toString():"";e.fontWeight=t.has(r)?r:"400";const i=parseFloat(s);e.italicAngle=isNaN(i)||i<-90||i>90?"14":s.toString();return!0}function recoverJsURL(e){const t=new RegExp("^\\s*("+["app.launchURL","window.open","xfa.host.gotoURL"].join("|").replaceAll(".","\\.")+")\\((?:'|\")([^'\"]*)(?:'|\")(?:,\\s*(\\w+)\\)|\\))","i").exec(e);return t?.[2]?{url:t[2],newWindow:"app.launchURL"===t[1]&&"true"===t[3]}:null}function numberToString(e){if(Number.isInteger(e))return e.toString();const t=Math.round(100*e);return t%100==0?(t/100).toString():t%10==0?e.toFixed(1):e.toFixed(2)}function getNewAnnotationsMap(e){if(!e)return null;const t=new Map;for(const[a,n]of e)a.startsWith(d)&&t.getOrInsertComputed(n.pageIndex,makeArr).push(n);return t.size>0?t:null}function stringToAsciiOrUTF16BE(e){return null==e||function isAscii(e){if("string"!=typeof e)return!1;return!e||/^[\x00-\x7F]*$/.test(e)}(e)?e:stringToUTF16String(e,!0)}function stringToUTF16HexString(e){const t=[];for(let a=0,n=e.length;a>8&255],Util.hexNums[255&n])}return t.join("")}function stringToUTF16String(e,t=!1){const a=[];t&&a.push("þÿ");for(let t=0,n=e.length;t>8&255),String.fromCharCode(255&n))}return a.join("")}function getRotationMatrix(e,t,a){switch(e){case 90:return[0,1,-1,0,t,0];case 180:return[-1,0,0,-1,t,a];case 270:return[0,-1,1,0,0,a];default:throw new Error("Invalid rotation")}}function getSizeInBytes(e){return Math.ceil(Math.ceil(Math.log2(1+e))/8)}class QCMS{static#s=null;static _memory=null;static _mustAddAlpha=!1;static _destBuffer=null;static _destOffset=0;static _destLength=0;static _cssColor="";static _makeHexColor=null;static get _memoryArray(){const e=this.#s;return e?.byteLength?e:this.#s=new Uint8Array(this._memory.buffer)}}const ja=Object.freeze({RGB8:0,0:"RGB8",RGBA8:1,1:"RGBA8",BGRA8:2,2:"BGRA8",Gray8:3,3:"Gray8",GrayA8:4,4:"GrayA8",CMYK:5,5:"CMYK"}),ka=Object.freeze({Perceptual:0,0:"Perceptual",RelativeColorimetric:1,1:"RelativeColorimetric",Saturation:2,2:"Saturation",AbsoluteColorimetric:3,3:"AbsoluteColorimetric"});function __wbg_get_imports(){return{__proto__:null,"./qcms_bg.js":{__proto__:null,__wbg___wbindgen_throw_6b64449b9b9ed33c:function(e,t){throw new Error(function getStringFromWasm0(e,t){return function decodeText(e,t){Sa+=t;if(Sa>=va){qa=new TextDecoder("utf-8",{ignoreBOM:!0,fatal:!0});qa.decode();Sa=t}return qa.decode(getUint8ArrayMemory0().subarray(e,e+t))}(e>>>=0,t)}(e,t))},__wbg_copy_result_0d15f3bf9d9012ae:function(e,t){!function copy_result(e,t){const{_mustAddAlpha:a,_destBuffer:n,_destOffset:s,_destLength:r,_memoryArray:i}=QCMS;if(t!==r)if(a)for(let a=e,r=e+t,o=s;a>>0,t>>>0)},__wbg_copy_rgb_0106d9d9464fce43:function(e){!function copy_rgb(e){const{_destBuffer:t,_destOffset:a,_memoryArray:n}=QCMS;t[a]=n[e];t[a+1]=n[e+1];t[a+2]=n[e+2]}(e>>>0)},__wbg_make_cssRGB_8e24b34f71f5363e:function(e){!function make_cssRGB(e){const{_memoryArray:t}=QCMS;QCMS._cssColor=QCMS._makeHexColor(t[e],t[e+1],t[e+2])}(e>>>0)},__wbindgen_init_externref_table:function(){const e=xa.__wbindgen_externrefs,t=e.grow(4);e.set(0,void 0);e.set(t+0,void 0);e.set(t+1,null);e.set(t+2,!0);e.set(t+3,!1)}}}}let ya=null;function getUint8ArrayMemory0(){null!==ya&&0!==ya.byteLength||(ya=new Uint8Array(xa.memory.buffer));return ya}function passArray8ToWasm0(e,t){const a=t(1*e.length,1)>>>0;getUint8ArrayMemory0().set(e,a/1);Ca=e.length;return a}let qa=new TextDecoder("utf-8",{ignoreBOM:!0,fatal:!0});qa.decode();const va=2146435072;let Sa=0;let Aa,xa,Ca=0;function __wbg_finalize_init(e,t){xa=e.exports;Aa=t;ya=null;xa.__wbindgen_start();return xa}function MathClamp(e,t,a){return Math.min(Math.max(e,t),a)}function isDefaultDecodeHelper(e,t){if(!Array.isArray(e))return!0;const a=e.length;if(at){info("Truncating too long decode map.");e.length=t}return!1}class ColorSpace{static#r=new Uint8ClampedArray(3);constructor(e,t){this.name=e;this.numComps=t}getRgb(e,t,a=new Uint8ClampedArray(3)){this.getRgbItem(e,t,a,0);return a}getRgbHex(e,t){const a=this.getRgb(e,t,ColorSpace.#r);return Util.makeHexColor(a[0],a[1],a[2])}getRgbItem(e,t,a,n){unreachable("Should not call ColorSpace.getRgbItem")}getRgbBuffer(e,t,a,n,s,r,i){unreachable("Should not call ColorSpace.getRgbBuffer")}getOutputLength(e,t){unreachable("Should not call ColorSpace.getOutputLength")}isPassthrough(e){return!1}isDefaultDecode(e,t){return ColorSpace.isDefaultDecode(e,this.numComps)}fillRgb(e,t,a,n,s,r,i,o,l){const f=t*a;let c=null;const h=1<h&&"DeviceGray"!==this.name&&"DeviceRGB"!==this.name){const t=i<=8?new Uint8Array(h):new Uint16Array(h);for(let e=0;e=.99554525?1:MathClamp(1.055*e**(1/2.4)-.055,0,1)}#j(e){return e<0?-this.#j(-e):e>8?((e+16)/116)**3:e*CalRGBCS.#p}#k(e,t,a){if(0===e[0]&&0===e[1]&&0===e[2]){a[0]=t[0];a[1]=t[1];a[2]=t[2];return}const n=this.#j(0),s=(1-n)/(1-this.#j(e[0])),r=1-s,i=(1-n)/(1-this.#j(e[1])),o=1-i,l=(1-n)/(1-this.#j(e[2])),f=1-l;a[0]=t[0]*s+r;a[1]=t[1]*i+o;a[2]=t[2]*l+f}#y(e,t,a){if(1===e[0]&&1===e[2]){a[0]=t[0];a[1]=t[1];a[2]=t[2];return}const n=a;this.#d(CalRGBCS.#o,t,n);const s=CalRGBCS.#h;this.#g(e,n,s);this.#d(CalRGBCS.#l,s,a)}#q(e,t,a){const n=a;this.#d(CalRGBCS.#o,t,n);const s=CalRGBCS.#h;this.#b(e,n,s);this.#d(CalRGBCS.#l,s,a)}#i(e,t,a,n,s){const r=MathClamp(e[t]*s,0,1),i=MathClamp(e[t+1]*s,0,1),o=MathClamp(e[t+2]*s,0,1),l=1===r?1:r**this.GR,f=1===i?1:i**this.GG,c=1===o?1:o**this.GB,h=this.MXA*l+this.MXB*f+this.MXC*c,u=this.MYA*l+this.MYB*f+this.MYC*c,m=this.MZA*l+this.MZB*f+this.MZC*c,p=CalRGBCS.#u;p[0]=h;p[1]=u;p[2]=m;const d=CalRGBCS.#m;this.#y(this.whitePoint,p,d);const g=CalRGBCS.#u;this.#k(this.blackPoint,d,g);const b=CalRGBCS.#m;this.#q(CalRGBCS.#c,g,b);const w=CalRGBCS.#u;this.#d(CalRGBCS.#f,b,w);a[n]=255*this.#w(w[0]);a[n+1]=255*this.#w(w[1]);a[n+2]=255*this.#w(w[2])}getRgbItem(e,t,a,n){this.#i(e,t,a,n,1)}getRgbBuffer(e,t,a,n,s,r,i){const o=1/((1<this.amax||this.bmin>this.bmax){info("Invalid Range, falling back to defaults");this.amin=-100;this.amax=100;this.bmin=-100;this.bmax=100}}#v(e){return e>=6/29?e**3:108/841*(e-4/29)}#S(e,t,a,n){return a+e*(n-a)/t}#i(e,t,a,n,s){let r=e[t],i=e[t+1],o=e[t+2];if(!1!==a){r=this.#S(r,a,0,100);i=this.#S(i,a,this.amin,this.amax);o=this.#S(o,a,this.bmin,this.bmax)}i>this.amax?i=this.amax:ithis.bmax?o=this.bmax:ofunction qcms_convert_one(e,t,a){xa.qcms_convert_one(e,t,a)}(this.#A,255*e[t],a);break;case 3:n=ja.RGB8;this.#x=(e,t,a)=>function qcms_convert_three(e,t,a,n,s){xa.qcms_convert_three(e,t,a,n,s)}(this.#A,255*e[t],255*e[t+1],255*e[t+2],a);break;case 4:n=ja.CMYK;this.#x=(e,t,a)=>function qcms_convert_four(e,t,a,n,s,r){xa.qcms_convert_four(e,t,a,n,s,r)}(this.#A,255*e[t],255*e[t+1],255*e[t+2],255*e[t+3],a);break;default:throw new Error(`Unsupported number of components: ${a}`)}this.#A=function qcms_transformer_from_memory(e,t,a){const n=passArray8ToWasm0(e,xa.__wbindgen_malloc),s=Ca;return xa.qcms_transformer_from_memory(n,s,t,a)>>>0}(e,n,ka.Perceptual);if(!this.#A)throw new Error("Failed to create ICC color space");IccColorSpace.#F||=new FinalizationRegistry(e=>{!function qcms_drop_transformer(e){xa.qcms_drop_transformer(e)}(e)});IccColorSpace.#F.register(this,this.#A)}getRgbHex(e,t){this.#x(e,t,!0);return QCMS._cssColor}getRgbItem(e,t,a,n){QCMS._destBuffer=a;QCMS._destOffset=n;QCMS._destLength=3;this.#x(e,t,!1);QCMS._destBuffer=null}getRgbBuffer(e,t,a,n,s,r,i){e=e.subarray(t,t+a*this.numComps);if(8!==r){const t=255/((1<=this.end?-1:this.bytes[this.pos++]}getBytes(e){const t=this.bytes,a=this.pos,n=this.end;if(!e){this.pos=n;return t.subarray(a,n)}let s=a+e;s>n&&(s=n);this.pos=s;return t.subarray(a,s)}getByteRange(e,t){e<0&&(e=0);t>this.end&&(t=this.end);return this.bytes.subarray(e,t)}reset(){this.pos=this.start}moveStart(){this.start=this.pos}makeSubStream(e,t,a=null){return new Stream(this.bytes.buffer,e,t,a)}clone(){return new Stream(this.bytes.buffer,this.start,this.end-this.start,this.dict.clone())}}class StringStream extends Stream{constructor(e){super(stringToBytes(e))}}class NullStream extends Stream{constructor(){super(new Uint8Array(0))}}class ChunkedStream extends Stream{progressiveDataLength=0;_lastSuccessfulEnsureByteChunk=-1;_loadedChunks=new Set;constructor(e,t,a){super(new Uint8Array(e),0,e,null);this.chunkSize=t;this.numChunks=Math.ceil(e/t);this.manager=a}getMissingChunks(){const e=[];for(let t=0,a=this.numChunks;t=this.end?this.numChunks:Math.floor(t/this.chunkSize);for(let e=a;ethis.numChunks)&&t!==this._lastSuccessfulEnsureByteChunk){if(!this._loadedChunks.has(t))throw new MissingDataException(e,e+1);this._lastSuccessfulEnsureByteChunk=t}}ensureRange(e,t){if(e>=t)return;if(t<=this.progressiveDataLength)return;const a=Math.floor(e/this.chunkSize);if(a>this.numChunks)return;const n=Math.min(Math.floor((t-1)/this.chunkSize)+1,this.numChunks);for(let s=a;s=this.end)return-1;e>=this.progressiveDataLength&&this.ensureByte(e);return this.bytes[this.pos++]}getBytes(e){const t=this.bytes,a=this.pos,n=this.end;if(!e){n>this.progressiveDataLength&&this.ensureRange(a,n);this.pos=n;return t.subarray(a,n)}let s=a+e;s>n&&(s=n);s>this.progressiveDataLength&&this.ensureRange(a,s);this.pos=s;return t.subarray(a,s)}getByteRange(e,t){e<0&&(e=0);t>this.end&&(t=this.end);t>this.progressiveDataLength&&this.ensureRange(e,t);return this.bytes.subarray(e,t)}makeSubStream(e,t,a=null){t?e+t>this.progressiveDataLength&&this.ensureRange(e,e+t):e>=this.progressiveDataLength&&this.ensureByte(e);function ChunkedStreamSubstream(){}ChunkedStreamSubstream.prototype=Object.create(this);ChunkedStreamSubstream.prototype.getMissingChunks=function(){const e=this.chunkSize,t=Math.floor(this.start/e),a=Math.floor((this.end-1)/e)+1,n=[];for(let e=t;e0){const e=this.groupChunks(s);for(const t of e){const e=t.beginChunk*this.chunkSize,a=Math.min(t.endChunk*this.chunkSize,this.length);this.sendRequest(e,a).catch(n.reject)}}return n.promise.catch(e=>{if(!this.aborted)throw e})}getStream(){return this.stream}requestRange(e,t){t=Math.min(t,this.length);const a=this.getBeginChunk(e),n=this.getEndChunk(t),s=[];for(let e=a;ee-t);return this._requestChunks(t)}groupChunks(e){const t=[];let a=-1,n=-1;for(let s=0,r=e.length;s=0&&n+1!==r){t.push({beginChunk:a,endChunk:n+1});a=r}s+1===e.length&&t.push({beginChunk:a,endChunk:r+1});n=r}return t}onReceiveData(e){const{chunkSize:t,length:a,stream:n}=this,s=e.chunk,r=void 0===e.begin,i=r?n.progressiveDataLength:e.begin,o=i+s.byteLength,l=Math.floor(i/t),f=o0||c.push(a)}}}if(!this.disableAutoFetch&&0===this._requestsByChunk.size){let e;if(1===n.numChunksLoaded){const t=n.numChunks-1;n.hasChunk(t)||(e=t)}else e=n.nextEmptyChunk(f);Number.isInteger(e)&&this._requestChunks([e])}for(const e of c){const t=this._promisesByRequest.get(e);this._promisesByRequest.delete(e);t.resolve()}this.msgHandler.send("DocProgress",{loaded:MathClamp(n.numChunksLoaded*t,n.progressiveDataLength,a),total:a})}onError(e){this._loadedStreamCapability.reject(e)}getBeginChunk(e){return Math.floor(e/this.chunkSize)}getEndChunk(e){return Math.floor((e-1)/this.chunkSize)+1}abort(e){this.aborted=!0;this.pdfStream?.cancelAllRequests(e);for(const t of this._promisesByRequest.values())t.reject(e)}}function convertToRGBA(e){switch(e.kind){case F:return convertBlackAndWhiteToRGBA(e);case T:return function convertRGBToRGBA({src:e,srcPos:t=0,dest:a,destPos:n=0,width:s,height:r}){let i=0;const o=s*r*3,l=o>>2,f=new Uint32Array(e.buffer,t,l),c=FeatureTest.isLittleEndian?4278190080:255;if(FeatureTest.isLittleEndian){for(;i>>24|t<<8|c;a[n+2]=t>>>16|s<<16|c;a[n+3]=s>>>8|c}for(let s=4*i,r=t+o;s>>8|c;a[n+2]=t<<16|s>>>16|c;a[n+3]=s<<8|c}for(let s=4*i,r=t+o;s>3,h=7&n,u=l^f,m=e.length;a=new Uint32Array(a.buffer);let p=0;for(let n=0;n>7&1)&u;a[p+1]=l^-(n>>6&1)&u;a[p+2]=l^-(n>>5&1)&u;a[p+3]=l^-(n>>4&1)&u;a[p+4]=l^-(n>>3&1)&u;a[p+5]=l^-(n>>2&1)&u;a[p+6]=l^-(n>>1&1)&u;a[p+7]=l^-(1&n)&u}if(0===h)continue;const n=t>7-e&1)&u}return{srcPos:t,destPos:p}}class ImageResizer{static#O=2048;static#R=FeatureTest.isImageDecoderSupported;constructor(e,t){this._imgData=e;this._isMask=t}static get canUseImageDecoder(){return shadow(this,"canUseImageDecoder",this.#R?ImageDecoder.isTypeSupported("image/bmp"):Promise.resolve(!1))}static needsToBeResized(e,t){if(e<=this.#O&&t<=this.#O)return!1;const{MAX_DIM:a}=this;if(e>a||t>a)return!0;const n=e*t;if(this._hasMaxArea)return n>this.MAX_AREA;if(n(this.MAX_AREA=this.#O**2)}static getReducePowerForJPX(e,t,a){const n=e*t,s=2**30/(4*a);if(!this.needsToBeResized(e,t))return n>s?Math.ceil(Math.log2(n/s)):0;const{MAX_DIM:r,MAX_AREA:i}=this,o=Math.max(e/r,t/r,Math.sqrt(n/Math.min(s,i)));return Math.ceil(Math.log2(o))}static get MAX_DIM(){return shadow(this,"MAX_DIM",this._guessMax(2048,32768,0,1))}static get MAX_AREA(){this._hasMaxArea=!0;return shadow(this,"MAX_AREA",this._guessMax(this.#O,this.MAX_DIM,128,0)**2)}static set MAX_AREA(e){if(e>=0){this._hasMaxArea=!0;shadow(this,"MAX_AREA",e)}}static setOptions({canvasMaxAreaInBytes:e=-1,isImageDecoderSupported:t=!1}){this._hasMaxArea||(this.MAX_AREA=e>>2);this.#R=t}static _areGoodDims(e,t){try{const a=new OffscreenCanvas(e,t),n=a.getContext("2d");n.fillRect(0,0,1,1);const s=n.getImageData(0,0,1,1).data[3];a.width=a.height=1;return 0!==s}catch{return!1}}static _guessMax(e,t,a,n){for(;e+a+1ma){const e=this.#H();if(e)return e}const n=this._encodeBMP();let s,r;if(await ImageResizer.canUseImageDecoder){s=new ImageDecoder({data:n,type:"image/bmp",preferAnimation:!1,transfer:[n.buffer]});r=s.decode().catch(e=>{warn(`BMP image decoding failed: ${e}`);return createImageBitmap(new Blob([this._encodeBMP().buffer],{type:"image/bmp"}))}).finally(()=>{s.close()})}else r=createImageBitmap(new Blob([n.buffer],{type:"image/bmp"}));const{MAX_AREA:i,MAX_DIM:o}=ImageResizer,l=Math.max(t/o,a/o,Math.sqrt(t*a/i)),f=Math.max(l,2),c=Math.round(10*(l+1.25))/10/f,h=Math.floor(Math.log2(c)),u=new Array(h+2).fill(2);u[0]=f;u.splice(-1,1,c/(1<>i,l=n>>i;let f,c=n;try{f=new Uint8Array(r)}catch{let e=Math.floor(Math.log2(r+1));for(;;)try{f=new Uint8Array(2**e-1);break}catch{e-=1}c=Math.floor((2**e-1)/(4*a));const t=a*c*4;t>i;e>3,i=a+3&-4;if(a!==i){const e=new Uint8Array(i*t);let n=0;for(let r=0,o=t*a;r{throw t},a=import.meta.url;try{new URL(".",a).href}catch{}0;console.log.bind(console);var n,s,r,i,o,l=console.error.bind(console),f=!1,c=!1;function updateMemoryViews(){var e=u.buffer;i=new Int8Array(e);new Int16Array(e);o=new Uint8Array(e);new Uint16Array(e);new Int32Array(e);new Uint32Array(e);new Float32Array(e);new Float64Array(e);new BigInt64Array(e);new BigUint64Array(e)}class ExitStatus{name="ExitStatus";constructor(e){this.message=`Program terminated with exit(${e})`;this.status=e}}var h,u,callRuntimeCallbacks=e=>{for(;e.length>0;)e.shift()(t)},m=[],addOnPostRun=e=>m.push(e),p=[],addOnPreRun=e=>p.push(e),d=!0,g=0,b={},handleException=e=>{if(e instanceof ExitStatus||"unwind"==e)return n;quit_(0,e)},keepRuntimeAlive=()=>d||g>0,_proc_exit=e=>{n=e;if(!keepRuntimeAlive()){t.onExit?.(e);f=!0}quit_(0,new ExitStatus(e))},_exit=(e,t)=>{n=e;_proc_exit(e)},callUserCallback=e=>{if(!f)try{return e()}catch(e){handleException(e)}finally{(()=>{if(!keepRuntimeAlive())try{_exit(n)}catch(e){handleException(e)}})()}},alignMemory=(e,t)=>Math.ceil(e/t)*t,growMemory=e=>{var t=(e-u.buffer.byteLength+65535)/65536|0;try{u.grow(t);updateMemoryViews();return 1}catch(e){}};t.noExitRuntime&&(d=t.noExitRuntime);t.print&&t.print;t.printErr&&(l=t.printErr);t.wasmBinary&&t.wasmBinary;t.arguments&&t.arguments;t.thisProgram&&t.thisProgram;if(t.preInit){"function"==typeof t.preInit&&(t.preInit=[t.preInit]);for(;t.preInit.length>0;)t.preInit.shift()()}t.writeArrayToMemory=(e,t)=>{i.set(e,t)};var w,j={e:()=>function abort(e){t.onAbort?.(e);l(e="Aborted("+e+")");f=!0;e+=". Build with -sASSERTIONS for more info.";var a=new WebAssembly.RuntimeError(e);r?.(a);throw a}(""),b:()=>{d=!1;g=0},c:(e,t)=>{if(b[e]){clearTimeout(b[e].id);delete b[e]}if(!t)return 0;var a=setTimeout(()=>{delete b[e];callUserCallback(()=>h(e,performance.now()))},t);b[e]={id:a,timeout_ms:t};return 0},g:function _createImageData(e){t.imageData=new Uint8Array(e)},d:e=>{var t=o.length,a=2147483648;if((e>>>=0)>a)return!1;for(var n=1;n<=4;n*=2){var s=t*(1+.2/n);s=Math.min(s,e+100663296);var r=Math.min(a,alignMemory(Math.max(e,s),65536));if(growMemory(r))return!0}return!1},a:_proc_exit,h:function _setImageData(e,a,n,s){if(n===a){t.imageData=new Uint8ClampedArray(o.subarray(e,e+n*s));return}const r=a*s,i=t.imageData=new Uint8ClampedArray(r);for(let t=e,s=0;s{t.instantiateWasm(e,(e,t)=>{a(receiveInstance(e))})})}();!function run(){!function preRun(){if(t.preRun){"function"==typeof t.preRun&&(t.preRun=[t.preRun]);for(;t.preRun.length;)addOnPreRun(t.preRun.shift())}callRuntimeCallbacks(p)}();function doRun(){t.calledRun=!0;if(!f){!function initRuntime(){c=!0;w.j()}();s?.(t);t.onRuntimeInitialized?.();!function postRun(){if(t.postRun){"function"==typeof t.postRun&&(t.postRun=[t.postRun]);for(;t.postRun.length;)addOnPostRun(t.postRun.shift())}callRuntimeCallbacks(m)}()}}if(t.setStatus){t.setStatus("Running...");setTimeout(()=>{setTimeout(()=>t.setStatus(""),1);doRun()},1)}else doRun()}();return c?t:new Promise((e,t)=>{s=e;r=t})};class Jbig2Error extends aa{constructor(e){super(e,"Jbig2Error")}}class JBig2CCITTFaxImage{static#B=null;static#D=null;static#M=null;static#C=!0;static#N=!0;static#I=null;static setOptions({handler:e,useWasm:t,useWorkerFetch:a,wasmUrl:n}){this.#C=t;this.#N=a;this.#I=n;a||(this.#D=e)}static async#P(e){const t=`${this.#I}jbig2_nowasm_fallback.js`;let a=null;try{a=(await import( +/*webpackIgnore: true*/ +/*@vite-ignore*/ +t)).default()}catch(e){warn(`JBig2CCITTFaxImage#getJsModule: ${e}`)}e(a)}static async#E(e,t,a){const n="jbig2.wasm";try{this.#B||(this.#N?this.#B=await fetchBinaryData(`${this.#I}${n}`):this.#B=await this.#D.sendWithPromise("FetchBinaryData",{kind:"wasmUrl",filename:n}));return a((await WebAssembly.instantiate(this.#B,t)).instance)}catch(t){warn(`JBig2CCITTFaxImage#instantiateWasm: ${t}`);this.#P(e);return null}finally{this.#D=null}}static async decode(e,t,a,n,s){if(!this.#M){const{promise:e,resolve:t}=Promise.withResolvers(),a=[e];this.#C?a.push(Ia({warn,instantiateWasm:this.#E.bind(this,t)})):this.#P(t);this.#M=Promise.race(a)}const r=await this.#M;if(!r)throw new Jbig2Error("JBig2 failed to initialize");let i,o;try{const l=e.length;i=r._malloc(l);r.writeArrayToMemory(e,i);if(s)r._ccitt_decode(i,l,t,a,s.K,s.EndOfLine?1:0,s.EncodedByteAlign?1:0,s.BlackIs1?1:0,s.Columns,s.Rows);else{const e=n?n.length:0;if(e>0){o=r._malloc(e);r.writeArrayToMemory(n,o)}r._jbig2_decode(i,l,t,a,o,e)}if(!r.imageData)throw new Jbig2Error("Unknown error");const{imageData:f}=r;r.imageData=null;return f}finally{i&&r._free(i);o&&r._free(o)}}static cleanup(){this.#M=null}}const Fa=new Uint8Array(0);class DecodeStream extends BaseStream{constructor(e){super();this._rawMinBufferLength=e||0;this.pos=0;this.bufferLength=0;this.eof=!1;this.buffer=Fa;this.minBufferLength=512;if(e)for(;this.minBufferLengths&&(n=s)}else{for(;!this.eof;)this.readBlock(t);n=this.bufferLength}this.pos=n;return this.buffer.subarray(a,n)}async getImageData(e,t){if(!this.canAsyncDecodeImageFromBuffer)return this.isAsyncDecoder?this.decodeImage(null,e,t):this.getBytes(e,t);const a=await this.stream.asyncGetBytes();return this.decodeImage(a,e,t)}async asyncGetBytesFromDecompressionStream(e){this.stream.reset();const t=this.stream.isAsync?await this.stream.asyncGetBytes():this.stream.getBytes();try{const{readable:a,writable:n}=new DecompressionStream(e),s=n.getWriter();await s.ready;s.write(t).then(async()=>{await s.ready;await s.close()}).catch(()=>{});const r=[];let i=0;for await(const e of a){r.push(e);i+=e.byteLength}const o=new Uint8Array(i);let l=0;for(const e of r){o.set(e,l);l+=e.byteLength}return{decompressed:o,compressed:t}}catch{return{decompressed:null,compressed:t}}}reset(){this.pos=0}makeSubStream(e,t,a=null){if(void 0===t)for(;!this.eof;)this.readBlock();else{const a=e+t;for(;this.bufferLength<=a&&!this.eof;)this.readBlock()}return new Stream(this.buffer,e,t,a)}getBaseStreams(){return this.stream?this.stream.getBaseStreams():null}clone(){for(;!this.eof;)this.readBlock();return new Stream(this.buffer,this.start,this.end-this.start,this.dict.clone())}}class StreamsSequenceStream extends DecodeStream{constructor(e,t=null){e=e.filter(e=>e instanceof BaseStream&&!e.isImageStream);let a=0;for(const t of e)a+=t instanceof DecodeStream?t._rawMinBufferLength:t.length;super(a);this.streams=e;this._onError=t}readBlock(){const e=this.streams;if(0===e.length){this.eof=!0;return}const t=e.shift();let a;try{a=t.getBytes()}catch(e){if(this._onError){this._onError(e,t.dict?.objId);return}throw e}const n=this.bufferLength,s=n+a.length;this.ensureBuffer(s).set(a,n);this.bufferLength=s}getBaseStreams(){const e=[];for(const t of this.streams){const a=t.getBaseStreams();a&&e.push(...a)}return e.length>0?e:null}}class ColorSpaceUtils{static parse({cs:e,xref:t,resources:a=null,pdfFunctionFactory:n,globalColorSpaceCache:s,localColorSpaceCache:r,asyncIfNotCached:i=!1}){const o={xref:t,resources:a,pdfFunctionFactory:n,globalColorSpaceCache:s,localColorSpaceCache:r};let l,f,c;if(e instanceof Ref){f=e;const a=s.getByRef(f)||r.getByRef(f);if(a)return a;e=t.fetch(e)}if(e instanceof Name){l=e.name;const t=r.getByName(l);if(t)return t}try{c=this.#_(e,o)}catch(e){if(i&&!(e instanceof MissingDataException))return Promise.reject(e);throw e}if(l||f){r.set(l,f,c);f&&s.set(null,f,c)}return i?Promise.resolve(c):c}static#z(e,t){const{globalColorSpaceCache:a}=t;let n;if(e instanceof Ref){n=e;const t=a.getByRef(n);if(t)return t}const s=this.#_(e,t);n&&a.set(null,n,s);return s}static#_(e,t){const{xref:a,resources:n,pdfFunctionFactory:s,globalColorSpaceCache:r}=t;if((e=a.fetchIfRef(e))instanceof Name)switch(e.name){case"G":case"DeviceGray":return this.gray;case"RGB":case"DeviceRGB":return this.rgb;case"DeviceRGBA":return this.rgba;case"CMYK":case"DeviceCMYK":return this.cmyk;case"Pattern":return new PatternCS(null);default:if(n instanceof Dict){const a=n.get("ColorSpace");if(a instanceof Dict){const n=a.get(e.name);if(n){if(n instanceof Name)return this.#_(n,t);e=n;break}}}warn(`Unrecognized ColorSpace: ${e.name}`);return this.gray}if(Array.isArray(e)){const n=a.fetchIfRef(e[0]).name;let i,o,l,f,c,h;switch(n){case"G":case"DeviceGray":return this.gray;case"RGB":case"DeviceRGB":return this.rgb;case"CMYK":case"DeviceCMYK":return this.cmyk;case"CalGray":i=a.fetchIfRef(e[1]);f=i.getArray("WhitePoint");c=i.getArray("BlackPoint");h=i.get("Gamma");return new CalGrayCS(f,c,h);case"CalRGB":i=a.fetchIfRef(e[1]);f=i.getArray("WhitePoint");c=i.getArray("BlackPoint");h=i.getArray("Gamma");const u=i.getArray("Matrix");return new CalRGBCS(f,c,h,u);case"ICCBased":const m=e[1]instanceof Ref;if(m){const t=r.getByRef(e[1]);if(t)return t}const p=a.fetchIfRef(e[1]),d=p.dict;o=d.get("N");if(IccColorSpace.isUsable)try{const t=new IccColorSpace(p.getBytes(),"ICCBased",o);m&&r.set(null,e[1],t);return t}catch(t){if(t instanceof MissingDataException)throw t;warn(`ICCBased color space (${e[1]}): "${t}".`)}const g=d.getRaw("Alternate");if(g){const e=this.#z(g,t);if(e.numComps===o)return e;warn("ICCBased color space: Ignoring incorrect /Alternate entry.")}if(1===o)return this.gray;if(3===o)return this.rgb;if(4===o)return this.cmyk;break;case"Pattern":l=e[1]||null;l&&(l=this.#z(l,t));return new PatternCS(l);case"I":case"Indexed":l=this.#z(e[1],t);const b=MathClamp(a.fetchIfRef(e[2]),0,255),w=a.fetchIfRef(e[3]);return new IndexedCS(l,b,w);case"Separation":case"DeviceN":const j=a.fetchIfRef(e[1]);o=Array.isArray(j)?j.length:1;l=this.#z(e[2],t);const k=s.create(e[3]);return new AlternateCS(o,l,k);case"Lab":i=a.fetchIfRef(e[1]);f=i.getArray("WhitePoint");c=i.getArray("BlackPoint");const y=i.getArray("Range");return new LabCS(f,c,y);default:warn(`Unimplemented ColorSpace object: ${n}`);return this.gray}}warn(`Unrecognized ColorSpace object: ${e}`);return this.gray}static get gray(){return shadow(this,"gray",new DeviceGrayCS)}static get rgb(){return shadow(this,"rgb",new DeviceRgbCS)}static get rgba(){return shadow(this,"rgba",new DeviceRgbaCS)}static get cmyk(){if(CmykICCBasedCS.isUsable)try{return shadow(this,"cmyk",new CmykICCBasedCS)}catch{warn("CMYK fallback: DeviceCMYK")}return shadow(this,"cmyk",new DeviceCmykCS)}}class JpegError extends aa{constructor(e){super(e,"JpegError")}}class DNLMarkerError extends aa{constructor(e,t){super(e,"DNLMarkerError");this.scanLines=t}}class EOIMarkerError extends aa{constructor(e){super(e,"EOIMarkerError")}}const Ta=new Uint8Array([0,1,8,16,9,2,3,10,17,24,32,25,18,11,4,5,12,19,26,33,40,48,41,34,27,20,13,6,7,14,21,28,35,42,49,56,57,50,43,36,29,22,15,23,30,37,44,51,58,59,52,45,38,31,39,46,53,60,61,54,47,55,62,63]),Oa=4017,Ra=799,Ha=3406,Ba=2276,Da=1567,Ma=3784,Na=5793,Pa=2896;function buildHuffmanTable(e,t){let a,n,s=0,r=16;for(;r>0&&!e[r-1];)r--;const i=[{children:[],index:0}];let o,l=i[0];for(a=0;a0;)l=i.pop();l.index++;i.push(l);for(;i.length<=a;){i.push(o={children:[],index:0});l.children[l.index]=o.children;l=o}s++}if(a+10){d--;return p>>d&1}p=e[a++];if(255===p){const s=e[a++];if(s){if(220===s&&c){a+=2;const e=t.getUint16(a);a+=2;if(e>0&&e!==n.scanLines)throw new DNLMarkerError("Found DNL marker (0xFFDC) while parsing scan data",e)}else if(217===s){if(c){const e=j*(8===n.precision?8:0);if(e>0&&Math.round(n.scanLines/e)>=5)throw new DNLMarkerError("Found EOI marker (0xFFD9) while parsing scan data, possibly caused by incorrect `scanLines` parameter",e)}throw new EOIMarkerError("Found EOI marker (0xFFD9) while parsing scan data")}throw new JpegError(`unexpected marker ${(p<<8|s).toString(16)}`)}}d=7;return p>>>7}function decodeHuffman(e){let t=e;for(;;){t=t[readBit()];switch(typeof t){case"number":return t;case"object":continue}throw new JpegError("invalid huffman sequence")}}function receive(e){let t=0;for(;e>0;){t=t<<1|readBit();e--}return t}function receiveAndExtend(e){if(1===e)return 1===readBit()?1:-1;const t=receive(e);return t>=1<0){g--;return}let a=i;const n=o;for(;a<=n;){const n=decodeHuffman(e.huffmanTableAC),s=15&n,r=n>>4;if(0===s){if(r<15){g=receive(r)+(1<>4;if(0===s)if(l<15){g=receive(l)+(1<>4;if(0===n){if(r<15)break;s+=16;continue}s+=r;const i=Ta[s];e.blockData[t+i]=receiveAndExtend(n);s++}};let F,T=0;const O=1===k?s[0].blocksPerLine*s[0].blocksPerColumn:h*n.mcusPerColumn;let R,H;for(;T<=O;){const n=r?Math.min(O-T,r):O;if(n>0){for(q=0;q0?"unexpected":"excessive"} MCU data, current marker is: ${F.invalid}`);a=F.offset}if(!(F.marker>=65488&&F.marker<=65495))break;a+=2}return a-m}function quantizeAndInverse(e,t,a){const n=e.quantizationTable,s=e.blockData;let r,i,o,l,f,c,h,u,m,p,d,g,b,w,j,k,y;if(!n)throw new JpegError("missing required Quantization Table.");for(let e=0;e<64;e+=8){m=s[t+e];p=s[t+e+1];d=s[t+e+2];g=s[t+e+3];b=s[t+e+4];w=s[t+e+5];j=s[t+e+6];k=s[t+e+7];m*=n[e];if(0!==(p|d|g|b|w|j|k)){p*=n[e+1];d*=n[e+2];g*=n[e+3];b*=n[e+4];w*=n[e+5];j*=n[e+6];k*=n[e+7];r=Na*m+128>>8;i=Na*b+128>>8;o=d;l=j;f=Pa*(p-k)+128>>8;u=Pa*(p+k)+128>>8;c=g<<4;h=w<<4;r=r+i+1>>1;i=r-i;y=o*Ma+l*Da+128>>8;o=o*Da-l*Ma+128>>8;l=y;f=f+h+1>>1;h=f-h;u=u+c+1>>1;c=u-c;r=r+l+1>>1;l=r-l;i=i+o+1>>1;o=i-o;y=f*Ba+u*Ha+2048>>12;f=f*Ha-u*Ba+2048>>12;u=y;y=c*Ra+h*Oa+2048>>12;c=c*Oa-h*Ra+2048>>12;h=y;a[e]=r+u;a[e+7]=r-u;a[e+1]=i+h;a[e+6]=i-h;a[e+2]=o+c;a[e+5]=o-c;a[e+3]=l+f;a[e+4]=l-f}else{y=Na*m+512>>10;a[e]=y;a[e+1]=y;a[e+2]=y;a[e+3]=y;a[e+4]=y;a[e+5]=y;a[e+6]=y;a[e+7]=y}}for(let e=0;e<8;++e){m=a[e];p=a[e+8];d=a[e+16];g=a[e+24];b=a[e+32];w=a[e+40];j=a[e+48];k=a[e+56];if(0!==(p|d|g|b|w|j|k)){r=Na*m+2048>>12;i=Na*b+2048>>12;o=d;l=j;f=Pa*(p-k)+2048>>12;u=Pa*(p+k)+2048>>12;c=g;h=w;r=4112+(r+i+1>>1);i=r-i;y=o*Ma+l*Da+2048>>12;o=o*Da-l*Ma+2048>>12;l=y;f=f+h+1>>1;h=f-h;u=u+c+1>>1;c=u-c;r=r+l+1>>1;l=r-l;i=i+o+1>>1;o=i-o;y=f*Ba+u*Ha+2048>>12;f=f*Ha-u*Ba+2048>>12;u=y;y=c*Ra+h*Oa+2048>>12;c=c*Oa-h*Ra+2048>>12;h=y;m=r+u;k=r-u;p=i+h;j=i-h;d=o+c;w=o-c;g=l+f;b=l-f;m<16?m=0:m>=4080?m=255:m>>=4;p<16?p=0:p>=4080?p=255:p>>=4;d<16?d=0:d>=4080?d=255:d>>=4;g<16?g=0:g>=4080?g=255:g>>=4;b<16?b=0:b>=4080?b=255:b>>=4;w<16?w=0:w>=4080?w=255:w>>=4;j<16?j=0:j>=4080?j=255:j>>=4;k<16?k=0:k>=4080?k=255:k>>=4;s[t+e]=m;s[t+e+8]=p;s[t+e+16]=d;s[t+e+24]=g;s[t+e+32]=b;s[t+e+40]=w;s[t+e+48]=j;s[t+e+56]=k}else{y=Na*m+8192>>14;y=y<-2040?0:y>=2024?255:y+2056>>4;s[t+e]=y;s[t+e+8]=y;s[t+e+16]=y;s[t+e+24]=y;s[t+e+32]=y;s[t+e+40]=y;s[t+e+48]=y;s[t+e+56]=y}}}function buildComponentData(e,t){const a=t.blocksPerLine,n=t.blocksPerColumn,s=new Int16Array(64);for(let e=0;e=s)return null;const i=t.getUint16(a);if(i>=65472&&i<=65534)return{invalid:null,marker:i,offset:a};let o=t.getUint16(r);for(;!(o>=65472&&o<=65534);){if(++r>=s)return null;o=t.getUint16(r)}return{invalid:i.toString(16),marker:o,offset:r}}function prepareComponents(e){const t=Math.ceil(e.samplesPerLine/8/e.maxH),a=Math.ceil(e.scanLines/8/e.maxV);for(const n of e.components){const s=Math.ceil(Math.ceil(e.samplesPerLine/8)*n.h/e.maxH),r=Math.ceil(Math.ceil(e.scanLines/8)*n.v/e.maxV),i=t*n.h,o=64*(a*n.v)*(i+1);n.blockData=new Int16Array(o);n.blocksPerLine=s;n.blocksPerColumn=r}e.mcusPerLine=t;e.mcusPerColumn=a}function readDataBlock(e,t,a){const n=t.getUint16(a);let s=(a+=2)+n-2;const r=findNextFileMarker(e,t,s,a);if(r?.invalid){warn("readDataBlock - incorrect length, current marker is: "+r.invalid);s=r.offset}const i=e.subarray(a,s);return{appData:i,oldOffset:a,newOffset:a+i.length}}function skipData(e,t,a){const n=t.getUint16(a),s=(a+=2)+n-2,r=findNextFileMarker(e,t,s,a);return r?.invalid?r.offset:s}class JpegImage{constructor({decodeTransform:e=null,colorTransform:t=-1}={}){this._decodeTransform=e;this._colorTransform=t}static canUseImageDecoder(e,t=-1){const a=new DataView(e.buffer,e.byteOffset,e.byteLength);let n=null,s=0,r=null,i=a.getUint16(s);s+=2;if(65496!==i)throw new JpegError("SOI not found");i=a.getUint16(s);s+=2;e:for(;65497!==i;){switch(i){case 65505:const{appData:t,oldOffset:o,newOffset:l}=readDataBlock(e,a,s);s=l;if(69===t[0]&&120===t[1]&&105===t[2]&&102===t[3]&&0===t[4]&&0===t[5]){if(n)throw new JpegError("Duplicate EXIF-blocks found.");n={exifStart:o+6,exifEnd:l}}i=a.getUint16(s);s+=2;continue;case 65472:case 65473:case 65474:r=e[s+7];break e;case 65535:255!==e[s]&&s--}s=skipData(e,a,s);i=a.getUint16(s);s+=2}return 4===r||3===r&&0===t?null:n||{}}parse(e,{dnlScanLines:t=null}={}){const a=new DataView(e.buffer,e.byteOffset,e.byteLength),n=e.length-1;let s,r,i=0,o=null,l=null,f=0;const c=[],h=[],u=[];let m=a.getUint16(i);i+=2;if(65496!==m)throw new JpegError("SOI not found");m=a.getUint16(i);i+=2;e:for(;65497!==m;){let p,d,g;switch(m){case 65504:case 65505:case 65506:case 65507:case 65508:case 65509:case 65510:case 65511:case 65512:case 65513:case 65514:case 65515:case 65516:case 65517:case 65518:case 65519:case 65534:const{appData:b,newOffset:w}=readDataBlock(e,a,i);i=w;65504===m&&74===b[0]&&70===b[1]&&73===b[2]&&70===b[3]&&0===b[4]&&(o={version:{major:b[5],minor:b[6]},densityUnits:b[7],xDensity:b[8]<<8|b[9],yDensity:b[10]<<8|b[11],thumbWidth:b[12],thumbHeight:b[13],thumbData:b.subarray(14,14+3*b[12]*b[13])});65518===m&&65===b[0]&&100===b[1]&&111===b[2]&&98===b[3]&&101===b[4]&&(l={version:b[5]<<8|b[6],flags0:b[7]<<8|b[8],flags1:b[9]<<8|b[10],transformCode:b[11]});break;case 65499:const j=a.getUint16(i);i+=2;const k=j+i-2;let y;for(;i>4){if(t>>4!=1)throw new JpegError("DQT - invalid table spec");for(d=0;d<64;d++){y=Ta[d];n[y]=a.getUint16(i);i+=2}}else for(d=0;d<64;d++){y=Ta[d];n[y]=e[i++]}c[15&t]=n}break;case 65472:case 65473:case 65474:if(s)throw new JpegError("Only single frame JPEGs supported");i+=2;s={};s.extended=65473===m;s.progressive=65474===m;s.precision=e[i++];const q=a.getUint16(i);i+=2;s.scanLines=t||q;s.samplesPerLine=a.getUint16(i);i+=2;s.components=[];s.componentIds={};const v=e[i++];let S=0,x=0;for(p=0;p>4,n=15&e[i+1];S>4?h:u)[15&t]=buildHuffmanTable(a,s)}break;case 65501:i+=2;r=a.getUint16(i);i+=2;break;case 65498:const F=1===++f&&!t;i+=2;const T=e[i++],O=[];for(p=0;p>4];n.huffmanTableAC=h[15&r];O.push(n)}const R=e[i++],H=e[i++],D=e[i++];try{i+=decodeScan(e,a,i,s,O,r,R,H,D>>4,15&D,F)}catch(t){if(t instanceof DNLMarkerError){warn(`${t.message} -- attempting to re-parse the JPEG image.`);return this.parse(e,{dnlScanLines:t.scanLines})}if(t instanceof EOIMarkerError){warn(`${t.message} -- ignoring the rest of the image data.`);break e}throw t}break;case 65500:i+=4;break;case 65535:255!==e[i]&&i--;break;default:const M=findNextFileMarker(e,a,i-2,i-3);if(M?.invalid){warn("JpegImage.parse - unexpected data, current marker is: "+M.invalid);i=M.offset;break}if(!M||i>=n){warn("JpegImage.parse - reached the end of the image data without finding an EOI marker (0xFFD9).");break e}throw new JpegError("JpegImage.parse - unknown marker: "+m.toString(16))}if(i>8)+v[m+1];return j}get _isColorConversionNeeded(){return this.adobe?!!this.adobe.transformCode:3===this.numComponents?0!==this._colorTransform&&(82!==this.components[0].index||71!==this.components[1].index||66!==this.components[2].index):1===this._colorTransform}_convertYccToRgb(e){let t,a,n;for(let s=0,r=e.length;s4)throw new JpegError("Unsupported color mode");const r=this._getLinearizedBlockData(e,t,s);if(1===this.numComponents&&(a||n)){const e=r.length*(a?4:3),t=new Uint8ClampedArray(e);let n=0;if(a)!function grayToRGBA(e,t){if(FeatureTest.isLittleEndian)for(let a=0,n=e.length;a0&&(e=e.subarray(t));break}return e}decodeImage(e){if(this.eof)return this.buffer;e=this.#L(e||this.bytes);const t=new JpegImage(this.jpegOptions);t.parse(e);const a=t.getData({width:this.drawWidth,height:this.drawHeight,forceRGBA:this.forceRGBA,forceRGB:this.forceRGB,isSourcePDF:!0});this.buffer=a;this.bufferLength=a.length;this.eof=!0;return this.buffer}get canAsyncDecodeImageFromBuffer(){return this.stream.isAsync}async getTransferableImage(){if(!await JpegStream.canUseImageDecoder)return null;const e=this.jpegOptions;if(e.decodeTransform)return null;let t;try{const a=this.canAsyncDecodeImageFromBuffer&&await this.stream.asyncGetBytes()||this.bytes;if(!a)return null;let n=this.#L(a);const s=JpegImage.canUseImageDecoder(n,e.colorTransform);if(!s)return null;if(s.exifStart){n=n.slice();n.fill(0,s.exifStart,s.exifEnd)}t=new ImageDecoder({data:n,type:"image/jpeg",preferAnimation:!1});return(await t.decode()).image}catch(e){warn(`getTransferableImage - failed: "${e}".`);return null}finally{t?.close()}}get isImageStream(){return!0}}const Ea=async function OpenJPEG(e={}){var t=e,a="./this.program",quit_=(e,t)=>{throw t},n=import.meta.url;try{new URL(".",n).href}catch{}0;var s,r,i,o,l,f,c,h=console.log.bind(console),u=console.error.bind(console),m=!1,p=!1;function updateMemoryViews(){var e=g.buffer;l=new Int8Array(e);new Int16Array(e);c=new Uint8Array(e);new Uint16Array(e);o=new Int32Array(e);f=new Uint32Array(e);new Float32Array(e);new Float64Array(e);new BigInt64Array(e);new BigUint64Array(e)}class ExitStatus{name="ExitStatus";constructor(e){this.message=`Program terminated with exit(${e})`;this.status=e}}var d,g,callRuntimeCallbacks=e=>{for(;e.length>0;)e.shift()(t)},b=[],addOnPostRun=e=>b.push(e),w=[],addOnPreRun=e=>w.push(e),j=!0,k=0,y={},handleException=e=>{if(e instanceof ExitStatus||"unwind"==e)return s;quit_(0,e)},keepRuntimeAlive=()=>j||k>0,_proc_exit=e=>{s=e;if(!keepRuntimeAlive()){t.onExit?.(e);m=!0}quit_(0,new ExitStatus(e))},_exit=(e,t)=>{s=e;_proc_exit(e)},callUserCallback=e=>{if(!m)try{return e()}catch(e){handleException(e)}finally{(()=>{if(!keepRuntimeAlive())try{_exit(s)}catch(e){handleException(e)}})()}},alignMemory=(e,t)=>Math.ceil(e/t)*t,growMemory=e=>{var t=(e-g.buffer.byteLength+65535)/65536|0;try{g.grow(t);updateMemoryViews();return 1}catch(e){}},q={},getEnvStrings=()=>{if(!getEnvStrings.strings){var e={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:(globalThis.navigator?.language??"C").replace("-","_")+".UTF-8",_:a||"./this.program"};for(var t in q)void 0===q[t]?delete e[t]:e[t]=q[t];var n=[];for(var t in e)n.push(`${t}=${e[t]}`);getEnvStrings.strings=n}return getEnvStrings.strings},stringToUTF8=(e,t,a)=>((e,t,a,n)=>{if(!(n>0))return 0;for(var s=a,r=a+n-1,i=0;i=r)break;t[a++]=o}else if(o<=2047){if(a+1>=r)break;t[a++]=192|o>>6;t[a++]=128|63&o}else if(o<=65535){if(a+2>=r)break;t[a++]=224|o>>12;t[a++]=128|o>>6&63;t[a++]=128|63&o}else{if(a+3>=r)break;t[a++]=240|o>>18;t[a++]=128|o>>12&63;t[a++]=128|o>>6&63;t[a++]=128|63&o;i++}}t[a]=0;return a-s})(e,c,t,a),lengthBytesUTF8=e=>{for(var t=0,a=0;a=55296&&n<=57343){t+=4;++a}else t+=3}return t},v=[null,[],[]],S=globalThis.TextDecoder&&new TextDecoder,UTF8ArrayToString=(e,t=0,a,n)=>{var s=((e,t,a,n)=>{var s=t+a;if(n)return s;for(;e[t]&&!(t>=s);)++t;return t})(e,t,a,n);if(s-t>16&&e.buffer&&S)return S.decode(e.subarray(t,s));for(var r="";t>10,56320|1023&f)}}else r+=String.fromCharCode((31&i)<<6|o)}else r+=String.fromCharCode(i)}return r},printChar=(e,t)=>{var a=v[e];if(0===t||10===t){(1===e?h:u)(UTF8ArrayToString(a));a.length=0}else a.push(t)},UTF8ToString=(e,t,a)=>e?UTF8ArrayToString(c,e,t,a):"";t.noExitRuntime&&(j=t.noExitRuntime);t.print&&(h=t.print);t.printErr&&(u=t.printErr);t.wasmBinary&&t.wasmBinary;t.arguments&&t.arguments;t.thisProgram&&(a=t.thisProgram);if(t.preInit){"function"==typeof t.preInit&&(t.preInit=[t.preInit]);for(;t.preInit.length>0;)t.preInit.shift()()}t.writeArrayToMemory=(e,t)=>{l.set(e,t)};var x,C={m:()=>function abort(e){t.onAbort?.(e);u(e=`Aborted(${e})`);m=!0;e+=". Build with -sASSERTIONS for more info.";var a=new WebAssembly.RuntimeError(e);i?.(a);throw a}(""),l:()=>{j=!1;k=0},i:(e,t)=>{if(y[e]){clearTimeout(y[e].id);delete y[e]}if(!t)return 0;var a=setTimeout(()=>{delete y[e];callUserCallback(()=>d(e,performance.now()))},t);y[e]={id:a,timeout_ms:t};return 0},f:function _copy_pixels_1(e,a){e>>=2;const n=t.imageData=new Uint8ClampedArray(a),s=o.subarray(e,e+a);n.set(s)},e:function _copy_pixels_3(e,a,n,s){e>>=2;a>>=2;n>>=2;const r=t.imageData=new Uint8ClampedArray(3*s),i=o.subarray(e,e+s),l=o.subarray(a,a+s),f=o.subarray(n,n+s);for(let e=0;e>=2;a>>=2;n>>=2;s>>=2;const i=t.imageData=new Uint8ClampedArray(4*r),l=o.subarray(e,e+r),f=o.subarray(a,a+r),c=o.subarray(n,n+r),h=o.subarray(s,s+r);for(let e=0;e{var t=c.length,a=2147483648;if((e>>>=0)>a)return!1;for(var n=1;n<=4;n*=2){var s=t*(1+.2/n);s=Math.min(s,e+100663296);var r=Math.min(a,alignMemory(Math.max(e,s),65536));if(growMemory(r))return!0}return!1},o:(e,t)=>{var a=0,n=0;for(var s of getEnvStrings()){var r=t+a;f[e+n>>2]=r;a+=stringToUTF8(s,r,1/0)+1;n+=4}return 0},p:(e,t)=>{var a=getEnvStrings();f[e>>2]=a.length;var n=0;for(var s of a)n+=lengthBytesUTF8(s)+1;f[t>>2]=n;return 0},n:function _fd_seek(e,t,a,n){t=(s=t)<-9007199254740992||s>9007199254740992?NaN:Number(s);var s;return 70},b:(e,t,a,n)=>{for(var s=0,r=0;r>2],o=f[t+4>>2];t+=8;for(var l=0;l>2]=s;return 0},q:function _gray_to_rgba(e,a){e>>=2;const n=t.imageData=new Uint8ClampedArray(4*a),s=o.subarray(e,e+a);for(let e=0;e>=2;a>>=2;const s=t.imageData=new Uint8ClampedArray(4*n),r=o.subarray(e,e+n),i=o.subarray(a,a+n);for(let e=0;e>=2;a>>=2;n>>=2;const r=t.imageData=new Uint8ClampedArray(4*s),i=o.subarray(e,e+s),l=o.subarray(a,a+s),f=o.subarray(n,n+s);for(let e=0;e{t.instantiateWasm(e,(e,t)=>{a(receiveInstance(e))})})}();!function run(){!function preRun(){if(t.preRun){"function"==typeof t.preRun&&(t.preRun=[t.preRun]);for(;t.preRun.length;)addOnPreRun(t.preRun.shift())}callRuntimeCallbacks(w)}();function doRun(){t.calledRun=!0;if(!m){!function initRuntime(){p=!0;x.s()}();r?.(t);t.onRuntimeInitialized?.();!function postRun(){if(t.postRun){"function"==typeof t.postRun&&(t.postRun=[t.postRun]);for(;t.postRun.length;)addOnPostRun(t.postRun.shift())}callRuntimeCallbacks(b)}()}}if(t.setStatus){t.setStatus("Running...");setTimeout(()=>{setTimeout(()=>t.setStatus(""),1);doRun()},1)}else doRun()}();return p?t:new Promise((e,t)=>{r=e;i=t})};class JpxError extends aa{constructor(e){super(e,"JpxError")}}class JpxImage{static#B=null;static#D=null;static#M=null;static#C=!0;static#N=!0;static#I=null;static setOptions({handler:e,useWasm:t,useWorkerFetch:a,wasmUrl:n}){this.#C=t;this.#N=a;this.#I=n;a||(this.#D=e)}static async#P(e){const t=`${this.#I}openjpeg_nowasm_fallback.js`;let a=null;try{a=(await import( +/*webpackIgnore: true*/ +/*@vite-ignore*/ +t)).default()}catch(e){warn(`JpxImage#getJsModule: ${e}`)}e(a)}static async#E(e,t,a){const n="openjpeg.wasm";try{this.#B||(this.#N?this.#B=await fetchBinaryData(`${this.#I}${n}`):this.#B=await this.#D.sendWithPromise("FetchBinaryData",{kind:"wasmUrl",filename:n}));return a((await WebAssembly.instantiate(this.#B,t)).instance)}catch(t){warn(`JpxImage#instantiateWasm: ${t}`);this.#P(e);return null}finally{this.#D=null}}static async decode(e,{numComponents:t=4,isIndexedColormap:a=!1,smaskInData:n=!1,reducePower:s=0}={}){if(!this.#M){const{promise:e,resolve:t}=Promise.withResolvers(),a=[e];this.#C?a.push(Ea({warn,instantiateWasm:this.#E.bind(this,t)})):this.#P(t);this.#M=Promise.race(a)}const r=await this.#M;if(!r)throw new JpxError("OpenJPEG failed to initialize");let i;try{const o=e.length;i=r._malloc(o);r.writeArrayToMemory(e,i);if(r._jp2_decode(i,o,t>0?t:0,!!a,!!n,s)){const{errorMessages:e}=r;if(e){delete r.errorMessages;throw new JpxError(e)}throw new JpxError("Unknown error")}const{imageData:l}=r;r.imageData=null;return l}finally{i&&r._free(i)}}static cleanup(){this.#M=null}static parseImageProperties(e){let t=e.getByte();for(;t>=0;){const a=t;t=e.getByte();if(65361===(a<<8|t)){e.skip(4);const t=e.getInt32()>>>0,a=e.getInt32()>>>0,n=e.getInt32()>>>0,s=e.getInt32()>>>0;e.skip(16);return{width:t-n,height:a-s,bitsPerComponent:8,componentsCount:e.getUint16()}}}throw new JpxError("No size marker found in JPX stream")}}function addState(e,t,a,n,s){let r=e;for(let e=0,a=t.length-1;e1e3){f=Math.max(f,u);m+=h+2;u=0;h=0}c.push({transform:t,x:u,y:m,w:a.width,h:a.height});u+=a.width+2;h=Math.max(h,a.height)}const p=Math.max(f,u)+1,d=m+h+1,g=new Uint8Array(p*d*4),b=p<<2;for(let e=0;e=0;){t[r-4]=t[r];t[r-3]=t[r+1];t[r-2]=t[r+2];t[r-1]=t[r+3];t[r+a]=t[r+a-4];t[r+a+1]=t[r+a-3];t[r+a+2]=t[r+a-2];t[r+a+3]=t[r+a-1];r-=b}}const w={width:p,height:d};if(e.isOffscreenCanvasSupported){const e=new OffscreenCanvas(p,d);e.getContext("2d").putImageData(new ImageData(new Uint8ClampedArray(g.buffer),p,d),0,0);w.bitmap=e.transferToImageBitmap();w.data=null}else{w.kind=O;w.data=g}a.splice(r,4*l,_t);n.splice(r,4*l,[w,c]);return r+1});addState(_a,[ke,qe,Mt,ye],null,function iterateImageMaskGroup(e,t){const a=e.fnArray,n=(t-(e.iCurr-3))%4;switch(n){case 0:return a[t]===ke;case 1:return a[t]===qe;case 2:return a[t]===Mt;case 3:return a[t]===ye}throw new Error(`iterateImageMaskGroup - invalid pos: ${n}`)},function foundImageMaskGroup(e,t){const a=e.fnArray,n=e.argsArray,s=e.iCurr,r=s-3,i=s-2,o=s-1;let l=Math.floor((t-r)/4);if(l<10)return t-(t-r)%4;let f,c,h=!1;const u=n[o][0],m=n[i][0],p=n[i][1],d=n[i][2],g=n[i][3];if(p===d){h=!0;f=i+4;let e=o+4;for(let t=1;t=4&&a[r-4]===a[i]&&a[r-3]===a[o]&&a[r-2]===a[l]&&a[r-1]===a[f]&&n[r-4][0]===c&&n[r-4][1]===h){u++;m-=5}let p=m+4;for(let e=1;e{const t=e.argsArray,a=t[e.iCurr-1][0];if(a!==Te&&a!==Oe&&a!==Be&&a!==De&&a!==Me&&a!==Ne)return!0;const n=t[e.iCurr-2];return 1===n[0]&&0===n[1]&&0===n[2]&&1===n[3]},()=>!1,(e,t)=>{const{fnArray:n,argsArray:s}=e,r=e.iCurr,i=r-3,o=r-2,l=s[r-1],f=s[o],[,[c],h]=l;if(h){const e=a.slice();Util.axialAlignedBoundingBox(h,f,e);h.set(e);for(let e=0,t=c.length;e=a)break}n=(n||_a)[e[t]];if(n&&!Array.isArray(n)){r.iCurr=t;t++;if(!n.checkFn||(0,n.checkFn)(r)){s=n;n=null}else n=null}else t++}this.state=n;this.match=s;this.lastProcessed=t}flush(){for(;this.match;){const e=this.queue.fnArray.length;this.lastProcessed=(0,this.match.processFn)(this.context,e);this.match=null;this.state=null;this._optimize()}}reset(){this.state=null;this.match=null;this.lastProcessed=0}}class OperatorList{static CHUNK_SIZE=1e3;static CHUNK_SIZE_ABOUT=this.CHUNK_SIZE-5;static isOffscreenCanvasSupported=!1;constructor(e=0,t){this._streamSink=t;this.fnArray=[];this.argsArray=[];this.optimizer=!t||e&p?new NullOptimizer(this):new QueueOptimizer(this);this.dependencies=new Set;this._totalLength=0;this.weight=0;this._resolved=t?null:Promise.resolve()}static setOptions({isOffscreenCanvasSupported:e}){this.isOffscreenCanvasSupported=e}get length(){return this.argsArray.length}get ready(){return this._resolved||this._streamSink.ready}get totalLength(){return this._totalLength+this.length}addOp(e,t){this.optimizer.push(e,t);this.weight++;this._streamSink&&(this.weight>=OperatorList.CHUNK_SIZE||this.weight>=OperatorList.CHUNK_SIZE_ABOUT&&(e===ye||e===Le))&&this.flush()}addImageOps(e,t,a,n=!1){if(n){this.addOp(ke);this.addOp(je,[[["SMask",!1]]])}void 0!==a&&this.addOp(xt,["OC",a]);this.addOp(e,t);void 0!==a&&this.addOp(Ct,[]);n&&this.addOp(ye)}addDependency(e){if(!this.dependencies.has(e)){this.dependencies.add(e);this.addOp(he,[e])}}addDependencies(e){for(const t of e)this.addDependency(t)}addOpList(e){if(e instanceof OperatorList){for(const t of e.dependencies)this.dependencies.add(t);for(let t=0,a=e.length;t"boolean"==typeof e)})(m,2)&&([h,u]=m);this.extendStart=h;this.extendEnd=u;const p=e.getRaw("Function"),d=n.create(p,!0),g=(f-l)/840,b=this.colorStops=[];if(l>=f||g<=0){info("Bad shading domain.");return}const w=new Float32Array(o.numComps),j=new Float32Array(1);let k=0;j[0]=l;d(j,0,w,0);const y=new Uint8ClampedArray(3);o.getRgb(w,0,y);let[q,v,S]=y;b.push([0,Util.makeHexColor(q,v,S)]);let x=1;j[0]=l+g;d(j,0,w,0);o.getRgb(w,0,y);let[C,F,T]=y,O=C-q+1,R=F-v+1,H=T-S+1,D=C-q-1,M=F-v-1,N=T-S-1;for(let e=2;e<840;e++){j[0]=l+e*g;d(j,0,w,0);o.getRgb(w,0,y);const[t,a,n]=y,s=e-k;O=Math.min(O,(t-q+1)/s);R=Math.min(R,(a-v+1)/s);H=Math.min(H,(n-S+1)/s);D=Math.max(D,(t-q-1)/s);M=Math.max(M,(a-v-1)/s);N=Math.max(N,(n-S-1)/s);if(!(D<=O&&M<=R&&N<=H)){const e=Util.makeHexColor(C,F,T);b.push([x/840,e]);O=t-C+1;R=a-F+1;H=n-T+1;D=t-C-1;M=a-F-1;N=n-T-1;k=x;q=C;v=F;S=T}x=e;C=t;F=a;T=n}b.push([1,Util.makeHexColor(C,F,T)]);let E="transparent";e.has("Background")&&(E=o.getRgbHex(e.get("Background"),0));if(!h){b.unshift([0,E]);b[1][0]+=BaseShading.SMALL_NUMBER}if(!u){b.at(-1)[0]-=BaseShading.SMALL_NUMBER;b.push([1,E])}this.colorStops=b}getIR(){const{coordsArr:e,shadingType:t}=this;let a,n,s,r,i;if(t===La){n=[e[0],e[1]];s=[e[2],e[3]];r=null;i=null;a="axial"}else if(t===Ua){n=[e[0],e[1]];s=[e[3],e[4]];r=e[2];i=e[5];a="radial"}else unreachable(`getPattern type unknown: ${t}`);return["RadialAxial",a,this.bbox,this.colorStops,n,s,r,i]}}function buildMeshVertexData(e,t,a){let n=0;for(const e of a)if(e.type===q)n+=e.coords.length;else if(e.type===v){const t=e.verticesPerRow;n+=(Math.floor(e.coords.length/t)-1)*(t-1)*6}const s=new Float32Array(2*n),r=new Uint8Array(4*n);let i=0,o=0;const addVertex=(a,n)=>{s[i++]=e[2*a];s[i++]=e[2*a+1];r[o++]=t[4*n];r[o++]=t[4*n+1];r[o++]=t[4*n+2];o++};for(const e of a){const t=e.coords,a=e.colors;if(e.type===q)for(let e=0,n=t.length;e0)return!0;const e=this.stream.getByte();if(e<0)return!1;this.buffer=e;this.bufferLength=8;return!0}readBits(e){const{stream:t}=this;let{buffer:a,bufferLength:n}=this;if(32===e){if(0===n)return t.getInt32()>>>0;a=a<<24|t.getByte()<<16|t.getByte()<<8|t.getByte();const e=t.getByte();this.buffer=e&(1<>n)>>>0}if(8===e&&0===n)return t.getByte();for(;n>n}align(){this.buffer=0;this.bufferLength=0}readFlag(){return this.readBits(this.context.bitsPerFlag)}readCoordinate(){const{bitsPerCoordinate:e,decode:t}=this.context,a=this.readBits(e),n=this.readBits(e),s=e<32?1/((1<i?i:t;a=a>o?o:a;n=n>>0}function hexToStr(e,t){return 1===t?String.fromCharCode(e[0],e[1]):3===t?String.fromCharCode(e[0],e[1],e[2],e[3]):String.fromCharCode(...e.subarray(0,t+1))}function addHex(e,t,a){let n=0;for(let s=a;s>=0;s--){n+=e[s]+t[s];e[s]=255&n;n>>=8}}function incHex(e,t){let a=1;for(let n=t;n>=0&&a>0;n--){a+=e[n];e[n]=255&a;a>>=8}}const $a=16;class BinaryCMapStream extends Stream{tmpBuf=new Uint8Array(19);constructor(e){super(e,0,e.length,null)}readNumber(){let e,t=0;do{const a=this.getByte();if(a<0)throw new FormatError("unexpected EOF in bcmap");e=!(128&a);t=t<<7|127&a}while(!e);return t}readSigned(){const e=this.readNumber();return 1&e?~(e>>>1):e>>>1}readHex(e,t){e.set(this.getBytes(t+1))}readHexNumber(e,t){let a;const n=this.tmpBuf;let s=0;do{const e=this.getByte();if(e<0)throw new FormatError("unexpected EOF in bcmap");a=!(128&e);n[s++]=127&e}while(!a);let r=t,i=0,o=0;for(;r>=0;){for(;o<8&&n.length>0;){i|=n[--s]<>=8;o-=8}}readHexSigned(e,t){this.readHexNumber(e,t);const a=1&e[t]?255:0;let n=0;for(let s=0;s<=t;s++){n=(1&n)<<8|e[s];e[s]=n>>1^a}}readString(){const e=this.readNumber(),t=new Array(e);for(let a=0;a=0;){const e=u>>5;if(7===e){switch(31&u){case 0:n.readString();break;case 1:r=n.readString()}continue}const a=!!(16&u),s=15&u;if(s+1>$a)throw new Error("BinaryCMapReader.process: Invalid dataSize.");const m=1,p=n.readNumber();switch(e){case 0:n.readHex(i,s);n.readHexNumber(o,s);addHex(o,i,s);t.addCodespaceRange(s+1,hexToInt(i,s),hexToInt(o,s));for(let e=1;e=0;--s){n[a+s]=255&i;i>>=8}}}}class AsciiHexStream extends DecodeStream{constructor(e,t){t&&(t*=.5);super(t);this.stream=e;this.dict=e.dict;this.firstDigit=-1}readBlock(){const e=this.stream.getBytes(8e3);if(!e.length){this.eof=!0;return}const t=e.length+1>>1,a=this.ensureBuffer(this.bufferLength+t);let n=this.bufferLength,s=this.firstDigit;for(const t of e){let e;if(t>=48&&t<=57)e=15&t;else{if(!(t>=65&&t<=70||t>=97&&t<=102)){if(62===t){this.eof=!0;break}continue}e=9+(15&t)}if(s<0)s=e;else{a[n++]=s<<4|e;s=-1}}if(s>=0&&this.eof){a[n++]=s<<4;s=-1}this.firstDigit=s;this.bufferLength=n}}let Ya=(()=>{const e=Int32Array.from([256,402,436,468,500,534,566,598,630,662,694,726,758,790,822,854,886,920,952,984,1016,1048,1080]),t=Int32Array.from([1,2,3,4,0,5,17,6,16,7,8,9,10,11,12,13,14,15]),a=Int32Array.from([0,3,2,1,0,0,0,0,0,0,3,3,3,3,3,3]),n=Int32Array.from([0,0,0,0,-1,1,-2,2,-3,3,-1,1,-2,2,-3,3]),s=Int32Array.from([131072,131076,131075,196610,131072,131076,131075,262145,131072,131076,131075,196610,131072,131076,131075,262149]),r=Int32Array.from([1,5,9,13,17,25,33,41,49,65,81,97,113,145,177,209,241,305,369,497,753,1265,2289,4337,8433,16625]),i=Int32Array.from([2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,6,6,7,8,9,10,11,12,13,24]),o=Int16Array.from([0,0,0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,7,8,9,10,12,14,24]),l=Int16Array.from([0,0,0,0,0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,7,8,9,10,24]),f=new Int16Array(2816);!function unpackCommandLookupTable(e){const t=new Int32Array(24),a=new Int32Array(24);a[0]=2;for(let e=0;e<23;++e){t[e+1]=t[e]+(1<>6,r=-4;if(s>=2){s-=2;r=0}const i=(170064>>2*s&3)<<3|n>>3&7,f=(156228>>2*s&3)<<3|7&n,c=a[f],h=r+Math.min(c,5)-2,u=4*n;e[u]=o[i]|l[f]<<8;e[u+1]=t[i];e[u+2]=a[f];e[u+3]=h}}(f);function log2floor(e){let t=-1,a=16,n=e;for(;a>0;){let e=n>>a;if(0!==e){t+=a;n=e}a>>=1}return t+n}function calculateDistanceAlphabetSize(e,t,a){return 16+t+2*(a<>a),r=log2floor(s)-1;return((r-1<<1|s>>r&1)-1<=16){e.accumulator32=e.shortBuffer[e.halfOffset++]<<16|e.accumulator32>>>16;e.bitOffset-=16}if(0!==readFewBits(e,1)){const t=readFewBits(e,3);return 0===t?1:readFewBits(e,t)+(1<>>a.bitOffset;n+=255&s;const r=e[n]>>16,i=65535&e[n];if(r<=8){a.bitOffset+=r;return i}n+=i;n+=(s&(1<>>8;a.bitOffset+=8+(e[n]>>16);return 65535&e[n]}function readBlockLength(e,t,a){if(a.bitOffset>=16){a.accumulator32=a.shortBuffer[a.halfOffset++]<<16|a.accumulator32>>>16;a.bitOffset-=16}const n=readSymbol(e,t,a),s=i[n];if(a.bitOffset>=16){a.accumulator32=a.shortBuffer[a.halfOffset++]<<16|a.accumulator32>>>16;a.bitOffset-=16}return r[n]+(s<=16?readFewBits(a,s):readManyBits(a,s))}function moveToFront(e,t){let a=t;const n=e[a];for(;a>0;){e[a]=e[a-1];a--}e[0]=n}function readSimpleHuffmanCode(e,t,a,n,s){const r=new Int32Array(t),i=new Int32Array(4),o=1+log2floor(e-1),l=readFewBits(s,2)+1;for(let e=0;e=16){s.accumulator32=s.shortBuffer[s.halfOffset++]<<16|s.accumulator32>>>16;s.bitOffset-=16}const a=readFewBits(s,o);if(a>=t)return makeError(s,-15);i[e]=a}const f=function checkDupes(e,t,a){for(let n=0;n=16){i.accumulator32=i.shortBuffer[i.halfOffset++]<<16|i.accumulator32>>>16;i.bitOffset-=16}const n=i.accumulator32>>>i.bitOffset&15;i.bitOffset+=s[n]>>16;const r=65535&s[n];l[a]=r;if(0!==r){f-=32>>r;c++;if(f<=0)break}}if(0!==f&&1!==c)return makeError(i,-4);const h=function readHuffmanCodeLengths(e,t,a,n){let s=0,r=8,i=0,o=0,l=32768;const f=new Int32Array(33);buildHuffmanTable(f,f.length-1,5,e,18);for(;s0;){if(n.halfOffset>2030){const e=readMoreInput(n);if(e<0)return e}if(n.bitOffset>=16){n.accumulator32=n.shortBuffer[n.halfOffset++]<<16|n.accumulator32>>>16;n.bitOffset-=16}const e=n.accumulator32>>>n.bitOffset&31;n.bitOffset+=f[e]>>16;const c=65535&f[e];if(c<16){i=0;a[s++]=c;if(0!==c){r=c;l-=32768>>c}}else{const e=c-14;let f=0;16===c&&(f=r);if(o!==f){i=0;o=f}const h=i;if(i>0){i-=2;i<<=e}if(n.bitOffset>=16){n.accumulator32=n.shortBuffer[n.halfOffset++]<<16|n.accumulator32>>>16;n.bitOffset-=16}i+=readFewBits(n,e)+3;const u=i-h;if(s+u>t)return makeError(n,-2);for(let e=0;e2030){const e=readMoreInput(s);if(e<0)return e}if(s.bitOffset>=16){s.accumulator32=s.shortBuffer[s.halfOffset++]<<16|s.accumulator32>>>16;s.bitOffset-=16}const r=readFewBits(s,2);return 1===r?readSimpleHuffmanCode(e,t,a,n,s):readComplexHuffmanCode(t,r,a,n,s)}function decodeContextMap(t,a,n){let s;if(n.halfOffset>2030){s=readMoreInput(n);if(s<0)return s}const r=decodeVarLenUnsignedByte(n)+1;if(1===r){a.fill(0,0,t);return r}if(n.bitOffset>=16){n.accumulator32=n.shortBuffer[n.halfOffset++]<<16|n.accumulator32>>>16;n.bitOffset-=16}let i=0;0!==readFewBits(n,1)&&(i=readFewBits(n,4)+1);const o=r+i,l=e[o+31>>5],f=new Int32Array(l+1),c=f.length-1;s=readHuffmanCode(o,o,f,c,n);if(s<0)return s;let h=0;for(;h2030){s=readMoreInput(n);if(s<0)return s}if(n.bitOffset>=16){n.accumulator32=n.shortBuffer[n.halfOffset++]<<16|n.accumulator32>>>16;n.bitOffset-=16}const e=readSymbol(f,c,n);if(0===e){a[h]=0;h++}else if(e<=i){if(n.bitOffset>=16){n.accumulator32=n.shortBuffer[n.halfOffset++]<<16|n.accumulator32>>>16;n.bitOffset-=16}let s=(1<=t)return makeError(n,-3);a[h]=0;h++;s--}}else{a[h]=e-i;h++}}if(n.bitOffset>=16){n.accumulator32=n.shortBuffer[n.halfOffset++]<<16|n.accumulator32>>>16;n.bitOffset-=16}1===readFewBits(n,1)&&function inverseMoveToFrontTransform(e,t){const a=new Int32Array(256);for(let e=0;e<256;++e)a[e]=e;for(let n=0;n=16){e.accumulator32=e.shortBuffer[e.halfOffset++]<<16|e.accumulator32>>>16;e.bitOffset-=16}let r=readSymbol(e.blockTrees,2*t,e);const i=readBlockLength(e.blockTrees,2*t+1,e);1===r?r=n[s+1]+1:0===r?r=n[s]:r-=2;r>=a&&(r-=a);n[s]=n[s+1];n[s+1]=r;return i}function decodeLiteralBlockSwitch(e){e.literalBlockLength=decodeBlockTypeAndLength(e,0,e.numLiteralBlockTypes);const t=e.rings[5];e.contextMapSlice=t<<6;e.literalTreeIdx=255&e.contextMap[e.contextMapSlice];const a=e.contextModes[t];e.contextLookupOffset1=a<<9;e.contextLookupOffset2=e.contextLookupOffset1+256}function decodeCommandBlockSwitch(e){e.commandBlockLength=decodeBlockTypeAndLength(e,1,e.numCommandBlockTypes);e.commandTreeIdx=e.rings[7]}function decodeDistanceBlockSwitch(e){e.distanceBlockLength=decodeBlockTypeAndLength(e,2,e.numDistanceBlockTypes);e.distContextMapSlice=e.rings[9]<<2}function readNextMetablockHeader(e){if(0!==e.inputEnd){e.nextRunningState=10;e.runningState=12;return 0}e.literalTreeGroup=new Int32Array(0);e.commandTreeGroup=new Int32Array(0);e.distanceTreeGroup=new Int32Array(0);let t;if(e.halfOffset>2030){t=readMoreInput(e);if(t<0)return t}t=function decodeMetaBlockLength(e){if(e.bitOffset>=16){e.accumulator32=e.shortBuffer[e.halfOffset++]<<16|e.accumulator32>>>16;e.bitOffset-=16}e.inputEnd=readFewBits(e,1);e.metaBlockLength=0;e.isUncompressed=0;e.isMetadata=0;if(0!==e.inputEnd&&0!==readFewBits(e,1))return 0;const t=readFewBits(e,2)+4;if(7===t){e.isMetadata=1;if(0!==readFewBits(e,1))return makeError(e,-6);const t=readFewBits(e,2);if(0===t)return 0;for(let a=0;a=16){e.accumulator32=e.shortBuffer[e.halfOffset++]<<16|e.accumulator32>>>16;e.bitOffset-=16}const n=readFewBits(e,8);if(0===n&&a+1===t&&t>1)return makeError(e,-8);e.metaBlockLength+=n<<8*a}}else for(let a=0;a=16){e.accumulator32=e.shortBuffer[e.halfOffset++]<<16|e.accumulator32>>>16;e.bitOffset-=16}const n=readFewBits(e,4);if(0===n&&a+1===t&&t>4)return makeError(e,-8);e.metaBlockLength+=n<<4*a}e.metaBlockLength++;0===e.inputEnd&&(e.isUncompressed=readFewBits(e,1));return 0}(e);if(t<0)return t;if(0===e.metaBlockLength&&0===e.isMetadata)return 0;if(0!==e.isUncompressed||0!==e.isMetadata){t=jumpToByteBoundary(e);if(t<0)return t;0===e.isMetadata?e.runningState=6:e.runningState=5}else e.runningState=3;if(0!==e.isMetadata)return 0;e.expectedTotalSize+=e.metaBlockLength;e.expectedTotalSize>1<<30&&(e.expectedTotalSize=1<<30);e.ringBufferSizee.expectedTotalSize){const a=e.expectedTotalSize;for(;t>>1>a;)t>>=1;0===e.inputEnd&&t<16384&&e.maxRingBufferSize>=16384&&(t=16384)}if(t<=e.ringBufferSize)return;const a=new Int8Array(t+37),n=e.ringBuffer;0!==n.length&&a.set(n.subarray(0,e.ringBufferSize),0);e.ringBuffer=a;e.ringBufferSize=t}(e);return 0}function readMetablockPartition(e,t,a){let n=e.blockTrees[2*t];if(a<=1){e.blockTrees[2*t+1]=n;e.blockTrees[2*t+2]=n;return 1<<28}const s=a+2;let r=readHuffmanCode(s,s,e.blockTrees,2*t,e);if(r<0)return r;n+=r;e.blockTrees[2*t+1]=n;r=readHuffmanCode(26,26,e.blockTrees,2*t+1,e);if(r<0)return r;n+=r;e.blockTrees[2*t+2]=n;return readBlockLength(e.blockTrees,2*t+1,e)}function readMetablockHuffmanCodesAndContextMaps(e){e.numLiteralBlockTypes=decodeVarLenUnsignedByte(e)+1;let t=readMetablockPartition(e,0,e.numLiteralBlockTypes);if(t<0)return t;e.literalBlockLength=t;e.numCommandBlockTypes=decodeVarLenUnsignedByte(e)+1;t=readMetablockPartition(e,1,e.numCommandBlockTypes);if(t<0)return t;e.commandBlockLength=t;e.numDistanceBlockTypes=decodeVarLenUnsignedByte(e)+1;t=readMetablockPartition(e,2,e.numDistanceBlockTypes);if(t<0)return t;e.distanceBlockLength=t;if(e.halfOffset>2030){t=readMoreInput(e);if(t<0)return t}if(e.bitOffset>=16){e.accumulator32=e.shortBuffer[e.halfOffset++]<<16|e.accumulator32>>>16;e.bitOffset-=16}e.distancePostfixBits=readFewBits(e,2);e.numDirectDistanceCodes=readFewBits(e,4)<=16){e.accumulator32=e.shortBuffer[e.halfOffset++]<<16|e.accumulator32>>>16;e.bitOffset-=16}e.contextModes[a]=readFewBits(e,2);a++}if(e.halfOffset>2030){t=readMoreInput(e);if(t<0)return t}}const n=e.numLiteralBlockTypes<<6;e.contextMap=new Int8Array(n);t=decodeContextMap(n,e.contextMap,e);if(t<0)return t;const s=t;e.trivialLiteralContext=1;for(let t=0;t>6){e.trivialLiteralContext=0;break}e.distContextMap=new Int8Array(e.numDistanceBlockTypes<<2);t=decodeContextMap(e.numDistanceBlockTypes<<2,e.distContextMap,e);if(t<0)return t;const r=t;e.literalTreeGroup=new Int32Array(huffmanTreeGroupAllocSize(256,s));t=decodeHuffmanTreeGroup(256,256,s,e,e.literalTreeGroup);if(t<0)return t;e.commandTreeGroup=new Int32Array(huffmanTreeGroupAllocSize(704,e.numCommandBlockTypes));t=decodeHuffmanTreeGroup(704,704,e.numCommandBlockTypes,e,e.commandTreeGroup);if(t<0)return t;let i=calculateDistanceAlphabetSize(e.distancePostfixBits,e.numDirectDistanceCodes,24),o=i;if(1===e.isLargeWindow){i=calculateDistanceAlphabetSize(e.distancePostfixBits,e.numDirectDistanceCodes,62);t=calculateDistanceAlphabetLimit(e,2147483644,e.distancePostfixBits,e.numDirectDistanceCodes);if(t<0)return t;o=t}e.distanceTreeGroup=new Int32Array(huffmanTreeGroupAllocSize(o,r));t=decodeHuffmanTreeGroup(i,o,r,e,e.distanceTreeGroup);if(t<0)return t;!function calculateDistanceLut(e,t){const a=e.distExtraBits,n=e.distOffset,s=e.distancePostfixBits,r=e.numDirectDistanceCodes,i=1<>>e.bitOffset;e.bitOffset+=8;r--}if(0===r)return 0;const i=Math.min(halfAvailable(e),r>>1);if(i>0){const a=e.halfOffset<<1,n=i<<1;t.set(e.byteBuffer.subarray(a,a+n),s);s+=n;r-=n;e.halfOffset+=i}if(0===r)return 0;if(halfAvailable(e)>0){if(e.bitOffset>=16){e.accumulator32=e.shortBuffer[e.halfOffset++]<<16|e.accumulator32>>>16;e.bitOffset-=16}for(;0!==r;){t[s++]=e.accumulator32>>>e.bitOffset;e.bitOffset+=8;r--}return checkHealth(e,0)}for(;r>0;){const a=readInput(e,t,s,r);if(a<-1)return a;if(a<=0)return makeError(e,-16);s+=a;r-=a}return 0}(e,t,e.pos,n);if(a<0)return a;e.metaBlockLength-=n;e.pos+=n;if(e.pos===e.ringBufferSize){e.nextRunningState=6;e.runningState=12;return 0}a=reload(e);if(a<0)return a;e.runningState=2;return 0}function writeRingBuffer(e){const t=Math.min(e.outputLength-e.outputUsed,e.ringBufferBytesReady-e.ringBufferBytesWritten);if(0!==t){e.output.set(e.ringBuffer.subarray(e.ringBufferBytesWritten,e.ringBufferBytesWritten+t),e.outputOffset+e.outputUsed);e.outputUsed+=t;e.ringBufferBytesWritten+=t}return e.outputUsed>5]}function decodeHuffmanTreeGroup(e,t,a,n,s){let r=a;for(let i=0;i2147483644)return makeError(e,-9);const a=e.distance-e.maxDistance-1-e.cdTotalSize;if(a<0){const t=function initializeCompoundDictionaryCopy(e,t,a){-1===e.cdBlockBits&&function initializeCompoundDictionary(e){e.cdBlockMap=new Int8Array(256);let t=8;for(;e.cdTotalSize-1>>t;)t++;t-=8;e.cdBlockBits=t;let a=0,n=0;for(;a>t]=n;a+=1<>e.cdBlockBits];for(;t>=e.cdChunkOffsets[n+1];)n++;if(e.cdTotalSize>t+a)return makeError(e,-9);e.distRbIdx=e.distRbIdx+1&3;e.rings[e.distRbIdx]=e.distance;e.metaBlockLength-=a;e.cdBrIndex=n;e.cdBrOffset=t-e.cdChunkOffsets[n];e.cdBrLength=a;e.cdBrCopied=0;return 0}(e,-a-1,e.copyLength);if(t<0)return t;e.runningState=14}else{const n=u,s=e.copyLength;if(s>31)return makeError(e,-9);const r=p[s];if(0===r)return makeError(e,-9);let i=m[s];const o=a>>r;i+=(a&(1<=l.numTransforms)return makeError(e,-9);const f=function transformDictionaryWord(e,t,a,n,s,r,i){let o=t;const l=r.triplets,f=r.prefixSuffixStorage,c=r.prefixSuffixHeads,h=3*i,u=l[h],m=l[h+1],p=l[h+2];let d=c[u];const g=c[u+1];let b=c[p];const w=c[p+1];let j=m-11,k=m;(j<1||j>9)&&(j=0);(k<1||k>9)&&(k=0);for(;d!==g;)e[o++]=f[d++];let y=s;j>y&&(j=y);let q=n+j;y-=j;y-=k;let v=y;for(;v>0;){e[o++]=a[q++];v--}if(10===m||11===m){let t=o-y;10===m&&(y=1);for(;y>0;){const a=255&e[t];if(a<192){a>=97&&a<=122&&(e[t]=32^e[t]);t+=1;y-=1}else if(a<224){e[t+1]=32^e[t+1];t+=2;y-=2}else{e[t+2]=5^e[t+2];t+=3;y-=3}}}else if(21===m||22===m){let t=o-y;const a=r.params[i];let n=16777216-(32768&a)+(32767&a);for(;y>0;){let a=1;const s=255&e[t];if(s<128){n+=s;e[t]=127&n}else if(s<192);else if(s<224)if(y>=2){const r=e[t+1];n+=63&r|(31&s)<<6;e[t]=192|n>>6&31;e[t+1]=192&r|63&n;a=2}else a=y;else if(s<240)if(y>=3){const r=e[t+1],i=e[t+2];n+=63&i|(63&r)<<6|(15&s)<<12;e[t]=224|n>>12&15;e[t+1]=192&r|n>>6&63;e[t+2]=192&i|63&n;a=3}else a=y;else if(s<248)if(y>=4){const r=e[t+1],i=e[t+2],o=e[t+3];n+=63&o|(63&i)<<6|(63&r)<<12|(7&s)<<18;e[t]=240|n>>18&7;e[t+1]=192&r|n>>12&63;e[t+2]=192&i|n>>6&63;e[t+3]=192&o|63&n;a=4}else a=y;t+=a;y-=a;21===m&&(y=0)}}for(;b!==w;)e[o++]=f[b++];return o-t}(e.ringBuffer,e.pos,n,i,s,l,o);e.pos+=f;e.metaBlockLength-=f;if(e.pos>=t){e.nextRunningState=4;e.runningState=12;return 0}e.runningState=4}return 0}function copyFromCompoundDictionary(e,t){let a=e.pos;const n=a;for(;e.cdBrLength!==e.cdBrCopied;){const n=t-a,s=e.cdChunkOffsets[e.cdBrIndex+1]-e.cdChunkOffsets[e.cdBrIndex]-e.cdBrOffset;let r=e.cdBrLength-e.cdBrCopied;r>s&&(r=s);r>n&&(r=n);e.ringBuffer.set(e.cdChunks[e.cdBrIndex].subarray(e.cdBrOffset,e.cdBrOffset+r),a);a+=r;e.cdBrOffset+=r;e.cdBrCopied+=r;if(r===s){e.cdBrIndex++;e.cdBrOffset=0}if(a>=t)break}return a-n}function decompress(e){let t;if(0===e.runningState)return makeError(e,-25);if(e.runningState<0)return makeError(e,-28);if(11===e.runningState)return makeError(e,-22);if(1===e.runningState){const t=function decodeWindowBits(e){const t=e.isLargeWindow;e.isLargeWindow=0;if(e.bitOffset>=16){e.accumulator32=e.shortBuffer[e.halfOffset++]<<16|e.accumulator32>>>16;e.bitOffset-=16}if(0===readFewBits(e,1))return 16;let a=readFewBits(e,3);if(0!==a)return 17+a;a=readFewBits(e,3);if(0!==a){if(1===a){if(0===t)return-1;e.isLargeWindow=1;if(1===readFewBits(e,1))return-1;a=readFewBits(e,6);return a<10||a>30?-1:a}return 8+a}return 17}(e);if(-1===t)return makeError(e,-11);e.maxRingBufferSize=1<2030){t=readMoreInput(e);if(t<0)return t}0===e.commandBlockLength&&decodeCommandBlockSwitch(e);e.commandBlockLength--;if(e.bitOffset>=16){e.accumulator32=e.shortBuffer[e.halfOffset++]<<16|e.accumulator32>>>16;e.bitOffset-=16}const o=readSymbol(e.commandTreeGroup,e.commandTreeIdx,e)<<2,l=f[o],c=f[o+1],u=f[o+2];e.distanceCode=f[o+3];if(e.bitOffset>=16){e.accumulator32=e.shortBuffer[e.halfOffset++]<<16|e.accumulator32>>>16;e.bitOffset-=16}const m=255&l;e.insertLength=c+(m<=16?readFewBits(e,m):readManyBits(e,m));if(e.bitOffset>=16){e.accumulator32=e.shortBuffer[e.halfOffset++]<<16|e.accumulator32>>>16;e.bitOffset-=16}const p=l>>8;e.copyLength=u+(p<=16?readFewBits(e,p):readManyBits(e,p));e.j=0;e.runningState=7;continue;case 7:if(0!==e.trivialLiteralContext)for(;e.j2030){t=readMoreInput(e);if(t<0)return t}0===e.literalBlockLength&&decodeLiteralBlockSwitch(e);e.literalBlockLength--;if(e.bitOffset>=16){e.accumulator32=e.shortBuffer[e.halfOffset++]<<16|e.accumulator32>>>16;e.bitOffset-=16}i[e.pos]=readSymbol(e.literalTreeGroup,e.literalTreeIdx,e);e.pos++;e.j++;if(e.pos>=s){e.nextRunningState=7;e.runningState=12;break}}else{let a=255&i[e.pos-1&r],n=255&i[e.pos-2&r];for(;e.j2030){t=readMoreInput(e);if(t<0)return t}0===e.literalBlockLength&&decodeLiteralBlockSwitch(e);const r=h[e.contextLookupOffset1+a]|h[e.contextLookupOffset2+n],o=255&e.contextMap[e.contextMapSlice+r];e.literalBlockLength--;n=a;if(e.bitOffset>=16){e.accumulator32=e.shortBuffer[e.halfOffset++]<<16|e.accumulator32>>>16;e.bitOffset-=16}a=readSymbol(e.literalTreeGroup,o,e);i[e.pos]=a;e.pos++;e.j++;if(e.pos>=s){e.nextRunningState=7;e.runningState=12;break}}}if(7!==e.runningState)continue;e.metaBlockLength-=e.insertLength;if(e.metaBlockLength<=0){e.runningState=4;continue}let d=e.distanceCode;if(d<0)e.distance=e.rings[e.distRbIdx];else{if(e.halfOffset>2030){t=readMoreInput(e);if(t<0)return t}0===e.distanceBlockLength&&decodeDistanceBlockSwitch(e);e.distanceBlockLength--;if(e.bitOffset>=16){e.accumulator32=e.shortBuffer[e.halfOffset++]<<16|e.accumulator32>>>16;e.bitOffset-=16}const s=255&e.distContextMap[e.distContextMapSlice+d];d=readSymbol(e.distanceTreeGroup,s,e);if(d<16){const t=e.distRbIdx+a[d]&3;e.distance=e.rings[t]+n[d];if(e.distance<0)return makeError(e,-12)}else{const t=e.distExtraBits[d];let a;if(e.bitOffset+t<=32)a=readFewBits(e,t);else{if(e.bitOffset>=16){e.accumulator32=e.shortBuffer[e.halfOffset++]<<16|e.accumulator32>>>16;e.bitOffset-=16}a=t<=16?readFewBits(e,t):readManyBits(e,t)}e.distance=e.distOffset[d]+(a<e.maxDistance){e.runningState=9;continue}if(d>0){e.distRbIdx=e.distRbIdx+1&3;e.rings[e.distRbIdx]=e.distance}if(e.copyLength>e.metaBlockLength)return makeError(e,-9);e.j=0;e.runningState=8;continue;case 8:let g=e.pos-e.distance&r,b=e.pos;const w=e.copyLength-e.j,j=g+w,k=b+w;if(jb&&k>g){const e=w+3>>2;for(let t=0;t=s){e.nextRunningState=8;e.runningState=12;break}}8===e.runningState&&(e.runningState=4);continue;case 9:t=doUseDictionary(e,s);if(t<0)return t;continue;case 14:e.pos+=copyFromCompoundDictionary(e,s);if(e.pos>=s){e.nextRunningState=14;e.runningState=12;return 2}e.runningState=4;continue;case 5:for(;e.metaBlockLength>0;){if(e.halfOffset>2030){t=readMoreInput(e);if(t<0)return t}if(e.bitOffset>=16){e.accumulator32=e.shortBuffer[e.halfOffset++]<<16|e.accumulator32>>>16;e.bitOffset-=16}readFewBits(e,8);e.metaBlockLength--}e.runningState=2;continue;case 6:t=copyUncompressedData(e);if(t<0)return t;continue;case 12:e.ringBufferBytesReady=Math.min(e.pos,e.ringBufferSize);e.runningState=13;continue;case 13:t=writeRingBuffer(e);if(0!==t)return t;e.pos>=e.maxBackwardDistance&&(e.maxDistance=e.maxBackwardDistance);if(e.pos>=e.ringBufferSize){e.pos>e.ringBufferSize&&i.copyWithin(0,e.ringBufferSize,e.pos);e.pos=e.pos&r;e.ringBufferBytesWritten=0}e.runningState=e.nextRunningState;continue;default:return makeError(e,-28)}if(10!==e.runningState)return makeError(e,-29);if(e.metaBlockLength<0)return makeError(e,-10);t=jumpToByteBoundary(e);if(0!==t)return t;t=checkHealth(e,1);return 0!==t?t:1}const c=new function Transforms(e,t,a){this.numTransforms=0;this.triplets=new Int32Array(0);this.prefixSuffixStorage=new Int8Array(0);this.prefixSuffixHeads=new Int32Array(0);this.params=new Int16Array(0);this.numTransforms=e;this.triplets=new Int32Array(3*e);this.params=new Int16Array(e);this.prefixSuffixStorage=new Int8Array(t);this.prefixSuffixHeads=new Int32Array(a+1)}(121,167,50);!function unpackTransforms(e,t,a,n,s){const r=toUtf8Runes(n),i=r.length;let o=1,l=0;for(let a=0;a#\n#]# for # a # that #. # with #\'# from # by #. The # on # as # is #ing #\n\t#:#ed #(# at #ly #="# of the #. This #,# not #er #al #=\'#ful #ive #less #est #ize #ous #'," !! ! , *! &! \" ! ) * * - ! # ! #!*! + ,$ ! - % . / # 0 1 . \" 2 3!* 4% ! # / 5 6 7 8 0 1 & $ 9 + : ; < ' != > ?! 4 @ 4 2 & A *# ( B C& ) % ) !*# *-% A +! *. D! %' & E *6 F G% ! *A *% H! D I!+! J!+ K +- *4! A L!*4 M N +6 O!*% +.! K *G P +%( ! G *D +D Q +# *K!*G!+D!+# +G +A +4!+% +K!+4!*D!+K!*K");function getNextKey(e,t){let a=1<>=1;return(e&a-1)+a}function replicateValue(e,t,a,n,s){let r=n;for(;r>0;){r-=a;e[t+r]=s}}function nextTableBitSize(e,t,a){let n=t,s=1<0;){replicateValue(e,r+u,p,c,t<<16|i[m++]);u=getNextKey(u,t);o[t]--}}const d=h-1;let g=-1,b=r;p=1;for(let t=a+1;t<=15;++t){p<<=1;for(;o[t]>0;){if((u&d)!==g){b+=c;f=nextTableBitSize(o,t,a);c=1<>a),p,c,t-a<<16|i[m++]);u=getNextKey(u,t);o[t]--}}return h}function readMoreInput(e){if(0!==e.endOfStreamReached)return halfAvailable(e)>=-2?0:makeError(e,-16);const t=e.halfOffset<<1;let a=4096-t;e.byteBuffer.copyWithin(0,t,4096);e.halfOffset=0;for(;a<4096;){const t=4096-a,n=readInput(e,e.byteBuffer,a,t);if(n<-1)return n;if(n<=0){e.endOfStreamReached=1;e.tailBytes=a;a+=1;break}a+=n}!function bytesToNibbles(e,t){const a=e.byteBuffer,n=t>>1,s=e.shortBuffer;for(let e=0;e>3)-4;return a>e.tailBytes?makeError(e,-13):0!==t&&a!==e.tailBytes?makeError(e,-17):0}function readFewBits(e,t){const a=e.accumulator32>>>e.bitOffset&(1<>>16;e.bitOffset-=16;return a|readFewBits(e,t-16)<<16}function prepare(e){if(e.halfOffset>2030){const t=readMoreInput(e);if(0!==t)return t}let t=checkHealth(e,0);if(0!==t)return t;e.accumulator32=e.shortBuffer[e.halfOffset++]<<16|e.accumulator32>>>16;e.bitOffset-=16;e.accumulator32=e.shortBuffer[e.halfOffset++]<<16|e.accumulator32>>>16;e.bitOffset-=16;return 0}function reload(e){return 32===e.bitOffset?prepare(e):0}function jumpToByteBoundary(e){const t=32-e.bitOffset&7;if(0!==t){if(0!==readFewBits(e,t))return makeError(e,-5)}return 0}function halfAvailable(e){let t=2048;0!==e.endOfStreamReached&&(t=e.tailBytes+1>>1);return t-e.halfOffset}const h=new Int32Array(2048);!function unpackLookupTable(e,t,a){for(let t=0;t<256;++t){e[t]=63&t;e[512+t]=t>>2;e[1792+t]=2+(t>>6)}for(let a=0;a<128;++a)e[1024+a]=4*(t.charCodeAt(a)-32);for(let t=0;t<64;++t){e[1152+t]=1&t;e[1216+t]=2+(1&t)}let n=1280;for(let t=0;t<19;++t){const s=3&t,r=a.charCodeAt(t)-32;for(let t=0;t>1;for(let e=0;e!pfwp6s{8-ip<73s{je#+pllmpfbwmlmfwvafyfqlpfmwqffgeb`wjmwldjewkbqn2;s{`bnfkjooalogyllnuljgfbpzqjmdejoosfbhjmjw`lpw0s{8ib`hwbdpajwpqloofgjwhmftmfbq?"..dqltIPLMgvwzMbnfpbofzlv#olwpsbjmibyy`logfzfpejpkttt-qjphwbapsqfu23s{qjpf16s{Aovfgjmd033/abooelqgfbqmtjogal{-ebjqob`hufqpsbjqivmfwf`kje+"sj`hfujo\'+! tbqnolqgglfpsvoo/333jgfbgqbtkvdfpslwevmgavqmkqfe`foohfzpwj`hklvqolppevfo21s{pvjwgfboQPP!bdfgdqfzDFW!fbpfbjnpdjqobjgp;s{8mbuzdqjgwjsp :::tbqpobgz`bqp*8#~sksolpfmvooubpwtjmgQPP#tfbqqfozaffmpbnfgvhfmbpb`bsftjpkdvoeW109kjwppolwdbwfhj`haovqwkfz26s{$$*8*8!=npjftjmpajqgplqwafwbpffhW2;9lqgpwqffnboo53s{ebqnlupalzpX3^-$*8!SLPWafbqhjgp*8~~nbqzwfmg+VH*rvbgyk9\n.pjy....sqls$*8ojewW2:9uj`fbmgzgfaw=QPPsllomf`haoltW259gllqfuboW249ofwpebjolqbosloomlub`lopdfmf#lxplewqlnfwjooqlpp?k0=slvqebgfsjmh?wq=njmj*"+njmfyk9abqpkfbq33*8njoh#..=jqlmeqfggjphtfmwpljosvwp,ip,klozW119JPAMW139bgbnpffp?k1=iplm$/#$`lmwW129#QPPollsbpjbnllm?,s=plvoOJMFelqw`bqwW279?k2=;3s{"..?:s{8W379njhf975Ymj`fjm`kZlqhqj`fyk9\b$**8svqfnbdfsbqbwlmfalmg904Y\\le\\$^*8333/yk9\vwbmhzbqgaltoavpk965YIbub03s{\t~\t&@0&907YifeeF[SJ`bpkujpbdloepmltyk9rvfq-`pppj`hnfbwnjm-ajmggfookjqfsj`pqfmw905YKWWS.132elwltloeFMG#{al{967YALGZgj`h8\t~\tf{jw906Yubqpafbw$~*8gjfw:::8bmmf~~?,Xj^-Obmdhn.^tjqfwlzpbggppfbobof{8\t\n~f`klmjmf-lqd336*wlmziftppbmgofdpqlle333*#133tjmfdfbqgldpallwdbqz`vwpwzofwfnswjlm-{no`l`hdbmd\'+$-63s{Sk-Gnjp`bobmolbmgfphnjofqzbmvmj{gjp`*8~\tgvpw`ojs*-\t\t43s{.133GUGp4^=?wbsfgfnlj((*tbdffvqlskjolswpklofEBRpbpjm.15WobapsfwpVQO#avoh`llh8~\tKFBGX3^*baaqivbm+2:;ofpkwtjm?,j=plmzdvzpev`hsjsf.\t"331*mgltX2^8X^8\tOld#pbow\t\n\nabmdwqjnabwk*x\t33s{\t~*8hl9\0effpbg=p9,,#X^8wloosovd+*x\tx\t#-ip$133sgvboalbw-ISD*8\t~rvlw*8\t\t$*8\t\t~1327132613251324132;132:13131312131113101317131613151314131;131:130313021301130013071306130513041320132113221323133:133;133413351336133713301331133213332:::2::;2::42::52::62::72::02::12::22::32:;:2:;;2:;42:;52:;62:;72:;02:;12:;22:;32:4:2:4;2:442:452:462:472:402:412:422:432:5:2:5;2:542:552:562:572:502:512:522:532:6:2:6;2:642:652:662:672:602:612:622:632333231720:73333::::`lnln/Mpfpwffpwbsfqlwlglkb`f`bgbb/]lajfmg/Abbp/Aujgb`bpllwqlelqlplollwqb`vbogjilpjgldqbmwjslwfnbgfafbodlrv/Efpwlmbgbwqfpsl`l`bpbabilwlgbpjmlbdvbsvfpvmlpbmwfgj`fovjpfoobnbzlylmbbnlqsjpllaqb`oj`foolgjlpklqb`bpj<[<\\!sbqhpnlvpfNlpw#---?,bnlmdaqbjmalgz#mlmf8abpfg`bqqzgqbewqfefqsbdf\\klnf-nfwfqgfobzgqfbnsqlufiljmw?,wq=gqvdp?"..#bsqjojgfboboofmf{b`welqwk`lgfpoldj`Ujft#pffnpaobmhslqwp#+133pbufg\\ojmhdlbopdqbmwdqffhklnfpqjmdpqbwfg03s{8tklpfsbqpf+*8!#Aol`hojmv{ilmfpsj{fo$*8!=*8je+.ofewgbujgklqpfEl`vpqbjpfal{fpWqb`hfnfmw?,fn=abq!=-pq`>wltfqbow>!`baofkfmqz17s{8pfwvsjwbozpkbqsnjmlqwbpwftbmwpwkjp-qfpfwtkffodjqop,`pp,233&8`ovappwveeajaofulwfp#2333hlqfb~*8\tabmgprvfvf>#x~8;3s{8`hjmdx\t\n\nbkfbg`ol`hjqjpkojhf#qbwjlpwbwpElqn!zbkll*X3^8Balvwejmgp?,k2=gfavdwbphpVQO#>`foop~*+*821s{8sqjnfwfoopwvqmp3{533-isd!psbjmafb`kwb{fpnj`qlbmdfo..=?,djewppwfuf.ojmhalgz-~*8\t\nnlvmw#+2::EBR?,qldfqeqbmh@obpp1;s{8effgp?k2=?p`lwwwfpwp11s{8gqjmh*##oftjppkboo 30:8#elq#olufgtbpwf33s{8ib9npjnlm?elmwqfsoznffwpvmwfq`kfbswjdkwAqbmg*#">#gqfpp`ojspqllnplmhfznlajonbjm-Mbnf#sobwfevmmzwqffp`ln,!2-isdtnlgfsbqbnPWBQWofew#jggfm/#132*8\t~\telqn-ujqvp`kbjqwqbmptlqpwSbdfpjwjlmsbw`k?"..\tl.`b`ejqnpwlvqp/333#bpjbmj((*xbglaf$*X3^jg>23alwk8nfmv#-1-nj-smd!hfujm`lb`k@kjogaqv`f1-isdVQO*(-isdpvjwfpoj`fkbqqz213!#ptffwwq=\tmbnf>gjfdlsbdf#ptjpp..=\t\t eee8!=Old-`ln!wqfbwpkffw*#%%#27s{8poffsmwfmwejofgib9ojg>!`Mbnf!tlqpfpklwp.al{.gfowb\t%ow8afbqp97;Y?gbwb.qvqbo?,b=#psfmgabhfqpklsp>#!!8sks!=`wjlm20s{8aqjbmkfoolpjyf>l>&1E#iljmnbzaf?jnd#jnd!=/#eipjnd!#!*X3^NWlsAWzsf!mftozGbmph`yf`kwqbjohmltp?,k6=ebr!=yk.`m23*8\t.2!*8wzsf>aovfpwqvozgbujp-ip$8=\t?"pwffo#zlv#k1=\telqn#ifpvp233&#nfmv-\t\n\ttbofpqjphpvnfmwggjmda.ojhwfb`kdje!#ufdbpgbmphffpwjpkrjspvlnjplaqfgfpgffmwqfwlglpsvfgfb/]lpfpw/Mwjfmfkbpwblwqlpsbqwfglmgfmvfulkb`fqelqnbnjpnlnfilqnvmglbrv/Ag/Abpp/_olbzvgbef`kbwlgbpwbmwlnfmlpgbwlplwqbppjwjlnv`klbklqbovdbqnbzlqfpwlpklqbpwfmfqbmwfpelwlpfpwbpsb/Apmvfubpbovgelqlpnfgjlrvjfmnfpfpslgfq`kjofpfq/Muf`fpgf`jqilp/Efpwbqufmwbdqvslkf`klfoolpwfmdlbnjdl`lpbpmjufodfmwfnjpnbbjqfpivojlwfnbpkb`jbebulqivmjlojaqfsvmwlavfmlbvwlqbaqjoavfmbwf{wlnbqylpbafqojpwbovfdl`/_nlfmfqlivfdlsfq/Vkbafqfpwlzmvm`bnvifqubolqevfqbojaqldvpwbjdvboulwlp`bplpdv/Absvfglplnlpbujplvpwfggfafmml`kfavp`bebowbfvqlppfqjfgj`kl`vqpl`obuf`bpbpof/_msobylobqdllaqbpujpwbbslzlivmwlwqbwbujpwl`qfbq`bnslkfnlp`jm`l`bqdlsjplplqgfmkb`fm/Mqfbgjp`lsfgql`fq`bsvfgbsbsfonfmlq/Vwjo`obqlilqdf`boofslmfqwbqgfmbgjfnbq`bpjdvffoobppjdol`l`kfnlwlpnbgqf`obpfqfpwlmj/]lrvfgbsbpbqabm`lkjilpujbifsbaol/Epwfujfmfqfjmlgfibqelmgl`bmbomlqwfofwqb`bvpbwlnbqnbmlpovmfpbvwlpujoobufmglsfpbqwjslpwfmdbnbq`loofubsbgqfvmjglubnlpylmbpbnalpabmgbnbqjbbavplnv`kbpvajqqjlibujujqdqbgl`kj`bboo/Ailufmgj`kbfpwbmwbofppbojqpvfolsfplpejmfpoobnbavp`l/Epwboofdbmfdqlsobybkvnlqsbdbqivmwbglaofjpobpalopbab/]lkbaobov`kb/mqfbgj`fmivdbqmlwbpuboofboo/M`bqdbglolqbabilfpw/Edvpwlnfmwfnbqjlejqnb`lpwlej`kbsobwbkldbqbqwfpofzfpbrvfonvpflabpfpsl`lpnjwbg`jfol`kj`lnjfgldbmbqpbmwlfwbsbgfafpsobzbqfgfppjfwf`lqwf`lqfbgvgbpgfpflujfilgfpfbbdvbp%rvlw8glnbjm`lnnlmpwbwvpfufmwpnbpwfqpzpwfnb`wjlmabmmfqqfnlufp`qloovsgbwfdolabonfgjvnejowfqmvnafq`kbmdfqfpvowsvaoj`p`qffm`kllpfmlqnbowqbufojppvfpplvq`fwbqdfwpsqjmdnlgvofnlajofptjw`ksklwlpalqgfqqfdjlmjwpfoepl`jbob`wjuf`lovnmqf`lqgelooltwjwof=fjwkfqofmdwkebnjozeqjfmgobzlvwbvwklq`qfbwfqfujftpvnnfqpfqufqsobzfgsobzfqf{sbmgsloj`zelqnbwglvaofsljmwppfqjfpsfqplmojujmdgfpjdmnlmwkpelq`fpvmjrvftfjdkwsflsoffmfqdzmbwvqfpfbq`kejdvqfkbujmd`vpwlnleepfwofwwfqtjmgltpvanjwqfmgfqdqlvspvsolbgkfbowknfwklgujgflpp`klloevwvqfpkbgltgfabwfubovfpLaif`wlwkfqpqjdkwpofbdvf`kqlnfpjnsofmlwj`fpkbqfgfmgjmdpfbplmqfslqwlmojmfprvbqfavwwlmjnbdfpfmbaofnlujmdobwfpwtjmwfqEqbm`fsfqjlgpwqlmdqfsfbwOlmglmgfwbjoelqnfggfnbmgpf`vqfsbppfgwlddofsob`fpgfuj`fpwbwj``jwjfppwqfbnzfooltbwwb`hpwqffweojdkwkjggfmjmel!=lsfmfgvpfevouboofz`bvpfpofbgfqpf`qfwpf`lmggbnbdfpslqwpf{`fswqbwjmdpjdmfgwkjmdpfeef`wejfogppwbwfpleej`fujpvbofgjwlqulovnfQfslqwnvpfvnnlujfpsbqfmwb``fppnlpwoznlwkfq!#jg>!nbqhfwdqlvmg`kbm`fpvqufzafelqfpznalonlnfmwpsff`knlwjlmjmpjgfnbwwfq@fmwfqlaif`wf{jpwpnjggofFvqlsfdqltwkofdb`znbmmfqfmlvdk`bqffqbmptfqlqjdjmslqwbo`ojfmwpfof`wqbmgln`olpfgwlsj`p`lnjmdebwkfqlswjlmpjnsozqbjpfgfp`bsf`klpfm`kvq`kgfejmfqfbplm`lqmfqlvwsvwnfnlqzjeqbnfsloj`fnlgfopMvnafqgvqjmdleefqppwzofphjoofgojpwfg`boofgpjoufqnbqdjmgfofwfafwwfqaqltpfojnjwpDolabopjmdoftjgdfw`fmwfqavgdfwmltqbs`qfgjw`objnpfmdjmfpbefwz`klj`fpsjqjw.pwzofpsqfbgnbhjmdmffgfgqvppjbsofbpff{wfmwP`qjswaqlhfmbooltp`kbqdfgjujgfeb`wlqnfnafq.abpfgwkflqz`lmejdbqlvmgtlqhfgkfosfg@kvq`kjnsb`wpklvogbotbzpoldl!#alwwlnojpw!=*xubq#sqfej{lqbmdfKfbgfq-svpk+`lvsofdbqgfmaqjgdfobvm`kQfujftwbhjmdujpjlmojwwofgbwjmdAvwwlmafbvwzwkfnfpelqdlwPfbq`kbm`klqbonlpwolbgfg@kbmdfqfwvqmpwqjmdqfolbgNlajofjm`lnfpvssozPlvq`flqgfqpujftfg%maps8`lvqpfBalvw#jpobmg?kwno#`llhjfmbnf>!bnbylmnlgfqmbguj`fjm?,b=9#Wkf#gjboldklvpfpAFDJM#Nf{j`lpwbqwp`fmwqfkfjdkwbggjmdJpobmgbppfwpFnsjqfP`kllofeelqwgjqf`wmfbqoznbmvboPfof`w-\t\tLmfiljmfgnfmv!=SkjojsbtbqgpkbmgofjnslqwLeej`fqfdbqgphjoopmbwjlmPslqwpgfdqfftffhoz#+f-d-afkjmggl`wlqolddfgvmjwfg?,a=?,afdjmpsobmwpbppjpwbqwjpwjppvfg033s{`bmbgbbdfm`zp`kfnfqfnbjmAqbyjopbnsofoldl!=afzlmg.p`bofb``fswpfqufgnbqjmfEllwfq`bnfqb?,k2=\t\\elqn!ofbufppwqfpp!#,=\t-dje!#lmolbgolbgfqL{elqgpjpwfqpvqujuojpwfmefnbofGfpjdmpjyf>!bssfbowf{w!=ofufopwkbmhpkjdkfqelq`fgbmjnbobmzlmfBeqj`bbdqffgqf`fmwSflsof?aq#,=tlmgfqsqj`fpwvqmfg#x~8nbjm!=jmojmfpvmgbztqbs!=ebjofg`fmpvpnjmvwfafb`lmrvlwfp263s{fpwbwfqfnlwffnbjo!ojmhfgqjdkw8pjdmboelqnbo2-kwnopjdmvssqjm`feolbw9-smd!#elqvn-B``fppsbsfqpplvmgpf{wfmgKfjdkwpojgfqVWE.;!%bns8#Afelqf-#TjwkpwvgjlltmfqpnbmbdfsqlejwiRvfqzbmmvbosbqbnpalvdkwebnlvpdlldofolmdfqj((*#xjpqbfopbzjmdgf`jgfklnf!=kfbgfqfmpvqfaqbm`ksjf`fpaol`h8pwbwfgwls!=?qb`jmdqfpjyf..%dw8sb`jwzpf{vboavqfbv-isd!#23/333lawbjmwjwofpbnlvmw/#Jm`-`lnfgznfmv!#ozqj`pwlgbz-jmgffg`lvmwz\\oldl-EbnjozollhfgNbqhfwopf#jeSobzfqwvqhfz*8ubq#elqfpwdjujmdfqqlqpGlnbjm~fopfxjmpfqwAold?,ellwfqoldjm-ebpwfqbdfmwp?algz#23s{#3sqbdnbeqjgbzivmjlqgloobqsob`fg`lufqpsovdjm6/333#sbdf!=alpwlm-wfpw+bubwbqwfpwfg\\`lvmwelqvnpp`kfnbjmgf{/ejoofgpkbqfpqfbgfqbofqw+bssfbqPvanjwojmf!=algz!=\t)#WkfWklvdkpffjmdifqpfzMftp?,ufqjezf{sfqwjmivqztjgwk>@llhjfPWBQW#b`qlpp\\jnbdfwkqfbgmbwjufsl`hfwal{!=\tPzpwfn#Gbujg`bm`fqwbaofpsqlufgBsqjo#qfboozgqjufqjwfn!=nlqf!=albqgp`lolqp`bnsvpejqpw##X^8nfgjb-dvjwbqejmjpktjgwk9pkltfgLwkfq#-sks!#bppvnfobzfqptjoplmpwlqfpqfojfeptfgfm@vpwlnfbpjoz#zlvq#Pwqjmd\t\tTkjowbzolq`ofbq9qfplqweqfm`kwklvdk!*#(#!?algz=avzjmdaqbmgpNfnafqmbnf!=lssjmdpf`wlq6s{8!=upsb`fslpwfqnbilq#`leeffnbqwjmnbwvqfkbssfm?,mbu=hbmpbpojmh!=Jnbdfp>ebopftkjof#kpsb`f3%bns8#\t\tJm##sltfqSlophj.`lolqilqgbmAlwwlnPwbqw#.`lvmw1-kwnomftp!=32-isdLmojmf.qjdkwnjoofqpfmjlqJPAM#33/333#dvjgfpubovf*f`wjlmqfsbjq-{no!##qjdkwp-kwno.aol`hqfdF{s9klufqtjwkjmujqdjmsklmfp?,wq=vpjmd#\t\nubq#=$*8\t\n?,wg=\t?,wq=\tabkbpbaqbpjodbofdlnbdzbqslophjpqsphj4]4C5d\bTA\nzk\vBl\bQ\vUmGx\bSM\nmC\bTA\twQ\nd}\bW@\bTl\bTF\ti@\tcT\vBM\v|jBV\tqw\tcC\bWI\npa\tfM\n{Z{X\bTF\bVV\bVK\tmkF\t[]\bPm\bTv\nsI\vpg\t[I\bQpmx\v_W\n^M\npe\vQ}\vGu\nel\npeChBV\bTA\tSo\nzk\vGL\vxD\nd[JzMY\bQpli\nfl\npC{BNt\vwT\ti_\bTgQQ\n|p\vXN\bQS\vxDQC\bWZ\tpD\vVS\bTWNtYh\nzuKjN}\twr\tHa\n_D\tj`\vQ}\vWp\nxZ{c\tji\tBU\nbDa|\tTn\tpV\nZd\nmC\vEV{X\tc}\tTo\bWl\bUd\tIQ\tcg\vxs\nXW\twR\vek\tc}\t]y\tJn\nrp\neg\npV\nz\\{W\npl\nz\\\nzU\tPc\t`{\bV@\nc|\bRw\ti_\bVb\nwX\tHvSu\bTF\v_W\vWs\vsIm\nTT\ndc\tUS\t}f\tiZ\bWz\tc}MD\tBe\tiD\v@@\bTl\bPv\t}tSwM`\vnU\tkW\ved\nqo\vxY\tA|\bTz\vy`BRBM\tiaXU\nyun^\tfL\tiI\nXW\tfD\bWz\bW@\tyj\tm\tav\tBN\vb\\\tpD\bTf\nY[\tJn\bQy\t[^\vWc\vyuDlCJ\vWj\vHR\t`V\vuW\tQy\np@\vGuplJm\bW[\nLP\nxC\n`m\twQuiR\nbI\twQ\tBZ\tWVBR\npg\tcgtiCW\n_y\tRg\bQa\vQB\vWc\nYble\ngESu\nL[\tQ\tea\tdj\v]W\nb~M`\twL\bTV\bVH\nt\npl\t|bs_\bU|\bTaoQlvSkM`\bTv\vK}\nfl\tcCoQBR\tHk\t|d\bQp\tHK\tBZ\vHR\bPv\vLx\vEZ\bT\bTv\tiDoDMU\vwBSuk`St\ntC\tPl\tKg\noi\tjY\vxYh}\nzk\bWZ\tm\ve`\tTB\tfE\nzk\t`zYh\nV|\tHK\tAJ\tAJ\bUL\tp\\\tql\nYcKd\nfyYh\t[I\vDgJm\n]n\nlb\bUd\n{Z\tlu\tfsoQ\bTWJm\vwB\teaYhBC\tsb\tTn\nzU\n_y\vxY\tQ]\ngwmt\tO\\\ntb\bWW\bQy\tmI\tV[\ny\\\naB\vRb\twQ\n]QQJ\bWg\vWa\bQj\ntC\bVH\nYm\vxs\bVK\nel\bWI\vxYCq\ntR\vHV\bTl\bVw\tay\bQa\bVV\t}t\tdj\nr|\tp\\\twR\n{i\nTT\t[I\ti[\tAJ\vxs\v_W\td{\vQ}\tcg\tTz\tA|\tCj\vLmN}m\nbK\tdZ\tp\\\t`V\tsV\np@\tiD\twQ\vQ}\bTfkaJm\v@@\bV`\tzp\n@NSw\tiI\tcg\noiSu\bVwloCy\tc}\vb\\\tsUBA\bWI\bTf\nxS\tVp\nd|\bTV\vbC\tNoJu\nTC\t|`\n{Z\tD]\bU|\tc}lm\bTl\tBv\tPl\tc}\bQp\tm\nLk\tkj\n@NSbKO\tj_\tp\\\nzU\bTl\bTg\bWI\tcfXO\bWW\ndzli\tBN\nd[\bWOMD\vKC\tdj\tI_\bVV\ny\\\vLmxl\txB\tkV\vb\\\vJW\vVS\tVx\vxD\td{MD\bTa\t|`\vPzR}\vWsBM\nsICN\bTaJm\npe\ti_\npV\nrh\tRd\tHv\n~A\nxR\vWh\vWk\nxS\vAz\vwX\nbIoQ\tfw\nqI\nV|\nunz\vpg\td\\\voA{D\ti_xB\bT\t`Vqr\tTTg]CA\vuR\tVJ\tT`\npw\vRb\tI_\nCxRo\vsICjKh\tBv\tWVBBoD{D\nhcKm\v^R\tQE\n{I\np@\nc|Gt\tc}Dl\nzUqN\tsVk}\tHh\v|j\nqou|\tQ]\vekZM`St\npe\tdj\bVG\veE\tm\vWc|I\n[W\tfL\bT\tBZSu\vKaCqNtY[\nqI\bTv\tfM\ti@\t}fB\\\tQy\vBl\bWgXDkc\vx[\bVV\tQ]\ta\tPy\vxD\nfI\t}foD\tdj\tSGls\t~DCN\n{Z\t\\v\n_D\nhc\vx_C[\tAJ\nLM\tVxCI\tbj\tc^\tcF\ntCSx\twrXA\bU\\\t|a\vK\\\bTV\bVj\nd|\tfsCX\ntb\bRw\tVx\tAE\tA|\bTNt\vDg\tVc\bTld@\npo\tM\tcF\npe\tiZ\tBo\bSq\nfHl`\bTx\bWf\tHE\vF{\tcO\tfD\nlm\vfZ\nlm\veU\tdGBH\bTV\tSiMW\nwX\nz\\\t\\cCX\nd}\tl}\bQp\bTV\tF~\bQ\t`i\ng@nO\bUd\bTl\nL[\twQ\tji\ntC\t|J\nLU\naB\vxYKj\tAJuN\ti[\npeSk\vDg\vx]\bVb\bVV\nea\tkV\nqI\bTaSk\nAO\tpD\ntb\nts\nyi\bVg\ti_\v_W\nLkNt\tyj\tfMR\tiI\bTl\vwX\tsV\vMl\nyu\tAJ\bVjKO\tWV\vA}\vW\nrp\tiD\v|olv\vsIBM\td~\tCU\bVbeV\npC\vwT\tj`\tc}\vxs\vps\vvh\tWV\vGg\vAe\vVK\v]W\trg\vWcF`\tBr\vb\\\tdZ\bQp\nqIkF\nLk\vAR\bWI\bTg\tbs\tdw\n{L\n_y\tiZ\bTA\tlg\bVV\bTl\tdk\n`k\ta{\ti_{Awj\twN\v@@\bTe\ti_\n_D\twL\nAH\viK\vek\n[]\tp_\tyj\bTv\tUS\t[r\n{I\npsGt\vVK\nplS}\vWP\t|dMD\vHV\bTR}M`\bTV\bVHlvCh\bW[Ke\tR{\v^R\tab\tBZ\tVA\tB`\nd|\nhsKe\tBeOi\tR{\td\\nB\bWZ\tdZ\tVJOs\tmuQ\vhZQ@QQ\nfI\bW[B\\li\nzU\nMdM`\nxS\bVV\n\\}\vxD\tm\bTpIS\nc|\tkVi~\tV{\vhZ\t|b\bWt\n@R\voA\vnU\bWI\tea\tB`\tiD\tc}\tTzBR\vQBNj\tCP\t[I\bTv\t`WuN\vpg\vpg\vWc\tiT\tbs\twL\tU_\tc\\\t|h\vKa\tNr\tfL\nq|\nzu\nz\\\tNr\bUg\t|bm`\bTv\nyd\nrp\bWf\tUXBV\nzk\nd}\twQ\t}fCe\ved\bTW\bSB\nxU\tcn\bTb\ne\ta\\\tSG\bU|\npV\nN\\Kn\vnU\tAt\tpD\v^R\vIrb[\tR{\tdE\vxD\vWK\vWA\bQL\bW@Su\bUd\nDM\tPcCADloQ\tHswiub\na\bQpOb\nLP\bTlY[\vK}\tAJ\bQn^\vsA\bSM\nqM\bWZ\n^W\vz{S|\tfD\bVK\bTv\bPvBB\tCPdF\tid\vxsmx\vws\tcC\ntC\tycM`\vW\nrh\bQp\vxD\\o\nsI_k\nzukF\tfDXsXO\tjp\bTvBS{B\tBr\nzQ\nbI\tc{BDBVnO\bTF\tcaJd\tfL\tPV\tI_\nlK`o\twX\npa\tgu\bP}{^\bWf\n{I\tBN\npaKl\vpg\tcn\tfL\vvhCq\bTl\vnU\bSqCm\twR\bUJ\npe\nyd\nYgCy\vKW\tfD\neaoQ\tj_\tBvnM\vID\bTa\nzApl\n]n\bTa\tR{\tfr\n_y\bUg{Xkk\vxD|Ixl\nfyCe\vwB\nLk\vd]\noi\n}h\tQ]\npe\bVwHkOQ\nzk\tAJ\npV\bPv\ny\\\tA{Oi\bSBXA\veE\tjp\nq}\tiDqN\v^R\tm\tiZ\tBr\bVg\noi\n\\X\tU_\nc|\vHV\bTf\tTn\\N\\N\nuBlv\nyu\tTd\bTf\bPL\v]W\tdG\nA`\nw^\ngI\npe\tdw\nz\\ia\bWZ\tcFJm\n{Z\bWO_kDfRR\td\\\bVV\vxsBNtilm\tTd\t]y\vHV\tSo\v|jXX\tA|\vZ^\vGu\bTWM`kF\vhZ\vVK\tdG\vBl\tay\nxUqEnO\bVw\nqICX\ne\tPl\bWO\vLm\tdLuHCm\tdTfn\vwBka\vnU\n@M\nyT\tHv\t\\}Kh\td~Yhk}\neR\td\\\bWI\t|b\tHK\tiD\bTWMY\npl\bQ_\twr\vAx\tHE\bTg\bSqvp\vb\\\bWO\nOl\nsI\nfy\vID\t\\c\n{Z\n^~\npe\nAO\tTT\vxvk_\bWO\v|j\vwB\tQy\ti@\tPl\tHa\tdZk}ra\tUT\vJc\ved\np@\tQN\nd|\tkj\tHkM`\noi\twr\td\\\nlq\no_\nlb\nL[\tacBBBHCm\npl\tIQ\bVK\vxs\n`e\viK\npaOi\tUS\bTp\tfD\nPGkkXA\nz\\\neg\vWh\twRqN\nqS\tcnlo\nxS\n^W\tBU\nt\tHE\tp\\\tfF\tfw\bVV\bW@\tak\vVKls\tVJ\bVV\veE\\o\nyX\nYmM`lL\nd|\nzk\tA{sE\twQXT\nt\tPl\t]y\vwT{pMD\vb\\\tQ]Kj\tJn\nAH\vRb\tBU\tHK\t\\c\nfIm\nqM\n@R\tSo\noiBT\tHv\n_yKh\tBZ\t]i\bUJ\tV{Sr\nbI\vGg\ta_\bTR\nfI\nfl\t[K\tIIS|\vuW\tiI\bWI\nqI\v|jBV\bVg\bWZkF\vx]\bTA\tab\tfr\ti@\tJd\tJd\vps\nAO\bTaxu\tiD\nzk\t|d\t|`\bW[\tlP\tdG\bVV\vw}\vqO\ti[\bQ\bTz\vVF\twNts\tdw\bTv\neS\ngi\tNryS\npe\bVV\bSq\n`m\tyj\tBZ\vWX\bSB\tc\\\nUR\t[J\tc_nM\bWQ\vAx\nMd\tBrui\vxY\bSM\vWc\v|j\vxs\t}Q\tBO\bPL\bWW\tfM\nAO\tPc\veUe^\bTg\nqI\tac\bPv\tcFoQ\tQ\vhZka\nz\\\tiK\tBU\n`k\tCPS|M`\n{I\tS{_O\tBZZiSk\tps\tp\\\nYu\n]s\nxC\bWt\nbD\tkV\vGuyS\nqA\t[r\neKM`\tdZlL\bUg\bTl\nbD\tUS\vb\\\tpV\nccS\\\tct\t`z\bPL\vWs\nA`\neg\bSquECR\vDg\t`W\vz{\vWcSkSk\tbW\bUg\tea\nxZ\tiI\tUX\tVJ\nqn\tS{\vRb\bTQ\nplGt\vuWuj\npF\nqI\tfL\t[I\tiaXO\nyu\vDg\ved\tq{VG\bQka\tVj\tkV\txB\nd|\np@\tQN\tPc\tps]j\tkV\toU\bTp\nzUnB\vB]\ta{\bV@\n]nm`\tcz\tR{m`\bQa\vwT\bSMMYqN\tdj~s\vQ}MY\vMB\tBv\twR\bRg\vQ}\tql\vKC\nrmxuCC\vwB\vvh\tBqXq\npV\ti_ObuE\nbd\nqo\v{i\nC~\tBL\veEuH\bVjEyGz\vzR\v{i\tcf\n{Z\n]nXA\vGu\vnU\thS\vGI\nCc\tHE\bTA\tHBBHCj\nCc\bTF\tHE\nXI\tA{\bQ\tc\\\vmO\vWX\nfH\np@MY\bTF\nlK\tBt\nzU\tTTKm\vwT\npV\ndt\vyI\tVx\tQ\tRg\tTd\nzU\bRS\nLM\twAnM\tTn\ndS\t]g\nLc\vwB\t}t\t[I\tCPkX\vFm\vhZm\ti[\np@\vQ}\vW\t|d\nMO\nMd\tf_\tfD\tcJ\tHz\vRb\tio\tPyY[\nxU\tct\v@@\tww\bPvBMFF\ntbv|\vKm\tBq\tBqKh`o\nZdXU\ti]\t|`\tStB\\\bQ\v_W\tTJ\nqI\t|a\tA{\vuPMD\tPl\nxR\tfL\vws\tc{\td\\\bV`\neg\tHKkc\nd|\bVV\ny\\kc\ti]\bVG\t`V\tss\tI_\tAE\tbs\tdu\nel\tpD\vW\nqslv\bSMZi\vVKia\vQB\tQ\n{Z\bPt\vKl\nlK\nhs\ndS\bVKmf\nd^\tkV\tcO\nc|\bVH\t\\]\bTv\bSq\tmI\vDg\tVJ\tcn\ny\\\bVg\bTv\nyX\bTF\t]]\bTp\noi\nhs\veU\nBf\tdjMr\n|p\t\\g\t]r\bVb{D\nd[XN\tfM\tO\\s_\tcf\tiZXN\vWc\tqv\n`m\tU^oD\nd|\vGg\tdE\vwflou}\nd|oQ\t`iOi\vxD\ndZ\nCxYw\nzk\ntb\ngw\tyj\tB`\nyX\vps\ntC\vpP\vqw\bPu\bPX\tDm\npwNj\tss\taG\vxs\bPt\noLGz\tOk\ti@\ti]eC\tIQ\tii\tdj\v@J\t|duh\bWZ\veU\vnU\bTa\tcCg]\nzkYh\bVK\nLU\np@\ntb\ntR\tCj\vNP\ti@\bP{\n\\}\n{c\nwX\tfL\bVG\tc{\t|`\tAJ\t|C\tfDln\t|d\tbs\nqI{B\vAx\np@\nzk\vRbOs\vWSe^\vD_\tBv\vWd\bVb\vxs\veE\bRw\n]n\n|p\vg|\tfwkc\bTIka\n\\TSp\tju\vps\npeu|\vGr\bVe\tCU]MXU\vxD\bTa\tIQ\vWq\tCU\tam\tdj\bSoSw\vnUCh\tQ]s_\bPt\tfS\bTa\t\\}\n@OYc\tUZ\bTx\npe\vnU\nzU\t|}\tiD\nz\\\bSM\vxDBR\nzQ\tQN]MYh\nLP\vFm\vLXvc\vqlka\tHK\bVb\ntC\nCy\bTv\nuVoQ\t`z\t[I\tB`\vRb\tyj\tsb\vWs\bTl\tkV\ved\nelL\vxN\tm\nJn\tjY\vxD\bVb\bSq\vyu\twL\vXL\bTA\tpg\tAt\tnDXX\twR\npl\nhwyS\nps\tcO\bW[\v|jXN\tsV\tp\\\tBe\nb~\nAJ\n]ek`qN\tdw\tWV\tHE\vEVJz\tid\tB`\tzhE]\tfD\bTgqN\bTa\tjaCv\bSM\nhc\bUet_\tieg]\twQ\nPn\bVB\tjw\bVg\vbE\tBZ\vRH\bP{\tjp\n\\}\ta_\tcC\t|a\vD]\tBZ\ti[\tfD\vxW\no_\td\\\n_D\ntb\t\\c\tAJ\nlKoQlo\vLx\vM@\bWZKn\vpg\nTi\nIv\n|r\v@}JzLmWhk}ln\vxD\n]sgc\vps\tBr\bTW\vBMtZ\nBYDW\tjf\vSWC}\nqo\tdE\tmv\tIQ\bPP\bUblvBC\nzQ\t[I\vgl\nig\bUsBT\vbC\bSq\tsU\tiW\nJn\tSY\tHK\trg\npV\vID\v|jKO\t`S\t|a`vbmglfmujbqnbgqjgavp`bqjmj`jlwjfnslslqrvf`vfmwbfpwbglsvfgfmivfdlp`lmwqbfpw/Mmmlnaqfwjfmfmsfqejonbmfqbbnjdlp`jvgbg`fmwqlbvmrvfsvfgfpgfmwqlsqjnfqsqf`jlpfd/Vmavfmlpuloufqsvmwlppfnbmbkba/Abbdlpwlmvfulpvmjglp`bqolpfrvjslmj/]lpnv`klpbodvmb`lqqfljnbdfmsbqwjqbqqjabnbq/Abklnaqffnsoflufqgbg`bnajlnv`kbpevfqlmsbpbglo/Amfbsbqf`fmvfubp`vqplpfpwbabrvjfqlojaqlp`vbmwlb``fplnjdvfoubqjlp`vbwqlwjfmfpdqvslppfq/Mmfvqlsbnfgjlpeqfmwfb`fq`bgfn/Mplefqwb`l`kfpnlgfoljwbojbofwqbpbod/Vm`lnsqb`vbofpf{jpwf`vfqslpjfmglsqfmpboofdbqujbifpgjmfqlnvq`jbslgq/Msvfpwlgjbqjlsvfaolrvjfqfnbmvfosqlsjl`qjpjp`jfqwlpfdvqlnvfqwfevfmwf`fqqbqdqbmgffef`wlsbqwfpnfgjgbsqlsjbleqf`fwjfqqbf.nbjoubqjbpelqnbpevwvqllaifwlpfdvjqqjfpdlmlqnbpnjpnlp/Vmj`l`bnjmlpjwjlpqby/_mgfajglsqvfabwlofglwfm/Abifp/Vpfpsfql`l`jmblqjdfmwjfmgb`jfmwl`/Mgjykbaobqpfq/Abobwjmbevfqybfpwjoldvfqqbfmwqbq/E{jwlo/_sfybdfmgbu/Agflfujwbqsbdjmbnfwqlpibujfqsbgqfpe/M`jo`bafyb/Mqfbppbojgbfmu/Alibs/_mbavplpajfmfpwf{wlpoofubqsvfgbmevfqwf`ln/Vm`obpfpkvnbmlwfmjglajoablvmjgbgfpw/Mpfgjwbq`qfbgl2%bns8Kjpwlqz#>#mft#@fmwqbovsgbwfgPsf`jboMfwtlqhqfrvjqf`lnnfmwtbqmjmd@loofdfwlloabqqfnbjmpaf`bvpffof`wfgGfvwp`kejmbm`ftlqhfqprvj`hozafwtffmf{b`wozpfwwjmdgjpfbpfPl`jfwztfbslmpf{kjajw%ow8"..@lmwqlo`obppfp`lufqfglvwojmfbwwb`hpgfuj`fp+tjmgltsvqslpfwjwof>!Nlajof#hjoojmdpkltjmdJwbojbmgqlssfgkfbujozfeef`wp.2$^*8\t`lmejqn@vqqfmwbgubm`fpkbqjmdlsfmjmdgqbtjmdajoojlmlqgfqfgDfqnbmzqfobwfg?,elqn=jm`ovgftkfwkfqgfejmfgP`jfm`f`bwboldBqwj`ofavwwlmpobqdfpwvmjelqnilvqmfzpjgfabq@kj`bdlklojgbzDfmfqbosbppbdf/%rvlw8bmjnbwfeffojmdbqqjufgsbppjmdmbwvqboqlvdkoz-\t\tWkf#avw#mlwgfmpjwzAqjwbjm@kjmfpfob`h#lewqjavwfJqfobmg!#gbwb.eb`wlqpqf`fjufwkbw#jpOjaqbqzkvpabmgjm#eb`wbeebjqp@kbqofpqbgj`boaqlvdkwejmgjmdobmgjmd9obmd>!qfwvqm#ofbgfqpsobmmfgsqfnjvnsb`hbdfBnfqj`bFgjwjlm^%rvlw8Nfppbdfmffg#wlubovf>!`lnsof{ollhjmdpwbwjlmafojfufpnboofq.nlajofqf`lqgptbmw#wlhjmg#leEjqfel{zlv#bqfpjnjobqpwvgjfgnb{jnvnkfbgjmdqbsjgoz`ojnbwfhjmdglnfnfqdfgbnlvmwpelvmgfgsjlmffqelqnvobgzmbpwzklt#wl#Pvsslqwqfufmvff`lmlnzQfpvowpaqlwkfqplogjfqobqdfoz`boojmd-%rvlw8B``lvmwFgtbqg#pfdnfmwQlafqw#feelqwpSb`jej`ofbqmfgvs#tjwkkfjdkw9tf#kbufBmdfofpmbwjlmp\\pfbq`kbssojfgb`rvjqfnbppjufdqbmwfg9#ebopfwqfbwfgajddfpwafmfejwgqjujmdPwvgjfpnjmjnvnsfqkbspnlqmjmdpfoojmdjp#vpfgqfufqpfubqjbmw#qlof>!njppjmdb`kjfufsqlnlwfpwvgfmwplnflmff{wqfnfqfpwlqfalwwln9fuloufgboo#wkfpjwfnbsfmdojpktbz#wl##Bvdvpwpznalop@lnsbmznbwwfqpnvpj`bobdbjmpwpfqujmd~*+*8\tsbznfmwwqlvaof`lm`fsw`lnsbqfsbqfmwpsobzfqpqfdjlmpnlmjwlq#$$Wkf#tjmmjmdf{solqfbgbswfgDboofqzsqlgv`fbajojwzfmkbm`f`bqffqp*-#Wkf#`loof`wPfbq`k#bm`jfmwf{jpwfgellwfq#kbmgofqsqjmwfg`lmplofFbpwfqmf{slqwptjmgltp@kbmmfojoofdbomfvwqbopvddfpw\\kfbgfqpjdmjmd-kwno!=pfwwofgtfpwfqm`bvpjmd.tfahjw`objnfgIvpwj`f`kbswfquj`wjnpWklnbp#nlyjoobsqlnjpfsbqwjfpfgjwjlmlvwpjgf9ebopf/kvmgqfgLoznsj`\\avwwlmbvwklqpqfb`kfg`kqlmj`gfnbmgppf`lmgpsqlwf`wbglswfgsqfsbqfmfjwkfqdqfbwozdqfbwfqlufqboojnsqluf`lnnbmgpsf`jbopfbq`k-tlqpkjsevmgjmdwklvdkwkjdkfpwjmpwfbgvwjojwzrvbqwfq@vowvqfwfpwjmd`ofbqozf{slpfgAqltpfqojafqbo~#`bw`kSqlif`wf{bnsofkjgf+*8EolqjgbbmptfqpbooltfgFnsfqlqgfefmpfpfqjlvpeqffglnPfufqbo.avwwlmEvqwkfqlvw#le#">#mvoowqbjmfgGfmnbqhuljg+3*,boo-ipsqfufmwQfrvfpwPwfskfm\t\tTkfm#lapfquf?,k1=\tNlgfqm#sqlujgf!#bow>!alqgfqp-\t\tElq#\t\tNbmz#bqwjpwpsltfqfgsfqelqnej`wjlmwzsf#lenfgj`bowj`hfwplsslpfg@lvm`jotjwmfppivpwj`fDflqdf#Afodjvn---?,b=wtjwwfqmlwbaoztbjwjmdtbqebqf#Lwkfq#qbmhjmdskqbpfpnfmwjlmpvqujufp`klobq?,s=\t#@lvmwqzjdmlqfgolpp#leivpw#bpDflqdjbpwqbmdf?kfbg=?pwlssfg2$^*8\tjpobmgpmlwbaofalqgfq9ojpw#le`bqqjfg233/333?,k0=\t#pfufqboaf`lnfppfof`w#tfggjmd33-kwnonlmbq`klee#wkfwfb`kfqkjdkoz#ajloldzojef#lelq#fufmqjpf#le%qbrvl8sovplmfkvmwjmd+wklvdkGlvdobpiljmjmd`jq`ofpElq#wkfBm`jfmwUjfwmbnufkj`ofpv`k#bp`qzpwboubovf#>Tjmgltpfmilzfgb#pnboobppvnfg?b#jg>!elqfjdm#Boo#qjklt#wkfGjpsobzqfwjqfgkltfufqkjggfm8abwwofppffhjmd`bajmfwtbp#mlwollh#bw`lmgv`wdfw#wkfIbmvbqzkbssfmpwvqmjmdb9klufqLmojmf#Eqfm`k#ob`hjmdwzsj`bof{wqb`wfmfnjfpfufm#jedfmfqbwgf`jgfgbqf#mlw,pfbq`kafojfep.jnbdf9ol`bwfgpwbwj`-oldjm!=`lmufqwujlofmwfmwfqfgejqpw!=`jq`vjwEjmobmg`kfnjpwpkf#tbp23s{8!=bp#pv`kgjujgfg?,psbm=tjoo#afojmf#leb#dqfbwnzpwfqz,jmgf{-eboojmdgvf#wl#qbjotbz`loofdfnlmpwfqgfp`fmwjw#tjwkmv`ofbqIftjpk#sqlwfpwAqjwjpkeoltfqpsqfgj`wqfelqnpavwwlm#tkl#tbpof`wvqfjmpwbmwpvj`jgfdfmfqj`sfqjlgpnbqhfwpPl`jbo#ejpkjmd`lnajmfdqbskj`tjmmfqp?aq#,=?az#wkf#MbwvqboSqjub`z`llhjfplvw`lnfqfploufPtfgjpkaqjfeozSfqpjbmpl#nv`k@fmwvqzgfsj`wp`lovnmpklvpjmdp`qjswpmf{w#wlafbqjmdnbssjmdqfujpfgiRvfqz+.tjgwk9wjwof!=wllowjsPf`wjlmgfpjdmpWvqhjpkzlvmdfq-nbw`k+~*+*8\t\tavqmjmdlsfqbwfgfdqffpplvq`f>Qj`kbqg`olpfozsobpwj`fmwqjfp?,wq=\t`lolq9 vo#jg>!slppfppqloojmdskzpj`pebjojmdf{f`vwf`lmwfpwojmh#wlGfebvow?aq#,=\t9#wqvf/`kbqwfqwlvqjpn`obppj`sql`ffgf{sobjm?,k2=\tlmojmf-<{no#ufkfosjmdgjbnlmgvpf#wkfbjqojmffmg#..=*-bwwq+qfbgfqpklpwjmd eeeeeeqfbojyfUjm`fmwpjdmbop#pq`>!,Sqlgv`wgfpsjwfgjufqpfwfoojmdSvaoj`#kfog#jmIlpfsk#wkfbwqfbeef`wp?pwzof=b#obqdfglfpm$wobwfq/#Fofnfmwebuj`lm`qfbwlqKvmdbqzBjqslqwpff#wkfpl#wkbwNj`kbfoPzpwfnpSqldqbnp/#bmg##tjgwk>f%rvlw8wqbgjmdofew!=\tsfqplmpDlogfm#Beebjqpdqbnnbqelqnjmdgfpwqlzjgfb#le`bpf#lelogfpw#wkjp#jp-pq`#>#`bqwllmqfdjpwq@lnnlmpNvpojnpTkbw#jpjm#nbmznbqhjmdqfufbopJmgffg/frvbooz,pklt\\blvwgllqfp`bsf+Bvpwqjbdfmfwj`pzpwfn/Jm#wkf#pjwwjmdKf#boplJpobmgpB`bgfnz\t\n\n?"..Gbmjfo#ajmgjmdaol`h!=jnslpfgvwjojyfBaqbkbn+f{`fswxtjgwk9svwwjmd*-kwno+#X^8\tGBWBX#)hjw`kfmnlvmwfgb`wvbo#gjbof`wnbjmoz#\\aobmh$jmpwboof{sfqwpje+wzsfJw#bopl%`lsz8#!=Wfqnpalqm#jmLswjlmpfbpwfqmwbohjmd`lm`fqmdbjmfg#lmdljmdivpwjez`qjwj`peb`wlqzjwp#ltmbppbvowjmujwfgobpwjmdkjp#ltmkqfe>!,!#qfo>!gfufols`lm`fqwgjbdqbngloobqp`ovpwfqsksbo`lklo*8~*+*8vpjmd#b=?psbm=ufppfopqfujuboBggqfppbnbwfvqbmgqljgboofdfgjoomfpptbohjmd`fmwfqprvbojeznbw`kfpvmjejfgf{wjm`wGfefmpfgjfg#jm\t\n?"..#`vpwlnpojmhjmdOjwwof#Allh#lefufmjmdnjm-iptfbqjmdBoo#Qjd8\t~*+*8qbjpjmd#Bopl/#`qv`jbobalvw!=gf`obqf..=\t?p`ejqfel{bp#nv`kbssojfpjmgf{/#p/#avw#wzsf#>#\t\t?"..wltbqgpQf`lqgpSqjubwfElqfjdmSqfnjfq`klj`fpUjqwvboqfwvqmp@lnnfmwSltfqfgjmojmf8slufqwz`kbnafqOjujmd#ulovnfpBmwklmzoldjm!#QfobwfgF`lmlnzqfb`kfp`vwwjmddqbujwzojef#jm@kbswfq.pkbgltMlwbaof?,wg=\t#qfwvqmpwbgjvntjgdfwpubqzjmdwqbufopkfog#aztkl#bqftlqh#jmeb`vowzbmdvobqtkl#kbgbjqslqwwltm#le\t\tPlnf#$`oj`h$`kbqdfphfztlqgjw#tjoo`jwz#le+wkjp*8Bmgqft#vmjrvf#`kf`hfglq#nlqf033s{8#qfwvqm8qpjlm>!sovdjmptjwkjm#kfqpfoePwbwjlmEfgfqboufmwvqfsvaojpkpfmw#wlwfmpjlmb`wqfpp`lnf#wlejmdfqpGvhf#lesflsof/f{soljwtkbw#jpkbqnlmzb#nbilq!9!kwwsjm#kjp#nfmv!=\tnlmwkozleej`fq`lvm`jodbjmjmdfufm#jmPvnnbqzgbwf#leolzbowzejwmfppbmg#tbpfnsfqlqpvsqfnfPf`lmg#kfbqjmdQvppjbmolmdfpwBoafqwbobwfqbopfw#le#pnboo!=-bssfmggl#tjwkefgfqboabmh#leafmfbwkGfpsjwf@bsjwbodqlvmgp*/#bmg#sfq`fmwjw#eqln`olpjmd`lmwbjmJmpwfbgejewffmbp#tfoo-zbkll-qfpslmgejdkwfqlap`vqfqfeof`wlqdbmj`>#Nbwk-fgjwjmdlmojmf#sbggjmdb#tkloflmfqqlqzfbq#lefmg#le#abqqjfqtkfm#jwkfbgfq#klnf#leqfpvnfgqfmbnfgpwqlmd=kfbwjmdqfwbjmp`olvgeqtbz#le#Nbq`k#2hmltjmdjm#sbqwAfwtffmofpplmp`olpfpwujqwvboojmhp!=`qlppfgFMG#..=ebnlvp#btbqgfgOj`fmpfKfbowk#ebjqoz#tfbowkznjmjnboBeqj`bm`lnsfwfobafo!=pjmdjmdebqnfqpAqbpjo*gjp`vppqfsob`fDqfdlqzelmw#`lsvqpvfgbssfbqpnbhf#vsqlvmgfgalwk#leaol`hfgpbt#wkfleej`fp`lolvqpje+gl`vtkfm#kffmelq`fsvpk+evBvdvpw#VWE.;!=Ebmwbpzjm#nlpwjmivqfgVpvboozebqnjmd`olpvqflaif`w#gfefm`fvpf#le#Nfgj`bo?algz=\tfujgfmwaf#vpfghfz@lgfpj{wffmJpobnj` 333333fmwjqf#tjgfoz#b`wjuf#+wzsflelmf#`bm`lolq#>psfbhfqf{wfmgpSkzpj`pwfqqbjm?walgz=evmfqboujftjmdnjggof#`qj`hfwsqlskfwpkjewfggl`wlqpQvppfoo#wbqdfw`lnsb`wbodfaqbpl`jbo.avoh#lenbm#bmg?,wg=\t#kf#ofew*-ubo+*ebopf*8oldj`boabmhjmdklnf#wlmbnjmd#Bqjylmb`qfgjwp*8\t~*8\telvmgfqjm#wvqm@loojmpafelqf#Avw#wkf`kbqdfgWjwof!=@bswbjmpsfoofgdlggfppWbd#..=Bggjmd9avw#tbpQf`fmw#sbwjfmwab`h#jm>ebopf%Ojm`lomtf#hmlt@lvmwfqIvgbjpnp`qjsw#bowfqfg$^*8\t##kbp#wkfvm`ofbqFufmw$/alwk#jmmlw#boo\t\t?"..#sob`jmdkbqg#wl#`fmwfqplqw#le`ojfmwppwqffwpAfqmbqgbppfqwpwfmg#wlebmwbpzgltm#jmkbqalvqEqffglniftfoqz,balvw--pfbq`kofdfmgpjp#nbgfnlgfqm#lmoz#lmlmoz#wljnbdf!#ojmfbq#sbjmwfqbmg#mlwqbqfoz#b`qlmzngfojufqpklqwfq33%bns8bp#nbmztjgwk>!,)#?"X@wjwof#>le#wkf#oltfpw#sj`hfg#fp`bsfgvpfp#lesflsofp#Svaoj`Nbwwkftwb`wj`pgbnbdfgtbz#elqobtp#lefbpz#wl#tjmgltpwqlmd##pjnsof~`bw`k+pfufmwkjmelal{tfmw#wlsbjmwfg`jwjyfmJ#glm$wqfwqfbw-#Plnf#tt-!*8\talnajmdnbjowl9nbgf#jm-#Nbmz#`bqqjfpx~8tjtlqh#lepzmlmzngfefbwpebulqfglswj`bosbdfWqbvmofpp#pfmgjmdofew!=?`lnP`lqBoo#wkfiRvfqz-wlvqjpw@obppj`ebopf!#Tjokfonpvavqapdfmvjmfajpklsp-psojw+dolabo#elooltpalgz#lemlnjmbo@lmwb`wpf`vobqofew#wl`kjfeoz.kjggfm.abmmfq?,oj=\t\t-#Tkfm#jm#alwkgjpnjppF{solqfbotbzp#ujb#wkfpsb/]lotfoebqfqvojmd#bqqbmdf`bswbjmkjp#plmqvof#lekf#wllhjwpfoe/>3%bns8+`boofgpbnsofpwl#nbhf`ln,sbdNbqwjm#Hfmmfgzb``fswpevoo#lekbmgofgAfpjgfp,,..=?,baof#wlwbqdfwpfppfm`fkjn#wl#jwp#az#`lnnlm-njmfqbowl#wbhftbzp#wlp-lqd,obgujpfgsfmbowzpjnsof9je#wkfzOfwwfqpb#pklqwKfqafqwpwqjhfp#dqlvsp-ofmdwkeojdkwplufqobspoltoz#ofppfq#pl`jbo#?,s=\t\n\njw#jmwlqbmhfg#qbwf#levo=\t##bwwfnswsbjq#lenbhf#jwHlmwbhwBmwlmjlkbujmd#qbwjmdp#b`wjufpwqfbnpwqbssfg!*-`pp+klpwjofofbg#wlojwwof#dqlvsp/Sj`wvqf..=\t\t#qltp>!#laif`wjmufqpf?ellwfq@vpwlnU=?_,p`qploujmd@kbnafqpobufqztlvmgfgtkfqfbp">#$vmgelq#boosbqwoz#.qjdkw9Bqbajbmab`hfg#`fmwvqzvmjw#lenlajof.Fvqlsf/jp#klnfqjph#legfpjqfg@ojmwlm`lpw#lebdf#le#af`lnf#mlmf#les%rvlw8Njggof#fbg$*X3@qjwj`ppwvgjlp=%`lsz8dqlvs!=bppfnaonbhjmd#sqfppfgtjgdfw-sp9!#<#qfavjowaz#plnfElqnfq#fgjwlqpgfobzfg@bmlmj`kbg#wkfsvpkjmd`obpp>!avw#bqfsbqwjboAbazolmalwwln#`bqqjfq@lnnbmgjwp#vpfBp#tjwk`lvqpfpb#wkjqggfmlwfpbopl#jmKlvpwlm13s{8!=b``vpfgglvaof#dlbo#leEbnlvp#*-ajmg+sqjfpwp#Lmojmfjm#Ivozpw#(#!d`lmpvowgf`jnbokfosevoqfujufgjp#ufqzq$($jswolpjmd#efnbofpjp#boplpwqjmdpgbzp#lebqqjuboevwvqf#?laif`welq`jmdPwqjmd+!#,=\t\n\nkfqf#jpfm`lgfg-##Wkf#aboollmglmf#az,`lnnlmad`lolqobt#le#Jmgjbmbbuljgfgavw#wkf1s{#0s{irvfqz-bewfq#bsloj`z-nfm#bmgellwfq.>#wqvf8elq#vpfp`qffm-Jmgjbm#jnbdf#>ebnjoz/kwws9,,#%maps8gqjufqpfwfqmbopbnf#bpmlwj`fgujftfqp~*+*8\t#jp#nlqfpfbplmpelqnfq#wkf#mftjp#ivpw`lmpfmw#Pfbq`ktbp#wkftkz#wkfpkjssfgaq=?aq=tjgwk9#kfjdkw>nbgf#le`vjpjmfjp#wkbwb#ufqz#Bgnjqbo#ej{fg8mlqnbo#NjppjlmSqfpp/#lmwbqjl`kbqpfwwqz#wl#jmubgfg>!wqvf!psb`jmdjp#nlpwb#nlqf#wlwboozeboo#le~*8\t##jnnfmpfwjnf#jmpfw#lvwpbwjpezwl#ejmggltm#wlolw#le#Sobzfqpjm#Ivmfrvbmwvnmlw#wkfwjnf#wlgjpwbmwEjmmjpkpq`#>#+pjmdof#kfos#leDfqnbm#obt#bmgobafofgelqfpwp`llhjmdpsb`f!=kfbgfq.tfoo#bpPwbmofzaqjgdfp,dolabo@qlbwjb#Balvw#X3^8\t##jw/#bmgdqlvsfgafjmd#b*xwkqltkf#nbgfojdkwfqfwkj`boEEEEEE!alwwln!ojhf#b#fnsolzpojuf#jmbp#pffmsqjmwfqnlpw#leva.ojmhqfif`wpbmg#vpfjnbdf!=pv``ffgeffgjmdMv`ofbqjmelqnbwl#kfosTlnfm$pMfjwkfqNf{j`bmsqlwfjm?wbaof#az#nbmzkfbowkzobtpvjwgfujpfg-svpk+xpfoofqppjnsoz#Wkqlvdk-`llhjf#Jnbdf+logfq!=vp-ip!=#Pjm`f#vmjufqpobqdfq#lsfm#wl"..#fmgojfp#jm$^*8\t##nbqhfwtkl#jp#+!GLN@lnbmbdfglmf#elqwzsfle#Hjmdglnsqlejwpsqlslpfwl#pklt`fmwfq8nbgf#jwgqfppfgtfqf#jmnj{wvqfsqf`jpfbqjpjmdpq`#>#$nbhf#b#pf`vqfgAbswjpwulwjmd#\t\n\nubq#Nbq`k#1dqft#vs@ojnbwf-qfnlufphjoofgtbz#wkf?,kfbg=eb`f#leb`wjmd#qjdkw!=wl#tlqhqfgv`fpkbp#kbgfqf`wfgpklt+*8b`wjlm>allh#lebm#bqfb>>#!kww?kfbgfq\t?kwno=`lmelqneb`jmd#`llhjf-qfoz#lmklpwfg#-`vpwlnkf#tfmwavw#elqpsqfbg#Ebnjoz#b#nfbmplvw#wkfelqvnp-ellwbdf!=Nlajo@ofnfmwp!#jg>!bp#kjdkjmwfmpf..=?"..efnbof#jp#pffmjnsojfgpfw#wkfb#pwbwfbmg#kjpebpwfpwafpjgfpavwwlm\\alvmgfg!=?jnd#Jmelal{fufmwp/b#zlvmdbmg#bqfMbwjuf#`kfbsfqWjnflvwbmg#kbpfmdjmfptlm#wkf+nlpwozqjdkw9#ejmg#b#.alwwlnSqjm`f#bqfb#lenlqf#lepfbq`k\\mbwvqf/ofdboozsfqjlg/obmg#lelq#tjwkjmgv`fgsqlujmdnjppjofol`boozBdbjmpwwkf#tbzh%rvlw8s{8!=\tsvpkfg#babmglmmvnfqbo@fqwbjmJm#wkjpnlqf#jmlq#plnfmbnf#jpbmg/#jm`qltmfgJPAM#3.`qfbwfpL`wlafqnbz#mlw`fmwfq#obwf#jmGfefm`ffmb`wfgtjpk#wlaqlbgoz`llojmdlmolbg>jw-#Wkfqf`lufqNfnafqpkfjdkw#bppvnfp?kwno=\tsflsof-jm#lmf#>tjmgltellwfq\\b#dllg#qfhobnblwkfqp/wl#wkjp\\`llhjfsbmfo!=Olmglm/gfejmfp`qvpkfgabswjpn`lbpwbopwbwvp#wjwof!#nluf#wlolpw#jmafwwfq#jnsojfpqjuboqzpfqufqp#PzpwfnSfqkbspfp#bmg#`lmwfmgeoltjmdobpwfg#qjpf#jmDfmfpjpujft#leqjpjmd#pffn#wlavw#jm#ab`hjmdkf#tjoodjufm#bdjujmd#`jwjfp-eolt#le#Obwfq#boo#avwKjdktbzlmoz#azpjdm#lekf#glfpgjeefqpabwwfqz%bns8obpjmdofpwkqfbwpjmwfdfqwbhf#lmqfevpfg`boofg#>VP%bnsPff#wkfmbwjufpaz#wkjppzpwfn-kfbg#le9klufq/ofpajbmpvqmbnfbmg#boo`lnnlm,kfbgfq\\\\sbqbnpKbqubqg,sj{fo-qfnlubopl#olmdqlof#leiljmwozphzp`qbVmj`lgfaq#,=\tBwobmwbmv`ofvp@lvmwz/svqfoz#`lvmw!=fbpjoz#avjog#blm`oj`hb#djufmsljmwfqk%rvlw8fufmwp#fopf#x\tgjwjlmpmlt#wkf/#tjwk#nbm#tkllqd,Tfalmf#bmg`buboqzKf#gjfgpfbwwof33/333#xtjmgltkbuf#wlje+tjmgbmg#jwpplofoz#n%rvlw8qfmftfgGfwqljwbnlmdpwfjwkfq#wkfn#jmPfmbwlqVp?,b=?Hjmd#leEqbm`jp.sqlgv`kf#vpfgbqw#bmgkjn#bmgvpfg#azp`lqjmdbw#klnfwl#kbufqfobwfpjajojwzeb`wjlmAveebolojmh!=?tkbw#kfeqff#wl@jwz#le`lnf#jmpf`wlqp`lvmwfglmf#gbzmfqulvpprvbqf#~8je+dljm#tkbwjnd!#bojp#lmozpfbq`k,wvfpgbzollpfozPlolnlmpf{vbo#.#?b#kqnfgjvn!GL#MLW#Eqbm`f/tjwk#b#tbq#bmgpf`lmg#wbhf#b#=\t\t\tnbqhfw-kjdktbzglmf#jm`wjujwz!obpw!=laojdfgqjpf#wl!vmgfejnbgf#wl#Fbqoz#sqbjpfgjm#jwp#elq#kjpbwkofwfIvsjwfqZbkll"#wfqnfg#pl#nbmzqfbooz#p-#Wkf#b#tlnbmgjqf`w#qjdkw!#aj`z`ofb`jmd>!gbz#bmgpwbwjmdQbwkfq/kjdkfq#Leej`f#bqf#mltwjnfp/#tkfm#b#sbz#elqlm#wkjp.ojmh!=8alqgfqbqlvmg#bmmvbo#wkf#Mftsvw#wkf-`ln!#wbhjm#wlb#aqjfe+jm#wkfdqlvsp-8#tjgwkfmyznfppjnsof#jm#obwfxqfwvqmwkfqbszb#sljmwabmmjmdjmhp!=\t+*8!#qfb#sob`f_v330@bbalvw#bwq=\t\n\n``lvmw#djufp#b?P@QJSWQbjotbzwkfnfp,wlloal{AzJg+!{kvnbmp/tbw`kfpjm#plnf#je#+tj`lnjmd#elqnbwp#Vmgfq#avw#kbpkbmgfg#nbgf#azwkbm#jmefbq#legfmlwfg,jeqbnfofew#jmulowbdfjm#fb`kb%rvlw8abpf#leJm#nbmzvmgfqdlqfdjnfpb`wjlm#?,s=\t?vpwlnUb8%dw8?,jnslqwplq#wkbwnlpwoz#%bns8qf#pjyf>!?,b=?,kb#`obppsbppjufKlpw#>#TkfwkfqefqwjofUbqjlvp>X^8+ev`bnfqbp,=?,wg=b`wp#bpJm#plnf=\t\t?"lqdbmjp#?aq#,=Afjijmd`bwbo/Lgfvwp`kfvqlsfvfvphbqbdbfjodfpufmphbfpsb/]bnfmpbifvpvbqjlwqbabiln/E{j`ls/Mdjmbpjfnsqfpjpwfnbl`wvaqfgvqbmwfb/]bgjqfnsqfpbnlnfmwlmvfpwqlsqjnfqbwqbu/Epdqb`jbpmvfpwqbsql`fplfpwbglp`bojgbgsfqplmbm/Vnfqlb`vfqgln/Vpj`bnjfnaqllefqwbpbodvmlpsb/Apfpfifnsolgfqf`klbgfn/Mpsqjubglbdqfdbqfmob`fpslpjaofklwfofppfujoobsqjnfql/Vowjnlfufmwlpbq`kjul`vowvqbnvifqfpfmwqbgbbmvm`jlfnabqdlnfq`bgldqbmgfpfpwvgjlnfilqfpefaqfqlgjpf/]lwvqjpnl`/_gjdlslqwbgbfpsb`jlebnjojbbmwlmjlsfqnjwfdvbqgbqbodvmbpsqf`jlpbodvjfmpfmwjglujpjwbpw/Awvol`lml`fqpfdvmgl`lmpfileqbm`jbnjmvwlppfdvmgbwfmfnlpfef`wlpn/Mobdbpfpj/_mqfujpwbdqbmbgb`lnsqbqjmdqfpldbq`/Abb``j/_mf`vbglqrvjfmfpjm`ovplgfafq/Mnbwfqjbklnaqfpnvfpwqbslgq/Abnb/]bmb/Vowjnbfpwbnlplej`jbowbnajfmmjmd/Vmpbovglpslgfnlpnfilqbqslpjwjlmavpjmfppklnfsbdfpf`vqjwzobmdvbdfpwbmgbqg`bnsbjdmefbwvqfp`bwfdlqzf{wfqmbo`kjogqfmqfpfqufgqfpfbq`kf{`kbmdfebulqjwfwfnsobwfnjojwbqzjmgvpwqzpfquj`fpnbwfqjbosqlgv`wpy.jmgf{9`lnnfmwpplewtbqf`lnsofwf`bofmgbqsobwelqnbqwj`ofpqfrvjqfgnlufnfmwrvfpwjlmavjogjmdslojwj`pslppjaofqfojdjlmskzpj`boeffgab`hqfdjpwfqsj`wvqfpgjpbaofgsqlwl`lobvgjfm`fpfwwjmdpb`wjujwzfofnfmwpofbqmjmdbmzwkjmdbapwqb`wsqldqfpplufqujftnbdbyjmff`lmlnj`wqbjmjmdsqfppvqfubqjlvp#?pwqlmd=sqlsfqwzpklssjmdwldfwkfqbgubm`fgafkbujlqgltmolbgefbwvqfgellwaboopfof`wfgObmdvbdfgjpwbm`fqfnfnafqwqb`hjmdsbpptlqgnlgjejfgpwvgfmwpgjqf`wozejdkwjmdmlqwkfqmgbwbabpfefpwjuboaqfbhjmdol`bwjlmjmwfqmfwgqlsgltmsqb`wj`ffujgfm`fevm`wjlmnbqqjbdfqfpslmpfsqlaofnpmfdbwjufsqldqbnpbmbozpjpqfofbpfgabmmfq!=svq`kbpfsloj`jfpqfdjlmbo`qfbwjufbqdvnfmwallhnbqhqfefqqfq`kfnj`bogjujpjlm`booab`hpfsbqbwfsqlif`wp`lmeoj`wkbqgtbqfjmwfqfpwgfojufqznlvmwbjmlawbjmfg>#ebopf8elq+ubq#b``fswfg`bsb`jwz`lnsvwfqjgfmwjwzbjq`qbewfnsolzfgsqlslpfgglnfpwj`jm`ovgfpsqlujgfgklpsjwboufqwj`bo`loobspfbssqlb`ksbqwmfqpoldl!=?bgbvdkwfqbvwklq!#`vowvqboebnjojfp,jnbdfp,bppfnaozsltfqevowfb`kjmdejmjpkfggjpwqj`w`qjwj`bo`dj.ajm,svqslpfpqfrvjqfpfof`wjlmaf`lnjmdsqlujgfpb`bgfnj`f{fq`jpfb`wvbooznfgj`jmf`lmpwbmwb``jgfmwNbdbyjmfgl`vnfmwpwbqwjmdalwwln!=lapfqufg9#%rvlw8f{wfmgfgsqfujlvpPlewtbqf`vpwlnfqgf`jpjlmpwqfmdwkgfwbjofgpojdkwozsobmmjmdwf{wbqfb`vqqfm`zfufqzlmfpwqbjdkwwqbmpefqslpjwjufsqlgv`fgkfqjwbdfpkjssjmdbaplovwfqf`fjufgqfofubmwavwwlm!#ujlofm`fbmztkfqfafmfejwpobvm`kfgqf`fmwozboojbm`felooltfgnvowjsofavoofwjmjm`ovgfgl``vqqfgjmwfqmbo\'+wkjp*-qfsvaoj`=?wq=?wg`lmdqfppqf`lqgfgvowjnbwfplovwjlm?vo#jg>!gjp`lufqKlnf?,b=tfapjwfpmfwtlqhpbowklvdkfmwjqfoznfnlqjbonfppbdfp`lmwjmvfb`wjuf!=plnftkbwuj`wlqjbTfpwfqm##wjwof>!Ol`bwjlm`lmwqb`wujpjwlqpGltmolbgtjwklvw#qjdkw!=\tnfbpvqfptjgwk#>#ubqjbaofjmuloufgujqdjmjbmlqnboozkbssfmfgb``lvmwppwbmgjmdmbwjlmboQfdjpwfqsqfsbqfg`lmwqlopb``vqbwfajqwkgbzpwqbwfdzleej`jbodqbskj`p`qjnjmboslppjaoz`lmpvnfqSfqplmbopsfbhjmdubojgbwfb`kjfufg-isd!#,=nb`kjmfp?,k1=\t##hfztlqgpeqjfmgozaqlwkfqp`lnajmfglqjdjmbo`lnslpfgf{sf`wfgbgfrvbwfsbhjpwbmeloolt!#ubovbaof?,obafo=qfobwjufaqjmdjmdjm`qfbpfdlufqmlqsovdjmp,Ojpw#le#Kfbgfq!=!#mbnf>!#+%rvlw8dqbgvbwf?,kfbg=\t`lnnfq`fnbobzpjbgjqf`wlqnbjmwbjm8kfjdkw9p`kfgvof`kbmdjmdab`h#wl#`bwkloj`sbwwfqmp`lolq9# dqfbwfpwpvssojfpqfojbaof?,vo=\t\n\n?pfof`w#`jwjyfmp`olwkjmdtbw`kjmd?oj#jg>!psf`jej``bqqzjmdpfmwfm`f?`fmwfq=`lmwqbpwwkjmhjmd`bw`k+f*plvwkfqmNj`kbfo#nfq`kbmw`bqlvpfosbggjmd9jmwfqjlq-psojw+!ojybwjlmL`wlafq#*xqfwvqmjnsqlufg..%dw8\t\t`lufqbdf`kbjqnbm-smd!#,=pvaif`wpQj`kbqg#tkbwfufqsqlabaozqf`lufqzabpfabooivgdnfmw`lmmf`w--`pp!#,=#tfapjwfqfslqwfggfebvow!,=?,b=\tfof`wqj`p`lwobmg`qfbwjlmrvbmwjwz-#JPAM#3gjg#mlw#jmpwbm`f.pfbq`k.!#obmd>!psfbhfqp@lnsvwfq`lmwbjmpbq`kjufpnjmjpwfqqfb`wjlmgjp`lvmwJwbojbml`qjwfqjbpwqlmdoz9#$kwws9$p`qjsw$`lufqjmdleefqjmdbssfbqfgAqjwjpk#jgfmwjezEb`fallhmvnfqlvpufkj`ofp`lm`fqmpBnfqj`bmkbmgojmdgju#jg>!Tjoojbn#sqlujgfq\\`lmwfmwb``vqb`zpf`wjlm#bmgfqplmeof{jaof@bwfdlqzobtqfm`f?p`qjsw=obzlvw>!bssqlufg#nb{jnvnkfbgfq!=?,wbaof=Pfquj`fpkbnjowlm`vqqfmw#`bmbgjbm`kbmmfop,wkfnfp,,bqwj`oflswjlmboslqwvdboubovf>!!jmwfqubotjqfofppfmwjwofgbdfm`jfpPfbq`k!#nfbpvqfgwklvpbmgpsfmgjmd%kfoojs8mft#Gbwf!#pjyf>!sbdfMbnfnjggof!#!#,=?,b=kjggfm!=pfrvfm`fsfqplmbolufqeoltlsjmjlmpjoojmljpojmhp!=\t\n?wjwof=ufqpjlmppbwvqgbzwfqnjmbojwfnsqlsfmdjmffqpf`wjlmpgfpjdmfqsqlslpbo>!ebopf!Fpsb/]loqfofbpfppvanjw!#fq%rvlw8bggjwjlmpznswlnplqjfmwfgqfplvq`fqjdkw!=?sofbpvqfpwbwjlmpkjpwlqz-ofbujmd##alqgfq>`lmwfmwp`fmwfq!=-\t\tPlnf#gjqf`wfgpvjwbaofavodbqjb-pklt+*8gfpjdmfgDfmfqbo#`lm`fswpF{bnsofptjoojbnpLqjdjmbo!=?psbm=pfbq`k!=lsfqbwlqqfrvfpwpb#%rvlw8booltjmdGl`vnfmwqfujpjlm-#\t\tWkf#zlvqpfoe@lmwb`w#nj`kjdbmFmdojpk#`lovnajbsqjlqjwzsqjmwjmdgqjmhjmdeb`jojwzqfwvqmfg@lmwfmw#leej`fqpQvppjbm#dfmfqbwf.;;6:.2!jmgj`bwfebnjojbq#rvbojwznbqdjm93#`lmwfmwujftslqw`lmwb`wp.wjwof!=slqwbaof-ofmdwk#fojdjaofjmuloufpbwobmwj`lmolbg>!gfebvow-pvssojfgsbznfmwpdolppbqz\t\tBewfq#dvjgbm`f?,wg=?wgfm`lgjmdnjggof!=`bnf#wl#gjpsobzpp`lwwjpkilmbwkbmnbilqjwztjgdfwp-`ojmj`bowkbjobmgwfb`kfqp?kfbg=\t\nbeef`wfgpvsslqwpsljmwfq8wlPwqjmd?,pnboo=lhobklnbtjoo#af#jmufpwlq3!#bow>!klojgbzpQfplvq`foj`fmpfg#+tkj`k#-#Bewfq#`lmpjgfqujpjwjmdf{solqfqsqjnbqz#pfbq`k!#bmgqljg!rvj`hoz#nffwjmdpfpwjnbwf8qfwvqm#8`lolq9 #kfjdkw>bssqlubo/#%rvlw8#`kf`hfg-njm-ip!nbdmfwj`=?,b=?,kelqf`bpw-#Tkjof#wkvqpgbzgufqwjpf%fb`vwf8kbp@obppfubovbwflqgfqjmdf{jpwjmdsbwjfmwp#Lmojmf#`lolqbglLswjlmp!`bnsafoo?"..#fmg?,psbm=??aq#,=\t\\slsvspp`jfm`fp/%rvlw8#rvbojwz#Tjmgltp#bppjdmfgkfjdkw9#?a#`obppof%rvlw8#ubovf>!#@lnsbmzf{bnsofp?jeqbnf#afojfufpsqfpfmwpnbqpkboosbqw#le#sqlsfqoz*-\t\tWkf#wb{lmlnznv`k#le#?,psbm=\t!#gbwb.pqwvdv/Fpp`qlooWl#sqlif`w?kfbg=\tbwwlqmfzfnskbpjppslmplqpebm`zal{tlqog$p#tjogojef`kf`hfg>pfppjlmpsqldqbnns{8elmw.#Sqlif`wilvqmbopafojfufgub`bwjlmwklnsplmojdkwjmdbmg#wkf#psf`jbo#alqgfq>3`kf`hjmd?,walgz=?avwwlm#@lnsofwf`ofbqej{\t?kfbg=\tbqwj`of#?pf`wjlmejmgjmdpqlof#jm#slsvobq##L`wlafqtfapjwf#f{slpvqfvpfg#wl##`kbmdfplsfqbwfg`oj`hjmdfmwfqjmd`lnnbmgpjmelqnfg#mvnafqp##?,gju=`qfbwjmdlmPvanjwnbqzobmg`loofdfpbmbozwj`ojpwjmdp`lmwb`w-olddfgJmbgujplqzpjaojmdp`lmwfmw!p%rvlw8*p-#Wkjp#sb`hbdfp`kf`hal{pvddfpwpsqfdmbmwwlnlqqltpsb`jmd>j`lm-smdibsbmfpf`lgfabpfavwwlm!=dbnaojmdpv`k#bp#/#tkjof#?,psbm=#njpplvqjpslqwjmdwls92s{#-?,psbm=wfmpjlmptjgwk>!1obyzolbgmlufnafqvpfg#jm#kfjdkw>!`qjsw!=\t%maps8?,?wq=?wg#kfjdkw91,sqlgv`w`lvmwqz#jm`ovgf#ellwfq!#%ow8"..#wjwof!=?,irvfqz-?,elqn=\t+\vBl\bQ*+\vUmGx*kqubwphjjwbojbmlqln/Nm(ow/Pqh/Kf4K4]4C5dwbnaj/Emmlwj`jbpnfmpbifpsfqplmbpgfqf`klpmb`jlmbopfquj`jl`lmwb`wlvpvbqjlpsqldqbnbdlajfqmlfnsqfpbpbmvm`jlpubofm`jb`lolnajbgfpsv/Epgfslqwfpsqlzf`wlsqlgv`wls/Vaoj`lmlplwqlpkjpwlqjbsqfpfmwfnjoolmfpnfgjbmwfsqfdvmwbbmwfqjlqqf`vqplpsqlaofnbpbmwjbdlmvfpwqlplsjmj/_mjnsqjnjqnjfmwqbpbn/Eqj`bufmgfglqpl`jfgbgqfpsf`wlqfbojybqqfdjpwqlsbobaqbpjmwfq/Epfmwlm`fpfpsf`jbonjfnaqlpqfbojgbg`/_qglabybqbdlybs/Mdjmbppl`jbofpaolrvfbqdfpwj/_mborvjofqpjpwfnbp`jfm`jbp`lnsofwlufqpj/_m`lnsofwbfpwvgjlps/Vaoj`blaifwjulboj`bmwfavp`bglq`bmwjgbgfmwqbgbpb``jlmfpbq`kjulppvsfqjlqnbzlq/Abbofnbmjbevm`j/_m/Vowjnlpkb`jfmglbrvfoolpfgj`j/_mefqmbmglbnajfmwfeb`fallhmvfpwqbp`ojfmwfpsql`fplpabpwbmwfsqfpfmwbqfslqwbq`lmdqfplsvaoj`bq`lnfq`jl`lmwqbwli/_ufmfpgjpwqjwlw/E`mj`b`lmivmwlfmfqd/Abwqbabibqbpwvqjbpqf`jfmwfvwjojybqalofw/Ampboubglq`lqqf`wbwqbabilpsqjnfqlpmfdl`jlpojafqwbggfwboofpsbmwboobsq/_{jnlbonfq/Abbmjnbofprvj/Emfp`lqby/_mpf``j/_mavp`bmglls`jlmfpf{wfqjlq`lm`fswlwlgbu/Abdbofq/Abfp`qjajqnfgj`jmboj`fm`jb`lmpvowbbpsf`wlp`q/Awj`bg/_obqfpivpwj`jbgfafq/Mmsfq/Alglmf`fpjwbnbmwfmfqsfrvf/]lqf`jajgbwqjavmbowfmfqjef`bm`j/_m`bmbqjbpgfp`bqdbgjufqplpnboolq`bqfrvjfqfw/E`mj`lgfafq/Abujujfmgbejmbmybpbgfobmwfevm`jlmb`lmpfilpgje/A`jo`jvgbgfpbmwjdvbpbubmybgbw/Eqnjmlvmjgbgfpp/Mm`kfy`bnsb/]bplewlmj`qfujpwbp`lmwjfmfpf`wlqfpnlnfmwlpeb`vowbg`q/Egjwlgjufqpbppvsvfpwleb`wlqfppfdvmglpsfrvf/]b<_!?,pfof`w=Bvpwqbojb!#`obpp>!pjwvbwjlmbvwklqjwzelooltjmdsqjnbqjozlsfqbwjlm`kboofmdfgfufolsfgbmlmznlvpevm`wjlm#evm`wjlmp`lnsbmjfppwqv`wvqfbdqffnfmw!#wjwof>!slwfmwjbofgv`bwjlmbqdvnfmwppf`lmgbqz`lszqjdkwobmdvbdfpf{`ovpjuf`lmgjwjlm?,elqn=\tpwbwfnfmwbwwfmwjlmAjldqbskz~#fopf#x\tplovwjlmptkfm#wkf#Bmbozwj`pwfnsobwfpgbmdfqlvppbwfoojwfgl`vnfmwpsvaojpkfqjnslqwbmwsqlwlwzsfjmeovfm`f%qbrvl8?,feef`wjufdfmfqboozwqbmpelqnafbvwjevowqbmpslqwlqdbmjyfgsvaojpkfgsqlnjmfmwvmwjo#wkfwkvnambjoMbwjlmbo#-el`vp+*8lufq#wkf#njdqbwjlmbmmlvm`fgellwfq!=\tf{`fswjlmofpp#wkbmf{sfmpjufelqnbwjlmeqbnftlqhwfqqjwlqzmgj`bwjlm`vqqfmwoz`obppMbnf`qjwj`jpnwqbgjwjlmfopftkfqfBof{bmgfqbssljmwfgnbwfqjbopaqlbg`bpwnfmwjlmfgbeejojbwf?,lswjlm=wqfbwnfmwgjeefqfmw,gfebvow-Sqfpjgfmwlm`oj`h>!ajldqbskzlwkfqtjpfsfqnbmfmwEqbm/KbjpKlooztllgf{sbmpjlmpwbmgbqgp?,pwzof=\tqfgv`wjlmGf`fnafq#sqfefqqfg@bnaqjgdflsslmfmwpAvpjmfpp#`lmevpjlm=\t?wjwof=sqfpfmwfgf{sobjmfgglfp#mlw#tlqogtjgfjmwfqeb`fslpjwjlmpmftpsbsfq?,wbaof=\tnlvmwbjmpojhf#wkf#fppfmwjboejmbm`jbopfof`wjlmb`wjlm>!,babmglmfgFgv`bwjlmsbqpfJmw+pwbajojwzvmbaof#wl?,wjwof=\tqfobwjlmpMlwf#wkbwfeej`jfmwsfqelqnfgwtl#zfbqpPjm`f#wkfwkfqfelqftqbssfq!=bowfqmbwfjm`qfbpfgAbwwof#lesfq`fjufgwqzjmd#wlmf`fppbqzslqwqbzfgfof`wjlmpFojybafwk?,jeqbnf=gjp`lufqzjmpvqbm`fp-ofmdwk8ofdfmgbqzDfldqbskz`bmgjgbwf`lqslqbwfplnfwjnfppfquj`fp-jmkfqjwfg?,pwqlmd=@lnnvmjwzqfojdjlvpol`bwjlmp@lnnjwwffavjogjmdpwkf#tlqogml#olmdfqafdjmmjmdqfefqfm`f`bmmlw#afeqfrvfm`zwzsj`boozjmwl#wkf#qfobwjuf8qf`lqgjmdsqfpjgfmwjmjwjboozwf`kmjrvfwkf#lwkfqjw#`bm#aff{jpwfm`fvmgfqojmfwkjp#wjnfwfofsklmfjwfnp`lsfsqb`wj`fpbgubmwbdf*8qfwvqm#Elq#lwkfqsqlujgjmdgfnl`qb`zalwk#wkf#f{wfmpjufpveefqjmdpvsslqwfg`lnsvwfqp#evm`wjlmsqb`wj`bopbjg#wkbwjw#nbz#afFmdojpk?,eqln#wkf#p`kfgvofggltmolbgp?,obafo=\tpvpsf`wfgnbqdjm9#3psjqjwvbo?,kfbg=\t\tnj`qlplewdqbgvboozgjp`vppfgkf#af`bnff{f`vwjufirvfqz-ipklvpfklog`lmejqnfgsvq`kbpfgojwfqboozgfpwqlzfgvs#wl#wkfubqjbwjlmqfnbjmjmdjw#jp#mlw`fmwvqjfpIbsbmfpf#bnlmd#wkf`lnsofwfgbodlqjwknjmwfqfpwpqfafoojlmvmgfejmfgfm`lvqbdfqfpjybaofjmuloujmdpfmpjwjufvmjufqpbosqlujpjlm+bowklvdkefbwvqjmd`lmgv`wfg*/#tkj`k#`lmwjmvfg.kfbgfq!=Efaqvbqz#mvnfqlvp#lufqeolt9`lnslmfmweqbdnfmwpf{`foofmw`lopsbm>!wf`kmj`bomfbq#wkf#Bgubm`fg#plvq`f#lef{sqfppfgKlmd#Hlmd#Eb`fallhnvowjsof#nf`kbmjpnfofubwjlmleefmpjuf?,elqn=\t\npslmplqfggl`vnfmw-lq#%rvlw8wkfqf#bqfwklpf#tklnlufnfmwpsql`fppfpgjeej`vowpvanjwwfgqf`lnnfmg`lmujm`fgsqlnlwjmd!#tjgwk>!-qfsob`f+`obppj`bo`lbojwjlmkjp#ejqpwgf`jpjlmpbppjpwbmwjmgj`bwfgfulovwjlm.tqbssfq!fmlvdk#wlbolmd#wkfgfojufqfg..=\t?"..Bnfqj`bm#sqlwf`wfgMlufnafq#?,pwzof=?evqmjwvqfJmwfqmfw##lmaovq>!pvpsfmgfgqf`jsjfmwabpfg#lm#Nlqflufq/balojpkfg`loof`wfgtfqf#nbgffnlwjlmbofnfqdfm`zmbqqbwjufbgul`bwfps{8alqgfq`lnnjwwfggjq>!owq!fnsolzffpqfpfbq`k-#pfof`wfgpv``fpplq`vpwlnfqpgjpsobzfgPfswfnafqbgg@obpp+Eb`fallh#pvddfpwfgbmg#obwfqlsfqbwjmdfobalqbwfPlnfwjnfpJmpwjwvwf`fqwbjmozjmpwboofgelooltfqpIfqvpbofnwkfz#kbuf`lnsvwjmddfmfqbwfgsqlujm`fpdvbqbmwffbqajwqbqzqf`ldmjyftbmwfg#wls{8tjgwk9wkflqz#leafkbujlvqTkjof#wkffpwjnbwfgafdbm#wl#jw#af`bnfnbdmjwvgfnvpw#kbufnlqf#wkbmGjqf`wlqzf{wfmpjlmpf`qfwbqzmbwvqboozl``vqqjmdubqjbaofpdjufm#wkfsobwelqn-?,obafo=?ebjofg#wl`lnslvmgphjmgp#le#pl`jfwjfpbolmdpjgf#..%dw8\t\tplvwktfpwwkf#qjdkwqbgjbwjlmnbz#kbuf#vmfp`bsf+pslhfm#jm!#kqfe>!,sqldqbnnflmoz#wkf#`lnf#eqlngjqf`wlqzavqjfg#jmb#pjnjobqwkfz#tfqf?,elmw=?,Mlqtfdjbmpsf`jejfgsqlgv`jmdsbppfmdfq+mft#Gbwfwfnslqbqzej`wjlmboBewfq#wkffrvbwjlmpgltmolbg-qfdvobqozgfufolsfqbaluf#wkfojmhfg#wlskfmlnfmbsfqjlg#lewllowjs!=pvapwbm`fbvwlnbwj`bpsf`w#leBnlmd#wkf`lmmf`wfgfpwjnbwfpBjq#Elq`fpzpwfn#lelaif`wjufjnnfgjbwfnbhjmd#jwsbjmwjmdp`lmrvfqfgbqf#pwjoosql`fgvqfdqltwk#lekfbgfg#azFvqlsfbm#gjujpjlmpnlof`vofpeqbm`kjpfjmwfmwjlmbwwqb`wfg`kjogkllgbopl#vpfggfgj`bwfgpjmdbslqfgfdqff#leebwkfq#le`lmeoj`wp?,b=?,s=\t`bnf#eqlntfqf#vpfgmlwf#wkbwqf`fjujmdF{f`vwjuffufm#nlqfb``fpp#wl`lnnbmgfqSlojwj`bonvpj`jbmpgfoj`jlvpsqjplmfqpbgufmw#leVWE.;!#,=?"X@GBWBX!=@lmwb`wPlvwkfqm#ad`lolq>!pfqjfp#le-#Jw#tbp#jm#Fvqlsfsfqnjwwfgubojgbwf-bssfbqjmdleej`jboppfqjlvpoz.obmdvbdfjmjwjbwfgf{wfmgjmdolmd.wfqnjmeobwjlmpv`k#wkbwdfw@llhjfnbqhfg#az?,avwwlm=jnsofnfmwavw#jw#jpjm`qfbpfpgltm#wkf#qfrvjqjmdgfsfmgfmw..=\t?"..#jmwfqujftTjwk#wkf#`lsjfp#le`lmpfmpvptbp#avjowUfmfyvfob+elqnfqozwkf#pwbwfsfqplmmfopwqbwfdj`ebulvq#lejmufmwjlmTjhjsfgjb`lmwjmfmwujqwvbooztkj`k#tbpsqjm`jsof@lnsofwf#jgfmwj`bopklt#wkbwsqjnjwjufbtbz#eqlnnlof`vobqsqf`jpfozgjpploufgVmgfq#wkfufqpjlm>!=%maps8?,Jw#jp#wkf#Wkjp#jp#tjoo#kbuflqdbmjpnpplnf#wjnfEqjfgqj`ktbp#ejqpwwkf#lmoz#eb`w#wkbwelqn#jg>!sqf`fgjmdWf`kmj`boskzpj`jpwl``vqp#jmmbujdbwlqpf`wjlm!=psbm#jg>!plvdkw#wlafolt#wkfpvqujujmd~?,pwzof=kjp#gfbwkbp#jm#wkf`bvpfg#azsbqwjboozf{jpwjmd#vpjmd#wkftbp#djufmb#ojpw#leofufop#lemlwjlm#leLeej`jbo#gjpnjppfgp`jfmwjpwqfpfnaofpgvsoj`bwff{solpjufqf`lufqfgboo#lwkfqdboofqjfpxsbggjmd9sflsof#leqfdjlm#lebggqfppfpbppl`jbwfjnd#bow>!jm#nlgfqmpklvog#afnfwklg#leqfslqwjmdwjnfpwbnsmffgfg#wlwkf#Dqfbwqfdbqgjmdpffnfg#wlujftfg#bpjnsb`w#lmjgfb#wkbwwkf#Tlqogkfjdkw#lef{sbmgjmdWkfpf#bqf`vqqfmw!=`bqfevooznbjmwbjmp`kbqdf#le@obppj`bobggqfppfgsqfgj`wfgltmfqpkjs?gju#jg>!qjdkw!=\tqfpjgfm`fofbuf#wkf`lmwfmw!=bqf#lewfm##~*+*8\tsqlabaoz#Sqlefpplq.avwwlm!#qfpslmgfgpbzp#wkbwkbg#wl#afsob`fg#jmKvmdbqjbmpwbwvp#lepfqufp#bpVmjufqpbof{f`vwjlmbddqfdbwfelq#tkj`kjmef`wjlmbdqffg#wlkltfufq/#slsvobq!=sob`fg#lm`lmpwqv`wfof`wlqbopznalo#lejm`ovgjmdqfwvqm#wlbq`kjwf`w@kqjpwjbmsqfujlvp#ojujmd#jmfbpjfq#wlsqlefpplq\t%ow8"..#feef`w#lebmbozwj`ptbp#wbhfmtkfqf#wkfwllh#lufqafojfe#jmBeqjhbbmpbp#ebq#bpsqfufmwfgtlqh#tjwkb#psf`jbo?ejfogpfw@kqjpwnbpQfwqjfufg\t\tJm#wkf#ab`h#jmwlmlqwkfbpwnbdbyjmfp=?pwqlmd=`lnnjwwffdlufqmjmddqlvsp#lepwlqfg#jmfpwbaojpkb#dfmfqbojwp#ejqpwwkfjq#ltmslsvobwfgbm#laif`w@bqjaafbmboolt#wkfgjpwqj`wptjp`lmpjmol`bwjlm-8#tjgwk9#jmkbajwfgPl`jbojpwIbmvbqz#2?,ellwfq=pjnjobqoz`klj`f#lewkf#pbnf#psf`jej`#avpjmfpp#Wkf#ejqpw-ofmdwk8#gfpjqf#wlgfbo#tjwkpjm`f#wkfvpfqBdfmw`lm`fjufgjmgf{-sksbp#%rvlw8fmdbdf#jmqf`fmwoz/eft#zfbqptfqf#bopl\t?kfbg=\t?fgjwfg#azbqf#hmltm`jwjfp#jmb``fpphfz`lmgfnmfgbopl#kbufpfquj`fp/ebnjoz#leP`kllo#le`lmufqwfgmbwvqf#le#obmdvbdfnjmjpwfqp?,laif`w=wkfqf#jp#b#slsvobqpfrvfm`fpbgul`bwfgWkfz#tfqfbmz#lwkfqol`bwjlm>fmwfq#wkfnv`k#nlqfqfeof`wfgtbp#mbnfglqjdjmbo#b#wzsj`botkfm#wkfzfmdjmffqp`lvog#mlwqfpjgfmwptfgmfpgbzwkf#wkjqg#sqlgv`wpIbmvbqz#1tkbw#wkfzb#`fqwbjmqfb`wjlmpsql`fpplqbewfq#kjpwkf#obpw#`lmwbjmfg!=?,gju=\t?,b=?,wg=gfsfmg#lmpfbq`k!=\tsjf`fp#le`lnsfwjmdQfefqfm`fwfmmfppfftkj`k#kbp#ufqpjlm>?,psbm=#??,kfbgfq=djufp#wkfkjpwlqjbmubovf>!!=sbggjmd93ujft#wkbwwldfwkfq/wkf#nlpw#tbp#elvmgpvapfw#lebwwb`h#lm`kjogqfm/sljmwp#lesfqplmbo#slpjwjlm9boofdfgoz@ofufobmgtbp#obwfqbmg#bewfqbqf#djufmtbp#pwjoop`qloojmdgfpjdm#lenbhfp#wkfnv`k#ofppBnfqj`bmp-\t\tBewfq#/#avw#wkfNvpfvn#leolvjpjbmb+eqln#wkfnjmmfplwbsbqwj`ofpb#sql`fppGlnjmj`bmulovnf#leqfwvqmjmdgfefmpjuf33s{qjdknbgf#eqlnnlvpflufq!#pwzof>!pwbwfp#le+tkj`k#jp`lmwjmvfpEqbm`jp`lavjogjmd#tjwklvw#btjwk#plnftkl#tlvogb#elqn#leb#sbqw#leafelqf#jwhmltm#bp##Pfquj`fpol`bwjlm#bmg#lewfmnfbpvqjmdbmg#jw#jpsbsfqab`hubovfp#le\t?wjwof=>#tjmglt-gfwfqnjmffq%rvlw8#sobzfg#azbmg#fbqoz?,`fmwfq=eqln#wkjpwkf#wkqffsltfq#bmgle#%rvlw8jmmfqKWNO?b#kqfe>!z9jmojmf8@kvq`k#lewkf#fufmwufqz#kjdkleej`jbo#.kfjdkw9#`lmwfmw>!,`dj.ajm,wl#`qfbwfbeqjhbbmpfpsfqbmwleqbm/Kbjpobwujf)Mvojfwvuj)_(`f)Mwjmb(af)Mwjmb\fUh\fT{\fTN\n{I\np@Fr\vBl\bQ\tA{\vUmGx\tA{ypYA\0zX\bTV\bWl\bUdBM\vB{\npV\v@xB\\\np@DbGz\tal\npa\tfM\tuD\bV~mx\vQ}\ndS\tp\\\bVK\bS]\bU|oD\tkV\ved\vHR\nb~M`\nJpoD|Q\nLPSw\bTl\nAI\nxC\bWt\tBqF`Cm\vLm\tKx\t}t\bPv\ny\\\naB\tV\nZdXUli\tfr\ti@\tBHBDBV\t`V\n[]\tp_\tTn\n~A\nxR\tuD\t`{\bV@\tTn\tHK\tAJ\vxsZf\nqIZf\vBM\v|j\t}t\bSM\nmC\vQ}pfquj`jlpbqw/A`volbqdfmwjmbabq`folmb`vborvjfqsvaoj`bglsqlgv`wlpslo/Awj`bqfpsvfpwbtjhjsfgjbpjdvjfmwfa/Vprvfgb`lnvmjgbgpfdvqjgbgsqjm`jsbosqfdvmwbp`lmwfmjglqfpslmgfqufmfyvfobsqlaofnbpgj`jfnaqfqfob`j/_mmlujfnaqfpjnjobqfpsqlzf`wlpsqldqbnbpjmpwjwvwlb`wjujgbgfm`vfmwqbf`lmln/Abjn/Mdfmfp`lmwb`wbqgfp`bqdbqmf`fpbqjlbwfm`j/_mwfo/Eelml`lnjpj/_m`bm`jlmfp`bsb`jgbgfm`lmwqbqbm/Mojpjpebulqjwlpw/Eqnjmlpsqlujm`jbfwjrvfwbpfofnfmwlpevm`jlmfpqfpvowbgl`bq/M`wfqsqlsjfgbgsqjm`jsjlmf`fpjgbgnvmj`jsbo`qfb`j/_mgfp`bqdbpsqfpfm`jb`lnfq`jbolsjmjlmfpfifq`j`jlfgjwlqjbopbobnbm`bdlmy/Mofygl`vnfmwlsfo/A`vobqf`jfmwfpdfmfqbofpwbqqbdlmbsq/M`wj`bmlufgbgfpsqlsvfpwbsb`jfmwfpw/E`mj`bplaifwjulp`lmwb`wlp\fHB\fIk\fHn\fH^\fHS\fHc\fHU\fId\fHn\fH{\fHC\fHR\fHT\fHR\fHI\fHc\fHY\fHn\fH\\\fHU\fIk\fHy\fIg\fHd\fHy\fIm\fHw\fH\\\fHU\fHR\fH@\fHR\fHJ\fHy\fHU\fHR\fHT\fHA\fIl\fHU\fIm\fHc\fH\\\fHU\fIl\fHB\fId\fHn\fHJ\fHS\fHD\fH@\fHR\fHHgjsolgl`p\fHT\fHB\fHC\fH\\\fIn\fHF\fHD\fHR\fHB\fHF\fHH\fHR\fHG\fHS\fH\\\fHx\fHT\fHH\fHH\fH\\\fHU\fH^\fIg\fH{\fHU\fIm\fHj\fH@\fHR\fH\\\fHJ\fIk\fHZ\fHU\fIm\fHd\fHz\fIk\fH^\fHC\fHJ\fHS\fHy\fHR\fHB\fHY\fIk\fH@\fHH\fIl\fHD\fH@\fIl\fHv\fHB\fI`\fHH\fHT\fHR\fH^\fH^\fIk\fHz\fHp\fIe\fH@\fHB\fHJ\fHJ\fHH\fHI\fHR\fHD\fHU\fIl\fHZ\fHU\fH\\\fHi\fH^\fH{\fHy\fHA\fIl\fHD\fH{\fH\\\fHF\fHR\fHT\fH\\\fHR\fHH\fHy\fHS\fHc\fHe\fHT\fIk\fH{\fHC\fIl\fHU\fIn\fHm\fHj\fH{\fIk\fHs\fIl\fHB\fHz\fIg\fHp\fHy\fHR\fH\\\fHi\fHA\fIl\fH{\fHC\fIk\fHH\fIm\fHB\fHY\fIg\fHs\fHJ\fIk\fHn\fHi\fH{\fH\\\fH|\fHT\fIk\fHB\fIk\fH^\fH^\fH{\fHR\fHU\fHR\fH^\fHf\fHF\fH\\\fHv\fHR\fH\\\fH|\fHT\fHR\fHJ\fIk\fH\\\fHp\fHS\fHT\fHJ\fHS\fH^\fH@\fHn\fHJ\fH@\fHD\fHR\fHU\fIn\fHn\fH^\fHR\fHz\fHp\fIl\fHH\fH@\fHs\fHD\fHB\fHS\fH^\fHk\fHT\fIk\fHj\fHD\fIk\fHD\fHC\fHR\fHy\fIm\fH^\fH^\fIe\fH{\fHA\fHR\fH{\fH\\\fIk\fH^\fHp\fH{\fHU\fH\\\fHR\fHB\fH^\fH{\fIk\fHF\fIk\fHp\fHU\fHR\fHI\fHk\fHT\fIl\fHT\fHU\fIl\fHy\fH^\fHR\fHL\fIl\fHy\fHU\fHR\fHm\fHJ\fIn\fH\\\fHH\fHU\fHH\fHT\fHR\fHH\fHC\fHR\fHJ\fHj\fHC\fHR\fHF\fHR\fHy\fHy\fI`\fHD\fHZ\fHR\fHB\fHJ\fIk\fHz\fHC\fHU\fIl\fH\\\fHR\fHC\fHz\fIm\fHJ\fH^\fH{\fIl`bwfdlqjfpf{sfqjfm`f?,wjwof=\t@lszqjdkw#ibubp`qjsw`lmgjwjlmpfufqzwkjmd?s#`obpp>!wf`kmloldzab`hdqlvmg?b#`obpp>!nbmbdfnfmw%`lsz8#132ibubP`qjsw`kbqb`wfqpaqfbg`qvnawkfnpfoufpklqjylmwbodlufqmnfmw@bojelqmjbb`wjujwjfpgjp`lufqfgMbujdbwjlmwqbmpjwjlm`lmmf`wjlmmbujdbwjlmbssfbqbm`f?,wjwof=?n`kf`hal{!#wf`kmjrvfpsqlwf`wjlmbssbqfmwozbp#tfoo#bpvmw$/#$VB.qfplovwjlmlsfqbwjlmpwfofujpjlmwqbmpobwfgTbpkjmdwlmmbujdbwlq-#>#tjmglt-jnsqfppjlm%ow8aq%dw8ojwfqbwvqfslsvobwjlmad`lolq>! fpsf`jbooz#`lmwfmw>!sqlgv`wjlmmftpofwwfqsqlsfqwjfpgfejmjwjlmofbgfqpkjsWf`kmloldzSbqojbnfmw`lnsbqjplmvo#`obpp>!-jmgf{Le+!`lm`ovpjlmgjp`vppjlm`lnslmfmwpajloldj`boQfulovwjlm\\`lmwbjmfqvmgfqpwllgmlp`qjsw=?sfqnjppjlmfb`k#lwkfqbwnlpskfqf#lmel`vp>!?elqn#jg>!sql`fppjmdwkjp-ubovfdfmfqbwjlm@lmefqfm`fpvapfrvfmwtfoo.hmltmubqjbwjlmpqfsvwbwjlmskfmlnfmlmgjp`jsojmfoldl-smd!#+gl`vnfmw/alvmgbqjfpf{sqfppjlmpfwwofnfmwAb`hdqlvmglvw#le#wkffmwfqsqjpf+!kwwsp9!#vmfp`bsf+!sbpptlqg!#gfnl`qbwj`?b#kqfe>!,tqbssfq!=\tnfnafqpkjsojmdvjpwj`s{8sbggjmdskjolplskzbppjpwbm`fvmjufqpjwzeb`jojwjfpqf`ldmjyfgsqfefqfm`fje#+wzsflenbjmwbjmfgul`bavobqzkzslwkfpjp-pvanjw+*8%bns8maps8bmmlwbwjlmafkjmg#wkfElvmgbwjlmsvaojpkfq!bppvnswjlmjmwqlgv`fg`lqqvswjlmp`jfmwjpwpf{soj`jwozjmpwfbg#legjnfmpjlmp#lm@oj`h>!`lmpjgfqfggfsbqwnfmwl``vsbwjlmpllm#bewfqjmufpwnfmwsqlmlvm`fgjgfmwjejfgf{sfqjnfmwNbmbdfnfmwdfldqbskj`!#kfjdkw>!ojmh#qfo>!-qfsob`f+,gfsqfppjlm`lmefqfm`fsvmjpknfmwfojnjmbwfgqfpjpwbm`fbgbswbwjlmlsslpjwjlmtfoo#hmltmpvssofnfmwgfwfqnjmfgk2#`obpp>!3s{8nbqdjmnf`kbmj`bopwbwjpwj`p`fofaqbwfgDlufqmnfmw\t\tGvqjmd#wgfufolsfqpbqwjej`jbofrvjubofmwlqjdjmbwfg@lnnjppjlmbwwb`knfmw?psbm#jg>!wkfqf#tfqfMfgfqobmgpafzlmg#wkfqfdjpwfqfgilvqmbojpweqfrvfmwozboo#le#wkfobmd>!fm!#?,pwzof=\tbaplovwf8#pvsslqwjmdf{wqfnfoz#nbjmpwqfbn?,pwqlmd=#slsvobqjwzfnsolznfmw?,wbaof=\t#`lopsbm>!?,elqn=\t##`lmufqpjlmbalvw#wkf#?,s=?,gju=jmwfdqbwfg!#obmd>!fmSlqwvdvfpfpvapwjwvwfjmgjujgvbojnslppjaofnvowjnfgjbbonlpw#boos{#plojg# bsbqw#eqlnpvaif`w#wljm#Fmdojpk`qjwj`jyfgf{`fsw#elqdvjgfojmfplqjdjmboozqfnbqhbaofwkf#pf`lmgk1#`obpp>!?b#wjwof>!+jm`ovgjmdsbqbnfwfqpsqlkjajwfg>#!kwws9,,gj`wjlmbqzsfq`fswjlmqfulovwjlmelvmgbwjlms{8kfjdkw9pv``fppevopvsslqwfqpnjoofmmjvnkjp#ebwkfqwkf#%rvlw8ml.qfsfbw8`lnnfq`jbojmgvpwqjbofm`lvqbdfgbnlvmw#le#vmleej`jbofeej`jfm`zQfefqfm`fp`llqgjmbwfgjp`objnfqf{sfgjwjlmgfufolsjmd`bo`vobwfgpjnsojejfgofdjwjnbwfpvapwqjmd+3!#`obpp>!`lnsofwfozjoovpwqbwfejuf#zfbqpjmpwqvnfmwSvaojpkjmd2!#`obpp>!spz`kloldz`lmejgfm`fmvnafq#le#bapfm`f#leel`vpfg#lmiljmfg#wkfpwqv`wvqfpsqfujlvpoz=?,jeqbnf=lm`f#bdbjmavw#qbwkfqjnnjdqbmwple#`lvqpf/b#dqlvs#leOjwfqbwvqfVmojhf#wkf?,b=%maps8\tevm`wjlm#jw#tbp#wkf@lmufmwjlmbvwlnlajofSqlwfpwbmwbddqfppjufbewfq#wkf#Pjnjobqoz/!#,=?,gju=`loof`wjlm\tevm`wjlmujpjajojwzwkf#vpf#leulovmwffqpbwwqb`wjlmvmgfq#wkf#wkqfbwfmfg)?"X@GBWBXjnslqwbm`fjm#dfmfqbowkf#obwwfq?,elqn=\t?,-jmgf{Le+$j#>#38#j#?gjeefqfm`fgfulwfg#wlwqbgjwjlmppfbq`k#elqvowjnbwfozwlvqmbnfmwbwwqjavwfppl.`boofg#~\t?,pwzof=fubovbwjlmfnskbpjyfgb``fppjaof?,pf`wjlm=pv``fppjlmbolmd#tjwkNfbmtkjof/jmgvpwqjfp?,b=?aq#,=kbp#af`lnfbpsf`wp#leWfofujpjlmpveej`jfmwabphfwabooalwk#pjgfp`lmwjmvjmdbm#bqwj`of?jnd#bow>!bgufmwvqfpkjp#nlwkfqnbm`kfpwfqsqjm`jsofpsbqwj`vobq`lnnfmwbqzfeef`wp#legf`jgfg#wl!=?pwqlmd=svaojpkfqpIlvqmbo#legjeej`vowzeb`jojwbwfb``fswbaofpwzof-`pp!\nevm`wjlm#jmmlubwjlm=@lszqjdkwpjwvbwjlmptlvog#kbufavpjmfppfpGj`wjlmbqzpwbwfnfmwplewfm#vpfgsfqpjpwfmwjm#Ibmvbqz`lnsqjpjmd?,wjwof=\t\ngjsolnbwj``lmwbjmjmdsfqelqnjmdf{wfmpjlmpnbz#mlw#af`lm`fsw#le#lm`oj`h>!Jw#jp#boplejmbm`jbo#nbhjmd#wkfOv{fnalvqdbggjwjlmbobqf#`boofgfmdbdfg#jm!p`qjsw!*8avw#jw#tbpfof`wqlmj`lmpvanjw>!\t?"..#Fmg#fof`wqj`boleej`jboozpvddfpwjlmwls#le#wkfvmojhf#wkfBvpwqbojbmLqjdjmboozqfefqfm`fp\t?,kfbg=\tqf`ldmjpfgjmjwjbojyfojnjwfg#wlBof{bmgqjbqfwjqfnfmwBgufmwvqfpelvq#zfbqp\t\t%ow8"..#jm`qfbpjmdgf`lqbwjlmk0#`obpp>!lqjdjmp#lelaojdbwjlmqfdvobwjlm`obppjejfg+evm`wjlm+bgubmwbdfpafjmd#wkf#kjpwlqjbmp?abpf#kqfeqfsfbwfgoztjoojmd#wl`lnsbqbaofgfpjdmbwfgmlnjmbwjlmevm`wjlmbojmpjgf#wkfqfufobwjlmfmg#le#wkfp#elq#wkf#bvwklqjyfgqfevpfg#wlwbhf#sob`fbvwlmlnlvp`lnsqlnjpfslojwj`bo#qfpwbvqbmwwtl#le#wkfEfaqvbqz#1rvbojwz#leptelaif`w-vmgfqpwbmgmfbqoz#bootqjwwfm#azjmwfqujftp!#tjgwk>!2tjwkgqbtboeolbw9ofewjp#vpvbooz`bmgjgbwfpmftpsbsfqpnzpwfqjlvpGfsbqwnfmwafpw#hmltmsbqojbnfmwpvssqfppfg`lmufmjfmwqfnfnafqfggjeefqfmw#pzpwfnbwj`kbp#ofg#wlsqlsbdbmgb`lmwqloofgjmeovfm`fp`fqfnlmjbosql`objnfgSqlwf`wjlmoj#`obpp>!P`jfmwjej``obpp>!ml.wqbgfnbqhpnlqf#wkbm#tjgfpsqfbgOjafqbwjlmwllh#sob`fgbz#le#wkfbp#olmd#bpjnsqjplmfgBggjwjlmbo\t?kfbg=\t?nObalqbwlqzMlufnafq#1f{`fswjlmpJmgvpwqjboubqjfwz#leeolbw9#ofeGvqjmd#wkfbppfppnfmwkbuf#affm#gfbop#tjwkPwbwjpwj`pl``vqqfm`f,vo=?,gju=`ofbqej{!=wkf#svaoj`nbmz#zfbqptkj`k#tfqflufq#wjnf/pzmlmznlvp`lmwfmw!=\tsqfpvnbaozkjp#ebnjozvpfqBdfmw-vmf{sf`wfgjm`ovgjmd#`kboofmdfgb#njmlqjwzvmgfejmfg!afolmdp#wlwbhfm#eqlnjm#L`wlafqslpjwjlm9#pbjg#wl#afqfojdjlvp#Efgfqbwjlm#qltpsbm>!lmoz#b#eftnfbmw#wkbwofg#wl#wkf..=\t?gju#?ejfogpfw=Bq`kajpkls#`obpp>!mlafjmd#vpfgbssqlb`kfpsqjujofdfpmlp`qjsw=\tqfpvowp#jmnbz#af#wkfFbpwfq#fddnf`kbmjpnpqfbplmbaofSlsvobwjlm@loof`wjlmpfof`wfg!=mlp`qjsw=,jmgf{-sksbqqjubo#le.ippgh$**8nbmbdfg#wljm`lnsofwf`bpvbowjfp`lnsofwjlm@kqjpwjbmpPfswfnafq#bqjwknfwj`sql`fgvqfpnjdkw#kbufSqlgv`wjlmjw#bssfbqpSkjolplskzeqjfmgpkjsofbgjmd#wldjujmd#wkfwltbqg#wkfdvbqbmwffggl`vnfmwfg`lolq9 333ujgfl#dbnf`lnnjppjlmqfeof`wjmd`kbmdf#wkfbppl`jbwfgpbmp.pfqjelmhfzsqfpp8#sbggjmd9Kf#tbp#wkfvmgfqozjmdwzsj`booz#/#bmg#wkf#pq`Fofnfmwpv``fppjufpjm`f#wkf#pklvog#af#mfwtlqhjmdb``lvmwjmdvpf#le#wkfoltfq#wkbmpkltp#wkbw?,psbm=\t\n\n`lnsobjmwp`lmwjmvlvprvbmwjwjfpbpwqlmlnfqkf#gjg#mlwgvf#wl#jwpbssojfg#wlbm#bufqbdffeelqwp#wlwkf#evwvqfbwwfnsw#wlWkfqfelqf/`bsbajojwzQfsvaoj`bmtbp#elqnfgFof`wqlmj`hjolnfwfqp`kboofmdfpsvaojpkjmdwkf#elqnfqjmgjdfmlvpgjqf`wjlmppvapjgjbqz`lmpsjqb`zgfwbjop#lebmg#jm#wkfbeelqgbaofpvapwbm`fpqfbplm#elq`lmufmwjlmjwfnwzsf>!baplovwfozpvsslpfgozqfnbjmfg#bbwwqb`wjufwqbufoojmdpfsbqbwfozel`vpfp#lmfofnfmwbqzbssoj`baofelvmg#wkbwpwzofpkffwnbmvp`qjswpwbmgp#elq#ml.qfsfbw+plnfwjnfp@lnnfq`jbojm#Bnfqj`bvmgfqwbhfmrvbqwfq#lebm#f{bnsofsfqplmboozjmgf{-sks!owqOjfvwfmbmw\t?gju#jg>!wkfz#tlvogbajojwz#lenbgf#vs#lemlwfg#wkbw`ofbq#wkbwbqdvf#wkbwwl#bmlwkfq`kjogqfm$psvqslpf#leelqnvobwfgabpfg#vslmwkf#qfdjlmpvaif`w#lesbppfmdfqpslppfppjlm-\t\tJm#wkf#Afelqf#wkfbewfqtbqgp`vqqfmwoz#b`qlpp#wkfp`jfmwjej``lnnvmjwz-`bsjwbojpnjm#Dfqnbmzqjdkw.tjmdwkf#pzpwfnPl`jfwz#leslojwj`jbmgjqf`wjlm9tfmw#lm#wlqfnlubo#le#Mft#Zlqh#bsbqwnfmwpjmgj`bwjlmgvqjmd#wkfvmofpp#wkfkjpwlqj`bokbg#affm#bgfejmjwjufjmdqfgjfmwbwwfmgbm`f@fmwfq#elqsqlnjmfm`fqfbgzPwbwfpwqbwfdjfpavw#jm#wkfbp#sbqw#le`lmpwjwvwf`objn#wkbwobalqbwlqz`lnsbwjaofebjovqf#le/#pv`k#bp#afdbm#tjwkvpjmd#wkf#wl#sqlujgfefbwvqf#leeqln#tkj`k,!#`obpp>!dfloldj`bopfufqbo#legfojafqbwfjnslqwbmw#klogp#wkbwjmd%rvlw8#ubojdm>wlswkf#Dfqnbmlvwpjgf#lemfdlwjbwfgkjp#`bqffqpfsbqbwjlmjg>!pfbq`ktbp#`boofgwkf#elvqwkqf`qfbwjlmlwkfq#wkbmsqfufmwjlmtkjof#wkf#fgv`bwjlm/`lmmf`wjmdb``vqbwfoztfqf#avjowtbp#hjoofgbdqffnfmwpnv`k#nlqf#Gvf#wl#wkftjgwk9#233plnf#lwkfqHjmdgln#lewkf#fmwjqfebnlvp#elqwl#`lmmf`wlaif`wjufpwkf#Eqfm`ksflsof#bmgefbwvqfg!=jp#pbjg#wlpwqv`wvqboqfefqfmgvnnlpw#lewfmb#pfsbqbwf.=\t?gju#jg#Leej`jbo#tlqogtjgf-bqjb.obafowkf#sobmfwbmg#jw#tbpg!#ubovf>!ollhjmd#bwafmfej`jbobqf#jm#wkfnlmjwlqjmdqfslqwfgozwkf#nlgfqmtlqhjmd#lmbooltfg#wltkfqf#wkf#jmmlubwjuf?,b=?,gju=plvmgwqb`hpfbq`kElqnwfmg#wl#afjmsvw#jg>!lsfmjmd#leqfpwqj`wfgbglswfg#azbggqfppjmdwkfloldjbmnfwklgp#leubqjbmw#le@kqjpwjbm#ufqz#obqdfbvwlnlwjufaz#ebq#wkfqbmdf#eqlnsvqpvjw#leeloolt#wkfaqlvdkw#wljm#Fmdobmgbdqff#wkbwb``vpfg#le`lnfp#eqlnsqfufmwjmdgju#pwzof>kjp#lq#kfqwqfnfmglvpeqffgln#le`lm`fqmjmd3#2fn#2fn8Abphfwaboo,pwzof-`ppbm#fbqojfqfufm#bewfq,!#wjwof>!-`ln,jmgf{wbhjmd#wkfsjwwpavqdk`lmwfmw!=?p`qjsw=+ewvqmfg#lvwkbujmd#wkf?,psbm=\t#l``bpjlmboaf`bvpf#jwpwbqwfg#wlskzpj`booz=?,gju=\t##`qfbwfg#az@vqqfmwoz/#ad`lolq>!wbajmgf{>!gjpbpwqlvpBmbozwj`p#bopl#kbp#b=?gju#jg>!?,pwzof=\t?`boofg#elqpjmdfq#bmg-pq`#>#!,,ujlobwjlmpwkjp#sljmw`lmpwbmwozjp#ol`bwfgqf`lqgjmdpg#eqln#wkfmfgfqobmgpslqwvdv/Fp;N;};D;u;F5m4K4]4_7`gfpbqqlool`lnfmwbqjlfgv`b`j/_mpfswjfnaqfqfdjpwqbglgjqf``j/_mvaj`b`j/_msvaoj`jgbgqfpsvfpwbpqfpvowbglpjnslqwbmwfqfpfqubglpbqw/A`volpgjefqfmwfppjdvjfmwfpqfs/Vaoj`bpjwvb`j/_mnjmjpwfqjlsqjub`jgbggjqf`wlqjlelqnb`j/_mslaob`j/_msqfpjgfmwf`lmw','fmjglpb``fplqjlpwf`kmlqbwjsfqplmbofp`bwfdlq/Abfpsf`jbofpgjpslmjaofb`wvbojgbgqfefqfm`jbuboobglojgajaojlwf`bqfob`jlmfp`bofmgbqjlslo/Awj`bpbmwfqjlqfpgl`vnfmwlpmbwvqbofybnbwfqjbofpgjefqfm`jbf`lm/_nj`bwqbmpslqwfqlgq/Advfysbqwj`jsbqfm`vfmwqbmgjp`vpj/_mfpwqv`wvqbevmgb`j/_meqf`vfmwfpsfqnbmfmwfwlwbonfmwf!2s{#plojg# -dje!#bow>!wqbmpsbqfmwjmelqnbwjlmbssoj`bwjlm!#lm`oj`h>!fpwbaojpkfgbgufqwjpjmd-smd!#bow>!fmujqlmnfmwsfqelqnbm`fbssqlsqjbwf%bns8ngbpk8jnnfgjbwfoz?,pwqlmd=?,qbwkfq#wkbmwfnsfqbwvqfgfufolsnfmw`lnsfwjwjlmsob`fklogfqujpjajojwz9`lszqjdkw!=3!#kfjdkw>!fufm#wklvdkqfsob`fnfmwgfpwjmbwjlm@lqslqbwjlm?vo#`obpp>!Bppl`jbwjlmjmgjujgvbopsfqpsf`wjufpfwWjnflvw+vqo+kwws9,,nbwkfnbwj`pnbqdjm.wls9fufmwvbooz#gfp`qjswjlm*#ml.qfsfbw`loof`wjlmp-ISDwkvnasbqwj`jsbwf,kfbg=?algzeolbw9ofew8?oj#`obpp>!kvmgqfgp#le\t\tKltfufq/#`lnslpjwjlm`ofbq9alwk8`llsfqbwjlmtjwkjm#wkf#obafo#elq>!alqgfq.wls9Mft#Yfbobmgqf`lnnfmgfgsklwldqbskzjmwfqfpwjmd%ow8pvs%dw8`lmwqlufqpzMfwkfqobmgpbowfqmbwjufnb{ofmdwk>!ptjwyfqobmgGfufolsnfmwfppfmwjbooz\t\tBowklvdk#?,wf{wbqfb=wkvmgfqajqgqfsqfpfmwfg%bns8mgbpk8psf`vobwjlm`lnnvmjwjfpofdjpobwjlmfof`wqlmj`p\t\n?gju#jg>!joovpwqbwfgfmdjmffqjmdwfqqjwlqjfpbvwklqjwjfpgjpwqjavwfg5!#kfjdkw>!pbmp.pfqje8`bsbaof#le#gjpbssfbqfgjmwfqb`wjufollhjmd#elqjw#tlvog#afBedkbmjpwbmtbp#`qfbwfgNbwk-eollq+pvqqlvmgjmd`bm#bopl#aflapfqubwjlmnbjmwfmbm`ffm`lvmwfqfg?k1#`obpp>!nlqf#qf`fmwjw#kbp#affmjmubpjlm#le*-dfwWjnf+*evmgbnfmwboGfpsjwf#wkf!=?gju#jg>!jmpsjqbwjlmf{bnjmbwjlmsqfsbqbwjlmf{sobmbwjlm?jmsvw#jg>!?,b=?,psbm=ufqpjlmp#lejmpwqvnfmwpafelqf#wkf##>#$kwws9,,Gfp`qjswjlmqfobwjufoz#-pvapwqjmd+fb`k#le#wkff{sfqjnfmwpjmeovfmwjbojmwfdqbwjlmnbmz#sflsofgvf#wl#wkf#`lnajmbwjlmgl#mlw#kbufNjggof#Fbpw?mlp`qjsw=?`lszqjdkw!#sfqkbsp#wkfjmpwjwvwjlmjm#Gf`fnafqbqqbmdfnfmwnlpw#ebnlvpsfqplmbojwz`qfbwjlm#leojnjwbwjlmpf{`ovpjufozplufqfjdmwz.`lmwfmw!=\t?wg#`obpp>!vmgfqdqlvmgsbqboofo#wlgl`wqjmf#lel``vsjfg#azwfqnjmloldzQfmbjppbm`fb#mvnafq#lepvsslqw#elqf{solqbwjlmqf`ldmjwjlmsqfgf`fpplq?jnd#pq`>!,?k2#`obpp>!svaoj`bwjlmnbz#bopl#afpsf`jbojyfg?,ejfogpfw=sqldqfppjufnjoojlmp#lepwbwfp#wkbwfmelq`fnfmwbqlvmg#wkf#lmf#bmlwkfq-sbqfmwMlgfbdqj`vowvqfBowfqmbwjufqfpfbq`kfqpwltbqgp#wkfNlpw#le#wkfnbmz#lwkfq#+fpsf`jbooz?wg#tjgwk>!8tjgwk9233&jmgfsfmgfmw?k0#`obpp>!#lm`kbmdf>!*-bgg@obpp+jmwfqb`wjlmLmf#le#wkf#gbvdkwfq#leb``fpplqjfpaqbm`kfp#le\t?gju#jg>!wkf#obqdfpwgf`obqbwjlmqfdvobwjlmpJmelqnbwjlmwqbmpobwjlmgl`vnfmwbqzjm#lqgfq#wl!=\t?kfbg=\t?!#kfjdkw>!2b`qlpp#wkf#lqjfmwbwjlm*8?,p`qjsw=jnsofnfmwfg`bm#af#pffmwkfqf#tbp#bgfnlmpwqbwf`lmwbjmfq!=`lmmf`wjlmpwkf#Aqjwjpktbp#tqjwwfm"jnslqwbmw8s{8#nbqdjm.elooltfg#azbajojwz#wl#`lnsoj`bwfggvqjmd#wkf#jnnjdqbwjlmbopl#`boofg?k7#`obpp>!gjpwjm`wjlmqfsob`fg#azdlufqmnfmwpol`bwjlm#lejm#Mlufnafqtkfwkfq#wkf?,s=\t?,gju=b`rvjpjwjlm`boofg#wkf#sfqpf`vwjlmgfpjdmbwjlmxelmw.pjyf9bssfbqfg#jmjmufpwjdbwff{sfqjfm`fgnlpw#ojhfoztjgfoz#vpfggjp`vppjlmpsqfpfm`f#le#+gl`vnfmw-f{wfmpjufozJw#kbp#affmjw#glfp#mlw`lmwqbqz#wljmkbajwbmwpjnsqlufnfmwp`klobqpkjs`lmpvnswjlmjmpwqv`wjlmelq#f{bnsoflmf#lq#nlqfs{8#sbggjmdwkf#`vqqfmwb#pfqjfp#lebqf#vpvboozqlof#jm#wkfsqfujlvpoz#gfqjubwjufpfujgfm`f#lef{sfqjfm`fp`lolqp`kfnfpwbwfg#wkbw`fqwjej`bwf?,b=?,gju=\t#pfof`wfg>!kjdk#p`klloqfpslmpf#wl`lnelqwbaofbglswjlm#lewkqff#zfbqpwkf#`lvmwqzjm#Efaqvbqzpl#wkbw#wkfsflsof#tkl#sqlujgfg#az?sbqbn#mbnfbeef`wfg#azjm#wfqnp#lebssljmwnfmwJPL.;;6:.2!tbp#alqm#jmkjpwlqj`bo#qfdbqgfg#bpnfbpvqfnfmwjp#abpfg#lm#bmg#lwkfq#9#evm`wjlm+pjdmjej`bmw`fofaqbwjlmwqbmpnjwwfg,ip,irvfqz-jp#hmltm#bpwkflqfwj`bo#wbajmgf{>!jw#`lvog#af?mlp`qjsw=\tkbujmd#affm\t?kfbg=\t?#%rvlw8Wkf#`lnsjobwjlmkf#kbg#affmsqlgv`fg#azskjolplskfq`lmpwqv`wfgjmwfmgfg#wlbnlmd#lwkfq`lnsbqfg#wlwl#pbz#wkbwFmdjmffqjmdb#gjeefqfmwqfefqqfg#wlgjeefqfm`fpafojfe#wkbwsklwldqbskpjgfmwjezjmdKjpwlqz#le#Qfsvaoj`#lemf`fppbqjozsqlabajojwzwf`kmj`boozofbujmd#wkfpsf`wb`vobqeqb`wjlm#lefof`wqj`jwzkfbg#le#wkfqfpwbvqbmwpsbqwmfqpkjsfnskbpjp#lmnlpw#qf`fmwpkbqf#tjwk#pbzjmd#wkbwejoofg#tjwkgfpjdmfg#wljw#jp#lewfm!=?,jeqbnf=bp#elooltp9nfqdfg#tjwkwkqlvdk#wkf`lnnfq`jbo#sljmwfg#lvwlsslqwvmjwzujft#le#wkfqfrvjqfnfmwgjujpjlm#lesqldqbnnjmdkf#qf`fjufgpfwJmwfqubo!=?,psbm=?,jm#Mft#Zlqhbggjwjlmbo#`lnsqfppjlm\t\t?gju#jg>!jm`lqslqbwf8?,p`qjsw=?bwwb`kFufmwaf`bnf#wkf#!#wbqdfw>!\\`bqqjfg#lvwPlnf#le#wkfp`jfm`f#bmgwkf#wjnf#le@lmwbjmfq!=nbjmwbjmjmd@kqjpwlskfqNv`k#le#wkftqjwjmdp#le!#kfjdkw>!1pjyf#le#wkfufqpjlm#le#nj{wvqf#le#afwtffm#wkfF{bnsofp#lefgv`bwjlmbo`lnsfwjwjuf#lmpvanjw>!gjqf`wlq#legjpwjm`wjuf,GWG#[KWNO#qfobwjmd#wlwfmgfm`z#wlsqlujm`f#letkj`k#tlvoggfpsjwf#wkfp`jfmwjej`#ofdjpobwvqf-jmmfqKWNO#boofdbwjlmpBdqj`vowvqftbp#vpfg#jmbssqlb`k#wljmwfoojdfmwzfbqp#obwfq/pbmp.pfqjegfwfqnjmjmdSfqelqnbm`fbssfbqbm`fp/#tkj`k#jp#elvmgbwjlmpbaaqfujbwfgkjdkfq#wkbmp#eqln#wkf#jmgjujgvbo#`lnslpfg#lepvsslpfg#wl`objnp#wkbwbwwqjavwjlmelmw.pjyf92fofnfmwp#leKjpwlqj`bo#kjp#aqlwkfqbw#wkf#wjnfbmmjufqpbqzdlufqmfg#azqfobwfg#wl#vowjnbwfoz#jmmlubwjlmpjw#jp#pwjoo`bm#lmoz#afgfejmjwjlmpwlDNWPwqjmdB#mvnafq#lejnd#`obpp>!Fufmwvbooz/tbp#`kbmdfgl``vqqfg#jmmfjdkalqjmdgjpwjmdvjpktkfm#kf#tbpjmwqlgv`jmdwfqqfpwqjboNbmz#le#wkfbqdvfp#wkbwbm#Bnfqj`bm`lmrvfpw#letjgfpsqfbg#tfqf#hjoofgp`qffm#bmg#Jm#lqgfq#wlf{sf`wfg#wlgfp`fmgbmwpbqf#ol`bwfgofdjpobwjufdfmfqbwjlmp#ab`hdqlvmgnlpw#sflsofzfbqp#bewfqwkfqf#jp#mlwkf#kjdkfpweqfrvfmwoz#wkfz#gl#mlwbqdvfg#wkbwpkltfg#wkbwsqfglnjmbmwwkfloldj`boaz#wkf#wjnf`lmpjgfqjmdpklqw.ojufg?,psbm=?,b=`bm#af#vpfgufqz#ojwwoflmf#le#wkf#kbg#boqfbgzjmwfqsqfwfg`lnnvmj`bwfefbwvqfp#ledlufqmnfmw/?,mlp`qjsw=fmwfqfg#wkf!#kfjdkw>!0Jmgfsfmgfmwslsvobwjlmpobqdf.p`bof-#Bowklvdk#vpfg#jm#wkfgfpwqv`wjlmslppjajojwzpwbqwjmd#jmwtl#lq#nlqff{sqfppjlmppvalqgjmbwfobqdfq#wkbmkjpwlqz#bmg?,lswjlm=\t@lmwjmfmwbofojnjmbwjmdtjoo#mlw#afsqb`wj`f#lejm#eqlmw#lepjwf#le#wkffmpvqf#wkbwwl#`qfbwf#bnjppjppjssjslwfmwjboozlvwpwbmgjmdafwwfq#wkbmtkbw#jp#mltpjwvbwfg#jmnfwb#mbnf>!WqbgjwjlmbopvddfpwjlmpWqbmpobwjlmwkf#elqn#lebwnlpskfqj`jgfloldj`bofmwfqsqjpfp`bo`vobwjmdfbpw#le#wkfqfnmbmwp#lesovdjmpsbdf,jmgf{-sks!Wkjp#jp#wkf#?b#kqfe>!,slsvobqjyfgjmuloufg#jmbqf#vpfg#wlbmg#pfufqbonbgf#az#wkfpffnp#wl#afojhfoz#wkbwSbofpwjmjbmmbnfg#bewfqjw#kbg#affmnlpw#`lnnlmwl#qfefq#wlavw#wkjp#jp`lmpf`vwjufwfnslqbqjozJm#dfmfqbo/`lmufmwjlmpwbhfp#sob`fpvagjujpjlmwfqqjwlqjbolsfqbwjlmbosfqnbmfmwoztbp#obqdfozlvwaqfbh#lejm#wkf#sbpwelooltjmd#b#{nomp9ld>!=?b#`obpp>!`obpp>!wf{w@lmufqpjlm#nbz#af#vpfgnbmveb`wvqfbewfq#afjmd`ofbqej{!=\trvfpwjlm#letbp#fof`wfgwl#af`lnf#baf`bvpf#le#plnf#sflsofjmpsjqfg#azpv``fppevo#b#wjnf#tkfmnlqf#`lnnlmbnlmdpw#wkfbm#leej`jbotjgwk9233&8wf`kmloldz/tbp#bglswfgwl#hffs#wkfpfwwofnfmwpojuf#ajqwkpjmgf{-kwno!@lmmf`wj`vwbppjdmfg#wl%bns8wjnfp8b``lvmw#elqbojdm>qjdkwwkf#`lnsbmzbotbzp#affmqfwvqmfg#wljmuloufnfmwAf`bvpf#wkfwkjp#sfqjlg!#mbnf>!r!#`lmejmfg#wlb#qfpvow#leubovf>!!#,=jp#b`wvboozFmujqlmnfmw\t?,kfbg=\t@lmufqpfoz/=\t?gju#jg>!3!#tjgwk>!2jp#sqlabaozkbuf#af`lnf`lmwqloojmdwkf#sqlaofn`jwjyfmp#leslojwj`jbmpqfb`kfg#wkfbp#fbqoz#bp9mlmf8#lufq?wbaof#`fooubojgjwz#legjqf`woz#wllmnlvpfgltmtkfqf#jw#jptkfm#jw#tbpnfnafqp#le#qfobwjlm#wlb``lnnlgbwfbolmd#tjwk#Jm#wkf#obwfwkf#Fmdojpkgfoj`jlvp!=wkjp#jp#mlwwkf#sqfpfmwje#wkfz#bqfbmg#ejmboozb#nbwwfq#le\t\n?,gju=\t\t?,p`qjsw=ebpwfq#wkbmnbilqjwz#lebewfq#tkj`k`lnsbqbwjufwl#nbjmwbjmjnsqluf#wkfbtbqgfg#wkffq!#`obpp>!eqbnfalqgfqqfpwlqbwjlmjm#wkf#pbnfbmbozpjp#lewkfjq#ejqpwGvqjmd#wkf#`lmwjmfmwbopfrvfm`f#leevm`wjlm+*xelmw.pjyf9#tlqh#lm#wkf?,p`qjsw=\t?afdjmp#tjwkibubp`qjsw9`lmpwjwvfmwtbp#elvmgfgfrvjojaqjvnbppvnf#wkbwjp#djufm#azmffgp#wl#af`llqgjmbwfpwkf#ubqjlvpbqf#sbqw#lelmoz#jm#wkfpf`wjlmp#lejp#b#`lnnlmwkflqjfp#legjp`lufqjfpbppl`jbwjlmfgdf#le#wkfpwqfmdwk#leslpjwjlm#jmsqfpfmw.gbzvmjufqpboozwl#elqn#wkfavw#jmpwfbg`lqslqbwjlmbwwb`kfg#wljp#`lnnlmozqfbplmp#elq#%rvlw8wkf#`bm#af#nbgftbp#baof#wltkj`k#nfbmpavw#gjg#mlwlmNlvpfLufqbp#slppjaoflsfqbwfg#az`lnjmd#eqlnwkf#sqjnbqzbggjwjlm#leelq#pfufqbowqbmpefqqfgb#sfqjlg#lebqf#baof#wlkltfufq/#jwpklvog#kbufnv`k#obqdfq\t\n?,p`qjsw=bglswfg#wkfsqlsfqwz#legjqf`wfg#azfeef`wjufoztbp#aqlvdkw`kjogqfm#leSqldqbnnjmdolmdfq#wkbmnbmvp`qjswptbq#bdbjmpwaz#nfbmp#lebmg#nlpw#lepjnjobq#wl#sqlsqjfwbqzlqjdjmbwjmdsqfpwjdjlvpdqbnnbwj`bof{sfqjfm`f-wl#nbhf#wkfJw#tbp#bopljp#elvmg#jm`lnsfwjwlqpjm#wkf#V-P-qfsob`f#wkfaqlvdkw#wkf`bo`vobwjlmeboo#le#wkfwkf#dfmfqbosqb`wj`boozjm#klmlq#leqfofbpfg#jmqfpjgfmwjbobmg#plnf#lehjmd#le#wkfqfb`wjlm#wl2pw#Fbqo#le`vowvqf#bmgsqjm`jsbooz?,wjwof=\t##wkfz#`bm#afab`h#wl#wkfplnf#le#kjpf{slpvqf#wlbqf#pjnjobqelqn#le#wkfbggEbulqjwf`jwjyfmpkjssbqw#jm#wkfsflsof#tjwkjm#sqb`wj`fwl#`lmwjmvf%bns8njmvp8bssqlufg#az#wkf#ejqpw#booltfg#wkfbmg#elq#wkfevm`wjlmjmdsobzjmd#wkfplovwjlm#wlkfjdkw>!3!#jm#kjp#allhnlqf#wkbm#belooltp#wkf`qfbwfg#wkfsqfpfm`f#jm%maps8?,wg=mbwjlmbojpwwkf#jgfb#leb#`kbqb`wfqtfqf#elq`fg#`obpp>!awmgbzp#le#wkfefbwvqfg#jmpkltjmd#wkfjmwfqfpw#jmjm#sob`f#lewvqm#le#wkfwkf#kfbg#leOlqg#le#wkfslojwj`boozkbp#jwp#ltmFgv`bwjlmbobssqlubo#leplnf#le#wkffb`k#lwkfq/afkbujlq#lebmg#af`bvpfbmg#bmlwkfqbssfbqfg#lmqf`lqgfg#jmaob`h%rvlw8nbz#jm`ovgfwkf#tlqog$p`bm#ofbg#wlqfefqp#wl#balqgfq>!3!#dlufqmnfmw#tjmmjmd#wkfqfpvowfg#jm#tkjof#wkf#Tbpkjmdwlm/wkf#pvaif`w`jwz#jm#wkf=?,gju=\t\n\nqfeof`w#wkfwl#`lnsofwfaf`bnf#nlqfqbgjlb`wjufqfif`wfg#aztjwklvw#bmzkjp#ebwkfq/tkj`k#`lvog`lsz#le#wkfwl#jmgj`bwfb#slojwj`bob``lvmwp#le`lmpwjwvwfptlqhfg#tjwkfq?,b=?,oj=le#kjp#ojefb``lnsbmjfg`ojfmwTjgwksqfufmw#wkfOfdjpobwjufgjeefqfmwozwldfwkfq#jmkbp#pfufqboelq#bmlwkfqwf{w#le#wkfelvmgfg#wkff#tjwk#wkf#jp#vpfg#elq`kbmdfg#wkfvpvbooz#wkfsob`f#tkfqftkfqfbp#wkf=#?b#kqfe>!!=?b#kqfe>!wkfnpfoufp/bowklvdk#kfwkbw#`bm#afwqbgjwjlmboqlof#le#wkfbp#b#qfpvowqfnluf@kjoggfpjdmfg#aztfpw#le#wkfPlnf#sflsofsqlgv`wjlm/pjgf#le#wkfmftpofwwfqpvpfg#az#wkfgltm#wl#wkfb``fswfg#azojuf#jm#wkfbwwfnswp#wllvwpjgf#wkfeqfrvfm`jfpKltfufq/#jmsqldqbnnfqpbw#ofbpw#jmbssql{jnbwfbowklvdk#jwtbp#sbqw#lebmg#ubqjlvpDlufqmlq#lewkf#bqwj`ofwvqmfg#jmwl=?b#kqfe>!,wkf#f`lmlnzjp#wkf#nlpwnlpw#tjgfoztlvog#obwfqbmg#sfqkbspqjpf#wl#wkfl``vqp#tkfmvmgfq#tkj`k`lmgjwjlmp-wkf#tfpwfqmwkflqz#wkbwjp#sqlgv`fgwkf#`jwz#lejm#tkj`k#kfpffm#jm#wkfwkf#`fmwqboavjogjmd#lenbmz#le#kjpbqfb#le#wkfjp#wkf#lmoznlpw#le#wkfnbmz#le#wkfwkf#TfpwfqmWkfqf#jp#mlf{wfmgfg#wlPwbwjpwj`bo`lopsbm>1#pklqw#pwlqzslppjaof#wlwlsloldj`bo`qjwj`bo#leqfslqwfg#wlb#@kqjpwjbmgf`jpjlm#wljp#frvbo#wlsqlaofnp#leWkjp#`bm#afnfq`kbmgjpfelq#nlpw#leml#fujgfm`ffgjwjlmp#lefofnfmwp#jm%rvlw8-#Wkf`ln,jnbdfp,tkj`k#nbhfpwkf#sql`fppqfnbjmp#wkfojwfqbwvqf/jp#b#nfnafqwkf#slsvobqwkf#bm`jfmwsqlaofnp#jmwjnf#le#wkfgfefbwfg#azalgz#le#wkfb#eft#zfbqpnv`k#le#wkfwkf#tlqh#le@bojelqmjb/pfqufg#bp#bdlufqmnfmw-`lm`fswp#lenlufnfmw#jm\n\n?gju#jg>!jw!#ubovf>!obmdvbdf#lebp#wkfz#bqfsqlgv`fg#jmjp#wkbw#wkff{sobjm#wkfgju=?,gju=\tKltfufq#wkfofbg#wl#wkf\n?b#kqfe>!,tbp#dqbmwfgsflsof#kbuf`lmwjmvbooztbp#pffm#bpbmg#qfobwfgwkf#qlof#lesqlslpfg#azle#wkf#afpwfb`k#lwkfq-@lmpwbmwjmfsflsof#eqlngjbof`wp#lewl#qfujpjlmtbp#qfmbnfgb#plvq`f#lewkf#jmjwjboobvm`kfg#jmsqlujgf#wkfwl#wkf#tfpwtkfqf#wkfqfbmg#pjnjobqafwtffm#wtljp#bopl#wkfFmdojpk#bmg`lmgjwjlmp/wkbw#jw#tbpfmwjwofg#wlwkfnpfoufp-rvbmwjwz#leqbmpsbqfm`zwkf#pbnf#bpwl#iljm#wkf`lvmwqz#bmgwkjp#jp#wkfWkjp#ofg#wlb#pwbwfnfmw`lmwqbpw#wlobpwJmgf{Lewkqlvdk#kjpjp#gfpjdmfgwkf#wfqn#jpjp#sqlujgfgsqlwf`w#wkfmd?,b=?,oj=Wkf#`vqqfmwwkf#pjwf#lepvapwbmwjbof{sfqjfm`f/jm#wkf#Tfpwwkfz#pklvogpolufm(ajmb`lnfmwbqjlpvmjufqpjgbg`lmgj`jlmfpb`wjujgbgfpf{sfqjfm`jbwf`mlold/Absqlgv``j/_msvmwvb`j/_mbsoj`b`j/_m`lmwqbpf/]b`bwfdlq/Abpqfdjpwqbqpfsqlefpjlmbowqbwbnjfmwlqfd/Apwqbwfpf`qfwbq/Absqjm`jsbofpsqlwf``j/_mjnslqwbmwfpjnslqwbm`jbslpjajojgbgjmwfqfpbmwf`qf`jnjfmwlmf`fpjgbgfppvp`qjajqpfbpl`jb`j/_mgjpslmjaofpfubovb`j/_mfpwvgjbmwfpqfpslmpbaofqfplov`j/_mdvbgbobibqbqfdjpwqbglplslqwvmjgbg`lnfq`jbofpelwldqbe/Abbvwlqjgbgfpjmdfmjfq/Abwfofujpj/_m`lnsfwfm`jblsfqb`jlmfpfpwbaof`jglpjnsofnfmwfb`wvbonfmwfmbufdb`j/_m`lmelqnjgbgojmf.kfjdkw9elmw.ebnjoz9!#9#!kwws9,,bssoj`bwjlmpojmh!#kqfe>!psf`jej`booz,,?"X@GBWBX\tLqdbmjybwjlmgjpwqjavwjlm3s{8#kfjdkw9qfobwjlmpkjsgfuj`f.tjgwk?gju#`obpp>!?obafo#elq>!qfdjpwqbwjlm?,mlp`qjsw=\t,jmgf{-kwno!tjmglt-lsfm+#"jnslqwbmw8bssoj`bwjlm,jmgfsfmgfm`f,,ttt-dlldoflqdbmjybwjlmbvwl`lnsofwfqfrvjqfnfmwp`lmpfqubwjuf?elqn#mbnf>!jmwfoof`wvbonbqdjm.ofew92;wk#`fmwvqzbm#jnslqwbmwjmpwjwvwjlmpbaaqfujbwjlm?jnd#`obpp>!lqdbmjpbwjlm`jujojybwjlm2:wk#`fmwvqzbq`kjwf`wvqfjm`lqslqbwfg13wk#`fmwvqz.`lmwbjmfq!=nlpw#mlwbaoz,=?,b=?,gju=mlwjej`bwjlm$vmgfejmfg$*Evqwkfqnlqf/afojfuf#wkbwjmmfqKWNO#>#sqjlq#wl#wkfgqbnbwj`boozqfefqqjmd#wlmfdlwjbwjlmpkfbgrvbqwfqpPlvwk#Beqj`bvmpv``fppevoSfmmpzoubmjbBp#b#qfpvow/?kwno#obmd>!%ow8,pvs%dw8gfbojmd#tjwkskjobgfoskjbkjpwlqj`booz*8?,p`qjsw=\tsbggjmd.wls9f{sfqjnfmwbodfwBwwqjavwfjmpwqv`wjlmpwf`kmloldjfpsbqw#le#wkf#>evm`wjlm+*xpvap`qjswjlmo-gwg!=\t?kwdfldqbskj`bo@lmpwjwvwjlm$/#evm`wjlm+pvsslqwfg#azbdqj`vowvqbo`lmpwqv`wjlmsvaoj`bwjlmpelmw.pjyf9#2b#ubqjfwz#le?gju#pwzof>!Fm`z`olsfgjbjeqbnf#pq`>!gfnlmpwqbwfgb``lnsojpkfgvmjufqpjwjfpGfnldqbskj`p*8?,p`qjsw=?gfgj`bwfg#wlhmltofgdf#lepbwjpeb`wjlmsbqwj`vobqoz?,gju=?,gju=Fmdojpk#+VP*bssfmg@kjog+wqbmpnjppjlmp-#Kltfufq/#jmwfoojdfm`f!#wbajmgf{>!eolbw9qjdkw8@lnnlmtfbowkqbmdjmd#eqlnjm#tkj`k#wkfbw#ofbpw#lmfqfsqlgv`wjlmfm`z`olsfgjb8elmw.pjyf92ivqjpgj`wjlmbw#wkbw#wjnf!=?b#`obpp>!Jm#bggjwjlm/gfp`qjswjlm(`lmufqpbwjlm`lmwb`w#tjwkjp#dfmfqboozq!#`lmwfmw>!qfsqfpfmwjmd%ow8nbwk%dw8sqfpfmwbwjlml``bpjlmbooz?jnd#tjgwk>!mbujdbwjlm!=`lnsfmpbwjlm`kbnsjlmpkjsnfgjb>!boo!#ujlobwjlm#leqfefqfm`f#wlqfwvqm#wqvf8Pwqj`w,,FM!#wqbmpb`wjlmpjmwfqufmwjlmufqjej`bwjlmJmelqnbwjlm#gjeej`vowjfp@kbnsjlmpkjs`bsbajojwjfp?"Xfmgje^..=~\t?,p`qjsw=\t@kqjpwjbmjwzelq#f{bnsof/Sqlefppjlmboqfpwqj`wjlmppvddfpw#wkbwtbp#qfofbpfg+pv`k#bp#wkfqfnluf@obpp+vmfnsolznfmwwkf#Bnfqj`bmpwqv`wvqf#le,jmgf{-kwno#svaojpkfg#jmpsbm#`obpp>!!=?b#kqfe>!,jmwqlgv`wjlmafolmdjmd#wl`objnfg#wkbw`lmpfrvfm`fp?nfwb#mbnf>!Dvjgf#wl#wkflufqtkfonjmdbdbjmpw#wkf#`lm`fmwqbwfg/\t-mlmwlv`k#lapfqubwjlmp?,b=\t?,gju=\te#+gl`vnfmw-alqgfq9#2s{#xelmw.pjyf92wqfbwnfmw#le3!#kfjdkw>!2nlgjej`bwjlmJmgfsfmgfm`fgjujgfg#jmwldqfbwfq#wkbmb`kjfufnfmwpfpwbaojpkjmdIbubP`qjsw!#mfufqwkfofpppjdmjej`bm`fAqlbg`bpwjmd=%maps8?,wg=`lmwbjmfq!=\tpv`k#bp#wkf#jmeovfm`f#leb#sbqwj`vobqpq`>$kwws9,,mbujdbwjlm!#kboe#le#wkf#pvapwbmwjbo#%maps8?,gju=bgubmwbdf#legjp`lufqz#leevmgbnfmwbo#nfwqlslojwbmwkf#lsslpjwf!#{no9obmd>!gfojafqbwfozbojdm>`fmwfqfulovwjlm#lesqfpfqubwjlmjnsqlufnfmwpafdjmmjmd#jmIfpvp#@kqjpwSvaoj`bwjlmpgjpbdqffnfmwwf{w.bojdm9q/#evm`wjlm+*pjnjobqjwjfpalgz=?,kwno=jp#`vqqfmwozboskbafwj`bojp#plnfwjnfpwzsf>!jnbdf,nbmz#le#wkf#eolt9kjggfm8bubjobaof#jmgfp`qjaf#wkff{jpwfm`f#leboo#lufq#wkfwkf#Jmwfqmfw\n?vo#`obpp>!jmpwboobwjlmmfjdkalqkllgbqnfg#elq`fpqfgv`jmd#wkf`lmwjmvfp#wlMlmfwkfofpp/wfnsfqbwvqfp\t\n\n?b#kqfe>!`olpf#wl#wkff{bnsofp#le#jp#balvw#wkf+pff#afolt*-!#jg>!pfbq`ksqlefppjlmbojp#bubjobaofwkf#leej`jbo\n\n?,p`qjsw=\t\t\n\n?gju#jg>!b``fofqbwjlmwkqlvdk#wkf#Kboo#le#Ebnfgfp`qjswjlmpwqbmpobwjlmpjmwfqefqfm`f#wzsf>$wf{w,qf`fmw#zfbqpjm#wkf#tlqogufqz#slsvobqxab`hdqlvmg9wqbgjwjlmbo#plnf#le#wkf#`lmmf`wfg#wlf{soljwbwjlmfnfqdfm`f#le`lmpwjwvwjlmB#Kjpwlqz#lepjdmjej`bmw#nbmveb`wvqfgf{sf`wbwjlmp=?mlp`qjsw=?`bm#af#elvmgaf`bvpf#wkf#kbp#mlw#affmmfjdkalvqjmdtjwklvw#wkf#bggfg#wl#wkf\n?oj#`obpp>!jmpwqvnfmwboPlujfw#Vmjlmb`hmltofgdfgtkj`k#`bm#afmbnf#elq#wkfbwwfmwjlm#wlbwwfnswp#wl#gfufolsnfmwpJm#eb`w/#wkf?oj#`obpp>!bjnsoj`bwjlmppvjwbaof#elqnv`k#le#wkf#`lolmjybwjlmsqfpjgfmwjbo`bm`foAvaaof#Jmelqnbwjlmnlpw#le#wkf#jp#gfp`qjafgqfpw#le#wkf#nlqf#lq#ofppjm#PfswfnafqJmwfoojdfm`fpq`>!kwws9,,s{8#kfjdkw9#bubjobaof#wlnbmveb`wvqfqkvnbm#qjdkwpojmh#kqfe>!,bubjobajojwzsqlslqwjlmbolvwpjgf#wkf#bpwqlmlnj`bokvnbm#afjmdpmbnf#le#wkf#bqf#elvmg#jmbqf#abpfg#lmpnboofq#wkbmb#sfqplm#tklf{sbmpjlm#lebqdvjmd#wkbwmlt#hmltm#bpJm#wkf#fbqozjmwfqnfgjbwfgfqjufg#eqlnP`bmgjmbujbm?,b=?,gju=\t`lmpjgfq#wkfbm#fpwjnbwfgwkf#Mbwjlmbo?gju#jg>!sbdqfpvowjmd#jm`lnnjppjlmfgbmboldlvp#wlbqf#qfrvjqfg,vo=\t?,gju=\ttbp#abpfg#lmbmg#af`bnf#b%maps8%maps8w!#ubovf>!!#tbp#`bswvqfgml#nlqf#wkbmqfpsf`wjufoz`lmwjmvf#wl#=\t?kfbg=\t?tfqf#`qfbwfgnlqf#dfmfqbojmelqnbwjlm#vpfg#elq#wkfjmgfsfmgfmw#wkf#Jnsfqjbo`lnslmfmw#lewl#wkf#mlqwkjm`ovgf#wkf#@lmpwqv`wjlmpjgf#le#wkf#tlvog#mlw#afelq#jmpwbm`fjmufmwjlm#lenlqf#`lnsof{`loof`wjufozab`hdqlvmg9#wf{w.bojdm9#jwp#lqjdjmbojmwl#b``lvmwwkjp#sql`fppbm#f{wfmpjufkltfufq/#wkfwkfz#bqf#mlwqfif`wfg#wkf`qjwj`jpn#legvqjmd#tkj`ksqlabaoz#wkfwkjp#bqwj`of+evm`wjlm+*xJw#pklvog#afbm#bdqffnfmwb``jgfmwboozgjeefqp#eqlnBq`kjwf`wvqfafwwfq#hmltmbqqbmdfnfmwpjmeovfm`f#lmbwwfmgfg#wkfjgfmwj`bo#wlplvwk#le#wkfsbpp#wkqlvdk{no!#wjwof>!tfjdkw9alog8`qfbwjmd#wkfgjpsobz9mlmfqfsob`fg#wkf?jnd#pq`>!,jkwwsp9,,ttt-Tlqog#Tbq#JJwfpwjnlmjbopelvmg#jm#wkfqfrvjqfg#wl#bmg#wkbw#wkfafwtffm#wkf#tbp#gfpjdmfg`lmpjpwp#le#`lmpjgfqbaozsvaojpkfg#azwkf#obmdvbdf@lmpfqubwjlm`lmpjpwfg#leqfefq#wl#wkfab`h#wl#wkf#`pp!#nfgjb>!Sflsof#eqln#bubjobaof#lmsqlufg#wl#afpvddfpwjlmp!tbp#hmltm#bpubqjfwjfp#leojhfoz#wl#af`lnsqjpfg#lepvsslqw#wkf#kbmgp#le#wkf`lvsofg#tjwk`lmmf`w#bmg#alqgfq9mlmf8sfqelqnbm`fpafelqf#afjmdobwfq#af`bnf`bo`vobwjlmplewfm#`boofgqfpjgfmwp#lenfbmjmd#wkbw=?oj#`obpp>!fujgfm`f#elqf{sobmbwjlmpfmujqlmnfmwp!=?,b=?,gju=tkj`k#booltpJmwqlgv`wjlmgfufolsfg#azb#tjgf#qbmdflm#afkboe#leubojdm>!wls!sqjm`jsof#lebw#wkf#wjnf/?,mlp`qjsw=pbjg#wl#kbufjm#wkf#ejqpwtkjof#lwkfqpkzslwkfwj`boskjolplskfqpsltfq#le#wkf`lmwbjmfg#jmsfqelqnfg#azjmbajojwz#wltfqf#tqjwwfmpsbm#pwzof>!jmsvw#mbnf>!wkf#rvfpwjlmjmwfmgfg#elqqfif`wjlm#lejnsojfp#wkbwjmufmwfg#wkfwkf#pwbmgbqgtbp#sqlabaozojmh#afwtffmsqlefpplq#lejmwfqb`wjlmp`kbmdjmd#wkfJmgjbm#L`fbm#`obpp>!obpwtlqhjmd#tjwk$kwws9,,ttt-zfbqp#afelqfWkjp#tbp#wkfqf`qfbwjlmbofmwfqjmd#wkfnfbpvqfnfmwpbm#f{wqfnfozubovf#le#wkfpwbqw#le#wkf\t?,p`qjsw=\t\tbm#feelqw#wljm`qfbpf#wkfwl#wkf#plvwkpsb`jmd>!3!=pveej`jfmwozwkf#Fvqlsfbm`lmufqwfg#wl`ofbqWjnflvwgjg#mlw#kbuf`lmpfrvfmwozelq#wkf#mf{wf{wfmpjlm#lef`lmlnj`#bmgbowklvdk#wkfbqf#sqlgv`fgbmg#tjwk#wkfjmpveej`jfmwdjufm#az#wkfpwbwjmd#wkbwf{sfmgjwvqfp?,psbm=?,b=\twklvdkw#wkbwlm#wkf#abpjp`foosbggjmd>jnbdf#le#wkfqfwvqmjmd#wljmelqnbwjlm/pfsbqbwfg#azbppbppjmbwfgp!#`lmwfmw>!bvwklqjwz#lemlqwktfpwfqm?,gju=\t?gju#!=?,gju=\t##`lmpvowbwjlm`lnnvmjwz#lewkf#mbwjlmbojw#pklvog#afsbqwj`jsbmwp#bojdm>!ofewwkf#dqfbwfpwpfof`wjlm#lepvsfqmbwvqbogfsfmgfmw#lmjp#nfmwjlmfgbooltjmd#wkftbp#jmufmwfgb``lnsbmzjmdkjp#sfqplmbobubjobaof#bwpwvgz#le#wkflm#wkf#lwkfqf{f`vwjlm#leKvnbm#Qjdkwpwfqnp#le#wkfbppl`jbwjlmpqfpfbq`k#bmgpv``ffgfg#azgfefbwfg#wkfbmg#eqln#wkfavw#wkfz#bqf`lnnbmgfq#lepwbwf#le#wkfzfbqp#le#bdfwkf#pwvgz#le?vo#`obpp>!psob`f#jm#wkftkfqf#kf#tbp?oj#`obpp>!ewkfqf#bqf#mltkj`k#af`bnfkf#svaojpkfgf{sqfppfg#jmwl#tkj`k#wkf`lnnjppjlmfqelmw.tfjdkw9wfqqjwlqz#lef{wfmpjlmp!=Qlnbm#Fnsjqffrvbo#wl#wkfJm#`lmwqbpw/kltfufq/#bmgjp#wzsj`boozbmg#kjp#tjef+bopl#`boofg=?vo#`obpp>!feef`wjufoz#fuloufg#jmwlpffn#wl#kbuftkj`k#jp#wkfwkfqf#tbp#mlbm#f{`foofmwboo#le#wkfpfgfp`qjafg#azJm#sqb`wj`f/aqlbg`bpwjmd`kbqdfg#tjwkqfeof`wfg#jmpvaif`wfg#wlnjojwbqz#bmgwl#wkf#sljmwf`lmlnj`boozpfwWbqdfwjmdbqf#b`wvboozuj`wlqz#lufq+*8?,p`qjsw=`lmwjmvlvpozqfrvjqfg#elqfulovwjlmbqzbm#feef`wjufmlqwk#le#wkf/#tkj`k#tbp#eqlmw#le#wkflq#lwkfqtjpfplnf#elqn#lekbg#mlw#affmdfmfqbwfg#azjmelqnbwjlm-sfqnjwwfg#wljm`ovgfp#wkfgfufolsnfmw/fmwfqfg#jmwlwkf#sqfujlvp`lmpjpwfmwozbqf#hmltm#bpwkf#ejfog#lewkjp#wzsf#ledjufm#wl#wkfwkf#wjwof#le`lmwbjmp#wkfjmpwbm`fp#lejm#wkf#mlqwkgvf#wl#wkfjqbqf#gfpjdmfg`lqslqbwjlmptbp#wkbw#wkflmf#le#wkfpfnlqf#slsvobqpv``ffgfg#jmpvsslqw#eqlnjm#gjeefqfmwglnjmbwfg#azgfpjdmfg#elqltmfqpkjs#lebmg#slppjaozpwbmgbqgjyfgqfpslmpfWf{wtbp#jmwfmgfgqf`fjufg#wkfbppvnfg#wkbwbqfbp#le#wkfsqjnbqjoz#jmwkf#abpjp#lejm#wkf#pfmpfb``lvmwp#elqgfpwqlzfg#azbw#ofbpw#wtltbp#gf`obqfg`lvog#mlw#afPf`qfwbqz#lebssfbq#wl#afnbqdjm.wls92,]_p(_p(\',df*xwkqlt#f~8wkf#pwbqw#lewtl#pfsbqbwfobmdvbdf#bmgtkl#kbg#affmlsfqbwjlm#legfbwk#le#wkfqfbo#mvnafqp\n?ojmh#qfo>!sqlujgfg#wkfwkf#pwlqz#le`lnsfwjwjlmpfmdojpk#+VH*fmdojpk#+VP*#evm`wjlm+*-isd!#tjgwk>!`lmejdvqbwjlm-smd!#tjgwk>!?algz#`obpp>!Nbwk-qbmgln+*`lmwfnslqbqz#Vmjwfg#Pwbwfp`jq`vnpwbm`fp-bssfmg@kjog+lqdbmjybwjlmp?psbm#`obpp>!!=?jnd#pq`>!,gjpwjmdvjpkfgwklvpbmgp#le#`lnnvmj`bwjlm`ofbq!=?,gju=jmufpwjdbwjlmebuj`lm-j`l!#nbqdjm.qjdkw9abpfg#lm#wkf#Nbppb`kvpfwwpwbaof#alqgfq>jmwfqmbwjlmbobopl#hmltm#bpsqlmvm`jbwjlmab`hdqlvmg9 esbggjmd.ofew9Elq#f{bnsof/#njp`foobmflvp%ow8,nbwk%dw8spz`kloldj`bojm#sbqwj`vobqfbq`k!#wzsf>!elqn#nfwklg>!bp#lsslpfg#wlPvsqfnf#@lvqwl``bpjlmbooz#Bggjwjlmbooz/Mlqwk#Bnfqj`bs{8ab`hdqlvmglsslqwvmjwjfpFmwfqwbjmnfmw-wlOltfq@bpf+nbmveb`wvqjmdsqlefppjlmbo#`lnajmfg#tjwkElq#jmpwbm`f/`lmpjpwjmd#le!#nb{ofmdwk>!qfwvqm#ebopf8`lmp`jlvpmfppNfgjwfqqbmfbmf{wqblqgjmbqzbppbppjmbwjlmpvapfrvfmwoz#avwwlm#wzsf>!wkf#mvnafq#lewkf#lqjdjmbo#`lnsqfkfmpjufqfefqp#wl#wkf?,vo=\t?,gju=\tskjolplskj`bool`bwjlm-kqfetbp#svaojpkfgPbm#Eqbm`jp`l+evm`wjlm+*x\t?gju#jg>!nbjmplskjpwj`bwfgnbwkfnbwj`bo#,kfbg=\t?algzpvddfpwp#wkbwgl`vnfmwbwjlm`lm`fmwqbwjlmqfobwjlmpkjspnbz#kbuf#affm+elq#f{bnsof/Wkjp#bqwj`of#jm#plnf#`bpfpsbqwp#le#wkf#gfejmjwjlm#leDqfbw#Aqjwbjm#`foosbggjmd>frvjubofmw#wlsob`fklogfq>!8#elmw.pjyf9#ivpwjej`bwjlmafojfufg#wkbwpveefqfg#eqlnbwwfnswfg#wl#ofbgfq#le#wkf`qjsw!#pq`>!,+evm`wjlm+*#xbqf#bubjobaof\t\n?ojmh#qfo>!#pq`>$kwws9,,jmwfqfpwfg#jm`lmufmwjlmbo#!#bow>!!#,=?,bqf#dfmfqboozkbp#bopl#affmnlpw#slsvobq#`lqqfpslmgjmd`qfgjwfg#tjwkwzof>!alqgfq9?,b=?,psbm=?,-dje!#tjgwk>!?jeqbnf#pq`>!wbaof#`obpp>!jmojmf.aol`h8b``lqgjmd#wl#wldfwkfq#tjwkbssql{jnbwfozsbqojbnfmwbqznlqf#bmg#nlqfgjpsobz9mlmf8wqbgjwjlmboozsqfglnjmbmwoz%maps8%maps8%maps8?,psbm=#`foopsb`jmd>?jmsvw#mbnf>!lq!#`lmwfmw>!`lmwqlufqpjbosqlsfqwz>!ld9,{.pkl`htbuf.gfnlmpwqbwjlmpvqqlvmgfg#azMfufqwkfofpp/tbp#wkf#ejqpw`lmpjgfqbaof#Bowklvdk#wkf#`loobalqbwjlmpklvog#mlw#afsqlslqwjlm#le?psbm#pwzof>!hmltm#bp#wkf#pklqwoz#bewfqelq#jmpwbm`f/gfp`qjafg#bp#,kfbg=\t?algz#pwbqwjmd#tjwkjm`qfbpjmdoz#wkf#eb`w#wkbwgjp`vppjlm#lenjggof#le#wkfbm#jmgjujgvbogjeej`vow#wl#sljmw#le#ujftklnlpf{vbojwzb``fswbm`f#le?,psbm=?,gju=nbmveb`wvqfqplqjdjm#le#wkf`lnnlmoz#vpfgjnslqwbm`f#legfmlnjmbwjlmpab`hdqlvmg9# ofmdwk#le#wkfgfwfqnjmbwjlmb#pjdmjej`bmw!#alqgfq>!3!=qfulovwjlmbqzsqjm`jsofp#lejp#`lmpjgfqfgtbp#gfufolsfgJmgl.Fvqlsfbmuvomfqbaof#wlsqlslmfmwp#lebqf#plnfwjnfp`olpfq#wl#wkfMft#Zlqh#@jwz#mbnf>!pfbq`kbwwqjavwfg#wl`lvqpf#le#wkfnbwkfnbwj`jbmaz#wkf#fmg#lebw#wkf#fmg#le!#alqgfq>!3!#wf`kmloldj`bo-qfnluf@obpp+aqbm`k#le#wkffujgfm`f#wkbw"Xfmgje^..=\tJmpwjwvwf#le#jmwl#b#pjmdofqfpsf`wjufoz-bmg#wkfqfelqfsqlsfqwjfp#lejp#ol`bwfg#jmplnf#le#tkj`kWkfqf#jp#bopl`lmwjmvfg#wl#bssfbqbm`f#le#%bns8mgbpk8#gfp`qjafp#wkf`lmpjgfqbwjlmbvwklq#le#wkfjmgfsfmgfmwozfrvjssfg#tjwkglfp#mlw#kbuf?,b=?b#kqfe>!`lmevpfg#tjwk?ojmh#kqfe>!,bw#wkf#bdf#lebssfbq#jm#wkfWkfpf#jm`ovgfqfdbqgofpp#le`lvog#af#vpfg#pwzof>%rvlw8pfufqbo#wjnfpqfsqfpfmw#wkfalgz=\t?,kwno=wklvdkw#wl#afslsvobwjlm#leslppjajojwjfpsfq`fmwbdf#leb``fpp#wl#wkfbm#bwwfnsw#wlsqlgv`wjlm#leirvfqz,irvfqzwtl#gjeefqfmwafolmd#wl#wkffpwbaojpknfmwqfsob`jmd#wkfgfp`qjswjlm!#gfwfqnjmf#wkfbubjobaof#elqB``lqgjmd#wl#tjgf#qbmdf#le\n?gju#`obpp>!nlqf#`lnnlmozlqdbmjpbwjlmpevm`wjlmbojwztbp#`lnsofwfg#%bns8ngbpk8#sbqwj`jsbwjlmwkf#`kbqb`wfqbm#bggjwjlmbobssfbqp#wl#afeb`w#wkbw#wkfbm#f{bnsof#lepjdmjej`bmwozlmnlvpflufq>!af`bvpf#wkfz#bpzm`#>#wqvf8sqlaofnp#tjwkpffnp#wl#kbufwkf#qfpvow#le#pq`>!kwws9,,ebnjojbq#tjwkslppfppjlm#leevm`wjlm#+*#xwllh#sob`f#jmbmg#plnfwjnfppvapwbmwjbooz?psbm=?,psbm=jp#lewfm#vpfgjm#bm#bwwfnswdqfbw#gfbo#leFmujqlmnfmwbopv``fppevooz#ujqwvbooz#boo13wk#`fmwvqz/sqlefppjlmbopmf`fppbqz#wl#gfwfqnjmfg#az`lnsbwjajojwzaf`bvpf#jw#jpGj`wjlmbqz#lenlgjej`bwjlmpWkf#elooltjmdnbz#qfefq#wl9@lmpfrvfmwoz/Jmwfqmbwjlmbobowklvdk#plnfwkbw#tlvog#aftlqog$p#ejqpw`obppjejfg#bpalwwln#le#wkf+sbqwj`vobqozbojdm>!ofew!#nlpw#`lnnlmozabpjp#elq#wkfelvmgbwjlm#le`lmwqjavwjlmpslsvobqjwz#le`fmwfq#le#wkfwl#qfgv`f#wkfivqjpgj`wjlmpbssql{jnbwjlm#lmnlvpflvw>!Mft#Wfpwbnfmw`loof`wjlm#le?,psbm=?,b=?,jm#wkf#Vmjwfgejon#gjqf`wlq.pwqj`w-gwg!=kbp#affm#vpfgqfwvqm#wl#wkfbowklvdk#wkjp`kbmdf#jm#wkfpfufqbo#lwkfqavw#wkfqf#bqfvmsqf`fgfmwfgjp#pjnjobq#wlfpsf`jbooz#jmtfjdkw9#alog8jp#`boofg#wkf`lnsvwbwjlmbojmgj`bwf#wkbwqfpwqj`wfg#wl\n?nfwb#mbnf>!bqf#wzsj`booz`lmeoj`w#tjwkKltfufq/#wkf#Bm#f{bnsof#le`lnsbqfg#tjwkrvbmwjwjfp#leqbwkfq#wkbm#b`lmpwfoobwjlmmf`fppbqz#elqqfslqwfg#wkbwpsf`jej`bwjlmslojwj`bo#bmg%maps8%maps8?qfefqfm`fp#wlwkf#pbnf#zfbqDlufqmnfmw#ledfmfqbwjlm#lekbuf#mlw#affmpfufqbo#zfbqp`lnnjwnfmw#wl\n\n?vo#`obpp>!ujpvbojybwjlm2:wk#`fmwvqz/sqb`wjwjlmfqpwkbw#kf#tlvogbmg#`lmwjmvfgl``vsbwjlm#lejp#gfejmfg#bp`fmwqf#le#wkfwkf#bnlvmw#le=?gju#pwzof>!frvjubofmw#legjeefqfmwjbwfaqlvdkw#balvwnbqdjm.ofew9#bvwlnbwj`boozwklvdkw#le#bpPlnf#le#wkfpf\t?gju#`obpp>!jmsvw#`obpp>!qfsob`fg#tjwkjp#lmf#le#wkffgv`bwjlm#bmgjmeovfm`fg#azqfsvwbwjlm#bp\t?nfwb#mbnf>!b``lnnlgbwjlm?,gju=\t?,gju=obqdf#sbqw#leJmpwjwvwf#elqwkf#pl.`boofg#bdbjmpw#wkf#Jm#wkjp#`bpf/tbp#bssljmwfg`objnfg#wl#afKltfufq/#wkjpGfsbqwnfmw#lewkf#qfnbjmjmdfeef`w#lm#wkfsbqwj`vobqoz#gfbo#tjwk#wkf\t?gju#pwzof>!bonlpw#botbzpbqf#`vqqfmwozf{sqfppjlm#leskjolplskz#leelq#nlqf#wkbm`jujojybwjlmplm#wkf#jpobmgpfof`wfgJmgf{`bm#qfpvow#jm!#ubovf>!!#,=wkf#pwqv`wvqf#,=?,b=?,gju=Nbmz#le#wkfpf`bvpfg#az#wkfle#wkf#Vmjwfgpsbm#`obpp>!n`bm#af#wqb`fgjp#qfobwfg#wlaf`bnf#lmf#lejp#eqfrvfmwozojujmd#jm#wkfwkflqfwj`boozElooltjmd#wkfQfulovwjlmbqzdlufqmnfmw#jmjp#gfwfqnjmfgwkf#slojwj`bojmwqlgv`fg#jmpveej`jfmw#wlgfp`qjswjlm!=pklqw#pwlqjfppfsbqbwjlm#lebp#wl#tkfwkfqhmltm#elq#jwptbp#jmjwjboozgjpsobz9aol`hjp#bm#f{bnsofwkf#sqjm`jsbo`lmpjpwp#le#bqf`ldmjyfg#bp,algz=?,kwno=b#pvapwbmwjboqf`lmpwqv`wfgkfbg#le#pwbwfqfpjpwbm`f#wlvmgfqdqbgvbwfWkfqf#bqf#wtldqbujwbwjlmbobqf#gfp`qjafgjmwfmwjlmboozpfqufg#bp#wkf`obpp>!kfbgfqlsslpjwjlm#wlevmgbnfmwboozglnjmbwfg#wkfbmg#wkf#lwkfqboojbm`f#tjwktbp#elq`fg#wlqfpsf`wjufoz/bmg#slojwj`bojm#pvsslqw#lesflsof#jm#wkf13wk#`fmwvqz-bmg#svaojpkfgolbg@kbqwafbwwl#vmgfqpwbmgnfnafq#pwbwfpfmujqlmnfmwboejqpw#kboe#le`lvmwqjfp#bmgbq`kjwf`wvqboaf#`lmpjgfqfg`kbqb`wfqjyfg`ofbqJmwfqubobvwklqjwbwjufEfgfqbwjlm#letbp#pv``ffgfgbmg#wkfqf#bqfb#`lmpfrvfm`fwkf#Sqfpjgfmwbopl#jm`ovgfgeqff#plewtbqfpv``fppjlm#legfufolsfg#wkftbp#gfpwqlzfgbtbz#eqln#wkf8\t?,p`qjsw=\t?bowklvdk#wkfzelooltfg#az#bnlqf#sltfqevoqfpvowfg#jm#bVmjufqpjwz#leKltfufq/#nbmzwkf#sqfpjgfmwKltfufq/#plnfjp#wklvdkw#wlvmwjo#wkf#fmgtbp#bmmlvm`fgbqf#jnslqwbmwbopl#jm`ovgfp=?jmsvw#wzsf>wkf#`fmwfq#le#GL#MLW#BOWFQvpfg#wl#qfefqwkfnfp,wkbw#kbg#affmwkf#abpjp#elqkbp#gfufolsfgjm#wkf#pvnnfq`lnsbqbwjufozgfp`qjafg#wkfpv`k#bp#wklpfwkf#qfpvowjmdjp#jnslppjaofubqjlvp#lwkfqPlvwk#Beqj`bmkbuf#wkf#pbnffeef`wjufmfppjm#tkj`k#`bpf8#wf{w.bojdm9pwqv`wvqf#bmg8#ab`hdqlvmg9qfdbqgjmd#wkfpvsslqwfg#wkfjp#bopl#hmltmpwzof>!nbqdjmjm`ovgjmd#wkfabkbpb#Nfobzvmlqph#alhn/Iomlqph#mzmlqphpolufm)M(ajmbjmwfqmb`jlmbo`bojej`b`j/_m`lnvmj`b`j/_m`lmpwqv``j/_m!=?gju#`obpp>!gjpbnajdvbwjlmGlnbjmMbnf$/#$bgnjmjpwqbwjlmpjnvowbmflvpozwqbmpslqwbwjlmJmwfqmbwjlmbo#nbqdjm.alwwln9qfpslmpjajojwz?"Xfmgje^..=\t?,=?nfwb#mbnf>!jnsofnfmwbwjlmjmeqbpwqv`wvqfqfsqfpfmwbwjlmalqgfq.alwwln9?,kfbg=\t?algz=>kwws&0B&1E&1E?elqn#nfwklg>!nfwklg>!slpw!#,ebuj`lm-j`l!#~*8\t?,p`qjsw=\t-pfwBwwqjavwf+Bgnjmjpwqbwjlm>#mft#Bqqbz+*8?"Xfmgje^..=\tgjpsobz9aol`h8Vmelqwvmbwfoz/!=%maps8?,gju=,ebuj`lm-j`l!=>$pwzofpkffw$#jgfmwjej`bwjlm/#elq#f{bnsof/?oj=?b#kqfe>!,bm#bowfqmbwjufbp#b#qfpvow#lesw!=?,p`qjsw=\twzsf>!pvanjw!#\t+evm`wjlm+*#xqf`lnnfmgbwjlmelqn#b`wjlm>!,wqbmpelqnbwjlmqf`lmpwqv`wjlm-pwzof-gjpsobz#B``lqgjmd#wl#kjggfm!#mbnf>!bolmd#tjwk#wkfgl`vnfmw-algz-bssql{jnbwfoz#@lnnvmj`bwjlmpslpw!#b`wjlm>!nfbmjmd#%rvlw8..?"Xfmgje^..=Sqjnf#Njmjpwfq`kbqb`wfqjpwj`?,b=#?b#`obpp>wkf#kjpwlqz#le#lmnlvpflufq>!wkf#dlufqmnfmwkqfe>!kwwsp9,,tbp#lqjdjmbooztbp#jmwqlgv`fg`obppjej`bwjlmqfsqfpfmwbwjufbqf#`lmpjgfqfg?"Xfmgje^..=\t\tgfsfmgp#lm#wkfVmjufqpjwz#le#jm#`lmwqbpw#wl#sob`fklogfq>!jm#wkf#`bpf#lejmwfqmbwjlmbo#`lmpwjwvwjlmbopwzof>!alqgfq.9#evm`wjlm+*#xAf`bvpf#le#wkf.pwqj`w-gwg!=\t?wbaof#`obpp>!b``lnsbmjfg#azb``lvmw#le#wkf?p`qjsw#pq`>!,mbwvqf#le#wkf#wkf#sflsof#jm#jm#bggjwjlm#wlp*8#ip-jg#>#jg!#tjgwk>!233&!qfdbqgjmd#wkf#Qlnbm#@bwkloj`bm#jmgfsfmgfmwelooltjmd#wkf#-dje!#tjgwk>!2wkf#elooltjmd#gjp`qjnjmbwjlmbq`kbfloldj`bosqjnf#njmjpwfq-ip!=?,p`qjsw=`lnajmbwjlm#le#nbqdjmtjgwk>!`qfbwfFofnfmw+t-bwwb`kFufmw+?,b=?,wg=?,wq=pq`>!kwwsp9,,bJm#sbqwj`vobq/#bojdm>!ofew!#@yf`k#Qfsvaoj`Vmjwfg#Hjmdgln`lqqfpslmgfm`f`lm`ovgfg#wkbw-kwno!#wjwof>!+evm`wjlm#+*#x`lnfp#eqln#wkfbssoj`bwjlm#le?psbm#`obpp>!pafojfufg#wl#affnfmw+$p`qjsw$?,b=\t?,oj=\t?ojufqz#gjeefqfmw=?psbm#`obpp>!lswjlm#ubovf>!+bopl#hmltm#bp\n?oj=?b#kqfe>!=?jmsvw#mbnf>!pfsbqbwfg#eqlnqfefqqfg#wl#bp#ubojdm>!wls!=elvmgfq#le#wkfbwwfnswjmd#wl#`bqalm#gjl{jgf\t\t?gju#`obpp>!`obpp>!pfbq`k.,algz=\t?,kwno=lsslqwvmjwz#wl`lnnvmj`bwjlmp?,kfbg=\t?algz#pwzof>!tjgwk9Wj\rVSmd#Uj\rWkw`kbmdfp#jm#wkfalqgfq.`lolq9 3!#alqgfq>!3!#?,psbm=?,gju=?tbp#gjp`lufqfg!#wzsf>!wf{w!#*8\t?,p`qjsw=\t\tGfsbqwnfmw#le#f``ofpjbpwj`bowkfqf#kbp#affmqfpvowjmd#eqln?,algz=?,kwno=kbp#mfufq#affmwkf#ejqpw#wjnfjm#qfpslmpf#wlbvwlnbwj`booz#?,gju=\t\t?gju#jtbp#`lmpjgfqfgsfq`fmw#le#wkf!#,=?,b=?,gju=`loof`wjlm#le#gfp`fmgfg#eqlnpf`wjlm#le#wkfb``fsw.`kbqpfwwl#af#`lmevpfgnfnafq#le#wkf#sbggjmd.qjdkw9wqbmpobwjlm#lejmwfqsqfwbwjlm#kqfe>$kwws9,,tkfwkfq#lq#mlwWkfqf#bqf#boplwkfqf#bqf#nbmzb#pnboo#mvnafqlwkfq#sbqwp#lejnslppjaof#wl##`obpp>!avwwlmol`bwfg#jm#wkf-#Kltfufq/#wkfbmg#fufmwvboozBw#wkf#fmg#le#af`bvpf#le#jwpqfsqfpfmwp#wkf?elqn#b`wjlm>!#nfwklg>!slpw!jw#jp#slppjaofnlqf#ojhfoz#wlbm#jm`qfbpf#jmkbuf#bopl#affm`lqqfpslmgp#wlbmmlvm`fg#wkbwbojdm>!qjdkw!=nbmz#`lvmwqjfpelq#nbmz#zfbqpfbqojfpw#hmltmaf`bvpf#jw#tbpsw!=?,p`qjsw=#ubojdm>!wls!#jmkbajwbmwp#leelooltjmd#zfbq\t?gju#`obpp>!njoojlm#sflsof`lmwqlufqpjbo#`lm`fqmjmd#wkfbqdvf#wkbw#wkfdlufqmnfmw#bmgb#qfefqfm`f#wlwqbmpefqqfg#wlgfp`qjajmd#wkf#pwzof>!`lolq9bowklvdk#wkfqfafpw#hmltm#elqpvanjw!#mbnf>!nvowjsoj`bwjlmnlqf#wkbm#lmf#qf`ldmjwjlm#le@lvm`jo#le#wkffgjwjlm#le#wkf##?nfwb#mbnf>!Fmwfqwbjmnfmw#btbz#eqln#wkf#8nbqdjm.qjdkw9bw#wkf#wjnf#lejmufpwjdbwjlmp`lmmf`wfg#tjwkbmg#nbmz#lwkfqbowklvdk#jw#jpafdjmmjmd#tjwk#?psbm#`obpp>!gfp`fmgbmwp#le?psbm#`obpp>!j#bojdm>!qjdkw!?,kfbg=\t?algz#bpsf`wp#le#wkfkbp#pjm`f#affmFvqlsfbm#Vmjlmqfnjmjp`fmw#lenlqf#gjeej`vowUj`f#Sqfpjgfmw`lnslpjwjlm#lesbppfg#wkqlvdknlqf#jnslqwbmwelmw.pjyf922s{f{sobmbwjlm#lewkf#`lm`fsw#letqjwwfm#jm#wkf\n?psbm#`obpp>!jp#lmf#le#wkf#qfpfnaobm`f#wllm#wkf#dqlvmgptkj`k#`lmwbjmpjm`ovgjmd#wkf#gfejmfg#az#wkfsvaoj`bwjlm#lenfbmp#wkbw#wkflvwpjgf#le#wkfpvsslqw#le#wkf?jmsvw#`obpp>!?psbm#`obpp>!w+Nbwk-qbmgln+*nlpw#sqlnjmfmwgfp`qjswjlm#le@lmpwbmwjmlsoftfqf#svaojpkfg?gju#`obpp>!pfbssfbqp#jm#wkf2!#kfjdkw>!2!#nlpw#jnslqwbmwtkj`k#jm`ovgfptkj`k#kbg#affmgfpwqv`wjlm#lewkf#slsvobwjlm\t\n?gju#`obpp>!slppjajojwz#leplnfwjnfp#vpfgbssfbq#wl#kbufpv``fpp#le#wkfjmwfmgfg#wl#afsqfpfmw#jm#wkfpwzof>!`ofbq9a\t?,p`qjsw=\t?tbp#elvmgfg#jmjmwfqujft#tjwk\\jg!#`lmwfmw>!`bsjwbo#le#wkf\t?ojmh#qfo>!pqfofbpf#le#wkfsljmw#lvw#wkbw{NOKwwsQfrvfpwbmg#pvapfrvfmwpf`lmg#obqdfpwufqz#jnslqwbmwpsf`jej`bwjlmppvqeb`f#le#wkfbssojfg#wl#wkfelqfjdm#sloj`z\\pfwGlnbjmMbnffpwbaojpkfg#jmjp#afojfufg#wlJm#bggjwjlm#wlnfbmjmd#le#wkfjp#mbnfg#bewfqwl#sqlwf`w#wkfjp#qfsqfpfmwfgGf`obqbwjlm#lenlqf#feej`jfmw@obppjej`bwjlmlwkfq#elqnp#lekf#qfwvqmfg#wl?psbm#`obpp>!`sfqelqnbm`f#le+evm`wjlm+*#xje#bmg#lmoz#jeqfdjlmp#le#wkfofbgjmd#wl#wkfqfobwjlmp#tjwkVmjwfg#Mbwjlmppwzof>!kfjdkw9lwkfq#wkbm#wkfzsf!#`lmwfmw>!Bppl`jbwjlm#le\t?,kfbg=\t?algzol`bwfg#lm#wkfjp#qfefqqfg#wl+jm`ovgjmd#wkf`lm`fmwqbwjlmpwkf#jmgjujgvbobnlmd#wkf#nlpwwkbm#bmz#lwkfq,=\t?ojmh#qfo>!#qfwvqm#ebopf8wkf#svqslpf#lewkf#bajojwz#wl8`lolq9 eee~\t-\t?psbm#`obpp>!wkf#pvaif`w#legfejmjwjlmp#le=\t?ojmh#qfo>!`objn#wkbw#wkfkbuf#gfufolsfg?wbaof#tjgwk>!`fofaqbwjlm#leElooltjmd#wkf#wl#gjpwjmdvjpk?psbm#`obpp>!awbhfp#sob`f#jmvmgfq#wkf#mbnfmlwfg#wkbw#wkf=?"Xfmgje^..=\tpwzof>!nbqdjm.jmpwfbg#le#wkfjmwqlgv`fg#wkfwkf#sql`fpp#lejm`qfbpjmd#wkfgjeefqfm`fp#jmfpwjnbwfg#wkbwfpsf`jbooz#wkf,gju=?gju#jg>!tbp#fufmwvboozwkqlvdklvw#kjpwkf#gjeefqfm`fplnfwkjmd#wkbwpsbm=?,psbm=?,pjdmjej`bmwoz#=?,p`qjsw=\t\tfmujqlmnfmwbo#wl#sqfufmw#wkfkbuf#affm#vpfgfpsf`jbooz#elqvmgfqpwbmg#wkfjp#fppfmwjbooztfqf#wkf#ejqpwjp#wkf#obqdfpwkbuf#affm#nbgf!#pq`>!kwws9,,jmwfqsqfwfg#bppf`lmg#kboe#le`qloojmd>!ml!#jp#`lnslpfg#leJJ/#Kloz#Qlnbmjp#f{sf`wfg#wlkbuf#wkfjq#ltmgfejmfg#bp#wkfwqbgjwjlmbooz#kbuf#gjeefqfmwbqf#lewfm#vpfgwl#fmpvqf#wkbwbdqffnfmw#tjwk`lmwbjmjmd#wkfbqf#eqfrvfmwozjmelqnbwjlm#lmf{bnsof#jp#wkfqfpvowjmd#jm#b?,b=?,oj=?,vo=#`obpp>!ellwfqbmg#fpsf`jboozwzsf>!avwwlm!#?,psbm=?,psbm=tkj`k#jm`ovgfg=\t?nfwb#mbnf>!`lmpjgfqfg#wkf`bqqjfg#lvw#azKltfufq/#jw#jpaf`bnf#sbqw#lejm#qfobwjlm#wlslsvobq#jm#wkfwkf#`bsjwbo#letbp#leej`jbooztkj`k#kbp#affmwkf#Kjpwlqz#lebowfqmbwjuf#wlgjeefqfmw#eqlnwl#pvsslqw#wkfpvddfpwfg#wkbwjm#wkf#sql`fpp##?gju#`obpp>!wkf#elvmgbwjlmaf`bvpf#le#kjp`lm`fqmfg#tjwkwkf#vmjufqpjwzlsslpfg#wl#wkfwkf#`lmwf{w#le?psbm#`obpp>!swf{w!#mbnf>!r!\n\n?gju#`obpp>!wkf#p`jfmwjej`qfsqfpfmwfg#aznbwkfnbwj`jbmpfof`wfg#az#wkfwkbw#kbuf#affm=?gju#`obpp>!`gju#jg>!kfbgfqjm#sbqwj`vobq/`lmufqwfg#jmwl*8\t?,p`qjsw=\t?skjolplskj`bo#pqsphlkqubwphjwj\rVSmd#Uj\rWkw!kwws9,,!=?psbm#`obpp>!nfnafqp#le#wkf#tjmglt-ol`bwjlmufqwj`bo.bojdm9,b=##?b#kqfe>!?"gl`wzsf#kwno=nfgjb>!p`qffm!#?lswjlm#ubovf>!ebuj`lm-j`l!#,=\t\n\n?gju#`obpp>!`kbqb`wfqjpwj`p!#nfwklg>!dfw!#,algz=\t?,kwno=\tpklqw`vw#j`lm!#gl`vnfmw-tqjwf+sbggjmd.alwwln9qfsqfpfmwbwjufppvanjw!#ubovf>!bojdm>!`fmwfq!#wkqlvdklvw#wkf#p`jfm`f#ej`wjlm\t##?gju#`obpp>!pvanjw!#`obpp>!lmf#le#wkf#nlpw#ubojdm>!wls!=?tbp#fpwbaojpkfg*8\t?,p`qjsw=\tqfwvqm#ebopf8!=*-pwzof-gjpsobzaf`bvpf#le#wkf#gl`vnfmw-`llhjf?elqn#b`wjlm>!,~algzxnbqdjm938Fm`z`olsfgjb#leufqpjlm#le#wkf#-`qfbwfFofnfmw+mbnf!#`lmwfmw>!?,gju=\t?,gju=\t\tbgnjmjpwqbwjuf#?,algz=\t?,kwno=kjpwlqz#le#wkf#!=?jmsvw#wzsf>!slqwjlm#le#wkf#bp#sbqw#le#wkf#%maps8?b#kqfe>!lwkfq#`lvmwqjfp!=\t?gju#`obpp>!?,psbm=?,psbm=?Jm#lwkfq#tlqgp/gjpsobz9#aol`h8`lmwqlo#le#wkf#jmwqlgv`wjlm#le,=\t?nfwb#mbnf>!bp#tfoo#bp#wkf#jm#qf`fmw#zfbqp\t\n?gju#`obpp>!?,gju=\t\n?,gju=\tjmpsjqfg#az#wkfwkf#fmg#le#wkf#`lnsbwjaof#tjwkaf`bnf#hmltm#bp#pwzof>!nbqdjm9-ip!=?,p`qjsw=?#Jmwfqmbwjlmbo#wkfqf#kbuf#affmDfqnbm#obmdvbdf#pwzof>!`lolq9 @lnnvmjpw#Sbqwz`lmpjpwfmw#tjwkalqgfq>!3!#`foo#nbqdjmkfjdkw>!wkf#nbilqjwz#le!#bojdm>!`fmwfqqfobwfg#wl#wkf#nbmz#gjeefqfmw#Lqwklgl{#@kvq`kpjnjobq#wl#wkf#,=\t?ojmh#qfo>!ptbp#lmf#le#wkf#vmwjo#kjp#gfbwk~*+*8\t?,p`qjsw=lwkfq#obmdvbdfp`lnsbqfg#wl#wkfslqwjlmp#le#wkfwkf#Mfwkfqobmgpwkf#nlpw#`lnnlmab`hdqlvmg9vqo+bqdvfg#wkbw#wkfp`qloojmd>!ml!#jm`ovgfg#jm#wkfMlqwk#Bnfqj`bm#wkf#mbnf#le#wkfjmwfqsqfwbwjlmpwkf#wqbgjwjlmbogfufolsnfmw#le#eqfrvfmwoz#vpfgb#`loof`wjlm#leufqz#pjnjobq#wlpvqqlvmgjmd#wkff{bnsof#le#wkjpbojdm>!`fmwfq!=tlvog#kbuf#affmjnbdf\\`bswjlm#>bwwb`kfg#wl#wkfpvddfpwjmd#wkbwjm#wkf#elqn#le#jmuloufg#jm#wkfjp#gfqjufg#eqlnmbnfg#bewfq#wkfJmwqlgv`wjlm#wlqfpwqj`wjlmp#lm#pwzof>!tjgwk9#`bm#af#vpfg#wl#wkf#`qfbwjlm#lenlpw#jnslqwbmw#jmelqnbwjlm#bmgqfpvowfg#jm#wkf`loobspf#le#wkfWkjp#nfbmp#wkbwfofnfmwp#le#wkftbp#qfsob`fg#azbmbozpjp#le#wkfjmpsjqbwjlm#elqqfdbqgfg#bp#wkfnlpw#pv``fppevohmltm#bp#%rvlw8b#`lnsqfkfmpjufKjpwlqz#le#wkf#tfqf#`lmpjgfqfgqfwvqmfg#wl#wkfbqf#qfefqqfg#wlVmplvq`fg#jnbdf=\t\n?gju#`obpp>!`lmpjpwp#le#wkfpwlsSqlsbdbwjlmjmwfqfpw#jm#wkfbubjobajojwz#lebssfbqp#wl#kbuffof`wqlnbdmfwj`fmbaofPfquj`fp+evm`wjlm#le#wkfJw#jp#jnslqwbmw?,p`qjsw=?,gju=evm`wjlm+*xubq#qfobwjuf#wl#wkfbp#b#qfpvow#le#wkf#slpjwjlm#leElq#f{bnsof/#jm#nfwklg>!slpw!#tbp#elooltfg#az%bns8ngbpk8#wkfwkf#bssoj`bwjlmip!=?,p`qjsw=\tvo=?,gju=?,gju=bewfq#wkf#gfbwktjwk#qfpsf`w#wlpwzof>!sbggjmd9jp#sbqwj`vobqozgjpsobz9jmojmf8#wzsf>!pvanjw!#jp#gjujgfg#jmwl\bTA\nzk#+\vBl\bQ*qfpslmpbajojgbgbgnjmjpwqb`j/_mjmwfqmb`jlmbofp`lqqfpslmgjfmwf\fHe\fHF\fHC\fIg\fH{\fHF\fIn\fH\\\fIa\fHY\fHU\fHB\fHR\fH\\\fIk\fH^\fIg\fH{\fIg\fHn\fHv\fIm\fHD\fHR\fHY\fH^\fIk\fHy\fHS\fHD\fHT\fH\\\fHy\fHR\fH\\\fHF\fIm\fH^\fHS\fHT\fHz\fIg\fHp\fIk\fHn\fHv\fHR\fHU\fHS\fHc\fHA\fIk\fHp\fIk\fHn\fHZ\fHR\fHB\fHS\fH^\fHU\fHB\fHR\fH\\\fIl\fHp\fHR\fH{\fH\\\fHO\fH@\fHD\fHR\fHD\fIk\fHy\fIm\fHB\fHR\fH\\\fH@\fIa\fH^\fIe\fH{\fHB\fHR\fH^\fHS\fHy\fHB\fHU\fHS\fH^\fHR\fHF\fIo\fH[\fIa\fHL\fH@\fHN\fHP\fHH\fIk\fHA\fHR\fHp\fHF\fHR\fHy\fIa\fH^\fHS\fHy\fHs\fIa\fH\\\fIk\fHD\fHz\fHS\fH^\fHR\fHG\fHJ\fI`\fH\\\fHR\fHD\fHB\fHR\fHB\fH^\fIk\fHB\fHH\fHJ\fHR\fHD\fH@\fHR\fHp\fHR\fH\\\fHY\fHS\fHy\fHR\fHT\fHy\fIa\fHC\fIg\fHn\fHv\fHR\fHU\fHH\fIk\fHF\fHU\fIm\fHm\fHv\fH@\fHH\fHR\fHC\fHR\fHT\fHn\fHY\fHR\fHJ\fHJ\fIk\fHz\fHD\fIk\fHF\fHS\fHw\fH^\fIk\fHY\fHS\fHZ\fIk\fH[\fH\\\fHR\fHp\fIa\fHC\fHe\fHH\fIa\fHH\fH\\\fHB\fIm\fHn\fH@\fHd\fHJ\fIg\fHD\fIg\fHn\fHe\fHF\fHy\fH\\\fHO\fHF\fHN\fHP\fIk\fHn\fHT\fIa\fHI\fHS\fHH\fHG\fHS\fH^\fIa\fHB\fHB\fIm\fHz\fIa\fHC\fHi\fHv\fIa\fHw\fHR\fHw\fIn\fHs\fHH\fIl\fHT\fHn\fH{\fIl\fHH\fHp\fHR\fHc\fH{\fHR\fHY\fHS\fHA\fHR\fH{\fHt\fHO\fIa\fHs\fIk\fHJ\fIn\fHT\fH\\\fIk\fHJ\fHS\fHD\fIg\fHn\fHU\fHH\fIa\fHC\fHR\fHT\fIk\fHy\fIa\fHT\fH{\fHR\fHn\fHK\fIl\fHY\fHS\fHZ\fIa\fHY\fH\\\fHR\fHH\fIk\fHn\fHJ\fId\fHs\fIa\fHT\fHD\fHy\fIa\fHZ\fHR\fHT\fHR\fHB\fHD\fIk\fHi\fHJ\fHR\fH^\fHH\fH@\fHS\fHp\fH^\fIl\fHF\fIm\fH\\\fIn\fH[\fHU\fHS\fHn\fHJ\fIl\fHB\fHS\fHH\fIa\fH\\\fHy\fHY\fHS\fHH\fHR\fH\\\fIm\fHF\fHC\fIk\fHT\fIa\fHI\fHR\fHD\fHy\fH\\\fIg\fHM\fHP\fHB\fIm\fHy\fIa\fHH\fHC\fIg\fHp\fHD\fHR\fHy\fIo\fHF\fHC\fHR\fHF\fIg\fHT\fIa\fHs\fHt\fH\\\fIk\fH^\fIn\fHy\fHR\fH\\\fIa\fHC\fHY\fHS\fHv\fHR\fH\\\fHT\fIn\fHv\fHD\fHR\fHB\fIn\fH^\fIa\fHC\fHJ\fIk\fHz\fIk\fHn\fHU\fHB\fIk\fHZ\fHR\fHT\fIa\fHy\fIn\fH^\fHB\fId\fHn\fHD\fIk\fHH\fId\fHC\fHR\fH\\\fHp\fHS\fHT\fHy\fIkqpp({no!#wjwof>!.wzsf!#`lmwfmw>!wjwof!#`lmwfmw>!bw#wkf#pbnf#wjnf-ip!=?,p`qjsw=\t?!#nfwklg>!slpw!#?,psbm=?,b=?,oj=ufqwj`bo.bojdm9w,irvfqz-njm-ip!=-`oj`h+evm`wjlm+#pwzof>!sbggjmd.~*+*8\t?,p`qjsw=\t?,psbm=?b#kqfe>!?b#kqfe>!kwws9,,*8#qfwvqm#ebopf8wf{w.gf`lqbwjlm9#p`qloojmd>!ml!#alqgfq.`loobspf9bppl`jbwfg#tjwk#Abkbpb#JmglmfpjbFmdojpk#obmdvbdf?wf{w#{no9psb`f>-dje!#alqgfq>!3!?,algz=\t?,kwno=\tlufqeolt9kjggfm8jnd#pq`>!kwws9,,bggFufmwOjpwfmfqqfpslmpjaof#elq#p-ip!=?,p`qjsw=\t,ebuj`lm-j`l!#,=lsfqbwjmd#pzpwfn!#pwzof>!tjgwk92wbqdfw>!\\aobmh!=Pwbwf#Vmjufqpjwzwf{w.bojdm9ofew8\tgl`vnfmw-tqjwf+/#jm`ovgjmd#wkf#bqlvmg#wkf#tlqog*8\t?,p`qjsw=\t?!#pwzof>!kfjdkw98lufqeolt9kjggfmnlqf#jmelqnbwjlmbm#jmwfqmbwjlmbob#nfnafq#le#wkf#lmf#le#wkf#ejqpw`bm#af#elvmg#jm#?,gju=\t\n\n?,gju=\tgjpsobz9#mlmf8!=!#,=\t?ojmh#qfo>!\t##+evm`wjlm+*#xwkf#26wk#`fmwvqz-sqfufmwGfebvow+obqdf#mvnafq#le#Azybmwjmf#Fnsjqf-isdwkvnaofewubpw#nbilqjwz#lenbilqjwz#le#wkf##bojdm>!`fmwfq!=Vmjufqpjwz#Sqfppglnjmbwfg#az#wkfPf`lmg#Tlqog#Tbqgjpwqjavwjlm#le#pwzof>!slpjwjlm9wkf#qfpw#le#wkf#`kbqb`wfqjyfg#az#qfo>!mleloolt!=gfqjufp#eqln#wkfqbwkfq#wkbm#wkf#b#`lnajmbwjlm#lepwzof>!tjgwk9233Fmdojpk.psfbhjmd`lnsvwfq#p`jfm`falqgfq>!3!#bow>!wkf#f{jpwfm`f#leGfnl`qbwj`#Sbqwz!#pwzof>!nbqdjm.Elq#wkjp#qfbplm/-ip!=?,p`qjsw=\t\npAzWbdMbnf+p*X3^ip!=?,p`qjsw=\t?-ip!=?,p`qjsw=\tojmh#qfo>!j`lm!#$#bow>$$#`obpp>$elqnbwjlm#le#wkfufqpjlmp#le#wkf#?,b=?,gju=?,gju=,sbdf=\t##?sbdf=\t?gju#`obpp>!`lmwaf`bnf#wkf#ejqpwabkbpb#Jmglmfpjbfmdojpk#+pjnsof*"y"W"W"["Q"U"V"@=i=l<^<\\=n=m!?gju#jg>!ellwfq!=wkf#Vmjwfg#Pwbwfp?jnd#pq`>!kwws9,,-isdqjdkwwkvna-ip!=?,p`qjsw=\t?ol`bwjlm-sqlwl`loeqbnfalqgfq>!3!#p!#,=\t?nfwb#mbnf>!?,b=?,gju=?,gju=?elmw.tfjdkw9alog8%rvlw8#bmg#%rvlw8gfsfmgjmd#lm#wkf#nbqdjm938sbggjmd9!#qfo>!mleloolt!#Sqfpjgfmw#le#wkf#wtfmwjfwk#`fmwvqzfujpjlm=\t##?,sbdfJmwfqmfw#F{solqfqb-bpzm`#>#wqvf8\tjmelqnbwjlm#balvw?gju#jg>!kfbgfq!=!#b`wjlm>!kwws9,,?b#kqfe>!kwwsp9,,?gju#jg>!`lmwfmw!?,gju=\t?,gju=\t?gfqjufg#eqln#wkf#?jnd#pq`>$kwws9,,b``lqgjmd#wl#wkf#\t?,algz=\t?,kwno=\tpwzof>!elmw.pjyf9p`qjsw#obmdvbdf>!Bqjbo/#Kfoufwj`b/?,b=?psbm#`obpp>!?,p`qjsw=?p`qjsw#slojwj`bo#sbqwjfpwg=?,wq=?,wbaof=?kqfe>!kwws9,,ttt-jmwfqsqfwbwjlm#leqfo>!pwzofpkffw!#gl`vnfmw-tqjwf+$?`kbqpfw>!vwe.;!=\tafdjmmjmd#le#wkf#qfufbofg#wkbw#wkfwfofujpjlm#pfqjfp!#qfo>!mleloolt!=#wbqdfw>!\\aobmh!=`objnjmd#wkbw#wkfkwws&0B&1E&1Ettt-nbmjefpwbwjlmp#leSqjnf#Njmjpwfq#lejmeovfm`fg#az#wkf`obpp>!`ofbqej{!=,gju=\t?,gju=\t\twkqff.gjnfmpjlmbo@kvq`k#le#Fmdobmgle#Mlqwk#@bqlojmbprvbqf#hjolnfwqfp-bggFufmwOjpwfmfqgjpwjm`w#eqln#wkf`lnnlmoz#hmltm#bpSklmfwj`#Boskbafwgf`obqfg#wkbw#wkf`lmwqloofg#az#wkfAfmibnjm#Eqbmhojmqlof.sobzjmd#dbnfwkf#Vmjufqpjwz#lejm#Tfpwfqm#Fvqlsfsfqplmbo#`lnsvwfqSqlif`w#Dvwfmafqdqfdbqgofpp#le#wkfkbp#affm#sqlslpfgwldfwkfq#tjwk#wkf=?,oj=?oj#`obpp>!jm#plnf#`lvmwqjfpnjm-ip!=?,p`qjsw=le#wkf#slsvobwjlmleej`jbo#obmdvbdf?jnd#pq`>!jnbdfp,jgfmwjejfg#az#wkfmbwvqbo#qfplvq`fp`obppjej`bwjlm#le`bm#af#`lmpjgfqfgrvbmwvn#nf`kbmj`pMfufqwkfofpp/#wkfnjoojlm#zfbqp#bdl?,algz=\t?,kwno="y"W"W"["Q"U"V"@\twbhf#bgubmwbdf#lebmg/#b``lqgjmd#wlbwwqjavwfg#wl#wkfNj`qlplew#Tjmgltpwkf#ejqpw#`fmwvqzvmgfq#wkf#`lmwqlogju#`obpp>!kfbgfqpklqwoz#bewfq#wkfmlwbaof#f{`fswjlmwfmp#le#wklvpbmgppfufqbo#gjeefqfmwbqlvmg#wkf#tlqog-qfb`kjmd#njojwbqzjplobwfg#eqln#wkflsslpjwjlm#wl#wkfwkf#Log#WfpwbnfmwBeqj`bm#Bnfqj`bmpjmpfqwfg#jmwl#wkfpfsbqbwf#eqln#wkfnfwqlslojwbm#bqfbnbhfp#jw#slppjaofb`hmltofgdfg#wkbwbqdvbaoz#wkf#nlpwwzsf>!wf{w,`pp!=\twkf#JmwfqmbwjlmboB``lqgjmd#wl#wkf#sf>!wf{w,`pp!#,=\t`ljm`jgf#tjwk#wkfwtl.wkjqgp#le#wkfGvqjmd#wkjp#wjnf/gvqjmd#wkf#sfqjlgbmmlvm`fg#wkbw#kfwkf#jmwfqmbwjlmbobmg#nlqf#qf`fmwozafojfufg#wkbw#wkf`lmp`jlvpmfpp#bmgelqnfqoz#hmltm#bppvqqlvmgfg#az#wkfejqpw#bssfbqfg#jml``bpjlmbooz#vpfgslpjwjlm9baplovwf8!#wbqdfw>!\\aobmh!#slpjwjlm9qfobwjuf8wf{w.bojdm9`fmwfq8ib{,ojap,irvfqz,2-ab`hdqlvmg.`lolq9 wzsf>!bssoj`bwjlm,bmdvbdf!#`lmwfmw>!?nfwb#kwws.frvju>!Sqjub`z#Sloj`z?,b=f+!&0@p`qjsw#pq`>$!#wbqdfw>!\\aobmh!=Lm#wkf#lwkfq#kbmg/-isdwkvnaqjdkw1?,gju=?gju#`obpp>!?gju#pwzof>!eolbw9mjmfwffmwk#`fmwvqz?,algz=\t?,kwno=\t?jnd#pq`>!kwws9,,p8wf{w.bojdm9`fmwfqelmw.tfjdkw9#alog8#B``lqgjmd#wl#wkf#gjeefqfm`f#afwtffm!#eqbnfalqgfq>!3!#!#pwzof>!slpjwjlm9ojmh#kqfe>!kwws9,,kwno7,ollpf-gwg!=\tgvqjmd#wkjp#sfqjlg?,wg=?,wq=?,wbaof=`olpfoz#qfobwfg#wlelq#wkf#ejqpw#wjnf8elmw.tfjdkw9alog8jmsvw#wzsf>!wf{w!#?psbm#pwzof>!elmw.lmqfbgzpwbwf`kbmdf\n?gju#`obpp>!`ofbqgl`vnfmw-ol`bwjlm-#Elq#f{bnsof/#wkf#b#tjgf#ubqjfwz#le#?"GL@WZSF#kwno=\t?%maps8%maps8%maps8!=?b#kqfe>!kwws9,,pwzof>!eolbw9ofew8`lm`fqmfg#tjwk#wkf>kwws&0B&1E&1Ettt-jm#slsvobq#`vowvqfwzsf>!wf{w,`pp!#,=jw#jp#slppjaof#wl#Kbqubqg#Vmjufqpjwzwzofpkffw!#kqfe>!,wkf#nbjm#`kbqb`wfqL{elqg#Vmjufqpjwz##mbnf>!hfztlqgp!#`pwzof>!wf{w.bojdm9wkf#Vmjwfg#Hjmdglnefgfqbo#dlufqmnfmw?gju#pwzof>!nbqdjm#gfsfmgjmd#lm#wkf#gfp`qjswjlm#le#wkf?gju#`obpp>!kfbgfq-njm-ip!=?,p`qjsw=gfpwqv`wjlm#le#wkfpojdkwoz#gjeefqfmwjm#b``lqgbm`f#tjwkwfof`lnnvmj`bwjlmpjmgj`bwfp#wkbw#wkfpklqwoz#wkfqfbewfqfpsf`jbooz#jm#wkf#Fvqlsfbm#`lvmwqjfpKltfufq/#wkfqf#bqfpq`>!kwws9,,pwbwj`pvddfpwfg#wkbw#wkf!#pq`>!kwws9,,ttt-b#obqdf#mvnafq#le#Wfof`lnnvmj`bwjlmp!#qfo>!mleloolt!#wKloz#Qlnbm#Fnsfqlqbonlpw#f{`ovpjufoz!#alqgfq>!3!#bow>!Pf`qfwbqz#le#Pwbwf`vonjmbwjmd#jm#wkf@JB#Tlqog#Eb`wallhwkf#nlpw#jnslqwbmwbmmjufqpbqz#le#wkfpwzof>!ab`hdqlvmg.?oj=?fn=?b#kqfe>!,wkf#Bwobmwj`#L`fbmpwqj`woz#psfbhjmd/pklqwoz#afelqf#wkfgjeefqfmw#wzsfp#lewkf#Lwwlnbm#Fnsjqf=?jnd#pq`>!kwws9,,Bm#Jmwqlgv`wjlm#wl`lmpfrvfm`f#le#wkfgfsbqwvqf#eqln#wkf@lmefgfqbwf#Pwbwfpjmgjdfmlvp#sflsofpSql`ffgjmdp#le#wkfjmelqnbwjlm#lm#wkfwkflqjfp#kbuf#affmjmuloufnfmw#jm#wkfgjujgfg#jmwl#wkqffbgib`fmw#`lvmwqjfpjp#qfpslmpjaof#elqgjpplovwjlm#le#wkf`loobalqbwjlm#tjwktjgfoz#qfdbqgfg#bpkjp#`lmwfnslqbqjfpelvmgjmd#nfnafq#leGlnjmj`bm#Qfsvaoj`dfmfqbooz#b``fswfgwkf#slppjajojwz#lebqf#bopl#bubjobaofvmgfq#`lmpwqv`wjlmqfpwlqbwjlm#le#wkfwkf#dfmfqbo#svaoj`jp#bonlpw#fmwjqfozsbppfp#wkqlvdk#wkfkbp#affm#pvddfpwfg`lnsvwfq#bmg#ujgflDfqnbmj`#obmdvbdfp#b``lqgjmd#wl#wkf#gjeefqfmw#eqln#wkfpklqwoz#bewfqtbqgpkqfe>!kwwsp9,,ttt-qf`fmw#gfufolsnfmwAlbqg#le#Gjqf`wlqp?gju#`obpp>!pfbq`k#?b#kqfe>!kwws9,,Jm#sbqwj`vobq/#wkfNvowjsof#ellwmlwfplq#lwkfq#pvapwbm`fwklvpbmgp#le#zfbqpwqbmpobwjlm#le#wkf?,gju=\t?,gju=\t\t?b#kqfe>!jmgf{-skstbp#fpwbaojpkfg#jmnjm-ip!=?,p`qjsw=\tsbqwj`jsbwf#jm#wkfb#pwqlmd#jmeovfm`fpwzof>!nbqdjm.wls9qfsqfpfmwfg#az#wkfdqbgvbwfg#eqln#wkfWqbgjwjlmbooz/#wkfFofnfmw+!p`qjsw!*8Kltfufq/#pjm`f#wkf,gju=\t?,gju=\t?gju#ofew8#nbqdjm.ofew9sqlwf`wjlm#bdbjmpw38#ufqwj`bo.bojdm9Vmelqwvmbwfoz/#wkfwzsf>!jnbdf,{.j`lm,gju=\t?gju#`obpp>!#`obpp>!`ofbqej{!=?gju#`obpp>!ellwfq\n\n?,gju=\t\n\n?,gju=\twkf#nlwjlm#sj`wvqf<}=f!t0-lqd,2:::,{kwno!=?b#wbqdfw>!\\aobmh!#wf{w,kwno8#`kbqpfw>!#wbqdfw>!\\aobmh!=?wbaof#`foosbggjmd>!bvwl`lnsofwf>!lee!#wf{w.bojdm9#`fmwfq8wl#obpw#ufqpjlm#az#ab`hdqlvmg.`lolq9# !#kqfe>!kwws9,,ttt-,gju=?,gju=?gju#jg>?b#kqfe>! !#`obpp>!!=?jnd#pq`>!kwws9,,`qjsw!#pq`>!kwws9,,\t?p`qjsw#obmdvbdf>!,,FM!#!kwws9,,ttt-tfm`lgfVQJ@lnslmfmw+!#kqfe>!ibubp`qjsw9?gju#`obpp>!`lmwfmwgl`vnfmw-tqjwf+$?p`slpjwjlm9#baplovwf8p`qjsw#pq`>!kwws9,,#pwzof>!nbqdjm.wls9-njm-ip!=?,p`qjsw=\t?,gju=\t?gju#`obpp>!t0-lqd,2:::,{kwno!#\t\t?,algz=\t?,kwno=gjpwjm`wjlm#afwtffm,!#wbqdfw>!\\aobmh!=?ojmh#kqfe>!kwws9,,fm`lgjmd>!vwe.;!<=\tt-bggFufmwOjpwfmfq!kwws9,,ttt-j`lm!#kqfe>!kwws9,,#pwzof>!ab`hdqlvmg9wzsf>!wf{w,`pp!#,=\tnfwb#sqlsfqwz>!ld9w?jmsvw#wzsf>!wf{w!##pwzof>!wf{w.bojdm9wkf#gfufolsnfmw#le#wzofpkffw!#wzsf>!wfkwno8#`kbqpfw>vwe.;jp#`lmpjgfqfg#wl#afwbaof#tjgwk>!233&!#Jm#bggjwjlm#wl#wkf#`lmwqjavwfg#wl#wkf#gjeefqfm`fp#afwtffmgfufolsnfmw#le#wkf#Jw#jp#jnslqwbmw#wl#?,p`qjsw=\t\t?p`qjsw##pwzof>!elmw.pjyf92=?,psbm=?psbm#jg>daOjaqbqz#le#@lmdqfpp?jnd#pq`>!kwws9,,jnFmdojpk#wqbmpobwjlmB`bgfnz#le#P`jfm`fpgju#pwzof>!gjpsobz9`lmpwqv`wjlm#le#wkf-dfwFofnfmwAzJg+jg*jm#`lmivm`wjlm#tjwkFofnfmw+$p`qjsw$*8#?nfwb#sqlsfqwz>!ld9<}=f!wf{w!#mbnf>!=Sqjub`z#Sloj`z?,b=bgnjmjpwfqfg#az#wkffmbaofPjmdofQfrvfpwpwzof>%rvlw8nbqdjm9?,gju=?,gju=?,gju=?=?jnd#pq`>!kwws9,,j#pwzof>%rvlw8eolbw9qfefqqfg#wl#bp#wkf#wlwbo#slsvobwjlm#lejm#Tbpkjmdwlm/#G-@-#pwzof>!ab`hdqlvmg.bnlmd#lwkfq#wkjmdp/lqdbmjybwjlm#le#wkfsbqwj`jsbwfg#jm#wkfwkf#jmwqlgv`wjlm#lejgfmwjejfg#tjwk#wkfej`wjlmbo#`kbqb`wfq#L{elqg#Vmjufqpjwz#njpvmgfqpwbmgjmd#leWkfqf#bqf/#kltfufq/pwzofpkffw!#kqfe>!,@lovnajb#Vmjufqpjwzf{sbmgfg#wl#jm`ovgfvpvbooz#qfefqqfg#wljmgj`bwjmd#wkbw#wkfkbuf#pvddfpwfg#wkbwbeejojbwfg#tjwk#wkf`lqqfobwjlm#afwtffmmvnafq#le#gjeefqfmw=?,wg=?,wq=?,wbaof=Qfsvaoj`#le#Jqfobmg\t?,p`qjsw=\t?p`qjsw#vmgfq#wkf#jmeovfm`f`lmwqjavwjlm#wl#wkfLeej`jbo#tfapjwf#lekfbgrvbqwfqp#le#wkf`fmwfqfg#bqlvmg#wkfjnsoj`bwjlmp#le#wkfkbuf#affm#gfufolsfgEfgfqbo#Qfsvaoj`#leaf`bnf#jm`qfbpjmdoz`lmwjmvbwjlm#le#wkfMlwf/#kltfufq/#wkbwpjnjobq#wl#wkbw#le#`bsbajojwjfp#le#wkfb``lqgbm`f#tjwk#wkfsbqwj`jsbmwp#jm#wkfevqwkfq#gfufolsnfmwvmgfq#wkf#gjqf`wjlmjp#lewfm#`lmpjgfqfgkjp#zlvmdfq#aqlwkfq?,wg=?,wq=?,wbaof=?b#kwws.frvju>![.VB.skzpj`bo#sqlsfqwjfple#Aqjwjpk#@lovnajbkbp#affm#`qjwj`jyfg+tjwk#wkf#f{`fswjlmrvfpwjlmp#balvw#wkfsbppjmd#wkqlvdk#wkf3!#`foosbggjmd>!3!#wklvpbmgp#le#sflsofqfgjqf`wp#kfqf-#Elqkbuf#`kjogqfm#vmgfq&0F&0@,p`qjsw&0F!**8?b#kqfe>!kwws9,,ttt-?oj=?b#kqfe>!kwws9,,pjwf\\mbnf!#`lmwfmw>!wf{w.gf`lqbwjlm9mlmfpwzof>!gjpsobz9#mlmf?nfwb#kwws.frvju>![.mft#Gbwf+*-dfwWjnf+*#wzsf>!jnbdf,{.j`lm!?,psbm=?psbm#`obpp>!obmdvbdf>!ibubp`qjswtjmglt-ol`bwjlm-kqfe?b#kqfe>!ibubp`qjsw9..=\t?p`qjsw#wzsf>!w?b#kqfe>$kwws9,,ttt-klqw`vw#j`lm!#kqfe>!?,gju=\t?gju#`obpp>!?p`qjsw#pq`>!kwws9,,!#qfo>!pwzofpkffw!#w?,gju=\t?p`qjsw#wzsf>,b=#?b#kqfe>!kwws9,,#booltWqbmpsbqfm`z>![.VB.@lnsbwjaof!#`lmqfobwjlmpkjs#afwtffm\t?,p`qjsw=\t?p`qjsw#?,b=?,oj=?,vo=?,gju=bppl`jbwfg#tjwk#wkf#sqldqbnnjmd#obmdvbdf?,b=?b#kqfe>!kwws9,,?,b=?,oj=?oj#`obpp>!elqn#b`wjlm>!kwws9,,?gju#pwzof>!gjpsobz9wzsf>!wf{w!#mbnf>!r!?wbaof#tjgwk>!233&!#ab`hdqlvmg.slpjwjlm9!#alqgfq>!3!#tjgwk>!qfo>!pklqw`vw#j`lm!#k5=?vo=?oj=?b#kqfe>!##?nfwb#kwws.frvju>!`pp!#nfgjb>!p`qffm!#qfpslmpjaof#elq#wkf#!#wzsf>!bssoj`bwjlm,!#pwzof>!ab`hdqlvmg.kwno8#`kbqpfw>vwe.;!#booltwqbmpsbqfm`z>!pwzofpkffw!#wzsf>!wf\t?nfwb#kwws.frvju>!=?,psbm=?psbm#`obpp>!3!#`foopsb`jmd>!3!=8\t?,p`qjsw=\t?p`qjsw#plnfwjnfp#`boofg#wkfglfp#mlw#mf`fppbqjozElq#nlqf#jmelqnbwjlmbw#wkf#afdjmmjmd#le#?"GL@WZSF#kwno=?kwnosbqwj`vobqoz#jm#wkf#wzsf>!kjggfm!#mbnf>!ibubp`qjsw9uljg+3*8!feef`wjufmfpp#le#wkf#bvwl`lnsofwf>!lee!#dfmfqbooz#`lmpjgfqfg=?jmsvw#wzsf>!wf{w!#!=?,p`qjsw=\t?p`qjswwkqlvdklvw#wkf#tlqog`lnnlm#njp`lm`fswjlmbppl`jbwjlm#tjwk#wkf?,gju=\t?,gju=\t?gju#`gvqjmd#kjp#ojefwjnf/`lqqfpslmgjmd#wl#wkfwzsf>!jnbdf,{.j`lm!#bm#jm`qfbpjmd#mvnafqgjsolnbwj`#qfobwjlmpbqf#lewfm#`lmpjgfqfgnfwb#`kbqpfw>!vwe.;!#?jmsvw#wzsf>!wf{w!#f{bnsofp#jm`ovgf#wkf!=?jnd#pq`>!kwws9,,jsbqwj`jsbwjlm#jm#wkfwkf#fpwbaojpknfmw#le\t?,gju=\t?gju#`obpp>!%bns8maps8%bns8maps8wl#gfwfqnjmf#tkfwkfqrvjwf#gjeefqfmw#eqlnnbqhfg#wkf#afdjmmjmdgjpwbm`f#afwtffm#wkf`lmwqjavwjlmp#wl#wkf`lmeoj`w#afwtffm#wkftjgfoz#`lmpjgfqfg#wltbp#lmf#le#wkf#ejqpwtjwk#ubqzjmd#gfdqffpkbuf#psf`vobwfg#wkbw+gl`vnfmw-dfwFofnfmwsbqwj`jsbwjmd#jm#wkflqjdjmbooz#gfufolsfgfwb#`kbqpfw>!vwe.;!=#wzsf>!wf{w,`pp!#,=\tjmwfq`kbmdfbaoz#tjwknlqf#`olpfoz#qfobwfgpl`jbo#bmg#slojwj`bowkbw#tlvog#lwkfqtjpfsfqsfmgj`vobq#wl#wkfpwzof#wzsf>!wf{w,`ppwzsf>!pvanjw!#mbnf>!ebnjojfp#qfpjgjmd#jmgfufolsjmd#`lvmwqjfp`lnsvwfq#sqldqbnnjmdf`lmlnj`#gfufolsnfmwgfwfqnjmbwjlm#le#wkfelq#nlqf#jmelqnbwjlmlm#pfufqbo#l``bpjlmpslqwvdv/Fp#+Fvqlsfv*VWE.;!#pfwWjnflvw+evm`wjlm+*gjpsobz9jmojmf.aol`h8?jmsvw#wzsf>!pvanjw!#wzsf#>#$wf{w,ibubp`qj?jnd#pq`>!kwws9,,ttt-!#!kwws9,,ttt-t0-lqd,pklqw`vw#j`lm!#kqfe>!!#bvwl`lnsofwf>!lee!#?,b=?,gju=?gju#`obpp>?,b=?,oj=\t?oj#`obpp>!`pp!#wzsf>!wf{w,`pp!#?elqn#b`wjlm>!kwws9,,{w,`pp!#kqfe>!kwws9,,ojmh#qfo>!bowfqmbwf!#\t?p`qjsw#wzsf>!wf{w,#lm`oj`h>!ibubp`qjsw9+mft#Gbwf*-dfwWjnf+*~kfjdkw>!2!#tjgwk>!2!#Sflsof$p#Qfsvaoj`#le##?b#kqfe>!kwws9,,ttt-wf{w.gf`lqbwjlm9vmgfqwkf#afdjmmjmd#le#wkf#?,gju=\t?,gju=\t?,gju=\tfpwbaojpknfmw#le#wkf#?,gju=?,gju=?,gju=?,g ujftslqwxnjm.kfjdkw9\t?p`qjsw#pq`>!kwws9,,lswjlm=?lswjlm#ubovf>lewfm#qfefqqfg#wl#bp#,lswjlm=\t?lswjlm#ubov?"GL@WZSF#kwno=\t?"..XJmwfqmbwjlmbo#Bjqslqw=\t?b#kqfe>!kwws9,,ttt?,b=?b#kqfe>!kwws9,,t\fTL\fT^\fTE\fT^\fUh\fT{\fTN\roI\ro|\roL\ro{\roO\rov\rot\nAOGx\bTA\nzk#+\vUmGx*\fHD\fHS\fH\\\fIa\fHJ\fIk\fHZ\fHM\fHR\fHe\fHD\fH^\fIg\fHM\fHy\fIa\fH[\fIk\fHH\fIa\fH\\\fHp\fHR\fHD\fHy\fHR\fH\\\fIl\fHT\fHn\fH@\fHn\fHK\fHS\fHH\fHT\fIa\fHI\fHR\fHF\fHD\fHR\fHT\fIa\fHY\fIl\fHy\fHR\fH\\\fHT\fHn\fHT\fIa\fHy\fH\\\fHO\fHT\fHR\fHB\fH{\fIa\fH\\\fIl\fHv\fHS\fHs\fIa\fHL\fIg\fHn\fHY\fHS\fHp\fIa\fHr\fHR\fHD\fHi\fHB\fIk\fH\\\fHS\fHy\fHR\fHY\fHS\fHA\fHS\fHD\fIa\fHD\fH{\fHR\fHM\fHS\fHC\fHR\fHm\fHy\fIa\fHC\fIg\fHn\fHy\fHS\fHT\fIm\fH\\\fHy\fIa\fH[\fHR\fHF\fHU\fIm\fHm\fHv\fHH\fIl\fHF\fIa\fH\\\fH@\fHn\fHK\fHD\fHs\fHS\fHF\fIa\fHF\fHO\fIl\fHy\fIa\fH\\\fHS\fHy\fIk\fHs\fHF\fIa\fH\\\fHR\fH\\\fHn\fHA\fHF\fIa\fH\\\fHR\fHF\fIa\fHH\fHB\fHR\fH^\fHS\fHy\fIg\fHn\fH\\\fHG\fHP\fIa\fHH\fHR\fH\\\fHD\fHS\fH\\\fIa\fHB\fHR\fHO\fH^\fHS\fHB\fHS\fHs\fIk\fHMgfp`qjswjlm!#`lmwfmw>!gl`vnfmw-ol`bwjlm-sqlw-dfwFofnfmwpAzWbdMbnf+?"GL@WZSF#kwno=\t?kwno#?nfwb#`kbqpfw>!vwe.;!=9vqo!#`lmwfmw>!kwws9,,-`pp!#qfo>!pwzofpkffw!pwzof#wzsf>!wf{w,`pp!=wzsf>!wf{w,`pp!#kqfe>!t0-lqd,2:::,{kwno!#{nowzsf>!wf{w,ibubp`qjsw!#nfwklg>!dfw!#b`wjlm>!ojmh#qfo>!pwzofpkffw!##>#gl`vnfmw-dfwFofnfmwwzsf>!jnbdf,{.j`lm!#,=`foosbggjmd>!3!#`foops-`pp!#wzsf>!wf{w,`pp!#?,b=?,oj=?oj=?b#kqfe>!!#tjgwk>!2!#kfjdkw>!2!!=?b#kqfe>!kwws9,,ttt-pwzof>!gjpsobz9mlmf8!=bowfqmbwf!#wzsf>!bssoj.,,T0@,,GWG#[KWNO#2-3#foopsb`jmd>!3!#`foosbg#wzsf>!kjggfm!#ubovf>!,b=%maps8?psbm#qlof>!p\t?jmsvw#wzsf>!kjggfm!#obmdvbdf>!IbubP`qjsw!##gl`vnfmw-dfwFofnfmwpAd>!3!#`foopsb`jmd>!3!#zsf>!wf{w,`pp!#nfgjb>!wzsf>$wf{w,ibubp`qjsw$tjwk#wkf#f{`fswjlm#le#zsf>!wf{w,`pp!#qfo>!pw#kfjdkw>!2!#tjgwk>!2!#>$(fm`lgfVQJ@lnslmfmw+?ojmh#qfo>!bowfqmbwf!#\talgz/#wq/#jmsvw/#wf{wnfwb#mbnf>!qlalwp!#`lmnfwklg>!slpw!#b`wjlm>!=\t?b#kqfe>!kwws9,,ttt-`pp!#qfo>!pwzofpkffw!#?,gju=?,gju=?gju#`obppobmdvbdf>!ibubp`qjsw!=bqjb.kjggfm>!wqvf!=.[?qjsw!#wzsf>!wf{w,ibubpo>38~*+*8\t+evm`wjlm+*xab`hdqlvmg.jnbdf9#vqo+,b=?,oj=?oj=?b#kqfe>!k\n\n?oj=?b#kqfe>!kwws9,,bwlq!#bqjb.kjggfm>!wqv=#?b#kqfe>!kwws9,,ttt-obmdvbdf>!ibubp`qjsw!#,lswjlm=\t?lswjlm#ubovf,gju=?,gju=?gju#`obpp>qbwlq!#bqjb.kjggfm>!wqf>+mft#Gbwf*-dfwWjnf+*slqwvdv/Fp#+gl#Aqbpjo*!wf{w,?nfwb#kwws.frvju>!@lmwfqbmpjwjlmbo,,FM!#!kwws9?kwno#{nomp>!kwws9,,ttt.,,T0@,,GWG#[KWNO#2-3#WGWG,{kwno2.wqbmpjwjlmbo,,ttt-t0-lqd,WQ,{kwno2,sf#>#$wf{w,ibubp`qjsw$8?nfwb#mbnf>!gfp`qjswjlmsbqfmwMlgf-jmpfqwAfelqf?jmsvw#wzsf>!kjggfm!#mbip!#wzsf>!wf{w,ibubp`qj+gl`vnfmw*-qfbgz+evm`wjp`qjsw#wzsf>!wf{w,ibubpjnbdf!#`lmwfmw>!kwws9,,VB.@lnsbwjaof!#`lmwfmw>wno8#`kbqpfw>vwe.;!#,=\tojmh#qfo>!pklqw`vw#j`lm?ojmh#qfo>!pwzofpkffw!#?,p`qjsw=\t?p`qjsw#wzsf>>#gl`vnfmw-`qfbwfFofnfm?b#wbqdfw>!\\aobmh!#kqfe>#gl`vnfmw-dfwFofnfmwpAjmsvw#wzsf>!wf{w!#mbnf>b-wzsf#>#$wf{w,ibubp`qjmsvw#wzsf>!kjggfm!#mbnfkwno8#`kbqpfw>vwe.;!#,=gwg!=\t?kwno#{nomp>!kwws.,,T0@,,GWG#KWNO#7-32#WfmwpAzWbdMbnf+$p`qjsw$*jmsvw#wzsf>!kjggfm!#mbn?p`qjsw#wzsf>!wf{w,ibubp!#pwzof>!gjpsobz9mlmf8!=gl`vnfmw-dfwFofnfmwAzJg+>gl`vnfmw-`qfbwfFofnfmw+$#wzsf>$wf{w,ibubp`qjsw$jmsvw#wzsf>!wf{w!#mbnf>!g-dfwFofnfmwpAzWbdMbnf+pmj`bo!#kqfe>!kwws9,,ttt-@,,GWG#KWNO#7-32#Wqbmpjw?pwzof#wzsf>!wf{w,`pp!=\t\t?pwzof#wzsf>!wf{w,`pp!=jlmbo-gwg!=\t?kwno#{nomp>kwws.frvju>!@lmwfmw.Wzsfgjmd>!3!#`foopsb`jmd>!3!kwno8#`kbqpfw>vwe.;!#,=\t#pwzof>!gjpsobz9mlmf8!=??oj=?b#kqfe>!kwws9,,ttt-#wzsf>$wf{w,ibubp`qjsw$=&*&'&^&ˆŸా&ƭ&ƒ&)&^&%&'&‚&P&1&±&3&]&m&u&E&t&C&Ï&V&V&/&>&6&ྲྀ᝼o&p&@&E&M&P&x&@&F&e&Ì&7&:&(&D&0&C&)&.&F&-&1&(&L&F&1ɞ*Ϫ⇳&፲&K&;&)&E&H&P&0&?&9&V&&-&v&a&,&E&)&?&=&'&'&B&മ&ԃ&̖*&*8&%&%&&&%,)&š&>&†&7&]&F&2&>&J&6&n&2&%&?&Ž&2&6&J&g&-&0&,&*&J&*&O&)&6&(&<&B&N&.&P&@&2&.&W&M&%Լ„(,(<&,&Ϛ&ᣇ&-&,(%&(&%&(Ļ0&X&D&&j&'&J&(&.&B&3&Z&R&h&3&E&E&<Æ-͠ỳ&%8?&@&,&Z&@&0&J&,&^&x&_&6&C&6&Cܬ⨥&f&-&-&-&-&,&J&2&8&z&8&C&Y&8&-&d&ṸÌ-&7&1&F&7&t&W&7&I&.&.&^&=ྜ᧓&8(>&/&/&ݻ')'ၥ')'%@/&0&%оী*&*@&CԽהɴ׫4෗ܚӑ6඄&/Ÿ̃Z&*%ɆϿ&Ĵ&1¨ҴŴ",g,"AAAAKKLLKKKKKJJIHHIHHGGFF");!function setData(e,t){const a=m,n=p;for(let e=0;e=0)return t;e.runningState>=0&&(e.runningState=t);throw new Error("Brotli error code: "+t)}return function decode(e,t){let a=new State;a.input=new InputStream(e);initState(a);if(t){let e=t.customDictionary;e&&function attachDictionaryChunk(e,t){if(1!==e.runningState)return makeError(e,-24);if(0===e.cdNumChunks){e.cdChunks=new Array(16);e.cdChunkOffsets=new Int32Array(16);e.cdBlockBits=-1}if(15===e.cdNumChunks)return makeError(e,-27);e.cdChunks[e.cdNumChunks]=t;e.cdNumChunks++;e.cdTotalSize+=t.length;e.cdChunkOffsets[e.cdNumChunks]=e.cdTotalSize;return 0}(a,e)}let n=0,s=[];for(;;){let e=new Int8Array(16384);s.push(e);a.output=e;a.outputOffset=0;a.outputLength=16384;a.outputUsed=0;decompress(a);n+=a.outputUsed;if(a.outputUsed<16384)break}!function close(e){if(0===e.runningState)return makeError(e,-25);e.runningState>0&&(e.runningState=11);return 0}(a);!function closeInput(e){e.input=new InputStream(new Int8Array(0))}(a);let r=new Int8Array(n),i=0;for(let e=0;e>e;this.codeSize=n-=e;return a}getCode(e){const t=this.stream,a=e[0],n=e[1];let s,r=this.codeSize,i=this.codeBuf;for(;r>16,f=65535&o;if(l<1||r>l;this.codeSize=r-l;return f}generateHuffmanTable(e){const t=e.length;let a,n=0;for(a=0;an&&(n=e[a]);const s=1<>=1}for(a=e;a>=1;if(0===t){let t;if(-1===(t=n.getByte())){this.#K("Bad block header in flate stream");return}let a=t;if(-1===(t=n.getByte())){this.#K("Bad block header in flate stream");return}a|=t<<8;if(-1===(t=n.getByte())){this.#K("Bad block header in flate stream");return}let s=t;if(-1===(t=n.getByte())){this.#K("Bad block header in flate stream");return}s|=t<<8;if(s!==(65535&~a)&&(0!==a||0!==s))throw new FormatError("Bad uncompressed block length in flate stream");this.codeBuf=0;this.codeSize=0;const r=this.bufferLength,i=r+a;e=this.ensureBuffer(i);this.bufferLength=i;if(0===a)-1===n.peekByte()&&(this.eof=!0);else{const t=n.getBytes(a);e.set(t,r);t.length0;)c[o++]=m}s=this.generateHuffmanTable(c.subarray(0,e));r=this.generateHuffmanTable(c.subarray(e,f))}}e=this.buffer;let i=e?e.length:0,o=this.bufferLength;for(;;){let t=this.getCode(s);if(t<256){if(o+1>=i){e=this.ensureBuffer(o+1);i=e.length}e[o++]=t;continue}if(256===t){this.bufferLength=o;return}t-=257;t=Qa[t];let n=t>>16;n>0&&(n=this.getBits(n));a=(65535&t)+n;t=this.getCode(r);t=Za[t];n=t>>16;n>0&&(n=this.getBits(n));const l=(65535&t)+n;if(o+a>=i){e=this.ensureBuffer(o+a);i=e.length}for(let t=0;t>>t&(1<0;if(e<256){u[0]=e;m=1}else{if(!(e>=258)){if(256===e){c=9;i=258;m=0;continue}this.eof=!0;delete this.lzwState;break}if(e=0;t--){u[t]=o[a];a=f[a]}}else u[m++]=u[0]}if(s){f[i]=h;l[i]=l[h]+1;o[i]=u[0];i++;c=i+r&i+r-1?c:0|Math.min(Math.log(i+r)/.6931471805599453+1,12)}h=e;p+=m;if(n15))throw new FormatError(`Unsupported predictor: ${n}`);this.readBlock=2===n?this.readBlockTiff:this.readBlockPng;this.stream=e;this.dict=e.dict;const s=this.colors=a.get("Colors")||1,r=this.bits=a.get("BPC","BitsPerComponent")||8,i=this.columns=a.get("Columns")||1;this.pixBytes=s*r+7>>3;this.rowBytes=i*s*r+7>>3;return this}readBlockTiff(){const e=this.rowBytes,t=this.bufferLength,a=this.ensureBuffer(t+e),n=this.bits,s=this.colors,r=this.stream.getBytes(e);this.eof=!r.length;if(this.eof)return;let i,o=0,l=0,f=0,c=0,h=t;if(1===n&&1===s)for(i=0;i>1;e^=e>>2;e^=e>>4;o=(1&e)<<7;a[h++]=e}else if(8===n){for(i=0;i>8&255;a[h++]=255&e}}else{const e=new Uint8Array(s+1),h=(1<>f-n)&h;f-=n;l=l<=8){a[m++]=l>>c-8&255;c-=8}}c>0&&(a[m++]=(l<<8-c)+(o&(1<<8-c)-1))}this.bufferLength+=e}readBlockPng(){const e=this.rowBytes,t=this.pixBytes,a=this.stream.getByte(),n=this.stream.getBytes(e);this.eof=!n.length;if(this.eof)return;const s=this.bufferLength,r=this.ensureBuffer(s+e);let i=r.subarray(s-e,s);0===i.length&&(i=new Uint8Array(e));let o,l,f,c=s;switch(a){case 0:for(o=0;o>1)+n[o];for(;o>1)+n[o]&255;c++}break;case 4:for(o=0;o0){const e=this.stream.getBytes(n);t.set(e,a);a+=n}}else{n=257-n;t=this.ensureBuffer(a+n+1);t.fill(e[1],a,a+n);a+=n}this.bufferLength=a}}class Parser{constructor({lexer:e,xref:t,allowStreams:a=!1,recoveryMode:n=!1}){this.lexer=e;this.xref=t;this.allowStreams=a;this.recoveryMode=n;this.imageCache=Object.create(null);this._imageId=0;this.refill()}refill(){this.buf1=this.lexer.getObj();this.buf2=this.lexer.getObj()}shift(){if(this.buf2 instanceof Cmd&&"ID"===this.buf2.cmd){this.buf1=this.buf2;this.buf2=null}else{this.buf1=this.buf2;this.buf2=this.lexer.getObj()}}tryShift(){try{this.shift();return!0}catch(e){if(e instanceof MissingDataException)throw e;return!1}}getObj(e=null){const t=this.buf1;this.shift();if(t instanceof Cmd)switch(t.cmd){case"BI":return this.makeInlineImage(e);case"[":const a=[];for(;!isCmd(this.buf1,"]")&&this.buf1!==oa;)a.push(this.getObj(e));if(this.buf1===oa){if(this.recoveryMode)return a;throw new ParserEOFException("End of file inside array.")}this.shift();return a;case"<<":const n=new Dict(this.xref);for(;!isCmd(this.buf1,">>")&&this.buf1!==oa;){if(!(this.buf1 instanceof Name)){info("Malformed dictionary: key must be a name object");this.shift();continue}const t=this.buf1.name;this.shift();if(this.buf1===oa)break;n.set(t,this.getObj(e))}if(this.buf1===oa){if(this.recoveryMode)return n;throw new ParserEOFException("End of file inside dictionary.")}if(isCmd(this.buf2,"stream"))return this.allowStreams?this.makeStream(n,e):n;this.shift();return n;default:return t}if(Number.isInteger(t)){if(Number.isInteger(this.buf1)&&isCmd(this.buf2,"R")){const e=Ref.get(t,this.buf1);this.shift();this.shift();return e}return t}return"string"==typeof t&&e?e.decryptString(t):t}findDefaultInlineStreamEnd(e){const{knownCommands:t}=this.lexer,a=e.pos;let n,s,r=0;for(;-1!==(n=e.getByte());)if(0===r)r=69===n?1:0;else if(1===r)r=73===n?2:0;else if(32===n||10===n||13===n){s=e.pos;const a=e.peekBytes(15),i=a.length;if(0===i)break;for(let e=0;e127))){r=0;break}}if(2!==r)continue;if(!t){warn("findDefaultInlineStreamEnd - `lexer.knownCommands` is undefined.");continue}const o=new Lexer(new Stream(e.peekBytes(75)),t);o._hexStringWarn=()=>{};let l=0;for(;;){const e=o.getObj();if(e===oa){r=0;break}if(e instanceof Cmd){const a=t[e.cmd];if(!a){r=0;break}if(a.variableArgs?l<=a.numArgs:l===a.numArgs)break;l=0;continue}l++}if(2===r)break}else r=0;if(-1===n){warn("findDefaultInlineStreamEnd: Reached the end of the stream without finding a valid EI marker");if(s){warn('... trying to recover by using the last "EI" occurrence.');e.skip(-(e.pos-s))}}let i=4;e.skip(-i);n=e.peekByte();e.skip(i);isWhiteSpace(n)||i--;return e.pos-i-a}findDCTDecodeInlineStreamEnd(e){const t=e.pos;let a,n,s=!1;for(;-1!==(a=e.getByte());)if(255===a){switch(e.getByte()){case 0:break;case 255:e.skip(-1);break;case 217:s=!0;break;case 192:case 193:case 194:case 195:case 197:case 198:case 199:case 201:case 202:case 203:case 205:case 206:case 207:case 196:case 204:case 218:case 219:case 220:case 221:case 222:case 223:case 224:case 225:case 226:case 227:case 228:case 229:case 230:case 231:case 232:case 233:case 234:case 235:case 236:case 237:case 238:case 239:case 254:n=e.getUint16();n>2?e.skip(n-2):e.skip(-2)}if(s)break}const r=e.pos-t;if(-1===a){warn("Inline DCTDecode image stream: EOI marker not found, searching for /EI/ instead.");e.skip(-r);return this.findDefaultInlineStreamEnd(e)}this.inlineStreamSkipEI(e);return r}findASCII85DecodeInlineStreamEnd(e){const t=e.pos;let a;for(;-1!==(a=e.getByte());)if(126===a){const t=e.pos;a=e.peekByte();for(;isWhiteSpace(a);){e.skip();a=e.peekByte()}if(62===a){e.skip();break}if(e.pos>t){const t=e.peekBytes(2);if(69===t[0]&&73===t[1])break}}const n=e.pos-t;if(-1===a){warn("Inline ASCII85Decode image stream: EOD marker not found, searching for /EI/ instead.");e.skip(-n);return this.findDefaultInlineStreamEnd(e)}this.inlineStreamSkipEI(e);return n}findASCIIHexDecodeInlineStreamEnd(e){const t=e.pos;let a;for(;-1!==(a=e.getByte())&&62!==a;);const n=e.pos-t;if(-1===a){warn("Inline ASCIIHexDecode image stream: EOD marker not found, searching for /EI/ instead.");e.skip(-n);return this.findDefaultInlineStreamEnd(e)}this.inlineStreamSkipEI(e);return n}inlineStreamSkipEI(e){let t,a=0;for(;-1!==(t=e.getByte());)if(0===a)a=69===t?1:0;else if(1===a)a=73===t?2:0;else if(2===a)break}makeInlineImage(e){const t=this.lexer,a=t.stream,n=Object.create(null);let s;for(;!isCmd(this.buf1,"ID")&&this.buf1!==oa;){if(!(this.buf1 instanceof Name))throw new FormatError("Dictionary key must be a name object");const t=this.buf1.name;this.shift();if(this.buf1===oa)break;n[t]=this.getObj(e)}-1!==t.beginInlineImagePos&&(s=a.pos-t.beginInlineImagePos);const r=this.xref.fetchIfRef(n.F||n.Filter);let i;if(r instanceof Name)i=r.name;else if(Array.isArray(r)){const e=this.xref.fetchIfRef(r[0]);e instanceof Name&&(i=e.name)}const o=a.pos;let l,f;switch(i){case"DCT":case"DCTDecode":l=this.findDCTDecodeInlineStreamEnd(a);break;case"A85":case"ASCII85Decode":l=this.findASCII85DecodeInlineStreamEnd(a);break;case"AHx":case"ASCIIHexDecode":l=this.findASCIIHexDecodeInlineStreamEnd(a);break;default:l=this.findDefaultInlineStreamEnd(a)}if(l<1e3&&s>0){const e=a.pos;a.pos=t.beginInlineImagePos;f=function getInlineImageCacheKey(e){const t=[],a=e.length;let n=0;for(;n=n){let n=!1;for(const e of s){const t=e.length;let s=0;for(;s=r){n=!0;break}if(s>=t){if(isWhiteSpace(i[l+o+s])){info(`Found "${bytesToString([...a,...e])}" when searching for endstream command.`);n=!0}break}}if(n){t.pos+=l;return t.pos-e}}l++}t.pos+=o}return-1}makeStream(e,t){const a=this.lexer;let n=a.stream;a.skipToNextLine();const s=n.pos-1;let r=e.get("Length");if(!Number.isInteger(r)){info(`Bad length "${r&&r.toString()}" in stream.`);r=0}n.pos=s+r;a.nextChar();if(this.tryShift()&&isCmd(this.buf2,"endstream"))this.shift();else{r=this.#G(s);if(r<0)throw new FormatError("Missing endstream command.");a.nextChar();this.shift();this.shift()}this.shift();n=n.makeSubStream(s,r,e);t&&(n=t.createStream(n,r));n=this.filter(n,e,r);n.dict=e;return n}filter(e,t,a){let n=t.get("F","Filter"),s=t.get("DP","DecodeParms");if(n instanceof Name){Array.isArray(s)&&warn("/DecodeParms should not be an Array, when /Filter is a Name.");return this.makeFilter(e,n.name,a,s)}let r=a;if(Array.isArray(n)){const t=n,a=s;for(let i=0,o=t.length;i=48&&e<=57?15&e:e>=65&&e<=70||e>=97&&e<=102?9+(15&e):-1}class Lexer{constructor(e,t=null){this.stream=e;this.nextChar();this.strBuf=[];this.knownCommands=t;this._hexStringNumWarn=0;this.beginInlineImagePos=-1}nextChar(){return this.currentChar=this.stream.getByte()}peekChar(){return this.stream.peekByte()}getNumber(){let e=this.currentChar,t=0,a=1;if(45===e){a=-1;e=this.nextChar();45===e&&(e=this.nextChar())}else 43===e&&(e=this.nextChar());if(10===e||13===e)do{e=this.nextChar()}while(10===e||13===e);if(46===e){t=10;e=this.nextChar()}if(e<48||e>57){const t=`Invalid number: ${String.fromCharCode(e)} (charCode ${e})`;if(isWhiteSpace(e)||40===e||60===e||-1===e){info(`Lexer.getNumber - "${t}".`);return 0}throw new FormatError(t)}let n=e-48;for(;(e=this.nextChar())>=0;)if(e>=48&&e<=57){0!==t&&(t*=10);n=10*n+(e-48)}else if(46===e){if(0!==t)break;t=1}else{if(45!==e)break;warn("Badly formatted number: minus sign in the middle")}0!==t&&(n/=t);return a*n}getString(){let e=1,t=!1;const a=this.strBuf;a.length=0;let n=this.nextChar();for(;;){let s=!1;switch(0|n){case-1:warn("Unterminated string");t=!0;break;case 40:++e;a.push("(");break;case 41:if(0===--e){this.nextChar();t=!0}else a.push(")");break;case 92:n=this.nextChar();switch(n){case-1:warn("Unterminated string");t=!0;break;case 110:a.push("\n");break;case 114:a.push("\r");break;case 116:a.push("\t");break;case 98:a.push("\b");break;case 102:a.push("\f");break;case 92:case 40:case 41:a.push(String.fromCharCode(n));break;case 48:case 49:case 50:case 51:case 52:case 53:case 54:case 55:let e=15&n;n=this.nextChar();s=!0;if(n>=48&&n<=55){e=(e<<3)+(15&n);n=this.nextChar();if(n>=48&&n<=55){s=!1;e=(e<<3)+(15&n)}}a.push(String.fromCharCode(e));break;case 13:10===this.peekChar()&&this.nextChar();break;case 10:break;default:a.push(String.fromCharCode(n))}break;default:a.push(String.fromCharCode(n))}if(t)break;s||(n=this.nextChar())}return a.join("")}getName(){let e,t;const a=this.strBuf;a.length=0;for(;(e=this.nextChar())>=0&&!an[e];)if(35===e){e=this.nextChar();if(an[e]){warn("Lexer_getName: NUMBER SIGN (#) should be followed by a hexadecimal number.");a.push("#");break}const n=toHexDigit(e);if(-1!==n){t=e;e=this.nextChar();const s=toHexDigit(e);if(-1===s){warn(`Lexer_getName: Illegal digit (${String.fromCharCode(e)}) in hexadecimal number.`);a.push("#",String.fromCharCode(t));if(an[e])break;a.push(String.fromCharCode(e));continue}a.push(String.fromCharCode(n<<4|s))}else a.push("#",String.fromCharCode(e))}else a.push(String.fromCharCode(e));a.length>127&&warn(`Name token is longer than allowed by the spec: ${a.length}`);return Name.get(a.join(""))}_hexStringWarn(e){5!==this._hexStringNumWarn++?this._hexStringNumWarn>5||warn(`getHexString - ignoring invalid character: ${e}`):warn("getHexString - ignoring additional invalid characters.")}getHexString(){const e=this.strBuf;e.length=0;let t=this.currentChar,a=-1,n=-1;this._hexStringNumWarn=0;for(;;){if(t<0){warn("Unterminated hex string");break}if(62===t){this.nextChar();break}if(1!==an[t]){n=toHexDigit(t);if(-1===n)this._hexStringWarn(t);else if(-1===a)a=n;else{e.push(String.fromCharCode(a<<4|n));a=-1}t=this.nextChar()}else t=this.nextChar()}-1!==a&&e.push(String.fromCharCode(a<<4));return e.join("")}getObj(){let e=!1,t=this.currentChar;for(;;){if(t<0)return oa;if(e)10!==t&&13!==t||(e=!1);else if(37===t)e=!0;else if(1!==an[t])break;t=this.nextChar()}switch(0|t){case 48:case 49:case 50:case 51:case 52:case 53:case 54:case 55:case 56:case 57:case 43:case 45:case 46:return this.getNumber();case 40:return this.getString();case 47:return this.getName();case 91:this.nextChar();return Cmd.get("[");case 93:this.nextChar();return Cmd.get("]");case 60:t=this.nextChar();if(60===t){this.nextChar();return Cmd.get("<<")}return this.getHexString();case 62:t=this.nextChar();if(62===t){this.nextChar();return Cmd.get(">>")}return Cmd.get(">");case 123:this.nextChar();return Cmd.get("{");case 125:this.nextChar();return Cmd.get("}");case 41:this.nextChar();throw new FormatError(`Illegal character: ${t}`)}let a=String.fromCharCode(t);if(t<32||t>127){const e=this.peekChar();if(e>=32&&e<=127){this.nextChar();return Cmd.get(a)}}const n=this.knownCommands;let s=void 0!==n?.[a];for(;(t=this.nextChar())>=0&&!an[t];){const e=a+String.fromCharCode(t);if(s&&void 0===n[e])break;if(128===a.length)throw new FormatError(`Command token too long: ${a.length}`);a=e;s=void 0!==n?.[a]}if("true"===a)return!0;if("false"===a)return!1;if("null"===a)return null;"BI"===a&&(this.beginInlineImagePos=this.stream.pos);return Cmd.get(a)}skipToNextLine(){let e=this.currentChar;for(;e>=0;){if(13===e){e=this.nextChar();10===e&&this.nextChar();break}if(10===e){this.nextChar();break}e=this.nextChar()}}}class Linearization{static create(e){function getInt(e,t,a=!1){const n=e.get(t);if(Number.isInteger(n)&&(a?n>=0:n>0))return n;throw new Error(`The "${t}" parameter in the linearization dictionary is invalid.`)}const t=new Parser({lexer:new Lexer(e),xref:null}),a=t.getObj(),n=t.getObj(),s=t.getObj(),r=t.getObj();let i,o;if(!(Number.isInteger(a)&&Number.isInteger(n)&&isCmd(s,"obj")&&r instanceof Dict&&"number"==typeof(i=r.get("Linearized"))&&i>0))return null;if((o=getInt(r,"L"))!==e.length)throw new Error('The "L" parameter in the linearization dictionary does not equal the stream length.');return{length:o,hints:function getHints(e){const t=e.get("H");let a;if(Array.isArray(t)&&(2===(a=t.length)||4===a)){for(let e=0;e0))throw new Error(`Hint (${e}) in the linearization dictionary is invalid.`)}return t}throw new Error("Hint array in the linearization dictionary is invalid.")}(r),objectNumberFirst:getInt(r,"O"),endFirst:getInt(r,"E"),numPages:getInt(r,"N"),mainXRefEntriesOffset:getInt(r,"T"),pageFirst:r.has("P")?getInt(r,"P",!0):0}}}const nn=["Adobe-GB1-UCS2","Adobe-CNS1-UCS2","Adobe-Japan1-UCS2","Adobe-Korea1-UCS2","78-EUC-H","78-EUC-V","78-H","78-RKSJ-H","78-RKSJ-V","78-V","78ms-RKSJ-H","78ms-RKSJ-V","83pv-RKSJ-H","90ms-RKSJ-H","90ms-RKSJ-V","90msp-RKSJ-H","90msp-RKSJ-V","90pv-RKSJ-H","90pv-RKSJ-V","Add-H","Add-RKSJ-H","Add-RKSJ-V","Add-V","Adobe-CNS1-0","Adobe-CNS1-1","Adobe-CNS1-2","Adobe-CNS1-3","Adobe-CNS1-4","Adobe-CNS1-5","Adobe-CNS1-6","Adobe-GB1-0","Adobe-GB1-1","Adobe-GB1-2","Adobe-GB1-3","Adobe-GB1-4","Adobe-GB1-5","Adobe-Japan1-0","Adobe-Japan1-1","Adobe-Japan1-2","Adobe-Japan1-3","Adobe-Japan1-4","Adobe-Japan1-5","Adobe-Japan1-6","Adobe-Korea1-0","Adobe-Korea1-1","Adobe-Korea1-2","B5-H","B5-V","B5pc-H","B5pc-V","CNS-EUC-H","CNS-EUC-V","CNS1-H","CNS1-V","CNS2-H","CNS2-V","ETHK-B5-H","ETHK-B5-V","ETen-B5-H","ETen-B5-V","ETenms-B5-H","ETenms-B5-V","EUC-H","EUC-V","Ext-H","Ext-RKSJ-H","Ext-RKSJ-V","Ext-V","GB-EUC-H","GB-EUC-V","GB-H","GB-V","GBK-EUC-H","GBK-EUC-V","GBK2K-H","GBK2K-V","GBKp-EUC-H","GBKp-EUC-V","GBT-EUC-H","GBT-EUC-V","GBT-H","GBT-V","GBTpc-EUC-H","GBTpc-EUC-V","GBpc-EUC-H","GBpc-EUC-V","H","HKdla-B5-H","HKdla-B5-V","HKdlb-B5-H","HKdlb-B5-V","HKgccs-B5-H","HKgccs-B5-V","HKm314-B5-H","HKm314-B5-V","HKm471-B5-H","HKm471-B5-V","HKscs-B5-H","HKscs-B5-V","Hankaku","Hiragana","KSC-EUC-H","KSC-EUC-V","KSC-H","KSC-Johab-H","KSC-Johab-V","KSC-V","KSCms-UHC-H","KSCms-UHC-HW-H","KSCms-UHC-HW-V","KSCms-UHC-V","KSCpc-EUC-H","KSCpc-EUC-V","Katakana","NWP-H","NWP-V","RKSJ-H","RKSJ-V","Roman","UniCNS-UCS2-H","UniCNS-UCS2-V","UniCNS-UTF16-H","UniCNS-UTF16-V","UniCNS-UTF32-H","UniCNS-UTF32-V","UniCNS-UTF8-H","UniCNS-UTF8-V","UniGB-UCS2-H","UniGB-UCS2-V","UniGB-UTF16-H","UniGB-UTF16-V","UniGB-UTF32-H","UniGB-UTF32-V","UniGB-UTF8-H","UniGB-UTF8-V","UniJIS-UCS2-H","UniJIS-UCS2-HW-H","UniJIS-UCS2-HW-V","UniJIS-UCS2-V","UniJIS-UTF16-H","UniJIS-UTF16-V","UniJIS-UTF32-H","UniJIS-UTF32-V","UniJIS-UTF8-H","UniJIS-UTF8-V","UniJIS2004-UTF16-H","UniJIS2004-UTF16-V","UniJIS2004-UTF32-H","UniJIS2004-UTF32-V","UniJIS2004-UTF8-H","UniJIS2004-UTF8-V","UniJISPro-UCS2-HW-V","UniJISPro-UCS2-V","UniJISPro-UTF8-V","UniJISX0213-UTF32-H","UniJISX0213-UTF32-V","UniJISX02132004-UTF32-H","UniJISX02132004-UTF32-V","UniKS-UCS2-H","UniKS-UCS2-V","UniKS-UTF16-H","UniKS-UTF16-V","UniKS-UTF32-H","UniKS-UTF32-V","UniKS-UTF8-H","UniKS-UTF8-V","V","WP-Symbol"],sn=2**24-1;class CMap{constructor(e=!1){this.codespaceRanges=[[],[],[],[]];this.numCodespaceRanges=0;this._map=[];this.name="";this.vertical=!1;this.useCMap=null;this.builtInCMap=e}addCodespaceRange(e,t,a){this.codespaceRanges[e-1].push(t,a);this.numCodespaceRanges++}mapCidRange(e,t,a){if(t-e>sn)throw new Error("mapCidRange - ignoring data above MAX_MAP_RANGE.");for(;e<=t;)this._map[e++]=a++}mapBfRange(e,t,a){if(t-e>sn)throw new Error("mapBfRange - ignoring data above MAX_MAP_RANGE.");const n=a.length-1;for(;e<=t;){this._map[e++]=a;const t=a.charCodeAt(n)+1;t>255?a=a.substring(0,n-1)+String.fromCharCode(a.charCodeAt(n-1)+1)+"\0":a=a.substring(0,n)+String.fromCharCode(t)}}mapBfRangeToArray(e,t,a){if(t-e>sn)throw new Error("mapBfRangeToArray - ignoring data above MAX_MAP_RANGE.");const n=a.length;let s=0;for(;e<=t&&s>>0;const i=s[r];for(let e=0,t=i.length;e=t&&n<=s){a.charcode=n;a.length=r+1;return}}}a.charcode=0;a.length=1}getCharCodeLength(e){const t=this.codespaceRanges;for(let a=0,n=t.length;a=s&&e<=r)return a+1}}return 1}get length(){return this._map.length}get isIdentityCMap(){if("Identity-H"!==this.name&&"Identity-V"!==this.name)return!1;if(65536!==this._map.length)return!1;for(let e=0;e<65536;e++)if(this._map[e]!==e)return!1;return!0}}class IdentityCMap extends CMap{constructor(e,t){super();this.vertical=e;this.addCodespaceRange(t,0,65535)}mapCidRange(e,t,a){unreachable("should not call mapCidRange")}mapBfRange(e,t,a){unreachable("should not call mapBfRange")}mapBfRangeToArray(e,t,a){unreachable("should not call mapBfRangeToArray")}mapOne(e,t){unreachable("should not call mapCidOne")}lookup(e){return Number.isInteger(e)&&e<=65535?e:void 0}contains(e){return Number.isInteger(e)&&e<=65535}forEach(e){for(let t=0;t<=65535;t++)e(t,t)}charCodeOf(e){return Number.isInteger(e)&&e<=65535?e:-1}getMap(){const e=new Array(65536);for(let t=0;t<=65535;t++)e[t]=t;return e}get length(){return 65536}get isIdentityCMap(){unreachable("should not access .isIdentityCMap")}}function strToInt(e){let t=0;for(let a=0;a>>0}function expectString(e){if("string"!=typeof e)throw new FormatError("Malformed CMap: expected string.")}function expectInt(e){if(!Number.isInteger(e))throw new FormatError("Malformed CMap: expected int.")}function parseBfChar(e,t){for(;;){let a=t.getObj();if(a===oa)break;if(isCmd(a,"endbfchar"))return;expectString(a);const n=strToInt(a);a=t.getObj();expectString(a);const s=a;e.mapOne(n,s)}}function parseBfRange(e,t){for(;;){let a=t.getObj();if(a===oa)break;if(isCmd(a,"endbfrange"))return;expectString(a);const n=strToInt(a);a=t.getObj();expectString(a);const s=strToInt(a);a=t.getObj();if(Number.isInteger(a)||"string"==typeof a){const t=Number.isInteger(a)?String.fromCharCode(a):a;e.mapBfRange(n,s,t)}else{if(!isCmd(a,"["))break;{a=t.getObj();const r=[];for(;!isCmd(a,"]")&&a!==oa;){r.push(a);a=t.getObj()}e.mapBfRangeToArray(n,s,r)}}}throw new FormatError("Invalid bf range.")}function parseCidChar(e,t){for(;;){let a=t.getObj();if(a===oa)break;if(isCmd(a,"endcidchar"))return;expectString(a);const n=strToInt(a);a=t.getObj();expectInt(a);const s=a;e.mapOne(n,s)}}function parseCidRange(e,t){for(;;){let a=t.getObj();if(a===oa)break;if(isCmd(a,"endcidrange"))return;expectString(a);const n=strToInt(a);a=t.getObj();expectString(a);const s=strToInt(a);a=t.getObj();expectInt(a);const r=a;e.mapCidRange(n,s,r)}}function parseCodespaceRange(e,t){for(;;){let a=t.getObj();if(a===oa)break;if(isCmd(a,"endcodespacerange"))return;if("string"!=typeof a)break;const n=strToInt(a);a=t.getObj();if("string"!=typeof a)break;const s=strToInt(a);e.addCodespaceRange(a.length,n,s)}throw new FormatError("Invalid codespace range.")}function parseWMode(e,t){const a=t.getObj();Number.isInteger(a)&&(e.vertical=!!a)}function parseCMapName(e,t){const a=t.getObj();a instanceof Name&&(e.name=a.name)}async function parseCMap(e,t,a,n){let s,r;e:for(;;)try{const a=t.getObj();if(a===oa)break;if(a instanceof Name){"WMode"===a.name?parseWMode(e,t):"CMapName"===a.name&&parseCMapName(e,t);s=a}else if(a instanceof Cmd)switch(a.cmd){case"endcmap":break e;case"usecmap":s instanceof Name&&(r=s.name);break;case"begincodespacerange":parseCodespaceRange(e,t);break;case"beginbfchar":parseBfChar(e,t);break;case"begincidchar":parseCidChar(e,t);break;case"beginbfrange":parseBfRange(e,t);break;case"begincidrange":parseCidRange(e,t)}}catch(e){if(e instanceof MissingDataException)throw e;warn("Invalid cMap data: "+e);continue}!n&&r&&(n=r);return n?extendCMap(e,a,n):e}async function extendCMap(e,t,a){e.useCMap=await createBuiltInCMap(a,t);if(0===e.numCodespaceRanges){const t=e.useCMap.codespaceRanges;for(let a=0;aextendCMap(s,t,e));const r=new Lexer(new Stream(a));return parseCMap(s,r,t,null)}class CMapFactory{static async create({encoding:e,fetchBuiltInCMap:t,useCMap:a}){if(e instanceof Name)return createBuiltInCMap(e.name,t);if(e instanceof BaseStream){if(e.isAsync){const t=await e.asyncGetBytes();t&&(e=new Stream(t,0,t.length,e.dict))}const n=await parseCMap(new CMap,new Lexer(e),t,a);return n.isIdentityCMap?createBuiltInCMap(n.name,t):n}throw new Error("Encoding required.")}}class CSS_FONT_INFO{static strings=["fontFamily","fontWeight","italicAngle"]}class SYSTEM_FONT_INFO{static strings=["css","loadedName","baseFontName","src"]}class FONT_INFO{static bools=["black","bold","disableFontFace","fontExtraProperties","isInvalidPDFjsFont","isType3Font","italic","missingFile","remeasure","vertical"];static numbers=["ascent","defaultWidth","descent"];static strings=["fallbackName","loadedName","mimetype","name"];static OFFSET_NUMBERS=Math.ceil(2*this.bools.length/8);static OFFSET_BBOX=this.OFFSET_NUMBERS+8*this.numbers.length;static OFFSET_FONT_MATRIX=this.OFFSET_BBOX+1+8;static OFFSET_DEFAULT_VMETRICS=this.OFFSET_FONT_MATRIX+1+48;static OFFSET_STRINGS=this.OFFSET_DEFAULT_VMETRICS+1+6}class PATTERN_INFO{static KIND=0;static HAS_BBOX=1;static HAS_BACKGROUND=2;static SHADING_TYPE=3;static N_COORD=4;static N_COLOR=8;static N_STOP=12;static N_FIGURES=16}function compileFontInfo(e){const t=e.systemFontInfo?function compileSystemFontInfo(e){const t=new TextEncoder,a={};let n=0;for(const s of SYSTEM_FONT_INFO.strings){const r=t.encode(e[s]);a[s]=r;n+=4+r.length}n+=4;let s,r,i=1+n;if(e.style){s=t.encode(e.style.style);r=t.encode(e.style.weight);i+=4+s.length+4+r.length}const o=new ArrayBuffer(i),l=new Uint8Array(o),f=new DataView(o);let c=0;f.setUint8(c++,e.guessFallback?1:0);f.setUint32(c,0);c+=4;n=0;for(const e of SYSTEM_FONT_INFO.strings){const t=a[e],s=t.length;n+=4+s;f.setUint32(c,s);l.set(t,c+4);c+=4+s}f.setUint32(c-n-4,n);if(e.style){f.setUint32(c,s.length);l.set(s,c+4);c+=4+s.length;f.setUint32(c,r.length);l.set(r,c+4);c+=4+r.length}assert(c<=o.byteLength,"compileSystemFontInfo: Buffer overflow");return o.transferToFixedLength(c)}(e.systemFontInfo):null,a=e.cssFontInfo?function compileCssFontInfo(e){const t=new TextEncoder,a={};let n=0;for(const s of CSS_FONT_INFO.strings){const r=t.encode(e[s]);a[s]=r;n+=4+r.length}const s=new ArrayBuffer(n),r=new Uint8Array(s),i=new DataView(s);let o=0;for(const e of CSS_FONT_INFO.strings){const t=a[e],n=t.length;i.setUint32(o,n);r.set(t,o+4);o+=4+n}assert(o===s.byteLength,"compileCssFontInfo: Buffer overflow");return s}(e.cssFontInfo):null,n=new TextEncoder,s={};let r=0;for(const t of FONT_INFO.strings){s[t]=n.encode(e[t]);r+=4+s[t].length}const i=FONT_INFO.OFFSET_STRINGS+4+r+4+(t?.byteLength??0)+4+(a?.byteLength??0)+4+(e.data?.length??0),o=new ArrayBuffer(i),l=new Uint8Array(o),f=new DataView(o);let c=0;const h=FONT_INFO.bools.length;let u=0,m=0;for(let t=0;t=5&&t<=7))return-1;n=e.substring(1)}if(n===n.toUpperCase()){a=parseInt(n,16);if(a>=0)return a}}return-1}const gn=[[0,127],[128,255],[256,383],[384,591],[592,687,7424,7551,7552,7615],[688,767,42752,42783],[768,879,7616,7679],[880,1023],[11392,11519],[1024,1279,1280,1327,11744,11775,42560,42655],[1328,1423],[1424,1535],[42240,42559],[1536,1791,1872,1919],[1984,2047],[2304,2431],[2432,2559],[2560,2687],[2688,2815],[2816,2943],[2944,3071],[3072,3199],[3200,3327],[3328,3455],[3584,3711],[3712,3839],[4256,4351,11520,11567],[6912,7039],[4352,4607],[7680,7935,11360,11391,42784,43007],[7936,8191],[8192,8303,11776,11903],[8304,8351],[8352,8399],[8400,8447],[8448,8527],[8528,8591],[8592,8703,10224,10239,10496,10623,11008,11263],[8704,8959,10752,11007,10176,10223,10624,10751],[8960,9215],[9216,9279],[9280,9311],[9312,9471],[9472,9599],[9600,9631],[9632,9727],[9728,9983],[9984,10175],[12288,12351],[12352,12447],[12448,12543,12784,12799],[12544,12591,12704,12735],[12592,12687],[43072,43135],[12800,13055],[13056,13311],[44032,55215],[55296,57343],[67840,67871],[19968,40959,11904,12031,12032,12255,12272,12287,13312,19903,131072,173791,12688,12703],[57344,63743],[12736,12783,63744,64255,194560,195103],[64256,64335],[64336,65023],[65056,65071],[65040,65055],[65104,65135],[65136,65279],[65280,65519],[65520,65535],[3840,4095],[1792,1871],[1920,1983],[3456,3583],[4096,4255],[4608,4991,4992,5023,11648,11743],[5024,5119],[5120,5759],[5760,5791],[5792,5887],[6016,6143],[6144,6319],[10240,10495],[40960,42127],[5888,5919,5920,5951,5952,5983,5984,6015],[66304,66351],[66352,66383],[66560,66639],[118784,119039,119040,119295,119296,119375],[119808,120831],[1044480,1048573],[65024,65039,917760,917999],[917504,917631],[6400,6479],[6480,6527],[6528,6623],[6656,6687],[11264,11359],[11568,11647],[19904,19967],[43008,43055],[65536,65663,65664,65791,65792,65855],[65856,65935],[66432,66463],[66464,66527],[66640,66687],[66688,66735],[67584,67647],[68096,68191],[119552,119647],[73728,74751,74752,74879],[119648,119679],[7040,7103],[7168,7247],[7248,7295],[43136,43231],[43264,43311],[43312,43359],[43520,43615],[65936,65999],[66e3,66047],[66208,66271,66176,66207,67872,67903],[127024,127135,126976,127023]];function getUnicodeRangeFor(e,t=-1){if(-1!==t){const a=gn[t];for(let n=0,s=a.length;n=a[n]&&e<=a[n+1])return t}for(let t=0,a=gn.length;t=a[n]&&e<=a[n+1])return t}return-1}const bn=new RegExp("^(\\s)|(\\p{Mn})|(\\p{Cf})$","u"),wn=new Map;const jn=!0,kn=1,yn=2,qn=4,vn=32,Sn=[".notdef",".null","nonmarkingreturn","space","exclam","quotedbl","numbersign","dollar","percent","ampersand","quotesingle","parenleft","parenright","asterisk","plus","comma","hyphen","period","slash","zero","one","two","three","four","five","six","seven","eight","nine","colon","semicolon","less","equal","greater","question","at","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","bracketleft","backslash","bracketright","asciicircum","underscore","grave","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","braceleft","bar","braceright","asciitilde","Adieresis","Aring","Ccedilla","Eacute","Ntilde","Odieresis","Udieresis","aacute","agrave","acircumflex","adieresis","atilde","aring","ccedilla","eacute","egrave","ecircumflex","edieresis","iacute","igrave","icircumflex","idieresis","ntilde","oacute","ograve","ocircumflex","odieresis","otilde","uacute","ugrave","ucircumflex","udieresis","dagger","degree","cent","sterling","section","bullet","paragraph","germandbls","registered","copyright","trademark","acute","dieresis","notequal","AE","Oslash","infinity","plusminus","lessequal","greaterequal","yen","mu","partialdiff","summation","product","pi","integral","ordfeminine","ordmasculine","Omega","ae","oslash","questiondown","exclamdown","logicalnot","radical","florin","approxequal","Delta","guillemotleft","guillemotright","ellipsis","nonbreakingspace","Agrave","Atilde","Otilde","OE","oe","endash","emdash","quotedblleft","quotedblright","quoteleft","quoteright","divide","lozenge","ydieresis","Ydieresis","fraction","currency","guilsinglleft","guilsinglright","fi","fl","daggerdbl","periodcentered","quotesinglbase","quotedblbase","perthousand","Acircumflex","Ecircumflex","Aacute","Edieresis","Egrave","Iacute","Icircumflex","Idieresis","Igrave","Oacute","Ocircumflex","apple","Ograve","Uacute","Ucircumflex","Ugrave","dotlessi","circumflex","tilde","macron","breve","dotaccent","ring","cedilla","hungarumlaut","ogonek","caron","Lslash","lslash","Scaron","scaron","Zcaron","zcaron","brokenbar","Eth","eth","Yacute","yacute","Thorn","thorn","minus","multiply","onesuperior","twosuperior","threesuperior","onehalf","onequarter","threequarters","franc","Gbreve","gbreve","Idotaccent","Scedilla","scedilla","Cacute","cacute","Ccaron","ccaron","dcroat"];function recoverGlyphName(e,t){if(void 0!==t[e])return e;const a=getUnicodeForGlyph(e,t);if(-1!==a)for(const e in t)if(t[e]===a)return e;info("Unable to recover a standard glyph name for: "+e);return e}function type1FontGlyphMapping(e,t,a){const n=Object.create(null);let s,r,i;const o=!!(e.flags&qn);if(e.isInternalFont){i=t;for(r=0;r=0?s:0}}else if(e.baseEncodingName){i=getEncoding(e.baseEncodingName);for(r=0;r=0?s:0}}else if(o)for(r in t)n[r]=t[r];else{i=fn;for(r=0;r=0?s:0}}const l=e.differences;let f;if(l)for(r in l){const e=l[r];s=a.indexOf(e);if(-1===s){f||(f=mn());const t=recoverGlyphName(e,f);t!==e&&(s=a.indexOf(t))}n[r]=s>=0?s:0}return n}function normalizeFontName(e){return e.replaceAll(/[,_]/g,"-").replaceAll(/\s/g,"")}const An=getLookupTableFactory(e=>{e[8211]=65074;e[8212]=65073;e[8229]=65072;e[8230]=65049;e[12289]=65041;e[12290]=65042;e[12296]=65087;e[12297]=65088;e[12298]=65085;e[12299]=65086;e[12300]=65089;e[12301]=65090;e[12302]=65091;e[12303]=65092;e[12304]=65083;e[12305]=65084;e[12308]=65081;e[12309]=65082;e[12310]=65047;e[12311]=65048;e[65103]=65076;e[65281]=65045;e[65288]=65077;e[65289]=65078;e[65292]=65040;e[65306]=65043;e[65307]=65044;e[65311]=65046;e[65339]=65095;e[65341]=65096;e[65343]=65075;e[65371]=65079;e[65373]=65080});const xn=[".notdef","space","exclam","quotedbl","numbersign","dollar","percent","ampersand","quoteright","parenleft","parenright","asterisk","plus","comma","hyphen","period","slash","zero","one","two","three","four","five","six","seven","eight","nine","colon","semicolon","less","equal","greater","question","at","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","bracketleft","backslash","bracketright","asciicircum","underscore","quoteleft","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","braceleft","bar","braceright","asciitilde","exclamdown","cent","sterling","fraction","yen","florin","section","currency","quotesingle","quotedblleft","guillemotleft","guilsinglleft","guilsinglright","fi","fl","endash","dagger","daggerdbl","periodcentered","paragraph","bullet","quotesinglbase","quotedblbase","quotedblright","guillemotright","ellipsis","perthousand","questiondown","grave","acute","circumflex","tilde","macron","breve","dotaccent","dieresis","ring","cedilla","hungarumlaut","ogonek","caron","emdash","AE","ordfeminine","Lslash","Oslash","OE","ordmasculine","ae","dotlessi","lslash","oslash","oe","germandbls","onesuperior","logicalnot","mu","trademark","Eth","onehalf","plusminus","Thorn","onequarter","divide","brokenbar","degree","thorn","threequarters","twosuperior","registered","minus","eth","multiply","threesuperior","copyright","Aacute","Acircumflex","Adieresis","Agrave","Aring","Atilde","Ccedilla","Eacute","Ecircumflex","Edieresis","Egrave","Iacute","Icircumflex","Idieresis","Igrave","Ntilde","Oacute","Ocircumflex","Odieresis","Ograve","Otilde","Scaron","Uacute","Ucircumflex","Udieresis","Ugrave","Yacute","Ydieresis","Zcaron","aacute","acircumflex","adieresis","agrave","aring","atilde","ccedilla","eacute","ecircumflex","edieresis","egrave","iacute","icircumflex","idieresis","igrave","ntilde","oacute","ocircumflex","odieresis","ograve","otilde","scaron","uacute","ucircumflex","udieresis","ugrave","yacute","ydieresis","zcaron"],Cn=[".notdef","space","exclamsmall","Hungarumlautsmall","dollaroldstyle","dollarsuperior","ampersandsmall","Acutesmall","parenleftsuperior","parenrightsuperior","twodotenleader","onedotenleader","comma","hyphen","period","fraction","zerooldstyle","oneoldstyle","twooldstyle","threeoldstyle","fouroldstyle","fiveoldstyle","sixoldstyle","sevenoldstyle","eightoldstyle","nineoldstyle","colon","semicolon","commasuperior","threequartersemdash","periodsuperior","questionsmall","asuperior","bsuperior","centsuperior","dsuperior","esuperior","isuperior","lsuperior","msuperior","nsuperior","osuperior","rsuperior","ssuperior","tsuperior","ff","fi","fl","ffi","ffl","parenleftinferior","parenrightinferior","Circumflexsmall","hyphensuperior","Gravesmall","Asmall","Bsmall","Csmall","Dsmall","Esmall","Fsmall","Gsmall","Hsmall","Ismall","Jsmall","Ksmall","Lsmall","Msmall","Nsmall","Osmall","Psmall","Qsmall","Rsmall","Ssmall","Tsmall","Usmall","Vsmall","Wsmall","Xsmall","Ysmall","Zsmall","colonmonetary","onefitted","rupiah","Tildesmall","exclamdownsmall","centoldstyle","Lslashsmall","Scaronsmall","Zcaronsmall","Dieresissmall","Brevesmall","Caronsmall","Dotaccentsmall","Macronsmall","figuredash","hypheninferior","Ogoneksmall","Ringsmall","Cedillasmall","onequarter","onehalf","threequarters","questiondownsmall","oneeighth","threeeighths","fiveeighths","seveneighths","onethird","twothirds","zerosuperior","onesuperior","twosuperior","threesuperior","foursuperior","fivesuperior","sixsuperior","sevensuperior","eightsuperior","ninesuperior","zeroinferior","oneinferior","twoinferior","threeinferior","fourinferior","fiveinferior","sixinferior","seveninferior","eightinferior","nineinferior","centinferior","dollarinferior","periodinferior","commainferior","Agravesmall","Aacutesmall","Acircumflexsmall","Atildesmall","Adieresissmall","Aringsmall","AEsmall","Ccedillasmall","Egravesmall","Eacutesmall","Ecircumflexsmall","Edieresissmall","Igravesmall","Iacutesmall","Icircumflexsmall","Idieresissmall","Ethsmall","Ntildesmall","Ogravesmall","Oacutesmall","Ocircumflexsmall","Otildesmall","Odieresissmall","OEsmall","Oslashsmall","Ugravesmall","Uacutesmall","Ucircumflexsmall","Udieresissmall","Yacutesmall","Thornsmall","Ydieresissmall"],In=[".notdef","space","dollaroldstyle","dollarsuperior","parenleftsuperior","parenrightsuperior","twodotenleader","onedotenleader","comma","hyphen","period","fraction","zerooldstyle","oneoldstyle","twooldstyle","threeoldstyle","fouroldstyle","fiveoldstyle","sixoldstyle","sevenoldstyle","eightoldstyle","nineoldstyle","colon","semicolon","commasuperior","threequartersemdash","periodsuperior","asuperior","bsuperior","centsuperior","dsuperior","esuperior","isuperior","lsuperior","msuperior","nsuperior","osuperior","rsuperior","ssuperior","tsuperior","ff","fi","fl","ffi","ffl","parenleftinferior","parenrightinferior","hyphensuperior","colonmonetary","onefitted","rupiah","centoldstyle","figuredash","hypheninferior","onequarter","onehalf","threequarters","oneeighth","threeeighths","fiveeighths","seveneighths","onethird","twothirds","zerosuperior","onesuperior","twosuperior","threesuperior","foursuperior","fivesuperior","sixsuperior","sevensuperior","eightsuperior","ninesuperior","zeroinferior","oneinferior","twoinferior","threeinferior","fourinferior","fiveinferior","sixinferior","seveninferior","eightinferior","nineinferior","centinferior","dollarinferior","periodinferior","commainferior"],Fn=[".notdef","space","exclam","quotedbl","numbersign","dollar","percent","ampersand","quoteright","parenleft","parenright","asterisk","plus","comma","hyphen","period","slash","zero","one","two","three","four","five","six","seven","eight","nine","colon","semicolon","less","equal","greater","question","at","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","bracketleft","backslash","bracketright","asciicircum","underscore","quoteleft","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","braceleft","bar","braceright","asciitilde","exclamdown","cent","sterling","fraction","yen","florin","section","currency","quotesingle","quotedblleft","guillemotleft","guilsinglleft","guilsinglright","fi","fl","endash","dagger","daggerdbl","periodcentered","paragraph","bullet","quotesinglbase","quotedblbase","quotedblright","guillemotright","ellipsis","perthousand","questiondown","grave","acute","circumflex","tilde","macron","breve","dotaccent","dieresis","ring","cedilla","hungarumlaut","ogonek","caron","emdash","AE","ordfeminine","Lslash","Oslash","OE","ordmasculine","ae","dotlessi","lslash","oslash","oe","germandbls","onesuperior","logicalnot","mu","trademark","Eth","onehalf","plusminus","Thorn","onequarter","divide","brokenbar","degree","thorn","threequarters","twosuperior","registered","minus","eth","multiply","threesuperior","copyright","Aacute","Acircumflex","Adieresis","Agrave","Aring","Atilde","Ccedilla","Eacute","Ecircumflex","Edieresis","Egrave","Iacute","Icircumflex","Idieresis","Igrave","Ntilde","Oacute","Ocircumflex","Odieresis","Ograve","Otilde","Scaron","Uacute","Ucircumflex","Udieresis","Ugrave","Yacute","Ydieresis","Zcaron","aacute","acircumflex","adieresis","agrave","aring","atilde","ccedilla","eacute","ecircumflex","edieresis","egrave","iacute","icircumflex","idieresis","igrave","ntilde","oacute","ocircumflex","odieresis","ograve","otilde","scaron","uacute","ucircumflex","udieresis","ugrave","yacute","ydieresis","zcaron","exclamsmall","Hungarumlautsmall","dollaroldstyle","dollarsuperior","ampersandsmall","Acutesmall","parenleftsuperior","parenrightsuperior","twodotenleader","onedotenleader","zerooldstyle","oneoldstyle","twooldstyle","threeoldstyle","fouroldstyle","fiveoldstyle","sixoldstyle","sevenoldstyle","eightoldstyle","nineoldstyle","commasuperior","threequartersemdash","periodsuperior","questionsmall","asuperior","bsuperior","centsuperior","dsuperior","esuperior","isuperior","lsuperior","msuperior","nsuperior","osuperior","rsuperior","ssuperior","tsuperior","ff","ffi","ffl","parenleftinferior","parenrightinferior","Circumflexsmall","hyphensuperior","Gravesmall","Asmall","Bsmall","Csmall","Dsmall","Esmall","Fsmall","Gsmall","Hsmall","Ismall","Jsmall","Ksmall","Lsmall","Msmall","Nsmall","Osmall","Psmall","Qsmall","Rsmall","Ssmall","Tsmall","Usmall","Vsmall","Wsmall","Xsmall","Ysmall","Zsmall","colonmonetary","onefitted","rupiah","Tildesmall","exclamdownsmall","centoldstyle","Lslashsmall","Scaronsmall","Zcaronsmall","Dieresissmall","Brevesmall","Caronsmall","Dotaccentsmall","Macronsmall","figuredash","hypheninferior","Ogoneksmall","Ringsmall","Cedillasmall","questiondownsmall","oneeighth","threeeighths","fiveeighths","seveneighths","onethird","twothirds","zerosuperior","foursuperior","fivesuperior","sixsuperior","sevensuperior","eightsuperior","ninesuperior","zeroinferior","oneinferior","twoinferior","threeinferior","fourinferior","fiveinferior","sixinferior","seveninferior","eightinferior","nineinferior","centinferior","dollarinferior","periodinferior","commainferior","Agravesmall","Aacutesmall","Acircumflexsmall","Atildesmall","Adieresissmall","Aringsmall","AEsmall","Ccedillasmall","Egravesmall","Eacutesmall","Ecircumflexsmall","Edieresissmall","Igravesmall","Iacutesmall","Icircumflexsmall","Idieresissmall","Ethsmall","Ntildesmall","Ogravesmall","Oacutesmall","Ocircumflexsmall","Otildesmall","Odieresissmall","OEsmall","Oslashsmall","Ugravesmall","Uacutesmall","Ucircumflexsmall","Udieresissmall","Yacutesmall","Thornsmall","Ydieresissmall","001.000","001.001","001.002","001.003","Black","Bold","Book","Light","Medium","Regular","Roman","Semibold"],Tn=391,On=[null,{id:"hstem",min:2,stackClearing:!0,stem:!0},null,{id:"vstem",min:2,stackClearing:!0,stem:!0},{id:"vmoveto",min:1,stackClearing:!0},{id:"rlineto",min:2,resetStack:!0},{id:"hlineto",min:1,resetStack:!0},{id:"vlineto",min:1,resetStack:!0},{id:"rrcurveto",min:6,resetStack:!0},null,{id:"callsubr",min:1},{id:"return",min:0},null,null,{id:"endchar",min:0,stackClearing:!0},null,null,null,{id:"hstemhm",min:2,stackClearing:!0,stem:!0},{id:"hintmask",min:0,stackClearing:!0},{id:"cntrmask",min:0,stackClearing:!0},{id:"rmoveto",min:2,stackClearing:!0},{id:"hmoveto",min:1,stackClearing:!0},{id:"vstemhm",min:2,stackClearing:!0,stem:!0},{id:"rcurveline",min:8,resetStack:!0},{id:"rlinecurve",min:8,resetStack:!0},{id:"vvcurveto",min:4,resetStack:!0},{id:"hhcurveto",min:4,resetStack:!0},null,{id:"callgsubr",min:1},{id:"vhcurveto",min:4,resetStack:!0},{id:"hvcurveto",min:4,resetStack:!0}],Rn=[null,null,null,{id:"and",min:2,stackDelta:-1},{id:"or",min:2,stackDelta:-1},{id:"not",min:1,stackDelta:0},null,null,null,{id:"abs",min:1,stackDelta:0},{id:"add",min:2,stackDelta:-1,stackFn(e,t){e[t-2]=e[t-2]+e[t-1]}},{id:"sub",min:2,stackDelta:-1,stackFn(e,t){e[t-2]=e[t-2]-e[t-1]}},{id:"div",min:2,stackDelta:-1,stackFn(e,t){e[t-2]=e[t-2]/e[t-1]}},null,{id:"neg",min:1,stackDelta:0,stackFn(e,t){e[t-1]=-e[t-1]}},{id:"eq",min:2,stackDelta:-1},null,null,{id:"drop",min:1,stackDelta:-1},null,{id:"put",min:2,stackDelta:-2},{id:"get",min:1,stackDelta:0},{id:"ifelse",min:4,stackDelta:-3},{id:"random",min:0,stackDelta:1},{id:"mul",min:2,stackDelta:-1,stackFn(e,t){e[t-2]=e[t-2]*e[t-1]}},null,{id:"sqrt",min:1,stackDelta:0},{id:"dup",min:1,stackDelta:1},{id:"exch",min:2,stackDelta:0},{id:"index",min:2,stackDelta:0},{id:"roll",min:3,stackDelta:-2},null,null,null,{id:"hflex",min:7,resetStack:!0},{id:"flex",min:13,resetStack:!0},{id:"hflex1",min:9,resetStack:!0},{id:"flex1",min:11,resetStack:!0}];class CFFParser{constructor(e,t,a){this.bytes=e.getBytes();this.properties=t;this.seacAnalysisEnabled=!!a}parse(){const e=this.properties,t=new CFF(this.bytes.length);this.cff=t;const a=this.parseHeader(),n=this.parseIndex(a.endPos),s=this.parseIndex(n.endPos),r=this.parseIndex(s.endPos),i=this.parseIndex(r.endPos),o=this.parseDict(s.obj.get(0)),l=this.createDict(CFFTopDict,o,t.strings);t.header=a.obj;t.names=this.parseNameIndex(n.obj);t.strings=this.parseStringIndex(r.obj);t.topDict=l;t.globalSubrIndex=i.obj;this.parsePrivateDict(t.topDict);t.isCIDFont=l.hasName("ROS");const f=l.getByName("CharStrings"),c=this.parseIndex(f).obj;t.charStringCount=c.count;const h=l.getByName("FontMatrix");h&&(e.fontMatrix=h);const u=l.getByName("FontBBox");if(u){e.ascent=Math.max(u[3],u[1]);e.descent=Math.min(u[1],u[3]);e.ascentScaled=!0}let m,p;if(t.isCIDFont){const e=this.parseIndex(l.getByName("FDArray")).obj;for(let a=0,n=e.count;a=t)throw new FormatError("Invalid CFF header");if(0!==a){info("cff data is shifted");e=e.subarray(a);this.bytes=e}const n=e[0],s=e[1],r=e[2],i=e[3];return{obj:new CFFHeader(n,s,r,i),endPos:r}}parseDict(e){const t=new DataView(e.buffer,e.byteOffset,e.bytesLength);let a=0;function parseOperand(){let n=e[a++];if(30===n)return function parseFloatOperand(){let t="";const n=15,s=["0","1","2","3","4","5","6","7","8","9",".","E","E-",null,"-"],r=e.length;for(;a>4,o=15&r;if(i===n)break;t+=s[i];if(o===n)break;t+=s[o]}return parseFloat(t)}();if(28===n){n=t.getInt16(a);a+=2;return n}if(29===n){n=t.getInt32(a);a+=4;return n}if(n>=32&&n<=246)return n-139;if(n>=247&&n<=250)return 256*(n-247)+e[a++]+108;if(n>=251&&n<=254)return-256*(n-251)-e[a++]-108;warn(`CFFParser.parseDict: "${n}" is a reserved command.`);return NaN}let n=[];const s=[];a=0;const r=e.length;for(;a10)return!1;const s=new DataView(t.buffer,t.byteOffset,t.bytesLength);let r=e.stackSize;const i=e.stack;let o=t.length;for(let l=0;l=4){r-=4;if(this.seacAnalysisEnabled){e.seac=i.slice(r,r+4);return!1}}c=On[f]}else if(f>=32&&f<=246){i[r]=f-139;r++}else if(f>=247&&f<=254){i[r]=f<251?(f-247<<8)+t[l]+108:-(f-251<<8)-t[l]-108;l++;r++}else if(255===f){i[r]=s.getInt32(l)/65536;l+=4;r++}else if(19===f||20===f){e.hints+=r>>1;if(0===e.hints){t.copyWithin(l-1,l,-1);l-=1;o-=1;continue}l+=e.hints+7>>3;r%=2;c=On[f]}else{if(10===f||29===f){const t=10===f?a:n;if(!t){c=On[f];warn("Missing subrsIndex for "+c.id);return!1}let s=32768;t.count<1240?s=107:t.count<33900&&(s=1131);const o=i[--r]+s;if(o<0||o>=t.count||isNaN(o)){c=On[f];warn("Out of bounds subrIndex for "+c.id);return!1}e.stackSize=r;e.callDepth++;if(!this.parseCharString(e,t.get(o),a,n))return!1;e.callDepth--;r=e.stackSize;continue}if(11===f){e.stackSize=r;return!0}if(0===f&&l===t.length){t[l-1]=14;c=On[14]}else{if(9===f){t.copyWithin(l-1,l,-1);l-=1;o-=1;continue}c=On[f]}}if(c){if(c.stem){e.hints+=r>>1;if(3===f||23===f)e.hasVStems=!0;else if(e.hasVStems&&(1===f||18===f)){warn("CFF stem hints are in wrong order");t[l-1]=1===f?3:23}}if(r=2&&c.stem?r%=2:r>1&&warn("Found too many parameters for stack-clearing command");r>0&&(e.width=i[r-1])}if("stackDelta"in c){"stackFn"in c&&c.stackFn(i,r);r+=c.stackDelta}else(c.stackClearing||c.resetStack)&&(r=0)}}o=s.length){warn("Invalid fd index for glyph index.");h=!1}if(h){m=s[e].privateDict;u=m.subrsIndex}}else t&&(u=t);h&&(h=this.parseCharString(c,l,u,a));if(null!==c.width){const e=m.getByName("nominalWidthX");o[f]=e+c.width}else{const e=m.getByName("defaultWidthX");o[f]=e}null!==c.seac&&(i[f]=c.seac);h||e.set(f,new Uint8Array([14]))}return{charStrings:e,seacs:i,widths:o}}emptyPrivateDictionary(e){const t=this.createDict(CFFPrivateDict,[],e.strings);e.setByKey(18,[0,0]);e.privateDict=t}parsePrivateDict(e){if(!e.hasName("Private")){this.emptyPrivateDictionary(e);return}const t=e.getByName("Private");if(!Array.isArray(t)||2!==t.length){e.removeByName("Private");return}const a=t[0],n=t[1];if(0===a||n>=this.bytes.length){this.emptyPrivateDictionary(e);return}const s=n+a,r=this.bytes.subarray(n,s),i=this.parseDict(r),o=this.createDict(CFFPrivateDict,i,e.strings);e.privateDict=o;0===o.getByName("ExpansionFactor")&&o.setByName("ExpansionFactor",.06);if(!o.getByName("Subrs"))return;const l=o.getByName("Subrs"),f=n+l;if(0===l||f>=this.bytes.length){this.emptyPrivateDictionary(e);return}const c=this.parseIndex(f);o.subrsIndex=c.obj}parseCharsets(e,t,a,n){if(0===e)return new CFFCharset(!0,Dn.ISO_ADOBE,xn);if(1===e)return new CFFCharset(!0,Dn.EXPERT,Cn);if(2===e)return new CFFCharset(!0,Dn.EXPERT_SUBSET,In);const s=this.bytes,r=e,i=s[e++],o=[n?0:".notdef"];let l,f,c;t-=1;switch(i){case 0:for(c=0;c=65535){warn("Not enough space in charstrings to duplicate first glyph.");return}const e=this.charStrings.get(0);this.charStrings.add(e);this.isCIDFont&&this.fdSelect.fdSelect.push(this.fdSelect.fdSelect[0])}hasGlyphId(e){if(e<0||e>=this.charStrings.count)return!1;return this.charStrings.get(e).length>0}}class CFFHeader{constructor(e,t,a,n){this.major=e;this.minor=t;this.hdrSize=a;this.offSize=n}}class CFFStrings{strings=[];get(e){return e>=0&&e<=390?Fn[e]:e-Tn<=this.strings.length?this.strings[e-Tn]:Fn[0]}getSID(e){let t=Fn.indexOf(e);if(-1!==t)return t;t=this.strings.indexOf(e);return-1!==t?t+Tn:-1}add(e){this.strings.push(e)}get count(){return this.strings.length}}class CFFIndex{objects=[];length=0;add(e){this.length+=e.length;this.objects.push(e)}set(e,t){this.length+=t.length-this.objects[e].length;this.objects[e]=t}get(e){return this.objects[e]}get count(){return this.objects.length}}class CFFDict{constructor(e,t){this.keyToNameMap=e.keyToNameMap;this.nameToKeyMap=e.nameToKeyMap;this.defaults=e.defaults;this.types=e.types;this.opcodes=e.opcodes;this.order=e.order;this.strings=t;this.values=Object.create(null)}setByKey(e,t){if(!(e in this.keyToNameMap))return!1;if(0===t.length)return!0;for(const a of t)if(isNaN(a)){warn(`Invalid CFFDict value: "${t}" for key "${e}".`);return!0}const a=this.types[e];"num"!==a&&"sid"!==a&&"offset"!==a||(t=t[0]);this.values[e]=t;return!0}setByName(e,t){if(!(e in this.nameToKeyMap))throw new FormatError(`Invalid dictionary name "${e}"`);this.values[this.nameToKeyMap[e]]=t}hasName(e){return this.nameToKeyMap[e]in this.values}getByName(e){if(!(e in this.nameToKeyMap))throw new FormatError(`Invalid dictionary name ${e}"`);const t=this.nameToKeyMap[e];return t in this.values?this.values[t]:this.defaults[t]}removeByName(e){delete this.values[this.nameToKeyMap[e]]}static createTables(e){const t={keyToNameMap:{},nameToKeyMap:{},defaults:{},types:{},opcodes:{},order:[]};for(const a of e){const e=Array.isArray(a[0])?(a[0][0]<<8)+a[0][1]:a[0];t.keyToNameMap[e]=a[1];t.nameToKeyMap[a[1]]=e;t.types[e]=a[2];t.defaults[e]=a[3];t.opcodes[e]=Array.isArray(a[0])?a[0]:[a[0]];t.order.push(e)}return t}}const Hn=[[[12,30],"ROS",["sid","sid","num"],null],[[12,20],"SyntheticBase","num",null],[0,"version","sid",null],[1,"Notice","sid",null],[[12,0],"Copyright","sid",null],[2,"FullName","sid",null],[3,"FamilyName","sid",null],[4,"Weight","sid",null],[[12,1],"isFixedPitch","num",0],[[12,2],"ItalicAngle","num",0],[[12,3],"UnderlinePosition","num",-100],[[12,4],"UnderlineThickness","num",50],[[12,5],"PaintType","num",0],[[12,6],"CharstringType","num",2],[[12,7],"FontMatrix",["num","num","num","num","num","num"],[.001,0,0,.001,0,0]],[13,"UniqueID","num",null],[5,"FontBBox",["num","num","num","num"],[0,0,0,0]],[[12,8],"StrokeWidth","num",0],[14,"XUID","array",null],[15,"charset","offset",0],[16,"Encoding","offset",0],[17,"CharStrings","offset",0],[18,"Private",["offset","offset"],null],[[12,21],"PostScript","sid",null],[[12,22],"BaseFontName","sid",null],[[12,23],"BaseFontBlend","delta",null],[[12,31],"CIDFontVersion","num",0],[[12,32],"CIDFontRevision","num",0],[[12,33],"CIDFontType","num",0],[[12,34],"CIDCount","num",8720],[[12,35],"UIDBase","num",null],[[12,37],"FDSelect","offset",null],[[12,36],"FDArray","offset",null],[[12,38],"FontName","sid",null]];class CFFTopDict extends CFFDict{static get tables(){return shadow(this,"tables",this.createTables(Hn))}constructor(e){super(CFFTopDict.tables,e);this.privateDict=null}}const Bn=[[6,"BlueValues","delta",null],[7,"OtherBlues","delta",null],[8,"FamilyBlues","delta",null],[9,"FamilyOtherBlues","delta",null],[[12,9],"BlueScale","num",.039625],[[12,10],"BlueShift","num",7],[[12,11],"BlueFuzz","num",1],[10,"StdHW","num",null],[11,"StdVW","num",null],[[12,12],"StemSnapH","delta",null],[[12,13],"StemSnapV","delta",null],[[12,14],"ForceBold","num",0],[[12,17],"LanguageGroup","num",0],[[12,18],"ExpansionFactor","num",.06],[[12,19],"initialRandomSeed","num",0],[20,"defaultWidthX","num",0],[21,"nominalWidthX","num",0],[19,"Subrs","offset",null]];class CFFPrivateDict extends CFFDict{static get tables(){return shadow(this,"tables",this.createTables(Bn))}constructor(e){super(CFFPrivateDict.tables,e);this.subrsIndex=null}}const Dn={ISO_ADOBE:0,EXPERT:1,EXPERT_SUBSET:2};class CFFCharset{constructor(e,t,a,n){this.predefined=e;this.format=t;this.charset=a;this.raw=n}}class CFFEncoding{constructor(e,t,a,n){this.predefined=e;this.format=t;this.encoding=a;this.raw=n}}class CFFFDSelect{constructor(e,t){this.format=e;this.fdSelect=t}getFDIndex(e){return e<0||e>=this.fdSelect.length?-1:this.fdSelect[e]}}class CFFOffsetTracker{offsets=Object.create(null);isTracking(e){return e in this.offsets}track(e,t){if(e in this.offsets)throw new FormatError(`Already tracking location of ${e}`);this.offsets[e]=t}offset(e){for(const t in this.offsets)this.offsets[t]+=e}setEntryLocation(e,t,a){if(!(e in this.offsets))throw new FormatError(`Not tracking location of ${e}`);const n=a.data,s=this.offsets[e];for(let e=0,a=t.length;e>24&255;n[i]=f>>16&255;n[o]=f>>8&255;n[l]=255&f}}}class CompilerOutput{#V;#$=1024;#Y=0;constructor(e){this.#J(e)}#J(e){for(;this.#$this.#$&&this.#J(t);this.#V.set(e,this.#Y);this.#Y=t}}class CFFCompiler{constructor(e){this.cff=e}compile(){const e=this.cff,t=new CompilerOutput(e.rawFileLength),a=this.compileHeader(e.header);t.add(a);const n=this.compileNameIndex(e.names);t.add(n);if(e.isCIDFont&&e.topDict.hasName("FontMatrix")){const t=e.topDict.getByName("FontMatrix");e.topDict.removeByName("FontMatrix");for(const a of e.fdArray){let e=t.slice(0);a.hasName("FontMatrix")&&(e=Util.transform(e,a.getByName("FontMatrix")));a.setByName("FontMatrix",e)}}const s=e.topDict.getByName("XUID");s?.length>16&&e.topDict.removeByName("XUID");e.topDict.setByName("charset",0);let r=this.compileTopDicts([e.topDict],t.length,e.isCIDFont);t.add(r.output);const i=r.trackers[0],o=this.compileStringIndex(e.strings.strings);t.add(o);const l=this.compileIndex(e.globalSubrIndex);t.add(l);if(e.encoding&&e.topDict.hasName("Encoding"))if(e.encoding.predefined)i.setEntryLocation("Encoding",[e.encoding.format],t);else{const a=this.compileEncoding(e.encoding);i.setEntryLocation("Encoding",[t.length],t);t.add(a)}const f=this.compileCharset(e.charset,e.charStrings.count,e.strings,e.isCIDFont);i.setEntryLocation("charset",[t.length],t);t.add(f);const c=this.compileCharStrings(e.charStrings);i.setEntryLocation("CharStrings",[t.length],t);t.add(c);if(e.isCIDFont){i.setEntryLocation("FDSelect",[t.length],t);const a=this.compileFDSelect(e.fdSelect);t.add(a);r=this.compileTopDicts(e.fdArray,t.length,!0);i.setEntryLocation("FDArray",[t.length],t);t.add(r.output);const n=r.trackers;this.compilePrivateDicts(e.fdArray,n,t)}this.compilePrivateDicts([e.topDict],[i],t);t.add([0]);return t.finalData}encodeNumber(e){return Number.isInteger(e)?this.encodeInteger(e):this.encodeFloat(e)}static get EncodeFloatRegExp(){return shadow(this,"EncodeFloatRegExp",/\.(\d*?)(?:9{5,20}|0{5,20})\d{0,2}(?:e(.+)|$)/)}encodeFloat(e){let t=e.toString();const a=CFFCompiler.EncodeFloatRegExp.exec(t);if(a){const n=parseFloat("1e"+((a[2]?+a[2]:0)+a[1].length));t=(Math.round(e*n)/n).toString()}let n,s,r="";for(n=0,s=t.length;n=-107&&e<=107?[e+139]:e>=108&&e<=1131?[247+((e-=108)>>8),255&e]:e>=-1131&&e<=-108?[251+((e=-e-108)>>8),255&e]:e>=-32768&&e<=32767?[28,e>>8&255,255&e]:[29,e>>24&255,e>>16&255,e>>8&255,255&e];return t}compileHeader(e){return[e.major,e.minor,4,e.offSize]}compileNameIndex(e){const t=new CFFIndex;for(const a of e){const e=Math.min(a.length,127);let n=new Array(e);for(let t=0;t"~"||"["===e||"]"===e||"("===e||")"===e||"{"===e||"}"===e||"<"===e||">"===e||"/"===e||"%"===e)&&(e="_");n[t]=e}n=n.join("");""===n&&(n="Bad_Font_Name");t.add(stringToBytes(n))}return this.compileIndex(t)}compileTopDicts(e,t,a){const n=[];let s=new CFFIndex;for(const r of e){if(a){r.removeByName("CIDFontVersion");r.removeByName("CIDFontRevision");r.removeByName("CIDFontType");r.removeByName("CIDCount");r.removeByName("UIDBase")}const e=new CFFOffsetTracker,i=this.compileDict(r,e);n.push(e);s.add(i);e.offset(t)}s=this.compileIndex(s,n);return{trackers:n,output:s}}compilePrivateDicts(e,t,a){for(let n=0,s=e.length;n>8&255,255&e])}else{s=new Uint8Array(1+2*r);let t=0;const n=e.charset.length;let i=!1;for(let r=1;r>8&255;s[r+1]=255&o}}return s}compileEncoding(e){return e.raw}compileFDSelect(e){const t=e.format;let a,n;switch(t){case 0:a=new Uint8Array(1+e.fdSelect.length);a[0]=t;for(n=0;n>8&255,255&s,r];for(n=1;n>8&255,255&n,t);r=t}}const o=(i.length-3)/3;i[1]=o>>8&255;i[2]=255&o;i.push(n>>8&255,255&n);a=new Uint8Array(i)}return a}compileIndex(e,t=[]){const a=e.objects,n=a.length;if(0===n)return new Uint8Array(2);let s,r,i=1;for(s=0;s>8&255;o[l++]=255&n;o[l++]=r;let f=1;for(s=0;s>8&255;o[l++]=255&f}else if(3===r){o[l++]=f>>16&255;o[l++]=f>>8&255;o[l++]=255&f}else{o[l++]=f>>>24&255;o[l++]=f>>16&255;o[l++]=f>>8&255;o[l++]=255&f}a[s]&&(f+=a[s].length)}for(s=0;s=this.firstChar&&e<=this.lastChar?e:-1}amend(e){unreachable("Should not call amend()")}}class CFFFont{constructor(e,t){this.properties=t;const a=new CFFParser(e,t,jn);this.cff=a.parse();this.cff.duplicateFirstGlyph();const n=new CFFCompiler(this.cff);this.seacs=this.cff.seacs;try{this.data=n.compile()}catch{warn("Failed to compile font "+t.loadedName);this.data=e}this._createBuiltInEncoding()}get numGlyphs(){return this.cff.charStrings.count}getCharset(){return this.cff.charset.charset}getGlyphMapping(){const e=this.cff,t=this.properties,{cidToGidMap:a,cMap:n}=t,s=e.charset.charset;let r,i;if(t.composite){let t,o;if(a?.length>0){t=Object.create(null);for(let e=0,n=a.length;e=0){const n=a[t];n&&(s[e]=n)}}s.length>0&&(this.properties.builtInEncoding=s)}}function getFloat214(e,t){return e.getInt16(t)/16384}function getSubroutineBias(e){const t=e.length;let a=32768;t<1240?a=107:t<33900&&(a=1131);return a}function parseCmap(e,t,a){const n=new DataView(e.buffer,e.byteOffset,e.byteLength),s=1===n.getUint16(t+2)?n.getUint32(t+8):n.getUint32(t+16),r=n.getUint16(t+s);let i,o,l;if(4===r){const e=n.getUint16(t+s+6)>>1;o=t+s+14;i=[];for(l=0;l>1;a0;)h.push({flags:o})}for(a=0;a>1;j=!0;break;case 4:i+=s.pop();moveTo(r,i);j=!0;break;case 5:for(;s.length>0;){r+=s.shift();i+=s.shift();lineTo(r,i)}break;case 6:for(;s.length>0;){r+=s.shift();lineTo(r,i);if(0===s.length)break;i+=s.shift();lineTo(r,i)}break;case 7:for(;s.length>0;){i+=s.shift();lineTo(r,i);if(0===s.length)break;r+=s.shift();lineTo(r,i)}break;case 8:for(;s.length>0;){c=r+s.shift();u=i+s.shift();h=c+s.shift();m=u+s.shift();r=h+s.shift();i=m+s.shift();bezierCurveTo(c,u,h,m,r,i)}break;case 10:b=s.pop();w=null;if(a.isCFFCIDFont){const e=a.fdSelect.getFDIndex(n);if(e>=0&&eMath.abs(i-t)?r+=s.shift():i+=s.shift();bezierCurveTo(c,u,h,m,r,i);break;default:throw new FormatError(`unknown operator: 12 ${k}`)}break;case 14:if(s.length>=4){const e=s.pop(),n=s.pop();i=s.pop();r=s.pop();t.save();t.translate(r,i);let o=lookupCmap(a.cmap,String.fromCharCode(a.glyphNameMap[fn[e]]));compileCharString(a.glyphs[o.glyphId],t,a,o.glyphId);t.restore();o=lookupCmap(a.cmap,String.fromCharCode(a.glyphNameMap[fn[n]]));compileCharString(a.glyphs[o.glyphId],t,a,o.glyphId)}return;case 19:case 20:o+=s.length>>1;f+=o+7>>3;j=!0;break;case 21:i+=s.pop();r+=s.pop();moveTo(r,i);j=!0;break;case 22:r+=s.pop();moveTo(r,i);j=!0;break;case 24:for(;s.length>2;){c=r+s.shift();u=i+s.shift();h=c+s.shift();m=u+s.shift();r=h+s.shift();i=m+s.shift();bezierCurveTo(c,u,h,m,r,i)}r+=s.shift();i+=s.shift();lineTo(r,i);break;case 25:for(;s.length>6;){r+=s.shift();i+=s.shift();lineTo(r,i)}c=r+s.shift();u=i+s.shift();h=c+s.shift();m=u+s.shift();r=h+s.shift();i=m+s.shift();bezierCurveTo(c,u,h,m,r,i);break;case 26:s.length%2&&(r+=s.shift());for(;s.length>0;){c=r;u=i+s.shift();h=c+s.shift();m=u+s.shift();r=h;i=m+s.shift();bezierCurveTo(c,u,h,m,r,i)}break;case 27:s.length%2&&(i+=s.shift());for(;s.length>0;){c=r+s.shift();u=i;h=c+s.shift();m=u+s.shift();r=h+s.shift();i=m;bezierCurveTo(c,u,h,m,r,i)}break;case 28:s.push(l.getInt16(f));f+=2;break;case 29:b=s.pop()+a.gsubrsBias;w=a.gsubrs[b];w&&parse(w);break;case 30:for(;s.length>0;){c=r;u=i+s.shift();h=c+s.shift();m=u+s.shift();r=h+s.shift();i=m+(1===s.length?s.shift():0);bezierCurveTo(c,u,h,m,r,i);if(0===s.length)break;c=r+s.shift();u=i;h=c+s.shift();m=u+s.shift();i=m+s.shift();r=h+(1===s.length?s.shift():0);bezierCurveTo(c,u,h,m,r,i)}break;case 31:for(;s.length>0;){c=r+s.shift();u=i;h=c+s.shift();m=u+s.shift();i=m+s.shift();r=h+(1===s.length?s.shift():0);bezierCurveTo(c,u,h,m,r,i);if(0===s.length)break;c=r;u=i+s.shift();h=c+s.shift();m=u+s.shift();r=h+s.shift();i=m+(1===s.length?s.shift():0);bezierCurveTo(c,u,h,m,r,i)}break;default:if(k<32)throw new FormatError(`unknown operator: ${k}`);if(k<247)s.push(k-139);else if(k<251)s.push(256*(k-247)+e[f++]+108);else if(k<255)s.push(256*-(k-251)-e[f++]-108);else{s.push(l.getInt32(f)/65536);f+=4}}j&&(s.length=0)}}(e)}class Commands{cmds=[];transformStack=[];currentTransform=[1,0,0,1,0,0];add(e,t){if(t){const{currentTransform:a}=this;for(let e=0,n=t.length;e=0&&ee.getUint32(t)}else{s=2;r=(e,t)=>2*e.getUint16(t)}const i=[];let o=r(n,0);for(let a=s;ae.getSize()+3&-4))}write(){const e=this.getSize(),t=new DataView(new ArrayBuffer(e)),a=e>131070,n=a?4:2,s=new DataView(new ArrayBuffer((this.glyphs.length+1)*n));a?s.setUint32(0,0):s.setUint16(0,0);let r=0,i=0;for(const e of this.glyphs){r+=e.write(r,t);r=r+3&-4;i+=n;a?s.setUint32(i,r):s.setUint16(i,r>>1)}return{isLocationLong:a,loca:new Uint8Array(s.buffer),glyf:new Uint8Array(t.buffer)}}scale(e){for(let t=0,a=this.glyphs.length;te.getSize()));return this.header.getSize()+e}write(e,t){if(!this.header)return 0;const a=e;e+=this.header.write(e,t);if(this.simple)e+=this.simple.write(e,t);else for(const a of this.composites)e+=a.write(e,t);return e-a}scale(e){if(!this.header)return;const t=(this.header.xMin+this.header.xMax)/2;this.header.scale(t,e);if(this.simple)this.simple.scale(t,e);else for(const a of this.composites)a.scale(t,e)}}class GlyphHeader{constructor({numberOfContours:e,xMin:t,yMin:a,xMax:n,yMax:s}){this.numberOfContours=e;this.xMin=t;this.yMin=a;this.xMax=n;this.yMax=s}static parse(e,t){return[10,new GlyphHeader({numberOfContours:t.getInt16(e),xMin:t.getInt16(e+2),yMin:t.getInt16(e+4),xMax:t.getInt16(e+6),yMax:t.getInt16(e+8)})]}getSize(){return 10}write(e,t){t.setInt16(e,this.numberOfContours);t.setInt16(e+2,this.xMin);t.setInt16(e+4,this.yMin);t.setInt16(e+6,this.xMax);t.setInt16(e+8,this.yMax);return 10}scale(e,t){this.xMin=Math.round(e+(this.xMin-e)*t);this.xMax=Math.round(e+(this.xMax-e)*t)}}class Contour{constructor({flags:e,xCoordinates:t,yCoordinates:a}){this.xCoordinates=t;this.yCoordinates=a;this.flags=e}}class SimpleGlyph{constructor({contours:e,instructions:t}){this.contours=e;this.instructions=t}static parse(e,t,a){const n=[];for(let s=0;s255?e+=2:o>0&&(e+=1);t=r;o=Math.abs(i-a);o>255?e+=2:o>0&&(e+=1);a=i}}return e}write(e,t){const a=e,n=[],s=[],r=[];let i=0,o=0;for(const a of this.contours){for(let e=0,t=a.xCoordinates.length;e=0?18:2;n.push(e)}else n.push(f)}i=l;const c=a.yCoordinates[e];f=c-o;if(0===f){t|=32;s.push(0)}else{const e=Math.abs(f);if(e<=255){t|=f>=0?36:4;s.push(e)}else s.push(f)}o=c;r.push(t)}t.setUint16(e,n.length-1);e+=2}t.setUint16(e,this.instructions.length);e+=2;if(this.instructions.length){new Uint8Array(t.buffer,0,t.buffer.byteLength).set(this.instructions,e);e+=this.instructions.length}for(const a of r)t.setUint8(e++,a);for(let a=0,s=n.length;a=-128&&this.argument1<=127&&this.argument2>=-128&&this.argument2<=127||(e+=2):this.argument1>=0&&this.argument1<=255&&this.argument2>=0&&this.argument2<=255||(e+=2);return e}write(e,t){const a=e;2&this.flags?this.argument1>=-128&&this.argument1<=127&&this.argument2>=-128&&this.argument2<=127||(this.flags|=1):this.argument1>=0&&this.argument1<=255&&this.argument2>=0&&this.argument2<=255||(this.flags|=1);t.setUint16(e,this.flags);t.setUint16(e+2,this.glyphIndex);e+=4;if(1&this.flags){if(2&this.flags){t.setInt16(e,this.argument1);t.setInt16(e+2,this.argument2)}else{t.setUint16(e,this.argument1);t.setUint16(e+2,this.argument2)}e+=4}else{t.setUint8(e,this.argument1);t.setUint8(e+1,this.argument2);e+=2}if(256&this.flags){t.setUint16(e,this.instructions.length);e+=2;if(this.instructions.length){new Uint8Array(t.buffer,0,t.buffer.byteLength).set(this.instructions,e);e+=this.instructions.length}}return e-a}scale(e,t){}}class OpenTypeFileBuilder{#Q=new Map;constructor(e){this.sfnt=e}static getSearchParams(e,t){let a=1,n=0;for(;(a^e)>a;){a<<=1;n++}const s=a*t;return{range:s,entry:n,rangeShift:t*e-s}}toArray(){let e=this.sfnt;const t=this.#Q,a=[...t.keys()].sort(),n=a.length;let s=12+16*n;const r=[s];for(let e=0;e>>0;r.push(s)}const i=new Uint8Array(s),o=new DataView(i.buffer);for(let e=0;e>>0}o.setInt32(s+4,l);o.setInt32(s+8,r[e]);o.setInt32(s+12,t.get(n).length);s+=16}this.#Q.clear();return i}addTable(e,t){if(this.#Q.has(e))throw new Error(`Table ${e} already exists`);this.#Q.set(e,t)}}const Kn=[4],Gn=[5],Vn=[6],$n=[7],Yn=[8],Jn=[12,35],Qn=[14],Zn=[21],es=[22],ts=[30],as=[31];class Type1CharString{width=0;lsb=0;flexing=!1;output=[];stack=[];convert(e,t,a){const n=e.length;let s,r,i,o=!1;for(let l=0;ln)return!0;const s=n-e;for(let e=s;e>8&255,255&t);else{t=65536*t|0;this.output.push(255,t>>24&255,t>>16&255,t>>8&255,255&t)}}this.output.push(...t);a?this.stack.splice(s,e):this.stack.length=0;return!1}}function isHexDigit(e){return e>=48&&e<=57||e>=65&&e<=70||e>=97&&e<=102}function decrypt(e,t,a){if(a>=e.length)return new Uint8Array(0);let n,s,r=0|t;for(n=0;n>8;r=52845*(t+r)+22719&65535}return o}function isSpecial(e){return 47===e||91===e||93===e||123===e||125===e||40===e||41===e}class Type1Parser{constructor(e,t,a){if(t){const t=e.getBytes(),a=!((isHexDigit(t[0])||isWhiteSpace(t[0]))&&isHexDigit(t[1])&&isHexDigit(t[2])&&isHexDigit(t[3])&&isHexDigit(t[4])&&isHexDigit(t[5])&&isHexDigit(t[6])&&isHexDigit(t[7]));e=new Stream(a?decrypt(t,55665,4):function decryptAscii(e,t,a){let n=0|t;const s=e.length,r=new Uint8Array(s>>>1);let i,o;for(i=0,o=0;i>8;n=52845*(e+n)+22719&65535}}return r.slice(a,o)}(t,55665,4))}this.seacAnalysisEnabled=!!a;this.stream=e;this.nextChar()}readNumberArray(){this.getToken();const e=[];for(;;){const t=this.getToken();if(null===t||"]"===t||"}"===t)break;e.push(parseFloat(t||0))}return e}readNumber(){const e=this.getToken();return parseFloat(e||0)}readInt(){const e=this.getToken();return 0|parseInt(e||0,10)}readBoolean(){return"true"===this.getToken()?1:0}nextChar(){return this.currentChar=this.stream.getByte()}prevChar(){this.stream.skip(-2);return this.currentChar=this.stream.getByte()}getToken(){let e=!1,t=this.currentChar;for(;;){if(-1===t)return null;if(e)10!==t&&13!==t||(e=!1);else if(37===t)e=!0;else if(!isWhiteSpace(t))break;t=this.nextChar()}if(isSpecial(t)){this.nextChar();return String.fromCharCode(t)}let a="";do{a+=String.fromCharCode(t);t=this.nextChar()}while(t>=0&&!isWhiteSpace(t)&&!isSpecial(t));return a}readCharStrings(e,t){return-1===t?e:decrypt(e,4330,t)}extractFontProgram(e){const t=this.stream,a=[],n=[],s=new Map([["lenIV",4]]),r={subrs:[],charstrings:[],properties:{privateData:s}};let i,o,l,f;for(;null!==(i=this.getToken());)if("/"===i){i=this.getToken();switch(i){case"CharStrings":this.getToken();this.getToken();this.getToken();this.getToken();for(;;){i=this.getToken();if(null===i||"end"===i)break;if("/"!==i)continue;const e=this.getToken();o=this.readInt();this.getToken();l=o>0?t.getBytes(o):new Uint8Array(0);f=s.get("lenIV");const a=this.readCharStrings(l,f);this.nextChar();i=this.getToken();"noaccess"===i?this.getToken():"/"===i&&this.prevChar();n.push({glyph:e,encoded:a})}break;case"Subrs":this.readInt();this.getToken();for(;"dup"===this.getToken();){const e=this.readInt();o=this.readInt();this.getToken();l=o>0?t.getBytes(o):new Uint8Array(0);f=s.get("lenIV");const n=this.readCharStrings(l,f);this.nextChar();i=this.getToken();"noaccess"===i&&this.getToken();a[e]=n}break;case"BlueValues":case"OtherBlues":case"FamilyBlues":case"FamilyOtherBlues":const e=this.readNumberArray();e.length>0&&e.length,0;break;case"StemSnapH":case"StemSnapV":s.set(i,this.readNumberArray());break;case"StdHW":case"StdVW":s.set(i,this.readNumberArray()[0]);break;case"BlueShift":case"lenIV":case"BlueFuzz":case"BlueScale":case"LanguageGroup":s.set(i,this.readNumber());break;case"ExpansionFactor":s.set(i,this.readNumber()||.06);break;case"ForceBold":s.set(i,this.readBoolean())}}for(const{encoded:t,glyph:s}of n){const n=new Type1CharString,i=n.convert(t,a,this.seacAnalysisEnabled);let o=n.output;i&&(o=[14]);const l={glyphName:s,charstring:o,width:n.width,lsb:n.lsb,seac:n.seac};".notdef"===s?r.charstrings.unshift(l):r.charstrings.push(l);if(e.builtInEncoding){const t=e.builtInEncoding.indexOf(s);t>-1&&void 0===e.widths[t]&&t>=e.firstChar&&t<=e.lastChar&&(e.widths[t]=n.width)}}return r}extractFontHeader(e){let t;for(;null!==(t=this.getToken());)if("/"===t){t=this.getToken();switch(t){case"FontMatrix":const a=this.readNumberArray();e.fontMatrix=a;break;case"Encoding":const n=this.getToken();let s;if(/^\d+$/.test(n)){s=[];const e=0|parseInt(n,10);this.getToken();for(let a=0;a=s){i+=a;for(;i=0&&(n[e]=s)}}return type1FontGlyphMapping(e,n,a)}hasGlyphId(e){if(e<0||e>=this.numGlyphs)return!1;if(0===e)return!0;return this.charstrings[e-1].charstring.length>0}getSeacs(e){const t=[];for(let a=0,n=e.length;a0;e--)t[e]-=t[e-1];h.setByName(e,t)}r.topDict.privateDict=h;const m=new CFFIndex;for(const e of n)m.add(e);h.subrsIndex=m;return new CFFCompiler(r).compile()}}const ns=[[57344,63743],[1048576,1114109]],ss=1e3,rs=["ascent","bbox","black","bold","cssFontInfo","data","defaultVMetrics","defaultWidth","descent","disableFontFace","fallbackName","fontExtraProperties","fontMatrix","isInvalidPDFjsFont","isType3Font","italic","loadedName","mimetype","missingFile","name","remeasure","systemFontInfo","vertical"],is=["cMap","composite","defaultEncoding","differences","isMonospace","isSerifFont","isSymbolicFont","seacMap","subtype","toFontChar","toUnicode","type","vmetrics","widths"];function adjustWidths(e){if(!e.fontMatrix)return;if(e.fontMatrix[0]===n[0])return;const t=.001/e.fontMatrix[0],a=e.widths;for(const e in a)a[e]*=t;e.defaultWidth*=t}function amendFallbackToUnicode(e){if(!e.fallbackToUnicode)return;if(e.toUnicode instanceof IdentityToUnicodeMap)return;const t=[];for(const a in e.fallbackToUnicode)e.toUnicode.has(a)||(t[a]=e.fallbackToUnicode[a]);t.length>0&&e.toUnicode.amend(t)}class fonts_Glyph{constructor(e,t,a,n,s,r,i,o,l){this.originalCharCode=e;this.fontChar=t;this.unicode=a;this.accent=n;this.width=s;this.vmetric=r;this.operatorListId=i;this.isSpace=o;this.isInFont=l}get category(){return shadow(this,"category",function getCharUnicodeCategory(e){const t=wn.get(e);if(t)return t;const a=e.match(bn),n={isWhitespace:!!a?.[1],isZeroWidthDiacritic:!!a?.[2],isInvisibleFormatMark:!!a?.[3]};wn.set(e,n);return n}(this.unicode),!0)}}function int16(e,t){return(e<<8)+t}function writeSignedInt16(e,t,a){e[t+1]=a;e[t]=a>>>8}function signedInt16(e,t){const a=(e<<8)+t;return 32768&a?a-65536:a}class TrueTypeTableBuilder{#V;#$=1024;#ee=!1;#Y=0;#te;constructor({exactLength:e,minLength:t}){this.#ee=!!e;this.#J(e||t)}#J(e){if(this.#ee)this.#$=e;else for(;this.#$this.#$&&this.#J(t);this.#V.set(e,this.#Y);this.#Y=t}setInt16(e){const t=this.#Y+2;!this.#ee&&t>this.#$&&this.#J(t);this.#te.setInt16(this.#Y,e);this.#Y=t}setSafeInt16(e){const t=this.#Y+2;!this.#ee&&t>this.#$&&this.#J(t);this.#te.setInt16(this.#Y,MathClamp(e,-32768,32767));this.#Y=t}setInt32(e){const t=this.#Y+4;!this.#ee&&t>this.#$&&this.#J(t);this.#te.setInt32(this.#Y,e);this.#Y=t}}function isTrueTypeCollectionFile(e){return"ttcf"===bytesToString(e.peekBytes(4))}function getFontFileType(e,{type:t,subtype:a,composite:n}){let s,r;if(function isTrueTypeFile(e){const t=bytesToString(e.peekBytes(4));return"\0\0\0"===t||"true"===t}(e)||isTrueTypeCollectionFile(e))s=n?"CIDFontType2":"TrueType";else if(function isOpenTypeFile(e){return"OTTO"===bytesToString(e.peekBytes(4))}(e))s=n?"CIDFontType2":"OpenType";else if(function isType1File(e){const t=e.peekBytes(2);return 37===t[0]&&33===t[1]||128===t[0]&&1===t[1]}(e))s=n?"CIDFontType0":"MMType1"===t?"MMType1":"Type1";else if(function isCFFFile(e){const t=e.peekBytes(4);return t[0]>=1&&t[3]>=1&&t[3]<=4}(e))if(n){s="CIDFontType0";r="CIDFontType0C"}else{s="MMType1"===t?"MMType1":"Type1";r="Type1C"}else{warn("getFontFileType: Unable to detect correct font file Type/Subtype.");s=t;r=a}return[s,r]}function applyStandardFontGlyphMap(e,t){for(const a in t)e[+a]=t[a]}function buildToFontChar(e,t,a){const n=[];let s;for(let a=0,r=e.length;ans[0][0]<=e&&e<=ns[0][1]||ns[1][0]<=e&&e<=ns[1][1];let h=null;for(const u in e){let m=e[u];if(!t(m))continue;if(f>c){l++;if(l>=ns.length){warn("Ran out of space in font private use area.");break}f=ns[l][0];c=ns[l][1]}const p=f++;0===m&&(m=a);let d=n.get(u);if("string"==typeof d)if(1===d.length)d=d.codePointAt(0);else{if(!h){h=new Map;for(let e=64256;e<=64335;e++){const t=String.fromCharCode(e).normalize("NFKD");t.length>1&&h.set(t,e)}}d=h.get(d)||d.codePointAt(0)}if(d&&!isInPrivateArea(d)&&!o.has(m)){r.set(d,m);o.add(m)}s[p]=m;i[u]=p}return{toFontChar:i,charCodeToGlyphId:s,toUnicodeExtraMap:r,nextAvailableFontCharCode:f}}function createCmapTable(e,t,a){const n=function getRanges(e,t,a){const n=[];for(const t in e)e[t]>=a||n.push({fontCharCode:0|t,glyphId:e[t]});if(t)for(const[e,s]of t)s>=a||n.push({fontCharCode:e,glyphId:s});0===n.length&&n.push({fontCharCode:0,glyphId:0});n.sort((e,t)=>e.fontCharCode-t.fontCharCode);const s=[],r=n.length;for(let e=0;e65535?2:1,r=new TrueTypeTableBuilder({exactLength:12});r.skip(2);r.setInt16(s);r.setArray([0,3]);r.setArray([0,1]);r.setInt32(4+8*s);let i,o,l,f;for(i=n.length-1;i>=0&&!(n[i][0]<=65535);--i);const c=i+1;n[i][0]<65535&&65535===n[i][1]&&(n[i][1]=65534);const h=n[i][1]<65535?1:0,u=c+h,m=OpenTypeFileBuilder.getSearchParams(u,2),p=2*c+2*h,d=new TrueTypeTableBuilder({exactLength:p}),g=new TrueTypeTableBuilder({exactLength:p}),b=new TrueTypeTableBuilder({exactLength:p}),w=new TrueTypeTableBuilder({exactLength:p}),j=new TrueTypeTableBuilder({});let k=0;for(i=0,o=c;i0){g.setArray([255,255]);d.setArray([255,255]);b.setArray([0,1]);w.skip(2)}const y=new TrueTypeTableBuilder({exactLength:12+d.length+g.length+b.length+w.length+j.length});y.skip(2);y.setInt16(2*u);y.setInt16(m.range);y.setInt16(m.entry);y.setInt16(m.rangeShift);y.setArray(g.data);y.skip(2);y.setArray(d.data);y.setArray(b.data);y.setArray(w.data);y.setArray(j.data);let q=null,v=null,S=null;if(s>1){q=new TrueTypeTableBuilder({exactLength:8});q.setArray([0,3]);q.setArray([0,10]);q.setInt32(4+8*s+4+y.length);v=new TrueTypeTableBuilder({});for(const e of n){let t=e[0];const a=e[2];let n=a[0];for(l=1,f=a.length;le||!o)&&(o=e);l 123 are reserved for internal usage");i|=1<65535&&(l=65535)}else{o=0;l=255}const c=e.bbox||[0,0,0,0],h=a.unitsPerEm||(e.fontMatrix?1/Math.max(...e.fontMatrix.slice(0,4).map(Math.abs)):1e3),u=e.ascentScaled?1:h/ss,m=a.ascent||Math.round(u*(e.ascent||c[3]));let p=a.descent||Math.round(u*(e.descent||c[1]));p>0&&e.descent>0&&c[1]<0&&(p=-p);const d=a.yMax||m,g=-a.yMin||-p,b=new TrueTypeTableBuilder({exactLength:96});b.setArray([0,3]);b.setArray([2,36]);b.setArray([1,244]);b.setArray([0,5]);b.skip(2);b.setArray([2,138]);b.setArray([2,187]);b.skip(2);b.setArray([0,140]);b.setArray([2,138]);b.setArray([2,187]);b.skip(2);b.setArray([1,223]);b.setArray([0,49]);b.setArray([1,2]);b.skip(2);b.setArray([0,0,6,e.fixedPitch?9:0,0,0,0,0,0,0]);b.setInt32(n);b.setInt32(s);b.setInt32(r);b.setInt32(i);b.setArray([42,50,49,42]);b.setInt16(e.italicAngle?1:0);b.setInt16(o||e.firstChar);b.setInt16(l||e.lastChar);b.setInt16(m);b.setInt16(p);b.setArray([0,100]);b.setInt16(d);b.setInt16(g);b.skip(8);b.setInt16(e.xHeight);b.setInt16(e.capHeight);b.skip(2);b.setInt16(o||e.firstChar);b.setArray([0,3]);return b.data}function createPostTable(e){const t=new TrueTypeTableBuilder({exactLength:32});t.setArray([0,3,0,0]);t.setInt32(Math.floor(65536*e.italicAngle));t.skip(4);t.setInt32(e.fixedPitch?1:0);t.skip(16);return t.data}function createPostscriptName(e){return e.replaceAll(/[^\x21-\x7E]|[[\](){}<>/%]/g,"").slice(0,63)}function createNameTable(e,t){t||=[[],[]];const a=[t[0][0]||"Original licence",t[0][1]||e,t[0][2]||"Unknown",t[0][3]||"uniqueID",t[0][4]||e,t[0][5]||"Version 0.11",t[0][6]||createPostscriptName(e),t[0][7]||"Unknown",t[0][8]||"Unknown",t[0][9]||"Unknown"],n=a.map(e=>stringToBytes(e)),s=new Array(a.length);let r,i,o,l,f;for(r=0,i=a.length;re.length))+Math.sumPrecise(n.map(e=>e.length))+Math.sumPrecise(s.map(e=>e.length))});b.skip(2);b.setInt16(g);b.setInt16(12*g+6);for(const e of p)b.setArray(e);for(const e of n)b.setArray(e);for(const e of s)b.setArray(e);return b.data}class Font{#ae=new Map;#ne=new Map;charProcOperatorList;constructor(e,t,a,n){this.name=e;this.psName=null;this.mimetype=null;this.disableFontFace=n.disableFontFace;this.fontExtraProperties=n.fontExtraProperties;this.loadedName=a.loadedName;this.isType3Font=a.isType3Font;this.missingFile=!1;this.cssFontInfo=a.cssFontInfo;let s=!!(a.flags&yn);if(!s&&!a.isSimulatedFlags){const t=Mn(),a=Pn(),n=En();for(const r of e.split("+")){let e=normalizeFontName(r);e=t[e]||a[e]||e;e=e.split("-",1)[0];if(n[e]){s=!0;break}}}this.isSerifFont=s;this.isSymbolicFont=!!(a.flags&qn);this.isMonospace=!!(a.flags&kn);let{type:r,subtype:i}=a;this.type=r;this.subtype=i;this.systemFontInfo=a.systemFontInfo;const o=e.match(/^InvalidPDFjsFont_(.*)_\d+$/);this.isInvalidPDFjsFont=!!o;this.isInvalidPDFjsFont?this.fallbackName=o[1]:this.isMonospace?this.fallbackName="monospace":this.isSerifFont?this.fallbackName="serif":this.fallbackName="sans-serif";if(this.systemFontInfo?.guessFallback){this.systemFontInfo.guessFallback=!1;this.systemFontInfo.css+=`,${this.fallbackName}`}this.differences=a.differences;this.widths=a.widths;this.defaultWidth=a.defaultWidth;this.composite=a.composite;this.cMap=a.cMap;this.capHeight=a.capHeight/ss;this.ascent=a.ascent/ss;this.descent=a.descent/ss;this.lineHeight=this.ascent-this.descent;this.fontMatrix=a.fontMatrix;this.bbox=a.bbox;this.defaultEncoding=a.defaultEncoding;this.toUnicode=a.toUnicode;this.toFontChar=[];if("Type3"===a.type){for(let e=0;e<256;e++)this.toFontChar[e]=this.differences[e]||a.defaultEncoding[e];return}this.cidEncoding=a.cidEncoding||"";this.vertical=!!a.vertical;if(this.vertical){this.vmetrics=a.vmetrics;this.defaultVMetrics=a.defaultVMetrics}if(!t||t.isEmpty){t&&warn('Font file is empty in "'+e+'" ('+this.loadedName+")");this.fallbackToSystemFont(a);return}[r,i]=getFontFileType(t,a);r===this.type&&i===this.subtype||info(`Inconsistent font file Type/SubType, expected: ${this.type}/${this.subtype} but found: ${r}/${i}.`);let l;try{switch(r){case"MMType1":info("MMType1 font ("+e+"), falling back to Type1.");case"Type1":case"CIDFontType0":this.mimetype="font/opentype";const n="Type1C"===i||"CIDFontType0C"===i?new CFFFont(t,a):new Type1Font(e,t,a);adjustWidths(a);l=this.convert(e,n,a);break;case"OpenType":case"TrueType":case"CIDFontType2":this.mimetype="font/opentype";l=this.checkAndRepair(e,t,a);adjustWidths(a);this.isOpenType&&(r="OpenType");break;default:throw new FormatError(`Font ${r} is not supported`)}}catch(e){warn(e);this.fallbackToSystemFont(a);return}amendFallbackToUnicode(a);this.data=l;this.type=r;this.subtype=i;this.fontMatrix=a.fontMatrix;this.widths=a.widths;this.defaultWidth=a.defaultWidth;this.toUnicode=a.toUnicode;this.seacMap=a.seacMap}get renderer(){return shadow(this,"renderer",FontRendererFactory.create(this,jn))}#se(e){const t=Object.create(null);for(const a of e){const e=this[a];void 0!==e&&(t[a]=e)}return t}exportData(){return{buffer:compileFontInfo(this.#se(rs)),charProcOperatorList:this.charProcOperatorList,extra:this.fontExtraProperties?this.#se(is):void 0}}fallbackToSystemFont(e){this.missingFile=!0;const{name:t,type:a}=this;let n=normalizeFontName(t);const s=Mn(),r=Pn(),i=!!s[n],o=!(!r[n]||!s[r[n]]);n=s[n]||r[n]||n;const l=Wn()[n];if(l){isNaN(this.ascent)&&(this.ascent=l.ascent/ss);isNaN(this.descent)&&(this.descent=l.descent/ss);isNaN(this.capHeight)&&(this.capHeight=l.capHeight/ss)}this.bold=/bold/gi.test(n);this.italic=/oblique|italic/gi.test(n);this.black=/Black/g.test(t);const f=/Narrow/g.test(t);this.remeasure=(!i||f)&&Object.keys(this.widths).length>0;if((i||o)&&"CIDFontType2"===a&&this.cidEncoding.startsWith("Identity-")){const a=e.cidToGidMap,n=[];applyStandardFontGlyphMap(n,zn());/Arial-?Black/i.test(t)?applyStandardFontGlyphMap(n,Ln()):/Calibri/i.test(t)&&applyStandardFontGlyphMap(n,Un());if(a){for(const e in n){const t=n[e];void 0!==a[t]&&(n[+e]=a[t])}a.length!==this.toUnicode.length&&e.hasIncludedToUnicodeMap&&this.toUnicode instanceof IdentityToUnicodeMap&&this.toUnicode.forEach(function(e,t){const s=n[e];void 0===a[s]&&(n[+e]=t)})}this.toUnicode instanceof IdentityToUnicodeMap||this.toUnicode.forEach(function(e,t){n[+e]=t});this.toFontChar=n;this.toUnicode=new ToUnicodeMap(n)}else if(/Symbol/i.test(n))this.toFontChar=buildToFontChar(hn,mn(),this.differences);else if(/Dingbats/i.test(n))this.toFontChar=buildToFontChar(un,pn(),this.differences);else if(i||o){const e=buildToFontChar(this.defaultEncoding,mn(),this.differences);"CIDFontType2"!==a||this.cidEncoding.startsWith("Identity-")||this.toUnicode instanceof IdentityToUnicodeMap||this.toUnicode.forEach(function(t,a){e[+t]=a});this.toFontChar=e}else{const e=mn(),a=[];this.toUnicode.forEach((t,n)=>{if(!this.composite){const a=getUnicodeForGlyph(this.differences[t]||this.defaultEncoding[t],e);-1!==a&&(n=a)}a[+t]=n});this.composite&&this.toUnicode instanceof IdentityToUnicodeMap&&/Tahoma|Verdana/i.test(t)&&applyStandardFontGlyphMap(a,zn());this.toFontChar=a}amendFallbackToUnicode(e);this.loadedName=n.split("-",1)[0]}checkAndRepair(e,t,a){const n=["OS/2","cmap","head","hhea","hmtx","maxp","name","post","loca","glyf","fpgm","prep","cvt ","CFF "];function readTables(e,t){const a=Object.create(null);a["OS/2"]=null;a.cmap=null;a.head=null;a.hhea=null;a.hmtx=null;a.maxp=null;a.name=null;a.post=null;for(let s=0;s>>0,n=e.getInt32()>>>0,s=e.getInt32()>>>0,r=e.pos;e.pos=e.start||0;e.skip(n);const i=e.getBytes(s);e.pos=r;if("head"===t){i[8]=i[9]=i[10]=i[11]=0;i[17]|=32}return{tag:t,checksum:a,length:s,offset:n,data:i,view:new DataView(i.buffer,i.byteOffset,i.byteLength)}}function readOpenTypeHeader(e){return{version:e.getString(4),numTables:e.getUint16(),searchRange:e.getUint16(),entrySelector:e.getUint16(),rangeShift:e.getUint16()}}function sanitizeGlyph(e,t,a,n,s,r){const i={length:0,sizeOfInstructions:0};if(t<0||t>=e.length||a>e.length||a-t<=12)return i;const o=e.subarray(t,a),l=signedInt16(o[2],o[3]),f=signedInt16(o[4],o[5]),c=signedInt16(o[6],o[7]),h=signedInt16(o[8],o[9]);if(l>c){writeSignedInt16(o,2,c);writeSignedInt16(o,6,l)}if(f>h){writeSignedInt16(o,4,h);writeSignedInt16(o,8,f)}const u=signedInt16(o[0],o[1]);if(u<0){if(u<-1)return i;n.set(o,s);i.length=o.length;return i}let m,p=10,d=0;for(m=0;mo.length)return i;if(!r&&b>0){n.set(o.subarray(0,g),s);n.set([0,0],s+g);n.set(o.subarray(w,k),s+g+2);k-=b;o.length-k>3&&(k=k+3&-4);i.length=k;return i}if(o.length-k>3){k=k+3&-4;n.set(o.subarray(0,k),s);i.length=k;return i}n.set(o,s);i.length=o.length;return i}function readNameTable(e){const a=(t.start||0)+e.offset;t.pos=a;const n=[[],[]],s=[],r=e.length,i=a+r;if(0!==t.getUint16()||r<6)return[n,s];const o=t.getUint16(),l=t.getUint16();let f,c;for(f=0;fi)continue;t.pos=r;const o=e.name;if(e.encoding){let a="";for(let n=0,s=e.length;n0&&(f+=e-1)}}else{if(g||w){warn("TT: nested FDEFs not allowed");d=!0}g=!0;h=f;i=u.pop();t.functionsDefined[i]={data:l,i:f}}else if(!g&&!w){i=u.at(-1);if(isNaN(i))info("TT: CALL empty stack (or invalid entry).");else{t.functionsUsed[i]=!0;if(i in t.functionsStackDeltas){const e=u.length+t.functionsStackDeltas[i];if(e<0){warn("TT: CALL invalid functions stack delta.");t.hintsValid=!1;return}u.length=e}else if(i in t.functionsDefined&&!p.includes(i)){m.push({data:l,i:f,stackTop:u.length-1});p.push(i);o=t.functionsDefined[i];if(!o){warn("TT: CALL non-existent function");t.hintsValid=!1;return}l=o.data;f=o.i}}}if(!g&&!w){let t=0;e<=142?t=s[e]:e>=192&&e<=223?t=-1:e>=224&&(t=-2);if(e>=113&&e<=117){n=u.pop();isNaN(n)||(t=2*-n)}for(;t<0&&u.length>0;){u.pop();t++}for(;t>0;){u.push(NaN);t--}}}t.tooComplexToFollowFunctions=d;const j=[l];f>l.length&&j.push(new Uint8Array(f-l.length));if(h>c){warn("TT: complementing a missing function tail");j.push(new Uint8Array([34,45]))}!function foldTTTable(e,t){if(t.length>1){let a,n,s=0;for(a=0,n=t.length;a>>0,r=[];for(let t=0;t>>0);const i={ttcTag:t,majorVersion:a,minorVersion:n,numFonts:s,offsetTable:r};switch(a){case 1:return i;case 2:i.dsigTag=e.getInt32()>>>0;i.dsigLength=e.getInt32()>>>0;i.dsigOffset=e.getInt32()>>>0;return i}throw new FormatError(`Invalid TrueType Collection majorVersion: ${a}.`)}(e),s=t.split("+");let r;for(let i=0;i=32))throw new FormatError('"maxp" table has a wrong version number');f=65536}!function writeUint32(e,t,a){e[t+3]=255&a;e[t+2]=a>>>8;e[t+1]=a>>>16;e[t]=a>>>24}(i.maxp.data,0,f)}let h=int16(i.head.data[50],i.head.data[51]);if(i.loca){const e=h?4*(c+1):2*(c+1);if(i.loca.length!==e){warn("Incorrect 'loca' table length -- attempting to fix it.");const a=Object.values(i).filter(Boolean).sort((e,t)=>e.offset-t.offset),n=a.indexOf(i.loca),s=a[n+1]||null;if(s&&i.loca.offset+e>8&255;o[a+1]=255&n;writeSignedInt16(o,a+2,Math.round(e[t]*signedInt16(o[a+2],o[a+3])))}}let u=c+1,m=!0;if(u>65535){m=!1;u=c;warn("Not enough space in glyfs to duplicate first glyph.")}let p=0,d=0;if(f>=65536&&i.maxp.length>=32){t.pos+=8;if(t.getUint16()>2){i.maxp.data[14]=0;i.maxp.data[15]=2}t.pos+=4;p=t.getUint16();t.pos+=4;d=t.getUint16()}i.maxp.data[4]=u>>8;i.maxp.data[5]=255&u;const g=function sanitizeTTPrograms(e,t,a,n){const s={functionsDefined:[],functionsUsed:[],functionsStackDeltas:[],tooComplexToFollowFunctions:!1,hintsValid:!0};e&&sanitizeTTProgram(e,s);t&&sanitizeTTProgram(t,s);e&&function checkInvalidFunctions(e,t){if(!e.tooComplexToFollowFunctions)if(e.functionsDefined.length>t){warn("TT: more functions defined than expected");e.hintsValid=!1}else for(let a=0,n=e.functionsUsed.length;at){warn("TT: invalid function id: "+a);e.hintsValid=!1;return}if(e.functionsUsed[a]&&!e.functionsDefined[a]){warn("TT: undefined function: "+a);e.hintsValid=!1;return}}}(s,n);if(a&&1&a.length){const e=new Uint8Array(a.length+1);e.set(a.data);a.data=e}return s.hintsValid}(i.fpgm,i.prep,i["cvt "],p);if(!g){delete i.fpgm;delete i.prep;delete i["cvt "]}!function sanitizeMetrics(e,t,a,n,s,r){if(!t){a&&(a.data=null);return}e.pos=(e.start||0)+t.offset;e.pos+=4;e.pos+=2;e.pos+=2;e.pos+=2;e.pos+=2;e.pos+=2;e.pos+=2;e.pos+=2;e.pos+=2;e.pos+=2;const i=e.getUint16();e.pos+=8;e.pos+=2;let o=e.getUint16();if(0!==i){if(!(2&int16(n.data[44],n.data[45]))){t.data[22]=0;t.data[23]=0}}if(o>s){info(`The numOfMetrics (${o}) should not be greater than the numGlyphs (${s}).`);o=s;t.data[34]=(65280&o)>>8;t.data[35]=255&o}const l=s-o-(a.length-4*o>>1);if(l>0){const e=new Uint8Array(a.length+2*l);e.set(a.data);if(r){e[a.length]=a.data[2];e[a.length+1]=a.data[3]}a.data=e}}(t,i.hhea,i.hmtx,i.head,u,m);if(!i.head)throw new FormatError('Required "head" table is not found');!function sanitizeHead(e,t,a){const{data:n,view:s}=e,r=s.getInt32(0);if(r>>16!=1){info("Attempting to fix invalid version in head table: "+r);s.setInt32(0,65536)}const i=signedInt16(n[50],n[51]);if(i<0||i>1){info("Attempting to fix invalid indexToLocFormat in head table: "+i);const e=t+1;if(a===e<<1){n[50]=0;n[51]=0}else{if(a!==e<<2)throw new FormatError("Could not fix indexToLocFormat: "+i);n[50]=0;n[51]=1}}}(i.head,c,o?i.loca.length:0);let b=Object.create(null);if(o){const e=function sanitizeGlyphLocations(e,t,a,n,s,r,i){let o,l,f;if(n){o=4;l=function fontItemDecodeLong(e,t){return e[t]<<24|e[t+1]<<16|e[t+2]<<8|e[t+3]};f=function fontItemEncodeLong(e,t,a){e[t]=a>>>24&255;e[t+1]=a>>16&255;e[t+2]=a>>8&255;e[t+3]=255&a}}else{o=2;l=function fontItemDecode(e,t){return e[t]<<9|e[t+1]<<1};f=function fontItemEncode(e,t,a){e[t]=a>>9&255;e[t+1]=a>>1&255}}const c=r?a+1:a,h=o*(1+c),u=new Uint8Array(h);u.set(e.data.subarray(0,h));e.data=u;const m=t.data,p=m.length,d=new Uint8Array(p);let g,b;const w=[];for(g=0,b=0;gp&&(e=p);w.push({index:g,offset:e,endOffset:0})}w.sort((e,t)=>e.offset-t.offset);for(g=0;ge.index-t.index);for(g=0;gi&&(i=e.sizeOfInstructions);y+=t;f(u,b,y)}if(0===y){const e=new Uint8Array([0,1,0,0,0,0,0,0,0,0,0,0,0,0,49,0]);for(g=0,b=o;ga+y)t.data=d.subarray(0,a+y);else{t.data=new Uint8Array(a+y);t.data.set(d.subarray(0,y))}t.data.set(d.subarray(0,a),y);f(e.data,u.length-o,y+a)}else t.data=d.subarray(0,y);return{missingGlyphs:k,maxSizeOfInstructions:i}}(i.loca,i.glyf,c,h,g,m,d);b=e.missingGlyphs;if(f>=65536&&i.maxp.length>=32){i.maxp.data[26]=e.maxSizeOfInstructions>>8;i.maxp.data[27]=255&e.maxSizeOfInstructions}}if(!i.hhea)throw new FormatError('Required "hhea" table is not found');if(0===i.hhea.data[10]&&0===i.hhea.data[11]){i.hhea.data[10]=255;i.hhea.data[11]=255}const w={unitsPerEm:int16(i.head.data[18],i.head.data[19]),yMax:signedInt16(i.head.data[42],i.head.data[43]),yMin:signedInt16(i.head.data[38],i.head.data[39]),ascent:signedInt16(i.hhea.data[4],i.hhea.data[5]),descent:signedInt16(i.hhea.data[6],i.hhea.data[7]),lineGap:signedInt16(i.hhea.data[8],i.hhea.data[9])};this.ascent=w.ascent/w.unitsPerEm;this.descent=w.descent/w.unitsPerEm;this.lineGap=w.lineGap/w.unitsPerEm;if(this.cssFontInfo?.lineHeight){this.lineHeight=this.cssFontInfo.metrics.lineHeight;this.lineGap=this.cssFontInfo.metrics.lineGap}else this.lineHeight=this.ascent-this.descent+this.lineGap;i.post&&function readPostScriptTable(e,a,n){const s=(t.start||0)+e.offset;t.pos=s;const r=s+e.length,i=t.getInt32();t.skip(28);let o,l,f=!0;switch(i){case 65536:o=Sn;break;case 131072:const e=t.getUint16();if(e!==n){f=!1;break}const s=[];for(l=0;l=32768){f=!1;break}s.push(e)}if(!f)break;const c=[];for(;t.pos65535)throw new FormatError("Max size of CID is 65,535");let s=-1;t?s=n:void 0!==e[n]&&(s=e[n]);s>=0&&s>>0;let c=!1;if(o?.platformId!==s||o?.encodingId!==r){if(0!==s||0!==r&&1!==r&&3!==r)if(1===s&&0===r)c=!0;else if(3!==s||1!==r||!n&&o){if(a&&3===s&&0===r){c=!0;let a=!0;if(e>3;e.push(n);a=Math.max(n,a)}const n=[];for(let e=0;e<=a;e++)n.push({firstCode:t.getUint16(),entryCount:t.getUint16(),idDelta:signedInt16(t.getByte(),t.getByte()),idRangePos:t.pos+t.getUint16()});for(let a=0;a<256;a++)if(0===e[a]){t.pos=n[0].idRangePos+2*a;m=t.getUint16();h.push({charCode:a,glyphId:m})}else{const s=n[e[a]];for(u=0;u>1;t.skip(6);const a=[];let n;for(n=0;n>1)-(e-n);s.offsetIndex=i;o=Math.max(o,i+s.end-s.start+1)}else s.offsetIndex=-1}const l=[];for(u=0;u>>0;for(u=0;u>>0,a=t.getInt32()>>>0;let n=t.getInt32()>>>0;for(let t=e;t<=a;t++)h.push({charCode:t,glyphId:n++})}}}h.sort((e,t)=>e.charCode-t.charCode);const p=[],d=new Set;for(const e of h){const{charCode:t}=e;if(!d.has(t)){d.add(t);p.push(e)}}return{platformId:o.platformId,encodingId:o.encodingId,mappings:p,hasShortCmap:c}}(i.cmap,t,this.isSymbolicFont,a.hasEncoding),n=e.platformId,s=e.encodingId,r=e.mappings;let o=[],l=!1;!a.hasEncoding||"MacRomanEncoding"!==a.baseEncodingName&&"WinAnsiEncoding"!==a.baseEncodingName||(o=getEncoding(a.baseEncodingName));if(a.hasEncoding&&!this.isSymbolicFont&&(3===n&&1===s||1===n&&0===s)){const e=mn();for(let t=0;t<256;t++){let i;i=void 0!==this.differences[t]?this.differences[t]:o.length&&""!==o[t]?o[t]:fn[t];if(!i)continue;const l=recoverGlyphName(i,e);let f;3===n&&1===s?f=e[l]:1===n&&0===s&&(f=ln.indexOf(l));if(void 0===f){if(!a.glyphNames&&a.hasIncludedToUnicodeMap&&!(this.toUnicode instanceof IdentityToUnicodeMap)){const e=this.toUnicode.get(t);e&&(f=e.codePointAt(0))}if(void 0===f)continue}for(const e of r)if(e.charCode===f){j[t]=e.glyphId;break}}}else if(0===n){for(const e of r)j[e.charCode]=e.glyphId;l=!0}else if(3===n&&0===s)for(const e of r){let t=e.charCode;t>=61440&&t<=61695&&(t&=255);j[t]=e.glyphId}else for(const e of r)j[e.charCode]=e.glyphId;if(a.glyphNames&&(o.length||this.differences.length))for(let e=0;e<256;++e){if(!l&&void 0!==j[e])continue;const t=this.differences[e]||o[e];if(!t)continue;const n=a.glyphNames.indexOf(t);n>0&&hasGlyph(n)&&(j[e]=n)}}0===j.length&&(j[0]=0);let k=u-1;m||(k=0);if(!a.cssFontInfo){const e=adjustMapping(j,hasGlyph,k,this.toUnicode);this.toFontChar=e.toFontChar;i.cmap={tag:"cmap",data:createCmapTable(e.charCodeToGlyphId,e.toUnicodeExtraMap,u)};i["OS/2"]&&function validateOS2Table(e,t){t.pos=(t.start||0)+e.offset;const a=t.getUint16();t.skip(60);const n=t.getUint16();if(a<4&&768&n)return!1;if(t.getUint16()>t.getUint16())return!1;t.skip(6);if(0===t.getUint16())return!1;e.data[8]=e.data[9]=0;return!0}(i["OS/2"],t)||(i["OS/2"]={tag:"OS/2",data:createOS2Table(a,e.charCodeToGlyphId,w)})}if(i.name){const[t,n]=readNameTable(i.name);i.name.data=createNameTable(e,t);this.psName=t[0][6]||null;a.composite||function adjustTrueTypeToUnicode(e,t,a){if(e.isInternalFont)return;if(e.hasIncludedToUnicodeMap)return;if(e.hasEncoding)return;if(e.toUnicode instanceof IdentityToUnicodeMap)return;if(!t)return;if(0===a.length)return;if(e.defaultEncoding===cn)return;for(const e of a)if(!isWinNameRecord(e))return;const n=cn,s=[],r=mn();for(const e in n){const t=n[e];if(""===t)continue;const a=r[t];void 0!==a&&(s[e]=String.fromCharCode(a))}s.length>0&&e.toUnicode.amend(s)}(a,this.isSymbolicFont,n)}else i.name={tag:"name",data:createNameTable(this.name)};const y=new OpenTypeFileBuilder(r.version);for(const e in i)y.addTable(e,i[e].data);return y.toArray()}convert(e,t,a){a.fixedPitch=!1;a.builtInEncoding&&function adjustType1ToUnicode(e,t){if(e.isInternalFont)return;if(e.hasIncludedToUnicodeMap)return;if(t===e.defaultEncoding)return;if(e.toUnicode instanceof IdentityToUnicodeMap)return;const a=[],n=mn();for(const s in t){if(e.hasEncoding&&(e.baseEncodingName||void 0!==e.differences[s]))continue;const r=getUnicodeForGlyph(t[s],n);-1!==r&&(a[s]=String.fromCharCode(r))}a.length>0&&e.toUnicode.amend(a)}(a,a.builtInEncoding);let s=1;t instanceof CFFFont&&(s=t.numGlyphs-1);const r=t.getGlyphMapping(a);let i=null,o=r,l=null;if(!a.cssFontInfo){i=adjustMapping(r,t.hasGlyphId.bind(t),s,this.toUnicode);this.toFontChar=i.toFontChar;o=i.charCodeToGlyphId;l=i.toUnicodeExtraMap}const f=t.numGlyphs;function getCharCodes(e,t){let a=null;for(const n in e)t===e[n]&&(a||=[]).push(0|n);return a}function createCharCode(e,t){for(const a in e)if(t===e[a])return 0|a;i.charCodeToGlyphId[i.nextAvailableFontCharCode]=t;return i.nextAvailableFontCharCode++}const c=t.seacs;if(i&&c?.length){const e=a.fontMatrix||n,s=t.getCharset(),o=Object.create(null);for(let t in c){t|=0;const a=c[t],n=fn[a[2]],l=fn[a[3]],f=s.indexOf(n),h=s.indexOf(l);if(f<0||h<0)continue;const u={x:a[0]*e[0]+a[1]*e[2]+e[4],y:a[0]*e[1]+a[1]*e[3]+e[5]},m=getCharCodes(r,t);if(m)for(const e of m){const t=i.charCodeToGlyphId,a=createCharCode(t,f),n=createCharCode(t,h);o[e]={baseFontCharCode:a,accentFontCharCode:n,accentOffset:u}}}a.seacMap=o}const h=a.fontMatrix?1/Math.max(...a.fontMatrix.slice(0,4).map(Math.abs)):1e3,u=new OpenTypeFileBuilder("OTTO");u.addTable("CFF ",t.data);u.addTable("OS/2",createOS2Table(a,o));u.addTable("cmap",createCmapTable(o,l,f));u.addTable("head",function fontTableHead(){const e=[0,0,0,0,158,11,126,39],t=new TrueTypeTableBuilder({exactLength:54});t.setArray([0,1,0,0]);t.setArray([0,0,16,0]);t.skip(4);t.setArray([95,15,60,245]);t.skip(2);t.setSafeInt16(h);t.setArray(e);t.setArray(e);t.skip(2);t.setSafeInt16(a.descent);t.setArray([15,255]);t.setSafeInt16(a.ascent);t.setInt16(a.italicAngle?2:0);t.setArray([0,17]);t.skip(6);return t.data}());u.addTable("hhea",function fontTableHhea(){const e=new TrueTypeTableBuilder({exactLength:36});e.setArray([0,1,0,0]);e.setSafeInt16(a.ascent);e.setSafeInt16(a.descent);e.skip(2);e.setArray([255,255]);e.skip(6);e.setSafeInt16(a.capHeight);e.setSafeInt16(Math.tan(a.italicAngle)*a.xHeight);e.skip(12);e.setInt16(f);return e.data}());u.addTable("hmtx",function fontTableHmtx(){const e=t.charstrings,a=t.cff?.widths??null,n=new TrueTypeTableBuilder({exactLength:4*f});n.skip(4);for(let t=1,s=f;t=65520&&e<=65535?0:e>=62976&&e<=63743?dn()[e]||e:173===e?45:e}(a)}this.isType3Font&&(s=a);let c=null;if(this.seacMap?.[e]){f=!0;const t=this.seacMap[e];a=t.baseFontCharCode;c={fontChar:String.fromCodePoint(t.accentFontCharCode),offset:t.accentOffset}}let h="";"number"==typeof a&&(a<=1114111?h=String.fromCodePoint(a):warn(`charToGlyph - invalid fontCharCode: ${a}`));if(this.missingFile&&this.vertical&&1===h.length){const e=An()[h.charCodeAt(0)];e&&(h=l=String.fromCharCode(e))}r=new fonts_Glyph(e,h,l,c,n,o,s,t,f);this.#ne.set(e,r);return r}charsToGlyphs(e){let t=this.#ae.get(e);if(t)return t;t=[];if(this.cMap){const a=Object.create(null),n=e.length;let s=0;for(;st.length%2==1,n=this.toUnicode instanceof IdentityToUnicodeMap?e=>this.toUnicode.charCodeOf(e):e=>this.toUnicode.charCodeOf(String.fromCodePoint(e));for(let s=0,r=e.length;s55295&&(r<57344||r>65533)&&s++;if(this.toUnicode){const e=n(r);if(-1!==e){if(hasCurrentBufErrors()){t.push(a.join(""));a.length=0}for(let t=(this.cMap?this.cMap.getCharCodeLength(e):1)-1;t>=0;t--)a.push(String.fromCharCode(e>>8*t&255));continue}}if(!hasCurrentBufErrors()){t.push(a.join(""));a.length=0}a.push(String.fromCodePoint(r))}t.push(a.join(""));return t}}class ErrorFont{constructor(e){this.error=e;this.loadedName="g_font_error";this.missingFile=!0}charsToGlyphs(){return[]}encodeString(e){return[e]}exportData(){return{error:this.error}}}const os=[1.3877,1,1,1,.97801,.92482,.89552,.91133,.81988,.97566,.98152,.93548,.93548,1.2798,.85284,.92794,1,.96134,1.54657,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.82845,.82845,.85284,.85284,.85284,.75859,.92138,.83908,.7762,.73293,.87289,.73133,.7514,.81921,.87356,.95958,.59526,.75727,.69225,1.04924,.9121,.86943,.79795,.88198,.77958,.70864,.81055,.90399,.88653,.96017,.82577,.77892,.78257,.97507,1.54657,.97507,.85284,.89552,.90176,.88762,.8785,.75241,.8785,.90518,.95015,.77618,.8785,.88401,.91916,.86304,.88401,.91488,.8785,.8801,.8785,.8785,.91343,.7173,1.04106,.8785,.85075,.95794,.82616,.85162,.79492,.88331,1.69808,.88331,.85284,.97801,.89552,.91133,.89552,.91133,1.7801,.89552,1.24487,1.13254,1.12401,.96839,.85284,.68787,.70645,.85592,.90747,1.01466,1.0088,.90323,1,1.07463,1,.91056,.75806,1.19118,.96839,.78864,.82845,.84133,.75859,.83908,.83908,.83908,.83908,.83908,.83908,.77539,.73293,.73133,.73133,.73133,.73133,.95958,.95958,.95958,.95958,.88506,.9121,.86943,.86943,.86943,.86943,.86943,.85284,.87508,.90399,.90399,.90399,.90399,.77892,.79795,.90807,.88762,.88762,.88762,.88762,.88762,.88762,.8715,.75241,.90518,.90518,.90518,.90518,.88401,.88401,.88401,.88401,.8785,.8785,.8801,.8801,.8801,.8801,.8801,.90747,.89049,.8785,.8785,.8785,.8785,.85162,.8785,.85162,.83908,.88762,.83908,.88762,.83908,.88762,.73293,.75241,.73293,.75241,.73293,.75241,.73293,.75241,.87289,.83016,.88506,.93125,.73133,.90518,.73133,.90518,.73133,.90518,.73133,.90518,.73133,.90518,.81921,.77618,.81921,.77618,.81921,.77618,1,1,.87356,.8785,.91075,.89608,.95958,.88401,.95958,.88401,.95958,.88401,.95958,.88401,.95958,.88401,.76229,.90167,.59526,.91916,1,1,.86304,.69225,.88401,1,1,.70424,.79468,.91926,.88175,.70823,.94903,.9121,.8785,1,1,.9121,.8785,.87802,.88656,.8785,.86943,.8801,.86943,.8801,.86943,.8801,.87402,.89291,.77958,.91343,1,1,.77958,.91343,.70864,.7173,.70864,.7173,.70864,.7173,.70864,.7173,1,1,.81055,.75841,.81055,1.06452,.90399,.8785,.90399,.8785,.90399,.8785,.90399,.8785,.90399,.8785,.90399,.8785,.96017,.95794,.77892,.85162,.77892,.78257,.79492,.78257,.79492,.78257,.79492,.9297,.56892,.83908,.88762,.77539,.8715,.87508,.89049,1,1,.81055,1.04106,1.20528,1.20528,1,1.15543,.70674,.98387,.94721,1.33431,1.45894,.95161,1.06303,.83908,.80352,.57184,.6965,.56289,.82001,.56029,.81235,1.02988,.83908,.7762,.68156,.80367,.73133,.78257,.87356,.86943,.95958,.75727,.89019,1.04924,.9121,.7648,.86943,.87356,.79795,.78275,.81055,.77892,.9762,.82577,.99819,.84896,.95958,.77892,.96108,1.01407,.89049,1.02988,.94211,.96108,.8936,.84021,.87842,.96399,.79109,.89049,1.00813,1.02988,.86077,.87445,.92099,.84723,.86513,.8801,.75638,.85714,.78216,.79586,.87965,.94211,.97747,.78287,.97926,.84971,1.02988,.94211,.8801,.94211,.84971,.73133,1,1,1,1,1,1,1,1,1,1,1,1,.90264,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.90518,1,1,1,1,1,1,1,1,1,1,1,1,.90548,1,1,1,1,1,1,.96017,.95794,.96017,.95794,.96017,.95794,.77892,.85162,1,1,.89552,.90527,1,.90363,.92794,.92794,.92794,.92794,.87012,.87012,.87012,.89552,.89552,1.42259,.71143,1.06152,1,1,1.03372,1.03372,.97171,1.4956,2.2807,.93835,.83406,.91133,.84107,.91133,1,1,1,.72021,1,1.23108,.83489,.88525,.88525,.81499,.90527,1.81055,.90527,1.81055,1.31006,1.53711,.94434,1.08696,1,.95018,.77192,.85284,.90747,1.17534,.69825,.9716,1.37077,.90747,.90747,.85356,.90747,.90747,1.44947,.85284,.8941,.8941,.70572,.8,.70572,.70572,.70572,.70572,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.99862,.99862,1,1,1,1,1,1.08004,.91027,1,1,1,.99862,1,1,1,1,1,1,1,1,1,1,1,1,.90727,.90727,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],ls={lineHeight:1.2207,lineGap:.2207},fs=[1.3877,1,1,1,.97801,.92482,.89552,.91133,.81988,.97566,.98152,.93548,.93548,1.2798,.85284,.92794,1,.96134,1.56239,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.82845,.82845,.85284,.85284,.85284,.75859,.92138,.83908,.7762,.71805,.87289,.73133,.7514,.81921,.87356,.95958,.59526,.75727,.69225,1.04924,.90872,.85938,.79795,.87068,.77958,.69766,.81055,.90399,.88653,.96068,.82577,.77892,.78257,.97507,1.529,.97507,.85284,.89552,.90176,.94908,.86411,.74012,.86411,.88323,.95015,.86411,.86331,.88401,.91916,.86304,.88401,.9039,.86331,.86331,.86411,.86411,.90464,.70852,1.04106,.86331,.84372,.95794,.82616,.84548,.79492,.88331,1.69808,.88331,.85284,.97801,.89552,.91133,.89552,.91133,1.7801,.89552,1.24487,1.13254,1.19129,.96839,.85284,.68787,.70645,.85592,.90747,1.01466,1.0088,.90323,1,1.07463,1,.91056,.75806,1.19118,.96839,.78864,.82845,.84133,.75859,.83908,.83908,.83908,.83908,.83908,.83908,.77539,.71805,.73133,.73133,.73133,.73133,.95958,.95958,.95958,.95958,.88506,.90872,.85938,.85938,.85938,.85938,.85938,.85284,.87068,.90399,.90399,.90399,.90399,.77892,.79795,.90807,.94908,.94908,.94908,.94908,.94908,.94908,.85887,.74012,.88323,.88323,.88323,.88323,.88401,.88401,.88401,.88401,.8785,.86331,.86331,.86331,.86331,.86331,.86331,.90747,.89049,.86331,.86331,.86331,.86331,.84548,.86411,.84548,.83908,.94908,.83908,.94908,.83908,.94908,.71805,.74012,.71805,.74012,.71805,.74012,.71805,.74012,.87289,.79538,.88506,.92726,.73133,.88323,.73133,.88323,.73133,.88323,.73133,.88323,.73133,.88323,.81921,.86411,.81921,.86411,.81921,.86411,1,1,.87356,.86331,.91075,.8777,.95958,.88401,.95958,.88401,.95958,.88401,.95958,.88401,.95958,.88401,.76467,.90167,.59526,.91916,1,1,.86304,.69225,.88401,1,1,.70424,.77312,.91926,.88175,.70823,.94903,.90872,.86331,1,1,.90872,.86331,.86906,.88116,.86331,.85938,.86331,.85938,.86331,.85938,.86331,.87402,.86549,.77958,.90464,1,1,.77958,.90464,.69766,.70852,.69766,.70852,.69766,.70852,.69766,.70852,1,1,.81055,.75841,.81055,1.06452,.90399,.86331,.90399,.86331,.90399,.86331,.90399,.86331,.90399,.86331,.90399,.86331,.96068,.95794,.77892,.84548,.77892,.78257,.79492,.78257,.79492,.78257,.79492,.9297,.56892,.83908,.94908,.77539,.85887,.87068,.89049,1,1,.81055,1.04106,1.20528,1.20528,1,1.15543,.70088,.98387,.94721,1.33431,1.45894,.95161,1.48387,.83908,.80352,.57118,.6965,.56347,.79179,.55853,.80346,1.02988,.83908,.7762,.67174,.86036,.73133,.78257,.87356,.86441,.95958,.75727,.89019,1.04924,.90872,.74889,.85938,.87891,.79795,.7957,.81055,.77892,.97447,.82577,.97466,.87179,.95958,.77892,.94252,.95612,.8753,1.02988,.92733,.94252,.87411,.84021,.8728,.95612,.74081,.8753,1.02189,1.02988,.84814,.87445,.91822,.84723,.85668,.86331,.81344,.87581,.76422,.82046,.96057,.92733,.99375,.78022,.95452,.86015,1.02988,.92733,.86331,.92733,.86015,.73133,1,1,1,1,1,1,1,1,1,1,1,1,.90631,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.88323,1,1,1,1,1,1,1,1,1,1,1,1,.85174,1,1,1,1,1,1,.96068,.95794,.96068,.95794,.96068,.95794,.77892,.84548,1,1,.89552,.90527,1,.90363,.92794,.92794,.92794,.89807,.87012,.87012,.87012,.89552,.89552,1.42259,.71094,1.06152,1,1,1.03372,1.03372,.97171,1.4956,2.2807,.92972,.83406,.91133,.83326,.91133,1,1,1,.72021,1,1.23108,.83489,.88525,.88525,.81499,.90616,1.81055,.90527,1.81055,1.3107,1.53711,.94434,1.08696,1,.95018,.77192,.85284,.90747,1.17534,.69825,.9716,1.37077,.90747,.90747,.85356,.90747,.90747,1.44947,.85284,.8941,.8941,.70572,.8,.70572,.70572,.70572,.70572,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.99862,.99862,1,1,1,1,1,1.08004,.91027,1,1,1,.99862,1,1,1,1,1,1,1,1,1,1,1,1,.90727,.90727,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],cs={lineHeight:1.2207,lineGap:.2207},hs=[1.3877,1,1,1,1.17223,1.1293,.89552,.91133,.80395,1.02269,1.15601,.91056,.91056,1.2798,.85284,.89807,1,.90861,1.39543,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.96309,.96309,.85284,.85284,.85284,.83319,.88071,.8675,.81552,.72346,.85193,.73206,.7522,.81105,.86275,.90685,.6377,.77892,.75593,1.02638,.89249,.84118,.77452,.85374,.75186,.67789,.79776,.88844,.85066,.94309,.77818,.7306,.76659,1.10369,1.38313,1.10369,1.06139,.89552,.8739,.9245,.9245,.83203,.9245,.85865,1.09842,.9245,.9245,1.03297,1.07692,.90918,1.03297,.94959,.9245,.92274,.9245,.9245,1.02933,.77832,1.20562,.9245,.8916,.98986,.86621,.89453,.79004,.94152,1.77256,.94152,.85284,.97801,.89552,.91133,.89552,.91133,1.91729,.89552,1.17889,1.13254,1.16359,.92098,.85284,.68787,.71353,.84737,.90747,1.0088,1.0044,.87683,1,1.09091,1,.92229,.739,1.15642,.92098,.76288,.80504,.80972,.75859,.8675,.8675,.8675,.8675,.8675,.8675,.76318,.72346,.73206,.73206,.73206,.73206,.90685,.90685,.90685,.90685,.86477,.89249,.84118,.84118,.84118,.84118,.84118,.85284,.84557,.88844,.88844,.88844,.88844,.7306,.77452,.86331,.9245,.9245,.9245,.9245,.9245,.9245,.84843,.83203,.85865,.85865,.85865,.85865,.82601,.82601,.82601,.82601,.94469,.9245,.92274,.92274,.92274,.92274,.92274,.90747,.86651,.9245,.9245,.9245,.9245,.89453,.9245,.89453,.8675,.9245,.8675,.9245,.8675,.9245,.72346,.83203,.72346,.83203,.72346,.83203,.72346,.83203,.85193,.8875,.86477,.99034,.73206,.85865,.73206,.85865,.73206,.85865,.73206,.85865,.73206,.85865,.81105,.9245,.81105,.9245,.81105,.9245,1,1,.86275,.9245,.90872,.93591,.90685,.82601,.90685,.82601,.90685,.82601,.90685,1.03297,.90685,.82601,.77896,1.05611,.6377,1.07692,1,1,.90918,.75593,1.03297,1,1,.76032,.9375,.98156,.93407,.77261,1.11429,.89249,.9245,1,1,.89249,.9245,.92534,.86698,.9245,.84118,.92274,.84118,.92274,.84118,.92274,.8667,.86291,.75186,1.02933,1,1,.75186,1.02933,.67789,.77832,.67789,.77832,.67789,.77832,.67789,.77832,1,1,.79776,.97655,.79776,1.23023,.88844,.9245,.88844,.9245,.88844,.9245,.88844,.9245,.88844,.9245,.88844,.9245,.94309,.98986,.7306,.89453,.7306,.76659,.79004,.76659,.79004,.76659,.79004,1.09231,.54873,.8675,.9245,.76318,.84843,.84557,.86651,1,1,.79776,1.20562,1.18622,1.18622,1,1.1437,.67009,.96334,.93695,1.35191,1.40909,.95161,1.48387,.8675,.90861,.6192,.7363,.64824,.82411,.56321,.85696,1.23516,.8675,.81552,.7286,.84134,.73206,.76659,.86275,.84369,.90685,.77892,.85871,1.02638,.89249,.75828,.84118,.85984,.77452,.76466,.79776,.7306,.90782,.77818,.903,.87291,.90685,.7306,.99058,1.03667,.94635,1.23516,.9849,.99058,.92393,.8916,.942,1.03667,.75026,.94635,1.0297,1.23516,.90918,.94048,.98217,.89746,.84153,.92274,.82507,.88832,.84438,.88178,1.03525,.9849,1.00225,.78086,.97248,.89404,1.23516,.9849,.92274,.9849,.89404,.73206,1,1,1,1,1,1,1,1,1,1,1,1,.89693,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.85865,1,1,1,1,1,1,1,1,1,1,1,1,.90933,1,1,1,1,1,1,.94309,.98986,.94309,.98986,.94309,.98986,.7306,.89453,1,1,.89552,.90527,1,.90186,1.12308,1.12308,1.12308,1.12308,1.2566,1.2566,1.2566,.89552,.89552,1.42259,.68994,1.03809,1,1,1.0176,1.0176,1.11523,1.4956,2.01462,.97858,.82616,.91133,.83437,.91133,1,1,1,.70508,1,1.23108,.79801,.84426,.84426,.774,.90572,1.81055,.90749,1.81055,1.28809,1.55469,.94434,1.07806,1,.97094,.7589,.85284,.90747,1.19658,.69825,.97622,1.33512,.90747,.90747,.85284,.90747,.90747,1.44947,.85284,.8941,.8941,.70572,.8,.70572,.70572,.70572,.70572,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.99862,.99862,1,1,1,1,1,1.0336,.91027,1,1,1,.99862,1,1,1,1,1,1,1,1,1,1,1,1,1.05859,1.05859,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],us={lineHeight:1.2207,lineGap:.2207},ms=[1.3877,1,1,1,1.17223,1.1293,.89552,.91133,.80395,1.02269,1.15601,.91056,.91056,1.2798,.85284,.89807,1,.90861,1.39016,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.96309,.96309,.85284,.85284,.85284,.83319,.88071,.8675,.81552,.73834,.85193,.73206,.7522,.81105,.86275,.90685,.6377,.77892,.75593,1.02638,.89385,.85122,.77452,.86503,.75186,.68887,.79776,.88844,.85066,.94258,.77818,.7306,.76659,1.10369,1.39016,1.10369,1.06139,.89552,.8739,.86128,.94469,.8457,.94469,.89464,1.09842,.84636,.94469,1.03297,1.07692,.90918,1.03297,.95897,.94469,.9482,.94469,.94469,1.04692,.78223,1.20562,.94469,.90332,.98986,.86621,.90527,.79004,.94152,1.77256,.94152,.85284,.97801,.89552,.91133,.89552,.91133,1.91729,.89552,1.17889,1.13254,1.08707,.92098,.85284,.68787,.71353,.84737,.90747,1.0088,1.0044,.87683,1,1.09091,1,.92229,.739,1.15642,.92098,.76288,.80504,.80972,.75859,.8675,.8675,.8675,.8675,.8675,.8675,.76318,.73834,.73206,.73206,.73206,.73206,.90685,.90685,.90685,.90685,.86477,.89385,.85122,.85122,.85122,.85122,.85122,.85284,.85311,.88844,.88844,.88844,.88844,.7306,.77452,.86331,.86128,.86128,.86128,.86128,.86128,.86128,.8693,.8457,.89464,.89464,.89464,.89464,.82601,.82601,.82601,.82601,.94469,.94469,.9482,.9482,.9482,.9482,.9482,.90747,.86651,.94469,.94469,.94469,.94469,.90527,.94469,.90527,.8675,.86128,.8675,.86128,.8675,.86128,.73834,.8457,.73834,.8457,.73834,.8457,.73834,.8457,.85193,.92454,.86477,.9921,.73206,.89464,.73206,.89464,.73206,.89464,.73206,.89464,.73206,.89464,.81105,.84636,.81105,.84636,.81105,.84636,1,1,.86275,.94469,.90872,.95786,.90685,.82601,.90685,.82601,.90685,.82601,.90685,1.03297,.90685,.82601,.77741,1.05611,.6377,1.07692,1,1,.90918,.75593,1.03297,1,1,.76032,.90452,.98156,1.11842,.77261,1.11429,.89385,.94469,1,1,.89385,.94469,.95877,.86901,.94469,.85122,.9482,.85122,.9482,.85122,.9482,.8667,.90016,.75186,1.04692,1,1,.75186,1.04692,.68887,.78223,.68887,.78223,.68887,.78223,.68887,.78223,1,1,.79776,.92188,.79776,1.23023,.88844,.94469,.88844,.94469,.88844,.94469,.88844,.94469,.88844,.94469,.88844,.94469,.94258,.98986,.7306,.90527,.7306,.76659,.79004,.76659,.79004,.76659,.79004,1.09231,.54873,.8675,.86128,.76318,.8693,.85311,.86651,1,1,.79776,1.20562,1.18622,1.18622,1,1.1437,.67742,.96334,.93695,1.35191,1.40909,.95161,1.48387,.86686,.90861,.62267,.74359,.65649,.85498,.56963,.88254,1.23516,.8675,.81552,.75443,.84503,.73206,.76659,.86275,.85122,.90685,.77892,.85746,1.02638,.89385,.75657,.85122,.86275,.77452,.74171,.79776,.7306,.95165,.77818,.89772,.88831,.90685,.7306,.98142,1.02191,.96576,1.23516,.99018,.98142,.9236,.89258,.94035,1.02191,.78848,.96576,.9561,1.23516,.90918,.92578,.95424,.89746,.83969,.9482,.80113,.89442,.85208,.86155,.98022,.99018,1.00452,.81209,.99247,.89181,1.23516,.99018,.9482,.99018,.89181,.73206,1,1,1,1,1,1,1,1,1,1,1,1,.88844,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.89464,1,1,1,1,1,1,1,1,1,1,1,1,.96766,1,1,1,1,1,1,.94258,.98986,.94258,.98986,.94258,.98986,.7306,.90527,1,1,.89552,.90527,1,.90186,1.12308,1.12308,1.12308,1.12308,1.2566,1.2566,1.2566,.89552,.89552,1.42259,.69043,1.03809,1,1,1.0176,1.0176,1.11523,1.4956,2.01462,.99331,.82616,.91133,.84286,.91133,1,1,1,.70508,1,1.23108,.79801,.84426,.84426,.774,.90527,1.81055,.90527,1.81055,1.28809,1.55469,.94434,1.07806,1,.97094,.7589,.85284,.90747,1.19658,.69825,.97622,1.33512,.90747,.90747,.85356,.90747,.90747,1.44947,.85284,.8941,.8941,.70572,.8,.70572,.70572,.70572,.70572,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.99862,.99862,1,1,1,1,1,1.0336,.91027,1,1,1,.99862,1,1,1,1,1,1,1,1,1,1,1,1,1.05859,1.05859,1,1,1,1.07185,.99413,.96334,1.08065,1,1,1,1,1,1,1,1,1,1,1],ps={lineHeight:1.2207,lineGap:.2207},ds=[.76116,1,1,1.0006,.99998,.99974,.99973,.99973,.99982,.99977,1.00087,.99998,.99998,.99959,1.00003,1.0006,.99998,1.0006,1.0006,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99998,1,1.00003,1.00003,1.00003,1.00026,.9999,.99977,.99977,.99977,.99977,1.00001,1.00026,1.00022,.99977,1.0006,.99973,.99977,1.00026,.99999,.99977,1.00022,1.00001,1.00022,.99977,1.00001,1.00026,.99977,1.00001,1.00016,1.00001,1.00001,1.00026,.99998,1.0006,.99998,1.00003,.99973,.99998,.99973,1.00026,.99973,1.00026,.99973,.99998,1.00026,1.00026,1.0006,1.0006,.99973,1.0006,.99982,1.00026,1.00026,1.00026,1.00026,.99959,.99973,.99998,1.00026,.99973,1.00022,.99973,.99973,1,.99959,1.00077,.99959,1.00003,.99998,.99973,.99973,.99973,.99973,1.00077,.99973,.99998,1.00025,.99968,.99973,1.00003,1.00025,.60299,1.00024,1.06409,1,1,.99998,1,.99973,1.0006,.99998,1,.99936,.99973,1.00002,1.00002,1.00002,1.00026,.99977,.99977,.99977,.99977,.99977,.99977,1,.99977,1.00001,1.00001,1.00001,1.00001,1.0006,1.0006,1.0006,1.0006,.99977,.99977,1.00022,1.00022,1.00022,1.00022,1.00022,1.00003,1.00022,.99977,.99977,.99977,.99977,1.00001,1.00001,1.00026,.99973,.99973,.99973,.99973,.99973,.99973,.99982,.99973,.99973,.99973,.99973,.99973,1.0006,1.0006,1.0006,1.0006,1.00026,1.00026,1.00026,1.00026,1.00026,1.00026,1.00026,1.06409,1.00026,1.00026,1.00026,1.00026,1.00026,.99973,1.00026,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,1.03374,.99977,1.00026,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00022,1.00026,1.00022,1.00026,1.00022,1.00026,1.00022,1.00026,.99977,1.00026,.99977,1.00026,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.00042,.99973,.99973,1.0006,.99977,.99973,.99973,1.00026,1.0006,1.00026,1.0006,1.00026,1.03828,1.00026,.99999,1.00026,1.0006,.99977,1.00026,.99977,1.00026,.99977,1.00026,.9993,.9998,1.00026,1.00022,1.00026,1.00022,1.00026,1.00022,1.00026,1,1.00016,.99977,.99959,.99977,.99959,.99977,.99959,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00026,.99998,1.00026,.8121,1.00026,.99998,.99977,1.00026,.99977,1.00026,.99977,1.00026,.99977,1.00026,.99977,1.00026,.99977,1.00026,1.00016,1.00022,1.00001,.99973,1.00001,1.00026,1,1.00026,1,1.00026,1,1.0006,.99973,.99977,.99973,1,.99982,1.00022,1.00026,1.00001,.99973,1.00026,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,1.00034,.99977,1,.99997,1.00026,1.00078,1.00036,.99973,1.00013,1.0006,.99977,.99977,.99988,.85148,1.00001,1.00026,.99977,1.00022,1.0006,.99977,1.00001,.99999,.99977,1.00069,1.00022,.99977,1.00001,.99984,1.00026,1.00001,1.00024,1.00001,.9999,1,1.0006,1.00001,1.00041,.99962,1.00026,1.0006,.99995,1.00041,.99942,.99973,.99927,1.00082,.99902,1.00026,1.00087,1.0006,1.00069,.99973,.99867,.99973,.9993,1.00026,1.00049,1.00056,1,.99988,.99935,.99995,.99954,1.00055,.99945,1.00032,1.0006,.99995,1.00026,.99995,1.00032,1.00001,1.00008,.99971,1.00019,.9994,1.00001,1.0006,1.00044,.99973,1.00023,1.00047,1,.99942,.99561,.99989,1.00035,.99977,1.00035,.99977,1.00019,.99944,1.00001,1.00021,.99926,1.00035,1.00035,.99942,1.00048,.99999,.99977,1.00022,1.00035,1.00001,.99977,1.00026,.99989,1.00057,1.00001,.99936,1.00052,1.00012,.99996,1.00043,1,1.00035,.9994,.99976,1.00035,.99973,1.00052,1.00041,1.00119,1.00037,.99973,1.00002,.99986,1.00041,1.00041,.99902,.9996,1.00034,.99999,1.00026,.99999,1.00026,.99973,1.00052,.99973,1,.99973,1.00041,1.00075,.9994,1.0003,.99999,1,1.00041,.99955,1,.99915,.99973,.99973,1.00026,1.00119,.99955,.99973,1.0006,.99911,1.0006,1.00026,.99972,1.00026,.99902,1.00041,.99973,.99999,1,1,1.00038,1.0005,1.00016,1.00022,1.00016,1.00022,1.00016,1.00022,1.00001,.99973,1,1,.99973,1,1,.99955,1.0006,1.0006,1.0006,1.0006,1,1,1,.99973,.99973,.99972,1,1,1.00106,.99999,.99998,.99998,.99999,.99998,1.66475,1,.99973,.99973,1.00023,.99973,.99971,1.00047,1.00023,1,.99991,.99984,1.00002,1.00002,1.00002,1.00002,1,1,1,1,1,1,1,.99972,1,1.20985,1.39713,1.00003,1.00031,1.00015,1,.99561,1.00027,1.00031,1.00031,.99915,1.00031,1.00031,.99999,1.00003,.99999,.99999,1.41144,1.6,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.40579,1.40579,1.36625,.99999,1,.99861,.99861,1,1.00026,1.00026,1.00026,1.00026,.99972,.99999,.99999,.99999,.99999,1.40483,1,.99977,1.00054,1,1,.99953,.99962,1.00042,.9995,1,1,1,1,1,1,1,1,.99998,.99998,.99998,.99998,1,1,1,1,1,1,1,1,1,1,1],gs={lineHeight:1.2,lineGap:.2},bs=[.76116,1,1,1.0006,.99998,.99974,.99973,.99973,.99982,.99977,1.00087,.99998,.99998,.99959,1.00003,1.0006,.99998,1.0006,1.0006,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99998,1,1.00003,1.00003,1.00003,1.00026,.9999,.99977,.99977,.99977,.99977,1.00001,1.00026,1.00022,.99977,1.0006,.99973,.99977,1.00026,.99999,.99977,1.00022,1.00001,1.00022,.99977,1.00001,1.00026,.99977,1.00001,1.00016,1.00001,1.00001,1.00026,.99998,1.0006,.99998,1.00003,.99973,.99998,.99973,1.00026,.99973,1.00026,.99973,.99998,1.00026,1.00026,1.0006,1.0006,.99973,1.0006,.99982,1.00026,1.00026,1.00026,1.00026,.99959,.99973,.99998,1.00026,.99973,1.00022,.99973,.99973,1,.99959,1.00077,.99959,1.00003,.99998,.99973,.99973,.99973,.99973,1.00077,.99973,.99998,1.00025,.99968,.99973,1.00003,1.00025,.60299,1.00024,1.06409,1,1,.99998,1,.99973,1.0006,.99998,1,.99936,.99973,1.00002,1.00002,1.00002,1.00026,.99977,.99977,.99977,.99977,.99977,.99977,1,.99977,1.00001,1.00001,1.00001,1.00001,1.0006,1.0006,1.0006,1.0006,.99977,.99977,1.00022,1.00022,1.00022,1.00022,1.00022,1.00003,1.00022,.99977,.99977,.99977,.99977,1.00001,1.00001,1.00026,.99973,.99973,.99973,.99973,.99973,.99973,.99982,.99973,.99973,.99973,.99973,.99973,1.0006,1.0006,1.0006,1.0006,1.00026,1.00026,1.00026,1.00026,1.00026,1.00026,1.00026,1.06409,1.00026,1.00026,1.00026,1.00026,1.00026,.99973,1.00026,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,1.0044,.99977,1.00026,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00022,1.00026,1.00022,1.00026,1.00022,1.00026,1.00022,1.00026,.99977,1.00026,.99977,1.00026,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,.99971,.99973,.99973,1.0006,.99977,.99973,.99973,1.00026,1.0006,1.00026,1.0006,1.00026,1.01011,1.00026,.99999,1.00026,1.0006,.99977,1.00026,.99977,1.00026,.99977,1.00026,.9993,.9998,1.00026,1.00022,1.00026,1.00022,1.00026,1.00022,1.00026,1,1.00016,.99977,.99959,.99977,.99959,.99977,.99959,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00026,.99998,1.00026,.8121,1.00026,.99998,.99977,1.00026,.99977,1.00026,.99977,1.00026,.99977,1.00026,.99977,1.00026,.99977,1.00026,1.00016,1.00022,1.00001,.99973,1.00001,1.00026,1,1.00026,1,1.00026,1,1.0006,.99973,.99977,.99973,1,.99982,1.00022,1.00026,1.00001,.99973,1.00026,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99977,1,1,1.00026,.99969,.99972,.99981,.9998,1.0006,.99977,.99977,1.00022,.91155,1.00001,1.00026,.99977,1.00022,1.0006,.99977,1.00001,.99999,.99977,.99966,1.00022,1.00032,1.00001,.99944,1.00026,1.00001,.99968,1.00001,1.00047,1,1.0006,1.00001,.99981,1.00101,1.00026,1.0006,.99948,.99981,1.00064,.99973,.99942,1.00101,1.00061,1.00026,1.00069,1.0006,1.00014,.99973,1.01322,.99973,1.00065,1.00026,1.00012,.99923,1,1.00064,1.00076,.99948,1.00055,1.00063,1.00007,.99943,1.0006,.99948,1.00026,.99948,.99943,1.00001,1.00001,1.00029,1.00038,1.00035,1.00001,1.0006,1.0006,.99973,.99978,1.00001,1.00057,.99989,.99967,.99964,.99967,.99977,.99999,.99977,1.00038,.99977,1.00001,.99973,1.00066,.99967,.99967,1.00041,.99998,.99999,.99977,1.00022,.99967,1.00001,.99977,1.00026,.99964,1.00031,1.00001,.99999,.99999,1,1.00023,1,1,.99999,1.00035,1.00001,.99999,.99973,.99977,.99999,1.00058,.99973,.99973,.99955,.9995,1.00026,1.00026,1.00032,.99989,1.00034,.99999,1.00026,1.00026,1.00026,.99973,.45998,.99973,1.00026,.99973,1.00001,.99999,.99982,.99994,.99996,1,1.00042,1.00044,1.00029,1.00023,.99973,.99973,1.00026,.99949,1.00002,.99973,1.0006,1.0006,1.0006,.99975,1.00026,1.00026,1.00032,.98685,.99973,1.00026,1,1,.99966,1.00044,1.00016,1.00022,1.00016,1.00022,1.00016,1.00022,1.00001,.99973,1,1,.99973,1,1,.99955,1.0006,1.0006,1.0006,1.0006,1,1,1,.99973,.99973,.99972,1,1,1.00106,.99999,.99998,.99998,.99999,.99998,1.66475,1,.99973,.99973,1,.99973,.99971,.99978,1,1,.99991,.99984,1.00002,1.00002,1.00002,1.00002,1.00098,1,1,1,1.00049,1,1,.99972,1,1.20985,1.39713,1.00003,1.00031,1.00015,1,.99561,1.00027,1.00031,1.00031,.99915,1.00031,1.00031,.99999,1.00003,.99999,.99999,1.41144,1.6,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.40579,1.40579,1.36625,.99999,1,.99861,.99861,1,1.00026,1.00026,1.00026,1.00026,.99972,.99999,.99999,.99999,.99999,1.40483,1,.99977,1.00054,1,1,.99953,.99962,1.00042,.9995,1,1,1,1,1,1,1,1,.99998,.99998,.99998,.99998,1,1,1,1,1,1,1,1,1,1,1],ws={lineHeight:1.35,lineGap:.2},js=[.76116,1,1,1.0006,1.0006,1.00006,.99973,.99973,.99982,1.00001,1.00043,.99998,.99998,.99959,1.00003,1.0006,.99998,1.0006,1.0006,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,1.0006,1,1.00003,1.00003,1.00003,.99973,.99987,1.00001,1.00001,.99977,.99977,1.00001,1.00026,1.00022,.99977,1.0006,1,1.00001,.99973,.99999,.99977,1.00022,1.00001,1.00022,.99977,1.00001,1.00026,.99977,1.00001,1.00016,1.00001,1.00001,1.00026,1.0006,1.0006,1.0006,.99949,.99973,.99998,.99973,.99973,1,.99973,.99973,1.0006,.99973,.99973,.99924,.99924,1,.99924,.99999,.99973,.99973,.99973,.99973,.99998,1,1.0006,.99973,1,.99977,1,1,1,1.00005,1.0009,1.00005,1.00003,.99998,.99973,.99973,.99973,.99973,1.0009,.99973,.99998,1.00025,.99968,.99973,1.00003,1.00025,.60299,1.00024,1.06409,1,1,.99998,1,.9998,1.0006,.99998,1,.99936,.99973,1.00002,1.00002,1.00002,1.00026,1.00001,1.00001,1.00001,1.00001,1.00001,1.00001,1,.99977,1.00001,1.00001,1.00001,1.00001,1.0006,1.0006,1.0006,1.0006,.99977,.99977,1.00022,1.00022,1.00022,1.00022,1.00022,1.00003,1.00022,.99977,.99977,.99977,.99977,1.00001,1.00001,1.00026,.99973,.99973,.99973,.99973,.99973,.99973,.99982,1,.99973,.99973,.99973,.99973,1.0006,1.0006,1.0006,1.0006,.99973,.99973,.99973,.99973,.99973,.99973,.99973,1.06409,1.00026,.99973,.99973,.99973,.99973,1,.99973,1,1.00001,.99973,1.00001,.99973,1.00001,.99973,.99977,1,.99977,1,.99977,1,.99977,1,.99977,1.0288,.99977,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00022,.99973,1.00022,.99973,1.00022,.99973,1.00022,.99973,.99977,.99973,.99977,.99973,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,.99924,1.0006,1.0006,.99946,1.00034,1,.99924,1.00001,1,1,.99973,.99924,.99973,.99924,.99973,1.06311,.99973,1.00024,.99973,.99924,.99977,.99973,.99977,.99973,.99977,.99973,1.00041,.9998,.99973,1.00022,.99973,1.00022,.99973,1.00022,.99973,1,1.00016,.99977,.99998,.99977,.99998,.99977,.99998,1.00001,1,1.00001,1,1.00001,1,1.00001,1,1.00026,1.0006,1.00026,.89547,1.00026,1.0006,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,1.00016,.99977,1.00001,1,1.00001,1.00026,1,1.00026,1,1.00026,1,.99924,.99973,1.00001,.99973,1,.99982,1.00022,1.00026,1.00001,1,1.00026,1.0006,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,1.00001,1,1.00054,.99977,1.00084,1.00007,.99973,1.00013,.99924,1.00001,1.00001,.99945,.91221,1.00001,1.00026,.99977,1.00022,1.0006,1.00001,1.00001,.99999,.99977,.99933,1.00022,1.00054,1.00001,1.00065,1.00026,1.00001,1.0001,1.00001,1.00052,1,1.0006,1.00001,.99945,.99897,.99968,.99924,1.00036,.99945,.99949,1,1.0006,.99897,.99918,.99968,.99911,.99924,1,.99962,1.01487,1,1.0005,.99973,1.00012,1.00043,1,.99995,.99994,1.00036,.99947,1.00019,1.00063,1.00025,.99924,1.00036,.99973,1.00036,1.00025,1.00001,1.00001,1.00027,1.0001,1.00068,1.00001,1.0006,1.0006,1,1.00008,.99957,.99972,.9994,.99954,.99975,1.00051,1.00001,1.00019,1.00001,1.0001,.99986,1.00001,1.00001,1.00038,.99954,.99954,.9994,1.00066,.99999,.99977,1.00022,1.00054,1.00001,.99977,1.00026,.99975,1.0001,1.00001,.99993,.9995,.99955,1.00016,.99978,.99974,1.00019,1.00022,.99955,1.00053,.99973,1.00089,1.00005,.99967,1.00048,.99973,1.00002,1.00034,.99973,.99973,.99964,1.00006,1.00066,.99947,.99973,.98894,.99973,1,.44898,1,.99946,1,1.00039,1.00082,.99991,.99991,.99985,1.00022,1.00023,1.00061,1.00006,.99966,.99973,.99973,.99973,1.00019,1.0008,1,.99924,.99924,.99924,.99983,1.00044,.99973,.99964,.98332,1,.99973,1,1,.99962,.99895,1.00016,.99977,1.00016,.99977,1.00016,.99977,1.00001,1,1,1,.99973,1,1,.99955,.99924,.99924,.99924,.99924,.99998,.99998,.99998,.99973,.99973,.99972,1,1,1.00267,.99999,.99998,.99998,1,.99998,1.66475,1,.99973,.99973,1.00023,.99973,1.00423,.99925,.99999,1,.99991,.99984,1.00002,1.00002,1.00002,1.00002,1.00049,1,1.00245,1,1,1,1,.96329,1,1.20985,1.39713,1.00003,.8254,1.00015,1,1.00035,1.00027,1.00031,1.00031,1.00003,1.00031,1.00031,.99999,1.00003,.99999,.99999,1.41144,1.6,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.40579,1.40579,1.36625,.99999,1,.99861,.99861,1,1.00026,1.00026,1.00026,1.00026,.95317,.99999,.99999,.99999,.99999,1.40483,1,.99977,1.00054,1,1,.99953,.99962,1.00042,.9995,1,1,1,1,1,1,1,1,.99998,.99998,.99998,.99998,1,1,1,1,1,1,1,1,1,1,1],ks={lineHeight:1.35,lineGap:.2},ys=[.76116,1,1,1.0006,1.0006,1.00006,.99973,.99973,.99982,1.00001,1.00043,.99998,.99998,.99959,1.00003,1.0006,.99998,1.0006,1.0006,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,1.0006,1,1.00003,1.00003,1.00003,.99973,.99987,1.00001,1.00001,.99977,.99977,1.00001,1.00026,1.00022,.99977,1.0006,1,1.00001,.99973,.99999,.99977,1.00022,1.00001,1.00022,.99977,1.00001,1.00026,.99977,1.00001,1.00016,1.00001,1.00001,1.00026,1.0006,1.0006,1.0006,.99949,.99973,.99998,.99973,.99973,1,.99973,.99973,1.0006,.99973,.99973,.99924,.99924,1,.99924,.99999,.99973,.99973,.99973,.99973,.99998,1,1.0006,.99973,1,.99977,1,1,1,1.00005,1.0009,1.00005,1.00003,.99998,.99973,.99973,.99973,.99973,1.0009,.99973,.99998,1.00025,.99968,.99973,1.00003,1.00025,.60299,1.00024,1.06409,1,1,.99998,1,.9998,1.0006,.99998,1,.99936,.99973,1.00002,1.00002,1.00002,1.00026,1.00001,1.00001,1.00001,1.00001,1.00001,1.00001,1,.99977,1.00001,1.00001,1.00001,1.00001,1.0006,1.0006,1.0006,1.0006,.99977,.99977,1.00022,1.00022,1.00022,1.00022,1.00022,1.00003,1.00022,.99977,.99977,.99977,.99977,1.00001,1.00001,1.00026,.99973,.99973,.99973,.99973,.99973,.99973,.99982,1,.99973,.99973,.99973,.99973,1.0006,1.0006,1.0006,1.0006,.99973,.99973,.99973,.99973,.99973,.99973,.99973,1.06409,1.00026,.99973,.99973,.99973,.99973,1,.99973,1,1.00001,.99973,1.00001,.99973,1.00001,.99973,.99977,1,.99977,1,.99977,1,.99977,1,.99977,1.04596,.99977,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00022,.99973,1.00022,.99973,1.00022,.99973,1.00022,.99973,.99977,.99973,.99977,.99973,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,.99924,1.0006,1.0006,1.00019,1.00034,1,.99924,1.00001,1,1,.99973,.99924,.99973,.99924,.99973,1.02572,.99973,1.00005,.99973,.99924,.99977,.99973,.99977,.99973,.99977,.99973,.99999,.9998,.99973,1.00022,.99973,1.00022,.99973,1.00022,.99973,1,1.00016,.99977,.99998,.99977,.99998,.99977,.99998,1.00001,1,1.00001,1,1.00001,1,1.00001,1,1.00026,1.0006,1.00026,.84533,1.00026,1.0006,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,1.00016,.99977,1.00001,1,1.00001,1.00026,1,1.00026,1,1.00026,1,.99924,.99973,1.00001,.99973,1,.99982,1.00022,1.00026,1.00001,1,1.00026,1.0006,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99928,1,.99977,1.00013,1.00055,.99947,.99945,.99941,.99924,1.00001,1.00001,1.0004,.91621,1.00001,1.00026,.99977,1.00022,1.0006,1.00001,1.00005,.99999,.99977,1.00015,1.00022,.99977,1.00001,.99973,1.00026,1.00001,1.00019,1.00001,.99946,1,1.0006,1.00001,.99978,1.00045,.99973,.99924,1.00023,.99978,.99966,1,1.00065,1.00045,1.00019,.99973,.99973,.99924,1,1,.96499,1,1.00055,.99973,1.00008,1.00027,1,.9997,.99995,1.00023,.99933,1.00019,1.00015,1.00031,.99924,1.00023,.99973,1.00023,1.00031,1.00001,.99928,1.00029,1.00092,1.00035,1.00001,1.0006,1.0006,1,.99988,.99975,1,1.00082,.99561,.9996,1.00035,1.00001,.99962,1.00001,1.00092,.99964,1.00001,.99963,.99999,1.00035,1.00035,1.00082,.99962,.99999,.99977,1.00022,1.00035,1.00001,.99977,1.00026,.9996,.99967,1.00001,1.00034,1.00074,1.00054,1.00053,1.00063,.99971,.99962,1.00035,.99975,.99977,.99973,1.00043,.99953,1.0007,.99915,.99973,1.00008,.99892,1.00073,1.00073,1.00114,.99915,1.00073,.99955,.99973,1.00092,.99973,1,.99998,1,1.0003,1,1.00043,1.00001,.99969,1.0003,1,1.00035,1.00001,.9995,1,1.00092,.99973,.99973,.99973,1.0007,.9995,1,.99924,1.0006,.99924,.99972,1.00062,.99973,1.00114,1.00073,1,.99955,1,1,1.00047,.99968,1.00016,.99977,1.00016,.99977,1.00016,.99977,1.00001,1,1,1,.99973,1,1,.99955,.99924,.99924,.99924,.99924,.99998,.99998,.99998,.99973,.99973,.99972,1,1,1.00267,.99999,.99998,.99998,1,.99998,1.66475,1,.99973,.99973,1.00023,.99973,.99971,.99925,1.00023,1,.99991,.99984,1.00002,1.00002,1.00002,1.00002,1,1,1,1,1,1,1,.96329,1,1.20985,1.39713,1.00003,.8254,1.00015,1,1.00035,1.00027,1.00031,1.00031,.99915,1.00031,1.00031,.99999,1.00003,.99999,.99999,1.41144,1.6,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.40579,1.40579,1.36625,.99999,1,.99861,.99861,1,1.00026,1.00026,1.00026,1.00026,.95317,.99999,.99999,.99999,.99999,1.40483,1,.99977,1.00054,1,1,.99953,.99962,1.00042,.9995,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],qs={lineHeight:1.2,lineGap:.2},vs=[365,0,333,278,333,474,556,556,889,722,238,333,333,389,584,278,333,278,278,556,556,556,556,556,556,556,556,556,556,333,333,584,584,584,611,975,722,722,722,722,667,611,778,722,278,556,722,611,833,722,778,667,778,722,667,611,722,667,944,667,667,611,333,278,333,584,556,333,556,611,556,611,556,333,611,611,278,278,556,278,889,611,611,611,611,389,556,333,611,556,778,556,556,500,389,280,389,584,333,556,556,556,556,280,556,333,737,370,556,584,737,552,400,549,333,333,333,576,556,278,333,333,365,556,834,834,834,611,722,722,722,722,722,722,1e3,722,667,667,667,667,278,278,278,278,722,722,778,778,778,778,778,584,778,722,722,722,722,667,667,611,556,556,556,556,556,556,889,556,556,556,556,556,278,278,278,278,611,611,611,611,611,611,611,549,611,611,611,611,611,556,611,556,722,556,722,556,722,556,722,556,722,556,722,556,722,556,722,719,722,611,667,556,667,556,667,556,667,556,667,556,778,611,778,611,778,611,778,611,722,611,722,611,278,278,278,278,278,278,278,278,278,278,785,556,556,278,722,556,556,611,278,611,278,611,385,611,479,611,278,722,611,722,611,722,611,708,723,611,778,611,778,611,778,611,1e3,944,722,389,722,389,722,389,667,556,667,556,667,556,667,556,611,333,611,479,611,333,722,611,722,611,722,611,722,611,722,611,722,611,944,778,667,556,667,611,500,611,500,611,500,278,556,722,556,1e3,889,778,611,667,556,611,333,333,333,333,333,333,333,333,333,333,333,465,722,333,853,906,474,825,927,838,278,722,722,601,719,667,611,722,778,278,722,667,833,722,644,778,722,667,600,611,667,821,667,809,802,278,667,615,451,611,278,582,615,610,556,606,475,460,611,541,278,558,556,612,556,445,611,766,619,520,684,446,582,715,576,753,845,278,582,611,582,845,667,669,885,567,711,667,278,276,556,1094,1062,875,610,722,622,719,722,719,722,567,712,667,904,626,719,719,610,702,833,722,778,719,667,722,611,622,854,667,730,703,1005,1019,870,979,719,711,1031,719,556,618,615,417,635,556,709,497,615,615,500,635,740,604,611,604,611,556,490,556,875,556,615,581,833,844,729,854,615,552,854,583,556,556,611,417,552,556,278,281,278,969,906,611,500,615,556,604,778,611,487,447,944,778,944,778,944,778,667,556,333,333,556,1e3,1e3,552,278,278,278,278,500,500,500,556,556,350,1e3,1e3,240,479,333,333,604,333,167,396,556,556,1094,556,885,489,1115,1e3,768,600,834,834,834,834,1e3,500,1e3,500,1e3,500,500,494,612,823,713,584,549,713,979,722,274,549,549,583,549,549,604,584,604,604,708,625,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,729,604,604,354,354,1e3,990,990,990,990,494,604,604,604,604,354,1021,1052,917,750,750,531,656,594,510,500,750,750,611,611,333,333,333,333,333,333,333,333,222,222,333,333,333,333,333,333,333,333],Ss=[-1,-1,-1,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,161,162,163,164,165,166,167,168,169,170,171,172,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,402,506,507,508,509,510,511,536,537,538,539,710,711,713,728,729,730,731,732,733,900,901,902,903,904,905,906,908,910,911,912,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,931,932,933,934,935,936,937,938,939,940,941,942,943,944,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,962,963,964,965,966,967,968,969,970,971,972,973,974,1024,1025,1026,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1037,1038,1039,1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,1104,1105,1106,1107,1108,1109,1110,1111,1112,1113,1114,1115,1116,1117,1118,1119,1138,1139,1168,1169,7808,7809,7810,7811,7812,7813,7922,7923,8208,8209,8211,8212,8213,8215,8216,8217,8218,8219,8220,8221,8222,8224,8225,8226,8230,8240,8242,8243,8249,8250,8252,8254,8260,8319,8355,8356,8359,8364,8453,8467,8470,8482,8486,8494,8539,8540,8541,8542,8592,8593,8594,8595,8596,8597,8616,8706,8710,8719,8721,8722,8730,8734,8735,8745,8747,8776,8800,8801,8804,8805,8962,8976,8992,8993,9472,9474,9484,9488,9492,9496,9500,9508,9516,9524,9532,9552,9553,9554,9555,9556,9557,9558,9559,9560,9561,9562,9563,9564,9565,9566,9567,9568,9569,9570,9571,9572,9573,9574,9575,9576,9577,9578,9579,9580,9600,9604,9608,9612,9616,9617,9618,9619,9632,9633,9642,9643,9644,9650,9658,9660,9668,9674,9675,9679,9688,9689,9702,9786,9787,9788,9792,9794,9824,9827,9829,9830,9834,9835,9836,61441,61442,61445,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],As=[365,0,333,278,333,474,556,556,889,722,238,333,333,389,584,278,333,278,278,556,556,556,556,556,556,556,556,556,556,333,333,584,584,584,611,975,722,722,722,722,667,611,778,722,278,556,722,611,833,722,778,667,778,722,667,611,722,667,944,667,667,611,333,278,333,584,556,333,556,611,556,611,556,333,611,611,278,278,556,278,889,611,611,611,611,389,556,333,611,556,778,556,556,500,389,280,389,584,333,556,556,556,556,280,556,333,737,370,556,584,737,552,400,549,333,333,333,576,556,278,333,333,365,556,834,834,834,611,722,722,722,722,722,722,1e3,722,667,667,667,667,278,278,278,278,722,722,778,778,778,778,778,584,778,722,722,722,722,667,667,611,556,556,556,556,556,556,889,556,556,556,556,556,278,278,278,278,611,611,611,611,611,611,611,549,611,611,611,611,611,556,611,556,722,556,722,556,722,556,722,556,722,556,722,556,722,556,722,740,722,611,667,556,667,556,667,556,667,556,667,556,778,611,778,611,778,611,778,611,722,611,722,611,278,278,278,278,278,278,278,278,278,278,782,556,556,278,722,556,556,611,278,611,278,611,396,611,479,611,278,722,611,722,611,722,611,708,723,611,778,611,778,611,778,611,1e3,944,722,389,722,389,722,389,667,556,667,556,667,556,667,556,611,333,611,479,611,333,722,611,722,611,722,611,722,611,722,611,722,611,944,778,667,556,667,611,500,611,500,611,500,278,556,722,556,1e3,889,778,611,667,556,611,333,333,333,333,333,333,333,333,333,333,333,333,722,333,854,906,473,844,930,847,278,722,722,610,671,667,611,722,778,278,722,667,833,722,657,778,718,667,590,611,667,822,667,829,781,278,667,620,479,611,278,591,620,621,556,610,479,492,611,558,278,566,556,603,556,450,611,712,605,532,664,409,591,704,578,773,834,278,591,611,591,834,667,667,886,614,719,667,278,278,556,1094,1042,854,622,719,677,719,722,708,722,614,722,667,927,643,719,719,615,687,833,722,778,719,667,722,611,677,781,667,729,708,979,989,854,1e3,708,719,1042,729,556,619,604,534,618,556,736,510,611,611,507,622,740,604,611,611,611,556,889,556,885,556,646,583,889,935,707,854,594,552,865,589,556,556,611,469,563,556,278,278,278,969,906,611,507,619,556,611,778,611,575,467,944,778,944,778,944,778,667,556,333,333,556,1e3,1e3,552,278,278,278,278,500,500,500,556,556,350,1e3,1e3,240,479,333,333,604,333,167,396,556,556,1104,556,885,516,1146,1e3,768,600,834,834,834,834,999,500,1e3,500,1e3,500,500,494,612,823,713,584,549,713,979,722,274,549,549,583,549,549,604,584,604,604,708,625,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,729,604,604,354,354,1e3,990,990,990,990,494,604,604,604,604,354,1021,1052,917,750,750,531,656,594,510,500,750,750,611,611,333,333,333,333,333,333,333,333,222,222,333,333,333,333,333,333,333,333],xs=[-1,-1,-1,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,161,162,163,164,165,166,167,168,169,170,171,172,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,402,506,507,508,509,510,511,536,537,538,539,710,711,713,728,729,730,731,732,733,900,901,902,903,904,905,906,908,910,911,912,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,931,932,933,934,935,936,937,938,939,940,941,942,943,944,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,962,963,964,965,966,967,968,969,970,971,972,973,974,1024,1025,1026,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1037,1038,1039,1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,1104,1105,1106,1107,1108,1109,1110,1111,1112,1113,1114,1115,1116,1117,1118,1119,1138,1139,1168,1169,7808,7809,7810,7811,7812,7813,7922,7923,8208,8209,8211,8212,8213,8215,8216,8217,8218,8219,8220,8221,8222,8224,8225,8226,8230,8240,8242,8243,8249,8250,8252,8254,8260,8319,8355,8356,8359,8364,8453,8467,8470,8482,8486,8494,8539,8540,8541,8542,8592,8593,8594,8595,8596,8597,8616,8706,8710,8719,8721,8722,8730,8734,8735,8745,8747,8776,8800,8801,8804,8805,8962,8976,8992,8993,9472,9474,9484,9488,9492,9496,9500,9508,9516,9524,9532,9552,9553,9554,9555,9556,9557,9558,9559,9560,9561,9562,9563,9564,9565,9566,9567,9568,9569,9570,9571,9572,9573,9574,9575,9576,9577,9578,9579,9580,9600,9604,9608,9612,9616,9617,9618,9619,9632,9633,9642,9643,9644,9650,9658,9660,9668,9674,9675,9679,9688,9689,9702,9786,9787,9788,9792,9794,9824,9827,9829,9830,9834,9835,9836,61441,61442,61445,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],Cs=[365,0,333,278,278,355,556,556,889,667,191,333,333,389,584,278,333,278,278,556,556,556,556,556,556,556,556,556,556,278,278,584,584,584,556,1015,667,667,722,722,667,611,778,722,278,500,667,556,833,722,778,667,778,722,667,611,722,667,944,667,667,611,278,278,278,469,556,333,556,556,500,556,556,278,556,556,222,222,500,222,833,556,556,556,556,333,500,278,556,500,722,500,500,500,334,260,334,584,333,556,556,556,556,260,556,333,737,370,556,584,737,552,400,549,333,333,333,576,537,278,333,333,365,556,834,834,834,611,667,667,667,667,667,667,1e3,722,667,667,667,667,278,278,278,278,722,722,778,778,778,778,778,584,778,722,722,722,722,667,667,611,556,556,556,556,556,556,889,500,556,556,556,556,278,278,278,278,556,556,556,556,556,556,556,549,611,556,556,556,556,500,556,500,667,556,667,556,667,556,722,500,722,500,722,500,722,500,722,625,722,556,667,556,667,556,667,556,667,556,667,556,778,556,778,556,778,556,778,556,722,556,722,556,278,278,278,278,278,278,278,222,278,278,733,444,500,222,667,500,500,556,222,556,222,556,281,556,400,556,222,722,556,722,556,722,556,615,723,556,778,556,778,556,778,556,1e3,944,722,333,722,333,722,333,667,500,667,500,667,500,667,500,611,278,611,354,611,278,722,556,722,556,722,556,722,556,722,556,722,556,944,722,667,500,667,611,500,611,500,611,500,222,556,667,556,1e3,889,778,611,667,500,611,278,333,333,333,333,333,333,333,333,333,333,333,667,278,789,846,389,794,865,775,222,667,667,570,671,667,611,722,778,278,667,667,833,722,648,778,725,667,600,611,667,837,667,831,761,278,667,570,439,555,222,550,570,571,500,556,439,463,555,542,222,500,492,548,500,447,556,670,573,486,603,374,550,652,546,728,779,222,550,556,550,779,667,667,843,544,708,667,278,278,500,1066,982,844,589,715,639,724,667,651,667,544,704,667,917,614,715,715,589,686,833,722,778,725,667,722,611,639,795,667,727,673,920,923,805,886,651,694,1022,682,556,562,522,493,553,556,688,465,556,556,472,564,686,550,556,556,556,500,833,500,835,500,572,518,830,851,621,736,526,492,752,534,556,556,556,378,496,500,222,222,222,910,828,556,472,565,500,556,778,556,492,339,944,722,944,722,944,722,667,500,333,333,556,1e3,1e3,552,222,222,222,222,333,333,333,556,556,350,1e3,1e3,188,354,333,333,500,333,167,365,556,556,1094,556,885,323,1083,1e3,768,600,834,834,834,834,1e3,500,998,500,1e3,500,500,494,612,823,713,584,549,713,979,719,274,549,549,584,549,549,604,584,604,604,708,625,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,729,604,604,354,354,1e3,990,990,990,990,494,604,604,604,604,354,1021,1052,917,750,750,531,656,594,510,500,750,750,500,500,333,333,333,333,333,333,333,333,222,222,294,294,324,324,316,328,398,285],Is=[-1,-1,-1,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,161,162,163,164,165,166,167,168,169,170,171,172,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,402,506,507,508,509,510,511,536,537,538,539,710,711,713,728,729,730,731,732,733,900,901,902,903,904,905,906,908,910,911,912,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,931,932,933,934,935,936,937,938,939,940,941,942,943,944,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,962,963,964,965,966,967,968,969,970,971,972,973,974,1024,1025,1026,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1037,1038,1039,1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,1104,1105,1106,1107,1108,1109,1110,1111,1112,1113,1114,1115,1116,1117,1118,1119,1138,1139,1168,1169,7808,7809,7810,7811,7812,7813,7922,7923,8208,8209,8211,8212,8213,8215,8216,8217,8218,8219,8220,8221,8222,8224,8225,8226,8230,8240,8242,8243,8249,8250,8252,8254,8260,8319,8355,8356,8359,8364,8453,8467,8470,8482,8486,8494,8539,8540,8541,8542,8592,8593,8594,8595,8596,8597,8616,8706,8710,8719,8721,8722,8730,8734,8735,8745,8747,8776,8800,8801,8804,8805,8962,8976,8992,8993,9472,9474,9484,9488,9492,9496,9500,9508,9516,9524,9532,9552,9553,9554,9555,9556,9557,9558,9559,9560,9561,9562,9563,9564,9565,9566,9567,9568,9569,9570,9571,9572,9573,9574,9575,9576,9577,9578,9579,9580,9600,9604,9608,9612,9616,9617,9618,9619,9632,9633,9642,9643,9644,9650,9658,9660,9668,9674,9675,9679,9688,9689,9702,9786,9787,9788,9792,9794,9824,9827,9829,9830,9834,9835,9836,61441,61442,61445,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],Fs=[365,0,333,278,278,355,556,556,889,667,191,333,333,389,584,278,333,278,278,556,556,556,556,556,556,556,556,556,556,278,278,584,584,584,556,1015,667,667,722,722,667,611,778,722,278,500,667,556,833,722,778,667,778,722,667,611,722,667,944,667,667,611,278,278,278,469,556,333,556,556,500,556,556,278,556,556,222,222,500,222,833,556,556,556,556,333,500,278,556,500,722,500,500,500,334,260,334,584,333,556,556,556,556,260,556,333,737,370,556,584,737,552,400,549,333,333,333,576,537,278,333,333,365,556,834,834,834,611,667,667,667,667,667,667,1e3,722,667,667,667,667,278,278,278,278,722,722,778,778,778,778,778,584,778,722,722,722,722,667,667,611,556,556,556,556,556,556,889,500,556,556,556,556,278,278,278,278,556,556,556,556,556,556,556,549,611,556,556,556,556,500,556,500,667,556,667,556,667,556,722,500,722,500,722,500,722,500,722,615,722,556,667,556,667,556,667,556,667,556,667,556,778,556,778,556,778,556,778,556,722,556,722,556,278,278,278,278,278,278,278,222,278,278,735,444,500,222,667,500,500,556,222,556,222,556,292,556,334,556,222,722,556,722,556,722,556,604,723,556,778,556,778,556,778,556,1e3,944,722,333,722,333,722,333,667,500,667,500,667,500,667,500,611,278,611,375,611,278,722,556,722,556,722,556,722,556,722,556,722,556,944,722,667,500,667,611,500,611,500,611,500,222,556,667,556,1e3,889,778,611,667,500,611,278,333,333,333,333,333,333,333,333,333,333,333,667,278,784,838,384,774,855,752,222,667,667,551,668,667,611,722,778,278,667,668,833,722,650,778,722,667,618,611,667,798,667,835,748,278,667,578,446,556,222,547,578,575,500,557,446,441,556,556,222,500,500,576,500,448,556,690,569,482,617,395,547,648,525,713,781,222,547,556,547,781,667,667,865,542,719,667,278,278,500,1057,1010,854,583,722,635,719,667,656,667,542,677,667,923,604,719,719,583,656,833,722,778,719,667,722,611,635,760,667,740,667,917,938,792,885,656,719,1010,722,556,573,531,365,583,556,669,458,559,559,438,583,688,552,556,542,556,500,458,500,823,500,573,521,802,823,625,719,521,510,750,542,556,556,556,365,510,500,222,278,222,906,812,556,438,559,500,552,778,556,489,411,944,722,944,722,944,722,667,500,333,333,556,1e3,1e3,552,222,222,222,222,333,333,333,556,556,350,1e3,1e3,188,354,333,333,500,333,167,365,556,556,1094,556,885,323,1073,1e3,768,600,834,834,834,834,1e3,500,1e3,500,1e3,500,500,494,612,823,713,584,549,713,979,719,274,549,549,583,549,549,604,584,604,604,708,625,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,729,604,604,354,354,1e3,990,990,990,990,494,604,604,604,604,354,1021,1052,917,750,750,531,656,594,510,500,750,750,500,500,333,333,333,333,333,333,333,333,222,222,294,294,324,324,316,328,398,285],Ts=[-1,-1,-1,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,161,162,163,164,165,166,167,168,169,170,171,172,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,402,506,507,508,509,510,511,536,537,538,539,710,711,713,728,729,730,731,732,733,900,901,902,903,904,905,906,908,910,911,912,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,931,932,933,934,935,936,937,938,939,940,941,942,943,944,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,962,963,964,965,966,967,968,969,970,971,972,973,974,1024,1025,1026,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1037,1038,1039,1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,1104,1105,1106,1107,1108,1109,1110,1111,1112,1113,1114,1115,1116,1117,1118,1119,1138,1139,1168,1169,7808,7809,7810,7811,7812,7813,7922,7923,8208,8209,8211,8212,8213,8215,8216,8217,8218,8219,8220,8221,8222,8224,8225,8226,8230,8240,8242,8243,8249,8250,8252,8254,8260,8319,8355,8356,8359,8364,8453,8467,8470,8482,8486,8494,8539,8540,8541,8542,8592,8593,8594,8595,8596,8597,8616,8706,8710,8719,8721,8722,8730,8734,8735,8745,8747,8776,8800,8801,8804,8805,8962,8976,8992,8993,9472,9474,9484,9488,9492,9496,9500,9508,9516,9524,9532,9552,9553,9554,9555,9556,9557,9558,9559,9560,9561,9562,9563,9564,9565,9566,9567,9568,9569,9570,9571,9572,9573,9574,9575,9576,9577,9578,9579,9580,9600,9604,9608,9612,9616,9617,9618,9619,9632,9633,9642,9643,9644,9650,9658,9660,9668,9674,9675,9679,9688,9689,9702,9786,9787,9788,9792,9794,9824,9827,9829,9830,9834,9835,9836,61441,61442,61445,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],Os=[1.36898,1,1,.72706,.80479,.83734,.98894,.99793,.9897,.93884,.86209,.94292,.94292,1.16661,1.02058,.93582,.96694,.93582,1.19137,.99793,.99793,.99793,.99793,.99793,.99793,.99793,.99793,.99793,.99793,.78076,.78076,1.02058,1.02058,1.02058,.72851,.78966,.90838,.83637,.82391,.96376,.80061,.86275,.8768,.95407,1.0258,.73901,.85022,.83655,1.0156,.95546,.92179,.87107,.92179,.82114,.8096,.89713,.94438,.95353,.94083,.91905,.90406,.9446,.94292,1.18777,.94292,1.02058,.89903,.90088,.94938,.97898,.81093,.97571,.94938,1.024,.9577,.95933,.98621,1.0474,.97455,.98981,.9672,.95933,.9446,.97898,.97407,.97646,.78036,1.10208,.95442,.95298,.97579,.9332,.94039,.938,.80687,1.01149,.80687,1.02058,.80479,.99793,.99793,.99793,.99793,1.01149,1.00872,.90088,.91882,1.0213,.8361,1.02058,.62295,.54324,.89022,1.08595,1,1,.90088,1,.97455,.93582,.90088,1,1.05686,.8361,.99642,.99642,.99642,.72851,.90838,.90838,.90838,.90838,.90838,.90838,.868,.82391,.80061,.80061,.80061,.80061,1.0258,1.0258,1.0258,1.0258,.97484,.95546,.92179,.92179,.92179,.92179,.92179,1.02058,.92179,.94438,.94438,.94438,.94438,.90406,.86958,.98225,.94938,.94938,.94938,.94938,.94938,.94938,.9031,.81093,.94938,.94938,.94938,.94938,.98621,.98621,.98621,.98621,.93969,.95933,.9446,.9446,.9446,.9446,.9446,1.08595,.9446,.95442,.95442,.95442,.95442,.94039,.97898,.94039,.90838,.94938,.90838,.94938,.90838,.94938,.82391,.81093,.82391,.81093,.82391,.81093,.82391,.81093,.96376,.84313,.97484,.97571,.80061,.94938,.80061,.94938,.80061,.94938,.80061,.94938,.80061,.94938,.8768,.9577,.8768,.9577,.8768,.9577,1,1,.95407,.95933,.97069,.95933,1.0258,.98621,1.0258,.98621,1.0258,.98621,1.0258,.98621,1.0258,.98621,.887,1.01591,.73901,1.0474,1,1,.97455,.83655,.98981,1,1,.83655,.73977,.83655,.73903,.84638,1.033,.95546,.95933,1,1,.95546,.95933,.8271,.95417,.95933,.92179,.9446,.92179,.9446,.92179,.9446,.936,.91964,.82114,.97646,1,1,.82114,.97646,.8096,.78036,.8096,.78036,1,1,.8096,.78036,1,1,.89713,.77452,.89713,1.10208,.94438,.95442,.94438,.95442,.94438,.95442,.94438,.95442,.94438,.95442,.94438,.95442,.94083,.97579,.90406,.94039,.90406,.9446,.938,.9446,.938,.9446,.938,1,.99793,.90838,.94938,.868,.9031,.92179,.9446,1,1,.89713,1.10208,.90088,.90088,.90088,.90088,.90088,.90088,.90088,.90088,.90088,.90989,.9358,.91945,.83181,.75261,.87992,.82976,.96034,.83689,.97268,1.0078,.90838,.83637,.8019,.90157,.80061,.9446,.95407,.92436,1.0258,.85022,.97153,1.0156,.95546,.89192,.92179,.92361,.87107,.96318,.89713,.93704,.95638,.91905,.91709,.92796,1.0258,.93704,.94836,1.0373,.95933,1.0078,.95871,.94836,.96174,.92601,.9498,.98607,.95776,.95933,1.05453,1.0078,.98275,.9314,.95617,.91701,1.05993,.9446,.78367,.9553,1,.86832,1.0128,.95871,.99394,.87548,.96361,.86774,1.0078,.95871,.9446,.95871,.86774,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.94083,.97579,.94083,.97579,.94083,.97579,.90406,.94039,.96694,1,.89903,1,1,1,.93582,.93582,.93582,1,.908,.908,.918,.94219,.94219,.96544,1,1.285,1,1,.81079,.81079,1,1,.74854,1,1,1,1,.99793,1,1,1,.65,1,1.36145,1,1,1,1,1,1,1,1,1,1,1,1.17173,1,.80535,.76169,1.02058,1.0732,1.05486,1,1,1.30692,1.08595,1.08595,1,1.08595,1.08595,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1.16161,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],Rs={lineHeight:1.2,lineGap:.2},Hs=[1.36898,1,1,.66227,.80779,.81625,.97276,.97276,.97733,.92222,.83266,.94292,.94292,1.16148,1.02058,.93582,.96694,.93582,1.17337,.97276,.97276,.97276,.97276,.97276,.97276,.97276,.97276,.97276,.97276,.78076,.78076,1.02058,1.02058,1.02058,.71541,.76813,.85576,.80591,.80729,.94299,.77512,.83655,.86523,.92222,.98621,.71743,.81698,.79726,.98558,.92222,.90637,.83809,.90637,.80729,.76463,.86275,.90699,.91605,.9154,.85308,.85458,.90531,.94292,1.21296,.94292,1.02058,.89903,1.18616,.99613,.91677,.78216,.91677,.90083,.98796,.9135,.92168,.95381,.98981,.95298,.95381,.93459,.92168,.91513,.92004,.91677,.95077,.748,1.04502,.91677,.92061,.94236,.89544,.89364,.9,.80687,.8578,.80687,1.02058,.80779,.97276,.97276,.97276,.97276,.8578,.99973,1.18616,.91339,1.08074,.82891,1.02058,.55509,.71526,.89022,1.08595,1,1,1.18616,1,.96736,.93582,1.18616,1,1.04864,.82711,.99043,.99043,.99043,.71541,.85576,.85576,.85576,.85576,.85576,.85576,.845,.80729,.77512,.77512,.77512,.77512,.98621,.98621,.98621,.98621,.95961,.92222,.90637,.90637,.90637,.90637,.90637,1.02058,.90251,.90699,.90699,.90699,.90699,.85458,.83659,.94951,.99613,.99613,.99613,.99613,.99613,.99613,.85811,.78216,.90083,.90083,.90083,.90083,.95381,.95381,.95381,.95381,.9135,.92168,.91513,.91513,.91513,.91513,.91513,1.08595,.91677,.91677,.91677,.91677,.91677,.89364,.92332,.89364,.85576,.99613,.85576,.99613,.85576,.99613,.80729,.78216,.80729,.78216,.80729,.78216,.80729,.78216,.94299,.76783,.95961,.91677,.77512,.90083,.77512,.90083,.77512,.90083,.77512,.90083,.77512,.90083,.86523,.9135,.86523,.9135,.86523,.9135,1,1,.92222,.92168,.92222,.92168,.98621,.95381,.98621,.95381,.98621,.95381,.98621,.95381,.98621,.95381,.86036,.97096,.71743,.98981,1,1,.95298,.79726,.95381,1,1,.79726,.6894,.79726,.74321,.81691,1.0006,.92222,.92168,1,1,.92222,.92168,.79464,.92098,.92168,.90637,.91513,.90637,.91513,.90637,.91513,.909,.87514,.80729,.95077,1,1,.80729,.95077,.76463,.748,.76463,.748,1,1,.76463,.748,1,1,.86275,.72651,.86275,1.04502,.90699,.91677,.90699,.91677,.90699,.91677,.90699,.91677,.90699,.91677,.90699,.91677,.9154,.94236,.85458,.89364,.85458,.90531,.9,.90531,.9,.90531,.9,1,.97276,.85576,.99613,.845,.85811,.90251,.91677,1,1,.86275,1.04502,1.18616,1.18616,1.18616,1.18616,1.18616,1.18616,1.18616,1.18616,1.18616,1.00899,1.30628,.85576,.80178,.66862,.7927,.69323,.88127,.72459,.89711,.95381,.85576,.80591,.7805,.94729,.77512,.90531,.92222,.90637,.98621,.81698,.92655,.98558,.92222,.85359,.90637,.90976,.83809,.94523,.86275,.83509,.93157,.85308,.83392,.92346,.98621,.83509,.92886,.91324,.92168,.95381,.90646,.92886,.90557,.86847,.90276,.91324,.86842,.92168,.99531,.95381,.9224,.85408,.92699,.86847,1.0051,.91513,.80487,.93481,1,.88159,1.05214,.90646,.97355,.81539,.89398,.85923,.95381,.90646,.91513,.90646,.85923,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.9154,.94236,.9154,.94236,.9154,.94236,.85458,.89364,.96694,1,.89903,1,1,1,.91782,.91782,.91782,1,.896,.896,.896,.9332,.9332,.95973,1,1.26,1,1,.80479,.80178,1,1,.85633,1,1,1,1,.97276,1,1,1,.698,1,1.36145,1,1,1,1,1,1,1,1,1,1,1,1.14542,1,.79199,.78694,1.02058,1.03493,1.05486,1,1,1.23026,1.08595,1.08595,1,1.08595,1.08595,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1.20006,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],Bs={lineHeight:1.2,lineGap:.2},Ds=[1.36898,1,1,.65507,.84943,.85639,.88465,.88465,.86936,.88307,.86948,.85283,.85283,1.06383,1.02058,.75945,.9219,.75945,1.17337,.88465,.88465,.88465,.88465,.88465,.88465,.88465,.88465,.88465,.88465,.75945,.75945,1.02058,1.02058,1.02058,.69046,.70926,.85158,.77812,.76852,.89591,.70466,.76125,.80094,.86822,.83864,.728,.77212,.79475,.93637,.87514,.8588,.76013,.8588,.72421,.69866,.77598,.85991,.80811,.87832,.78112,.77512,.8562,1.0222,1.18417,1.0222,1.27014,.89903,1.15012,.93859,.94399,.846,.94399,.81453,1.0186,.94219,.96017,1.03075,1.02175,.912,1.03075,.96998,.96017,.93859,.94399,.94399,.95493,.746,1.12658,.94578,.91,.979,.882,.882,.83,.85034,.83537,.85034,1.02058,.70869,.88465,.88465,.88465,.88465,.83537,.90083,1.15012,.9161,.94565,.73541,1.02058,.53609,.69353,.79519,1.08595,1,1,1.15012,1,.91974,.75945,1.15012,1,.9446,.73361,.9005,.9005,.9005,.62864,.85158,.85158,.85158,.85158,.85158,.85158,.773,.76852,.70466,.70466,.70466,.70466,.83864,.83864,.83864,.83864,.90561,.87514,.8588,.8588,.8588,.8588,.8588,1.02058,.85751,.85991,.85991,.85991,.85991,.77512,.76013,.88075,.93859,.93859,.93859,.93859,.93859,.93859,.8075,.846,.81453,.81453,.81453,.81453,.82424,.82424,.82424,.82424,.9278,.96017,.93859,.93859,.93859,.93859,.93859,1.08595,.8562,.94578,.94578,.94578,.94578,.882,.94578,.882,.85158,.93859,.85158,.93859,.85158,.93859,.76852,.846,.76852,.846,.76852,.846,.76852,.846,.89591,.8544,.90561,.94399,.70466,.81453,.70466,.81453,.70466,.81453,.70466,.81453,.70466,.81453,.80094,.94219,.80094,.94219,.80094,.94219,1,1,.86822,.96017,.86822,.96017,.83864,.82424,.83864,.82424,.83864,.82424,.83864,1.03075,.83864,.82424,.81402,1.02738,.728,1.02175,1,1,.912,.79475,1.03075,1,1,.79475,.83911,.79475,.66266,.80553,1.06676,.87514,.96017,1,1,.87514,.96017,.86865,.87396,.96017,.8588,.93859,.8588,.93859,.8588,.93859,.867,.84759,.72421,.95493,1,1,.72421,.95493,.69866,.746,.69866,.746,1,1,.69866,.746,1,1,.77598,.88417,.77598,1.12658,.85991,.94578,.85991,.94578,.85991,.94578,.85991,.94578,.85991,.94578,.85991,.94578,.87832,.979,.77512,.882,.77512,.8562,.83,.8562,.83,.8562,.83,1,.88465,.85158,.93859,.773,.8075,.85751,.8562,1,1,.77598,1.12658,1.15012,1.15012,1.15012,1.15012,1.15012,1.15313,1.15012,1.15012,1.15012,1.08106,1.03901,.85158,.77025,.62264,.7646,.65351,.86026,.69461,.89947,1.03075,.85158,.77812,.76449,.88836,.70466,.8562,.86822,.8588,.83864,.77212,.85308,.93637,.87514,.82352,.8588,.85701,.76013,.89058,.77598,.8156,.82565,.78112,.77899,.89386,.83864,.8156,.9486,.92388,.96186,1.03075,.91123,.9486,.93298,.878,.93942,.92388,.84596,.96186,.95119,1.03075,.922,.88787,.95829,.88,.93559,.93859,.78815,.93758,1,.89217,1.03737,.91123,.93969,.77487,.85769,.86799,1.03075,.91123,.93859,.91123,.86799,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.87832,.979,.87832,.979,.87832,.979,.77512,.882,.9219,1,.89903,1,1,1,.87321,.87321,.87321,1,1.027,1.027,1.027,.86847,.86847,.79121,1,1.124,1,1,.73572,.73572,1,1,.85034,1,1,1,1,.88465,1,1,1,.669,1,1.36145,1,1,1,1,1,1,1,1,1,1,1,1.04828,1,.74948,.75187,1.02058,.98391,1.02119,1,1,1.06233,1.08595,1.08595,1,1.08595,1.08595,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1.05233,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],Ms={lineHeight:1.2,lineGap:.2},Ns=[1.36898,1,1,.76305,.82784,.94935,.89364,.92241,.89073,.90706,.98472,.85283,.85283,1.0664,1.02058,.74505,.9219,.74505,1.23456,.92241,.92241,.92241,.92241,.92241,.92241,.92241,.92241,.92241,.92241,.74505,.74505,1.02058,1.02058,1.02058,.73002,.72601,.91755,.8126,.80314,.92222,.73764,.79726,.83051,.90284,.86023,.74,.8126,.84869,.96518,.91115,.8858,.79761,.8858,.74498,.73914,.81363,.89591,.83659,.89633,.85608,.8111,.90531,1.0222,1.22736,1.0222,1.27014,.89903,.90088,.86667,1.0231,.896,1.01411,.90083,1.05099,1.00512,.99793,1.05326,1.09377,.938,1.06226,1.00119,.99793,.98714,1.0231,1.01231,.98196,.792,1.19137,.99074,.962,1.01915,.926,.942,.856,.85034,.92006,.85034,1.02058,.69067,.92241,.92241,.92241,.92241,.92006,.9332,.90088,.91882,.93484,.75339,1.02058,.56866,.54324,.79519,1.08595,1,1,.90088,1,.95325,.74505,.90088,1,.97198,.75339,.91009,.91009,.91009,.66466,.91755,.91755,.91755,.91755,.91755,.91755,.788,.80314,.73764,.73764,.73764,.73764,.86023,.86023,.86023,.86023,.92915,.91115,.8858,.8858,.8858,.8858,.8858,1.02058,.8858,.89591,.89591,.89591,.89591,.8111,.79611,.89713,.86667,.86667,.86667,.86667,.86667,.86667,.86936,.896,.90083,.90083,.90083,.90083,.84224,.84224,.84224,.84224,.97276,.99793,.98714,.98714,.98714,.98714,.98714,1.08595,.89876,.99074,.99074,.99074,.99074,.942,1.0231,.942,.91755,.86667,.91755,.86667,.91755,.86667,.80314,.896,.80314,.896,.80314,.896,.80314,.896,.92222,.93372,.92915,1.01411,.73764,.90083,.73764,.90083,.73764,.90083,.73764,.90083,.73764,.90083,.83051,1.00512,.83051,1.00512,.83051,1.00512,1,1,.90284,.99793,.90976,.99793,.86023,.84224,.86023,.84224,.86023,.84224,.86023,1.05326,.86023,.84224,.82873,1.07469,.74,1.09377,1,1,.938,.84869,1.06226,1,1,.84869,.83704,.84869,.81441,.85588,1.08927,.91115,.99793,1,1,.91115,.99793,.91887,.90991,.99793,.8858,.98714,.8858,.98714,.8858,.98714,.894,.91434,.74498,.98196,1,1,.74498,.98196,.73914,.792,.73914,.792,1,1,.73914,.792,1,1,.81363,.904,.81363,1.19137,.89591,.99074,.89591,.99074,.89591,.99074,.89591,.99074,.89591,.99074,.89591,.99074,.89633,1.01915,.8111,.942,.8111,.90531,.856,.90531,.856,.90531,.856,1,.92241,.91755,.86667,.788,.86936,.8858,.89876,1,1,.81363,1.19137,.90088,.90088,.90088,.90088,.90088,.90088,.90088,.90088,.90088,.90388,1.03901,.92138,.78105,.7154,.86169,.80513,.94007,.82528,.98612,1.06226,.91755,.8126,.81884,.92819,.73764,.90531,.90284,.8858,.86023,.8126,.91172,.96518,.91115,.83089,.8858,.87791,.79761,.89297,.81363,.88157,.89992,.85608,.81992,.94307,.86023,.88157,.95308,.98699,.99793,1.06226,.95817,.95308,.97358,.928,.98088,.98699,.92761,.99793,.96017,1.06226,.986,.944,.95978,.938,.96705,.98714,.80442,.98972,1,.89762,1.04552,.95817,.99007,.87064,.91879,.88888,1.06226,.95817,.98714,.95817,.88888,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.89633,1.01915,.89633,1.01915,.89633,1.01915,.8111,.942,.9219,1,.89903,1,1,1,.93173,.93173,.93173,1,1.06304,1.06304,1.06904,.89903,.89903,.80549,1,1.156,1,1,.76575,.76575,1,1,.72458,1,1,1,1,.92241,1,1,1,.619,1,1.36145,1,1,1,1,1,1,1,1,1,1,1,1.07257,1,.74705,.71119,1.02058,1.024,1.02119,1,1,1.1536,1.08595,1.08595,1,1.08595,1.08595,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1.05638,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],Es={lineHeight:1.2,lineGap:.2},_s=[1.76738,1,1,.99297,.9824,1.04016,1.06497,1.03424,.97529,1.17647,1.23203,1.1085,1.1085,1.16939,1.2107,.9754,1.21408,.9754,1.59578,1.03424,1.03424,1.03424,1.03424,1.03424,1.03424,1.03424,1.03424,1.03424,1.03424,.81378,.81378,1.2107,1.2107,1.2107,.71703,.97847,.97363,.88776,.8641,1.02096,.79795,.85132,.914,1.06085,1.1406,.8007,.89858,.83693,1.14889,1.09398,.97489,.92094,.97489,.90399,.84041,.95923,1.00135,1,1.06467,.98243,.90996,.99361,1.1085,1.56942,1.1085,1.2107,.74627,.94282,.96752,1.01519,.86304,1.01359,.97278,1.15103,1.01359,.98561,1.02285,1.02285,1.00527,1.02285,1.0302,.99041,1.0008,1.01519,1.01359,1.02258,.79104,1.16862,.99041,.97454,1.02511,.99298,.96752,.95801,.94856,1.16579,.94856,1.2107,.9824,1.03424,1.03424,1,1.03424,1.16579,.8727,1.3871,1.18622,1.10818,1.04478,1.2107,1.18622,.75155,.94994,1.28826,1.21408,1.21408,.91056,1,.91572,.9754,.64663,1.18328,1.24866,1.04478,1.14169,1.15749,1.17389,.71703,.97363,.97363,.97363,.97363,.97363,.97363,.93506,.8641,.79795,.79795,.79795,.79795,1.1406,1.1406,1.1406,1.1406,1.02096,1.09398,.97426,.97426,.97426,.97426,.97426,1.2107,.97489,1.00135,1.00135,1.00135,1.00135,.90996,.92094,1.02798,.96752,.96752,.96752,.96752,.96752,.96752,.93136,.86304,.97278,.97278,.97278,.97278,1.02285,1.02285,1.02285,1.02285,.97122,.99041,1,1,1,1,1,1.28826,1.0008,.99041,.99041,.99041,.99041,.96752,1.01519,.96752,.97363,.96752,.97363,.96752,.97363,.96752,.8641,.86304,.8641,.86304,.8641,.86304,.8641,.86304,1.02096,1.03057,1.02096,1.03517,.79795,.97278,.79795,.97278,.79795,.97278,.79795,.97278,.79795,.97278,.914,1.01359,.914,1.01359,.914,1.01359,1,1,1.06085,.98561,1.06085,1.00879,1.1406,1.02285,1.1406,1.02285,1.1406,1.02285,1.1406,1.02285,1.1406,1.02285,.97138,1.08692,.8007,1.02285,1,1,1.00527,.83693,1.02285,1,1,.83693,.9455,.83693,.90418,.83693,1.13005,1.09398,.99041,1,1,1.09398,.99041,.96692,1.09251,.99041,.97489,1.0008,.97489,1.0008,.97489,1.0008,.93994,.97931,.90399,1.02258,1,1,.90399,1.02258,.84041,.79104,.84041,.79104,.84041,.79104,.84041,.79104,1,1,.95923,1.07034,.95923,1.16862,1.00135,.99041,1.00135,.99041,1.00135,.99041,1.00135,.99041,1.00135,.99041,1.00135,.99041,1.06467,1.02511,.90996,.96752,.90996,.99361,.95801,.99361,.95801,.99361,.95801,1.07733,1.03424,.97363,.96752,.93506,.93136,.97489,1.0008,1,1,.95923,1.16862,1.15103,1.15103,1.01173,1.03959,.75953,.81378,.79912,1.15103,1.21994,.95161,.87815,1.01149,.81525,.7676,.98167,1.01134,1.02546,.84097,1.03089,1.18102,.97363,.88776,.85134,.97826,.79795,.99361,1.06085,.97489,1.1406,.89858,1.0388,1.14889,1.09398,.86039,.97489,1.0595,.92094,.94793,.95923,.90996,.99346,.98243,1.02112,.95493,1.1406,.90996,1.03574,1.02597,1.0008,1.18102,1.06628,1.03574,1.0192,1.01932,1.00886,.97531,1.0106,1.0008,1.13189,1.18102,1.02277,.98683,1.0016,.99561,1.07237,1.0008,.90434,.99921,.93803,.8965,1.23085,1.06628,1.04983,.96268,1.0499,.98439,1.18102,1.06628,1.0008,1.06628,.98439,.79795,1,1,1,1,1,1,1,1,1,1,1,1,1.09466,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.97278,1,1,1,1,1,1,1,1,1,1,1,1,1.02065,1,1,1,1,1,1,1.06467,1.02511,1.06467,1.02511,1.06467,1.02511,.90996,.96752,1,1.21408,.89903,1,1,.75155,1.04394,1.04394,1.04394,1.04394,.98633,.98633,.98633,.73047,.73047,1.20642,.91211,1.25635,1.222,1.02956,1.03372,1.03372,.96039,1.24633,1,1.12454,.93503,1.03424,1.19687,1.03424,1,1,1,.771,1,1,1.15749,1.15749,1.15749,1.10948,.86279,.94434,.86279,.94434,.86182,1,1,1.16897,1,.96085,.90137,1.2107,1.18416,1.13973,.69825,.9716,2.10339,1.29004,1.29004,1.21172,1.29004,1.29004,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1.42603,1,.99862,.99862,1,.87025,.87025,.87025,.87025,1.18874,1.42603,1,1.42603,1.42603,.99862,1,1,1,1,1,1.2886,1.04315,1.15296,1.34163,1,1,1,1.09193,1.09193,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],zs={lineHeight:1.33008,lineGap:0},Ls=[1.76738,1,1,.98946,1.03959,1.04016,1.02809,1.036,.97639,1.10953,1.23203,1.11144,1.11144,1.16939,1.21237,.9754,1.21261,.9754,1.59754,1.036,1.036,1.036,1.036,1.036,1.036,1.036,1.036,1.036,1.036,.81378,.81378,1.21237,1.21237,1.21237,.73541,.97847,.97363,.89723,.87897,1.0426,.79429,.85292,.91149,1.05815,1.1406,.79631,.90128,.83853,1.04396,1.10615,.97552,.94436,.97552,.88641,.80527,.96083,1.00135,1,1.06777,.9817,.91142,.99361,1.11144,1.57293,1.11144,1.21237,.74627,1.31818,1.06585,.97042,.83055,.97042,.93503,1.1261,.97042,.97922,1.14236,.94552,1.01054,1.14236,1.02471,.97922,.94165,.97042,.97042,1.0276,.78929,1.1261,.97922,.95874,1.02197,.98507,.96752,.97168,.95107,1.16579,.95107,1.21237,1.03959,1.036,1.036,1,1.036,1.16579,.87357,1.31818,1.18754,1.26781,1.05356,1.21237,1.18622,.79487,.94994,1.29004,1.24047,1.24047,1.31818,1,.91484,.9754,1.31818,1.1349,1.24866,1.05356,1.13934,1.15574,1.17389,.73541,.97363,.97363,.97363,.97363,.97363,.97363,.94385,.87897,.79429,.79429,.79429,.79429,1.1406,1.1406,1.1406,1.1406,1.0426,1.10615,.97552,.97552,.97552,.97552,.97552,1.21237,.97552,1.00135,1.00135,1.00135,1.00135,.91142,.94436,.98721,1.06585,1.06585,1.06585,1.06585,1.06585,1.06585,.96705,.83055,.93503,.93503,.93503,.93503,1.14236,1.14236,1.14236,1.14236,.93125,.97922,.94165,.94165,.94165,.94165,.94165,1.29004,.94165,.97922,.97922,.97922,.97922,.96752,.97042,.96752,.97363,1.06585,.97363,1.06585,.97363,1.06585,.87897,.83055,.87897,.83055,.87897,.83055,.87897,.83055,1.0426,1.0033,1.0426,.97042,.79429,.93503,.79429,.93503,.79429,.93503,.79429,.93503,.79429,.93503,.91149,.97042,.91149,.97042,.91149,.97042,1,1,1.05815,.97922,1.05815,.97922,1.1406,1.14236,1.1406,1.14236,1.1406,1.14236,1.1406,1.14236,1.1406,1.14236,.97441,1.04302,.79631,1.01582,1,1,1.01054,.83853,1.14236,1,1,.83853,1.09125,.83853,.90418,.83853,1.19508,1.10615,.97922,1,1,1.10615,.97922,1.01034,1.10466,.97922,.97552,.94165,.97552,.94165,.97552,.94165,.91602,.91981,.88641,1.0276,1,1,.88641,1.0276,.80527,.78929,.80527,.78929,.80527,.78929,.80527,.78929,1,1,.96083,1.05403,.95923,1.16862,1.00135,.97922,1.00135,.97922,1.00135,.97922,1.00135,.97922,1.00135,.97922,1.00135,.97922,1.06777,1.02197,.91142,.96752,.91142,.99361,.97168,.99361,.97168,.99361,.97168,1.23199,1.036,.97363,1.06585,.94385,.96705,.97552,.94165,1,1,.96083,1.1261,1.31818,1.31818,1.31818,1.31818,1.31818,1.31818,1.31818,1.31818,1.31818,.95161,1.27126,1.00811,.83284,.77702,.99137,.95253,1.0347,.86142,1.07205,1.14236,.97363,.89723,.86869,1.09818,.79429,.99361,1.05815,.97552,1.1406,.90128,1.06662,1.04396,1.10615,.84918,.97552,1.04694,.94436,.98015,.96083,.91142,1.00356,.9817,1.01945,.98999,1.1406,.91142,1.04961,.9898,1.00639,1.14236,1.07514,1.04961,.99607,1.02897,1.008,.9898,.95134,1.00639,1.11121,1.14236,1.00518,.97981,1.02186,1,1.08578,.94165,.99314,.98387,.93028,.93377,1.35125,1.07514,1.10687,.93491,1.04232,1.00351,1.14236,1.07514,.94165,1.07514,1.00351,.79429,1,1,1,1,1,1,1,1,1,1,1,1,1.09097,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.93503,1,1,1,1,1,1,1,1,1,1,1,1,.96609,1,1,1,1,1,1,1.06777,1.02197,1.06777,1.02197,1.06777,1.02197,.91142,.96752,1,1.21261,.89903,1,1,.75155,1.04745,1.04745,1.04745,1.04394,.98633,.98633,.98633,.72959,.72959,1.20502,.91406,1.26514,1.222,1.02956,1.03372,1.03372,.96039,1.24633,1,1.09125,.93327,1.03336,1.16541,1.036,1,1,1,.771,1,1,1.15574,1.15574,1.15574,1.15574,.86364,.94434,.86279,.94434,.86224,1,1,1.16798,1,.96085,.90068,1.21237,1.18416,1.13904,.69825,.9716,2.10339,1.29004,1.29004,1.21339,1.29004,1.29004,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1.42603,1,.99862,.99862,1,.87025,.87025,.87025,.87025,1.18775,1.42603,1,1.42603,1.42603,.99862,1,1,1,1,1,1.2886,1.04315,1.15296,1.34163,1,1,1,1.13269,1.13269,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],Us={lineHeight:1.33008,lineGap:0},Xs=[1.76738,1,1,.98946,1.14763,1.05365,1.06234,.96927,.92586,1.15373,1.18414,.91349,.91349,1.07403,1.17308,.78383,1.20088,.78383,1.42531,.96927,.96927,.96927,.96927,.96927,.96927,.96927,.96927,.96927,.96927,.78383,.78383,1.17308,1.17308,1.17308,.77349,.94565,.94729,.85944,.88506,.9858,.74817,.80016,.88449,.98039,.95782,.69238,.89898,.83231,.98183,1.03989,.96924,.86237,.96924,.80595,.74524,.86091,.95402,.94143,.98448,.8858,.83089,.93285,1.0949,1.39016,1.0949,1.45994,.74627,1.04839,.97454,.97454,.87207,.97454,.87533,1.06151,.97454,1.00176,1.16484,1.08132,.98047,1.16484,1.02989,1.01054,.96225,.97454,.97454,1.06598,.79004,1.16344,1.00351,.94629,.9973,.91016,.96777,.9043,.91082,.92481,.91082,1.17308,.95748,.96927,.96927,1,.96927,.92481,.80597,1.04839,1.23393,1.1781,.9245,1.17308,1.20808,.63218,.94261,1.24822,1.09971,1.09971,1.04839,1,.85273,.78032,1.04839,1.09971,1.22326,.9245,1.09836,1.13525,1.15222,.70424,.94729,.94729,.94729,.94729,.94729,.94729,.85498,.88506,.74817,.74817,.74817,.74817,.95782,.95782,.95782,.95782,.9858,1.03989,.96924,.96924,.96924,.96924,.96924,1.17308,.96924,.95402,.95402,.95402,.95402,.83089,.86237,.88409,.97454,.97454,.97454,.97454,.97454,.97454,.92916,.87207,.87533,.87533,.87533,.87533,.93146,.93146,.93146,.93146,.93854,1.01054,.96225,.96225,.96225,.96225,.96225,1.24822,.8761,1.00351,1.00351,1.00351,1.00351,.96777,.97454,.96777,.94729,.97454,.94729,.97454,.94729,.97454,.88506,.87207,.88506,.87207,.88506,.87207,.88506,.87207,.9858,.95391,.9858,.97454,.74817,.87533,.74817,.87533,.74817,.87533,.74817,.87533,.74817,.87533,.88449,.97454,.88449,.97454,.88449,.97454,1,1,.98039,1.00176,.98039,1.00176,.95782,.93146,.95782,.93146,.95782,.93146,.95782,1.16484,.95782,.93146,.84421,1.12761,.69238,1.08132,1,1,.98047,.83231,1.16484,1,1,.84723,1.04861,.84723,.78755,.83231,1.23736,1.03989,1.01054,1,1,1.03989,1.01054,.9857,1.03849,1.01054,.96924,.96225,.96924,.96225,.96924,.96225,.92383,.90171,.80595,1.06598,1,1,.80595,1.06598,.74524,.79004,.74524,.79004,.74524,.79004,.74524,.79004,1,1,.86091,1.02759,.85771,1.16344,.95402,1.00351,.95402,1.00351,.95402,1.00351,.95402,1.00351,.95402,1.00351,.95402,1.00351,.98448,.9973,.83089,.96777,.83089,.93285,.9043,.93285,.9043,.93285,.9043,1.31868,.96927,.94729,.97454,.85498,.92916,.96924,.8761,1,1,.86091,1.16344,1.04839,1.04839,1.04839,1.04839,1.04839,1.04839,1.04839,1.04839,1.04839,.81965,.81965,.94729,.78032,.71022,.90883,.84171,.99877,.77596,1.05734,1.2,.94729,.85944,.82791,.9607,.74817,.93285,.98039,.96924,.95782,.89898,.98316,.98183,1.03989,.78614,.96924,.97642,.86237,.86075,.86091,.83089,.90082,.8858,.97296,1.01284,.95782,.83089,1.0976,1.04,1.03342,1.2,1.0675,1.0976,.98205,1.03809,1.05097,1.04,.95364,1.03342,1.05401,1.2,1.02148,1.0119,1.04724,1.0127,1.02732,.96225,.8965,.97783,.93574,.94818,1.30679,1.0675,1.11826,.99821,1.0557,1.0326,1.2,1.0675,.96225,1.0675,1.0326,.74817,1,1,1,1,1,1,1,1,1,1,1,1,1.03754,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.87533,1,1,1,1,1,1,1,1,1,1,1,1,.98705,1,1,1,1,1,1,.98448,.9973,.98448,.9973,.98448,.9973,.83089,.96777,1,1.20088,.89903,1,1,.75155,.94945,.94945,.94945,.94945,1.12317,1.12317,1.12317,.67603,.67603,1.15621,.73584,1.21191,1.22135,1.06483,.94868,.94868,.95996,1.24633,1,1.07497,.87709,.96927,1.01473,.96927,1,1,1,.77295,1,1,1.09836,1.09836,1.09836,1.01522,.86321,.94434,.8649,.94434,.86182,1,1,1.083,1,.91578,.86438,1.17308,1.18416,1.14589,.69825,.97622,1.96791,1.24822,1.24822,1.17308,1.24822,1.24822,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1.42603,1,.99862,.99862,1,.87025,.87025,.87025,.87025,1.17984,1.42603,1,1.42603,1.42603,.99862,1,1,1,1,1,1.2886,1.04315,1.15296,1.34163,1,1,1,1.10742,1.10742,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],Ws={lineHeight:1.33008,lineGap:0},Ks=[1.76738,1,1,.98594,1.02285,1.10454,1.06234,.96927,.92037,1.19985,1.2046,.90616,.90616,1.07152,1.1714,.78032,1.20088,.78032,1.40246,.96927,.96927,.96927,.96927,.96927,.96927,.96927,.96927,.96927,.96927,.78032,.78032,1.1714,1.1714,1.1714,.80597,.94084,.96706,.85944,.85734,.97093,.75842,.79936,.88198,.9831,.95782,.71387,.86969,.84636,1.07796,1.03584,.96924,.83968,.96924,.82826,.79649,.85771,.95132,.93119,.98965,.88433,.8287,.93365,1.08612,1.3638,1.08612,1.45786,.74627,.80499,.91484,1.05707,.92383,1.05882,.9403,1.12654,1.05882,1.01756,1.09011,1.09011,.99414,1.09011,1.034,1.01756,1.05356,1.05707,1.05882,1.04399,.84863,1.21968,1.01756,.95801,1.00068,.91797,.96777,.9043,.90351,.92105,.90351,1.1714,.85337,.96927,.96927,.99912,.96927,.92105,.80597,1.2434,1.20808,1.05937,.90957,1.1714,1.20808,.75155,.94261,1.24644,1.09971,1.09971,.84751,1,.85273,.78032,.61584,1.05425,1.17914,.90957,1.08665,1.11593,1.14169,.73381,.96706,.96706,.96706,.96706,.96706,.96706,.86035,.85734,.75842,.75842,.75842,.75842,.95782,.95782,.95782,.95782,.97093,1.03584,.96924,.96924,.96924,.96924,.96924,1.1714,.96924,.95132,.95132,.95132,.95132,.8287,.83968,.89049,.91484,.91484,.91484,.91484,.91484,.91484,.93575,.92383,.9403,.9403,.9403,.9403,.8717,.8717,.8717,.8717,1.00527,1.01756,1.05356,1.05356,1.05356,1.05356,1.05356,1.24644,.95923,1.01756,1.01756,1.01756,1.01756,.96777,1.05707,.96777,.96706,.91484,.96706,.91484,.96706,.91484,.85734,.92383,.85734,.92383,.85734,.92383,.85734,.92383,.97093,1.0969,.97093,1.05882,.75842,.9403,.75842,.9403,.75842,.9403,.75842,.9403,.75842,.9403,.88198,1.05882,.88198,1.05882,.88198,1.05882,1,1,.9831,1.01756,.9831,1.01756,.95782,.8717,.95782,.8717,.95782,.8717,.95782,1.09011,.95782,.8717,.84784,1.11551,.71387,1.09011,1,1,.99414,.84636,1.09011,1,1,.84636,1.0536,.84636,.94298,.84636,1.23297,1.03584,1.01756,1,1,1.03584,1.01756,1.00323,1.03444,1.01756,.96924,1.05356,.96924,1.05356,.96924,1.05356,.93066,.98293,.82826,1.04399,1,1,.82826,1.04399,.79649,.84863,.79649,.84863,.79649,.84863,.79649,.84863,1,1,.85771,1.17318,.85771,1.21968,.95132,1.01756,.95132,1.01756,.95132,1.01756,.95132,1.01756,.95132,1.01756,.95132,1.01756,.98965,1.00068,.8287,.96777,.8287,.93365,.9043,.93365,.9043,.93365,.9043,1.08571,.96927,.96706,.91484,.86035,.93575,.96924,.95923,1,1,.85771,1.21968,1.11437,1.11437,.93109,.91202,.60411,.84164,.55572,1.01173,.97361,.81818,.81818,.96635,.78032,.72727,.92366,.98601,1.03405,.77968,1.09799,1.2,.96706,.85944,.85638,.96491,.75842,.93365,.9831,.96924,.95782,.86969,.94152,1.07796,1.03584,.78437,.96924,.98715,.83968,.83491,.85771,.8287,.94492,.88433,.9287,1.0098,.95782,.8287,1.0625,.98248,1.03424,1.2,1.01071,1.0625,.95246,1.03809,1.04912,.98248,1.00221,1.03424,1.05443,1.2,1.04785,.99609,1.00169,1.05176,.99346,1.05356,.9087,1.03004,.95542,.93117,1.23362,1.01071,1.07831,1.02512,1.05205,1.03502,1.2,1.01071,1.05356,1.01071,1.03502,.75842,1,1,1,1,1,1,1,1,1,1,1,1,1.03719,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.9403,1,1,1,1,1,1,1,1,1,1,1,1,1.04021,1,1,1,1,1,1,.98965,1.00068,.98965,1.00068,.98965,1.00068,.8287,.96777,1,1.20088,.89903,1,1,.75155,1.03077,1.03077,1.03077,1.03077,1.13196,1.13196,1.13196,.67428,.67428,1.16039,.73291,1.20996,1.22135,1.06483,.94868,.94868,.95996,1.24633,1,1.07497,.87796,.96927,1.01518,.96927,1,1,1,.77295,1,1,1.10539,1.10539,1.11358,1.06967,.86279,.94434,.86279,.94434,.86182,1,1,1.083,1,.91578,.86507,1.1714,1.18416,1.14589,.69825,.97622,1.9697,1.24822,1.24822,1.17238,1.24822,1.24822,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1.42603,1,.99862,.99862,1,.87025,.87025,.87025,.87025,1.18083,1.42603,1,1.42603,1.42603,.99862,1,1,1,1,1,1.2886,1.04315,1.15296,1.34163,1,1,1,1.10938,1.10938,1,1,1,1.05425,1.09971,1.09971,1.09971,1,1,1,1,1,1,1,1,1,1,1],Gs={lineHeight:1.33008,lineGap:0},Vs=getLookupTableFactory(function(e){e["MyriadPro-Regular"]=e["PdfJS-Fallback-Regular"]={name:"LiberationSans-Regular",factors:Ns,baseWidths:Fs,baseMapping:Ts,metrics:Es};e["MyriadPro-Bold"]=e["PdfJS-Fallback-Bold"]={name:"LiberationSans-Bold",factors:Os,baseWidths:vs,baseMapping:Ss,metrics:Rs};e["MyriadPro-It"]=e["MyriadPro-Italic"]=e["PdfJS-Fallback-Italic"]={name:"LiberationSans-Italic",factors:Ds,baseWidths:Cs,baseMapping:Is,metrics:Ms};e["MyriadPro-BoldIt"]=e["MyriadPro-BoldItalic"]=e["PdfJS-Fallback-BoldItalic"]={name:"LiberationSans-BoldItalic",factors:Hs,baseWidths:As,baseMapping:xs,metrics:Bs};e.ArialMT=e.Arial=e["Arial-Regular"]={name:"LiberationSans-Regular",baseWidths:Fs,baseMapping:Ts};e["Arial-BoldMT"]=e["Arial-Bold"]={name:"LiberationSans-Bold",baseWidths:vs,baseMapping:Ss};e["Arial-ItalicMT"]=e["Arial-Italic"]={name:"LiberationSans-Italic",baseWidths:Cs,baseMapping:Is};e["Arial-BoldItalicMT"]=e["Arial-BoldItalic"]={name:"LiberationSans-BoldItalic",baseWidths:As,baseMapping:xs};e["Calibri-Regular"]={name:"LiberationSans-Regular",factors:ms,baseWidths:Fs,baseMapping:Ts,metrics:ps};e["Calibri-Bold"]={name:"LiberationSans-Bold",factors:os,baseWidths:vs,baseMapping:Ss,metrics:ls};e["Calibri-Italic"]={name:"LiberationSans-Italic",factors:hs,baseWidths:Cs,baseMapping:Is,metrics:us};e["Calibri-BoldItalic"]={name:"LiberationSans-BoldItalic",factors:fs,baseWidths:As,baseMapping:xs,metrics:cs};e["Segoeui-Regular"]={name:"LiberationSans-Regular",factors:Ks,baseWidths:Fs,baseMapping:Ts,metrics:Gs};e["Segoeui-Bold"]={name:"LiberationSans-Bold",factors:_s,baseWidths:vs,baseMapping:Ss,metrics:zs};e["Segoeui-Italic"]={name:"LiberationSans-Italic",factors:Xs,baseWidths:Cs,baseMapping:Is,metrics:Ws};e["Segoeui-BoldItalic"]={name:"LiberationSans-BoldItalic",factors:Ls,baseWidths:As,baseMapping:xs,metrics:Us};e["Helvetica-Regular"]=e.Helvetica={name:"LiberationSans-Regular",factors:ys,baseWidths:Fs,baseMapping:Ts,metrics:qs};e["Helvetica-Bold"]={name:"LiberationSans-Bold",factors:ds,baseWidths:vs,baseMapping:Ss,metrics:gs};e["Helvetica-Italic"]={name:"LiberationSans-Italic",factors:js,baseWidths:Cs,baseMapping:Is,metrics:ks};e["Helvetica-BoldItalic"]={name:"LiberationSans-BoldItalic",factors:bs,baseWidths:As,baseMapping:xs,metrics:ws}});function getXfaFontName(e){const t=normalizeFontName(e);return Vs()[t]}function getXfaFontDict(e){const t=function getXfaFontWidths(e){const t=getXfaFontName(e);if(!t)return null;const{baseWidths:a,baseMapping:n,factors:s}=t,r=s?a.map((e,t)=>e*s[t]):a;let i,o=-2;const l=[];for(const[e,t]of n.map((e,t)=>[e,t]).sort(([e],[t])=>e-t))if(-1!==e)if(e===o+1){i.push(r[t]);o+=1}else{o=e;i=[r[t]];l.push(e,i)}return l}(e),a=new Dict(null);a.set("BaseFont",Name.get(e));a.set("Type",Name.get("Font"));a.set("Subtype",Name.get("CIDFontType2"));a.set("Encoding",Name.get("Identity-H"));a.set("CIDToGIDMap",Name.get("Identity"));a.set("W",t);a.set("FirstChar",t[0]);a.set("LastChar",t.at(-2)+t.at(-1).length-1);const n=new Dict(null);a.set("FontDescriptor",n);const s=new Dict(null);s.set("Ordering","Identity");s.set("Registry","Adobe");s.set("Supplement",0);a.set("CIDSystemInfo",s);return a}const $s={number:0,lbrace:1,rbrace:2,true:3,false:4,add:5,sub:6,mul:7,div:8,idiv:9,mod:10,exp:11,eq:12,ne:13,gt:14,ge:15,lt:16,le:17,and:18,or:19,xor:20,bitshift:21,abs:22,neg:23,ceiling:24,floor:25,round:26,truncate:27,not:28,sqrt:29,sin:30,cos:31,ln:32,log:33,atan:34,cvi:35,cvr:36,dup:37,exch:38,pop:39,copy:40,index:41,roll:42,if:43,ifelse:44,eof:45,min:46,max:47};class Token{constructor(e,t=null){this.id=e;this.value=t}}class lexer_Lexer{static#re=null;static#ie=null;static#oe(){const e=Object.create(null),t=Object.create(null);for(const[a,n]of Object.entries($s)){if("number"===a)continue;const s=n>=$s.true&&n<=$s.ifelse,r=new Token(n,s?a:null);e[a]=r;s&&(t[a]=r)}this.#re=e;this.#ie=t}constructor(e){lexer_Lexer.#re||lexer_Lexer.#oe();this.data=e;this.pos=0;this.len=e.length;this._numberPattern=/[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?/y;this._identifierPattern=/[a-z]+/y}_skipComment(){const e=this.data.indexOf("\n",this.pos),t=this.data.indexOf("\r",this.pos),a=Math.min(e<0?this.len:e,t<0?this.len:t);this.pos=Math.min(a+1,this.len)}_getNumber(){this._numberPattern.lastIndex=this.pos;const e=this._numberPattern.exec(this.data);if(!e)return new Token($s.number,0);const t=parseFloat(e[0]);if(!Number.isFinite(t))return new Token($s.number,0);this.pos=this._numberPattern.lastIndex;return new Token($s.number,t)}_getOperator(){this._identifierPattern.lastIndex=this.pos;const e=this._identifierPattern.exec(this.data);if(!e)return new Token($s.number,0);this.pos=this._identifierPattern.lastIndex;const t=e[0],a=lexer_Lexer.#ie[t];return a||new Token($s.number,0)}next(){for(;this.pos=48&&e<=57){this.pos--;return this._getNumber()}if(e>=97&&e<=122){this.pos--;return this._getOperator()}return new Token($s.number,0)}}return lexer_Lexer.#re.eof}}const Ys=0,Js=1,Qs=2,Zs=0,er=1,tr=2,ar=3,nr=4,sr=5,rr=6,ir=7,or=8,lr=9,fr=10;class PsNode{constructor(e){this.type=e}}class PsProgram extends PsNode{constructor(e){super(Zs);this.body=e}}class PsBlock extends PsNode{constructor(e){super(er);this.instructions=e}}class PsNumber extends PsNode{constructor(e){super(tr);this.value=e}}class PsOperator extends PsNode{constructor(e){super(ar);this.op=e}}class PsIf extends PsNode{constructor(e){super(nr);this.then=e}}class PsIfElse extends PsNode{constructor(e,t){super(sr);this.then=e;this.otherwise=t}}class PsArgNode extends PsNode{constructor(e){super(rr);this.index=e;this.valueType=Ys}}class PsConstNode extends PsNode{constructor(e){super(ir);this.value=e;this.valueType="boolean"==typeof e?Js:Ys}}class PsUnaryNode extends PsNode{constructor(e,t,a=Qs){super(or);this.op=e;this.operand=t;this.valueType=a}}class PsBinaryNode extends PsNode{constructor(e,t,a,n=Qs){super(lr);this.op=e;this.first=t;this.second=a;this.valueType=n}}class PsTernaryNode extends PsNode{constructor(e,t,a,n=Qs){super(fr);this.cond=e;this.then=t;this.otherwise=a;this.valueType=n}}class ast_Parser{constructor(e){this.lexer=e;this._token=null}static _isRegularOperator(e){return e>=$s.true&&e<$s.if}_advance(){this._token=this.lexer.next()}_expect(e){if(this._token.id!==e)throw new FormatError(`PostScript function: expected token id ${e}, got ${this._token.id}.`);const t=this._token;this._advance();return t}parse(){this._advance();this._expect($s.lbrace);const e=this._parseBlock();this._expect($s.rbrace);this._token.id!==$s.eof&&warn("PostScript function: unexpected content after closing brace.");return new PsProgram(e)}_parseBlock(){const e=[];for(;;){const t=this._token;switch(t.id){case $s.number:e.push(new PsNumber(t.value));this._advance();break;case $s.lbrace:{this._advance();const t=this._parseBlock();this._expect($s.rbrace);if(this._token.id===$s.if){this._advance();e.push(new PsIf(t))}else{if(this._token.id!==$s.lbrace)throw new FormatError("PostScript function: a procedure block must be followed by 'if' or '{…} ifelse'.");{this._advance();const a=this._parseBlock();this._expect($s.rbrace);this._expect($s.ifelse);e.push(new PsIfElse(t,a))}}break}case $s.rbrace:case $s.eof:return new PsBlock(e);case $s.if:case $s.ifelse:throw new FormatError(`PostScript function: unexpected '${t.value}' operator.`);default:if(ast_Parser._isRegularOperator(t.id)){e.push(new PsOperator(t.id));this._advance();break}throw new FormatError(`PostScript function: unexpected token id ${t.id}.`)}}}}function parsePostScriptFunction(e){return new ast_Parser(new lexer_Lexer(e)).parse()}function _nodesEqual(e,t){if(e===t)return!0;if(e.type!==t.type)return!1;switch(e.type){case rr:return e.index===t.index;case ir:return e.value===t.value;case or:return e.op===t.op&&_nodesEqual(e.operand,t.operand);case lr:return e.op===t.op&&_nodesEqual(e.first,t.first)&&_nodesEqual(e.second,t.second);case fr:return _nodesEqual(e.cond,t.cond)&&_nodesEqual(e.then,t.then)&&_nodesEqual(e.otherwise,t.otherwise);default:return!1}}const cr=100;class PSStackToTree{static#le=null;static#fe=null;static#ce=null;static#he=null;static#ue(){this.#le=new Set([$s.add,$s.sub,$s.mul,$s.div,$s.idiv,$s.mod,$s.exp,$s.atan,$s.eq,$s.ne,$s.gt,$s.ge,$s.lt,$s.le,$s.and,$s.or,$s.xor,$s.bitshift]);this.#fe=new Set([$s.abs,$s.neg,$s.ceiling,$s.floor,$s.round,$s.truncate,$s.sqrt,$s.sin,$s.cos,$s.ln,$s.log,$s.cvi,$s.cvr,$s.not]);this.#ce=new Set([$s.abs,$s.ceiling,$s.cvi,$s.cvr,$s.floor,$s.round,$s.truncate]);this.#he=new Map([[$s.eq,$s.ne],[$s.ne,$s.eq],[$s.lt,$s.ge],[$s.le,$s.gt],[$s.gt,$s.le],[$s.ge,$s.lt]])}evaluate(e,t){PSStackToTree.#le||PSStackToTree.#ue();this._failed=!1;if(t>cr)return null;const a=[];for(let e=0;e{if(!e||e.type===rr||e.type===ir)return;const a=t.get(e)??0;t.set(e,a+1);if(!(a>0))switch(e.type){case or:visit(e.operand);break;case lr:visit(e.first);visit(e.second);break;case fr:visit(e.cond);visit(e.then);visit(e.otherwise)}};for(const t of e)visit(t);for(const[e,a]of t)if(a>1){e.shared=!0;e.sharedCount=a}}_evalBlock(e,t){this._evalBlockFrom(e.instructions,0,t)}_evalBlockFrom(e,t,a){for(let n=t;ncr&&(this._failed=!0);break;case ar:this._evalOp(t.op,a);break;case nr:{if(a.length<1){this._failed=!0;break}const s=a.pop(),r=a.slice();this._evalBlock(t.then,a);if(this._failed)break;if(a.length===r.length)for(let e=0;er.length){if(s.type===ir){if(!s.value){a.length=0;a.push(...r)}break}const t=a.slice();this._evalBlockFrom(e,n+1,t);if(this._failed)break;const i=r;this._evalBlockFrom(e,n+1,i);if(this._failed)break;if(t.length!==i.length){const e=new PsConstNode(0);for(;t.lengthcr&&(this._failed=!0);break;case $s.false:t.push(new PsConstNode(!1));t.length>cr&&(this._failed=!0);break;case $s.dup:if(t.length<1){this._failed=!0;break}t.push(t.at(-1));t.length>cr&&(this._failed=!0);break;case $s.exch:{if(t.length<2){this._failed=!0;break}const e=t.pop(),a=t.pop();t.push(e,a);break}case $s.pop:if(t.length<1){this._failed=!0;break}t.pop();break;case $s.copy:{if(t.length<1){this._failed=!0;break}const e=t.pop();if(e.type===ir){const a=0|e.value;if(0===a);else if(a<0||a>t.length)this._failed=!0;else{t.push(...t.slice(-a));t.length>cr&&(this._failed=!0)}}else this._failed=!0;break}case $s.index:{if(t.length<1){this._failed=!0;break}const e=t.pop();if(e.type===ir){const a=0|e.value;a<0||a>=t.length?this._failed=!0:t.push(t.at(-a-1))}else this._failed=!0;break}case $s.roll:{if(t.length<2){this._failed=!0;break}const e=t.pop(),a=t.pop();if(a.type===ir&&e.type===ir){const n=0|a.value;if(0===n);else if(n<0||n>t.length)this._failed=!0;else{const a=((0|e.value)%n+n)%n;if(a>0){const e=t.splice(-n,n);t.push(...e.slice(n-a),...e.slice(0,n-a))}}}else this._failed=!0;break}default:this._failed=!0}}_makeBinary(e,t,a){if(t.type===ir&&a.type===ir){const n=function _evalBinaryConst(e,t,a){switch(e){case $s.add:return t+a;case $s.sub:return t-a;case $s.mul:return t*a;case $s.div:return 0!==a?t/a:0;case $s.idiv:return 0!==a?Math.trunc(t/a):0;case $s.mod:return 0!==a?t-Math.trunc(t/a)*a:0;case $s.exp:{const e=t**a;return Number.isFinite(e)?e:void 0}case $s.atan:{let e=Math.atan2(t,a)*(180/Math.PI);e<0&&(e+=360);return e}case $s.eq:return t===a;case $s.ne:return t!==a;case $s.gt:return t>a;case $s.ge:return t>=a;case $s.lt:return t=0?t<>-a|0;case $s.min:return Math.min(t,a);case $s.max:return Math.max(t,a);default:return}}(e,a.value,t.value);if(void 0!==n)return new PsConstNode(n)}if(_nodesEqual(t,a))switch(e){case $s.sub:return new PsConstNode(0);case $s.xor:return new PsConstNode(t.valueType!==Js&&0);case $s.and:case $s.or:case $s.min:case $s.max:return t;case $s.eq:case $s.ge:case $s.le:return new PsConstNode(!0);case $s.ne:case $s.gt:case $s.lt:return new PsConstNode(!1)}if(t.type===ir){const n=t.value;switch(e){case $s.add:case $s.sub:if(0===n)return a;break;case $s.mul:if(1===n)return a;if(0===n)return t;if(-1===n)return this._makeUnary($s.neg,a);break;case $s.div:if(0!==n)return this._makeBinary($s.mul,new PsConstNode(1/n),a);break;case $s.idiv:if(1===n)return a;break;case $s.exp:if(1===n)return a;if(-1===n)return this._makeBinary($s.div,a,new PsConstNode(1));if(.5===n)return this._makeUnary($s.sqrt,a);if(.25===n){const e=this._makeUnary($s.sqrt,a);return this._makeUnary($s.sqrt,e)}if(2===n)return this._makeBinary($s.mul,a,a);if(3===n)return this._makeBinary($s.mul,this._makeBinary($s.mul,a,a),a);if(4===n){const e=this._makeBinary($s.mul,a,a);return this._makeBinary($s.mul,e,e)}if(0===n)return new PsConstNode(1);break;case $s.and:if(!0===n)return a;if(!1===n)return t;break;case $s.or:if(!1===n)return a;if(!0===n)return t;break;case $s.min:if(a.type===lr&&a.op===$s.max&&a.first.type===ir&&a.first.value>=n)return t;break;case $s.max:if(a.type===lr&&a.op===$s.min&&a.first.type===ir&&a.first.value<=n)return t}}if(a.type===ir){const n=a.value;switch(e){case $s.add:if(0===n)return t;break;case $s.sub:if(0===n)return this._makeUnary($s.neg,t);break;case $s.mul:if(1===n)return t;if(0===n)return a;if(-1===n)return this._makeUnary($s.neg,t);break;case $s.and:if(!0===n)return t;if(!1===n)return a;break;case $s.or:if(!1===n)return t;if(!0===n)return a}}return new PsBinaryNode(e,t,a,function _binaryValueType(e,t,a){switch(e){case $s.eq:case $s.ne:case $s.gt:case $s.ge:case $s.lt:case $s.le:return Js;case $s.and:case $s.or:case $s.xor:return t===a&&t!==Qs?t:Qs;default:return Ys}}(e,t.valueType,a.valueType))}_makeUnary(e,t){if(t.type===ir){const a=function _evalUnaryConst(e,t){switch(e){case $s.abs:return Math.abs(t);case $s.neg:return-t;case $s.ceiling:return Math.ceil(t);case $s.floor:return Math.floor(t);case $s.round:return Math.round(t);case $s.truncate:return Math.trunc(t);case $s.sqrt:{const e=Math.sqrt(t);return Number.isFinite(e)?e:void 0}case $s.sin:return Math.sin(t%360*Math.PI/180);case $s.cos:return Math.cos(t%360*Math.PI/180);case $s.ln:{const e=Math.log(t);return Number.isFinite(e)?e:void 0}case $s.log:{const e=Math.log10(t);return Number.isFinite(e)?e:void 0}case $s.cvi:return Math.trunc(t);case $s.cvr:return t;case $s.not:return"boolean"==typeof t?!t:~t;default:return}}(e,t.value);if(void 0!==a)return new PsConstNode(a)}if(e===$s.not&&t.type===lr){const e=PSStackToTree.#he.get(t.op);if(void 0!==e)return new PsBinaryNode(e,t.first,t.second,Js)}if(e===$s.neg&&t.type===lr&&t.op===$s.sub)return this._makeBinary($s.sub,t.second,t.first);if(t.type===or){if(e===$s.neg&&t.op===$s.neg||e===$s.not&&t.op===$s.not)return t.operand;if(e===$s.abs&&t.op===$s.neg)return this._makeUnary($s.abs,t.operand);if(PSStackToTree.#ce.has(e)&&e===t.op)return t}return new PsUnaryNode(e,t,function _unaryValueType(e,t){return e===$s.not?t:Ys}(e,t.valueType))}_makeTernary(e,t,a){if(e.type===ir)return e.value?t:a;if(_nodesEqual(t,a))return t;if(t.type===ir&&a.type===ir){if(!0===t.value&&!1===a.value)return e;if(!1===t.value&&!0===a.value)return this._makeUnary($s.not,e)}if(e.type===lr){const{op:n,first:s,second:r}=e;if(n===$s.gt||n===$s.ge){if(_nodesEqual(t,s)&&_nodesEqual(a,r))return this._makeBinary($s.min,s,r);if(_nodesEqual(t,r)&&_nodesEqual(a,s))return this._makeBinary($s.max,s,r)}else if(n===$s.lt||n===$s.le){if(_nodesEqual(t,s)&&_nodesEqual(a,r))return this._makeBinary($s.max,s,r);if(_nodesEqual(t,r)&&_nodesEqual(a,s))return this._makeBinary($s.min,s,r)}}return new PsTernaryNode(e,t,a,t.valueType===a.valueType?t.valueType:Qs)}}const hr=0,ur=1,mr=2,pr=3,dr=4,gr=5,br=6,wr=7,jr=8,kr=9,yr=10,qr=11,vr=12,Sr=13,Ar=14,xr=15,Cr=16,Ir=17,Fr=18,Tr=19,Or=20,Rr=21,Hr=22,Dr=23,Mr=24,Nr=25,Pr=26,Er=27,_r=28,zr=29,Lr=30,Ur=31,Xr=32,Wr=33,Kr=34,Gr=35,Vr=36,$r=37,Yr=38,Jr=39,Qr=40,Zr=Math.PI/180,ei=180/Math.PI;class PsJsCompiler{static#pe=new Float64Array(64);static#de=new Float64Array(64);constructor(e,t){this.nIn=e.length>>1;this.nOut=t.length>>1;this.range=t;this.ir=[];this._tmpMap=new Map;this._nextTmp=0}_compileNode(e){if(e.shared){const t=this._tmpMap.get(e);if(void 0!==t){this.ir.push(Qr,t);return!0}if(!this._compileNodeImpl(e))return!1;const a=this._nextTmp++;this._tmpMap.set(e,a);this.ir.push(Jr,a);return!0}return this._compileNodeImpl(e)}_compileNodeImpl(e){switch(e.type){case rr:this.ir.push(hr,e.index);return!0;case ir:{const t=e.value;this.ir.push(ur,"boolean"==typeof t?Number(t):t);return!0}case or:return this._compileUnary(e);case lr:return this._compileBinary(e);case fr:return this._compileTernary(e);default:return!1}}_compileUnary(e){const{op:t,operand:a,valueType:n}=e;if(t===$s.cvr)return this._compileNode(a);if(!this._compileNode(a))return!1;switch(t){case $s.abs:this.ir.push(gr);break;case $s.neg:this.ir.push(br);break;case $s.ceiling:this.ir.push(wr);break;case $s.floor:this.ir.push(jr);break;case $s.round:this.ir.push(kr);break;case $s.truncate:this.ir.push(yr);break;case $s.sqrt:this.ir.push(Sr);break;case $s.sin:this.ir.push(Ar);break;case $s.cos:this.ir.push(xr);break;case $s.ln:this.ir.push(Cr);break;case $s.log:this.ir.push(Ir);break;case $s.cvi:this.ir.push(Fr);break;case $s.not:if(n===Js)this.ir.push(qr);else{if(n!==Ys)return!1;this.ir.push(vr)}break;default:return!1}return!0}_compileBinary(e){const{op:t,first:a,second:n}=e;if(t===$s.bitshift){if(a.type!==ir||!Number.isInteger(a.value))return!1;if(!this._compileNode(n))return!1;this.ir.push(Tr,a.value);return!0}if(!this._compileNode(n))return!1;if(!this._compileNode(a))return!1;switch(t){case $s.add:this.ir.push(Or);break;case $s.sub:this.ir.push(Rr);break;case $s.mul:this.ir.push(Hr);break;case $s.div:this.ir.push(Dr);break;case $s.idiv:this.ir.push(Mr);break;case $s.mod:this.ir.push(Nr);break;case $s.exp:this.ir.push(Pr);break;case $s.eq:this.ir.push(Er);break;case $s.ne:this.ir.push(_r);break;case $s.gt:this.ir.push(zr);break;case $s.ge:this.ir.push(Lr);break;case $s.lt:this.ir.push(Ur);break;case $s.le:this.ir.push(Xr);break;case $s.and:this.ir.push(Wr);break;case $s.or:this.ir.push(Kr);break;case $s.xor:this.ir.push(Gr);break;case $s.atan:this.ir.push(Vr);break;case $s.min:this.ir.push($r);break;case $s.max:this.ir.push(Yr);break;default:return!1}return!0}_compileTernary(e){if(!this._compileNode(e.cond))return!1;this.ir.push(pr,0);const t=this.ir.length-1;if(!this._compileNode(e.then))return!1;this.ir.push(dr,0);const a=this.ir.length-1;this.ir[t]=this.ir.length;if(!this._compileNode(e.otherwise))return!1;this.ir[a]=this.ir.length;return!0}compile(e){const t=(new PSStackToTree).evaluate(e,this.nIn);if(!t||t.length0?a<>-t:a;break}case Or:{const e=l[--i];l[i-1]+=e;break}case Rr:{const e=l[--i];l[i-1]-=e;break}case Hr:{const e=l[--i];l[i-1]*=e;break}case Dr:{const e=l[--i];l[i-1]=0!==e?l[i-1]/e:0;break}case Mr:{const e=l[--i];l[i-1]=0!==e?Math.trunc(l[i-1]/e):0;break}case Nr:{const e=l[--i];l[i-1]=0!==e?l[i-1]%e:0;break}case Pr:{const e=l[--i];l[i-1]**=e;break}case Er:{const e=l[--i];l[i-1]=l[i-1]===e?1:0;break}case _r:{const e=l[--i];l[i-1]=l[i-1]!==e?1:0;break}case zr:{const e=l[--i];l[i-1]=l[i-1]>e?1:0;break}case Lr:{const e=l[--i];l[i-1]=l[i-1]>=e?1:0;break}case Ur:{const e=l[--i];l[i-1]=l[i-1]e?1:0;break}case $s.ge:{const e=t[--this.#ge];t[this.#ge-1]=t[this.#ge-1]>=e?1:0;break}case $s.lt:{const e=t[--this.#ge];t[this.#ge-1]=t[this.#ge-1]0?a<>-e;break}case $s.min:{const e=t[--this.#ge];t[this.#ge-1]=Math.min(t[this.#ge-1],e);break}case $s.max:{const e=t[--this.#ge];t[this.#ge-1]=Math.max(t[this.#ge-1],e);break}case $s.dup:this.#be(t[this.#ge-1]);break;case $s.exch:{const e=t[--this.#ge],a=t[--this.#ge];this.#be(e);this.#be(a);break}case $s.pop:this.#ge--;break;case $s.copy:{const e=Math.trunc(t[--this.#ge]),a=this.#ge-e;for(let n=0;n1&&0!==e){const n=(e%a+a)%a;if(0!==n){const e=this.#ge-a,s=t.slice(e,this.#ge);for(let r=0;r>1,s=a.length>>1,{instructions:r}=e.body;return(e,t,i,o)=>{this.#ge=0;for(let a=0;a=0?this.#pe[l+e]:0;i[o+e]=MathClamp(a[2*e+1],a[2*e],t)}}}}const ti={if:4,else:5,end:11,select:27,call:16,local_get:32,local_set:33,local_tee:34,i32_const:65,i32_eqz:69,i32_and:113,i32_or:114,i32_xor:115,i32_shl:116,i32_shr_s:117,i32_trunc_f64_s:170,f64_const:68,f64_eq:97,f64_ne:98,f64_lt:99,f64_gt:100,f64_le:101,f64_ge:102,f64_abs:153,f64_neg:154,f64_ceil:155,f64_floor:156,f64_trunc:157,f64_nearest:158,f64_sqrt:159,f64_add:160,f64_sub:161,f64_mul:162,f64_div:163,f64_min:164,f64_max:165,f64_convert_i32_s:183,f64_store:57},ai=124,ni=1,si=2,ri=3,ii=5,oi=7,li=10;function unsignedLEB128(e){const t=[];do{let a=127&e;0!==(e>>>=7)&&(a|=128);t.push(a)}while(0!==e);return t}function encodeASCIIString(e){return[...unsignedLEB128(e.length),...Array.from(e,e=>e.charCodeAt(0))]}function section(e,t){return[e,...unsignedLEB128(t.length),...t]}function vec(e){const t=unsignedLEB128(e.length);for(const a of e)if("number"!=typeof a)for(const e of a)t.push(e);else t.push(a);return t}const fi=[["sin","Math","sin",[ai],[ai]],["cos","Math","cos",[ai],[ai]],["atan2","Math","atan2",[ai,ai],[ai]],["log","Math","log",[ai],[ai]],["log10","Math","log10",[ai],[ai]],["pow","Math","pow",[ai,ai],[ai]]],ci={Math:Object.fromEntries(fi.map(([e])=>[e,Math[e]]))};class PsWasmCompiler{static#ke=!1;static#ye=null;static#qe=null;static#ve=0;static#Se=0;static#Ae=null;static#xe=null;static#Ce=null;static#Ie=null;static#Fe=null;static#Te=null;static#Oe=null;static#Re=null;static#ue(){this.#ye=new Map([[$s.eq,ti.f64_eq],[$s.ne,ti.f64_ne],[$s.lt,ti.f64_lt],[$s.le,ti.f64_le],[$s.gt,ti.f64_gt],[$s.ge,ti.f64_ge]]);this.#qe=Object.create(null);for(let e=0;e[96,...vec(e),...vec(t)]);this.#xe=new Uint8Array(section(si,vec(fi.map(([,e,t],a)=>[...encodeASCIIString(e),...encodeASCIIString(t),0,...unsignedLEB128(a+1)]))));this.#Ce=new Uint8Array(section(ri,vec([[0]])));this.#Ie=new Uint8Array(section(ii,vec([[0,1]])));this.#Fe=new Uint8Array(section(oi,vec([[...encodeASCIIString("fn"),0,...unsignedLEB128(fi.length)],[...encodeASCIIString("mem"),2,0]])));this.#Te=new Uint8Array([0,97,115,109,1,0,0,0]);const e=new ArrayBuffer(8);this.#Oe=new DataView(e);this.#Re=new Uint8Array(e);this.#ke=!0}constructor(e,t){PsWasmCompiler.#ke||PsWasmCompiler.#ue();this._nIn=e.length>>1;this._nOut=t.length>>1;this._range=t;this._code=[];this._nextLocal=this._nIn;this._freeLocals=[];this._sharedLocals=new Map}_allocLocal(){return this._freeLocals.pop()??this._nextLocal++}_releaseLocal(e){this._freeLocals.push(e)}_emitULEB128(e){do{let t=127&e;0!==(e>>>=7)&&(t|=128);this._code.push(t)}while(0!==e)}_emitF64Const(e){this._code.push(ti.f64_const);PsWasmCompiler.#Oe.setFloat64(0,e,!0);for(let e=0;e<8;e++)this._code.push(PsWasmCompiler.#Re[e])}_emitLocalGet(e){this._code.push(ti.local_get);this._emitULEB128(e)}_emitLocalSet(e){this._code.push(ti.local_set);this._emitULEB128(e)}_emitLocalTee(e){this._code.push(ti.local_tee);this._emitULEB128(e)}_compileNode(e){if(e.shared){const t=this._sharedLocals.get(e);if(void 0!==t){this._emitLocalGet(t.local);0===--t.remaining&&this._releaseLocal(t.local);return!0}if(!this._compileNodeImpl(e))return!1;const a=this._allocLocal();this._sharedLocals.set(e,{local:a,remaining:e.sharedCount-1});this._emitLocalTee(a);return!0}return this._compileNodeImpl(e)}_compileNodeImpl(e){switch(e.type){case rr:this._emitLocalGet(e.index);return!0;case ir:{let t=e.value;"boolean"==typeof t&&(t=t?1:0);this._emitF64Const(t);return!0}case or:return this._compileUnaryNode(e);case lr:return this._compileBinaryNode(e);case fr:return this._compileTernaryNode(e);default:return!1}}_compileSinCosNode(e){const t=this._allocLocal();try{if(!this._compileNode(e.operand))return!1;const a=this._code;this._emitLocalSet(t);this._emitLocalGet(t);this._emitLocalGet(t);this._emitF64Const(360);a.push(ti.f64_div,ti.f64_trunc);this._emitF64Const(360);a.push(ti.f64_mul,ti.f64_sub);this._emitF64Const(PsWasmCompiler.#ve);a.push(ti.f64_mul,ti.call);this._emitULEB128(PsWasmCompiler.#qe[e.op===$s.sin?"sin":"cos"]);return!0}finally{this._releaseLocal(t)}}_compileUnaryNode(e){const t=this._code;if(e.op===$s.sin||e.op===$s.cos)return this._compileSinCosNode(e);if(e.op===$s.not){if(e.valueType===Js){if(!this._compileNodeAsBoolI32(e.operand))return!1;t.push(ti.i32_eqz,ti.f64_convert_i32_s);return!0}if(e.valueType===Ys){if(!this._compileNode(e.operand))return!1;t.push(ti.i32_trunc_f64_s,ti.i32_const,127,ti.i32_xor,ti.f64_convert_i32_s);return!0}return!1}if(!this._compileNode(e.operand))return!1;switch(e.op){case $s.abs:t.push(ti.f64_abs);break;case $s.neg:t.push(ti.f64_neg);break;case $s.sqrt:t.push(ti.f64_sqrt);break;case $s.floor:t.push(ti.f64_floor);break;case $s.ceiling:t.push(ti.f64_ceil);break;case $s.round:this._emitF64Const(.5);t.push(ti.f64_add,ti.f64_floor);break;case $s.truncate:t.push(ti.f64_trunc);break;case $s.cvi:t.push(ti.i32_trunc_f64_s,ti.f64_convert_i32_s);break;case $s.cvr:break;case $s.ln:t.push(ti.call);this._emitULEB128(PsWasmCompiler.#qe.log);break;case $s.log:t.push(ti.call);this._emitULEB128(PsWasmCompiler.#qe.log10);break;default:return!1}return!0}_compileSafeDivNode(e,t){const a=this._allocLocal();try{if(!this._compileNode(t))return!1;if(!this._compileNode(e))return!1;const n=this._code;this._emitLocalTee(a);n.push(ti.f64_div);this._emitF64Const(0);this._emitLocalGet(a);this._emitF64Const(0);n.push(ti.f64_ne,ti.select);return!0}finally{this._releaseLocal(a)}}_compileSafeIdivNode(e,t){const a=this._allocLocal();try{if(!this._compileNode(t))return!1;if(!this._compileNode(e))return!1;const n=this._code;this._emitLocalTee(a);n.push(ti.f64_div,ti.f64_trunc);this._emitF64Const(0);this._emitLocalGet(a);this._emitF64Const(0);n.push(ti.f64_ne,ti.select);return!0}finally{this._releaseLocal(a)}}_compileBitshiftNode(e,t){if(e.type!==ir||!Number.isInteger(e.value))return!1;if(!this._compileNode(t))return!1;const a=this._code;a.push(ti.i32_trunc_f64_s);const n=e.value;if(n>0){a.push(ti.i32_const);this._emitULEB128(n);a.push(ti.i32_shl)}else if(n<0){a.push(ti.i32_const);this._emitULEB128(-n);a.push(ti.i32_shr_s)}a.push(ti.f64_convert_i32_s);return!0}_compileModNode(e,t){if(e.type===ir&&0===e.value){if(!this._compileNode(t))return!1;this._code.push(ti.drop);this._emitF64Const(0);return!0}const a=this._allocLocal();try{if(!this._compileNode(t))return!1;this._emitLocalTee(a);const n=this._code;if(e.type===ir){this._emitLocalGet(a);this._emitF64Const(e.value);n.push(ti.f64_div,ti.f64_trunc);this._emitF64Const(e.value);n.push(ti.f64_mul,ti.f64_sub)}else{const t=this._allocLocal();try{if(!this._compileNode(e))return!1;this._emitLocalSet(t);this._emitLocalGet(a);this._emitLocalGet(t);n.push(ti.f64_div,ti.f64_trunc);this._emitLocalGet(t);n.push(ti.f64_mul,ti.f64_sub);this._emitF64Const(0);this._emitLocalGet(t);this._emitF64Const(0);n.push(ti.f64_ne,ti.select)}finally{this._releaseLocal(t)}}return!0}finally{this._releaseLocal(a)}}_compileAtanNode(e,t){const a=this._allocLocal();try{if(!this._compileNode(t))return!1;if(!this._compileNode(e))return!1;const n=this._code;n.push(ti.call);this._emitULEB128(PsWasmCompiler.#qe.atan2);this._emitF64Const(PsWasmCompiler.#Se);n.push(ti.f64_mul);this._emitLocalTee(a);this._emitF64Const(0);n.push(ti.f64_lt,ti.if,ai);this._emitLocalGet(a);this._emitF64Const(360);n.push(ti.f64_add,ti.else);this._emitLocalGet(a);n.push(ti.end);return!0}finally{this._releaseLocal(a)}}_compileBitwiseNode(e,t,a){if(!this._compileBitwiseOperandI32(a))return!1;if(!this._compileBitwiseOperandI32(t))return!1;const n=this._code;switch(e){case $s.and:n.push(ti.i32_and);break;case $s.or:n.push(ti.i32_or);break;case $s.xor:n.push(ti.i32_xor);break;default:return!1}n.push(ti.f64_convert_i32_s);return!0}_compileBitwiseOperandI32(e){if(e.valueType===Js)return this._compileNodeAsBoolI32(e);if(!this._compileNode(e))return!1;this._code.push(ti.i32_trunc_f64_s);return!0}_compileStandardBinaryNode(e,t,a){if(t!==a||t.type===rr||t.type===ir||t.shared){if(!this._compileNode(a))return!1;if(!this._compileNode(t))return!1}else{const e=this._allocLocal();try{if(!this._compileNode(t))return!1;this._emitLocalTee(e);this._emitLocalGet(e)}finally{this._releaseLocal(e)}}const n=this._code;switch(e){case $s.add:n.push(ti.f64_add);break;case $s.sub:n.push(ti.f64_sub);break;case $s.mul:n.push(ti.f64_mul);break;case $s.exp:n.push(ti.call);this._emitULEB128(PsWasmCompiler.#qe.pow);break;case $s.eq:n.push(ti.f64_eq,ti.f64_convert_i32_s);break;case $s.ne:n.push(ti.f64_ne,ti.f64_convert_i32_s);break;case $s.lt:n.push(ti.f64_lt,ti.f64_convert_i32_s);break;case $s.le:n.push(ti.f64_le,ti.f64_convert_i32_s);break;case $s.gt:n.push(ti.f64_gt,ti.f64_convert_i32_s);break;case $s.ge:n.push(ti.f64_ge,ti.f64_convert_i32_s);break;case $s.min:n.push(ti.f64_min);break;case $s.max:n.push(ti.f64_max);break;default:return!1}return!0}_compileBinaryNode(e){const{op:t,first:a,second:n}=e;return t===$s.bitshift?this._compileBitshiftNode(a,n):t===$s.div?this._compileSafeDivNode(a,n):t===$s.idiv?this._compileSafeIdivNode(a,n):t===$s.mod?this._compileModNode(a,n):t===$s.atan?this._compileAtanNode(a,n):t===$s.and||t===$s.or||t===$s.xor?this._compileBitwiseNode(t,a,n):this._compileStandardBinaryNode(t,a,n)}_compileNodeAsBoolI32(e){if(e.type===lr){const t=PsWasmCompiler.#ye.get(e.op);if(void 0!==t){if(!this._compileNode(e.second))return!1;if(!this._compileNode(e.first))return!1;this._code.push(t);return!0}if(e.valueType===Js&&(e.op===$s.and||e.op===$s.or||e.op===$s.xor)){if(!this._compileNodeAsBoolI32(e.second))return!1;if(!this._compileNodeAsBoolI32(e.first))return!1;switch(e.op){case $s.and:this._code.push(ti.i32_and);break;case $s.or:this._code.push(ti.i32_or);break;case $s.xor:this._code.push(ti.i32_xor)}return!0}}if(e.type===or&&e.op===$s.not&&e.valueType===Js){if(!this._compileNodeAsBoolI32(e.operand))return!1;this._code.push(ti.i32_eqz);return!0}if(!this._compileNode(e))return!1;if(e.valueType===Js)this._code.push(ti.i32_trunc_f64_s);else{this._emitF64Const(0);this._code.push(ti.f64_ne)}return!0}_compileTernaryNode(e){if(!this._compileNodeAsBoolI32(e.cond))return!1;this._code.push(ti.if,ai);if(!this._compileNode(e.then))return!1;this._code.push(ti.else);if(!this._compileNode(e.otherwise))return!1;this._code.push(ti.end);return!0}compile(e){const t=(new PSStackToTree).evaluate(e,this._nIn);if(!t||t.length0?[[...unsignedLEB128(s),ai]]:[]),l=o.length+a.length,f=new Uint8Array(section(li,vec([[...unsignedLEB128(l),...o,...a]]))),c=PsWasmCompiler.#Te,h=PsWasmCompiler.#xe,u=PsWasmCompiler.#Ce,m=PsWasmCompiler.#Ie,p=PsWasmCompiler.#Fe,d=c.length+i.length+h.length+u.length+m.length+p.length+f.length,g=new Uint8Array(d);let b=0;g.set(c,b);b+=c.length;g.set(i,b);b+=i.length;g.set(h,b);b+=h.length;g.set(u,b);b+=u.length;g.set(m,b);b+=m.length;g.set(p,b);b+=p.length;g.set(f,b);return g}}function buildPostScriptWasmFunction(e,t,a){const n=function compilePostScriptToWasm(e,t,a){return new PsWasmCompiler(t,a).compile(parsePostScriptFunction(e))}(e,t,a);if(!n)return null;try{return function _makeWrapper(e,t,a){const{fn:n,mem:s}=e,r=new Float64Array(s.buffer,0,a);let i;switch(a){case 1:i=(e,t)=>{e[t]=r[0]};break;case 2:i=(e,t)=>{e[t]=r[0];e[t+1]=r[1]};break;case 3:i=(e,t)=>{e[t]=r[0];e[t+1]=r[1];e[t+2]=r[2]};break;case 4:i=(e,t)=>{e[t]=r[0];e[t+1]=r[1];e[t+2]=r[2];e[t+3]=r[3]};break;default:i=(e,t)=>{for(let n=0;n{n(e[t]);i(a,s)};case 2:return(e,t,a,s)=>{n(e[t],e[t+1]);i(a,s)};case 3:return(e,t,a,s)=>{n(e[t],e[t+1],e[t+2]);i(a,s)};case 4:return(e,t,a,s)=>{n(e[t],e[t+1],e[t+2],e[t+3]);i(a,s)};default:{const e=new Float64Array(t);return(a,s,r,o)=>{for(let n=0;n>1,a.length>>1)}catch{return null}}class BaseLocalCache{constructor(e){this._onlyRefs=!0===e?.onlyRefs;if(!this._onlyRefs){this._nameRefMap=new Map;this._imageMap=new Map}this._imageCache=new RefSetCache}getByName(e){this._onlyRefs&&unreachable("Should not call `getByName` method.");const t=this._nameRefMap.get(e);return t?this.getByRef(t):this._imageMap.get(e)||null}getByRef(e){return this._imageCache.get(e)||null}set(e,t,a){unreachable("Abstract method `set` called.")}}class LocalImageCache extends BaseLocalCache{set(e,t=null,a){if("string"!=typeof e)throw new Error('LocalImageCache.set - expected "name" argument.');if(t){if(this._imageCache.has(t))return;this._nameRefMap.set(e,t);this._imageCache.put(t,a)}else this._imageMap.has(e)||this._imageMap.set(e,a)}}class LocalColorSpaceCache extends BaseLocalCache{set(e=null,t=null,a){if("string"!=typeof e&&!t)throw new Error('LocalColorSpaceCache.set - expected "name" and/or "ref" argument.');if(t){if(this._imageCache.has(t))return;null!==e&&this._nameRefMap.set(e,t);this._imageCache.put(t,a)}else this._imageMap.has(e)||this._imageMap.set(e,a)}}class LocalFunctionCache extends BaseLocalCache{constructor(e){super({onlyRefs:!0})}set(e=null,t,a){if(!t)throw new Error('LocalFunctionCache.set - expected "ref" argument.');this._imageCache.has(t)||this._imageCache.put(t,a)}}class LocalGStateCache extends BaseLocalCache{set(e,t=null,a){if("string"!=typeof e)throw new Error('LocalGStateCache.set - expected "name" argument.');if(t){if(this._imageCache.has(t))return;this._nameRefMap.set(e,t);this._imageCache.put(t,a)}else this._imageMap.has(e)||this._imageMap.set(e,a)}}class LocalTilingPatternCache extends BaseLocalCache{constructor(e){super({onlyRefs:!0})}set(e=null,t,a){if(!t)throw new Error('LocalTilingPatternCache.set - expected "ref" argument.');this._imageCache.has(t)||this._imageCache.put(t,a)}}class RegionalImageCache extends BaseLocalCache{constructor(e){super({onlyRefs:!0})}set(e=null,t,a){if(!t)throw new Error('RegionalImageCache.set - expected "ref" argument.');this._imageCache.has(t)||this._imageCache.put(t,a)}}class GlobalColorSpaceCache extends BaseLocalCache{constructor(e){super({onlyRefs:!0})}set(e=null,t,a){if(!t)throw new Error('GlobalColorSpaceCache.set - expected "ref" argument.');this._imageCache.has(t)||this._imageCache.put(t,a)}clear(){this._imageCache.clear()}}class GlobalImageCache{static NUM_PAGES_THRESHOLD=2;static MIN_IMAGES_TO_CACHE=10;static MAX_BYTE_SIZE=5e7;#He=new RefSet;constructor(){this._refCache=new RefSetCache;this._imageCache=new RefSetCache}get#Be(){let e=0;for(const t of this._imageCache)e+=t.byteSize;return e}get#De(){return!(this._imageCache.size+e):null}class PDFFunction{static getSampleArray(e,t,a,n){let s=t;for(const t of e)s*=t;const r=new Array(s);let i=0,o=0;const l=1/(2**a-1),f=n.getBytes((s*a+7)/8);let c=0;for(let e=0;e>i)*l;o&=(1<0?r[h-1]:a[0],m=h{PsJsCompiler.execute(r,e,t,a,n)}:PSStackBasedInterpreter.build(s,t,a)}(r,n,s)}}function isPDFFunction(e){let t;if(e instanceof Dict)t=e;else{if(!(e instanceof BaseStream))return!1;t=e.dict}return t.has("FunctionType")}const di=["BN","BN","BN","BN","BN","BN","BN","BN","BN","S","B","S","WS","B","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","B","B","B","S","WS","ON","ON","ET","ET","ET","ON","ON","ON","ON","ON","ES","CS","ES","CS","CS","EN","EN","EN","EN","EN","EN","EN","EN","EN","EN","CS","ON","ON","ON","ON","ON","ON","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","ON","ON","ON","ON","ON","ON","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","ON","ON","ON","ON","BN","BN","BN","BN","BN","BN","B","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","CS","ON","ET","ET","ET","ET","ON","ON","ON","ON","L","ON","ON","BN","ON","ON","ET","ET","EN","EN","ON","L","ON","ON","ON","EN","L","ON","ON","ON","ON","ON","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","ON","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","ON","L","L","L","L","L","L","L","L"],gi=["AN","AN","AN","AN","AN","AN","ON","ON","AL","ET","ET","AL","CS","AL","ON","ON","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","AL","AL","","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","AN","AN","AN","AN","AN","AN","AN","AN","AN","AN","ET","AN","AN","AL","AL","AL","NSM","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","NSM","NSM","NSM","NSM","NSM","NSM","NSM","AN","ON","NSM","NSM","NSM","NSM","NSM","NSM","AL","AL","NSM","NSM","ON","NSM","NSM","NSM","NSM","AL","AL","EN","EN","EN","EN","EN","EN","EN","EN","EN","EN","AL","AL","AL","AL","AL","AL"];function isOdd(e){return!!(1&e)}function isEven(e){return!(1&e)}function findUnequal(e,t,a){let n,s;for(n=t,s=e.length;n4){n=!0;t=0}else{n=!1;t=1}const l=[];for(r=0;r=0&&"ET"===wi[e];--e)wi[e]="EN";for(let e=r+1;e=0;e--){const a=wi[e];if("L"===a){t="L";break}if("R"===a||"EN"===a||"AN"===a){t="R";break}}let a=h;for(let t=e;tp&&isOdd(p)&&(g=p)}for(p=d;p>=g;--p){let e=-1;for(r=0,i=l.length;r=0){reverseValues(bi,e,r);e=-1}}else e<0&&(e=r);e>=0&&reverseValues(bi,e,l.length)}for(r=0,i=bi.length;r"!==e||(bi[r]="")}return createBidiText(bi.join(""),n)}const ji={style:"normal",weight:"normal"},ki={style:"normal",weight:"bold"},yi={style:"italic",weight:"normal"},qi={style:"italic",weight:"bold"},vi=new Map([["Times-Roman",{local:["Times New Roman","Times-Roman","Times","Liberation Serif","Nimbus Roman","Nimbus Roman L","Tinos","Thorndale","TeX Gyre Termes","FreeSerif","Linux Libertine O","Libertinus Serif","PT Astra Serif","DejaVu Serif","Bitstream Vera Serif","Ubuntu"],style:ji,ultimate:"serif"}],["Times-Bold",{alias:"Times-Roman",style:ki,ultimate:"serif"}],["Times-Italic",{alias:"Times-Roman",style:yi,ultimate:"serif"}],["Times-BoldItalic",{alias:"Times-Roman",style:qi,ultimate:"serif"}],["Helvetica",{local:["Helvetica","Helvetica Neue","Arial","Arial Nova","Liberation Sans","Arimo","Nimbus Sans","Nimbus Sans L","A030","TeX Gyre Heros","FreeSans","DejaVu Sans","Albany","Bitstream Vera Sans","Arial Unicode MS","Microsoft Sans Serif","Apple Symbols","Cantarell"],path:"LiberationSans-Regular.ttf",style:ji,ultimate:"sans-serif"}],["Helvetica-Bold",{alias:"Helvetica",path:"LiberationSans-Bold.ttf",style:ki,ultimate:"sans-serif"}],["Helvetica-Oblique",{alias:"Helvetica",path:"LiberationSans-Italic.ttf",style:yi,ultimate:"sans-serif"}],["Helvetica-BoldOblique",{alias:"Helvetica",path:"LiberationSans-BoldItalic.ttf",style:qi,ultimate:"sans-serif"}],["Courier",{local:["Courier","Courier New","Liberation Mono","Nimbus Mono","Nimbus Mono L","Cousine","Cumberland","TeX Gyre Cursor","FreeMono","Linux Libertine Mono O","Libertinus Mono"],style:ji,ultimate:"monospace"}],["Courier-Bold",{alias:"Courier",style:ki,ultimate:"monospace"}],["Courier-Oblique",{alias:"Courier",style:yi,ultimate:"monospace"}],["Courier-BoldOblique",{alias:"Courier",style:qi,ultimate:"monospace"}],["ArialBlack",{local:["Arial Black"],style:{style:"normal",weight:"900"},fallback:"Helvetica-Bold"}],["ArialBlack-Bold",{alias:"ArialBlack"}],["ArialBlack-Italic",{alias:"ArialBlack",style:{style:"italic",weight:"900"},fallback:"Helvetica-BoldOblique"}],["ArialBlack-BoldItalic",{alias:"ArialBlack-Italic"}],["ArialNarrow",{local:["Arial Narrow","Liberation Sans Narrow","Helvetica Condensed","Nimbus Sans Narrow","TeX Gyre Heros Cn"],style:ji,fallback:"Helvetica"}],["ArialNarrow-Bold",{alias:"ArialNarrow",style:ki,fallback:"Helvetica-Bold"}],["ArialNarrow-Italic",{alias:"ArialNarrow",style:yi,fallback:"Helvetica-Oblique"}],["ArialNarrow-BoldItalic",{alias:"ArialNarrow",style:qi,fallback:"Helvetica-BoldOblique"}],["Calibri",{local:["Calibri","Carlito"],style:ji,fallback:"Helvetica"}],["Calibri-Bold",{alias:"Calibri",style:ki,fallback:"Helvetica-Bold"}],["Calibri-Italic",{alias:"Calibri",style:yi,fallback:"Helvetica-Oblique"}],["Calibri-BoldItalic",{alias:"Calibri",style:qi,fallback:"Helvetica-BoldOblique"}],["Wingdings",{local:["Wingdings","URW Dingbats"],style:ji}],["Wingdings-Regular",{alias:"Wingdings"}],["Wingdings-Bold",{alias:"Wingdings"}],["ËÎÌå",{local:["SimSun","SimSun Regular","NSimSun"],style:ji,ultimate:"serif"}],["ºÚÌå",{local:["SimHei","SimHei Regular"],style:ji,ultimate:"sans-serif"}],["¿¬Ìå",{local:["KaiTi","SimKai","SimKai Regular"],style:ji,ultimate:"sans-serif"}],["·ÂËÎ",{local:["FangSong","SimFang","SimFang Regular"],style:ji,ultimate:"serif"}],["¿¬Ìå_GB2312",{alias:"¿¬Ìå"}],["·ÂËÎ_GB2312",{alias:"·ÂËÎ"}],["Á¥Êé",{local:["SimLi","SimLi Regular"],style:ji,ultimate:"serif"}],["ÐÂËÎ",{alias:"ËÎÌå"}]]),Si=new Map([["Arial-Black","ArialBlack"]]);function getFamilyName(e){const t=new Set(["thin","extralight","ultralight","demilight","semilight","light","book","regular","normal","medium","demibold","semibold","bold","extrabold","ultrabold","black","heavy","extrablack","ultrablack","roman","italic","oblique","ultracondensed","extracondensed","condensed","semicondensed","normal","semiexpanded","expanded","extraexpanded","ultraexpanded","bolditalic"]);return e.split(/[- ,+]+/g).filter(e=>!t.has(e.toLowerCase())).join(" ")}function generateFont({alias:e,local:t,path:a,fallback:n,style:s,ultimate:r},i,o,l=!0,f=!0,c=""){const h={style:null,ultimate:null};if(t){const e=c?` ${c}`:"";for(const a of t)i.push(`local(${a}${e})`)}if(e){const t=vi.get(e),r=c||function getStyleToAppend(e){switch(e){case ki:return"Bold";case yi:return"Italic";case qi:return"Bold Italic";default:if("bold"===e?.weight)return"Bold";if("italic"===e?.style)return"Italic"}return""}(s);Object.assign(h,generateFont(t,i,o,l&&!n,f&&!a,r))}s&&(h.style=s);r&&(h.ultimate=r);if(l&&n){const e=vi.get(n),{ultimate:t}=generateFont(e,i,o,l,f&&!a,c);h.ultimate||=t}f&&a&&o&&i.push(`url(${o}${a})`);return h}function getFontSubstitution(e,t,a,n,s,r){if(n.startsWith("InvalidPDFjsFont_"))return null;"TrueType"!==r&&"Type1"!==r||!/^[A-Z]{6}\+/.test(n)||(n=n.slice(7));const i=n=normalizeFontName(n);let o=e.get(i);if(o)return o;let l=vi.get(n);if(!l)for(const[e,t]of Si)if(n.startsWith(e)){n=`${t}${n.substring(e.length)}`;l=vi.get(n);break}let f=!1;if(!l){l=vi.get(s);f=!0}const c=`${t.getDocId()}_s${t.createFontId()}`;if(!l){if(!validateFontName(n)){warn(`Cannot substitute the font because of its name: ${n}`);e.set(i,null);return null}const t=/bold/gi.test(n),a=/oblique|italic/gi.test(n),s=t&&a&&qi||t&&ki||a&&yi||ji;o={css:`"${getFamilyName(n)}",${c}`,guessFallback:!0,loadedName:c,baseFontName:n,src:`local(${n})`,style:s};e.set(i,o);return o}const h=[];f&&validateFontName(n)&&h.push(`local(${n})`);const{style:u,ultimate:m}=generateFont(l,h,a),p=null===m,d=p?"":`,${m}`;o={css:`"${getFamilyName(n)}",${c}${d}`,guessFallback:p,loadedName:c,baseFontName:n,src:h.join(","),style:u};e.set(i,o);return o}const Ai=3285377520,xi=4294901760,Ci=65535;class MurmurHash3_64{constructor(e){this.h1=e?4294967295&e:Ai;this.h2=e?4294967295&e:Ai}update(e){let t,a;if("string"==typeof e){t=new Uint8Array(2*e.length);a=0;for(let n=0,s=e.length;n>>8;t[a++]=255&s}}}else{if(!ArrayBuffer.isView(e))throw new Error("Invalid data format, must be a string or TypedArray.");t=e.slice();a=t.byteLength}const n=a>>2,s=a-4*n,r=new Uint32Array(t.buffer,0,n);let i=0,o=0,l=this.h1,f=this.h2;const c=3432918353,h=461845907,u=11601,m=13715;for(let e=0;e>>17;i=i*h&xi|i*m&Ci;l^=i;l=l<<13|l>>>19;l=5*l+3864292196}else{o=r[e];o=o*c&xi|o*u&Ci;o=o<<15|o>>>17;o=o*h&xi|o*m&Ci;f^=o;f=f<<13|f>>>19;f=5*f+3864292196}i=0;switch(s){case 3:i^=t[4*n+2]<<16;case 2:i^=t[4*n+1]<<8;case 1:i^=t[4*n];i=i*c&xi|i*u&Ci;i=i<<15|i>>>17;i=i*h&xi|i*m&Ci;1&n?l^=i:f^=i}this.h1=l;this.h2=f}hexdigest(){let e=this.h1,t=this.h2;e^=t>>>1;e=3981806797*e&xi|36045*e&Ci;t=4283543511*t&xi|(2950163797*(t<<16|e>>>16)&xi)>>>16;e^=t>>>1;e=444984403*e&xi|60499*e&Ci;t=3301882366*t&xi|(3120437893*(t<<16|e>>>16)&xi)>>>16;e^=t>>>1;return(e>>>0).toString(16).padStart(8,"0")+(t>>>0).toString(16).padStart(8,"0")}}class PDFImage{constructor({xref:e,res:t,image:a,isInline:n=!1,smask:s=null,mask:r=null,isMask:i=!1,pdfFunctionFactory:o,globalColorSpaceCache:l,localColorSpaceCache:f}){this.image=a;const c=a.dict,h=c.get("F","Filter");let u;if(h instanceof Name)u=h.name;else if(Array.isArray(h)){const t=e.fetchIfRef(h[0]);t instanceof Name&&(u=t.name)}switch(u){case"JPXDecode":({width:a.width,height:a.height,componentsCount:a.numComps,bitsPerComponent:a.bitsPerComponent}=JpxImage.parseImageProperties(a.stream));a.stream.reset();const e=ImageResizer.getReducePowerForJPX(a.width,a.height,a.numComps);this.jpxDecoderOptions={numComponents:0,isIndexedColormap:!1,smaskInData:c.has("SMaskInData"),reducePower:e};if(e){const t=2**e;a.width=Math.ceil(a.width/t);a.height=Math.ceil(a.height/t)}break;case"JBIG2Decode":a.bitsPerComponent=1;a.numComps=1}let m=c.get("W","Width"),p=c.get("H","Height");if(Number.isInteger(a.width)&&a.width>0&&Number.isInteger(a.height)&&a.height>0&&(a.width!==m||a.height!==p)){warn("PDFImage - using the Width/Height of the image data, rather than the image dictionary.");m=a.width;p=a.height}else{const e="number"==typeof m&&m>0,t="number"==typeof p&&p>0;if(!e||!t){if(!a.fallbackDims)throw new FormatError(`Invalid image width: ${m} or height: ${p}`);warn("PDFImage - using the Width/Height of the parent image, for SMask/Mask data.");e||(m=a.fallbackDims.width);t||(p=a.fallbackDims.height)}}this.width=m;this.height=p;this.interpolate=c.get("I","Interpolate");this.imageMask=c.get("IM","ImageMask")||!1;this.matte=c.get("Matte")||!1;let d=a.bitsPerComponent;if(!d){d=c.get("BPC","BitsPerComponent");if(!d){if(!this.imageMask)throw new FormatError(`Bits per component missing in image: ${this.imageMask}`);d=1}}this.bpc=d;if(this.imageMask)this.numComps=1;else{let s=c.getRaw("CS")||c.getRaw("ColorSpace");const r=!!s;if(r)this.jpxDecoderOptions?.smaskInData&&(s=Name.get("DeviceRGBA"));else if(this.jpxDecoderOptions)s=Name.get("DeviceRGBA");else switch(a.numComps){case 1:s=Name.get("DeviceGray");break;case 3:s=Name.get("DeviceRGB");break;case 4:s=Name.get("DeviceCMYK");break;default:throw new Error(`Images with ${a.numComps} color components not supported.`)}this.colorSpace=ColorSpaceUtils.parse({cs:s,xref:e,resources:n?t:null,pdfFunctionFactory:o,globalColorSpaceCache:l,localColorSpaceCache:f});this.numComps=this.colorSpace.numComps;if(this.jpxDecoderOptions){this.jpxDecoderOptions.numComponents=r?this.numComps:0;this.jpxDecoderOptions.isIndexedColormap="Indexed"===this.colorSpace.name}}this.decode=c.getArray("D","Decode");this.needsDecode=!1;if(this.decode&&(this.colorSpace&&!this.colorSpace.isDefaultDecode(this.decode,d)||i&&!ColorSpace.isDefaultDecode(this.decode,1))){this.needsDecode=!0;const e=(1<0,l=(n+7>>3)*s,f=await e.getImageData(l),c=1===n&&1===s&&o===(0===f.length||!!(128&f[0]));if(c)return{isSingleOpaquePixel:c};if(t){if(ImageResizer.needsToBeResized(n,s)){const e=new Uint8ClampedArray(n*s*4);convertBlackAndWhiteToRGBA({src:f,dest:e,width:n,height:s,nonBlackColor:0,inverseDecode:o});return ImageResizer.createImage({kind:O,data:e,width:n,height:s,interpolate:r})}const e=new OffscreenCanvas(n,s),t=e.getContext("2d"),a=t.createImageData(n,s);convertBlackAndWhiteToRGBA({src:f,dest:a.data,width:n,height:s,nonBlackColor:0,inverseDecode:o});t.putImageData(a,0,0);return{data:null,width:n,height:s,interpolate:r,bitmap:e.transferToImageBitmap()}}const h=f.byteLength;let u;if(e instanceof DecodeStream&&(!o||l===h))u=f;else if(o){u=new Uint8Array(l);u.set(f);u.fill(255,h)}else u=new Uint8Array(f);if(o)for(let e=0;e>7&1;i[u+1]=h>>6&1;i[u+2]=h>>5&1;i[u+3]=h>>4&1;i[u+4]=h>>3&1;i[u+5]=h>>2&1;i[u+6]=h>>1&1;i[u+7]=1&h;u+=8}if(u>=1}}}}else{let a=0;h=0;for(u=0,c=r;u>n;s<0?s=0:s>f&&(s=f);i[u]=s;h&=(1<this.smask.fillGrayBuffer(e,{...n,destWidth:t,destHeight:a});else if(this.mask)if(this.mask instanceof PDFImage)r=(e,n)=>this.mask.fillGrayBuffer(e,{...n,invertOutput:!0,destWidth:t,destHeight:a});else{if(!Array.isArray(this.mask))throw new FormatError("Unknown mask format.");r=(e,{maxRows:a,offset:n,stride:r})=>{for(let i=0,o=t*a;ithis.mask[r+1]){t=255;break}}e[i*r+n]=t}}}else r=(e,{maxRows:a,offset:n,stride:s})=>{for(let r=0,i=t*a;r>3,c=t&&ImageResizer.needsToBeResized(a,n);if(!this.smask&&!this.mask&&"DeviceRGBA"===this.colorSpace.name){s.kind=O;const e=s.data=await this.getImageBytes(o*i*4,{internal:t&&c});return t?c?ImageResizer.createImage(s,!1):this.createBitmap(O,a,n,e):s}if(!e){let e;"DeviceGray"===this.colorSpace.name&&1===l?e=F:"DeviceRGB"!==this.colorSpace.name||8!==l||this.needsDecode||(e=T);if(e&&!this.smask&&!this.mask&&a===i&&n===o){const r=await this.#Me(i,o);if(r)return r;const l=await this.getImageBytes(o*f,{internal:t&&c});if(t)return c?ImageResizer.createImage({data:l,kind:e,width:a,height:n,interpolate:this.interpolate},this.needsDecode):this.createBitmap(e,i,o,l);s.kind=e;s.data=l;if(this.needsDecode){assert(e===F,"PDFImage.createImageData: The image must be grayscale.");const t=s.data;for(let e=0,a=t.length;e>3,u=await this.getImageBytes(f*h,{internal:!0}),m=this.getComponents(u),p=t??l,d=a??f,g=p!==l||d!==f,b=void 0===s?d:Math.min(d,s);let w=l,j=0,k=null;if(g){w=p;j=f/d;const e=l/p;k=new Uint32Array(p);for(let t=0;t0&&n[0].count++}class TimeSlotManager{static TIME_SLOT_DURATION_MS=20;static CHECK_TIME_EVERY=100;constructor(){this.reset()}check(){if(++this.checked!isFinite(e))&&(m=null);l.has("OC")&&(h=await this.parseMarkedContentProps(l.get("OC"),e));void 0!==h&&n.addOp(xt,["OC",h]);const p=l.get("Group");let d;const g=[f&&new Float32Array(f),!p&&m||null],b=l.get("Resources");if(p){u={matrix:f,bbox:m,smask:a,isolated:!1,knockout:!1,needsIsolation:!1};let t=null;if(isName(p.get("S"),"Transparency")){u.isolated=p.get("I")||!1;u.knockout=p.get("K")||!1;if(p.has("CS")){const a=this._getColorSpace(p.getRaw("CS"),e,i);t=a instanceof ColorSpace?a:await this._handleColorSpace(a)}}if(a?.backdrop){t||=ColorSpaceUtils.rgb;a.backdrop=t.getRgbHex(a.backdrop,0)}d=new CheckedOperatorList}else{d=n;n.addOp(Tt,g)}await this.getOperatorList({stream:t,task:s,resources:b instanceof Dict?b:e,operatorList:d,initialState:r,prevRefs:o});if(p){u.needsIsolation=d.needsIsolation||!!a;n.addOp(Rt,[u]);n.addOp(Tt,g);n.addOpList(d);n.addOp(Ot,[]);n.addOp(Ht,[u])}else n.addOp(Ot,[]);void 0!==h&&n.addOp(Ct,[])}_sendImgData(e,t,a=!1){const n=t?[t.bitmap||t.data.buffer]:null;return this.parsingType3Font||a?this.handler.send("commonobj",[e,"Image",t],n):this.handler.send("obj",[e,this.pageIndex,"Image",t],n)}async buildPaintImageXObject({resources:e,image:t,isInline:a=!1,operatorList:n,cacheKey:s,localImageCache:r,localColorSpaceCache:i}){const{maxImageSize:o,ignoreErrors:l,isOffscreenCanvasSupported:f}=this.options,{dict:c}=t,h=c.objId,u=c.get("W","Width"),m=c.get("H","Height");if(!u||"number"!=typeof u||!m||"number"!=typeof m){warn("Image dimensions are missing, or not numbers.");return}if(-1!==o&&u*m>o){const e="Image exceeded maximum allowed size and was removed.";if(!l)throw new Error(e);warn(e);return}let p;c.has("OC")&&(p=await this.parseMarkedContentProps(c.get("OC"),e));let d,g,b;if(c.get("IM","ImageMask")||!1){d=await PDFImage.createMask({image:t,isOffscreenCanvasSupported:f&&!this.parsingType3Font});if(d.isSingleOpaquePixel){g=Ut;b=[];n.addImageOps(g,b,p);if(s){const e={fn:g,args:b,optionalContent:p};r.set(s,h,e);h&&this._regionalImageCache.set(null,h,e)}return}if(this.parsingType3Font){b=function compileType3Glyph({data:e,width:t,height:a}){if(t>1e3||a>1e3)return null;const n=new Uint8Array([0,2,4,0,1,0,5,4,8,10,0,8,0,2,1,0]),s=t+1,r=new Uint8Array(s*(a+1));let i,o,l;const f=t+7&-8,c=new Uint8Array(f*a);let h=0;for(const t of e){let e=128;for(;e>0;){c[h++]=t&e?0:255;e>>=1}}let u=0;h=0;if(0!==c[h]){r[0]=1;++u}for(o=1;o>2)+(c[h+1]?4:0)+(c[h-f+1]?8:0);if(n[e]){r[l+o]=n[e];++u}h++}if(c[h-f]!==c[h]){r[l+o]=c[h]?2:4;++u}if(u>1e3)return null}h=f*(a-1);l=i*s;if(0!==c[h]){r[l]=8;++u}for(o=1;o1e3)return null;const m=new Int32Array([0,s,-1,0,-s,0,0,0,1]),p=[],{a:d,b:g,c:b,d:w,e:j,f:k}=(new DOMMatrix).scaleSelf(1/t,-1/a).translateSelf(0,-a);for(i=0;u&&i<=a;i++){let e=i*s;const a=e+t;for(;e>4;r[e]&=f>>2|f<<2}n=e%s;o=e/s|0;p.push($t,d*n+b*o+j,g*n+w*o+k);r[e]||--u}while(l!==e);--i}return[Gt,[new Float32Array(p)],new Float32Array([0,0,t,a])]}(d);if(b){n.addImageOps(Xt,b,p);return}warn("Cannot compile Type3 glyph.");n.addImageOps(Mt,[d],p);return}const e=`mask_${this.idFactory.createObjId()}`;n.addDependency(e);d.dataLen=d.bitmap?d.width*d.height*4:d.data.length;this._sendImgData(e,d);g=Mt;b=[{data:e,width:d.width,height:d.height,interpolate:d.interpolate,count:1}];n.addImageOps(g,b,p);if(s){const t={objId:e,fn:g,args:b,optionalContent:p};r.set(s,h,t);h&&this._regionalImageCache.set(null,h,t)}return}const w=c.has("SMask")||c.has("Mask");if(a&&u+m<200&&!w){try{const s=new PDFImage({xref:this.xref,res:e,image:t,isInline:a,pdfFunctionFactory:this._pdfFunctionFactory,globalColorSpaceCache:this.globalColorSpaceCache,localColorSpaceCache:i});d=await s.createImageData(!0,!1);n.addImageOps(Et,[d],p)}catch(e){const t=`Unable to decode inline image: "${e}".`;if(!l)throw new Error(t);warn(t)}return}let j=`img_${this.idFactory.createObjId()}`,k=!1,y=null;if(this.parsingType3Font)j=`${this.idFactory.getDocId()}_type3_${j}`;else if(s&&h){k=this.globalImageCache.shouldCache(h,this.pageIndex);if(k){assert(!a,"Cannot cache an inline image globally.");j=`${this.idFactory.getDocId()}_${j}`}}n.addDependency(j);g=Pt;b=[j,u,m];n.addImageOps(g,b,p,w);if(k){y={objId:j,fn:g,args:b,optionalContent:p,hasMask:w,byteSize:0};if(this.globalImageCache.hasDecodeFailed(h)){this.globalImageCache.setData(h,y);this._sendImgData(j,null,k);return}if(u*m>25e4||w){const e=await this.handler.sendWithPromise("commonobj",[j,"CopyLocalImage",{imageRef:h}]);if(e){this.globalImageCache.setData(h,y);this.globalImageCache.addByteSize(h,e);return}}}PDFImage.buildImage({xref:this.xref,res:e,image:t,isInline:a,pdfFunctionFactory:this._pdfFunctionFactory,globalColorSpaceCache:this.globalColorSpaceCache,localColorSpaceCache:i}).then(async e=>{d=await e.createImageData(!1,f);d.dataLen=d.bitmap?d.width*d.height*4:d.data.length;d.ref=h;k&&this.globalImageCache.addByteSize(h,d.dataLen);return this._sendImgData(j,d,k)}).catch(e=>{warn(`Unable to decode image "${j}": "${e}".`);h&&this.globalImageCache.addDecodeFailed(h);return this._sendImgData(j,null,k)});if(s){const e={objId:j,fn:g,args:b,optionalContent:p,hasMask:w};r.set(s,h,e);if(h){this._regionalImageCache.set(null,h,e);if(k){assert(y,"The global cache-data must be available.");this.globalImageCache.setData(h,y)}}}}handleSMask(e,t,a,n,s,r,i){const o=e.get("G"),l={subtype:e.get("S").name,backdrop:e.get("BC")},f=e.get("TR");if(isPDFFunction(f)){const e=this._pdfFunctionFactory.create(f),t=new Uint8Array(256),a=new Float32Array(1);for(let n=0;n<256;n++){a[0]=n/255;e(a,0,a,0);t[n]=255*a[0]|0}l.transferMap=t}return this.buildFormXObject(t,o,l,a,n,s.state.clone({newPath:!0}),r,i)}handleTransferFunction(e){let t;if(Array.isArray(e)){t=e;e.length>1&&e.every(t=>t===e[0])&&(t=[e[0]])}else{if(!isPDFFunction(e))return null;t=[e]}const a=[];let n=0,s=0;for(const e of t){const t=this.xref.fetchIfRef(e);n++;if(isName(t,"Identity")){a.push(null);continue}if(!isPDFFunction(t))return null;const r=this._pdfFunctionFactory.create(t),i=new Uint8Array(256),o=new Float32Array(1);for(let e=0;e<256;e++){o[0]=e/255;r(o,0,o,0);i[e]=255*o[0]|0}a.push(i);s++}return 1!==n&&4!==n||0===s?null:a}handleTilingType(e,t,a,n,s,r,i,o){const l=new CheckedOperatorList,f=Dict.merge({xref:this.xref,dictArray:[s.get("Resources"),a]});return this.getOperatorList({stream:n,task:i,resources:f,operatorList:l}).then(function(){const a=l.getIR(),{needsIsolation:n}=l,i=getTilingPatternIR(a,s,t,n);r.addDependencies(l.dependencies);r.addOp(e,i);s.objId&&o.set(null,s.objId,{operatorListIR:a,needsIsolation:n,dict:s})}).catch(e=>{if(!(e instanceof AbortException)){if(!this.options.ignoreErrors)throw e;warn(`handleTilingType - ignoring pattern: "${e}".`)}})}async handleSetFont(e,t,a,n,s,r,i=null,o=null){const l=t?.[0]instanceof Name?t[0].name:null,f=await this.loadFont(l,a,e,s,i,o);f.font.isType3Font&&n.addDependencies(f.type3Dependencies);r.font=f.font;f.send(this.handler);return f.loadedName}handleText(e,t){const a=t.font,n=a.charsToGlyphs(e);if(a.data){(!!(t.textRenderingMode&C)||"Pattern"===t.fillColorSpace.name||"Pattern"===t.strokeColorSpace.name||a.disableFontFace)&&PartialEvaluator.buildFontPaths(a,n,this.handler,this.options)}return n}ensureStateFont(e){if(e.font)return;const t=new FormatError("Missing setFont (Tf) operator before text rendering operator.");if(!this.options.ignoreErrors)throw t;warn(`ensureStateFont: "${t}".`)}async setGState({resources:e,gState:t,operatorList:a,cacheKey:n,task:s,stateManager:r,localGStateCache:i,localColorSpaceCache:o,seenRefs:l}){const f=t.objId;let c=!0;const h=[];let u=Promise.resolve();for(const[n,i]of t)switch(n){case"Type":break;case"LW":if("number"!=typeof i){warn(`Invalid LW (line width): ${i}`);break}h.push([n,Math.abs(i)]);break;case"LC":case"LJ":case"ML":case"D":case"RI":case"FL":case"CA":case"ca":h.push([n,i]);break;case"Font":c=!1;u=u.then(()=>this.handleSetFont(e,null,i[0],a,s,r.state).then(function(e){a.addDependency(e);h.push([n,[e,i[1]]])}));break;case"BM":h.push([n,normalizeBlendMode(i)]);break;case"SMask":if(isName(i,"None")){h.push([n,!1]);break}if(i instanceof Dict){c=!1;u=u.then(()=>this.handleSMask(i,e,a,s,r,o,l));h.push([n,!0])}else warn("Unsupported SMask type");break;case"TR":const t=this.handleTransferFunction(i);h.push([n,t]);break;case"OP":case"op":case"OPM":case"BG":case"BG2":case"UCR":case"UCR2":case"TR2":case"HT":case"SM":case"SA":case"AIS":case"TK":info("graphic state operator "+n);break;default:info("Unknown graphic state operator "+n)}await u;h.length>0&&a.addOp(je,[h]);c&&i.set(n,f,h)}loadFont(e,t,a,n,s=null,r=null){const errorFont=async()=>new TranslatedFont({loadedName:"g_font_error",font:new ErrorFont(`Font "${e}" is not available.`),dict:t});let i;if(t)t instanceof Ref&&(i=t);else{const t=a.get("Font");t&&(i=t.getRaw(e))}if(i){if(this.type3FontRefs?.has(i))return errorFont();if(this.fontCache.has(i))return this.fontCache.get(i);try{t=this.xref.fetchIfRef(i)}catch(e){warn(`loadFont - lookup failed: "${e}".`)}}if(!(t instanceof Dict)){if(!this.options.ignoreErrors&&!this.parsingType3Font){warn(`Font "${e}" is not available.`);return errorFont()}warn(`Font "${e}" is not available -- attempting to fallback to a default font.`);t=s||PartialEvaluator.fallbackFontDict}if(t.cacheKey&&this.fontCache.has(t.cacheKey))return this.fontCache.get(t.cacheKey);const{promise:o,resolve:l}=Promise.withResolvers();let f;try{f=this.preEvaluateFont(t);f.cssFontInfo=r}catch(e){warn(`loadFont - preEvaluateFont failed: "${e}".`);return errorFont()}const{descriptor:c,hash:h}=f,u=i instanceof Ref;let m;if(h&&c instanceof Dict){const e=c.fontAliases||=Object.create(null);if(e[h]){const t=e[h].aliasRef;if(u&&t&&this.fontCache.has(t)){this.fontCache.putAlias(i,t);return this.fontCache.get(i)}}else e[h]={fontID:this.idFactory.createFontId()};u&&(e[h].aliasRef=i);m=e[h].fontID}else m=this.idFactory.createFontId();assert(m?.startsWith("f"),'The "fontID" must be (correctly) defined.');if(u)this.fontCache.put(i,o);else{t.cacheKey=`cacheKey_${m}`;this.fontCache.put(t.cacheKey,o)}t.loadedName=`${this.idFactory.getDocId()}_${m}`;this.translateFont(f).then(async e=>{const s=new TranslatedFont({loadedName:t.loadedName,font:e,dict:t});if(e.isType3Font)try{await s.loadType3Data(this,a,n)}catch(e){throw new Error(`Type3 font load error: ${e}`)}l(s)}).catch(e=>{warn(`loadFont - translateFont failed: "${e}".`);l(new TranslatedFont({loadedName:t.loadedName,font:new ErrorFont(e?.message),dict:t}))});return o}buildPath(e,t,a){const{pathMinMax:n,pathBuffer:s}=a;switch(0|e){case Fe:{const e=a.currentPointX=t[0],r=a.currentPointY=t[1],i=t[2],o=t[3],l=e+i,f=r+o;0===i||0===o?s.push(Vt,e,r,$t,l,f,Qt):s.push(Vt,e,r,$t,l,r,$t,l,f,$t,e,f,Qt);Util.rectBoundingBox(e,r,l,f,n);break}case ve:{const e=a.currentPointX=t[0],r=a.currentPointY=t[1];s.push(Vt,e,r);Util.pointBoundingBox(e,r,n);break}case Se:{const e=a.currentPointX=t[0],r=a.currentPointY=t[1];s.push($t,e,r);Util.pointBoundingBox(e,r,n);break}case Ae:{const e=a.currentPointX,r=a.currentPointY,[i,o,l,f,c,h]=t;a.currentPointX=c;a.currentPointY=h;s.push(Yt,i,o,l,f,c,h);Util.bezierBoundingBox(e,r,i,o,l,f,c,h,n);break}case xe:{const e=a.currentPointX,r=a.currentPointY,[i,o,l,f]=t;a.currentPointX=l;a.currentPointY=f;s.push(Yt,e,r,i,o,l,f);Util.bezierBoundingBox(e,r,e,r,i,o,l,f,n);break}case Ce:{const e=a.currentPointX,r=a.currentPointY,[i,o,l,f]=t;a.currentPointX=l;a.currentPointY=f;s.push(Yt,i,o,l,f,l,f);Util.bezierBoundingBox(e,r,i,o,l,f,l,f,n);break}case Ie:s.push(Qt)}}_getColorSpace(e,t,a){return ColorSpaceUtils.parse({cs:e,xref:this.xref,resources:t,pdfFunctionFactory:this._pdfFunctionFactory,globalColorSpaceCache:this.globalColorSpaceCache,localColorSpaceCache:a,asyncIfNotCached:!0})}async _handleColorSpace(e){try{return await e}catch(e){if(e instanceof AbortException)return null;if(this.options.ignoreErrors){warn(`_handleColorSpace - ignoring ColorSpace: "${e}".`);return null}throw e}}parseShading({shading:e,resources:t,localColorSpaceCache:a,localShadingPatternCache:n}){let s,r=n.get(e);if(r)return r;try{s=Pattern.parseShading(e,this.xref,t,this._pdfFunctionFactory,this.globalColorSpaceCache,a).getIR()}catch(t){if(t instanceof AbortException)return null;if(this.options.ignoreErrors){warn(`parseShading - ignoring shading: "${t}".`);n.set(e,null);return null}throw t}r=`pattern_${this.idFactory.createObjId()}`;this.parsingType3Font&&(r=`${this.idFactory.getDocId()}_type3_${r}`);n.set(e,r);if(this.parsingType3Font){const e=function compilePatternInfo(e){let t,a=null,n=[],s=[],r=[],i=null,o=null;switch(e[0]){case"RadialAxial":t="axial"===e[1]?1:2;a=e[2];r=e[3];1===t?n.push(...e[4],...e[5]):n.push(e[4][0],e[4][1],e[6],e[5][0],e[5][1],e[7]);break;case"Mesh":t=3;i=e[1];n=e[2];s=e[3];a=e[6];o=e[7];break;default:throw new Error(`Unsupported pattern type: ${e[0]}`)}const l=Math.floor(n.length/2),f=Math.floor(s.length/4),c=r.length,h=new ArrayBuffer(20+8*l+4*f+8*c+(a?16:0)+(o?3:0)),u=new DataView(h),m=new Uint8Array(h);u.setUint8(PATTERN_INFO.KIND,t);u.setUint8(PATTERN_INFO.HAS_BBOX,a?1:0);u.setUint8(PATTERN_INFO.HAS_BACKGROUND,o?1:0);u.setUint8(PATTERN_INFO.SHADING_TYPE,i);u.setUint32(PATTERN_INFO.N_COORD,l,!0);u.setUint32(PATTERN_INFO.N_COLOR,f,!0);u.setUint32(PATTERN_INFO.N_STOP,c,!0);u.setUint32(PATTERN_INFO.N_FIGURES,0,!0);let p=20;new Float32Array(h,p,2*l).set(n);p+=8*l;m.set(s,p);p+=4*f;for(const[e,t]of r){u.setFloat32(p,e,!0);p+=4;u.setUint32(p,parseInt(t.slice(1),16),!0);p+=4}if(a)for(const e of a){u.setFloat32(p,e,!0);p+=4}o&&m.set(o,p);return h}(s);this.handler.send("commonobj",[r,"Pattern",e],[e])}else this.handler.send("obj",[r,this.pageIndex,"Pattern",s]);return r}handleColorN(e,t,a,n,s,r,i,o,l,f){const c=a.pop();if(c instanceof Name){const h=s.getRaw(c.name),u=h instanceof Ref&&l.getByRef(h);if(u)try{const s=n.base?n.base.getRgbHex(a,0):null,r=getTilingPatternIR(u.operatorListIR,u.dict,s,u.needsIsolation);e.addOp(t,r);return}catch{}const m=this.xref.fetchIfRef(h);if(m){const s=m instanceof BaseStream?m.dict:m,c=s.get("PatternType");if(c===Fi){const o=n.base?n.base.getRgbHex(a,0):null;return this.handleTilingType(t,o,r,m,s,e,i,l)}if(c===Ti){const a=s.get("Shading"),n=this.parseShading({shading:a,resources:r,localColorSpaceCache:o,localShadingPatternCache:f});if(n){const a=lookupMatrix(s.getArray("Matrix"),null);e.addOp(t,["Shading",n,a])}return}throw new FormatError(`Unknown PatternType: ${c}`)}}throw new FormatError(`Unknown PatternName: ${c}`)}_parseVisibilityExpression(e,t,a){if(++t>10){warn("Visibility expression is too deeply nested");return}const n=e.length,s=this.xref.fetchIfRef(e[0]);if(!(n<2)&&s instanceof Name){switch(s.name){case"And":case"Or":case"Not":a.push(s.name);break;default:warn(`Invalid operator ${s.name} in visibility expression`);return}for(let s=1;s0)return{type:"OCMD",expression:t}}const t=a.get("OCGs");if(Array.isArray(t)||t instanceof Dict){const e=[];if(Array.isArray(t))for(const a of t)e.push(a.toString());else e.push(t.objId);return{type:n,ids:e,policy:a.get("P")instanceof Name?a.get("P").name:null,expression:null}}if(t instanceof Ref)return{type:n,id:t.toString()}}return null}async getOperatorList({stream:e,task:a,resources:n,operatorList:s,initialState:r=null,fallbackFontDict:i=null,prevRefs:o=null}){if(e.isAsync){const t=await e.asyncGetBytes();t&&(e=new Stream(t,0,t.length,e.dict))}const l=e.dict?.objId,f=new RefSet(o);if(l){if(o?.has(l))throw new Error(`getOperatorList - ignoring circular reference: ${l}`);f.put(l)}n||=Dict.empty;r||=new EvalState;if(!s)throw new Error('getOperatorList: missing "operatorList" parameter');const c=this,h=this.xref,u=new LocalImageCache,m=new LocalColorSpaceCache,p=new LocalGStateCache,d=new LocalTilingPatternCache,g=new Map,b=n.get("XObject")||Dict.empty,w=n.get("Pattern")||Dict.empty,j=new StateManager(r),k=new EvaluatorPreprocessor(e,h,j),y=new TimeSlotManager;function closePendingRestoreOPS(e){for(let e=0,t=k.savedStatesDepth;e{j.state.fillColorSpace=e||ColorSpaceUtils.gray}));return}case it:{const t=c._getColorSpace(e[0],n,m);if(t instanceof ColorSpace){j.state.strokeColorSpace=t;continue}next(c._handleColorSpace(t).then(e=>{j.state.strokeColorSpace=e||ColorSpaceUtils.gray}));return}case ct:S=j.state.fillColorSpace;e=[S.getRgbHex(e,0)];r=dt;break;case lt:S=j.state.strokeColorSpace;e=[S.getRgbHex(e,0)];r=pt;break;case mt:j.state.fillColorSpace=ColorSpaceUtils.gray;e=[ColorSpaceUtils.gray.getRgbHex(e,0)];r=dt;break;case ut:j.state.strokeColorSpace=ColorSpaceUtils.gray;e=[ColorSpaceUtils.gray.getRgbHex(e,0)];r=pt;break;case bt:j.state.fillColorSpace=ColorSpaceUtils.cmyk;e=[ColorSpaceUtils.cmyk.getRgbHex(e,0)];r=dt;break;case gt:j.state.strokeColorSpace=ColorSpaceUtils.cmyk;e=[ColorSpaceUtils.cmyk.getRgbHex(e,0)];r=pt;break;case dt:j.state.fillColorSpace=ColorSpaceUtils.rgb;e=[ColorSpaceUtils.rgb.getRgbHex(e,0)];break;case pt:j.state.strokeColorSpace=ColorSpaceUtils.rgb;e=[ColorSpaceUtils.rgb.getRgbHex(e,0)];break;case ht:S=j.state.patternFillColorSpace;if(!S){if(isNumberArray(e,null)){e=[ColorSpaceUtils.gray.getRgbHex(e,0)];r=dt;break}e=[];r=Kt;break}if("Pattern"===S.name){next(c.handleColorN(s,ht,e,S,w,n,a,m,d,g));return}e=[S.getRgbHex(e,0)];r=dt;break;case ft:S=j.state.patternStrokeColorSpace;if(!S){if(isNumberArray(e,null)){e=[ColorSpaceUtils.gray.getRgbHex(e,0)];r=pt;break}e=[];r=Wt;break}if("Pattern"===S.name){next(c.handleColorN(s,ft,e,S,w,n,a,m,d,g));return}e=[S.getRgbHex(e,0)];r=pt;break;case wt:let F;try{const t=n.get("Shading");if(!t)throw new FormatError("No shading resource found");F=t.get(e[0].name);if(!F)throw new FormatError("No shading object found")}catch(e){if(e instanceof AbortException)continue;if(c.options.ignoreErrors){warn(`getOperatorList - ignoring Shading: "${e}".`);continue}throw e}const T=c.parseShading({shading:F,resources:n,localColorSpaceCache:m,localShadingPatternCache:g});if(!T)continue;e=[T];r=wt;break;case je:C=e[0]instanceof Name;x=e[0].name;if(C){const t=p.getByName(x);if(t){t.length>0&&s.addOp(je,[t]);e=null;continue}}next(new Promise(function(e,t){if(!C)throw new FormatError("GState must be referred to by name.");const r=n.get("ExtGState");if(!(r instanceof Dict))throw new FormatError("ExtGState should be a dictionary.");const i=r.get(x);if(!(i instanceof Dict))throw new FormatError("GState should be a dictionary.");c.setGState({resources:n,gState:i,operatorList:s,cacheKey:x,task:a,stateManager:j,localGStateCache:p,localColorSpaceCache:m,seenRefs:f}).then(e,t)}).catch(function(e){if(!(e instanceof AbortException)){if(!c.options.ignoreErrors)throw e;warn(`getOperatorList - ignoring ExtGState: "${e}".`)}}));return;case ue:{const[t]=e;if("number"!=typeof t){warn(`Invalid setLineWidth: ${t}`);continue}e[0]=Math.abs(t);break}case ge:{const t=e[1];if("number"!=typeof t){warn(`Invalid setDash: ${t}`);continue}const a=e[0];if(!Array.isArray(a)){warn(`Invalid setDash: ${a}`);continue}a.some(e=>"number"!=typeof e)&&(e[0]=a.filter(e=>"number"==typeof e));break}case ve:case Se:case Ae:case xe:case Ce:case Ie:case Fe:c.buildPath(r,e,j.state);continue;case Te:case Oe:case Re:case He:case Be:case De:case Me:case Ne:case Pe:{const{state:{pathBuffer:e,pathMinMax:a}}=j;r!==Oe&&r!==Me&&r!==Ne||e.push(Qt);if(0===e.length)s.addOp(Xt,[r,[null],null]);else{s.addOp(Xt,[r,[new Float32Array(e)],a.slice()]);e.length=0;a.set(t,0)}continue}case Qe:s.addOp(r,[new Float32Array(e)]);continue;case vt:case St:case It:case Ft:continue;case xt:if(!(e[0]instanceof Name)){warn(`Expected name for beginMarkedContentProps arg0=${e[0]}`);s.addOp(xt,["OC",null]);continue}if("OC"===e[0].name){next(c.parseMarkedContentProps(e[1],n).then(e=>{s.addOp(xt,["OC",e])}).catch(e=>{if(!(e instanceof AbortException)){if(!c.options.ignoreErrors)throw e;warn(`getOperatorList - ignoring beginMarkedContentProps: "${e}".`);s.addOp(xt,["OC",null])}}));return}e=[e[0].name,e[1]instanceof Dict?e[1].get("MCID"):null];break;default:if(null!==e){for(q=0,v=e.length;q{if(!(e instanceof AbortException)){if(!this.options.ignoreErrors)throw e;warn(`getOperatorList - ignoring errors during "${a.name}" task: "${e}".`);closePendingRestoreOPS()}})}async getTextContent({stream:e,task:t,resources:a,stateManager:s=null,includeMarkedContent:r=!1,sink:i,seenStyles:o=new Set,viewBox:l,lang:f=null,markedContentData:c=null,disableNormalization:h=!1,keepWhiteSpace:u=!1,prevRefs:m=null,intersector:p=null}){if(e.isAsync){const t=await e.asyncGetBytes();t&&(e=new Stream(t,0,t.length,e.dict))}const d=e.dict?.objId,g=new RefSet(m);if(d){if(m?.has(d))throw new Error(`getTextContent - ignoring circular reference: ${d}`);g.put(d)}a||=Dict.empty;s||=new StateManager(new TextState);r&&(c||={level:0});const b={items:[],styles:Object.create(null),lang:f},w={initialized:!1,str:[],totalWidth:0,totalHeight:0,width:0,height:0,vertical:!1,prevTransform:null,prevTextRise:0,textAdvanceScale:0,spaceInFlowMin:0,spaceInFlowMax:0,trackingSpaceMin:1/0,negativeSpaceMax:-1/0,notASpace:-1/0,transform:null,fontName:null,hasEOL:!1},j=[" "," "];let k=0;function saveLastChar(e){const t=(k+1)%2,a=" "!==j[k]&&" "===j[t];j[k]=e;k=t;return!u&&a}function shouldAddWhitepsace(){return!u&&" "!==j[k]&&" "===j[(k+1)%2]}function resetLastChars(){j[0]=j[1]=" ";k=0}const y=this,q=this.xref,v=[];let S=null;const x=new LocalImageCache,C=new LocalGStateCache,F=new EvaluatorPreprocessor(e,q,s);let T,O;function pushWhitespace({width:e=0,height:t=0,transform:a=w.prevTransform,fontName:n=w.fontName}){p?.addExtraChar(" ");b.items.push({str:" ",dir:"ltr",width:e,height:t,transform:a,fontName:n,hasEOL:!1})}function getCurrentTextTransform(){const e=T.font,t=[T.fontSize*T.textHScale,0,0,T.fontSize,0,T.textRise];if(e.isType3Font&&(T.fontSize<=1||e.isCharBBox)&&!isArrayEqual(T.fontMatrix,n)){const a=e.bbox[3]-e.bbox[1];a>0&&(t[3]*=a*T.fontMatrix[3])}return Util.transform(T.ctm,Util.transform(T.textMatrix,t))}function ensureTextContentItem(){if(w.initialized)return w;const{font:e,loadedName:t}=T;if(!o.has(t)){o.add(t);b.styles[t]={fontFamily:e.fallbackName,ascent:e.ascent,descent:e.descent,vertical:e.vertical};if(y.options.fontExtraProperties&&e.systemFontInfo){const a=b.styles[t];a.fontSubstitution=e.systemFontInfo.css;a.fontSubstitutionLoadedName=e.systemFontInfo.loadedName}}w.fontName=t;const a=w.transform=getCurrentTextTransform();if(e.vertical){w.width=w.totalWidth=Math.hypot(a[0],a[1]);w.height=w.totalHeight=0;w.vertical=!0}else{w.width=w.totalWidth=0;w.height=w.totalHeight=Math.hypot(a[2],a[3]);w.vertical=!1}const n=Math.hypot(T.textLineMatrix[0],T.textLineMatrix[1]),s=Math.hypot(T.ctm[0],T.ctm[1]);w.textAdvanceScale=s*n;const{fontSize:r}=T;w.trackingSpaceMin=.102*r;w.notASpace=.03*r;w.negativeSpaceMax=-.2*r;w.spaceInFlowMin=.102*r;w.spaceInFlowMax=.6*r;w.hasEOL=!1;w.initialized=!0;return w}function updateAdvanceScale(){if(!w.initialized)return;const e=Math.hypot(T.textLineMatrix[0],T.textLineMatrix[1]),t=Math.hypot(T.ctm[0],T.ctm[1])*e;if(t!==w.textAdvanceScale){if(w.vertical){w.totalHeight+=w.height*w.textAdvanceScale;w.height=0}else{w.totalWidth+=w.width*w.textAdvanceScale;w.width=0}w.textAdvanceScale=t}}function runBidiTransform(e){let t=e.str.join("");h||(t=function normalizeUnicode(e){if(!sa){sa=/([\u00a0\u00b5\u037e\u0eb3\u2000-\u200a\u202f\u2126\ufb00-\ufb04\ufb06\ufb20-\ufb36\ufb38-\ufb3c\ufb3e\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufba1\ufba4-\ufba9\ufbae-\ufbb1\ufbd3-\ufbdc\ufbde-\ufbe7\ufbea-\ufbf8\ufbfc-\ufbfd\ufc00-\ufc5d\ufc64-\ufcf1\ufcf5-\ufd3d\ufd88\ufdf4\ufdfa-\ufdfb\ufe71\ufe77\ufe79\ufe7b\ufe7d]+)|(\ufb05+)/gu;ra=new Map([["ſt","ſt"]])}return e.replaceAll(sa,(e,t,a)=>t?t.normalize("NFKC"):ra.get(a))}(t));const a=bidi(t,-1,e.vertical);return{str:a.str,dir:a.dir,width:Math.abs(e.totalWidth),height:Math.abs(e.totalHeight),transform:e.transform,fontName:e.fontName,hasEOL:e.hasEOL}}async function handleSetFont(e,s){const r=await y.loadFont(e,s,a,t);T.loadedName=r.loadedName;T.font=r.font;T.fontMatrix=r.font.fontMatrix||n}function applyInverseRotation(e,t,a){const n=Math.hypot(a[0],a[1]);return[(a[0]*e+a[1]*t)/n,(a[2]*e+a[3]*t)/n]}function compareWithLastPosition(e){const t=getCurrentTextTransform();let a=t[4],n=t[5];if(T.font?.vertical){if(al[2]||n+el[3])return!1}else if(a+el[2]||nl[3])return!1;if(!T.font||!w.prevTransform)return!0;let s=w.prevTransform[4],r=w.prevTransform[5];if(s===a&&r===n)return!0;let i=-1;t[0]&&0===t[1]&&0===t[2]?i=t[0]>0?0:180:t[1]&&0===t[0]&&0===t[3]&&(i=t[1]>0?90:270);switch(i){case 0:break;case 90:[a,n]=[n,a];[s,r]=[r,s];break;case 180:[a,n,s,r]=[-a,-n,-s,-r];break;case 270:[a,n]=[-n,-a];[s,r]=[-r,-s];break;default:[a,n]=applyInverseRotation(a,n,t);[s,r]=applyInverseRotation(s,r,w.prevTransform)}if(T.font.vertical){const e=(r-n)/w.textAdvanceScale,t=a-s,i=Math.sign(w.height||w.totalHeight);if(e.5*w.width){appendEOL();return!0}resetLastChars();flushTextContentItem();return!0}if(Math.abs(t)>w.width){appendEOL();return!0}e<=i*w.notASpace&&resetLastChars();if(e<=i*w.trackingSpaceMin)if(shouldAddWhitepsace()){resetLastChars();flushTextContentItem();pushWhitespace({height:Math.abs(e)})}else w.height+=e;else if(!addFakeSpaces(e,w.prevTransform,i))if(0===w.str.length){resetLastChars();pushWhitespace({height:Math.abs(e)})}else w.height+=e;Math.abs(t)>.25*w.width&&flushTextContentItem();return!0}const o=(a-s)/w.textAdvanceScale,f=n-r,c=Math.sign(w.width||w.totalWidth);if(o.5*w.height){appendEOL();return!0}resetLastChars();flushTextContentItem();return!0}const h=T.textRise-w.prevTextRise,u=0===h?f:f-t[3]/T.fontSize*h;if(Math.abs(u)>w.height){appendEOL();return!0}o<=c*w.notASpace&&resetLastChars();if(o<=c*w.trackingSpaceMin)if(shouldAddWhitepsace()){resetLastChars();flushTextContentItem();pushWhitespace({width:Math.abs(o)})}else w.width+=o;else if(!addFakeSpaces(o,w.prevTransform,c))if(0===w.str.length){resetLastChars();pushWhitespace({width:Math.abs(o)})}else w.width+=o;Math.abs(f)>.25*w.height&&flushTextContentItem();return!0}function buildTextContentItem({chars:e,extraSpacing:t}){if(O!==T&&(O.fontSize!==T.fontSize||O.fontName!==T.fontName&&(O.font.name!==T.font.name||O.font.vertical!==T.font.vertical))){flushTextContentItem();O=T.clone()}const a=T.font,n=a.vertical?-T.charSpacing:T.charSpacing;if(!e){const e=n+t;e&&(a.vertical?T.translateTextMatrix(0,-e):T.translateTextMatrix(e*T.textHScale,0));u&&compareWithLastPosition(0);return}const s=a.charsToGlyphs(e),r=T.fontMatrix[0]*T.fontSize;for(let e=0,i=s.length;e0){const e=v.join("");v.length=0;buildTextContentItem({chars:e,extraSpacing:0})}break;case et:if(!s.state.font){y.ensureStateFont(s.state);continue}buildTextContentItem({chars:j[0],extraSpacing:0});break;case at:if(!s.state.font){y.ensureStateFont(s.state);continue}T.carriageReturn();buildTextContentItem({chars:j[0],extraSpacing:0});break;case nt:if(!s.state.font){y.ensureStateFont(s.state);continue}T.wordSpacing=j[0];T.charSpacing=j[1];T.carriageReturn();buildTextContentItem({chars:j[2],extraSpacing:0});break;case qt:flushTextContentItem();S??=a.get("XObject")||Dict.empty;w=j[0]instanceof Name;d=j[0].name;if(w&&x.getByName(d))break;next(new Promise(function(e,n){if(!w)throw new FormatError("XObject must be referred to by name.");let m=S.getRaw(d);if(m instanceof Ref){if(x.getByRef(m)){e();return}if(y.globalImageCache.getData(m,y.pageIndex)){e();return}m=q.fetch(m)}if(!(m instanceof BaseStream))throw new FormatError("XObject should be a stream");const{dict:p}=m,b=p.get("Subtype");if(!(b instanceof Name))throw new FormatError("XObject should have a Name subtype");if("Form"!==b.name){x.set(d,p.objId,!0);e();return}const j=s.state.clone(),k=new StateManager(j),v=lookupMatrix(p.getArray("Matrix"),null);v&&k.transform(v);const C=p.get("Resources");enqueueChunk();const F={enqueueInvoked:!1,enqueue(e,t){this.enqueueInvoked=!0;i.enqueue(e,t)},get desiredSize(){return i.desiredSize??0},get ready(){return i.ready}};y.getTextContent({stream:m,task:t,resources:C instanceof Dict?C:a,stateManager:k,includeMarkedContent:r,sink:i&&F,seenStyles:o,viewBox:l,lang:f,markedContentData:c,disableNormalization:h,keepWhiteSpace:u,prevRefs:g}).then(function(){F.enqueueInvoked||x.set(d,p.objId,!0);e()},n)}).catch(function(e){if(!(e instanceof AbortException)){if(!y.options.ignoreErrors)throw e;warn(`getTextContent - ignoring XObject: "${e}".`)}}));return;case je:w=j[0]instanceof Name;d=j[0].name;if(w&&C.getByName(d))break;next(new Promise(function(e,t){if(!w)throw new FormatError("GState must be referred to by name.");const n=a.get("ExtGState");if(!(n instanceof Dict))throw new FormatError("ExtGState should be a dictionary.");const s=n.get(d);if(!(s instanceof Dict))throw new FormatError("GState should be a dictionary.");const r=s.get("Font");if(r){flushTextContentItem();T.fontName=null;T.fontSize=r[1];handleSetFont(null,r[0]).then(e,t)}else{C.set(d,s.objId,!0);e()}}).catch(function(e){if(!(e instanceof AbortException)){if(!y.options.ignoreErrors)throw e;warn(`getTextContent - ignoring ExtGState: "${e}".`)}}));return;case At:flushTextContentItem();if(r){c.level++;b.items.push({type:"beginMarkedContent",tag:j[0]instanceof Name?j[0].name:null})}break;case xt:flushTextContentItem();if(r){c.level++;let e=null;j[1]instanceof Dict&&(e=j[1].get("MCID"));b.items.push({type:"beginMarkedContentProps",id:Number.isInteger(e)?`${y.idFactory.getPageObjId()}_mc${e}`:null,tag:j[0]instanceof Name?j[0].name:null})}break;case Ct:flushTextContentItem();if(r){if(0===c.level)break;c.level--;b.items.push({type:"endMarkedContent"})}}if(b.items.length>=(i?.desiredSize??1)){p=!0;break}}if(p)next(Oi);else{flushTextContentItem();enqueueChunk();e()}}).catch(e=>{if(!(e instanceof AbortException)){if(!this.options.ignoreErrors)throw e;warn(`getTextContent - ignoring errors during "${t.name}" task: "${e}".`);flushTextContentItem();enqueueChunk()}})}async extractDataStructures(e,t){const a=this.xref;let n;const s=this.readToUnicode(t.toUnicode);if(t.composite){const a=e.get("CIDSystemInfo");a instanceof Dict&&!t.cidSystemInfo&&(t.cidSystemInfo={registry:stringToPDFString(a.get("Registry")),ordering:stringToPDFString(a.get("Ordering")),supplement:a.get("Supplement")});try{const t=e.get("CIDToGIDMap");t instanceof BaseStream&&(n=t.getBytes())}catch(e){if(!this.options.ignoreErrors)throw e;warn(`extractDataStructures - ignoring CIDToGIDMap data: "${e}".`)}}const r=[];let i,o=null;if(e.has("Encoding")){i=e.get("Encoding");if(i instanceof Dict){o=i.get("BaseEncoding");o=o instanceof Name?o.name:null;if(i.has("Differences")){const e=i.get("Differences");let t=0;for(const n of e){const e=a.fetchIfRef(n);if("number"==typeof e)t=e;else{if(!(e instanceof Name))throw new FormatError(`Invalid entry in 'Differences' array: ${e}`);r[t++]=e.name}}}}else if(i instanceof Name)o=i.name;else{const e="Encoding is not a Name nor a Dict";if(!this.options.ignoreErrors)throw new FormatError(e);warn(e)}"MacRomanEncoding"!==o&&"MacExpertEncoding"!==o&&"WinAnsiEncoding"!==o&&(o=null)}const l=!t.file||t.isInternalFont,f=_n()[t.name];o&&l&&f&&(o=null);if("WinAnsiEncoding"===o&&l&&t.name?.charCodeAt(0)>=183){const e=t.name;if(["ËÎÌå","ºÚÌå","¿¬Ìå","·ÂËÎ","¿¬Ìå_GB2312","·ÂËÎ_GB2312","Á¥Êé","ÐÂËÎ","·ÂËÎÌå","С±êËÎ"].includes(e)){o=null;t.defaultEncoding="Adobe-GB1-UCS2";t.composite=!0;t.cidEncoding=Name.get("GBK-EUC-H");const e=await CMapFactory.create({encoding:t.cidEncoding,fetchBuiltInCMap:this._fetchBuiltInCMapBound,useCMap:null});t.cMap=e;t.vertical=t.cMap.vertical;t.cidSystemInfo={registry:"Adobe",ordering:"GB1",supplement:0}}}if(o)t.defaultEncoding=getEncoding(o);else{let e=!!(t.flags&qn);const a=!!(t.flags&vn);if("TrueType"===t.type&&e&&a&&0!==r.length){t.flags&=~qn;e=!1}i=fn;"TrueType"!==t.type||a||(i=cn);if(e||f){i=ln;l&&(/Symbol/i.test(t.name)?i=hn:/Dingbats/i.test(t.name)?i=un:/Wingdings/i.test(t.name)&&(i=cn))}t.defaultEncoding=i}t.differences=r;t.baseEncodingName=o;t.hasEncoding=!!o||r.length>0;t.dict=e;t.toUnicode=await s;const c=await this.buildToUnicode(t);t.toUnicode=c;n&&(t.cidToGidMap=this.readCidToGidMap(n,c));return t}_simpleFontToUnicode(e,t=!1){assert(!e.composite,"Must be a simple font.");const a=[],n=e.defaultEncoding.slice(),s=e.baseEncodingName,r=e.differences;for(const e in r){const t=r[e];".notdef"!==t&&(n[e]=t)}const i=mn();for(const r in n){let o=n[r];if(""===o)continue;let l=i[o];if(void 0!==l){a[r]=String.fromCharCode(l);continue}let f=0;switch(o[0]){case"G":3===o.length&&(f=parseInt(o.substring(1),16));break;case"g":5===o.length&&(f=parseInt(o.substring(1),16));break;case"C":case"c":if(o.length>=3&&o.length<=4){const a=o.substring(1);if(t){f=parseInt(a,16);break}f=+a;if(Number.isNaN(f)&&Number.isInteger(parseInt(a,16)))return this._simpleFontToUnicode(e,!0)}break;case"u":l=getUnicodeForGlyph(o,i);-1!==l&&(f=l);break;default:switch(o){case"f_h":case"f_t":case"T_h":a[r]=o.replaceAll("_","");continue}}if(f>0&&f<=1114111&&Number.isInteger(f)){if(s&&f===+r){const e=getEncoding(s);if(e&&(o=e[r])){a[r]=String.fromCharCode(i[o]);continue}}a[r]=String.fromCodePoint(f)}}return a}async buildToUnicode(e){e.hasIncludedToUnicodeMap=e.toUnicode?.length>0;if(e.hasIncludedToUnicodeMap){!e.composite&&e.hasEncoding&&(e.fallbackToUnicode=this._simpleFontToUnicode(e));return e.toUnicode}if(!e.composite)return new ToUnicodeMap(this._simpleFontToUnicode(e));if(e.composite&&(e.cMap.builtInCMap&&!(e.cMap instanceof IdentityCMap)||"Adobe"===e.cidSystemInfo?.registry&&("GB1"===e.cidSystemInfo.ordering||"CNS1"===e.cidSystemInfo.ordering||"Japan1"===e.cidSystemInfo.ordering||"Korea1"===e.cidSystemInfo.ordering))){const{registry:t,ordering:a}=e.cidSystemInfo,n=Name.get(`${t}-${a}-UCS2`),s=await CMapFactory.create({encoding:n,fetchBuiltInCMap:this._fetchBuiltInCMapBound,useCMap:null}),r=[],i=[];e.cMap.forEach(function(e,t){if(t>65535)throw new FormatError("Max size of CID is 65,535");const a=s.lookup(t);if(a){i.length=0;for(let e=0,t=a.length;e>1;(0!==s||t.has(r))&&(a[r]=s)}return a}extractWidths(e,t,a){const n=this.xref;let s=[],r=0;const i=[];let o;if(a.composite){const t=e.get("DW");r="number"==typeof t?Math.ceil(t):1e3;const l=e.get("W");if(Array.isArray(l))for(let e=0,t=l.length;e{const t=l.get(e),n=new OperatorList;return r.getOperatorList({stream:t,task:a,resources:f,operatorList:n}).then(()=>{switch(n.fnArray[0]){case rt:this.#Ee(n,b);break;case st:b||this.#_e(n)}c[e]=n.getIR();for(const e of n.dependencies)s.add(e)}).catch(function(t){warn(`Type3 font resource "${e}" is not available.`);const a=new OperatorList;c[e]=a.getIR()})});this.#Pe=o.then(()=>{n.charProcOperatorList=c;if(this._bbox){n.isCharBBox=!0;n.bbox=this._bbox}});return this.#Pe}#Ee(e,a=NaN){const n=Util.normalizeRect(e.argsArray[0].slice(2)),s=n[2]-n[0],r=n[3]-n[1],i=Math.hypot(s,r);if(0===s||0===r){e.fnArray.splice(0,1);e.argsArray.splice(0,1)}else if(0===a||Math.round(i/a)>=10){this._bbox??=t.slice();Util.rectBoundingBox(...n,this._bbox)}let o=0,l=e.length;for(;o=ve&&r<=Pe;if(s.variableArgs)o>i&&info(`Command ${n}: expected [0, ${i}] args, but received ${o} args.`);else{if(o!==i){const e=this.nonProcessedArgs;for(;o>i;){e.push(t.shift());o--}for(;oEvaluatorPreprocessor.MAX_INVALID_PATH_OPS)throw new FormatError(`Invalid ${e}`);warn(`Skipping ${e}`);null!==t&&(t.length=0);continue}}this.preprocessCommand(r,t);e.fn=r;e.args=t;return!0}if(a===oa)return!1;if(null!==a){null===t&&(t=[]);t.push(a);if(t.length>33)throw new FormatError("Too many arguments")}}}preprocessCommand(e,t){switch(0|e){case ke:this.stateManager.save();break;case ye:this.stateManager.restore();break;case qe:this.stateManager.transform(t)}}}class DefaultAppearanceEvaluator extends EvaluatorPreprocessor{constructor(e){super(new StringStream(e))}parse(){const e={fn:0,args:[]},t={fontSize:0,fontName:"",fontColor:new Uint8ClampedArray(3)};try{for(;;){e.args.length=0;if(!this.read(e))break;if(0!==this.savedStatesDepth)continue;const{fn:a,args:n}=e;switch(0|a){case Ge:const[e,a]=n;e instanceof Name&&(t.fontName=e.name);"number"==typeof a&&a>0&&(t.fontSize=a);break;case dt:ColorSpaceUtils.rgb.getRgbItem(n,0,t.fontColor,0);break;case mt:ColorSpaceUtils.gray.getRgbItem(n,0,t.fontColor,0);break;case bt:ColorSpaceUtils.cmyk.getRgbItem(n,0,t.fontColor,0)}}}catch(e){warn(`parseDefaultAppearance - ignoring errors: "${e}".`)}return t}}function parseDefaultAppearance(e){return new DefaultAppearanceEvaluator(e).parse()}class AppearanceStreamEvaluator extends EvaluatorPreprocessor{constructor(e,t,a){super(e);this.stream=e;this.xref=t;this.globalColorSpaceCache=a;this.resources=e.dict?.get("Resources")}parse(){const e={fn:0,args:[]};let t={scaleFactor:1,fontSize:0,fontName:"",fontColor:new Uint8ClampedArray(3),fillColorSpace:ColorSpaceUtils.gray},a=!1;const n=[];try{for(;;){e.args.length=0;if(a||!this.read(e))break;const{fn:s,args:r}=e;switch(0|s){case ke:n.push({scaleFactor:t.scaleFactor,fontSize:t.fontSize,fontName:t.fontName,fontColor:t.fontColor.slice(),fillColorSpace:t.fillColorSpace});break;case ye:t=n.pop()||t;break;case Qe:t.scaleFactor*=Math.hypot(r[0],r[1]);break;case Ge:const[e,s]=r;e instanceof Name&&(t.fontName=e.name);"number"==typeof s&&s>0&&(t.fontSize=s*t.scaleFactor);break;case ot:t.fillColorSpace=ColorSpaceUtils.parse({cs:r[0],xref:this.xref,resources:this.resources,pdfFunctionFactory:this._pdfFunctionFactory,globalColorSpaceCache:this.globalColorSpaceCache,localColorSpaceCache:this._localColorSpaceCache});break;case ct:t.fillColorSpace.getRgbItem(r,0,t.fontColor,0);break;case dt:ColorSpaceUtils.rgb.getRgbItem(r,0,t.fontColor,0);break;case mt:ColorSpaceUtils.gray.getRgbItem(r,0,t.fontColor,0);break;case bt:ColorSpaceUtils.cmyk.getRgbItem(r,0,t.fontColor,0);break;case et:case tt:case at:case nt:a=!0}}}catch(e){warn(`parseAppearanceStream - ignoring errors: "${e}".`)}this.stream.reset();delete t.scaleFactor;delete t.fillColorSpace;return t}get _localColorSpaceCache(){return shadow(this,"_localColorSpaceCache",new LocalColorSpaceCache)}get _pdfFunctionFactory(){return shadow(this,"_pdfFunctionFactory",new PDFFunctionFactory({xref:this.xref}))}}function getPdfColor(e,t){if(e[0]===e[1]&&e[1]===e[2]){return`${numberToString(e[0]/255)} ${t?"g":"G"}`}return Array.from(e,e=>numberToString(e/255)).join(" ")+" "+(t?"rg":"RG")}class FakeUnicodeFont{static#ze=1;constructor(e,t){this.xref=e;this.widths=null;this.firstChar=1/0;this.lastChar=-1/0;this.fontFamily=t;const a=new OffscreenCanvas(1,1);this.ctxMeasure=a.getContext("2d",{willReadFrequently:!0});this.fontName=Name.get(`InvalidPDFjsFont_${t}_${FakeUnicodeFont.#ze++}`)}get fontDescriptorRef(){if(!FakeUnicodeFont._fontDescriptorRef){const e=new Dict(this.xref);e.setIfName("Type","FontDescriptor");e.set("FontName",this.fontName);e.set("FontFamily","MyriadPro Regular");e.set("FontBBox",[0,0,0,0]);e.setIfName("FontStretch","Normal");e.set("FontWeight",400);e.set("ItalicAngle",0);FakeUnicodeFont._fontDescriptorRef=this.xref.getNewPersistentRef(e)}return FakeUnicodeFont._fontDescriptorRef}get descendantFontRef(){const e=new Dict(this.xref);e.set("BaseFont",this.fontName);e.setIfName("Type","Font");e.setIfName("Subtype","CIDFontType0");e.setIfName("CIDToGIDMap","Identity");e.set("FirstChar",this.firstChar);e.set("LastChar",this.lastChar);e.set("FontDescriptor",this.fontDescriptorRef);e.set("DW",1e3);const t=[],a=[...this.widths].sort();let n=null,s=null;for(const[e,r]of a)if(n)if(e===n+s.length)s.push(r);else{t.push(n,s);n=e;s=[r]}else{n=e;s=[r]}n&&t.push(n,s);e.set("W",t);const r=new Dict(this.xref);r.set("Ordering","Identity");r.set("Registry","Adobe");r.set("Supplement",0);e.set("CIDSystemInfo",r);return this.xref.getNewPersistentRef(e)}get baseFontRef(){const e=new Dict(this.xref);e.set("BaseFont",this.fontName);e.setIfName("Type","Font");e.setIfName("Subtype","Type0");e.setIfName("Encoding","Identity-H");e.set("DescendantFonts",[this.descendantFontRef]);e.setIfName("ToUnicode","Identity-H");return this.xref.getNewPersistentRef(e)}get resources(){const e=new Dict(this.xref),t=new Dict(this.xref);t.set(this.fontName.name,this.baseFontRef);e.set("Font",t);return e}_createContext(){this.widths=new Map;this.ctxMeasure.font=`1000px ${this.fontFamily}`;return this.ctxMeasure}createFontResources(e){const t=this._createContext();for(const a of e.split(/\r\n?|\n/))for(const e of a.split("")){const a=e.charCodeAt(0);if(this.widths.has(a))continue;const n=t.measureText(e),s=Math.ceil(n.width);this.widths.set(a,s);this.firstChar=Math.min(a,this.firstChar);this.lastChar=Math.max(a,this.lastChar)}return this.resources}static getFirstPositionInfo(e,t,a){const[n,i,o,l]=e;let f=o-n,c=l-i;t%180!=0&&([f,c]=[c,f]);const h=s*a;return{coords:[0,c+r*a-h],bbox:[0,0,f,c],matrix:0!==t?getRotationMatrix(t,c,h):void 0}}createAppearance(e,t,a,n,i,o){const l=this._createContext(),f=[];let c=-1/0;for(const t of e.split(/\r\n?|\n/)){f.push(t);const e=l.measureText(t).width;c=Math.max(c,e);for(const e of codePointIter(t)){const t=String.fromCodePoint(e);let a=this.widths.get(e);if(void 0===a){const n=l.measureText(t);a=Math.ceil(n.width);this.widths.set(e,a);this.firstChar=Math.min(e,this.firstChar);this.lastChar=Math.max(e,this.lastChar)}}}c*=n/1e3;const[h,u,m,p]=t;let d=m-h,g=p-u;a%180!=0&&([d,g]=[g,d]);let b=1;c>d&&(b=d/c);let w=1;const j=s*n,k=r*n,y=j*f.length;y>g&&(w=g/y);const q=n*Math.min(b,w),v=["q",`0 0 ${numberToString(d)} ${numberToString(g)} re W n`,"BT",`1 0 0 1 0 ${numberToString(g+k)} Tm 0 Tc ${getPdfColor(i,!0)}`,`/${this.fontName.name} ${numberToString(q)} Tf`],{resources:S}=this;if(1!==(o="number"==typeof o&&o>=0&&o<=1?o:1)){v.push("/R0 gs");const e=new Dict(this.xref),t=new Dict(this.xref);t.set("ca",o);t.set("CA",o);t.setIfName("Type","ExtGState");e.set("R0",t);S.set("ExtGState",e)}const x=numberToString(j);for(const e of f)v.push(`0 -${x} Td <${stringToUTF16HexString(e)}> Tj`);v.push("ET","Q");const C=v.join("\n"),F=new Dict(this.xref);F.setIfName("Subtype","Form");F.setIfName("Type","XObject");F.set("BBox",[0,0,d,g]);F.set("Length",C.length);F.set("Resources",S);if(a){const e=getRotationMatrix(a,d,g);F.set("Matrix",e)}const T=new StringStream(C);T.dict=F;return T}}const Ri=["m/d","m/d/yy","mm/dd/yy","mm/yy","d-mmm","d-mmm-yy","dd-mmm-yy","yy-mm-dd","mmm-yy","mmmm-yy","mmm d, yyyy","mmmm d, yyyy","m/d/yy h:MM tt","m/d/yy HH:MM"],Hi=["HH:MM","h:MM tt","HH:MM:ss","h:MM:ss tt"];class NameOrNumberTree{constructor(e,t,a){this.root=e;this.xref=t;this._type=a}getAll(e=!1){const t=new Map;if(!this.root)return t;const a=this.xref,n=new RefSet;this.root instanceof Ref&&n.put(this.root);const s=[this.root];for(;s.length>0;){const r=a.fetchIfRef(s.shift());if(!(r instanceof Dict))continue;if(r.has("Kids")){const e=r.get("Kids");if(!Array.isArray(e))continue;for(const t of e){if(t instanceof Ref){if(n.has(t))throw new FormatError(`Duplicate entry in "${this._type}" tree.`);n.put(t)}s.push(t)}continue}const i=r.get(this._type);if(Array.isArray(i))for(let n=0,s=i.length;n10){warn(`Search depth limit reached for "${this._type}" tree.`);return null}const s=a.get("Kids");if(!Array.isArray(s))return null;let r=0,i=s.length-1;for(;r<=i;){const n=r+i>>1,o=t.fetchIfRef(s[n]),l=o.get("Limits");if(et.fetchIfRef(l[1]))){a=o;break}r=n+1}}if(r>i)return null}const s=a.get(this._type);if(Array.isArray(s)){let a=0,n=s.length-2;for(;a<=n;){const r=a+n>>1,i=r+(1&r),o=t.fetchIfRef(s[i]);if(eo))return s[i+1];a=i+2}}}return null}get(e){return this.xref.fetchIfRef(this.getRaw(e))}}class NameTree extends NameOrNumberTree{constructor(e,t){super(e,t,"Names")}}class NumberTree extends NameOrNumberTree{constructor(e,t){super(e,t,"Nums")}}function clearGlobalCaches(){!function clearPatternCaches(){Va=Object.create(null)}();!function clearPrimitiveCaches(){la=Object.create(null);fa=Object.create(null);ca=Object.create(null)}();!function clearUnicodeCaches(){wn.clear()}();JBig2CCITTFaxImage.cleanup();JpxImage.cleanup()}function pickPlatformItem(e){if(e instanceof Dict)for(const t of["UF","F","Unix","Mac","DOS"])if(e.has(t))return e.get(t);return null}class FileSpec{#Le=!1;constructor(e,t=!1){if(e instanceof Dict){this.root=e;e.has("FS")&&(this.fs=e.get("FS"));e.has("RF")&&warn("Related file specifications are not supported");t||(e.has("EF")?this.#Le=!0:warn("Non-embedded file specifications are not supported"))}}get filename(){const e=pickPlatformItem(this.root);return e&&"string"==typeof e?stringToPDFString(e,!0).replaceAll("\\\\","\\").replaceAll("\\/","/").replaceAll("\\","/"):""}get content(){if(!this.#Le)return null;const e=pickPlatformItem(this.root?.get("EF"));if(e instanceof BaseStream)return e.getBytes();warn("Embedded file specification points to non-existing/invalid content");return null}get description(){const e=this.root?.get("Desc");return e&&"string"==typeof e?stringToPDFString(e):""}get serializable(){const{filename:e,content:t,description:a}=this;return{rawFilename:e,filename:(n=e,n.substring(n.lastIndexOf("/")+1))||"unnamed",content:t,description:a};var n}}const Bi=0,Di=-2,Mi=-3,Ni=-4,Pi=-5,Ei=-6,_i=-9;function isWhitespace(e,t){const a=e[t];return" "===a||"\n"===a||"\r"===a||"\t"===a}class XMLParserBase{_resolveEntities(e){return e.replaceAll(/&([^;]+);/g,(e,t)=>{if("#x"===t.substring(0,2))return String.fromCodePoint(parseInt(t.substring(2),16));if("#"===t.substring(0,1))return String.fromCodePoint(parseInt(t.substring(1),10));switch(t){case"lt":return"<";case"gt":return">";case"amp":return"&";case"quot":return'"';case"apos":return"'"}return this.onResolveEntity(t)})}_parseContent(e,t){const a=[];let n=t;function skipWs(){for(;n"!==e[n]&&"/"!==e[n];)++n;const s=e.substring(t,n);skipWs();for(;n"!==e[n]&&"/"!==e[n]&&"?"!==e[n];){skipWs();let t="",s="";for(;n"!==e[a]&&"?"!==e[a]&&"/"!==e[a];)++a;const n=e.substring(t,a);!function skipWs(){for(;a"!==e[a+1]);)++a;return{name:n,value:e.substring(s,a),parsed:a-t}}parseXml(e){let t=0;for(;t",a);if(t<0){this.onError(_i);return}this.onEndElement(e.substring(a,t));a=t+1;break;case"?":++a;const n=this._parseProcessingInstruction(e,a);if("?>"!==e.substring(a+n.parsed,a+n.parsed+2)){this.onError(Mi);return}this.onPi(n.name,n.value);a+=n.parsed+2;break;case"!":if("--"===e.substring(a+1,a+3)){t=e.indexOf("--\x3e",a+3);if(t<0){this.onError(Pi);return}this.onComment(e.substring(a+3,t));a=t+3}else if("[CDATA["===e.substring(a+1,a+8)){t=e.indexOf("]]>",a+8);if(t<0){this.onError(Di);return}this.onCdata(e.substring(a+8,t));a=t+3}else{if("DOCTYPE"!==e.substring(a+1,a+8)){this.onError(Ei);return}{const n=e.indexOf("[",a+8);let s=!1;t=e.indexOf(">",a+8);if(t<0){this.onError(Ni);return}if(n>0&&t>n){t=e.indexOf("]>",a+8);if(t<0){this.onError(Ni);return}s=!0}const r=e.substring(a+8,t+(s?1:0));this.onDoctype(r);a=t+(s?2:1)}}break;default:const s=this._parseContent(e,a);if(null===s){this.onError(Ei);return}let r=!1;if("/>"===e.substring(a+s.parsed,a+s.parsed+2))r=!0;else if(">"!==e.substring(a+s.parsed,a+s.parsed+1)){this.onError(_i);return}this.onBeginElement(s.name,s.attributes,r);a+=s.parsed+(r?2:1)}}else{for(;ae.textContent).join(""):this.nodeValue||""}get children(){return this.childNodes||[]}hasChildNodes(){return this.childNodes?.length>0}searchNode(e,t){if(t>=e.length)return this;const a=e[t];if(a.name.startsWith("#")&&t0){n.push([s,0]);s=s.childNodes[0]}else{if(0===n.length)return null;for(;0!==n.length;){const[e,t]=n.pop(),a=t+1;if(a");for(const t of this.childNodes)t.dump(e);e.push(``)}else this.nodeValue?e.push(`>${encodeToXmlString(this.nodeValue)}`):e.push("/>")}else e.push(encodeToXmlString(this.nodeValue))}}class SimpleXMLParser extends XMLParserBase{constructor({hasAttributes:e=!1,lowerCaseName:t=!1}){super();this._currentFragment=null;this._stack=null;this._errorCode=Bi;this._hasAttributes=e;this._lowerCaseName=t}parseFromString(e){this._currentFragment=[];this._stack=[];this._errorCode=Bi;this.parseXml(e);if(this._errorCode!==Bi)return;const[t]=this._currentFragment;return t?{documentElement:t}:void 0}onText(e){if(function isWhitespaceString(e){for(let t=0,a=e.length;t\\376\\377([^<]+)/g,function(e,t){const a=t.replaceAll(/\\([0-3])([0-7])([0-7])/g,function(e,t,a,n){return String.fromCharCode(64*t+8*a+1*n)}).replaceAll(/&(amp|apos|gt|lt|quot);/g,function(e,t){switch(t){case"amp":return"&";case"apos":return"'";case"gt":return">";case"lt":return"<";case"quot":return'"'}throw new Error(`_repair: ${t} isn't defined.`)}),n=[">"];for(let e=0,t=a.length;e=32&&t<127&&60!==t&&62!==t&&38!==t?n.push(String.fromCharCode(t)):n.push("&#x"+(65536+t).toString(16).substring(1)+";")}return n.join("")})}_getSequence(e){const t=e.nodeName;return"rdf:bag"!==t&&"rdf:seq"!==t&&"rdf:alt"!==t?null:e.childNodes.filter(e=>"rdf:li"===e.nodeName)}_parseArray(e){if(!e.hasChildNodes())return;const[t]=e.childNodes,a=this._getSequence(t)||[];this._metadataMap.set(e.nodeName,a.map(e=>e.textContent.trim()))}_parse(e){let t=e.documentElement;if("rdf:rdf"!==t.nodeName){t=t.firstChild;for(;t&&"rdf:rdf"!==t.nodeName;)t=t.nextSibling}if(t&&"rdf:rdf"===t.nodeName&&t.hasChildNodes())for(const e of t.childNodes)if("rdf:description"===e.nodeName)for(const t of e.childNodes){const e=t.nodeName;switch(e){case"#text":continue;case"dc:creator":case"dc:subject":this._parseArray(t);continue}this._metadataMap.set(e,t.textContent.trim())}}get serializable(){return{parsedData:this._metadataMap,rawData:this._data}}}const zi=1,Xi=2,Wi=3,Ki=4,Gi=5;class StructTreeRoot{constructor(e,t,a){this.xref=e;this.dict=t;this.ref=a instanceof Ref?a:null;this.roleMap=new Map;this.structParentIds=null;this.kidRefToPosition=void 0;this.parentTree=null}getKidPosition(e){if(void 0===this.kidRefToPosition){const e=this.dict.get("K");if(Array.isArray(e)){const t=this.kidRefToPosition=new Map;for(let a=0,n=e.length;a=0||(e.parentTreeId=a++);i=!1}}if(i){for(const e of t.values())for(const t of e){delete t.parentTreeId;delete t.structTreeParent}return!1}return!0}async updateStructureTree({newAnnotationsByPage:e,pdfManager:t,changes:a}){const{ref:n,xref:s}=this,r=this.dict.clone(),i=new RefSetCache;i.put(n,r);let o,l=r.getRaw("ParentTree");if(l instanceof Ref)o=s.fetch(l);else{o=l;l=s.getNewTemporaryRef();r.set("ParentTree",l)}o=o.clone();i.put(l,o);let f=o.getRaw("Nums"),c=null;if(f instanceof Ref){c=f;f=s.fetch(c)}f=f.slice();c||o.set("Nums",f);const h=await StructTreeRoot.#Xe({newAnnotationsByPage:e,structTreeRootRef:n,structTreeRoot:this,kids:null,nums:f,xref:s,pdfManager:t,changes:a,cache:i});if(-1!==h){r.set("ParentTreeNextKey",h);c&&i.put(c,f);for(const[e,t]of i.items())a.put(e,{data:t})}}static async#Xe({newAnnotationsByPage:e,structTreeRootRef:t,structTreeRoot:a,kids:n,nums:s,xref:r,pdfManager:i,changes:o,cache:l}){const f=Name.get("OBJR");let c,h=-1;for(const[u,m]of e){const e=await i.getPage(u),{ref:p}=e,d=p instanceof Ref;for(const{accessibilityData:i,ref:g,parentTreeId:b,structTreeParent:w}of m){if(!i?.type)continue;const{structParent:m}=i;if(a&&Number.isInteger(m)&&m>=0){let t=(c||=new Map).get(u);if(void 0===t){t=new StructTreePage(a,e.pageDict).collectObjects(p);c.set(u,t)}const n=t?.get(m);if(n){const e=r.fetch(n).clone();StructTreeRoot.#Ke(e,i);o.put(n,{data:e});continue}}h=Math.max(h,b);const j=r.getNewTemporaryRef(),k=new Dict(r);StructTreeRoot.#Ke(k,i);await this.#Ge({structTreeParent:w,tagDict:k,newTagRef:j,structTreeRootRef:t,fallbackKids:n,xref:r,cache:l});const y=new Dict(r);k.set("K",y);y.set("Type",f);d&&y.set("Pg",p);y.set("Obj",g);l.put(j,k);s.push(b,j)}}return h+1}static#Ke(e,{type:t,title:a,lang:n,alt:s,expanded:r,actualText:i}){e.set("S",Name.get(t));a&&e.set("T",stringToAsciiOrUTF16BE(a));n&&e.set("Lang",stringToAsciiOrUTF16BE(n));s&&e.set("Alt",stringToAsciiOrUTF16BE(s));r&&e.set("E",stringToAsciiOrUTF16BE(r));i&&e.set("ActualText",stringToAsciiOrUTF16BE(i))}static#We({elements:e,xref:t,pageDict:a,numberTree:n}){const s=new Map;for(const t of e)if(t.structTreeParentId){const e=parseInt(t.structTreeParentId.split("_mc")[1],10);s.getOrInsertComputed(e,makeArr).push(t)}const r=a.get("StructParents");if(!Number.isInteger(r))return;const i=n.get(r),updateElement=(e,a,n)=>{const r=s.get(e);if(r){const e=a.getRaw("P"),s=t.fetchIfRef(e);if(e instanceof Ref&&s instanceof Dict){const e={ref:n,dict:a};for(const t of r)t.structTreeParent=e}return!0}return!1};for(const e of i){if(!(e instanceof Ref))continue;const a=t.fetch(e),n=a.get("K");if(Number.isInteger(n))updateElement(n,a,e);else if(Array.isArray(n))for(let s of n){s=t.fetchIfRef(s);if(Number.isInteger(s)&&updateElement(s,a,e))break;if(!(s instanceof Dict))continue;if(!isName(s.get("Type"),"MCR"))break;const n=s.get("MCID");if(Number.isInteger(n)&&updateElement(n,a,e))break}}}static async#Ge({structTreeParent:e,tagDict:t,newTagRef:a,structTreeRootRef:n,fallbackKids:s,xref:r,cache:i}){let o,l=null;if(e){({ref:l}=e);o=e.dict.getRaw("P")||n}else o=n;t.set("P",o);const f=r.fetchIfRef(o);if(!f){s.push(a);return}let c=i.get(o);if(!c){c=f.clone();i.put(o,c)}const h=c.getRaw("K");let u=h instanceof Ref?i.get(h):null;if(!u){u=r.fetchIfRef(h);u=Array.isArray(u)?u.slice():[h];const e=r.getNewTemporaryRef();c.set("K",e);i.put(e,u)}const m=u.indexOf(l);u.splice(m>=0?m+1:u.length,0,a)}}class StructElementNode{constructor(e,t){this.tree=e;this.xref=e.xref;this.dict=t;this.kids=[];this.parseKids()}get role(){const e=this.dict.get("S"),t=e instanceof Name?e.name:"",{root:a}=this.tree;return a.roleMap.get(t)??t}get mathML(){let e=this.dict.get("AF")||[];Array.isArray(e)||(e=[e]);for(let t of e){t=this.xref.fetchIfRef(t);if(!(t instanceof Dict))continue;if(!isName(t.get("Type"),"Filespec"))continue;if(!isName(t.get("AFRelationship"),"Supplement"))continue;const e=t.get("EF");if(!(e instanceof Dict))continue;const a=e.get("UF")||e.get("F");if(a instanceof BaseStream&&(isName(a.dict.get("Type"),"EmbeddedFile")&&isName(a.dict.get("Subtype"),"application/mathml+xml")))return stringToUTF8String(a.getString())}const t=this.dict.get("A");if(t instanceof Dict){if(isName(t.get("O"),"MSFT_Office")){const e=t.get("MSFT_MathML");return e?stringToPDFString(e):null}}return null}parseKids(){let e=null;const t=this.dict.getRaw("Pg");t instanceof Ref&&(e=t.toString());const a=this.dict.get("K");if(Array.isArray(a))for(const t of a){const a=this.parseKid(e,this.xref.fetchIfRef(t));a&&this.kids.push(a)}else{const t=this.parseKid(e,a);t&&this.kids.push(t)}}parseKid(e,t){if(Number.isInteger(t))return this.tree.pageDict.objId!==e?null:new StructElement({type:zi,mcid:t,pageObjId:e});if(!(t instanceof Dict))return null;const a=t.getRaw("Pg");a instanceof Ref&&(e=a.toString());const n=t.get("Type")instanceof Name?t.get("Type").name:null;if("MCR"===n){if(this.tree.pageDict.objId!==e)return null;const a=t.getRaw("Stm");return new StructElement({type:Xi,refObjId:a instanceof Ref?a.toString():null,pageObjId:e,mcid:t.get("MCID")})}if("OBJR"===n){if(this.tree.pageDict.objId!==e)return null;const a=t.getRaw("Obj");return new StructElement({type:Wi,refObjId:a instanceof Ref?a.toString():null,pageObjId:e})}return new StructElement({type:Gi,dict:t})}}class StructElement{constructor({type:e,dict:t=null,mcid:a=null,pageObjId:n=null,refObjId:s=null}){this.type=e;this.dict=t;this.mcid=a;this.pageObjId=n;this.refObjId=s;this.parentNode=null}}class StructTreePage{constructor(e,t){this.root=e;this.xref=e?.xref??null;this.rootDict=e?.dict??null;this.pageDict=t;this.nodes=[]}collectObjects(e){if(!(this.root&&this.rootDict&&e instanceof Ref))return null;const t=this.rootDict.get("ParentTree");if(!t)return null;const a=this.root.structParentIds?.get(e);if(!a)return null;const n=new Map,s=new NumberTree(t,this.xref);for(const[e]of a){const t=s.getRaw(e);t instanceof Ref&&n.set(e,t)}return n}parse(e){if(!(this.root&&this.rootDict&&e instanceof Ref))return;const{parentTree:t}=this.root;if(!t)return;const a=this.pageDict.get("StructParents"),n=this.root.structParentIds?.get(e);if(!Number.isInteger(a)&&!n)return;const s=new Map;if(Number.isInteger(a)){const e=t.get(a);if(Array.isArray(e))for(const t of e)t instanceof Ref&&this.addNode(this.xref.fetch(t),s)}if(n)for(const[e,a]of n){const n=t.get(e);if(n){const e=this.addNode(this.xref.fetchIfRef(n),s);1===e?.kids?.length&&e.kids[0].type===Wi&&(e.kids[0].type=a)}}}addNode(e,t,a=0){if(a>40){warn("StructTree MAX_DEPTH reached.");return null}if(!(e instanceof Dict))return null;if(t.has(e))return t.get(e);const n=new StructElementNode(this,e);t.set(e,n);switch(n.role){case"L":case"LBody":case"LI":case"Table":case"THead":case"TBody":case"TFoot":case"TR":for(const e of n.kids)e.type===Gi&&this.addNode(e.dict,t,a-1)}const s=e.get("P");if(!(s instanceof Dict)||isName(s.get("Type"),"StructTreeRoot")){this.addTopLevelNode(e,n)||t.delete(e);return n}const r=this.addNode(s,t,a+1);if(!r)return n;let i=!1;for(const t of r.kids)if(t.type===Gi&&t.dict===e){t.parentNode=n;i=!0}i||t.delete(e);return n}addTopLevelNode(e,t){const a=this.root.getKidPosition(e.objId);if(isNaN(a))return!1;-1!==a&&(this.nodes[a]=t);return!0}get serializable(){function nodeToSerializable(e,t,a=0){if(a>40){warn("StructTree too deep to be fully serialized.");return}const n=Object.create(null);n.role=e.role;n.children=[];t.children.push(n);let s=e.dict.get("Alt");"string"!=typeof s&&(s=e.dict.get("ActualText"));"string"==typeof s&&(n.alt=stringToPDFString(s));if("Formula"===n.role){const{mathML:t}=e;t&&(n.mathML=t)}const r=e.dict.get("A");if(r instanceof Dict){const e=lookupNormalRect(r.getArray("BBox"),null);if(e)n.bbox=e;else{const e=r.get("Width"),t=r.get("Height");"number"==typeof e&&e>0&&"number"==typeof t&&t>0&&(n.bbox=[0,0,e,t])}}const i=e.dict.get("Lang");"string"==typeof i&&(n.lang=stringToPDFString(i));for(const t of e.kids){const e=t.type===Gi?t.parentNode:null;e?nodeToSerializable(e,n,a+1):t.type===zi||t.type===Xi?n.children.push({type:"content",id:`p${t.pageObjId}_mc${t.mcid}`}):t.type===Wi?n.children.push({type:"object",id:t.refObjId}):t.type===Ki&&n.children.push({type:"annotation",id:`pdfjs_internal_id_${t.refObjId}`})}}const e=Object.create(null);e.children=[];e.role="Root";for(const t of this.nodes)t&&nodeToSerializable(t,e);return e}}const Vi=function _isValidExplicitDest(e,t,a){if(!Array.isArray(a)||a.length<2)return!1;const[n,s,...r]=a;if(!e(n)&&!Number.isInteger(n))return!1;if(!t(s))return!1;const i=r.length;let o=!0;switch(s.name){case"XYZ":if(i<2||i>3)return!1;break;case"Fit":case"FitB":return 0===i;case"FitH":case"FitBH":case"FitV":case"FitBV":if(i>1)return!1;break;case"FitR":if(4!==i)return!1;o=!1;break;default:return!1}for(const e of r)if(!("number"==typeof e||o&&null===e))return!1;return!0}.bind(null,e=>e instanceof Ref,isName);function fetchDest(e){e instanceof Dict&&(e=e.get("D"));return Vi(e)?e:null}function fetchRemoteDest(e){let t=e.get("D");if(t){t instanceof Name&&(t=t.name);if("string"==typeof t)return stringToPDFString(t,!0);if(Vi(t))return JSON.stringify(t)}return null}class Catalog{#Ve=null;#$e=null;builtInCMapCache=new Map;fontCache=new RefSetCache;globalColorSpaceCache=new GlobalColorSpaceCache;globalImageCache=new GlobalImageCache;nonBlendModesSet=new RefSet;pageDictCache=new RefSetCache;pageIndexCache=new RefSetCache;pageKidsCountCache=new RefSetCache;standardFontDataCache=new Map;systemFontCache=new Map;constructor(e,t){this.pdfManager=e;this.xref=t;this.#$e=t.getCatalogObj();if(!(this.#$e instanceof Dict))throw new FormatError("Catalog object is not a dictionary.");this.toplevelPagesDict}cloneDict(){return this.#$e.clone()}get version(){const e=this.#$e.get("Version");if(e instanceof Name){if(ua.test(e.name))return shadow(this,"version",e.name);warn(`Invalid PDF catalog version: ${e.name}`)}return shadow(this,"version",null)}get lang(){const e=this.#$e.get("Lang");return shadow(this,"lang",e&&"string"==typeof e?stringToPDFString(e):null)}get needsRendering(){const e=this.#$e.get("NeedsRendering");return shadow(this,"needsRendering","boolean"==typeof e&&e)}get collection(){let e=null;try{const t=this.#$e.get("Collection");t instanceof Dict&&t.size>0&&(e=t)}catch(e){if(e instanceof MissingDataException)throw e;info("Cannot fetch Collection entry; assuming no collection is present.")}return shadow(this,"collection",e)}get acroForm(){let e=null;try{const t=this.#$e.get("AcroForm");t instanceof Dict&&t.size>0&&(e=t)}catch(e){if(e instanceof MissingDataException)throw e;info("Cannot fetch AcroForm entry; assuming no forms are present.")}return shadow(this,"acroForm",e)}get acroFormRef(){const e=this.#$e.getRaw("AcroForm");return shadow(this,"acroFormRef",e instanceof Ref?e:null)}get metadata(){const e=this.#$e.getRaw("Metadata");if(!(e instanceof Ref))return shadow(this,"metadata",null);let t=null;try{const a=this.xref.fetch(e,!this.xref.encrypt?.encryptMetadata);if(a instanceof BaseStream&&a.dict instanceof Dict){const e=a.dict.get("Type"),n=a.dict.get("Subtype");if(isName(e,"Metadata")&&isName(n,"XML")){const e=stringToUTF8String(a.getString());e&&(t=new MetadataParser(e).serializable)}}}catch(e){if(e instanceof MissingDataException)throw e;info(`Skipping invalid Metadata: "${e}".`)}return shadow(this,"metadata",t)}get markInfo(){let e=null;try{e=this.#Ye()}catch(e){if(e instanceof MissingDataException)throw e;warn("Unable to read mark info.")}return shadow(this,"markInfo",e)}#Ye(){const e=this.#$e.get("MarkInfo");if(!(e instanceof Dict))return null;const t={Marked:!1,UserProperties:!1,Suspects:!1};for(const a in t){const n=e.get(a);"boolean"==typeof n&&(t[a]=n)}return t}get hasStructTree(){return this.#$e.has("StructTreeRoot")}get structTreeRoot(){let e=null;try{e=this.#Je()}catch(e){if(e instanceof MissingDataException)throw e;warn("Unable read to structTreeRoot info.")}return shadow(this,"structTreeRoot",e)}#Je(){const e=this.#$e.getRaw("StructTreeRoot"),t=this.xref.fetchIfRef(e);if(!(t instanceof Dict))return null;const a=new StructTreeRoot(this.xref,t,e);a.init();return a}get toplevelPagesDict(){const e=this.#$e.get("Pages");if(!(e instanceof Dict))throw new FormatError("Invalid top-level pages dictionary.");return shadow(this,"toplevelPagesDict",e)}get documentOutline(){let e=null;try{e=this.#Qe()}catch(e){if(e instanceof MissingDataException)throw e;warn("Unable to read document outline.")}return shadow(this,"documentOutline",e)}#Qe(e={}){let t=this.#$e.get("Outlines");if(!(t instanceof Dict))return null;t=t.getRaw("First");if(!(t instanceof Ref))return null;const a={items:[]},n=[{obj:t,parent:a}],s=new RefSet;s.put(t);const r=this.xref,i=new Uint8ClampedArray(3);for(;n.length>0;){const a=n.shift(),o=r.fetchIfRef(a.obj);if(null===o)continue;o.has("Title")||warn("Invalid outline item encountered.");const l={url:null,dest:null,action:null};Catalog.parseDestDictionary({destDict:o,resultObj:l,docBaseUrl:this.baseUrl,docAttachments:this.attachments});const f=o.get("Title"),c=o.get("F")||0,h=o.getArray("C"),u=o.get("Count");let m=i;!isNumberArray(h,3)||0===h[0]&&0===h[1]&&0===h[2]||(m=ColorSpaceUtils.rgb.getRgb(h,0));const p={action:l.action,attachment:l.attachment,dest:l.dest,url:l.url,unsafeUrl:l.unsafeUrl,newWindow:l.newWindow,setOCGState:l.setOCGState,title:"string"==typeof f?stringToPDFString(f):"",color:m,count:Number.isInteger(u)?u:void 0,bold:!!(2&c),italic:!!(1&c),items:[]};e.keepRawDict&&(p.rawDict=o);a.parent.items.push(p);t=o.getRaw("First");if(t instanceof Ref&&!s.has(t)){n.push({obj:t,parent:p});s.put(t)}t=o.getRaw("Next");if(t instanceof Ref&&!s.has(t)){n.push({obj:t,parent:a.parent});s.put(t)}}return a.items.length>0?a.items:null}get documentOutlineForEditor(){let e=null;try{e=this.#Qe({keepRawDict:!0})}catch(e){if(e instanceof MissingDataException)throw e;warn("Unable to read document outline.")}return shadow(this,"documentOutlineForEditor",e)}get permissions(){let e=null;try{e=this.#Ze()}catch(e){if(e instanceof MissingDataException)throw e;warn("Unable to read permissions.")}return shadow(this,"permissions",e)}#Ze(){const e=this.xref.trailer.get("Encrypt");if(!(e instanceof Dict))return null;let t=e.get("P");if("number"!=typeof t)return null;t+=2**32;const a=[];for(const e in y){const n=y[e];t&n&&a.push(n)}return a}get optionalContentConfig(){let e=null;try{const t=this.#$e.get("OCProperties");if(!t)return shadow(this,"optionalContentConfig",null);const a=t.get("D");if(!a)return shadow(this,"optionalContentConfig",null);const n=t.get("OCGs");if(!Array.isArray(n))return shadow(this,"optionalContentConfig",null);const s=new RefSetCache;for(const e of n)e instanceof Ref&&!s.has(e)&&s.put(e,this.#et(e));e=this.#tt(a,s)}catch(e){if(e instanceof MissingDataException)throw e;warn(`Unable to read optional content config: ${e}`)}return shadow(this,"optionalContentConfig",e)}#et(e){const t=this.xref.fetch(e),a={id:e.toString(),name:null,intent:null,usage:{print:null,view:null},rbGroups:[]},n=t.get("Name");"string"==typeof n&&(a.name=stringToPDFString(n));let s=t.getArray("Intent");Array.isArray(s)||(s=[s]);s.every(e=>e instanceof Name)&&(a.intent=s.map(e=>e.name));const r=t.get("Usage");if(!(r instanceof Dict))return a;const i=a.usage,o=r.get("Print");if(o instanceof Dict){const e=o.get("PrintState");if(e instanceof Name)switch(e.name){case"ON":case"OFF":i.print={printState:e.name}}}const l=r.get("View");if(l instanceof Dict){const e=l.get("ViewState");if(e instanceof Name)switch(e.name){case"ON":case"OFF":i.view={viewState:e.name}}}return a}#tt(e,t){function parseOnOff(e){const a=[];if(Array.isArray(e))for(const n of e)n instanceof Ref&&t.has(n)&&a.push(n.toString());return a}function parseOrder(e,a=0){if(!Array.isArray(e))return null;const s=[];for(const r of e){if(r instanceof Ref&&t.has(r)){n.put(r);s.push(r.toString());continue}const e=parseNestedOrder(r,a);e&&s.push(e)}if(a>0)return s;const r=[];for(const[e]of t.items())n.has(e)||r.push(e.toString());r.length&&s.push({name:null,order:r});return s}function parseNestedOrder(e,t){if(++t>s){warn("parseNestedOrder - reached MAX_NESTED_LEVELS.");return null}const n=a.fetchIfRef(e);if(!Array.isArray(n))return null;const r=a.fetchIfRef(n[0]);if("string"!=typeof r)return null;const i=parseOrder(n.slice(1),t);return i?.length?{name:stringToPDFString(r),order:i}:null}const a=this.xref,n=new RefSet,s=10;!function parseRBGroups(e){if(Array.isArray(e))for(const n of e){const e=a.fetchIfRef(n);if(!Array.isArray(e)||!e.length)continue;const s=new Set;for(const a of e)if(a instanceof Ref&&t.has(a)&&!s.has(a.toString())){s.add(a.toString());t.get(a).rbGroups.push(s)}}}(e.get("RBGroups"));return{name:"string"==typeof e.get("Name")?stringToPDFString(e.get("Name")):null,creator:"string"==typeof e.get("Creator")?stringToPDFString(e.get("Creator")):null,baseState:e.get("BaseState")instanceof Name?e.get("BaseState").name:null,on:parseOnOff(e.get("ON")),off:parseOnOff(e.get("OFF")),order:parseOrder(e.get("Order")),groups:[...t]}}setActualNumPages(e=null){this.#Ve=e}get hasActualNumPages(){return null!==this.#Ve}get _pagesCount(){const e=this.toplevelPagesDict.get("Count");if(!Number.isInteger(e))throw new FormatError("Page count in top-level pages dictionary is not an integer.");return shadow(this,"_pagesCount",e)}get numPages(){return this.#Ve??this._pagesCount}get destinations(){const e=this.#at(),t=Object.create(null);for(const a of e)if(a instanceof NameTree)for(const[e,n]of a.getAll()){const a=fetchDest(n);a&&(t[stringToPDFString(e,!0)]=a)}else if(a instanceof Dict)for(const[e,n]of a){const a=fetchDest(n);a&&(t[stringToPDFString(e,!0)]||=a)}return shadow(this,"destinations",t)}getDestination(e){if(Object.hasOwn(this,"destinations"))return this.destinations[e]??null;const t=this.#at();for(const a of t)if(a instanceof NameTree||a instanceof Dict){const t=fetchDest(a.get(e));if(t)return t}if(t.length){const t=this.destinations[e];if(t)return t}return null}#at(){const e=this.#$e.get("Names"),t=[];e?.has("Dests")&&t.push(new NameTree(e.getRaw("Dests"),this.xref));this.#$e.has("Dests")&&t.push(this.#$e.get("Dests"));return t}get rawPageLabels(){const e=this.#$e.getRaw("PageLabels");if(!e)return null;return new NumberTree(e,this.xref).getAll()}get pageLabels(){let e=null;try{e=this.#nt()}catch(e){if(e instanceof MissingDataException)throw e;warn("Unable to read page labels.")}return shadow(this,"pageLabels",e)}#nt(){const e=this.rawPageLabels;if(!e)return null;const t=new Array(this.numPages);let a=null,n="",s="",r=1;for(let i=0,o=this.numPages;i=1))throw new FormatError("Invalid start in PageLabel dictionary.");r=e}else r=1}switch(a){case"D":s=r;break;case"R":case"r":s=toRomanNumerals(r,"r"===a);break;case"A":case"a":const e=26,t="a"===a?97:65,n=r-1;s=String.fromCharCode(t+n%e).repeat(Math.floor(n/e)+1);break;default:if(a)throw new FormatError(`Invalid style "${a}" in PageLabel dictionary.`);s=""}t[i]=n+s;r++}return t}get pageLayout(){const e=this.#$e.get("PageLayout");let t="";if(e instanceof Name)switch(e.name){case"SinglePage":case"OneColumn":case"TwoColumnLeft":case"TwoColumnRight":case"TwoPageLeft":case"TwoPageRight":t=e.name}return shadow(this,"pageLayout",t)}get pageMode(){const e=this.#$e.get("PageMode");let t="UseNone";if(e instanceof Name)switch(e.name){case"UseNone":case"UseOutlines":case"UseThumbs":case"FullScreen":case"UseOC":case"UseAttachments":t=e.name}return shadow(this,"pageMode",t)}get viewerPreferences(){const e=this.#$e.get("ViewerPreferences");if(!(e instanceof Dict))return shadow(this,"viewerPreferences",null);let t=null;for(const[a,n]of e){let e;switch(a){case"HideToolbar":case"HideMenubar":case"HideWindowUI":case"FitWindow":case"CenterWindow":case"DisplayDocTitle":case"PickTrayByPDFSize":"boolean"==typeof n&&(e=n);break;case"NonFullScreenPageMode":if(n instanceof Name)switch(n.name){case"UseNone":case"UseOutlines":case"UseThumbs":case"UseOC":e=n.name;break;default:e="UseNone"}break;case"Direction":if(n instanceof Name)switch(n.name){case"L2R":case"R2L":e=n.name;break;default:e="L2R"}break;case"ViewArea":case"ViewClip":case"PrintArea":case"PrintClip":if(n instanceof Name)switch(n.name){case"MediaBox":case"CropBox":case"BleedBox":case"TrimBox":case"ArtBox":e=n.name;break;default:e="CropBox"}break;case"PrintScaling":if(n instanceof Name)switch(n.name){case"None":case"AppDefault":e=n.name;break;default:e="AppDefault"}break;case"Duplex":if(n instanceof Name)switch(n.name){case"Simplex":case"DuplexFlipShortEdge":case"DuplexFlipLongEdge":e=n.name;break;default:e="None"}break;case"PrintPageRange":if(Array.isArray(n)&&n.length%2==0){n.every((e,t,a)=>Number.isInteger(e)&&e>0&&(0===t||e>=a[t-1])&&e<=this.numPages)&&(e=n)}break;case"NumCopies":Number.isInteger(n)&&n>0&&(e=n);break;default:warn(`Ignoring non-standard key in ViewerPreferences: ${a}.`);continue}if(void 0!==e){t??=Object.create(null);t[a]=e}else warn(`Bad value, for key "${a}", in ViewerPreferences: ${n}.`)}return shadow(this,"viewerPreferences",t)}get openAction(){const e=this.#$e.get("OpenAction"),t=Object.create(null);if(e instanceof Dict){const a=new Dict(this.xref);a.set("A",e);const n={url:null,dest:null,action:null};Catalog.parseDestDictionary({destDict:a,resultObj:n});Array.isArray(n.dest)?t.dest=n.dest:n.action&&(t.action=n.action)}else Vi(e)&&(t.dest=e);return shadow(this,"openAction",objectSize(t)>0?t:null)}get attachments(){const e=this.#$e.get("Names");let t=null;if(e instanceof Dict&&e.has("EmbeddedFiles")){const a=new NameTree(e.getRaw("EmbeddedFiles"),this.xref);for(const[e,n]of a.getAll()){const a=new FileSpec(n);t??=Object.create(null);t[stringToPDFString(e,!0)]=a.serializable}}return shadow(this,"attachments",t)}get rawEmbeddedFiles(){const e=this.#$e.get("Names");if(!(e instanceof Dict&&e.has("EmbeddedFiles")))return null;return new NameTree(e.getRaw("EmbeddedFiles"),this.xref).getAll(!0)}get xfaImages(){const e=this.#$e.get("Names");let t=null;if(e instanceof Dict&&e.has("XFAImages")){const a=new NameTree(e.getRaw("XFAImages"),this.xref);for(const[e,n]of a.getAll())if(n instanceof BaseStream){t??=new Map;t.set(stringToPDFString(e,!0),n.getBytes())}}return shadow(this,"xfaImages",t)}#st(){const e=this.#$e.get("Names");let t=null;function appendIfJavaScriptDict(e,a){if(!(a instanceof Dict))return;if(!isName(a.get("S"),"JavaScript"))return;let n=a.get("JS");if(n instanceof BaseStream)n=n.getString();else if("string"!=typeof n)return;n=stringToPDFString(n,!0).replaceAll("\0","");n&&(t||=new Map).set(e,n)}if(e instanceof Dict&&e.has("JavaScript")){const t=new NameTree(e.getRaw("JavaScript"),this.xref);for(const[e,a]of t.getAll())appendIfJavaScriptDict(stringToPDFString(e,!0),a)}const a=this.#$e.get("OpenAction");a&&appendIfJavaScriptDict("OpenAction",a);return t}get jsActions(){const e=this.#st();let t=collectActions(this.xref,this.#$e,oe);if(e){t||=Object.create(null);for(const[a,n]of e)a in t?t[a].push(n):t[a]=[n]}return shadow(this,"jsActions",t)}async cleanup(e=!1){clearGlobalCaches();this.globalColorSpaceCache.clear();this.globalImageCache.clear(e);this.pageKidsCountCache.clear();this.pageIndexCache.clear();this.pageDictCache.clear();this.nonBlendModesSet.clear();for(const{dict:e}of await Promise.all(this.fontCache))delete e.cacheKey;this.fontCache.clear();this.builtInCMapCache.clear();this.standardFontDataCache.clear();this.systemFontCache.clear()}async getPageDict(e){const t=[this.toplevelPagesDict],a=new RefSet,n=this.#$e.getRaw("Pages");n instanceof Ref&&a.put(n);const s=this.xref,r=this.pageKidsCountCache,i=this.pageIndexCache,o=this.pageDictCache;let l=0;for(;t.length;){const n=t.pop();if(n instanceof Ref){const f=r.get(n);if(f>=0&&l+f<=e){l+=f;continue}if(a.has(n))throw new FormatError("Pages tree contains circular reference.");a.put(n);const c=await(o.get(n)||s.fetchAsync(n));if(c instanceof Dict){let t=c.getRaw("Type");t instanceof Ref&&(t=await s.fetchAsync(t));if(isName(t,"Page")||!c.has("Kids")){r.has(n)||r.put(n,1);i.has(n)||i.put(n,l);if(l===e)return[c,n];l++;continue}}t.push(c);continue}if(!(n instanceof Dict))throw new FormatError("Page dictionary kid reference points to wrong type of object.");const{objId:f}=n;let c=n.getRaw("Count");c instanceof Ref&&(c=await s.fetchAsync(c));if(Number.isInteger(c)&&c>=0){f&&!r.has(f)&&r.put(f,c);if(l+c<=e){l+=c;continue}}let h=n.getRaw("Kids");h instanceof Ref&&(h=await s.fetchAsync(h));if(!Array.isArray(h)){let t=n.getRaw("Type");t instanceof Ref&&(t=await s.fetchAsync(t));if(isName(t,"Page")||!n.has("Kids")){if(l===e)return[n,null];l++;continue}throw new FormatError("Page dictionary kids object is not an array.")}for(let e=h.length-1;e>=0;e--){const a=h[e];t.push(a);n===this.toplevelPagesDict&&a instanceof Ref&&!o.has(a)&&o.put(a,s.fetchAsync(a))}}throw new Error(`Page index ${e} not found.`)}async getAllPageDicts(e=!1){const{ignoreErrors:t}=this.pdfManager.evaluatorOptions,a=[{currentNode:this.toplevelPagesDict,posInKids:0}],n=new RefSet,s=this.#$e.getRaw("Pages");s instanceof Ref&&n.put(s);const r=new Map,i=this.xref,o=this.pageIndexCache;let l=0;function addPageDict(e,t){t&&!o.has(t)&&o.put(t,l);r.set(l++,[e,t])}function addPageError(a){if(a instanceof XRefEntryException&&!e)throw a;if(e&&t&&0===l){warn(`getAllPageDicts - Skipping invalid first page: "${a}".`);a=Dict.empty}r.set(l++,[a,null])}for(;a.length>0;){const e=a.at(-1),{currentNode:t,posInKids:s}=e;let r=t.getRaw("Kids");if(r instanceof Ref)try{r=await i.fetchAsync(r)}catch(e){addPageError(e);break}if(!Array.isArray(r)){addPageError(new FormatError("Page dictionary kids object is not an array."));break}if(s>=r.length){a.pop();continue}const o=r[s];let l;if(o instanceof Ref){if(n.has(o)){addPageError(new FormatError("Pages tree contains circular reference."));break}n.put(o);try{l=await i.fetchAsync(o)}catch(e){addPageError(e);break}}else l=o;if(!(l instanceof Dict)){addPageError(new FormatError("Page dictionary kid reference points to wrong type of object."));break}let f=l.getRaw("Type");if(f instanceof Ref)try{f=await i.fetchAsync(f)}catch(e){addPageError(e);break}isName(f,"Page")||!l.has("Kids")?addPageDict(l,o instanceof Ref?o:null):a.push({currentNode:l,posInKids:0});e.posInKids++}return r}getPageIndex(e){const t=this.pageIndexCache.get(e);if(void 0!==t)return Promise.resolve(t);const a=this.xref;let n=0;const next=t=>function pagesBeforeRef(t){let n,s=0;return a.fetchAsync(t).then(function(a){if(isRefsEqual(t,e)&&!isDict(a,"Page")&&!(a instanceof Dict&&!a.has("Type")&&a.has("Contents")))throw new FormatError("The reference does not point to a /Page dictionary.");if(!a)return null;if(!(a instanceof Dict))throw new FormatError("Node must be a dictionary.");n=a.getRaw("Parent");return a.getAsync("Parent")}).then(function(e){if(!e)return null;if(!(e instanceof Dict))throw new FormatError("Parent must be a dictionary.");return e.getAsync("Kids")}).then(function(e){if(!e)return null;const r=[];let i=!1;for(const n of e){if(!(n instanceof Ref))throw new FormatError("Kid must be a reference.");if(isRefsEqual(n,t)){i=!0;break}r.push(a.fetchAsync(n).then(function(e){if(!(e instanceof Dict))throw new FormatError("Kid node must be a dictionary.");e.has("Count")?s+=e.get("Count"):s++}))}if(!i)throw new FormatError("Kid reference not found in parent's kids.");return Promise.all(r).then(()=>[s,n])})}(t).then(t=>{if(!t){this.pageIndexCache.put(e,n);return n}const[a,s]=t;n+=a;return next(s)});return next(e)}get baseUrl(){const e=this.#$e.get("URI");if(e instanceof Dict){const t=e.get("Base");if("string"==typeof t){const e=createValidAbsoluteUrl(t,null,{tryConvertEncoding:!0});if(e)return shadow(this,"baseUrl",e.href)}}return shadow(this,"baseUrl",this.pdfManager.docBaseUrl)}static#rt(e,t){const a=e.fetchIfRef(t);if(!(a instanceof Dict))return null;let n=null;const s=a.getRaw("Pg");s instanceof Ref&&(n=s);if(!n){const t=[a];for(;t.length>0&&!n;){const a=t.shift().get("K");let s;if(Array.isArray(a))s=a;else{if(!a)continue;s=[a]}for(const a of s){const s=e.fetchIfRef(a);if(!(s instanceof Dict))continue;const r=s.getRaw("Pg");if(r instanceof Ref){n=r;break}t.push(s)}}}if(!n){const t=40;let s=a;for(let a=0;ae.startsWith("http://www.xfa.org/schema/xci/")},connectionSet:{id:1,check:e=>e.startsWith("http://www.xfa.org/schema/xfa-connection-set/")},datasets:{id:2,check:e=>e.startsWith("http://www.xfa.org/schema/xfa-data/")},form:{id:3,check:e=>e.startsWith("http://www.xfa.org/schema/xfa-form/")},localeSet:{id:4,check:e=>e.startsWith("http://www.xfa.org/schema/xfa-locale-set/")},pdf:{id:5,check:e=>"http://ns.adobe.com/xdp/pdf/"===e},signature:{id:6,check:e=>"http://www.w3.org/2000/09/xmldsig#"===e},sourceSet:{id:7,check:e=>e.startsWith("http://www.xfa.org/schema/xfa-source-set/")},stylesheet:{id:8,check:e=>"http://www.w3.org/1999/XSL/Transform"===e},template:{id:9,check:e=>e.startsWith("http://www.xfa.org/schema/xfa-template/")},xdc:{id:10,check:e=>e.startsWith("http://www.xfa.org/schema/xdc/")},xdp:{id:11,check:e=>"http://ns.adobe.com/xdp/"===e},xfdf:{id:12,check:e=>"http://ns.adobe.com/xfdf/"===e},xhtml:{id:13,check:e=>"http://www.w3.org/1999/xhtml"===e},xmpmeta:{id:14,check:e=>"http://ns.adobe.com/xmpmeta/"===e}},ml={pt:e=>e,cm:e=>e/2.54*72,mm:e=>e/25.4*72,in:e=>72*e,px:e=>e},pl=/([+-]?\d+\.?\d*)(.*)/;function stripQuotes(e){return e.startsWith("'")||e.startsWith('"')?e.slice(1,-1):e}function getInteger({data:e,defaultValue:t,validate:a}){if(!e)return t;e=e.trim();const n=parseInt(e,10);return!isNaN(n)&&a(n)?n:t}function getFloat({data:e,defaultValue:t,validate:a}){if(!e)return t;e=e.trim();const n=parseFloat(e);return!isNaN(n)&&a(n)?n:t}function getKeyword({data:e,defaultValue:t,validate:a}){return e&&a(e=e.trim())?e:t}function getStringOption(e,t){return getKeyword({data:e,defaultValue:t[0],validate:e=>t.includes(e)})}function getMeasurement(e,t="0"){t||="0";if(!e)return getMeasurement(t);const a=e.trim().match(pl);if(!a)return getMeasurement(t);const[,n,s]=a,r=parseFloat(n);if(isNaN(r))return getMeasurement(t);if(0===r)return 0;const i=ml[s];return i?i(r):r}function getRatio(e){if(!e)return{num:1,den:1};const t=e.split(":",2).map(e=>parseFloat(e.trim())).filter(e=>!isNaN(e));1===t.length&&t.push(1);if(0===t.length)return{num:1,den:1};const[a,n]=t;return{num:a,den:n}}function getRelevant(e){return e?e.trim().split(/\s+/).map(e=>({excluded:"-"===e[0],viewname:e.substring(1)})):[]}class HTMLResult{static get FAILURE(){return shadow(this,"FAILURE",new HTMLResult(!1,null,null,null))}static get EMPTY(){return shadow(this,"EMPTY",new HTMLResult(!0,null,null,null))}constructor(e,t,a,n){this.success=e;this.html=t;this.bbox=a;this.breakNode=n}isBreak(){return!!this.breakNode}static breakNode(e){return new HTMLResult(!1,null,null,e)}static success(e,t=null){return new HTMLResult(!0,e,t,null)}}class FontFinder{constructor(e){this.fonts=new Map;this.cache=new Map;this.warned=new Set;this.defaultFont=null;this.add(e)}add(e,t=null){for(const t of e)this.addPdfFont(t);for(const e of this.fonts.values())e.regular||(e.regular=e.italic||e.bold||e.bolditalic);if(!t||0===t.size)return;const a=this.fonts.get("PdfJS-Fallback-PdfJS-XFA");for(const e of t)this.fonts.set(e,a)}addPdfFont(e){const t=e.cssFontInfo,a=t.fontFamily,n=this.fonts.getOrInsertComputed(a,makeObj);this.defaultFont??=n;let s="";const r=parseFloat(t.fontWeight);0!==parseFloat(t.italicAngle)?s=r>=700?"bolditalic":"italic":r>=700&&(s="bold");if(!s){(e.name.includes("Bold")||e.psName?.includes("Bold"))&&(s="bold");(e.name.includes("Italic")||e.name.endsWith("It")||e.psName?.includes("Italic")||e.psName?.endsWith("It"))&&(s+="italic")}s||(s="regular");n[s]=e}getDefault(){return this.defaultFont}find(e,t=!0){let a=this.fonts.get(e)||this.cache.get(e);if(a)return a;const n=/,|-|_| |bolditalic|bold|italic|regular|it/gi;let s=e.replaceAll(n,"");a=this.fonts.get(s);if(a){this.cache.set(e,a);return a}s=s.toLowerCase();const r=[];for(const[e,t]of this.fonts)e.replaceAll(n,"").toLowerCase().startsWith(s)&&r.push(t);if(0===r.length)for(const e of this.fonts.values())e.regular.name?.replaceAll(n,"").toLowerCase().startsWith(s)&&r.push(e);if(0===r.length){s=s.replaceAll(/psmt|mt/gi,"");for(const[e,t]of this.fonts)e.replaceAll(n,"").toLowerCase().startsWith(s)&&r.push(t)}if(0===r.length)for(const e of this.fonts.values())e.regular.name?.replaceAll(n,"").toLowerCase().startsWith(s)&&r.push(e);if(r.length>=1){1!==r.length&&t&&warn(`XFA - Too many choices to guess the correct font: ${e}`);this.cache.set(e,r[0]);return r[0]}if(t&&!this.warned.has(e)){this.warned.add(e);warn(`XFA - Cannot find the font: ${e}`)}return null}}function selectFont(e,t){return"italic"===e.posture?"bold"===e.weight?t.bolditalic:t.italic:"bold"===e.weight?t.bold:t.regular}class FontInfo{constructor(e,t,a,n){this.lineHeight=a;this.paraMargin=t||{top:0,bottom:0,left:0,right:0};if(!e){[this.pdfFont,this.xfaFont]=this.defaultFont(n);return}this.xfaFont={typeface:e.typeface,posture:e.posture,weight:e.weight,size:e.size,letterSpacing:e.letterSpacing};const s=n.find(e.typeface);if(s){this.pdfFont=selectFont(e,s);this.pdfFont||([this.pdfFont,this.xfaFont]=this.defaultFont(n))}else[this.pdfFont,this.xfaFont]=this.defaultFont(n)}defaultFont(e){const t=e.find("Helvetica",!1)||e.find("Myriad Pro",!1)||e.find("Arial",!1)||e.getDefault();if(t?.regular){const e=t.regular;return[e,{typeface:e.cssFontInfo.fontFamily,posture:"normal",weight:"normal",size:10,letterSpacing:0}]}return[null,{typeface:"Courier",posture:"normal",weight:"normal",size:10,letterSpacing:0}]}}class FontSelector{constructor(e,t,a,n){this.fontFinder=n;this.stack=[new FontInfo(e,t,a,n)]}pushData(e,t,a){const n=this.stack.at(-1);for(const t of["typeface","posture","weight","size","letterSpacing"])e[t]||=n.xfaFont[t];for(const e of["top","bottom","left","right"])isNaN(t[e])&&(t[e]=n.paraMargin[e]);const s=new FontInfo(e,t,a||n.lineHeight,this.fontFinder);s.pdfFont||=n.pdfFont;this.stack.push(s)}popFont(){this.stack.pop()}topFont(){return this.stack.at(-1)}}class TextMeasure{constructor(e,t,a,n){this.glyphs=[];this.fontSelector=new FontSelector(e,t,a,n);this.extraHeight=0}pushData(e,t,a){this.fontSelector.pushData(e,t,a)}popFont(e){return this.fontSelector.popFont()}addPara(){const e=this.fontSelector.topFont();this.extraHeight+=e.paraMargin.top+e.paraMargin.bottom}addString(e){if(!e)return;const t=this.fontSelector.topFont(),a=t.xfaFont.size;if(t.pdfFont){const n=t.xfaFont.letterSpacing,s=t.pdfFont,r=s.lineHeight||1.2,i=t.lineHeight||Math.max(1.2,r)*a,o=r-(void 0===s.lineGap?.2:s.lineGap),l=Math.max(1,o)*a,f=a/1e3,c=s.defaultWidth||s.charsToGlyphs(" ")[0].width;for(const t of e.split(/[\u2029\n]/)){const e=s.encodeString(t).join(""),a=s.charsToGlyphs(e);for(const e of a){const t=e.width||c;this.glyphs.push([t*f+n,i,l,e.unicode,!1])}this.glyphs.push([0,0,0,"\n",!0])}this.glyphs.pop();return}for(const t of e.split(/[\u2029\n]/)){for(const e of t.split(""))this.glyphs.push([a,1.2*a,a,e,!1]);this.glyphs.push([0,0,0,"\n",!0])}this.glyphs.pop()}compute(e){let t=-1,a=0,n=0,s=0,r=0,i=0,o=!1,l=!0;for(let f=0,c=this.glyphs.length;fe){n=Math.max(n,r);r=0;s+=i;i=g;t=-1;a=0;o=!0;l=!1}else{i=Math.max(g,i);a=r;r+=c;t=f}else if(r+c>e){s+=i;i=g;if(-1!==t){f=t;n=Math.max(n,a);r=0;t=-1;a=0}else{n=Math.max(n,r);r=c}o=!0;l=!1}else{r+=c;i=Math.max(g,i)}}n=Math.max(n,r);s+=i+this.extraHeight;return{width:1.02*n,height:s,isBroken:o}}}const dl=/^[^.[]+/,gl=/^[^\]]+/,bl=0,wl=1,jl=2,kl=3,yl=4,ql=new Map([["$data",(e,t)=>e.datasets?e.datasets.data:e],["$record",(e,t)=>(e.datasets?e.datasets.data:e)[ko]()[0]],["$template",(e,t)=>e.template],["$connectionSet",(e,t)=>e.connectionSet],["$form",(e,t)=>e.form],["$layout",(e,t)=>e.layout],["$host",(e,t)=>e.host],["$dataWindow",(e,t)=>e.dataWindow],["$event",(e,t)=>e.event],["!",(e,t)=>e.datasets],["$xfa",(e,t)=>e],["xfa",(e,t)=>e],["$",(e,t)=>t]]),vl=new WeakMap;function parseIndex(e){return"*"===(e=e.trim())?1/0:parseInt(e,10)||0}function parseExpression(e,t,a=!0){let n=e.match(dl);if(!n)return null;let[s]=n;const r=[{name:s,cacheName:"."+s,index:0,js:null,formCalc:null,operator:bl}];let i=s.length;for(;i0&&c.push(e)}if(0===c.length&&!o&&0===l){const a=t[So]();if(!(t=a))return null;l=-1;e=[t];continue}e=isFinite(f)?c.filter(e=>fe[f]):c.flat()}return 0===e.length?null:e}function createDataNode(e,t,a){const n=parseExpression(a);if(!n)return null;if(n.some(e=>e.operator===wl))return null;const s=ql.get(n[0].name);let r=0;if(s){e=s(e,t);r=1}else e=t||e;for(let t=n.length;re[rl]()).join("")}get[xl](){const e=Object.getPrototypeOf(this);if(!e._attributes){const t=e._attributes=new Set;for(const e of Object.getOwnPropertyNames(this)){if(null===this[e]||this[e]instanceof XFAObject||this[e]instanceof XFAObjectArray)break;t.add(e)}}return shadow(this,xl,e._attributes)}[Do](e){let t=this;for(;t;){if(t===e)return!0;t=t[So]()}return!1}[So](){return this[Pl]}[vo](){return this[So]()}[ko](e=null){return e?this[e]:this[Cl]}[io](){const e=Object.create(null);this[so]&&(e.$content=this[so]);for(const t of Object.getOwnPropertyNames(this)){const a=this[t];null!==a&&(a instanceof XFAObject?e[t]=a[io]():a instanceof XFAObjectArray?a.isEmpty()||(e[t]=a.dump()):e[t]=a)}return e}[fl](){return null}[ol](){return HTMLResult.EMPTY}*[yo](){for(const e of this[ko]())yield e}*[Rl](e,t){for(const a of this[yo]())if(!e||t===e.has(a[Uo])){const e=this[uo](),t=a[ol](e);t.success||(this[oo].failingNode=a);yield t}}[fo](){return null}[Yi](e,t){this[oo].children.push(e)}[uo](){}[Qi]({filter:e=null,include:t=!0}){if(this[oo].generator){const e=this[uo](),t=this[oo].failingNode[ol](e);if(!t.success)return t;t.html&&this[Yi](t.html,t.bbox);delete this[oo].failingNode}else this[oo].generator=this[Rl](e,t);for(;;){const e=this[oo].generator.next();if(e.done)break;const t=e.value;if(!t.success)return t;t.html&&this[Yi](t.html,t.bbox)}this[oo].generator=null;return HTMLResult.EMPTY}[al](e){this[_l]=new Set(Object.keys(e))}[Bl](e){const t=this[xl],a=this[_l];return[...e].filter(e=>t.has(e)&&!a.has(e))}[Zo](e,t=new Set){for(const a of this[Cl])a[El](e,t)}[El](e,t){const a=this[Hl](e,t);a?this[Sl](a,e,t):this[Zo](e,t)}[Hl](e,t){const{use:a,usehref:n}=this;if(!a&&!n)return null;let s=null,r=null,i=null,o=a;if(n){o=n;n.startsWith("#som(")&&n.endsWith(")")?r=n.slice(5,-1):n.startsWith(".#som(")&&n.endsWith(")")?r=n.slice(6,-1):n.startsWith("#")?i=n.slice(1):n.startsWith(".#")&&(i=n.slice(2))}else a.startsWith("#")?i=a.slice(1):r=a;this.use=this.usehref="";if(i)s=e.get(i);else{s=searchNode(e.get(Qo),this,r,!0,!1);s&&(s=s[0])}if(!s){warn(`XFA - Invalid prototype reference: ${o}.`);return null}if(s[Uo]!==this[Uo]){warn(`XFA - Incompatible prototype: ${s[Uo]} !== ${this[Uo]}.`);return null}if(t.has(s)){warn("XFA - Cycle detected in prototypes use.");return null}t.add(s);const l=s[Hl](e,t);l&&s[Sl](l,e,t);s[Zo](e,t);t.delete(s);return s}[Sl](e,t,a){if(a.has(e)){warn("XFA - Cycle detected in prototypes use.");return}!this[so]&&e[so]&&(this[so]=e[so]);new Set(a).add(e);for(const t of this[Bl](e[_l])){this[t]=e[t];this[_l]&&this[_l].add(t)}for(const n of Object.getOwnPropertyNames(this)){if(this[xl].has(n))continue;const s=this[n],r=e[n];if(s instanceof XFAObjectArray){for(const e of s[Cl])e[El](t,a);for(let n=s[Cl].length,i=r[Cl].length;nXFAObject[Il](e)):"object"==typeof e&&null!==e?Object.assign({},e):e}[ao](){const e=Object.create(Object.getPrototypeOf(this));for(const t of Object.getOwnPropertySymbols(this))try{e[t]=this[t]}catch{shadow(e,t,this[t])}e[cl]=`${e[Uo]}${Ll++}`;e[Cl]=[];for(const t of Object.getOwnPropertyNames(this)){if(this[xl].has(t)){e[t]=XFAObject[Il](this[t]);continue}const a=this[t];e[t]=a instanceof XFAObjectArray?new XFAObjectArray(a[Ml]):null}for(const t of this[Cl]){const a=t[Uo],n=t[ao]();e[Cl].push(n);n[Pl]=e;null===e[a]?e[a]=n:e[a][Cl].push(n)}return e}[ko](e=null){return e?this[Cl].filter(t=>t[Uo]===e):this[Cl]}[mo](e){return this[e]}[po](e,t,a=!0){return Array.from(this[go](e,t,a))}*[go](e,t,a=!0){if("parent"!==e){for(const a of this[Cl]){a[Uo]===e&&(yield a);a.name===e&&(yield a);(t||a[Eo]())&&(yield*a[go](e,t,!1))}a&&this[xl].has(e)&&(yield new XFAAttribute(this,e,this[e]))}else yield this[Pl]}}class XFAObjectArray{constructor(e=1/0){this[Ml]=e;this[Cl]=[]}get isXFAObject(){return!1}get isXFAObjectArray(){return!0}push(e){if(this[Cl].length<=this[Ml]){this[Cl].push(e);return!0}warn(`XFA - node "${e[Uo]}" accepts no more than ${this[Ml]} children`);return!1}isEmpty(){return 0===this[Cl].length}dump(){return 1===this[Cl].length?this[Cl][0][io]():this[Cl].map(e=>e[io]())}[ao](){const e=new XFAObjectArray(this[Ml]);e[Cl]=this[Cl].map(e=>e[ao]());return e}get children(){return this[Cl]}clear(){this[Cl].length=0}}class XFAAttribute{constructor(e,t,a){this[Pl]=e;this[Uo]=t;this[so]=a;this[no]=!1;this[cl]="attribute"+Ll++}[So](){return this[Pl]}[Bo](){return!0}[bo](){return this[so].trim()}[nl](e){e=e.value||"";this[so]=e.toString()}[rl](){return this[so]}[Do](e){return this[Pl]===e||this[Pl][Do](e)}}class XmlObject extends XFAObject{constructor(e,t,a={}){super(e,t);this[so]="";this[Fl]=null;if("#text"!==t){const e=new Map;this[Al]=e;for(const[t,n]of Object.entries(a))e.set(t,new XFAAttribute(this,t,n));if(Object.hasOwn(a,Xo)){const e=a[Xo].xfa.dataNode;void 0!==e&&("dataGroup"===e?this[Fl]=!1:"dataValue"===e&&(this[Fl]=!0))}}this[no]=!1}[ll](e){const t=this[Uo];if("#text"===t){e.push(encodeToXmlString(this[so]));return}const a=utf8StringToString(t),n=this[Lo]===Xl?"xfa:":"";e.push(`<${n}${a}`);for(const[t,a]of this[Al]){const n=utf8StringToString(t);e.push(` ${n}="${encodeToXmlString(a[so])}"`)}null!==this[Fl]&&(this[Fl]?e.push(' xfa:dataNode="dataValue"'):e.push(' xfa:dataNode="dataGroup"'));if(this[so]||0!==this[Cl].length){e.push(">");if(this[so])"string"==typeof this[so]?e.push(encodeToXmlString(this[so])):this[so][ll](e);else for(const t of this[Cl])t[ll](e);e.push(``)}else e.push("/>")}[Wo](e){if(this[so]){const e=new XmlObject(this[Lo],"#text");this[Ji](e);e[so]=this[so];this[so]=""}this[Ji](e);return!0}[Go](e){this[so]+=e}[lo](){if(this[so]&&this[Cl].length>0){const e=new XmlObject(this[Lo],"#text");this[Ji](e);e[so]=this[so];delete this[so]}}[ol](){return"#text"===this[Uo]?HTMLResult.success({name:"#text",value:this[so]}):HTMLResult.EMPTY}[ko](e=null){return e?this[Cl].filter(t=>t[Uo]===e):this[Cl]}[ho](){return this[Al]}[mo](e){const t=this[Al].get(e);return void 0!==t?t:this[ko](e)}*[go](e,t){const a=this[Al].get(e);a&&(yield a);for(const a of this[Cl]){a[Uo]===e&&(yield a);t&&(yield*a[go](e,t))}}*[co](e,t){const a=this[Al].get(e);!a||t&&a[no]||(yield a);for(const a of this[Cl])yield*a[co](e,t)}*[jo](e,t,a){for(const n of this[Cl]){n[Uo]!==e||a&&n[no]||(yield n);t&&(yield*n[jo](e,t,a))}}[Bo](){return null===this[Fl]?0===this[Cl].length||this[Cl][0][Lo]===ul.xhtml.id:this[Fl]}[bo](){return null===this[Fl]?0===this[Cl].length?this[so].trim():this[Cl][0][Lo]===ul.xhtml.id?this[Cl][0][rl]().trim():null:this[so].trim()}[nl](e){e=e.value||"";this[so]=e.toString()}[io](e=!1){const t=Object.create(null);e&&(t.$ns=this[Lo]);this[so]&&(t.$content=this[so]);t.$name=this[Uo];t.children=[];for(const a of this[Cl])t.children.push(a[io](e));t.attributes=Object.create(null);for(const[e,a]of this[Al])t.attributes[e]=a[so];return t}}class ContentObject extends XFAObject{constructor(e,t){super(e,t);this[so]=""}[Go](e){this[so]+=e}[lo](){}}class OptionObject extends ContentObject{constructor(e,t,a){super(e,t);this[Nl]=a}[lo](){this[so]=getKeyword({data:this[so],defaultValue:this[Nl][0],validate:e=>this[Nl].includes(e)})}[Zi](e){super[Zi](e);delete this[Nl]}}class StringObject extends ContentObject{[lo](){this[so]=this[so].trim()}}class IntegerObject extends ContentObject{constructor(e,t,a,n){super(e,t);this[Tl]=a;this[zl]=n}[lo](){this[so]=getInteger({data:this[so],defaultValue:this[Tl],validate:this[zl]})}[Zi](e){super[Zi](e);delete this[Tl];delete this[zl]}}class Option01 extends IntegerObject{constructor(e,t){super(e,t,0,e=>1===e)}}class Option10 extends IntegerObject{constructor(e,t){super(e,t,1,e=>0===e)}}function measureToString(e){return"string"==typeof e?"0px":Number.isInteger(e)?`${e}px`:`${e.toFixed(2)}px`}const Wl={anchorType(e,t){const a=e[vo]();if(a&&(!a.layout||"position"===a.layout)){"transform"in t||(t.transform="");switch(e.anchorType){case"bottomCenter":t.transform+="translate(-50%, -100%)";break;case"bottomLeft":t.transform+="translate(0,-100%)";break;case"bottomRight":t.transform+="translate(-100%,-100%)";break;case"middleCenter":t.transform+="translate(-50%,-50%)";break;case"middleLeft":t.transform+="translate(0,-50%)";break;case"middleRight":t.transform+="translate(-100%,-50%)";break;case"topCenter":t.transform+="translate(-50%,0)";break;case"topRight":t.transform+="translate(-100%,0)"}}},dimensions(e,t){const a=e[vo]();let n=e.w;const s=e.h;if(a.layout?.includes("row")){const t=a[oo],s=e.colSpan;let r;if(-1===s){r=Math.sumPrecise(t.columnWidths.slice(t.currentColumn));t.currentColumn=0}else{r=Math.sumPrecise(t.columnWidths.slice(t.currentColumn,t.currentColumn+s));t.currentColumn=(t.currentColumn+e.colSpan)%t.columnWidths.length}isNaN(r)||(n=e.w=r)}t.width=""!==n?measureToString(n):"auto";t.height=""!==s?measureToString(s):"auto"},position(e,t){const a=e[vo]();if(!a?.layout||"position"===a.layout){t.position="absolute";t.left=measureToString(e.x);t.top=measureToString(e.y)}},rotate(e,t){if(e.rotate){"transform"in t||(t.transform="");t.transform+=`rotate(-${e.rotate}deg)`;t.transformOrigin="top left"}},presence(e,t){switch(e.presence){case"invisible":t.visibility="hidden";break;case"hidden":case"inactive":t.display="none"}},hAlign(e,t){if("para"===e[Uo])switch(e.hAlign){case"justifyAll":t.textAlign="justify-all";break;case"radix":t.textAlign="left";break;default:t.textAlign=e.hAlign}else switch(e.hAlign){case"left":t.alignSelf="start";break;case"center":t.alignSelf="center";break;case"right":t.alignSelf="end"}},margin(e,t){e.margin&&(t.margin=e.margin[fl]().margin)}};function setMinMaxDimensions(e,t){if("position"===e[vo]().layout){e.minW>0&&(t.minWidth=measureToString(e.minW));e.maxW>0&&(t.maxWidth=measureToString(e.maxW));e.minH>0&&(t.minHeight=measureToString(e.minH));e.maxH>0&&(t.maxHeight=measureToString(e.maxH))}}function layoutText(e,t,a,n,s,r){const i=new TextMeasure(t,a,n,s);"string"==typeof e?i.addString(e):e[Vo](i);return i.compute(r)}function layoutNode(e,t){let a=null,n=null,s=!1;if((!e.w||!e.h)&&e.value){let r=0,i=0;if(e.margin){r=e.margin.leftInset+e.margin.rightInset;i=e.margin.topInset+e.margin.bottomInset}let o=null,l=null;if(e.para){l=Object.create(null);o=""===e.para.lineHeight?null:e.para.lineHeight;l.top=""===e.para.spaceAbove?0:e.para.spaceAbove;l.bottom=""===e.para.spaceBelow?0:e.para.spaceBelow;l.left=""===e.para.marginLeft?0:e.para.marginLeft;l.right=""===e.para.marginRight?0:e.para.marginRight}let f=e.font;if(!f){const t=e[Ao]();let a=e[So]();for(;a&&a!==t;){if(a.font){f=a.font;break}a=a[So]()}}const c=(e.w||t.width)-r,h=e[xo].fontFinder;if(e.value.exData&&e.value.exData[so]&&"text/html"===e.value.exData.contentType){const t=layoutText(e.value.exData[so],f,l,o,h,c);n=t.width;a=t.height;s=t.isBroken}else{const t=e.value[rl]();if(t){const e=layoutText(t,f,l,o,h,c);n=e.width;a=e.height;s=e.isBroken}}null===n||e.w||(n+=r);null===a||e.h||(a+=i)}return{w:n,h:a,isBroken:s}}function computeBbox(e,t,a){let n;if(""!==e.w&&""!==e.h)n=[e.x,e.y,e.w,e.h];else{if(!a)return null;let s=e.w;if(""===s){if(0===e.maxW){const t=e[vo]();s="position"===t.layout&&""!==t.w?0:e.minW}else s=Math.min(e.maxW,a.width);t.attributes.style.width=measureToString(s)}let r=e.h;if(""===r){if(0===e.maxH){const t=e[vo]();r="position"===t.layout&&""!==t.h?0:e.minH}else r=Math.min(e.maxH,a.height);t.attributes.style.height=measureToString(r)}n=[e.x,e.y,s,r]}return n}function fixDimensions(e){const t=e[vo]();if(t.layout?.includes("row")){const a=t[oo],n=e.colSpan;let s;s=-1===n?Math.sumPrecise(a.columnWidths.slice(a.currentColumn)):Math.sumPrecise(a.columnWidths.slice(a.currentColumn,a.currentColumn+n));isNaN(s)||(e.w=s)}t.layout&&"position"!==t.layout&&(e.x=e.y=0);"table"===e.layout&&""===e.w&&Array.isArray(e.columnWidths)&&(e.w=Math.sumPrecise(e.columnWidths))}function layoutClass(e){switch(e.layout){case"position":default:return"xfaPosition";case"lr-tb":return"xfaLrTb";case"rl-row":return"xfaRlRow";case"rl-tb":return"xfaRlTb";case"row":return"xfaRow";case"table":return"xfaTable";case"tb":return"xfaTb"}}function toStyle(e,...t){const a=Object.create(null);for(const n of t){const t=e[n];if(null!==t)if(Object.hasOwn(Wl,n))Wl[n](e,a);else if(t instanceof XFAObject){const e=t[fl]();e?Object.assign(a,e):warn(`(DEBUG) - XFA - style for ${n} not implemented yet`)}}return a}function createWrapper(e,t){const{attributes:a}=t,{style:n}=a,s={name:"div",attributes:{class:["xfaWrapper"],style:Object.create(null)},children:[]};a.class.push("xfaWrapped");if(e.border){const{widths:a,insets:r}=e.border[oo];let i,o,l=r[0],f=r[3];const c=r[0]+r[2],h=r[1]+r[3];switch(e.border.hand){case"even":l-=a[0]/2;f-=a[3]/2;i=`calc(100% + ${(a[1]+a[3])/2-h}px)`;o=`calc(100% + ${(a[0]+a[2])/2-c}px)`;break;case"left":l-=a[0];f-=a[3];i=`calc(100% + ${a[1]+a[3]-h}px)`;o=`calc(100% + ${a[0]+a[2]-c}px)`;break;case"right":i=h?`calc(100% - ${h}px)`:"100%";o=c?`calc(100% - ${c}px)`:"100%"}const u=["xfaBorder"];isPrintOnly(e.border)&&u.push("xfaPrintOnly");const m={name:"div",attributes:{class:u,style:{top:`${l}px`,left:`${f}px`,width:i,height:o}},children:[]};for(const e of["border","borderWidth","borderColor","borderRadius","borderStyle"])if(void 0!==n[e]){m.attributes.style[e]=n[e];delete n[e]}s.children.push(m,t)}else s.children.push(t);for(const e of["background","backgroundClip","top","left","width","height","minWidth","minHeight","maxWidth","maxHeight","transform","transformOrigin","visibility"])if(void 0!==n[e]){s.attributes.style[e]=n[e];delete n[e]}s.attributes.style.position="absolute"===n.position?"absolute":"relative";delete n.position;if(n.alignSelf){s.attributes.style.alignSelf=n.alignSelf;delete n.alignSelf}return s}function fixTextIndent(e){const t=getMeasurement(e.textIndent,"0px");if(t>=0)return;const a="padding"+("left"===("right"===e.textAlign?"right":"left")?"Left":"Right"),n=getMeasurement(e[a],"0px");e[a]=n-t+"px"}function setAccess(e,t){switch(e.access){case"nonInteractive":t.push("xfaNonInteractive");break;case"readOnly":t.push("xfaReadOnly");break;case"protected":t.push("xfaDisabled")}}function isPrintOnly(e){return e.relevant.length>0&&!e.relevant[0].excluded&&"print"===e.relevant[0].viewname}function getCurrentPara(e){const t=e[Ao]()[oo].paraStack;return t.length?t.at(-1):null}function setPara(e,t,a){if(a.attributes.class?.includes("xfaRich")){if(t){""===e.h&&(t.height="auto");""===e.w&&(t.width="auto")}const n=getCurrentPara(e);if(n){const e=a.attributes.style;e.display="flex";e.flexDirection="column";switch(n.vAlign){case"top":e.justifyContent="start";break;case"bottom":e.justifyContent="end";break;case"middle":e.justifyContent="center"}const t=n[fl]();for(const[a,n]of Object.entries(t))a in e||(e[a]=n)}}}function setFontFamily(e,t,a,n){if(!a){delete n.fontFamily;return}const s=stripQuotes(e.typeface);n.fontFamily=`"${s}"`;const r=a.find(s);if(r){const{fontFamily:a}=r.regular.cssFontInfo;a!==s&&(n.fontFamily=`"${a}"`);const i=getCurrentPara(t);if(i&&""!==i.lineHeight)return;if(n.lineHeight)return;const o=selectFont(e,r);o&&(n.lineHeight=Math.max(1.2,o.lineHeight))}}function fixURL(e){const t=createValidAbsoluteUrl(e,null,{addDefaultProtocol:!0,tryConvertEncoding:!0});return t?t.href:null}function createLine(e,t){return{name:"div",attributes:{class:["lr-tb"===e.layout?"xfaLr":"xfaRl"]},children:t}}function flushHTML(e){if(!e[oo])return null;const t={name:"div",attributes:e[oo].attributes,children:e[oo].children};if(e[oo].failingNode){const a=e[oo].failingNode[fo]();a&&(e.layout.endsWith("-tb")?t.children.push(createLine(e,[a])):t.children.push(a))}return 0===t.children.length?null:t}function addHTML(e,t,a){const n=e[oo],s=n.availableSpace,[r,i,o,l]=a;switch(e.layout){case"position":n.width=Math.max(n.width,r+o);n.height=Math.max(n.height,i+l);n.children.push(t);break;case"lr-tb":case"rl-tb":if(!n.line||1===n.attempt){n.line=createLine(e,[]);n.children.push(n.line);n.numberInLine=0}n.numberInLine+=1;n.line.children.push(t);if(0===n.attempt){n.currentWidth+=o;n.height=Math.max(n.height,n.prevHeight+l)}else{n.currentWidth=o;n.prevHeight=n.height;n.height+=l;n.attempt=0}n.width=Math.max(n.width,n.currentWidth);break;case"rl-row":case"row":{n.children.push(t);n.width+=o;n.height=Math.max(n.height,l);const e=measureToString(n.height);for(const t of n.children)t.attributes.style.height=e;break}case"table":case"tb":n.width=MathClamp(o,n.width,s.width);n.height+=l;n.children.push(t)}}function getAvailableSpace(e){const t=e[oo].availableSpace,a=e.margin?e.margin.topInset+e.margin.bottomInset:0,n=e.margin?e.margin.leftInset+e.margin.rightInset:0;switch(e.layout){case"lr-tb":case"rl-tb":return 0===e[oo].attempt?{width:t.width-n-e[oo].currentWidth,height:t.height-a-e[oo].prevHeight}:{width:t.width-n,height:t.height-a-e[oo].height};case"rl-row":case"row":return{width:Math.sumPrecise(e[oo].columnWidths.slice(e[oo].currentColumn)),height:t.height-n};case"table":case"tb":return{width:t.width-n,height:t.height-a-e[oo].height};default:return t}}function checkDimensions(e,t){if(null===e[Ao]()[oo].firstUnsplittable)return!0;if(0===e.w||0===e.h)return!0;const a=e[vo](),n=a[oo]?.attempt||0,[,s,r,i]=function getTransformedBBox(e){let t,a,n=""===e.w?NaN:e.w,s=""===e.h?NaN:e.h,[r,i]=[0,0];switch(e.anchorType||""){case"bottomCenter":[r,i]=[n/2,s];break;case"bottomLeft":[r,i]=[0,s];break;case"bottomRight":[r,i]=[n,s];break;case"middleCenter":[r,i]=[n/2,s/2];break;case"middleLeft":[r,i]=[0,s/2];break;case"middleRight":[r,i]=[n,s/2];break;case"topCenter":[r,i]=[n/2,0];break;case"topRight":[r,i]=[n,0]}switch(e.rotate||0){case 0:[t,a]=[-r,-i];break;case 90:[t,a]=[-i,r];[n,s]=[s,-n];break;case 180:[t,a]=[r,i];[n,s]=[-n,-s];break;case 270:[t,a]=[i,-r];[n,s]=[-s,n]}return[e.x+t+Math.min(0,n),e.y+a+Math.min(0,s),Math.abs(n),Math.abs(s)]}(e);switch(a.layout){case"lr-tb":case"rl-tb":return 0===n?e[Ao]()[oo].noLayoutFailure?""!==e.w?Math.round(r-t.width)<=2:t.width>2:!(""!==e.h&&Math.round(i-t.height)>2)&&(""!==e.w?Math.round(r-t.width)<=2||0===a[oo].numberInLine&&t.height>2:t.width>2):!!e[Ao]()[oo].noLayoutFailure||!(""!==e.h&&Math.round(i-t.height)>2)&&((""===e.w||Math.round(r-t.width)<=2||!a[Po]())&&t.height>2);case"table":case"tb":return!!e[Ao]()[oo].noLayoutFailure||(""===e.h||e[No]()?(""===e.w||Math.round(r-t.width)<=2||!a[Po]())&&t.height>2:Math.round(i-t.height)<=2);case"position":if(e[Ao]()[oo].noLayoutFailure)return!0;if(""===e.h||Math.round(i+s-t.height)<=2)return!0;return i+s>e[Ao]()[oo].currentContentArea.h;case"rl-row":case"row":return!!e[Ao]()[oo].noLayoutFailure||(""===e.h||Math.round(i-t.height)<=2);default:return!0}}const Kl=ul.template.id,Gl="http://www.w3.org/2000/svg",Vl=/^H(\d+)$/,$l=new Set(["image/gif","image/jpeg","image/jpg","image/pjpeg","image/png","image/apng","image/x-png","image/bmp","image/x-ms-bmp","image/tiff","image/tif","application/octet-stream"]),Yl=[[[66,77],"image/bmp"],[[255,216,255],"image/jpeg"],[[73,73,42,0],"image/tiff"],[[77,77,0,42],"image/tiff"],[[71,73,70,56,57,97],"image/gif"],[[137,80,78,71,13,10,26,10],"image/png"]];function getBorderDims(e){if(!e||!e.border)return{w:0,h:0};const t=e.border[wo]();return t?{w:t.widths[0]+t.widths[2]+t.insets[0]+t.insets[2],h:t.widths[1]+t.widths[3]+t.insets[1]+t.insets[3]}:{w:0,h:0}}function hasMargin(e){return e.margin&&(e.margin.topInset||e.margin.rightInset||e.margin.bottomInset||e.margin.leftInset)}function _setValue(e,t){if(!e.value){const t=new Value({});e[Ji](t);e.value=t}e.value[nl](t)}function*getContainedChildren(e){for(const t of e[ko]())t instanceof SubformSet?yield*t[yo]():yield t}function isRequired(e){return"error"===e.validate?.nullTest}function setTabIndex(e){for(;e;){if(!e.traversal){e[sl]=e[So]()[sl];return}if(e[sl])return;let t=null;for(const a of e.traversal[ko]())if("next"===a.operation){t=a;break}if(!t||!t.ref){e[sl]=e[So]()[sl];return}const a=e[Ao]();e[sl]=++a[sl];const n=a[el](t.ref,e);if(!n)return;e=n[0]}}function applyAssist(e,t){const a=e.assist;if(a){const e=a[ol]();e&&(t.title=e);const n=a.role.match(Vl);if(n){const e="heading",a=n[1];t.role=e;t["aria-level"]=a}}if("table"===e.layout)t.role="table";else if("row"===e.layout)t.role="row";else{const a=e[So]();"row"===a.layout&&(t.role="TH"===a.assist?.role?"columnheader":"cell")}}function ariaLabel(e){if(!e.assist)return null;const t=e.assist;return t.speak&&""!==t.speak[so]?t.speak[so]:t.toolTip?t.toolTip[so]:null}function valueToHtml(e){return HTMLResult.success({name:"div",attributes:{class:["xfaRich"],style:Object.create(null)},children:[{name:"span",attributes:{style:Object.create(null)},value:e}]})}function setFirstUnsplittable(e){const t=e[Ao]();if(null===t[oo].firstUnsplittable){t[oo].firstUnsplittable=e;t[oo].noLayoutFailure=!0}}function unsetFirstUnsplittable(e){const t=e[Ao]();t[oo].firstUnsplittable===e&&(t[oo].noLayoutFailure=!1)}function handleBreak(e){if(e[oo])return!1;e[oo]=Object.create(null);if("auto"===e.targetType)return!1;const t=e[Ao]();let a=null;if(e.target){a=t[el](e.target,e[So]());if(!a)return!1;a=a[0]}const{currentPageArea:n,currentContentArea:s}=t[oo];if("pageArea"===e.targetType){a instanceof PageArea||(a=null);if(e.startNew){e[oo].target=a||n;return!0}if(a&&a!==n){e[oo].target=a;return!0}return!1}a instanceof ContentArea||(a=null);const r=a&&a[So]();let i,o=r;if(e.startNew)if(a){const e=r.contentArea.children,t=e.indexOf(s),n=e.indexOf(a);-1!==t&&te;n[oo].noLayoutFailure=!0;const i=t[ol](a);e[Yi](i.html,i.bbox);n[oo].noLayoutFailure=s;t[vo]=r}class AppearanceFilter extends StringObject{constructor(e){super(Kl,"appearanceFilter");this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||""}}class Arc extends XFAObject{constructor(e){super(Kl,"arc",!0);this.circular=getInteger({data:e.circular,defaultValue:0,validate:e=>1===e});this.hand=getStringOption(e.hand,["even","left","right"]);this.id=e.id||"";this.startAngle=getFloat({data:e.startAngle,defaultValue:0,validate:e=>!0});this.sweepAngle=getFloat({data:e.sweepAngle,defaultValue:360,validate:e=>!0});this.use=e.use||"";this.usehref=e.usehref||"";this.edge=null;this.fill=null}[ol](){const e=this.edge||new Edge({}),t=e[fl](),a=Object.create(null);"visible"===this.fill?.presence?Object.assign(a,this.fill[fl]()):a.fill="transparent";a.strokeWidth=measureToString("visible"===e.presence?e.thickness:0);a.stroke=t.color;let n;const s={xmlns:Gl,style:{width:"100%",height:"100%",overflow:"visible"}};if(360===this.sweepAngle)n={name:"ellipse",attributes:{xmlns:Gl,cx:"50%",cy:"50%",rx:"50%",ry:"50%",style:a}};else{const e=this.startAngle*Math.PI/180,t=this.sweepAngle*Math.PI/180,r=this.sweepAngle>180?1:0,[i,o,l,f]=[50*(1+Math.cos(e)),50*(1-Math.sin(e)),50*(1+Math.cos(e+t)),50*(1-Math.sin(e+t))];n={name:"path",attributes:{xmlns:Gl,d:`M ${i} ${o} A 50 50 0 ${r} 0 ${l} ${f}`,vectorEffect:"non-scaling-stroke",style:a}};Object.assign(s,{viewBox:"0 0 100 100",preserveAspectRatio:"none"})}const r={name:"svg",children:[n],attributes:s};if(hasMargin(this[So]()[So]()))return HTMLResult.success({name:"div",attributes:{style:{display:"inline",width:"100%",height:"100%"}},children:[r]});r.attributes.style.position="absolute";return HTMLResult.success(r)}}class Area extends XFAObject{constructor(e){super(Kl,"area",!0);this.colSpan=getInteger({data:e.colSpan,defaultValue:1,validate:e=>e>=1||-1===e});this.id=e.id||"";this.name=e.name||"";this.relevant=getRelevant(e.relevant);this.use=e.use||"";this.usehref=e.usehref||"";this.x=getMeasurement(e.x,"0pt");this.y=getMeasurement(e.y,"0pt");this.desc=null;this.extras=null;this.area=new XFAObjectArray;this.draw=new XFAObjectArray;this.exObject=new XFAObjectArray;this.exclGroup=new XFAObjectArray;this.field=new XFAObjectArray;this.subform=new XFAObjectArray;this.subformSet=new XFAObjectArray}*[yo](){yield*getContainedChildren(this)}[Eo](){return!0}[Ho](){return!0}[Yi](e,t){const[a,n,s,r]=t;this[oo].width=Math.max(this[oo].width,a+s);this[oo].height=Math.max(this[oo].height,n+r);this[oo].children.push(e)}[uo](){return this[oo].availableSpace}[ol](e){const t=toStyle(this,"position"),a={style:t,id:this[cl],class:["xfaArea"]};isPrintOnly(this)&&a.class.push("xfaPrintOnly");this.name&&(a.xfaName=this.name);const n=[];this[oo]={children:n,width:0,height:0,availableSpace:e};const s=this[Qi]({filter:new Set(["area","draw","field","exclGroup","subform","subformSet"]),include:!0});if(!s.success){if(s.isBreak())return s;delete this[oo];return HTMLResult.FAILURE}t.width=measureToString(this[oo].width);t.height=measureToString(this[oo].height);const r={name:"div",attributes:a,children:n},i=[this.x,this.y,this[oo].width,this[oo].height];delete this[oo];return HTMLResult.success(r,i)}}class Assist extends XFAObject{constructor(e){super(Kl,"assist",!0);this.id=e.id||"";this.role=e.role||"";this.use=e.use||"";this.usehref=e.usehref||"";this.speak=null;this.toolTip=null}[ol](){return this.toolTip?.[so]||null}}class Barcode extends XFAObject{constructor(e){super(Kl,"barcode",!0);this.charEncoding=getKeyword({data:e.charEncoding?e.charEncoding.toLowerCase():"",defaultValue:"",validate:e=>["utf-8","big-five","fontspecific","gbk","gb-18030","gb-2312","ksc-5601","none","shift-jis","ucs-2","utf-16"].includes(e)||e.match(/iso-8859-\d{2}/)});this.checksum=getStringOption(e.checksum,["none","1mod10","1mod10_1mod11","2mod10","auto"]);this.dataColumnCount=getInteger({data:e.dataColumnCount,defaultValue:-1,validate:e=>e>=0});this.dataLength=getInteger({data:e.dataLength,defaultValue:-1,validate:e=>e>=0});this.dataPrep=getStringOption(e.dataPrep,["none","flateCompress"]);this.dataRowCount=getInteger({data:e.dataRowCount,defaultValue:-1,validate:e=>e>=0});this.endChar=e.endChar||"";this.errorCorrectionLevel=getInteger({data:e.errorCorrectionLevel,defaultValue:-1,validate:e=>e>=0&&e<=8});this.id=e.id||"";this.moduleHeight=getMeasurement(e.moduleHeight,"5mm");this.moduleWidth=getMeasurement(e.moduleWidth,"0.25mm");this.printCheckDigit=getInteger({data:e.printCheckDigit,defaultValue:0,validate:e=>1===e});this.rowColumnRatio=getRatio(e.rowColumnRatio);this.startChar=e.startChar||"";this.textLocation=getStringOption(e.textLocation,["below","above","aboveEmbedded","belowEmbedded","none"]);this.truncate=getInteger({data:e.truncate,defaultValue:0,validate:e=>1===e});this.type=getStringOption(e.type?e.type.toLowerCase():"",["aztec","codabar","code2of5industrial","code2of5interleaved","code2of5matrix","code2of5standard","code3of9","code3of9extended","code11","code49","code93","code128","code128a","code128b","code128c","code128sscc","datamatrix","ean8","ean8add2","ean8add5","ean13","ean13add2","ean13add5","ean13pwcd","fim","logmars","maxicode","msi","pdf417","pdf417macro","plessey","postauscust2","postauscust3","postausreplypaid","postausstandard","postukrm4scc","postusdpbc","postusimb","postusstandard","postus5zip","qrcode","rfid","rss14","rss14expanded","rss14limited","rss14stacked","rss14stackedomni","rss14truncated","telepen","ucc128","ucc128random","ucc128sscc","upca","upcaadd2","upcaadd5","upcapwcd","upce","upceadd2","upceadd5","upcean2","upcean5","upsmaxicode"]);this.upsMode=getStringOption(e.upsMode,["usCarrier","internationalCarrier","secureSymbol","standardSymbol"]);this.use=e.use||"";this.usehref=e.usehref||"";this.wideNarrowRatio=getRatio(e.wideNarrowRatio);this.encrypt=null;this.extras=null}}class Bind extends XFAObject{constructor(e){super(Kl,"bind",!0);this.match=getStringOption(e.match,["once","dataRef","global","none"]);this.ref=e.ref||"";this.picture=null}}class BindItems extends XFAObject{constructor(e){super(Kl,"bindItems");this.connection=e.connection||"";this.labelRef=e.labelRef||"";this.ref=e.ref||"";this.valueRef=e.valueRef||""}}class Bookend extends XFAObject{constructor(e){super(Kl,"bookend");this.id=e.id||"";this.leader=e.leader||"";this.trailer=e.trailer||"";this.use=e.use||"";this.usehref=e.usehref||""}}class BooleanElement extends Option01{constructor(e){super(Kl,"boolean");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}[ol](e){return valueToHtml(1===this[so]?"1":"0")}}class Border extends XFAObject{constructor(e){super(Kl,"border",!0);this.break=getStringOption(e.break,["close","open"]);this.hand=getStringOption(e.hand,["even","left","right"]);this.id=e.id||"";this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.relevant=getRelevant(e.relevant);this.use=e.use||"";this.usehref=e.usehref||"";this.corner=new XFAObjectArray(4);this.edge=new XFAObjectArray(4);this.extras=null;this.fill=null;this.margin=null}[wo](){if(!this[oo]){const e=this.edge.children.slice();if(e.length<4){const t=e.at(-1)||new Edge({});for(let a=e.length;a<4;a++)e.push(t)}const t=e.map(e=>e.thickness),a=[0,0,0,0];if(this.margin){a[0]=this.margin.topInset;a[1]=this.margin.rightInset;a[2]=this.margin.bottomInset;a[3]=this.margin.leftInset}this[oo]={widths:t,insets:a,edges:e}}return this[oo]}[fl](){const{edges:e}=this[wo](),t=e.map(e=>{const t=e[fl]();t.color||="#000000";return t}),a=Object.create(null);this.margin&&Object.assign(a,this.margin[fl]());"visible"===this.fill?.presence&&Object.assign(a,this.fill[fl]());if(this.corner.children.some(e=>0!==e.radius)){const e=this.corner.children.map(e=>e[fl]());if(2===e.length||3===e.length){const t=e.at(-1);for(let a=e.length;a<4;a++)e.push(t)}a.borderRadius=e.map(e=>e.radius).join(" ")}switch(this.presence){case"invisible":case"hidden":a.borderStyle="";break;case"inactive":a.borderStyle="none";break;default:a.borderStyle=t.map(e=>e.style).join(" ")}a.borderWidth=t.map(e=>e.width).join(" ");a.borderColor=t.map(e=>e.color).join(" ");return a}}class Break extends XFAObject{constructor(e){super(Kl,"break",!0);this.after=getStringOption(e.after,["auto","contentArea","pageArea","pageEven","pageOdd"]);this.afterTarget=e.afterTarget||"";this.before=getStringOption(e.before,["auto","contentArea","pageArea","pageEven","pageOdd"]);this.beforeTarget=e.beforeTarget||"";this.bookendLeader=e.bookendLeader||"";this.bookendTrailer=e.bookendTrailer||"";this.id=e.id||"";this.overflowLeader=e.overflowLeader||"";this.overflowTarget=e.overflowTarget||"";this.overflowTrailer=e.overflowTrailer||"";this.startNew=getInteger({data:e.startNew,defaultValue:0,validate:e=>1===e});this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null}}class BreakAfter extends XFAObject{constructor(e){super(Kl,"breakAfter",!0);this.id=e.id||"";this.leader=e.leader||"";this.startNew=getInteger({data:e.startNew,defaultValue:0,validate:e=>1===e});this.target=e.target||"";this.targetType=getStringOption(e.targetType,["auto","contentArea","pageArea"]);this.trailer=e.trailer||"";this.use=e.use||"";this.usehref=e.usehref||"";this.script=null}}class BreakBefore extends XFAObject{constructor(e){super(Kl,"breakBefore",!0);this.id=e.id||"";this.leader=e.leader||"";this.startNew=getInteger({data:e.startNew,defaultValue:0,validate:e=>1===e});this.target=e.target||"";this.targetType=getStringOption(e.targetType,["auto","contentArea","pageArea"]);this.trailer=e.trailer||"";this.use=e.use||"";this.usehref=e.usehref||"";this.script=null}[ol](e){this[oo]={};return HTMLResult.FAILURE}}class Button extends XFAObject{constructor(e){super(Kl,"button",!0);this.highlight=getStringOption(e.highlight,["inverted","none","outline","push"]);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null}[ol](e){const t=this[So]()[So](),a={name:"button",attributes:{id:this[cl],class:["xfaButton"],style:{}},children:[]};for(const e of t.event.children){if("click"!==e.activity||!e.script)continue;const t=recoverJsURL(e.script[so]);if(!t)continue;const n=fixURL(t.url);n&&a.children.push({name:"a",attributes:{id:"link"+this[cl],href:n,newWindow:t.newWindow,class:["xfaLink"],style:{}},children:[]})}return HTMLResult.success(a)}}class Calculate extends XFAObject{constructor(e){super(Kl,"calculate",!0);this.id=e.id||"";this.override=getStringOption(e.override,["disabled","error","ignore","warning"]);this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.message=null;this.script=null}}class Caption extends XFAObject{constructor(e){super(Kl,"caption",!0);this.id=e.id||"";this.placement=getStringOption(e.placement,["left","bottom","inline","right","top"]);this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.reserve=Math.ceil(getMeasurement(e.reserve));this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.font=null;this.margin=null;this.para=null;this.value=null}[nl](e){_setValue(this,e)}[wo](e){if(!this[oo]){let{width:t,height:a}=e;switch(this.placement){case"left":case"right":case"inline":t=this.reserve<=0?t:this.reserve;break;case"top":case"bottom":a=this.reserve<=0?a:this.reserve}this[oo]=layoutNode(this,{width:t,height:a})}return this[oo]}[ol](e){if(!this.value)return HTMLResult.EMPTY;this[Yo]();const t=this.value[ol](e).html;if(!t){this[$o]();return HTMLResult.EMPTY}const a=this.reserve;if(this.reserve<=0){const{w:t,h:a}=this[wo](e);switch(this.placement){case"left":case"right":case"inline":this.reserve=t;break;case"top":case"bottom":this.reserve=a}}const n=[];"string"==typeof t?n.push({name:"#text",value:t}):n.push(t);const s=toStyle(this,"font","margin","visibility");switch(this.placement){case"left":case"right":this.reserve>0&&(s.width=measureToString(this.reserve));break;case"top":case"bottom":this.reserve>0&&(s.height=measureToString(this.reserve))}setPara(this,null,t);this[$o]();this.reserve=a;return HTMLResult.success({name:"div",attributes:{style:s,class:["xfaCaption"]},children:n})}}class Certificate extends StringObject{constructor(e){super(Kl,"certificate");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class Certificates extends XFAObject{constructor(e){super(Kl,"certificates",!0);this.credentialServerPolicy=getStringOption(e.credentialServerPolicy,["optional","required"]);this.id=e.id||"";this.url=e.url||"";this.urlPolicy=e.urlPolicy||"";this.use=e.use||"";this.usehref=e.usehref||"";this.encryption=null;this.issuers=null;this.keyUsage=null;this.oids=null;this.signing=null;this.subjectDNs=null}}class CheckButton extends XFAObject{constructor(e){super(Kl,"checkButton",!0);this.id=e.id||"";this.mark=getStringOption(e.mark,["default","check","circle","cross","diamond","square","star"]);this.shape=getStringOption(e.shape,["square","round"]);this.size=getMeasurement(e.size,"10pt");this.use=e.use||"";this.usehref=e.usehref||"";this.border=null;this.extras=null;this.margin=null}[ol](e){const t=toStyle(this,"margin"),a=measureToString(this.size);t.width=t.height=a;let n,s,r;const i=this[So]()[So](),o=i.items.children.length&&i.items.children[0][ol]().html||[],l={on:(void 0!==o[0]?o[0]:"on").toString(),off:(void 0!==o[1]?o[1]:"off").toString()},f=(i.value?.[rl]()||"off")===l.on||void 0,c=i[vo](),h=i[cl];let u;if(c instanceof ExclGroup){r=c[cl];n="radio";s="xfaRadio";u=c[ro]?.[cl]||c[cl]}else{n="checkbox";s="xfaCheckbox";u=i[ro]?.[cl]||i[cl]}const m={name:"input",attributes:{class:[s],style:t,fieldId:h,dataId:u,type:n,checked:f,xfaOn:l.on,xfaOff:l.off,"aria-label":ariaLabel(i),"aria-required":!1}};r&&(m.attributes.name=r);if(isRequired(i)){m.attributes["aria-required"]=!0;m.attributes.required=!0}return HTMLResult.success({name:"label",attributes:{class:["xfaLabel"]},children:[m]})}}class ChoiceList extends XFAObject{constructor(e){super(Kl,"choiceList",!0);this.commitOn=getStringOption(e.commitOn,["select","exit"]);this.id=e.id||"";this.open=getStringOption(e.open,["userControl","always","multiSelect","onEntry"]);this.textEntry=getInteger({data:e.textEntry,defaultValue:0,validate:e=>1===e});this.use=e.use||"";this.usehref=e.usehref||"";this.border=null;this.extras=null;this.margin=null}[ol](e){const t=toStyle(this,"border","margin"),a=this[So]()[So](),n={fontSize:`calc(${a.font?.size||10}px * var(--total-scale-factor))`},s=[];if(a.items.children.length>0){const e=a.items;let t=0,r=0;if(2===e.children.length){t=e.children[0].save;r=1-t}const i=e.children[t][ol]().html,o=e.children[r][ol]().html;let l=!1;const f=a.value?.[rl]()||"";for(let e=0,t=i.length;eMathClamp(parseInt(e.trim(),10),0,255)).map(e=>isNaN(e)?0:e);if(r.length<3)return{r:a,g:n,b:s};[a,n,s]=r;return{r:a,g:n,b:s}}(e.value):"";this.extras=null}[Co](){return!1}[fl](){return this.value?Util.makeHexColor(this.value.r,this.value.g,this.value.b):null}}class Comb extends XFAObject{constructor(e){super(Kl,"comb");this.id=e.id||"";this.numberOfCells=getInteger({data:e.numberOfCells,defaultValue:0,validate:e=>e>=0});this.use=e.use||"";this.usehref=e.usehref||""}}class Connect extends XFAObject{constructor(e){super(Kl,"connect",!0);this.connection=e.connection||"";this.id=e.id||"";this.ref=e.ref||"";this.usage=getStringOption(e.usage,["exportAndImport","exportOnly","importOnly"]);this.use=e.use||"";this.usehref=e.usehref||"";this.picture=null}}class ContentArea extends XFAObject{constructor(e){super(Kl,"contentArea",!0);this.h=getMeasurement(e.h);this.id=e.id||"";this.name=e.name||"";this.relevant=getRelevant(e.relevant);this.use=e.use||"";this.usehref=e.usehref||"";this.w=getMeasurement(e.w);this.x=getMeasurement(e.x,"0pt");this.y=getMeasurement(e.y,"0pt");this.desc=null;this.extras=null}[ol](e){const t={left:measureToString(this.x),top:measureToString(this.y),width:measureToString(this.w),height:measureToString(this.h)},a=["xfaContentarea"];isPrintOnly(this)&&a.push("xfaPrintOnly");return HTMLResult.success({name:"div",children:[],attributes:{style:t,class:a,id:this[cl]}})}}class Corner extends XFAObject{constructor(e){super(Kl,"corner",!0);this.id=e.id||"";this.inverted=getInteger({data:e.inverted,defaultValue:0,validate:e=>1===e});this.join=getStringOption(e.join,["square","round"]);this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.radius=getMeasurement(e.radius);this.stroke=getStringOption(e.stroke,["solid","dashDot","dashDotDot","dashed","dotted","embossed","etched","lowered","raised"]);this.thickness=getMeasurement(e.thickness,"0.5pt");this.use=e.use||"";this.usehref=e.usehref||"";this.color=null;this.extras=null}[fl](){const e=toStyle(this,"visibility");e.radius=measureToString("square"===this.join?0:this.radius);return e}}class DateElement extends ContentObject{constructor(e){super(Kl,"date");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}[lo](){const e=this[so].trim();this[so]=e?new Date(e):null}[ol](e){return valueToHtml(this[so]?this[so].toString():"")}}class DateTime extends ContentObject{constructor(e){super(Kl,"dateTime");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}[lo](){const e=this[so].trim();this[so]=e?new Date(e):null}[ol](e){return valueToHtml(this[so]?this[so].toString():"")}}class DateTimeEdit extends XFAObject{constructor(e){super(Kl,"dateTimeEdit",!0);this.hScrollPolicy=getStringOption(e.hScrollPolicy,["auto","off","on"]);this.id=e.id||"";this.picker=getStringOption(e.picker,["host","none"]);this.use=e.use||"";this.usehref=e.usehref||"";this.border=null;this.comb=null;this.extras=null;this.margin=null}[ol](e){const t=toStyle(this,"border","font","margin"),a=this[So]()[So](),n={name:"input",attributes:{type:"text",fieldId:a[cl],dataId:a[ro]?.[cl]||a[cl],class:["xfaTextfield"],style:t,"aria-label":ariaLabel(a),"aria-required":!1}};if(isRequired(a)){n.attributes["aria-required"]=!0;n.attributes.required=!0}return HTMLResult.success({name:"label",attributes:{class:["xfaLabel"]},children:[n]})}}class Decimal extends ContentObject{constructor(e){super(Kl,"decimal");this.fracDigits=getInteger({data:e.fracDigits,defaultValue:2,validate:e=>!0});this.id=e.id||"";this.leadDigits=getInteger({data:e.leadDigits,defaultValue:-1,validate:e=>!0});this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}[lo](){const e=parseFloat(this[so].trim());this[so]=isNaN(e)?null:e}[ol](e){return valueToHtml(null!==this[so]?this[so].toString():"")}}class DefaultUi extends XFAObject{constructor(e){super(Kl,"defaultUi",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null}}class Desc extends XFAObject{constructor(e){super(Kl,"desc",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.boolean=new XFAObjectArray;this.date=new XFAObjectArray;this.dateTime=new XFAObjectArray;this.decimal=new XFAObjectArray;this.exData=new XFAObjectArray;this.float=new XFAObjectArray;this.image=new XFAObjectArray;this.integer=new XFAObjectArray;this.text=new XFAObjectArray;this.time=new XFAObjectArray}}class DigestMethod extends OptionObject{constructor(e){super(Kl,"digestMethod",["","SHA1","SHA256","SHA512","RIPEMD160"]);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||""}}class DigestMethods extends XFAObject{constructor(e){super(Kl,"digestMethods",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.digestMethod=new XFAObjectArray}}class Draw extends XFAObject{constructor(e){super(Kl,"draw",!0);this.anchorType=getStringOption(e.anchorType,["topLeft","bottomCenter","bottomLeft","bottomRight","middleCenter","middleLeft","middleRight","topCenter","topRight"]);this.colSpan=getInteger({data:e.colSpan,defaultValue:1,validate:e=>e>=1||-1===e});this.h=e.h?getMeasurement(e.h):"";this.hAlign=getStringOption(e.hAlign,["left","center","justify","justifyAll","radix","right"]);this.id=e.id||"";this.locale=e.locale||"";this.maxH=getMeasurement(e.maxH,"0pt");this.maxW=getMeasurement(e.maxW,"0pt");this.minH=getMeasurement(e.minH,"0pt");this.minW=getMeasurement(e.minW,"0pt");this.name=e.name||"";this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.relevant=getRelevant(e.relevant);this.rotate=getInteger({data:e.rotate,defaultValue:0,validate:e=>e%90==0});this.use=e.use||"";this.usehref=e.usehref||"";this.w=e.w?getMeasurement(e.w):"";this.x=getMeasurement(e.x,"0pt");this.y=getMeasurement(e.y,"0pt");this.assist=null;this.border=null;this.caption=null;this.desc=null;this.extras=null;this.font=null;this.keep=null;this.margin=null;this.para=null;this.traversal=null;this.ui=null;this.value=null;this.setProperty=new XFAObjectArray}[nl](e){_setValue(this,e)}[ol](e){setTabIndex(this);if("hidden"===this.presence||"inactive"===this.presence)return HTMLResult.EMPTY;fixDimensions(this);this[Yo]();const t=this.w,a=this.h,{w:n,h:s,isBroken:r}=layoutNode(this,e);if(n&&""===this.w){if(r&&this[vo]()[Po]()){this[$o]();return HTMLResult.FAILURE}this.w=n}s&&""===this.h&&(this.h=s);setFirstUnsplittable(this);if(!checkDimensions(this,e)){this.w=t;this.h=a;this[$o]();return HTMLResult.FAILURE}unsetFirstUnsplittable(this);const i=toStyle(this,"font","hAlign","dimensions","position","presence","rotate","anchorType","border","margin");setMinMaxDimensions(this,i);if(i.margin){i.padding=i.margin;delete i.margin}const o=["xfaDraw"];this.font&&o.push("xfaFont");isPrintOnly(this)&&o.push("xfaPrintOnly");const l={style:i,id:this[cl],class:o};this.name&&(l.xfaName=this.name);const f={name:"div",attributes:l,children:[]};applyAssist(this,l);const c=computeBbox(this,f,e),h=this.value?this.value[ol](e).html:null;if(null===h){this.w=t;this.h=a;this[$o]();return HTMLResult.success(createWrapper(this,f),c)}f.children.push(h);setPara(this,i,h);this.w=t;this.h=a;this[$o]();return HTMLResult.success(createWrapper(this,f),c)}}class Edge extends XFAObject{constructor(e){super(Kl,"edge",!0);this.cap=getStringOption(e.cap,["square","butt","round"]);this.id=e.id||"";this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.stroke=getStringOption(e.stroke,["solid","dashDot","dashDotDot","dashed","dotted","embossed","etched","lowered","raised"]);this.thickness=getMeasurement(e.thickness,"0.5pt");this.use=e.use||"";this.usehref=e.usehref||"";this.color=null;this.extras=null}[fl](){const e=toStyle(this,"visibility");Object.assign(e,{linecap:this.cap,width:measureToString(this.thickness),color:this.color?this.color[fl]():"#000000",style:""});if("visible"!==this.presence)e.style="none";else switch(this.stroke){case"solid":e.style="solid";break;case"dashDot":case"dashDotDot":case"dashed":e.style="dashed";break;case"dotted":e.style="dotted";break;case"embossed":e.style="ridge";break;case"etched":e.style="groove";break;case"lowered":e.style="inset";break;case"raised":e.style="outset"}return e}}class Encoding extends OptionObject{constructor(e){super(Kl,"encoding",["adbe.x509.rsa_sha1","adbe.pkcs7.detached","adbe.pkcs7.sha1"]);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||""}}class Encodings extends XFAObject{constructor(e){super(Kl,"encodings",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.encoding=new XFAObjectArray}}class Encrypt extends XFAObject{constructor(e){super(Kl,"encrypt",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.certificate=null}}class EncryptData extends XFAObject{constructor(e){super(Kl,"encryptData",!0);this.id=e.id||"";this.operation=getStringOption(e.operation,["encrypt","decrypt"]);this.target=e.target||"";this.use=e.use||"";this.usehref=e.usehref||"";this.filter=null;this.manifest=null}}class Encryption extends XFAObject{constructor(e){super(Kl,"encryption",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.certificate=new XFAObjectArray}}class EncryptionMethod extends OptionObject{constructor(e){super(Kl,"encryptionMethod",["","AES256-CBC","TRIPLEDES-CBC","AES128-CBC","AES192-CBC"]);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||""}}class EncryptionMethods extends XFAObject{constructor(e){super(Kl,"encryptionMethods",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.encryptionMethod=new XFAObjectArray}}class Event extends XFAObject{constructor(e){super(Kl,"event",!0);this.activity=getStringOption(e.activity,["click","change","docClose","docReady","enter","exit","full","indexChange","initialize","mouseDown","mouseEnter","mouseExit","mouseUp","postExecute","postOpen","postPrint","postSave","postSign","postSubmit","preExecute","preOpen","prePrint","preSave","preSign","preSubmit","ready","validationState"]);this.id=e.id||"";this.listen=getStringOption(e.listen,["refOnly","refAndDescendents"]);this.name=e.name||"";this.ref=e.ref||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.encryptData=null;this.execute=null;this.script=null;this.signData=null;this.submit=null}}class ExData extends ContentObject{constructor(e){super(Kl,"exData");this.contentType=e.contentType||"";this.href=e.href||"";this.id=e.id||"";this.maxLength=getInteger({data:e.maxLength,defaultValue:-1,validate:e=>e>=-1});this.name=e.name||"";this.rid=e.rid||"";this.transferEncoding=getStringOption(e.transferEncoding,["none","base64","package"]);this.use=e.use||"";this.usehref=e.usehref||""}[Ro](){return"text/html"===this.contentType}[Wo](e){if("text/html"===this.contentType&&e[Lo]===ul.xhtml.id){this[so]=e;return!0}if("text/xml"===this.contentType){this[so]=e;return!0}return!1}[ol](e){return"text/html"===this.contentType&&this[so]?this[so][ol](e):HTMLResult.EMPTY}}class ExObject extends XFAObject{constructor(e){super(Kl,"exObject",!0);this.archive=e.archive||"";this.classId=e.classId||"";this.codeBase=e.codeBase||"";this.codeType=e.codeType||"";this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.boolean=new XFAObjectArray;this.date=new XFAObjectArray;this.dateTime=new XFAObjectArray;this.decimal=new XFAObjectArray;this.exData=new XFAObjectArray;this.exObject=new XFAObjectArray;this.float=new XFAObjectArray;this.image=new XFAObjectArray;this.integer=new XFAObjectArray;this.text=new XFAObjectArray;this.time=new XFAObjectArray}}class ExclGroup extends XFAObject{constructor(e){super(Kl,"exclGroup",!0);this.access=getStringOption(e.access,["open","nonInteractive","protected","readOnly"]);this.accessKey=e.accessKey||"";this.anchorType=getStringOption(e.anchorType,["topLeft","bottomCenter","bottomLeft","bottomRight","middleCenter","middleLeft","middleRight","topCenter","topRight"]);this.colSpan=getInteger({data:e.colSpan,defaultValue:1,validate:e=>e>=1||-1===e});this.h=e.h?getMeasurement(e.h):"";this.hAlign=getStringOption(e.hAlign,["left","center","justify","justifyAll","radix","right"]);this.id=e.id||"";this.layout=getStringOption(e.layout,["position","lr-tb","rl-row","rl-tb","row","table","tb"]);this.maxH=getMeasurement(e.maxH,"0pt");this.maxW=getMeasurement(e.maxW,"0pt");this.minH=getMeasurement(e.minH,"0pt");this.minW=getMeasurement(e.minW,"0pt");this.name=e.name||"";this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.relevant=getRelevant(e.relevant);this.use=e.use||"";this.usehref=e.usehref||"";this.w=e.w?getMeasurement(e.w):"";this.x=getMeasurement(e.x,"0pt");this.y=getMeasurement(e.y,"0pt");this.assist=null;this.bind=null;this.border=null;this.calculate=null;this.caption=null;this.desc=null;this.extras=null;this.margin=null;this.para=null;this.traversal=null;this.validate=null;this.connect=new XFAObjectArray;this.event=new XFAObjectArray;this.field=new XFAObjectArray;this.setProperty=new XFAObjectArray}[Ho](){return!0}[Co](){return!0}[nl](e){for(const t of this.field.children){if(!t.value){const e=new Value({});t[Ji](e);t.value=e}t.value[nl](e)}}[Po](){return this.layout.endsWith("-tb")&&0===this[oo].attempt&&this[oo].numberInLine>0||this[So]()[Po]()}[No](){const e=this[vo]();if(!e[No]())return!1;if(void 0!==this[oo]._isSplittable)return this[oo]._isSplittable;if("position"===this.layout||this.layout.includes("row")){this[oo]._isSplittable=!1;return!1}if(e.layout?.endsWith("-tb")&&0!==e[oo].numberInLine)return!1;this[oo]._isSplittable=!0;return!0}[fo](){return flushHTML(this)}[Yi](e,t){addHTML(this,e,t)}[uo](){return getAvailableSpace(this)}[ol](e){setTabIndex(this);if("hidden"===this.presence||"inactive"===this.presence||0===this.h||0===this.w)return HTMLResult.EMPTY;fixDimensions(this);const t=[],a={id:this[cl],class:[]};setAccess(this,a.class);this[oo]||=Object.create(null);Object.assign(this[oo],{children:t,attributes:a,attempt:0,line:null,numberInLine:0,availableSpace:{width:Math.min(this.w||1/0,e.width),height:Math.min(this.h||1/0,e.height)},width:0,height:0,prevHeight:0,currentWidth:0});const n=this[No]();n||setFirstUnsplittable(this);if(!checkDimensions(this,e))return HTMLResult.FAILURE;const s=new Set(["field"]);if(this.layout.includes("row")){const e=this[vo]().columnWidths;if(Array.isArray(e)&&e.length>0){this[oo].columnWidths=e;this[oo].currentColumn=0}}const r=toStyle(this,"anchorType","dimensions","position","presence","border","margin","hAlign"),i=["xfaExclgroup"],o=layoutClass(this);o&&i.push(o);isPrintOnly(this)&&i.push("xfaPrintOnly");a.style=r;a.class=i;this.name&&(a.xfaName=this.name);this[Yo]();const l="lr-tb"===this.layout||"rl-tb"===this.layout,f=l?2:1;for(;this[oo].attempte>=1||-1===e});this.h=e.h?getMeasurement(e.h):"";this.hAlign=getStringOption(e.hAlign,["left","center","justify","justifyAll","radix","right"]);this.id=e.id||"";this.locale=e.locale||"";this.maxH=getMeasurement(e.maxH,"0pt");this.maxW=getMeasurement(e.maxW,"0pt");this.minH=getMeasurement(e.minH,"0pt");this.minW=getMeasurement(e.minW,"0pt");this.name=e.name||"";this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.relevant=getRelevant(e.relevant);this.rotate=getInteger({data:e.rotate,defaultValue:0,validate:e=>e%90==0});this.use=e.use||"";this.usehref=e.usehref||"";this.w=e.w?getMeasurement(e.w):"";this.x=getMeasurement(e.x,"0pt");this.y=getMeasurement(e.y,"0pt");this.assist=null;this.bind=null;this.border=null;this.calculate=null;this.caption=null;this.desc=null;this.extras=null;this.font=null;this.format=null;this.items=new XFAObjectArray(2);this.keep=null;this.margin=null;this.para=null;this.traversal=null;this.ui=null;this.validate=null;this.value=null;this.bindItems=new XFAObjectArray;this.connect=new XFAObjectArray;this.event=new XFAObjectArray;this.setProperty=new XFAObjectArray}[Ho](){return!0}[nl](e){_setValue(this,e)}[ol](e){setTabIndex(this);if(!this.ui){this.ui=new Ui({});this.ui[xo]=this[xo];this[Ji](this.ui);let e;switch(this.items.children.length){case 0:e=new TextEdit({});this.ui.textEdit=e;break;case 1:e=new CheckButton({});this.ui.checkButton=e;break;case 2:e=new ChoiceList({});this.ui.choiceList=e}this.ui[Ji](e)}if(!this.ui||"hidden"===this.presence||"inactive"===this.presence||0===this.h||0===this.w)return HTMLResult.EMPTY;this.caption&&delete this.caption[oo];this[Yo]();const t=this.caption?this.caption[ol](e).html:null,a=this.w,n=this.h;let s=0,r=0;if(this.margin){s=this.margin.leftInset+this.margin.rightInset;r=this.margin.topInset+this.margin.bottomInset}let i=null;if(""===this.w||""===this.h){let t=null,a=null,n=0,o=0;if(this.ui.checkButton)n=o=this.ui.checkButton.size;else{const{w:t,h:a}=layoutNode(this,e);if(null!==t){n=t;o=a}else o=function fonts_getMetrics(e,t=!1){let a=null;if(e){const t=stripQuotes(e.typeface),n=e[xo].fontFinder.find(t);a=selectFont(e,n)}if(!a)return{lineHeight:12,lineGap:2,lineNoGap:10};const n=e.size||10,s=a.lineHeight?Math.max(t?0:1.2,a.lineHeight):1.2,r=void 0===a.lineGap?.2:a.lineGap;return{lineHeight:s*n,lineGap:r*n,lineNoGap:Math.max(1,s-r)*n}}(this.font,!0).lineNoGap}i=getBorderDims(this.ui[wo]());n+=i.w;o+=i.h;if(this.caption){const{w:s,h:r,isBroken:i}=this.caption[wo](e);if(i&&this[vo]()[Po]()){this[$o]();return HTMLResult.FAILURE}t=s;a=r;switch(this.caption.placement){case"left":case"right":case"inline":t+=n;break;case"top":case"bottom":a+=o}}else{t=n;a=o}if(t&&""===this.w){t+=s;this.w=Math.min(this.maxW<=0?1/0:this.maxW,this.minW+1e>=1&&e<=5});this.appearanceFilter=null;this.certificates=null;this.digestMethods=null;this.encodings=null;this.encryptionMethods=null;this.handler=null;this.lockDocument=null;this.mdp=null;this.reasons=null;this.timeStamp=null}}class Float extends ContentObject{constructor(e){super(Kl,"float");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}[lo](){const e=parseFloat(this[so].trim());this[so]=isNaN(e)?null:e}[ol](e){return valueToHtml(null!==this[so]?this[so].toString():"")}}class template_Font extends XFAObject{constructor(e){super(Kl,"font",!0);this.baselineShift=getMeasurement(e.baselineShift);this.fontHorizontalScale=getFloat({data:e.fontHorizontalScale,defaultValue:100,validate:e=>e>=0});this.fontVerticalScale=getFloat({data:e.fontVerticalScale,defaultValue:100,validate:e=>e>=0});this.id=e.id||"";this.kerningMode=getStringOption(e.kerningMode,["none","pair"]);this.letterSpacing=getMeasurement(e.letterSpacing,"0");this.lineThrough=getInteger({data:e.lineThrough,defaultValue:0,validate:e=>1===e||2===e});this.lineThroughPeriod=getStringOption(e.lineThroughPeriod,["all","word"]);this.overline=getInteger({data:e.overline,defaultValue:0,validate:e=>1===e||2===e});this.overlinePeriod=getStringOption(e.overlinePeriod,["all","word"]);this.posture=getStringOption(e.posture,["normal","italic"]);this.size=getMeasurement(e.size,"10pt");this.typeface=e.typeface||"Courier";this.underline=getInteger({data:e.underline,defaultValue:0,validate:e=>1===e||2===e});this.underlinePeriod=getStringOption(e.underlinePeriod,["all","word"]);this.use=e.use||"";this.usehref=e.usehref||"";this.weight=getStringOption(e.weight,["normal","bold"]);this.extras=null;this.fill=null}[Zi](e){super[Zi](e);this[xo].usedTypefaces.add(this.typeface)}[fl](){const e=toStyle(this,"fill"),t=e.color;if(t)if("#000000"===t)delete e.color;else if(!t.startsWith("#")){e.background=t;e.backgroundClip="text";e.color="transparent"}this.baselineShift&&(e.verticalAlign=measureToString(this.baselineShift));e.fontKerning="none"===this.kerningMode?"none":"normal";e.letterSpacing=measureToString(this.letterSpacing);if(0!==this.lineThrough){e.textDecoration="line-through";2===this.lineThrough&&(e.textDecorationStyle="double")}if(0!==this.overline){e.textDecoration="overline";2===this.overline&&(e.textDecorationStyle="double")}e.fontStyle=this.posture;e.fontSize=measureToString(.99*this.size);setFontFamily(this,this,this[xo].fontFinder,e);if(0!==this.underline){e.textDecoration="underline";2===this.underline&&(e.textDecorationStyle="double")}e.fontWeight=this.weight;return e}}class Format extends XFAObject{constructor(e){super(Kl,"format",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.picture=null}}class Handler extends StringObject{constructor(e){super(Kl,"handler");this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||""}}class Hyphenation extends XFAObject{constructor(e){super(Kl,"hyphenation");this.excludeAllCaps=getInteger({data:e.excludeAllCaps,defaultValue:0,validate:e=>1===e});this.excludeInitialCap=getInteger({data:e.excludeInitialCap,defaultValue:0,validate:e=>1===e});this.hyphenate=getInteger({data:e.hyphenate,defaultValue:0,validate:e=>1===e});this.id=e.id||"";this.pushCharacterCount=getInteger({data:e.pushCharacterCount,defaultValue:3,validate:e=>e>=0});this.remainCharacterCount=getInteger({data:e.remainCharacterCount,defaultValue:3,validate:e=>e>=0});this.use=e.use||"";this.usehref=e.usehref||"";this.wordCharacterCount=getInteger({data:e.wordCharacterCount,defaultValue:7,validate:e=>e>=0})}}class Image extends StringObject{constructor(e){super(Kl,"image");this.aspect=getStringOption(e.aspect,["fit","actual","height","none","width"]);this.contentType=e.contentType||"";this.href=e.href||"";this.id=e.id||"";this.name=e.name||"";this.transferEncoding=getStringOption(e.transferEncoding,["base64","none","package"]);this.use=e.use||"";this.usehref=e.usehref||""}[ol](){if(this.contentType&&!$l.has(this.contentType.toLowerCase()))return HTMLResult.EMPTY;let e=this[xo].images?.get(this.href);if(!e&&(this.href||!this[so]))return HTMLResult.EMPTY;e||"base64"!==this.transferEncoding||(e=Uint8Array.fromBase64(this[so]));if(!e)return HTMLResult.EMPTY;if(!this.contentType){for(const[t,a]of Yl)if(e.length>t.length&&t.every((t,a)=>t===e[a])){this.contentType=a;break}if(!this.contentType)return HTMLResult.EMPTY}const t=new Blob([e],{type:this.contentType});let a;switch(this.aspect){case"fit":case"actual":break;case"height":a={height:"100%",objectFit:"fill"};break;case"none":a={width:"100%",height:"100%",objectFit:"fill"};break;case"width":a={width:"100%",objectFit:"fill"}}const n=this[So]();return HTMLResult.success({name:"img",attributes:{class:["xfaImage"],style:a,src:URL.createObjectURL(t),alt:n?ariaLabel(n[So]()):null}})}}class ImageEdit extends XFAObject{constructor(e){super(Kl,"imageEdit",!0);this.data=getStringOption(e.data,["link","embed"]);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.border=null;this.extras=null;this.margin=null}[ol](e){return"embed"===this.data?HTMLResult.success({name:"div",children:[],attributes:{}}):HTMLResult.EMPTY}}class Integer extends ContentObject{constructor(e){super(Kl,"integer");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}[lo](){const e=parseInt(this[so].trim(),10);this[so]=isNaN(e)?null:e}[ol](e){return valueToHtml(null!==this[so]?this[so].toString():"")}}class Issuers extends XFAObject{constructor(e){super(Kl,"issuers",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.certificate=new XFAObjectArray}}class Items extends XFAObject{constructor(e){super(Kl,"items",!0);this.id=e.id||"";this.name=e.name||"";this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.ref=e.ref||"";this.save=getInteger({data:e.save,defaultValue:0,validate:e=>1===e});this.use=e.use||"";this.usehref=e.usehref||"";this.boolean=new XFAObjectArray;this.date=new XFAObjectArray;this.dateTime=new XFAObjectArray;this.decimal=new XFAObjectArray;this.exData=new XFAObjectArray;this.float=new XFAObjectArray;this.image=new XFAObjectArray;this.integer=new XFAObjectArray;this.text=new XFAObjectArray;this.time=new XFAObjectArray}[ol](){const e=[];for(const t of this[ko]())e.push(t[rl]());return HTMLResult.success(e)}}class Keep extends XFAObject{constructor(e){super(Kl,"keep",!0);this.id=e.id||"";const t=["none","contentArea","pageArea"];this.intact=getStringOption(e.intact,t);this.next=getStringOption(e.next,t);this.previous=getStringOption(e.previous,t);this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null}}class KeyUsage extends XFAObject{constructor(e){super(Kl,"keyUsage");const t=["","yes","no"];this.crlSign=getStringOption(e.crlSign,t);this.dataEncipherment=getStringOption(e.dataEncipherment,t);this.decipherOnly=getStringOption(e.decipherOnly,t);this.digitalSignature=getStringOption(e.digitalSignature,t);this.encipherOnly=getStringOption(e.encipherOnly,t);this.id=e.id||"";this.keyAgreement=getStringOption(e.keyAgreement,t);this.keyCertSign=getStringOption(e.keyCertSign,t);this.keyEncipherment=getStringOption(e.keyEncipherment,t);this.nonRepudiation=getStringOption(e.nonRepudiation,t);this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||""}}class Line extends XFAObject{constructor(e){super(Kl,"line",!0);this.hand=getStringOption(e.hand,["even","left","right"]);this.id=e.id||"";this.slope=getStringOption(e.slope,["\\","/"]);this.use=e.use||"";this.usehref=e.usehref||"";this.edge=null}[ol](){const e=this[So]()[So](),t=this.edge||new Edge({}),a=t[fl](),n=Object.create(null),s="visible"===t.presence?t.thickness:0;n.strokeWidth=measureToString(s);n.stroke=a.color;let r,i,o,l,f="100%",c="100%";if(e.w<=s){[r,i,o,l]=["50%",0,"50%","100%"];f=n.strokeWidth}else if(e.h<=s){[r,i,o,l]=[0,"50%","100%","50%"];c=n.strokeWidth}else"\\"===this.slope?[r,i,o,l]=[0,0,"100%","100%"]:[r,i,o,l]=[0,"100%","100%",0];const h={name:"svg",children:[{name:"line",attributes:{xmlns:Gl,x1:r,y1:i,x2:o,y2:l,style:n}}],attributes:{xmlns:Gl,width:f,height:c,style:{overflow:"visible"}}};if(hasMargin(e))return HTMLResult.success({name:"div",attributes:{style:{display:"inline",width:"100%",height:"100%"}},children:[h]});h.attributes.style.position="absolute";return HTMLResult.success(h)}}class Linear extends XFAObject{constructor(e){super(Kl,"linear",!0);this.id=e.id||"";this.type=getStringOption(e.type,["toRight","toBottom","toLeft","toTop"]);this.use=e.use||"";this.usehref=e.usehref||"";this.color=null;this.extras=null}[fl](e){e=e?e[fl]():"#FFFFFF";return`linear-gradient(${this.type.replace(/([RBLT])/," $1").toLowerCase()}, ${e}, ${this.color?this.color[fl]():"#000000"})`}}class LockDocument extends ContentObject{constructor(e){super(Kl,"lockDocument");this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||""}[lo](){this[so]=getStringOption(this[so],["auto","0","1"])}}class Manifest extends XFAObject{constructor(e){super(Kl,"manifest",!0);this.action=getStringOption(e.action,["include","all","exclude"]);this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.ref=new XFAObjectArray}}class Margin extends XFAObject{constructor(e){super(Kl,"margin",!0);this.bottomInset=getMeasurement(e.bottomInset,"0");this.id=e.id||"";this.leftInset=getMeasurement(e.leftInset,"0");this.rightInset=getMeasurement(e.rightInset,"0");this.topInset=getMeasurement(e.topInset,"0");this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null}[fl](){return{margin:measureToString(this.topInset)+" "+measureToString(this.rightInset)+" "+measureToString(this.bottomInset)+" "+measureToString(this.leftInset)}}}class Mdp extends XFAObject{constructor(e){super(Kl,"mdp");this.id=e.id||"";this.permissions=getInteger({data:e.permissions,defaultValue:2,validate:e=>1===e||3===e});this.signatureType=getStringOption(e.signatureType,["filler","author"]);this.use=e.use||"";this.usehref=e.usehref||""}}class Medium extends XFAObject{constructor(e){super(Kl,"medium");this.id=e.id||"";this.imagingBBox=function getBBox(e){const t=-1;if(!e)return{x:t,y:t,width:t,height:t};const a=e.split(",",4).map(e=>getMeasurement(e.trim(),"-1"));if(a.length<4||a[2]<0||a[3]<0)return{x:t,y:t,width:t,height:t};const[n,s,r,i]=a;return{x:n,y:s,width:r,height:i}}(e.imagingBBox);this.long=getMeasurement(e.long);this.orientation=getStringOption(e.orientation,["portrait","landscape"]);this.short=getMeasurement(e.short);this.stock=e.stock||"";this.trayIn=getStringOption(e.trayIn,["auto","delegate","pageFront"]);this.trayOut=getStringOption(e.trayOut,["auto","delegate"]);this.use=e.use||"";this.usehref=e.usehref||""}}class Message extends XFAObject{constructor(e){super(Kl,"message",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.text=new XFAObjectArray}}class NumericEdit extends XFAObject{constructor(e){super(Kl,"numericEdit",!0);this.hScrollPolicy=getStringOption(e.hScrollPolicy,["auto","off","on"]);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.border=null;this.comb=null;this.extras=null;this.margin=null}[ol](e){const t=toStyle(this,"border","font","margin"),a=this[So]()[So](),n={name:"input",attributes:{type:"text",fieldId:a[cl],dataId:a[ro]?.[cl]||a[cl],class:["xfaTextfield"],style:t,"aria-label":ariaLabel(a),"aria-required":!1}};if(isRequired(a)){n.attributes["aria-required"]=!0;n.attributes.required=!0}return HTMLResult.success({name:"label",attributes:{class:["xfaLabel"]},children:[n]})}}class Occur extends XFAObject{constructor(e){super(Kl,"occur",!0);this.id=e.id||"";this.initial=""!==e.initial?getInteger({data:e.initial,defaultValue:"",validate:e=>!0}):"";this.max=""!==e.max?getInteger({data:e.max,defaultValue:-1,validate:e=>!0}):"";this.min=""!==e.min?getInteger({data:e.min,defaultValue:1,validate:e=>!0}):"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null}[Zi](){const e=this[So](),t=this.min;""===this.min&&(this.min=e instanceof PageArea||e instanceof PageSet?0:1);""===this.max&&(this.max=""===t?e instanceof PageArea||e instanceof PageSet?-1:1:this.min);-1!==this.max&&this.max!0});this.name=e.name||"";this.numbered=getInteger({data:e.numbered,defaultValue:1,validate:e=>!0});this.oddOrEven=getStringOption(e.oddOrEven,["any","even","odd"]);this.pagePosition=getStringOption(e.pagePosition,["any","first","last","only","rest"]);this.relevant=getRelevant(e.relevant);this.use=e.use||"";this.usehref=e.usehref||"";this.desc=null;this.extras=null;this.medium=null;this.occur=null;this.area=new XFAObjectArray;this.contentArea=new XFAObjectArray;this.draw=new XFAObjectArray;this.exclGroup=new XFAObjectArray;this.field=new XFAObjectArray;this.subform=new XFAObjectArray}[_o](){if(!this[oo]){this[oo]={numberOfUse:0};return!0}return!this.occur||-1===this.occur.max||this[oo].numberOfUsee.oddOrEven===t&&e.pagePosition===a);if(n)return n;n=this.pageArea.children.find(e=>"any"===e.oddOrEven&&e.pagePosition===a);if(n)return n;n=this.pageArea.children.find(e=>"any"===e.oddOrEven&&"any"===e.pagePosition);return n||this.pageArea.children[0]}}class Para extends XFAObject{constructor(e){super(Kl,"para",!0);this.hAlign=getStringOption(e.hAlign,["left","center","justify","justifyAll","radix","right"]);this.id=e.id||"";this.lineHeight=e.lineHeight?getMeasurement(e.lineHeight,"0pt"):"";this.marginLeft=e.marginLeft?getMeasurement(e.marginLeft,"0pt"):"";this.marginRight=e.marginRight?getMeasurement(e.marginRight,"0pt"):"";this.orphans=getInteger({data:e.orphans,defaultValue:0,validate:e=>e>=0});this.preserve=e.preserve||"";this.radixOffset=e.radixOffset?getMeasurement(e.radixOffset,"0pt"):"";this.spaceAbove=e.spaceAbove?getMeasurement(e.spaceAbove,"0pt"):"";this.spaceBelow=e.spaceBelow?getMeasurement(e.spaceBelow,"0pt"):"";this.tabDefault=e.tabDefault?getMeasurement(this.tabDefault):"";this.tabStops=(e.tabStops||"").trim().split(/\s+/).map((e,t)=>t%2==1?getMeasurement(e):e);this.textIndent=e.textIndent?getMeasurement(e.textIndent,"0pt"):"";this.use=e.use||"";this.usehref=e.usehref||"";this.vAlign=getStringOption(e.vAlign,["top","bottom","middle"]);this.widows=getInteger({data:e.widows,defaultValue:0,validate:e=>e>=0});this.hyphenation=null}[fl](){const e=toStyle(this,"hAlign");""!==this.marginLeft&&(e.paddingLeft=measureToString(this.marginLeft));""!==this.marginRight&&(e.paddingRight=measureToString(this.marginRight));""!==this.spaceAbove&&(e.paddingTop=measureToString(this.spaceAbove));""!==this.spaceBelow&&(e.paddingBottom=measureToString(this.spaceBelow));if(""!==this.textIndent){e.textIndent=measureToString(this.textIndent);fixTextIndent(e)}this.lineHeight>0&&(e.lineHeight=measureToString(this.lineHeight));""!==this.tabDefault&&(e.tabSize=measureToString(this.tabDefault));this.tabStops.length;this.hyphenatation&&Object.assign(e,this.hyphenatation[fl]());return e}}class PasswordEdit extends XFAObject{constructor(e){super(Kl,"passwordEdit",!0);this.hScrollPolicy=getStringOption(e.hScrollPolicy,["auto","off","on"]);this.id=e.id||"";this.passwordChar=e.passwordChar||"*";this.use=e.use||"";this.usehref=e.usehref||"";this.border=null;this.extras=null;this.margin=null}}class template_Pattern extends XFAObject{constructor(e){super(Kl,"pattern",!0);this.id=e.id||"";this.type=getStringOption(e.type,["crossHatch","crossDiagonal","diagonalLeft","diagonalRight","horizontal","vertical"]);this.use=e.use||"";this.usehref=e.usehref||"";this.color=null;this.extras=null}[fl](e){e=e?e[fl]():"#FFFFFF";const t=this.color?this.color[fl]():"#000000",a="repeating-linear-gradient",n=`${e},${e} 5px,${t} 5px,${t} 10px`;switch(this.type){case"crossHatch":return`${a}(to top,${n}) ${a}(to right,${n})`;case"crossDiagonal":return`${a}(45deg,${n}) ${a}(-45deg,${n})`;case"diagonalLeft":return`${a}(45deg,${n})`;case"diagonalRight":return`${a}(-45deg,${n})`;case"horizontal":return`${a}(to top,${n})`;case"vertical":return`${a}(to right,${n})`}return""}}class Picture extends StringObject{constructor(e){super(Kl,"picture");this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||""}}class Proto extends XFAObject{constructor(e){super(Kl,"proto",!0);this.appearanceFilter=new XFAObjectArray;this.arc=new XFAObjectArray;this.area=new XFAObjectArray;this.assist=new XFAObjectArray;this.barcode=new XFAObjectArray;this.bindItems=new XFAObjectArray;this.bookend=new XFAObjectArray;this.boolean=new XFAObjectArray;this.border=new XFAObjectArray;this.break=new XFAObjectArray;this.breakAfter=new XFAObjectArray;this.breakBefore=new XFAObjectArray;this.button=new XFAObjectArray;this.calculate=new XFAObjectArray;this.caption=new XFAObjectArray;this.certificate=new XFAObjectArray;this.certificates=new XFAObjectArray;this.checkButton=new XFAObjectArray;this.choiceList=new XFAObjectArray;this.color=new XFAObjectArray;this.comb=new XFAObjectArray;this.connect=new XFAObjectArray;this.contentArea=new XFAObjectArray;this.corner=new XFAObjectArray;this.date=new XFAObjectArray;this.dateTime=new XFAObjectArray;this.dateTimeEdit=new XFAObjectArray;this.decimal=new XFAObjectArray;this.defaultUi=new XFAObjectArray;this.desc=new XFAObjectArray;this.digestMethod=new XFAObjectArray;this.digestMethods=new XFAObjectArray;this.draw=new XFAObjectArray;this.edge=new XFAObjectArray;this.encoding=new XFAObjectArray;this.encodings=new XFAObjectArray;this.encrypt=new XFAObjectArray;this.encryptData=new XFAObjectArray;this.encryption=new XFAObjectArray;this.encryptionMethod=new XFAObjectArray;this.encryptionMethods=new XFAObjectArray;this.event=new XFAObjectArray;this.exData=new XFAObjectArray;this.exObject=new XFAObjectArray;this.exclGroup=new XFAObjectArray;this.execute=new XFAObjectArray;this.extras=new XFAObjectArray;this.field=new XFAObjectArray;this.fill=new XFAObjectArray;this.filter=new XFAObjectArray;this.float=new XFAObjectArray;this.font=new XFAObjectArray;this.format=new XFAObjectArray;this.handler=new XFAObjectArray;this.hyphenation=new XFAObjectArray;this.image=new XFAObjectArray;this.imageEdit=new XFAObjectArray;this.integer=new XFAObjectArray;this.issuers=new XFAObjectArray;this.items=new XFAObjectArray;this.keep=new XFAObjectArray;this.keyUsage=new XFAObjectArray;this.line=new XFAObjectArray;this.linear=new XFAObjectArray;this.lockDocument=new XFAObjectArray;this.manifest=new XFAObjectArray;this.margin=new XFAObjectArray;this.mdp=new XFAObjectArray;this.medium=new XFAObjectArray;this.message=new XFAObjectArray;this.numericEdit=new XFAObjectArray;this.occur=new XFAObjectArray;this.oid=new XFAObjectArray;this.oids=new XFAObjectArray;this.overflow=new XFAObjectArray;this.pageArea=new XFAObjectArray;this.pageSet=new XFAObjectArray;this.para=new XFAObjectArray;this.passwordEdit=new XFAObjectArray;this.pattern=new XFAObjectArray;this.picture=new XFAObjectArray;this.radial=new XFAObjectArray;this.reason=new XFAObjectArray;this.reasons=new XFAObjectArray;this.rectangle=new XFAObjectArray;this.ref=new XFAObjectArray;this.script=new XFAObjectArray;this.setProperty=new XFAObjectArray;this.signData=new XFAObjectArray;this.signature=new XFAObjectArray;this.signing=new XFAObjectArray;this.solid=new XFAObjectArray;this.speak=new XFAObjectArray;this.stipple=new XFAObjectArray;this.subform=new XFAObjectArray;this.subformSet=new XFAObjectArray;this.subjectDN=new XFAObjectArray;this.subjectDNs=new XFAObjectArray;this.submit=new XFAObjectArray;this.text=new XFAObjectArray;this.textEdit=new XFAObjectArray;this.time=new XFAObjectArray;this.timeStamp=new XFAObjectArray;this.toolTip=new XFAObjectArray;this.traversal=new XFAObjectArray;this.traverse=new XFAObjectArray;this.ui=new XFAObjectArray;this.validate=new XFAObjectArray;this.value=new XFAObjectArray;this.variables=new XFAObjectArray}}class Radial extends XFAObject{constructor(e){super(Kl,"radial",!0);this.id=e.id||"";this.type=getStringOption(e.type,["toEdge","toCenter"]);this.use=e.use||"";this.usehref=e.usehref||"";this.color=null;this.extras=null}[fl](e){e=e?e[fl]():"#FFFFFF";const t=this.color?this.color[fl]():"#000000";return`radial-gradient(circle at center, ${"toEdge"===this.type?`${e},${t}`:`${t},${e}`})`}}class Reason extends StringObject{constructor(e){super(Kl,"reason");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class Reasons extends XFAObject{constructor(e){super(Kl,"reasons",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.reason=new XFAObjectArray}}class Rectangle extends XFAObject{constructor(e){super(Kl,"rectangle",!0);this.hand=getStringOption(e.hand,["even","left","right"]);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.corner=new XFAObjectArray(4);this.edge=new XFAObjectArray(4);this.fill=null}[ol](){const e=this.edge.children.length?this.edge.children[0]:new Edge({}),t=e[fl](),a=Object.create(null);"visible"===this.fill?.presence?Object.assign(a,this.fill[fl]()):a.fill="transparent";a.strokeWidth=measureToString("visible"===e.presence?e.thickness:0);a.stroke=t.color;const n=(this.corner.children.length?this.corner.children[0]:new Corner({}))[fl](),s={name:"svg",children:[{name:"rect",attributes:{xmlns:Gl,width:"100%",height:"100%",x:0,y:0,rx:n.radius,ry:n.radius,style:a}}],attributes:{xmlns:Gl,style:{overflow:"visible"},width:"100%",height:"100%"}};if(hasMargin(this[So]()[So]()))return HTMLResult.success({name:"div",attributes:{style:{display:"inline",width:"100%",height:"100%"}},children:[s]});s.attributes.style.position="absolute";return HTMLResult.success(s)}}class RefElement extends StringObject{constructor(e){super(Kl,"ref");this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||""}}class Script extends StringObject{constructor(e){super(Kl,"script");this.binding=e.binding||"";this.contentType=e.contentType||"";this.id=e.id||"";this.name=e.name||"";this.runAt=getStringOption(e.runAt,["client","both","server"]);this.use=e.use||"";this.usehref=e.usehref||""}}class SetProperty extends XFAObject{constructor(e){super(Kl,"setProperty");this.connection=e.connection||"";this.ref=e.ref||"";this.target=e.target||""}}class SignData extends XFAObject{constructor(e){super(Kl,"signData",!0);this.id=e.id||"";this.operation=getStringOption(e.operation,["sign","clear","verify"]);this.ref=e.ref||"";this.target=e.target||"";this.use=e.use||"";this.usehref=e.usehref||"";this.filter=null;this.manifest=null}}class Signature extends XFAObject{constructor(e){super(Kl,"signature",!0);this.id=e.id||"";this.type=getStringOption(e.type,["PDF1.3","PDF1.6"]);this.use=e.use||"";this.usehref=e.usehref||"";this.border=null;this.extras=null;this.filter=null;this.manifest=null;this.margin=null}}class Signing extends XFAObject{constructor(e){super(Kl,"signing",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.certificate=new XFAObjectArray}}class Solid extends XFAObject{constructor(e){super(Kl,"solid",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null}[fl](e){return e?e[fl]():"#FFFFFF"}}class Speak extends StringObject{constructor(e){super(Kl,"speak");this.disable=getInteger({data:e.disable,defaultValue:0,validate:e=>1===e});this.id=e.id||"";this.priority=getStringOption(e.priority,["custom","caption","name","toolTip"]);this.rid=e.rid||"";this.use=e.use||"";this.usehref=e.usehref||""}}class Stipple extends XFAObject{constructor(e){super(Kl,"stipple",!0);this.id=e.id||"";this.rate=getInteger({data:e.rate,defaultValue:50,validate:e=>e>=0&&e<=100});this.use=e.use||"";this.usehref=e.usehref||"";this.color=null;this.extras=null}[fl](e){const t=this.rate/100;return Util.makeHexColor(Math.round(e.value.r*(1-t)+this.value.r*t),Math.round(e.value.g*(1-t)+this.value.g*t),Math.round(e.value.b*(1-t)+this.value.b*t))}}class Subform extends XFAObject{constructor(e){super(Kl,"subform",!0);this.access=getStringOption(e.access,["open","nonInteractive","protected","readOnly"]);this.allowMacro=getInteger({data:e.allowMacro,defaultValue:0,validate:e=>1===e});this.anchorType=getStringOption(e.anchorType,["topLeft","bottomCenter","bottomLeft","bottomRight","middleCenter","middleLeft","middleRight","topCenter","topRight"]);this.colSpan=getInteger({data:e.colSpan,defaultValue:1,validate:e=>e>=1||-1===e});this.columnWidths=(e.columnWidths||"").trim().split(/\s+/).map(e=>"-1"===e?-1:getMeasurement(e));this.h=e.h?getMeasurement(e.h):"";this.hAlign=getStringOption(e.hAlign,["left","center","justify","justifyAll","radix","right"]);this.id=e.id||"";this.layout=getStringOption(e.layout,["position","lr-tb","rl-row","rl-tb","row","table","tb"]);this.locale=e.locale||"";this.maxH=getMeasurement(e.maxH,"0pt");this.maxW=getMeasurement(e.maxW,"0pt");this.mergeMode=getStringOption(e.mergeMode,["consumeData","matchTemplate"]);this.minH=getMeasurement(e.minH,"0pt");this.minW=getMeasurement(e.minW,"0pt");this.name=e.name||"";this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.relevant=getRelevant(e.relevant);this.restoreState=getStringOption(e.restoreState,["manual","auto"]);this.scope=getStringOption(e.scope,["name","none"]);this.use=e.use||"";this.usehref=e.usehref||"";this.w=e.w?getMeasurement(e.w):"";this.x=getMeasurement(e.x,"0pt");this.y=getMeasurement(e.y,"0pt");this.assist=null;this.bind=null;this.bookend=null;this.border=null;this.break=null;this.calculate=null;this.desc=null;this.extras=null;this.keep=null;this.margin=null;this.occur=null;this.overflow=null;this.pageSet=null;this.para=null;this.traversal=null;this.validate=null;this.variables=null;this.area=new XFAObjectArray;this.breakAfter=new XFAObjectArray;this.breakBefore=new XFAObjectArray;this.connect=new XFAObjectArray;this.draw=new XFAObjectArray;this.event=new XFAObjectArray;this.exObject=new XFAObjectArray;this.exclGroup=new XFAObjectArray;this.field=new XFAObjectArray;this.proto=new XFAObjectArray;this.setProperty=new XFAObjectArray;this.subform=new XFAObjectArray;this.subformSet=new XFAObjectArray}[vo](){const e=this[So]();return e instanceof SubformSet?e[vo]():e}[Ho](){return!0}[Po](){return this.layout.endsWith("-tb")&&0===this[oo].attempt&&this[oo].numberInLine>0||this[So]()[Po]()}*[yo](){yield*getContainedChildren(this)}[fo](){return flushHTML(this)}[Yi](e,t){addHTML(this,e,t)}[uo](){return getAvailableSpace(this)}[No](){const e=this[vo]();if(!e[No]())return!1;if(void 0!==this[oo]._isSplittable)return this[oo]._isSplittable;if("position"===this.layout||this.layout.includes("row")){this[oo]._isSplittable=!1;return!1}if(this.keep&&"none"!==this.keep.intact){this[oo]._isSplittable=!1;return!1}if(e.layout?.endsWith("-tb")&&0!==e[oo].numberInLine)return!1;this[oo]._isSplittable=!0;return!0}[ol](e){setTabIndex(this);if(this.break){if("auto"!==this.break.after||""!==this.break.afterTarget){const e=new BreakAfter({targetType:this.break.after,target:this.break.afterTarget,startNew:this.break.startNew.toString()});e[xo]=this[xo];this[Ji](e);this.breakAfter.push(e)}if("auto"!==this.break.before||""!==this.break.beforeTarget){const e=new BreakBefore({targetType:this.break.before,target:this.break.beforeTarget,startNew:this.break.startNew.toString()});e[xo]=this[xo];this[Ji](e);this.breakBefore.push(e)}if(""!==this.break.overflowTarget){const e=new Overflow({target:this.break.overflowTarget,leader:this.break.overflowLeader,trailer:this.break.overflowTrailer});e[xo]=this[xo];this[Ji](e);this.overflow.push(e)}this[Jo](this.break);this.break=null}if("hidden"===this.presence||"inactive"===this.presence)return HTMLResult.EMPTY;(this.breakBefore.children.length>1||this.breakAfter.children.length>1)&&warn("XFA - Several breakBefore or breakAfter in subforms: please file a bug.");if(this.breakBefore.children.length>=1){const e=this.breakBefore.children[0];if(handleBreak(e))return HTMLResult.breakNode(e)}if(this[oo]?.afterBreakAfter)return HTMLResult.EMPTY;fixDimensions(this);const t=[],a={id:this[cl],class:[]};setAccess(this,a.class);this[oo]||=Object.create(null);Object.assign(this[oo],{children:t,line:null,attributes:a,attempt:0,numberInLine:0,availableSpace:{width:Math.min(this.w||1/0,e.width),height:Math.min(this.h||1/0,e.height)},width:0,height:0,prevHeight:0,currentWidth:0});const n=this[Ao](),s=n[oo].noLayoutFailure,r=this[No]();r||setFirstUnsplittable(this);if(!checkDimensions(this,e))return HTMLResult.FAILURE;const i=new Set(["area","draw","exclGroup","field","subform","subformSet"]);if(this.layout.includes("row")){const e=this[vo]().columnWidths;if(Array.isArray(e)&&e.length>0){this[oo].columnWidths=e;this[oo].currentColumn=0}}const o=toStyle(this,"anchorType","dimensions","position","presence","border","margin","hAlign"),l=["xfaSubform"],f=layoutClass(this);f&&l.push(f);a.style=o;a.class=l;this.name&&(a.xfaName=this.name);if(this.overflow){const t=this.overflow[wo]();if(t.addLeader){t.addLeader=!1;handleOverflow(this,t.leader,e)}}this[Yo]();const c="lr-tb"===this.layout||"rl-tb"===this.layout,h=c?2:1;for(;this[oo].attempt=1){const e=this.breakAfter.children[0];if(handleBreak(e)){this[oo].afterBreakAfter=w;return HTMLResult.breakNode(e)}}delete this[oo];return w}}class SubformSet extends XFAObject{constructor(e){super(Kl,"subformSet",!0);this.id=e.id||"";this.name=e.name||"";this.relation=getStringOption(e.relation,["ordered","choice","unordered"]);this.relevant=getRelevant(e.relevant);this.use=e.use||"";this.usehref=e.usehref||"";this.bookend=null;this.break=null;this.desc=null;this.extras=null;this.occur=null;this.overflow=null;this.breakAfter=new XFAObjectArray;this.breakBefore=new XFAObjectArray;this.subform=new XFAObjectArray;this.subformSet=new XFAObjectArray}*[yo](){yield*getContainedChildren(this)}[vo](){let e=this[So]();for(;!(e instanceof Subform);)e=e[So]();return e}[Ho](){return!0}}class SubjectDN extends ContentObject{constructor(e){super(Kl,"subjectDN");this.delimiter=e.delimiter||",";this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}[lo](){this[so]=new Map(this[so].split(this.delimiter).map(e=>{(e=e.split("=",2))[0]=e[0].trim();return e}))}}class SubjectDNs extends XFAObject{constructor(e){super(Kl,"subjectDNs",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.subjectDN=new XFAObjectArray}}class Submit extends XFAObject{constructor(e){super(Kl,"submit",!0);this.embedPDF=getInteger({data:e.embedPDF,defaultValue:0,validate:e=>1===e});this.format=getStringOption(e.format,["xdp","formdata","pdf","urlencoded","xfd","xml"]);this.id=e.id||"";this.target=e.target||"";this.textEncoding=getKeyword({data:e.textEncoding?e.textEncoding.toLowerCase():"",defaultValue:"",validate:e=>["utf-8","big-five","fontspecific","gbk","gb-18030","gb-2312","ksc-5601","none","shift-jis","ucs-2","utf-16"].includes(e)||e.match(/iso-8859-\d{2}/)});this.use=e.use||"";this.usehref=e.usehref||"";this.xdpContent=e.xdpContent||"";this.encrypt=null;this.encryptData=new XFAObjectArray;this.signData=new XFAObjectArray}}class Template extends XFAObject{constructor(e){super(Kl,"template",!0);this.baseProfile=getStringOption(e.baseProfile,["full","interactiveForms"]);this.extras=null;this.subform=new XFAObjectArray}[lo](){0===this.subform.children.length&&warn("XFA - No subforms in template node.");this.subform.children.length>=2&&warn("XFA - Several subforms in template node: please file a bug.");this[sl]=5e3}[No](){return!0}[el](e,t){return e.startsWith("#")?[this[Io].get(e.slice(1))]:searchNode(this,t,e,!0,!0)}*[il](){if(!this.subform.children.length)return HTMLResult.success({name:"div",children:[]});this[oo]={overflowNode:null,firstUnsplittable:null,currentContentArea:null,currentPageArea:null,noLayoutFailure:!1,pageNumber:1,pagePosition:"first",oddOrEven:"odd",blankOrNotBlank:"nonBlank",paraStack:[]};const e=this.subform.children[0];e.pageSet[eo]();const t=e.pageSet.pageArea.children,a={name:"div",children:[]};let n=null,s=null,r=null;if(e.breakBefore.children.length>=1){s=e.breakBefore.children[0];r=s.target}else if(e.subform.children.length>=1&&e.subform.children[0].breakBefore.children.length>=1){s=e.subform.children[0].breakBefore.children[0];r=s.target}else if(e.break?.beforeTarget){s=e.break;r=s.beforeTarget}else if(e.subform.children.length>=1&&e.subform.children[0].break?.beforeTarget){s=e.subform.children[0].break;r=s.beforeTarget}if(s){const e=this[el](r,s[So]());if(e instanceof PageArea){n=e;s[oo]={}}}n||=t[0];n[oo]={numberOfUse:1};const i=n[So]();i[oo]={numberOfUse:1,pageIndex:i.pageArea.children.indexOf(n),pageSetIndex:0};let o,l=null,f=null,c=!0,h=0,u=0;for(;;){if(c)h=0;else{a.children.pop();if(3===++h){warn("XFA - Something goes wrong: please file a bug.");return a}}o=null;this[oo].currentPageArea=n;const t=n[ol]().html;a.children.push(t);if(l){this[oo].noLayoutFailure=!0;t.children.push(l[ol](n[oo].space).html);l=null}if(f){this[oo].noLayoutFailure=!0;t.children.push(f[ol](n[oo].space).html);f=null}const s=n.contentArea.children,r=t.children.filter(e=>e.attributes.class.includes("xfaContentarea"));c=!1;this[oo].firstUnsplittable=null;this[oo].noLayoutFailure=!1;const flush=t=>{const a=e[fo]();if(a){c||=a.children?.length>0;r[t].children.push(a)}};for(let t=u,n=s.length;t0;r[t].children.push(h.html)}else!c&&a.children.length>1&&a.children.pop();return a}if(h.isBreak()){const e=h.breakNode;flush(t);if("auto"===e.targetType)continue;if(e.leader){l=this[el](e.leader,e[So]());l=l?l[0]:null}if(e.trailer){f=this[el](e.trailer,e[So]());f=f?f[0]:null}if("pageArea"===e.targetType){o=e[oo].target;t=1/0}else if(e[oo].target){o=e[oo].target;u=e[oo].index+1;t=1/0}else t=e[oo].index;continue}if(this[oo].overflowNode){const e=this[oo].overflowNode;this[oo].overflowNode=null;const a=e[wo](),n=a.target;a.addLeader=null!==a.leader;a.addTrailer=null!==a.trailer;flush(t);const r=t;t=1/0;if(n instanceof PageArea)o=n;else if(n instanceof ContentArea){const e=s.indexOf(n);if(-1!==e)e>r?t=e-1:u=e;else{o=n[So]();u=o.contentArea.children.indexOf(n)}}continue}flush(t)}this[oo].pageNumber+=1;o&&(o[_o]()?o[oo].numberOfUse+=1:o=null);n=o||n[qo]();yield null}}}class Text extends ContentObject{constructor(e){super(Kl,"text");this.id=e.id||"";this.maxChars=getInteger({data:e.maxChars,defaultValue:0,validate:e=>e>=0});this.name=e.name||"";this.rid=e.rid||"";this.use=e.use||"";this.usehref=e.usehref||""}[$i](){return!0}[Wo](e){if(e[Lo]===ul.xhtml.id){this[so]=e;return!0}warn(`XFA - Invalid content in Text: ${e[Uo]}.`);return!1}[Go](e){this[so]instanceof XFAObject||super[Go](e)}[lo](){"string"==typeof this[so]&&(this[so]=this[so].replaceAll("\r\n","\n"))}[wo](){return"string"==typeof this[so]?this[so].split(/[\u2029\u2028\n]/).filter(e=>!!e).join("\n"):this[so][rl]()}[ol](e){if("string"==typeof this[so]){const e=valueToHtml(this[so]).html;if(this[so].includes("\u2029")){e.name="div";e.children=[];this[so].split("\u2029").map(e=>e.split(/[\u2028\n]/).flatMap(e=>[{name:"span",value:e},{name:"br"}])).forEach(t=>{e.children.push({name:"p",children:t})})}else if(/[\u2028\n]/.test(this[so])){e.name="div";e.children=[];this[so].split(/[\u2028\n]/).forEach(t=>{e.children.push({name:"span",value:t},{name:"br"})})}return HTMLResult.success(e)}return this[so][ol](e)}}class TextEdit extends XFAObject{constructor(e){super(Kl,"textEdit",!0);this.allowRichText=getInteger({data:e.allowRichText,defaultValue:0,validate:e=>1===e});this.hScrollPolicy=getStringOption(e.hScrollPolicy,["auto","off","on"]);this.id=e.id||"";this.multiLine=getInteger({data:e.multiLine,defaultValue:"",validate:e=>0===e||1===e});this.use=e.use||"";this.usehref=e.usehref||"";this.vScrollPolicy=getStringOption(e.vScrollPolicy,["auto","off","on"]);this.border=null;this.comb=null;this.extras=null;this.margin=null}[ol](e){const t=toStyle(this,"border","font","margin");let a;const n=this[So]()[So]();""===this.multiLine&&(this.multiLine=n instanceof Draw?1:0);a=1===this.multiLine?{name:"textarea",attributes:{dataId:n[ro]?.[cl]||n[cl],fieldId:n[cl],class:["xfaTextfield"],style:t,"aria-label":ariaLabel(n),"aria-required":!1}}:{name:"input",attributes:{type:"text",dataId:n[ro]?.[cl]||n[cl],fieldId:n[cl],class:["xfaTextfield"],style:t,"aria-label":ariaLabel(n),"aria-required":!1}};if(isRequired(n)){a.attributes["aria-required"]=!0;a.attributes.required=!0}return HTMLResult.success({name:"label",attributes:{class:["xfaLabel"]},children:[a]})}}class Time extends StringObject{constructor(e){super(Kl,"time");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}[lo](){const e=this[so].trim();this[so]=e?new Date(e):null}[ol](e){return valueToHtml(this[so]?this[so].toString():"")}}class TimeStamp extends XFAObject{constructor(e){super(Kl,"timeStamp");this.id=e.id||"";this.server=e.server||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||""}}class ToolTip extends StringObject{constructor(e){super(Kl,"toolTip");this.id=e.id||"";this.rid=e.rid||"";this.use=e.use||"";this.usehref=e.usehref||""}}class Traversal extends XFAObject{constructor(e){super(Kl,"traversal",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.traverse=new XFAObjectArray}}class Traverse extends XFAObject{constructor(e){super(Kl,"traverse",!0);this.id=e.id||"";this.operation=getStringOption(e.operation,["next","back","down","first","left","right","up"]);this.ref=e.ref||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.script=null}get name(){return this.operation}[Eo](){return!1}}class Ui extends XFAObject{constructor(e){super(Kl,"ui",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.picture=null;this.barcode=null;this.button=null;this.checkButton=null;this.choiceList=null;this.dateTimeEdit=null;this.defaultUi=null;this.imageEdit=null;this.numericEdit=null;this.passwordEdit=null;this.signature=null;this.textEdit=null}[wo](){if(void 0===this[oo]){for(const e of Object.getOwnPropertyNames(this)){if("extras"===e||"picture"===e)continue;const t=this[e];if(t instanceof XFAObject){this[oo]=t;return t}}this[oo]=null}return this[oo]}[ol](e){const t=this[wo]();return t?t[ol](e):HTMLResult.EMPTY}}class Validate extends XFAObject{constructor(e){super(Kl,"validate",!0);this.formatTest=getStringOption(e.formatTest,["warning","disabled","error"]);this.id=e.id||"";this.nullTest=getStringOption(e.nullTest,["disabled","error","warning"]);this.scriptTest=getStringOption(e.scriptTest,["error","disabled","warning"]);this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.message=null;this.picture=null;this.script=null}}class Value extends XFAObject{constructor(e){super(Kl,"value",!0);this.id=e.id||"";this.override=getInteger({data:e.override,defaultValue:0,validate:e=>1===e});this.relevant=getRelevant(e.relevant);this.use=e.use||"";this.usehref=e.usehref||"";this.arc=null;this.boolean=null;this.date=null;this.dateTime=null;this.decimal=null;this.exData=null;this.float=null;this.image=null;this.integer=null;this.line=null;this.rectangle=null;this.text=null;this.time=null}[nl](e){const t=this[So]();if(t instanceof Field&&t.ui?.imageEdit){if(!this.image){this.image=new Image({});this[Ji](this.image)}this.image[so]=e[so];return}const a=e[Uo];if(null===this[a]){for(const e of Object.getOwnPropertyNames(this)){const t=this[e];if(t instanceof XFAObject){this[e]=null;this[Jo](t)}}this[e[Uo]]=e;this[Ji](e)}else this[a][so]=e[so]}[rl](){if(this.exData)return"string"==typeof this.exData[so]?this.exData[so].trim():this.exData[so][rl]().trim();for(const e of Object.getOwnPropertyNames(this)){if("image"===e)continue;const t=this[e];if(t instanceof XFAObject)return(t[so]||"").toString().trim()}return null}[ol](e){for(const t of Object.getOwnPropertyNames(this)){const a=this[t];if(a instanceof XFAObject)return a[ol](e)}return HTMLResult.EMPTY}}class Variables extends XFAObject{constructor(e){super(Kl,"variables",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.boolean=new XFAObjectArray;this.date=new XFAObjectArray;this.dateTime=new XFAObjectArray;this.decimal=new XFAObjectArray;this.exData=new XFAObjectArray;this.float=new XFAObjectArray;this.image=new XFAObjectArray;this.integer=new XFAObjectArray;this.manifest=new XFAObjectArray;this.script=new XFAObjectArray;this.text=new XFAObjectArray;this.time=new XFAObjectArray}[Eo](){return!0}}class TemplateNamespace{static[hl](e,t){if(Object.hasOwn(TemplateNamespace,e)){const a=TemplateNamespace[e](t);a[al](t);return a}}static appearanceFilter(e){return new AppearanceFilter(e)}static arc(e){return new Arc(e)}static area(e){return new Area(e)}static assist(e){return new Assist(e)}static barcode(e){return new Barcode(e)}static bind(e){return new Bind(e)}static bindItems(e){return new BindItems(e)}static bookend(e){return new Bookend(e)}static boolean(e){return new BooleanElement(e)}static border(e){return new Border(e)}static break(e){return new Break(e)}static breakAfter(e){return new BreakAfter(e)}static breakBefore(e){return new BreakBefore(e)}static button(e){return new Button(e)}static calculate(e){return new Calculate(e)}static caption(e){return new Caption(e)}static certificate(e){return new Certificate(e)}static certificates(e){return new Certificates(e)}static checkButton(e){return new CheckButton(e)}static choiceList(e){return new ChoiceList(e)}static color(e){return new Color(e)}static comb(e){return new Comb(e)}static connect(e){return new Connect(e)}static contentArea(e){return new ContentArea(e)}static corner(e){return new Corner(e)}static date(e){return new DateElement(e)}static dateTime(e){return new DateTime(e)}static dateTimeEdit(e){return new DateTimeEdit(e)}static decimal(e){return new Decimal(e)}static defaultUi(e){return new DefaultUi(e)}static desc(e){return new Desc(e)}static digestMethod(e){return new DigestMethod(e)}static digestMethods(e){return new DigestMethods(e)}static draw(e){return new Draw(e)}static edge(e){return new Edge(e)}static encoding(e){return new Encoding(e)}static encodings(e){return new Encodings(e)}static encrypt(e){return new Encrypt(e)}static encryptData(e){return new EncryptData(e)}static encryption(e){return new Encryption(e)}static encryptionMethod(e){return new EncryptionMethod(e)}static encryptionMethods(e){return new EncryptionMethods(e)}static event(e){return new Event(e)}static exData(e){return new ExData(e)}static exObject(e){return new ExObject(e)}static exclGroup(e){return new ExclGroup(e)}static execute(e){return new Execute(e)}static extras(e){return new Extras(e)}static field(e){return new Field(e)}static fill(e){return new Fill(e)}static filter(e){return new Filter(e)}static float(e){return new Float(e)}static font(e){return new template_Font(e)}static format(e){return new Format(e)}static handler(e){return new Handler(e)}static hyphenation(e){return new Hyphenation(e)}static image(e){return new Image(e)}static imageEdit(e){return new ImageEdit(e)}static integer(e){return new Integer(e)}static issuers(e){return new Issuers(e)}static items(e){return new Items(e)}static keep(e){return new Keep(e)}static keyUsage(e){return new KeyUsage(e)}static line(e){return new Line(e)}static linear(e){return new Linear(e)}static lockDocument(e){return new LockDocument(e)}static manifest(e){return new Manifest(e)}static margin(e){return new Margin(e)}static mdp(e){return new Mdp(e)}static medium(e){return new Medium(e)}static message(e){return new Message(e)}static numericEdit(e){return new NumericEdit(e)}static occur(e){return new Occur(e)}static oid(e){return new Oid(e)}static oids(e){return new Oids(e)}static overflow(e){return new Overflow(e)}static pageArea(e){return new PageArea(e)}static pageSet(e){return new PageSet(e)}static para(e){return new Para(e)}static passwordEdit(e){return new PasswordEdit(e)}static pattern(e){return new template_Pattern(e)}static picture(e){return new Picture(e)}static proto(e){return new Proto(e)}static radial(e){return new Radial(e)}static reason(e){return new Reason(e)}static reasons(e){return new Reasons(e)}static rectangle(e){return new Rectangle(e)}static ref(e){return new RefElement(e)}static script(e){return new Script(e)}static setProperty(e){return new SetProperty(e)}static signData(e){return new SignData(e)}static signature(e){return new Signature(e)}static signing(e){return new Signing(e)}static solid(e){return new Solid(e)}static speak(e){return new Speak(e)}static stipple(e){return new Stipple(e)}static subform(e){return new Subform(e)}static subformSet(e){return new SubformSet(e)}static subjectDN(e){return new SubjectDN(e)}static subjectDNs(e){return new SubjectDNs(e)}static submit(e){return new Submit(e)}static template(e){return new Template(e)}static text(e){return new Text(e)}static textEdit(e){return new TextEdit(e)}static time(e){return new Time(e)}static timeStamp(e){return new TimeStamp(e)}static toolTip(e){return new ToolTip(e)}static traversal(e){return new Traversal(e)}static traverse(e){return new Traverse(e)}static ui(e){return new Ui(e)}static validate(e){return new Validate(e)}static value(e){return new Value(e)}static variables(e){return new Variables(e)}}const Jl=ul.datasets.id;function createText(e){const t=new Text({});t[so]=e;return t}class Binder{constructor(e){this.root=e;this.datasets=e.datasets;this.data=e.datasets?.data||new XmlObject(ul.datasets.id,"data");this.emptyMerge=0===this.data[ko]().length;this.root.form=this.form=e.template[ao]()}_isConsumeData(){return!this.emptyMerge&&this._mergeMode}_isMatchTemplate(){return!this._isConsumeData()}bind(){this._bindElement(this.form,this.data);return this.form}getData(){return this.data}_bindValue(e,t,a){e[ro]=t;if(e[Co]())if(t[Bo]()){const a=t[bo]();e[nl](createText(a))}else if(e instanceof Field&&"multiSelect"===e.ui?.choiceList?.open){const a=t[ko]().map(e=>e[so].trim()).join("\n");e[nl](createText(a))}else this._isConsumeData()&&warn("XFA - Nodes haven't the same type.");else!t[Bo]()||this._isMatchTemplate()?this._bindElement(e,t):warn("XFA - Nodes haven't the same type.")}_findDataByNameToConsume(e,t,a,n){if(!e)return null;let s,r;for(let n=0;n<3;n++){s=a[jo](e,!1,!0);for(;;){r=s.next().value;if(!r)break;if(t===r[Bo]())return r}if(a[Lo]===ul.datasets.id&&"data"===a[Uo])break;a=a[So]()}if(!n)return null;s=this.data[jo](e,!0,!1);r=s.next().value;if(r)return r;s=this.data[co](e,!0);r=s.next().value;return r?.[Bo]()?r:null}_setProperties(e,t){if(Object.hasOwn(e,"setProperty"))for(const{ref:a,target:n,connection:s}of e.setProperty.children){if(s)continue;if(!a)continue;const r=searchNode(this.root,t,a,!1,!1);if(!r){warn(`XFA - Invalid reference: ${a}.`);continue}const[i]=r;if(!i[Do](this.data)){warn("XFA - Invalid node: must be a data node.");continue}const o=searchNode(this.root,e,n,!1,!1);if(!o){warn(`XFA - Invalid target: ${n}.`);continue}const[l]=o;if(!l[Do](e)){warn("XFA - Invalid target: must be a property or subproperty.");continue}const f=l[So]();if(l instanceof SetProperty||f instanceof SetProperty){warn("XFA - Invalid target: cannot be a setProperty or one of its properties.");continue}if(l instanceof BindItems||f instanceof BindItems){warn("XFA - Invalid target: cannot be a bindItems or one of its properties.");continue}const c=i[rl](),h=l[Uo];if(l instanceof XFAAttribute){const e=Object.create(null);e[h]=c;const t=Reflect.construct(Object.getPrototypeOf(f).constructor,[e]);f[h]=t[h];continue}if(Object.hasOwn(l,so)){l[ro]=i;l[so]=c;l[lo]()}else warn("XFA - Invalid node to use in setProperty")}}_bindItems(e,t){if(!Object.hasOwn(e,"items")||!Object.hasOwn(e,"bindItems")||e.bindItems.isEmpty())return;for(const t of e.items.children)e[Jo](t);e.items.clear();const a=new Items({}),n=new Items({});e[Ji](a);e.items.push(a);e[Ji](n);e.items.push(n);for(const{ref:s,labelRef:r,valueRef:i,connection:o}of e.bindItems.children){if(o)continue;if(!s)continue;const e=searchNode(this.root,t,s,!1,!1);if(e)for(const t of e){if(!t[Do](this.datasets)){warn(`XFA - Invalid ref (${s}): must be a datasets child.`);continue}const e=searchNode(this.root,t,r,!0,!1);if(!e){warn(`XFA - Invalid label: ${r}.`);continue}const[o]=e;if(!o[Do](this.datasets)){warn("XFA - Invalid label: must be a datasets child.");continue}const l=searchNode(this.root,t,i,!0,!1);if(!l){warn(`XFA - Invalid value: ${i}.`);continue}const[f]=l;if(!f[Do](this.datasets)){warn("XFA - Invalid value: must be a datasets child.");continue}const c=createText(o[rl]()),h=createText(f[rl]());a[Ji](c);a.text.push(c);n[Ji](h);n.text.push(h)}else warn(`XFA - Invalid reference: ${s}.`)}}_bindOccurrences(e,t,a){let n;if(t.length>1){n=e[ao]();n[Jo](n.occur);n.occur=null}this._bindValue(e,t[0],a);this._setProperties(e,t[0]);this._bindItems(e,t[0]);if(1===t.length)return;const s=e[So](),r=e[Uo],i=s[Fo](e);for(let e=1,o=t.length;et.name===e.name).length:a[n].children.length;const r=a[Fo](e)+1,i=t.initial-s;if(i){const t=e[ao]();t[Jo](t.occur);t.occur=null;a[n].push(t);a[Oo](r,t);for(let e=1;e0)this._bindOccurrences(n,[e[0]],null);else if(this.emptyMerge){const e=t[Lo]===Jl?-1:t[Lo],a=n[ro]=new XmlObject(e,n.name||"root");t[Ji](a);this._bindElement(n,a)}continue}if(!n[Ho]())continue;let e=!1,s=null,r=null,i=null;if(n.bind){switch(n.bind.match){case"none":this._setAndBind(n,t);continue;case"global":e=!0;break;case"dataRef":if(!n.bind.ref){warn(`XFA - ref is empty in node ${n[Uo]}.`);this._setAndBind(n,t);continue}r=n.bind.ref}n.bind.picture&&(s=n.bind.picture[so])}const[o,l]=this._getOccurInfo(n);if(r){i=searchNode(this.root,t,r,!0,!1);if(null===i){i=createDataNode(this.data,t,r);if(!i)continue;this._isConsumeData()&&(i[no]=!0);this._setAndBind(n,i);continue}this._isConsumeData()&&(i=i.filter(e=>!e[no]));i.length>l?i=i.slice(0,l):0===i.length&&(i=null);i&&this._isConsumeData()&&i.forEach(e=>{e[no]=!0})}else{if(!n.name){this._setAndBind(n,t);continue}if(this._isConsumeData()){const a=[];for(;a.length0?a:null}else{i=t[jo](n.name,!1,this.emptyMerge).next().value;if(!i){if(0===o){a.push(n);continue}const e=t[Lo]===Jl?-1:t[Lo];i=n[ro]=new XmlObject(e,n.name);this.emptyMerge&&(i[no]=!0);t[Ji](i);this._setAndBind(n,i);continue}this.emptyMerge&&(i[no]=!0);i=[i]}}i?this._bindOccurrences(n,i,s):o>0?this._setAndBind(n,t):a.push(n)}a.forEach(e=>e[So]()[Jo](e))}}class DataHandler{constructor(e,t){this.data=t;this.dataset=e.datasets||null}serialize(e){const t=[[-1,this.data[ko]()]];for(;t.length>0;){const a=t.at(-1),[n,s]=a;if(n+1===s.length){t.pop();continue}const r=s[++a[0]],i=e.get(r[cl]);if(i)r[nl](i);else{const t=r[ho]();for(const a of t.values()){const t=e.get(a[cl]);if(t){a[nl](t);break}}}const o=r[ko]();o.length>0&&t.push([-1,o])}const a=[''];if(this.dataset)for(const e of this.dataset[ko]())"data"!==e[Uo]&&e[ll](a);this.data[ll](a);a.push("");return a.join("")}}const Ql=ul.config.id;class Acrobat extends XFAObject{constructor(e){super(Ql,"acrobat",!0);this.acrobat7=null;this.autoSave=null;this.common=null;this.validate=null;this.validateApprovalSignatures=null;this.submitUrl=new XFAObjectArray}}class Acrobat7 extends XFAObject{constructor(e){super(Ql,"acrobat7",!0);this.dynamicRender=null}}class ADBE_JSConsole extends OptionObject{constructor(e){super(Ql,"ADBE_JSConsole",["delegate","Enable","Disable"])}}class ADBE_JSDebugger extends OptionObject{constructor(e){super(Ql,"ADBE_JSDebugger",["delegate","Enable","Disable"])}}class AddSilentPrint extends Option01{constructor(e){super(Ql,"addSilentPrint")}}class AddViewerPreferences extends Option01{constructor(e){super(Ql,"addViewerPreferences")}}class AdjustData extends Option10{constructor(e){super(Ql,"adjustData")}}class AdobeExtensionLevel extends IntegerObject{constructor(e){super(Ql,"adobeExtensionLevel",0,e=>e>=1&&e<=8)}}class Agent extends XFAObject{constructor(e){super(Ql,"agent",!0);this.name=e.name?e.name.trim():"";this.common=new XFAObjectArray}}class AlwaysEmbed extends ContentObject{constructor(e){super(Ql,"alwaysEmbed")}}class Amd extends StringObject{constructor(e){super(Ql,"amd")}}class config_Area extends XFAObject{constructor(e){super(Ql,"area");this.level=getInteger({data:e.level,defaultValue:0,validate:e=>e>=1&&e<=3});this.name=getStringOption(e.name,["","barcode","coreinit","deviceDriver","font","general","layout","merge","script","signature","sourceSet","templateCache"])}}class Attributes extends OptionObject{constructor(e){super(Ql,"attributes",["preserve","delegate","ignore"])}}class AutoSave extends OptionObject{constructor(e){super(Ql,"autoSave",["disabled","enabled"])}}class Base extends StringObject{constructor(e){super(Ql,"base")}}class BatchOutput extends XFAObject{constructor(e){super(Ql,"batchOutput");this.format=getStringOption(e.format,["none","concat","zip","zipCompress"])}}class BehaviorOverride extends ContentObject{constructor(e){super(Ql,"behaviorOverride")}[lo](){this[so]=new Map(this[so].trim().split(/\s+/).filter(e=>e.includes(":")).map(e=>e.split(":",2)))}}class Cache extends XFAObject{constructor(e){super(Ql,"cache",!0);this.templateCache=null}}class Change extends Option01{constructor(e){super(Ql,"change")}}class Common extends XFAObject{constructor(e){super(Ql,"common",!0);this.data=null;this.locale=null;this.localeSet=null;this.messaging=null;this.suppressBanner=null;this.template=null;this.validationMessaging=null;this.versionControl=null;this.log=new XFAObjectArray}}class Compress extends XFAObject{constructor(e){super(Ql,"compress");this.scope=getStringOption(e.scope,["imageOnly","document"])}}class CompressLogicalStructure extends Option01{constructor(e){super(Ql,"compressLogicalStructure")}}class CompressObjectStream extends Option10{constructor(e){super(Ql,"compressObjectStream")}}class Compression extends XFAObject{constructor(e){super(Ql,"compression",!0);this.compressLogicalStructure=null;this.compressObjectStream=null;this.level=null;this.type=null}}class Config extends XFAObject{constructor(e){super(Ql,"config",!0);this.acrobat=null;this.present=null;this.trace=null;this.agent=new XFAObjectArray}}class Conformance extends OptionObject{constructor(e){super(Ql,"conformance",["A","B"])}}class ContentCopy extends Option01{constructor(e){super(Ql,"contentCopy")}}class Copies extends IntegerObject{constructor(e){super(Ql,"copies",1,e=>e>=1)}}class Creator extends StringObject{constructor(e){super(Ql,"creator")}}class CurrentPage extends IntegerObject{constructor(e){super(Ql,"currentPage",0,e=>e>=0)}}class Data extends XFAObject{constructor(e){super(Ql,"data",!0);this.adjustData=null;this.attributes=null;this.incrementalLoad=null;this.outputXSL=null;this.range=null;this.record=null;this.startNode=null;this.uri=null;this.window=null;this.xsl=null;this.excludeNS=new XFAObjectArray;this.transform=new XFAObjectArray}}class Debug extends XFAObject{constructor(e){super(Ql,"debug",!0);this.uri=null}}class DefaultTypeface extends ContentObject{constructor(e){super(Ql,"defaultTypeface");this.writingScript=getStringOption(e.writingScript,["*","Arabic","Cyrillic","EastEuropeanRoman","Greek","Hebrew","Japanese","Korean","Roman","SimplifiedChinese","Thai","TraditionalChinese","Vietnamese"])}}class Destination extends OptionObject{constructor(e){super(Ql,"destination",["pdf","pcl","ps","webClient","zpl"])}}class DocumentAssembly extends Option01{constructor(e){super(Ql,"documentAssembly")}}class Driver extends XFAObject{constructor(e){super(Ql,"driver",!0);this.name=e.name?e.name.trim():"";this.fontInfo=null;this.xdc=null}}class DuplexOption extends OptionObject{constructor(e){super(Ql,"duplexOption",["simplex","duplexFlipLongEdge","duplexFlipShortEdge"])}}class DynamicRender extends OptionObject{constructor(e){super(Ql,"dynamicRender",["forbidden","required"])}}class Embed extends Option01{constructor(e){super(Ql,"embed")}}class config_Encrypt extends Option01{constructor(e){super(Ql,"encrypt")}}class config_Encryption extends XFAObject{constructor(e){super(Ql,"encryption",!0);this.encrypt=null;this.encryptionLevel=null;this.permissions=null}}class EncryptionLevel extends OptionObject{constructor(e){super(Ql,"encryptionLevel",["40bit","128bit"])}}class Enforce extends StringObject{constructor(e){super(Ql,"enforce")}}class Equate extends XFAObject{constructor(e){super(Ql,"equate");this.force=getInteger({data:e.force,defaultValue:1,validate:e=>0===e});this.from=e.from||"";this.to=e.to||""}}class EquateRange extends XFAObject{constructor(e){super(Ql,"equateRange");this.from=e.from||"";this.to=e.to||"";this._unicodeRange=e.unicodeRange||""}get unicodeRange(){const e=[],t=/U\+([0-9a-fA-F]+)/,a=this._unicodeRange;for(let n of a.split(",").map(e=>e.trim()).filter(e=>!!e)){n=n.split("-",2).map(e=>{const a=e.match(t);return a?parseInt(a[1],16):0});1===n.length&&n.push(n[0]);e.push(n)}return shadow(this,"unicodeRange",e)}}class Exclude extends ContentObject{constructor(e){super(Ql,"exclude")}[lo](){this[so]=this[so].trim().split(/\s+/).filter(e=>e&&["calculate","close","enter","exit","initialize","ready","validate"].includes(e))}}class ExcludeNS extends StringObject{constructor(e){super(Ql,"excludeNS")}}class FlipLabel extends OptionObject{constructor(e){super(Ql,"flipLabel",["usePrinterSetting","on","off"])}}class config_FontInfo extends XFAObject{constructor(e){super(Ql,"fontInfo",!0);this.embed=null;this.map=null;this.subsetBelow=null;this.alwaysEmbed=new XFAObjectArray;this.defaultTypeface=new XFAObjectArray;this.neverEmbed=new XFAObjectArray}}class FormFieldFilling extends Option01{constructor(e){super(Ql,"formFieldFilling")}}class GroupParent extends StringObject{constructor(e){super(Ql,"groupParent")}}class IfEmpty extends OptionObject{constructor(e){super(Ql,"ifEmpty",["dataValue","dataGroup","ignore","remove"])}}class IncludeXDPContent extends StringObject{constructor(e){super(Ql,"includeXDPContent")}}class IncrementalLoad extends OptionObject{constructor(e){super(Ql,"incrementalLoad",["none","forwardOnly"])}}class IncrementalMerge extends Option01{constructor(e){super(Ql,"incrementalMerge")}}class Interactive extends Option01{constructor(e){super(Ql,"interactive")}}class Jog extends OptionObject{constructor(e){super(Ql,"jog",["usePrinterSetting","none","pageSet"])}}class LabelPrinter extends XFAObject{constructor(e){super(Ql,"labelPrinter",!0);this.name=getStringOption(e.name,["zpl","dpl","ipl","tcpl"]);this.batchOutput=null;this.flipLabel=null;this.fontInfo=null;this.xdc=null}}class Layout extends OptionObject{constructor(e){super(Ql,"layout",["paginate","panel"])}}class Level extends IntegerObject{constructor(e){super(Ql,"level",0,e=>e>0)}}class Linearized extends Option01{constructor(e){super(Ql,"linearized")}}class Locale extends StringObject{constructor(e){super(Ql,"locale")}}class LocaleSet extends StringObject{constructor(e){super(Ql,"localeSet")}}class Log extends XFAObject{constructor(e){super(Ql,"log",!0);this.mode=null;this.threshold=null;this.to=null;this.uri=null}}class MapElement extends XFAObject{constructor(e){super(Ql,"map",!0);this.equate=new XFAObjectArray;this.equateRange=new XFAObjectArray}}class MediumInfo extends XFAObject{constructor(e){super(Ql,"mediumInfo",!0);this.map=null}}class config_Message extends XFAObject{constructor(e){super(Ql,"message",!0);this.msgId=null;this.severity=null}}class Messaging extends XFAObject{constructor(e){super(Ql,"messaging",!0);this.message=new XFAObjectArray}}class Mode extends OptionObject{constructor(e){super(Ql,"mode",["append","overwrite"])}}class ModifyAnnots extends Option01{constructor(e){super(Ql,"modifyAnnots")}}class MsgId extends IntegerObject{constructor(e){super(Ql,"msgId",1,e=>e>=1)}}class NameAttr extends StringObject{constructor(e){super(Ql,"nameAttr")}}class NeverEmbed extends ContentObject{constructor(e){super(Ql,"neverEmbed")}}class NumberOfCopies extends IntegerObject{constructor(e){super(Ql,"numberOfCopies",null,e=>e>=2&&e<=5)}}class OpenAction extends XFAObject{constructor(e){super(Ql,"openAction",!0);this.destination=null}}class Output extends XFAObject{constructor(e){super(Ql,"output",!0);this.to=null;this.type=null;this.uri=null}}class OutputBin extends StringObject{constructor(e){super(Ql,"outputBin")}}class OutputXSL extends XFAObject{constructor(e){super(Ql,"outputXSL",!0);this.uri=null}}class Overprint extends OptionObject{constructor(e){super(Ql,"overprint",["none","both","draw","field"])}}class Packets extends StringObject{constructor(e){super(Ql,"packets")}[lo](){"*"!==this[so]&&(this[so]=this[so].trim().split(/\s+/).filter(e=>["config","datasets","template","xfdf","xslt"].includes(e)))}}class PageOffset extends XFAObject{constructor(e){super(Ql,"pageOffset");this.x=getInteger({data:e.x,defaultValue:"useXDCSetting",validate:e=>!0});this.y=getInteger({data:e.y,defaultValue:"useXDCSetting",validate:e=>!0})}}class PageRange extends StringObject{constructor(e){super(Ql,"pageRange")}[lo](){const e=this[so].trim().split(/\s+/).map(e=>parseInt(e,10)),t=[];for(let a=0,n=e.length;a!1)}}class Pcl extends XFAObject{constructor(e){super(Ql,"pcl",!0);this.name=e.name||"";this.batchOutput=null;this.fontInfo=null;this.jog=null;this.mediumInfo=null;this.outputBin=null;this.pageOffset=null;this.staple=null;this.xdc=null}}class Pdf extends XFAObject{constructor(e){super(Ql,"pdf",!0);this.name=e.name||"";this.adobeExtensionLevel=null;this.batchOutput=null;this.compression=null;this.creator=null;this.encryption=null;this.fontInfo=null;this.interactive=null;this.linearized=null;this.openAction=null;this.pdfa=null;this.producer=null;this.renderPolicy=null;this.scriptModel=null;this.silentPrint=null;this.submitFormat=null;this.tagged=null;this.version=null;this.viewerPreferences=null;this.xdc=null}}class Pdfa extends XFAObject{constructor(e){super(Ql,"pdfa",!0);this.amd=null;this.conformance=null;this.includeXDPContent=null;this.part=null}}class Permissions extends XFAObject{constructor(e){super(Ql,"permissions",!0);this.accessibleContent=null;this.change=null;this.contentCopy=null;this.documentAssembly=null;this.formFieldFilling=null;this.modifyAnnots=null;this.plaintextMetadata=null;this.print=null;this.printHighQuality=null}}class PickTrayByPDFSize extends Option01{constructor(e){super(Ql,"pickTrayByPDFSize")}}class config_Picture extends StringObject{constructor(e){super(Ql,"picture")}}class PlaintextMetadata extends Option01{constructor(e){super(Ql,"plaintextMetadata")}}class Presence extends OptionObject{constructor(e){super(Ql,"presence",["preserve","dissolve","dissolveStructure","ignore","remove"])}}class Present extends XFAObject{constructor(e){super(Ql,"present",!0);this.behaviorOverride=null;this.cache=null;this.common=null;this.copies=null;this.destination=null;this.incrementalMerge=null;this.layout=null;this.output=null;this.overprint=null;this.pagination=null;this.paginationOverride=null;this.script=null;this.validate=null;this.xdp=null;this.driver=new XFAObjectArray;this.labelPrinter=new XFAObjectArray;this.pcl=new XFAObjectArray;this.pdf=new XFAObjectArray;this.ps=new XFAObjectArray;this.submitUrl=new XFAObjectArray;this.webClient=new XFAObjectArray;this.zpl=new XFAObjectArray}}class Print extends Option01{constructor(e){super(Ql,"print")}}class PrintHighQuality extends Option01{constructor(e){super(Ql,"printHighQuality")}}class PrintScaling extends OptionObject{constructor(e){super(Ql,"printScaling",["appdefault","noScaling"])}}class PrinterName extends StringObject{constructor(e){super(Ql,"printerName")}}class Producer extends StringObject{constructor(e){super(Ql,"producer")}}class Ps extends XFAObject{constructor(e){super(Ql,"ps",!0);this.name=e.name||"";this.batchOutput=null;this.fontInfo=null;this.jog=null;this.mediumInfo=null;this.outputBin=null;this.staple=null;this.xdc=null}}class Range extends ContentObject{constructor(e){super(Ql,"range")}[lo](){this[so]=this[so].split(",",2).map(e=>e.split("-").map(e=>parseInt(e.trim(),10))).filter(e=>e.every(e=>!isNaN(e))).map(e=>{1===e.length&&e.push(e[0]);return e})}}class Record extends ContentObject{constructor(e){super(Ql,"record")}[lo](){this[so]=this[so].trim();const e=parseInt(this[so],10);!isNaN(e)&&e>=0&&(this[so]=e)}}class Relevant extends ContentObject{constructor(e){super(Ql,"relevant")}[lo](){this[so]=this[so].trim().split(/\s+/)}}class Rename extends ContentObject{constructor(e){super(Ql,"rename")}[lo](){this[so]=this[so].trim();(this[so].toLowerCase().startsWith("xml")||new RegExp("[\\p{L}_][\\p{L}\\d._\\p{M}-]*","u").test(this[so]))&&warn("XFA - Rename: invalid XFA name")}}class RenderPolicy extends OptionObject{constructor(e){super(Ql,"renderPolicy",["server","client"])}}class RunScripts extends OptionObject{constructor(e){super(Ql,"runScripts",["both","client","none","server"])}}class config_Script extends XFAObject{constructor(e){super(Ql,"script",!0);this.currentPage=null;this.exclude=null;this.runScripts=null}}class ScriptModel extends OptionObject{constructor(e){super(Ql,"scriptModel",["XFA","none"])}}class Severity extends OptionObject{constructor(e){super(Ql,"severity",["ignore","error","information","trace","warning"])}}class SilentPrint extends XFAObject{constructor(e){super(Ql,"silentPrint",!0);this.addSilentPrint=null;this.printerName=null}}class Staple extends XFAObject{constructor(e){super(Ql,"staple");this.mode=getStringOption(e.mode,["usePrinterSetting","on","off"])}}class StartNode extends StringObject{constructor(e){super(Ql,"startNode")}}class StartPage extends IntegerObject{constructor(e){super(Ql,"startPage",0,e=>!0)}}class SubmitFormat extends OptionObject{constructor(e){super(Ql,"submitFormat",["html","delegate","fdf","xml","pdf"])}}class SubmitUrl extends StringObject{constructor(e){super(Ql,"submitUrl")}}class SubsetBelow extends IntegerObject{constructor(e){super(Ql,"subsetBelow",100,e=>e>=0&&e<=100)}}class SuppressBanner extends Option01{constructor(e){super(Ql,"suppressBanner")}}class Tagged extends Option01{constructor(e){super(Ql,"tagged")}}class config_Template extends XFAObject{constructor(e){super(Ql,"template",!0);this.base=null;this.relevant=null;this.startPage=null;this.uri=null;this.xsl=null}}class Threshold extends OptionObject{constructor(e){super(Ql,"threshold",["trace","error","information","warning"])}}class To extends OptionObject{constructor(e){super(Ql,"to",["null","memory","stderr","stdout","system","uri"])}}class TemplateCache extends XFAObject{constructor(e){super(Ql,"templateCache");this.maxEntries=getInteger({data:e.maxEntries,defaultValue:5,validate:e=>e>=0})}}class Trace extends XFAObject{constructor(e){super(Ql,"trace",!0);this.area=new XFAObjectArray}}class Transform extends XFAObject{constructor(e){super(Ql,"transform",!0);this.groupParent=null;this.ifEmpty=null;this.nameAttr=null;this.picture=null;this.presence=null;this.rename=null;this.whitespace=null}}class Type extends OptionObject{constructor(e){super(Ql,"type",["none","ascii85","asciiHex","ccittfax","flate","lzw","runLength","native","xdp","mergedXDP"])}}class Uri extends StringObject{constructor(e){super(Ql,"uri")}}class config_Validate extends OptionObject{constructor(e){super(Ql,"validate",["preSubmit","prePrint","preExecute","preSave"])}}class ValidateApprovalSignatures extends ContentObject{constructor(e){super(Ql,"validateApprovalSignatures")}[lo](){this[so]=this[so].trim().split(/\s+/).filter(e=>["docReady","postSign"].includes(e))}}class ValidationMessaging extends OptionObject{constructor(e){super(Ql,"validationMessaging",["allMessagesIndividually","allMessagesTogether","firstMessageOnly","noMessages"])}}class Version extends OptionObject{constructor(e){super(Ql,"version",["1.7","1.6","1.5","1.4","1.3","1.2"])}}class VersionControl extends XFAObject{constructor(e){super(Ql,"VersionControl");this.outputBelow=getStringOption(e.outputBelow,["warn","error","update"]);this.sourceAbove=getStringOption(e.sourceAbove,["warn","error"]);this.sourceBelow=getStringOption(e.sourceBelow,["update","maintain"])}}class ViewerPreferences extends XFAObject{constructor(e){super(Ql,"viewerPreferences",!0);this.ADBE_JSConsole=null;this.ADBE_JSDebugger=null;this.addViewerPreferences=null;this.duplexOption=null;this.enforce=null;this.numberOfCopies=null;this.pageRange=null;this.pickTrayByPDFSize=null;this.printScaling=null}}class WebClient extends XFAObject{constructor(e){super(Ql,"webClient",!0);this.name=e.name?e.name.trim():"";this.fontInfo=null;this.xdc=null}}class Whitespace extends OptionObject{constructor(e){super(Ql,"whitespace",["preserve","ltrim","normalize","rtrim","trim"])}}class Window extends ContentObject{constructor(e){super(Ql,"window")}[lo](){const e=this[so].split(",",2).map(e=>parseInt(e.trim(),10));if(e.some(e=>isNaN(e)))this[so]=[0,0];else{1===e.length&&e.push(e[0]);this[so]=e}}}class Xdc extends XFAObject{constructor(e){super(Ql,"xdc",!0);this.uri=new XFAObjectArray;this.xsl=new XFAObjectArray}}class Xdp extends XFAObject{constructor(e){super(Ql,"xdp",!0);this.packets=null}}class Xsl extends XFAObject{constructor(e){super(Ql,"xsl",!0);this.debug=null;this.uri=null}}class Zpl extends XFAObject{constructor(e){super(Ql,"zpl",!0);this.name=e.name?e.name.trim():"";this.batchOutput=null;this.flipLabel=null;this.fontInfo=null;this.xdc=null}}class ConfigNamespace{static[hl](e,t){if(Object.hasOwn(ConfigNamespace,e))return ConfigNamespace[e](t)}static acrobat(e){return new Acrobat(e)}static acrobat7(e){return new Acrobat7(e)}static ADBE_JSConsole(e){return new ADBE_JSConsole(e)}static ADBE_JSDebugger(e){return new ADBE_JSDebugger(e)}static addSilentPrint(e){return new AddSilentPrint(e)}static addViewerPreferences(e){return new AddViewerPreferences(e)}static adjustData(e){return new AdjustData(e)}static adobeExtensionLevel(e){return new AdobeExtensionLevel(e)}static agent(e){return new Agent(e)}static alwaysEmbed(e){return new AlwaysEmbed(e)}static amd(e){return new Amd(e)}static area(e){return new config_Area(e)}static attributes(e){return new Attributes(e)}static autoSave(e){return new AutoSave(e)}static base(e){return new Base(e)}static batchOutput(e){return new BatchOutput(e)}static behaviorOverride(e){return new BehaviorOverride(e)}static cache(e){return new Cache(e)}static change(e){return new Change(e)}static common(e){return new Common(e)}static compress(e){return new Compress(e)}static compressLogicalStructure(e){return new CompressLogicalStructure(e)}static compressObjectStream(e){return new CompressObjectStream(e)}static compression(e){return new Compression(e)}static config(e){return new Config(e)}static conformance(e){return new Conformance(e)}static contentCopy(e){return new ContentCopy(e)}static copies(e){return new Copies(e)}static creator(e){return new Creator(e)}static currentPage(e){return new CurrentPage(e)}static data(e){return new Data(e)}static debug(e){return new Debug(e)}static defaultTypeface(e){return new DefaultTypeface(e)}static destination(e){return new Destination(e)}static documentAssembly(e){return new DocumentAssembly(e)}static driver(e){return new Driver(e)}static duplexOption(e){return new DuplexOption(e)}static dynamicRender(e){return new DynamicRender(e)}static embed(e){return new Embed(e)}static encrypt(e){return new config_Encrypt(e)}static encryption(e){return new config_Encryption(e)}static encryptionLevel(e){return new EncryptionLevel(e)}static enforce(e){return new Enforce(e)}static equate(e){return new Equate(e)}static equateRange(e){return new EquateRange(e)}static exclude(e){return new Exclude(e)}static excludeNS(e){return new ExcludeNS(e)}static flipLabel(e){return new FlipLabel(e)}static fontInfo(e){return new config_FontInfo(e)}static formFieldFilling(e){return new FormFieldFilling(e)}static groupParent(e){return new GroupParent(e)}static ifEmpty(e){return new IfEmpty(e)}static includeXDPContent(e){return new IncludeXDPContent(e)}static incrementalLoad(e){return new IncrementalLoad(e)}static incrementalMerge(e){return new IncrementalMerge(e)}static interactive(e){return new Interactive(e)}static jog(e){return new Jog(e)}static labelPrinter(e){return new LabelPrinter(e)}static layout(e){return new Layout(e)}static level(e){return new Level(e)}static linearized(e){return new Linearized(e)}static locale(e){return new Locale(e)}static localeSet(e){return new LocaleSet(e)}static log(e){return new Log(e)}static map(e){return new MapElement(e)}static mediumInfo(e){return new MediumInfo(e)}static message(e){return new config_Message(e)}static messaging(e){return new Messaging(e)}static mode(e){return new Mode(e)}static modifyAnnots(e){return new ModifyAnnots(e)}static msgId(e){return new MsgId(e)}static nameAttr(e){return new NameAttr(e)}static neverEmbed(e){return new NeverEmbed(e)}static numberOfCopies(e){return new NumberOfCopies(e)}static openAction(e){return new OpenAction(e)}static output(e){return new Output(e)}static outputBin(e){return new OutputBin(e)}static outputXSL(e){return new OutputXSL(e)}static overprint(e){return new Overprint(e)}static packets(e){return new Packets(e)}static pageOffset(e){return new PageOffset(e)}static pageRange(e){return new PageRange(e)}static pagination(e){return new Pagination(e)}static paginationOverride(e){return new PaginationOverride(e)}static part(e){return new Part(e)}static pcl(e){return new Pcl(e)}static pdf(e){return new Pdf(e)}static pdfa(e){return new Pdfa(e)}static permissions(e){return new Permissions(e)}static pickTrayByPDFSize(e){return new PickTrayByPDFSize(e)}static picture(e){return new config_Picture(e)}static plaintextMetadata(e){return new PlaintextMetadata(e)}static presence(e){return new Presence(e)}static present(e){return new Present(e)}static print(e){return new Print(e)}static printHighQuality(e){return new PrintHighQuality(e)}static printScaling(e){return new PrintScaling(e)}static printerName(e){return new PrinterName(e)}static producer(e){return new Producer(e)}static ps(e){return new Ps(e)}static range(e){return new Range(e)}static record(e){return new Record(e)}static relevant(e){return new Relevant(e)}static rename(e){return new Rename(e)}static renderPolicy(e){return new RenderPolicy(e)}static runScripts(e){return new RunScripts(e)}static script(e){return new config_Script(e)}static scriptModel(e){return new ScriptModel(e)}static severity(e){return new Severity(e)}static silentPrint(e){return new SilentPrint(e)}static staple(e){return new Staple(e)}static startNode(e){return new StartNode(e)}static startPage(e){return new StartPage(e)}static submitFormat(e){return new SubmitFormat(e)}static submitUrl(e){return new SubmitUrl(e)}static subsetBelow(e){return new SubsetBelow(e)}static suppressBanner(e){return new SuppressBanner(e)}static tagged(e){return new Tagged(e)}static template(e){return new config_Template(e)}static templateCache(e){return new TemplateCache(e)}static threshold(e){return new Threshold(e)}static to(e){return new To(e)}static trace(e){return new Trace(e)}static transform(e){return new Transform(e)}static type(e){return new Type(e)}static uri(e){return new Uri(e)}static validate(e){return new config_Validate(e)}static validateApprovalSignatures(e){return new ValidateApprovalSignatures(e)}static validationMessaging(e){return new ValidationMessaging(e)}static version(e){return new Version(e)}static versionControl(e){return new VersionControl(e)}static viewerPreferences(e){return new ViewerPreferences(e)}static webClient(e){return new WebClient(e)}static whitespace(e){return new Whitespace(e)}static window(e){return new Window(e)}static xdc(e){return new Xdc(e)}static xdp(e){return new Xdp(e)}static xsl(e){return new Xsl(e)}static zpl(e){return new Zpl(e)}}const Zl=ul.connectionSet.id;class ConnectionSet extends XFAObject{constructor(e){super(Zl,"connectionSet",!0);this.wsdlConnection=new XFAObjectArray;this.xmlConnection=new XFAObjectArray;this.xsdConnection=new XFAObjectArray}}class EffectiveInputPolicy extends XFAObject{constructor(e){super(Zl,"effectiveInputPolicy");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class EffectiveOutputPolicy extends XFAObject{constructor(e){super(Zl,"effectiveOutputPolicy");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class Operation extends StringObject{constructor(e){super(Zl,"operation");this.id=e.id||"";this.input=e.input||"";this.name=e.name||"";this.output=e.output||"";this.use=e.use||"";this.usehref=e.usehref||""}}class RootElement extends StringObject{constructor(e){super(Zl,"rootElement");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class SoapAction extends StringObject{constructor(e){super(Zl,"soapAction");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class SoapAddress extends StringObject{constructor(e){super(Zl,"soapAddress");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class connection_set_Uri extends StringObject{constructor(e){super(Zl,"uri");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class WsdlAddress extends StringObject{constructor(e){super(Zl,"wsdlAddress");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class WsdlConnection extends XFAObject{constructor(e){super(Zl,"wsdlConnection",!0);this.dataDescription=e.dataDescription||"";this.name=e.name||"";this.effectiveInputPolicy=null;this.effectiveOutputPolicy=null;this.operation=null;this.soapAction=null;this.soapAddress=null;this.wsdlAddress=null}}class XmlConnection extends XFAObject{constructor(e){super(Zl,"xmlConnection",!0);this.dataDescription=e.dataDescription||"";this.name=e.name||"";this.uri=null}}class XsdConnection extends XFAObject{constructor(e){super(Zl,"xsdConnection",!0);this.dataDescription=e.dataDescription||"";this.name=e.name||"";this.rootElement=null;this.uri=null}}class ConnectionSetNamespace{static[hl](e,t){if(Object.hasOwn(ConnectionSetNamespace,e))return ConnectionSetNamespace[e](t)}static connectionSet(e){return new ConnectionSet(e)}static effectiveInputPolicy(e){return new EffectiveInputPolicy(e)}static effectiveOutputPolicy(e){return new EffectiveOutputPolicy(e)}static operation(e){return new Operation(e)}static rootElement(e){return new RootElement(e)}static soapAction(e){return new SoapAction(e)}static soapAddress(e){return new SoapAddress(e)}static uri(e){return new connection_set_Uri(e)}static wsdlAddress(e){return new WsdlAddress(e)}static wsdlConnection(e){return new WsdlConnection(e)}static xmlConnection(e){return new XmlConnection(e)}static xsdConnection(e){return new XsdConnection(e)}}const ef=ul.datasets.id;class datasets_Data extends XmlObject{constructor(e){super(ef,"data",e)}[Mo](){return!0}}class Datasets extends XFAObject{constructor(e){super(ef,"datasets",!0);this.data=null;this.Signature=null}[Wo](e){const t=e[Uo];("data"===t&&e[Lo]===ef||"Signature"===t&&e[Lo]===ul.signature.id)&&(this[t]=e);this[Ji](e)}}class DatasetsNamespace{static[hl](e,t){if(Object.hasOwn(DatasetsNamespace,e))return DatasetsNamespace[e](t)}static datasets(e){return new Datasets(e)}static data(e){return new datasets_Data(e)}}const tf=ul.localeSet.id;class CalendarSymbols extends XFAObject{constructor(e){super(tf,"calendarSymbols",!0);this.name="gregorian";this.dayNames=new XFAObjectArray(2);this.eraNames=null;this.meridiemNames=null;this.monthNames=new XFAObjectArray(2)}}class CurrencySymbol extends StringObject{constructor(e){super(tf,"currencySymbol");this.name=getStringOption(e.name,["symbol","isoname","decimal"])}}class CurrencySymbols extends XFAObject{constructor(e){super(tf,"currencySymbols",!0);this.currencySymbol=new XFAObjectArray(3)}}class DatePattern extends StringObject{constructor(e){super(tf,"datePattern");this.name=getStringOption(e.name,["full","long","med","short"])}}class DatePatterns extends XFAObject{constructor(e){super(tf,"datePatterns",!0);this.datePattern=new XFAObjectArray(4)}}class DateTimeSymbols extends ContentObject{constructor(e){super(tf,"dateTimeSymbols")}}class Day extends StringObject{constructor(e){super(tf,"day")}}class DayNames extends XFAObject{constructor(e){super(tf,"dayNames",!0);this.abbr=getInteger({data:e.abbr,defaultValue:0,validate:e=>1===e});this.day=new XFAObjectArray(7)}}class Era extends StringObject{constructor(e){super(tf,"era")}}class EraNames extends XFAObject{constructor(e){super(tf,"eraNames",!0);this.era=new XFAObjectArray(2)}}class locale_set_Locale extends XFAObject{constructor(e){super(tf,"locale",!0);this.desc=e.desc||"";this.name="isoname";this.calendarSymbols=null;this.currencySymbols=null;this.datePatterns=null;this.dateTimeSymbols=null;this.numberPatterns=null;this.numberSymbols=null;this.timePatterns=null;this.typeFaces=null}}class locale_set_LocaleSet extends XFAObject{constructor(e){super(tf,"localeSet",!0);this.locale=new XFAObjectArray}}class Meridiem extends StringObject{constructor(e){super(tf,"meridiem")}}class MeridiemNames extends XFAObject{constructor(e){super(tf,"meridiemNames",!0);this.meridiem=new XFAObjectArray(2)}}class Month extends StringObject{constructor(e){super(tf,"month")}}class MonthNames extends XFAObject{constructor(e){super(tf,"monthNames",!0);this.abbr=getInteger({data:e.abbr,defaultValue:0,validate:e=>1===e});this.month=new XFAObjectArray(12)}}class NumberPattern extends StringObject{constructor(e){super(tf,"numberPattern");this.name=getStringOption(e.name,["full","long","med","short"])}}class NumberPatterns extends XFAObject{constructor(e){super(tf,"numberPatterns",!0);this.numberPattern=new XFAObjectArray(4)}}class NumberSymbol extends StringObject{constructor(e){super(tf,"numberSymbol");this.name=getStringOption(e.name,["decimal","grouping","percent","minus","zero"])}}class NumberSymbols extends XFAObject{constructor(e){super(tf,"numberSymbols",!0);this.numberSymbol=new XFAObjectArray(5)}}class TimePattern extends StringObject{constructor(e){super(tf,"timePattern");this.name=getStringOption(e.name,["full","long","med","short"])}}class TimePatterns extends XFAObject{constructor(e){super(tf,"timePatterns",!0);this.timePattern=new XFAObjectArray(4)}}class TypeFace extends XFAObject{constructor(e){super(tf,"typeFace",!0);this.name=""|e.name}}class TypeFaces extends XFAObject{constructor(e){super(tf,"typeFaces",!0);this.typeFace=new XFAObjectArray}}class LocaleSetNamespace{static[hl](e,t){if(Object.hasOwn(LocaleSetNamespace,e))return LocaleSetNamespace[e](t)}static calendarSymbols(e){return new CalendarSymbols(e)}static currencySymbol(e){return new CurrencySymbol(e)}static currencySymbols(e){return new CurrencySymbols(e)}static datePattern(e){return new DatePattern(e)}static datePatterns(e){return new DatePatterns(e)}static dateTimeSymbols(e){return new DateTimeSymbols(e)}static day(e){return new Day(e)}static dayNames(e){return new DayNames(e)}static era(e){return new Era(e)}static eraNames(e){return new EraNames(e)}static locale(e){return new locale_set_Locale(e)}static localeSet(e){return new locale_set_LocaleSet(e)}static meridiem(e){return new Meridiem(e)}static meridiemNames(e){return new MeridiemNames(e)}static month(e){return new Month(e)}static monthNames(e){return new MonthNames(e)}static numberPattern(e){return new NumberPattern(e)}static numberPatterns(e){return new NumberPatterns(e)}static numberSymbol(e){return new NumberSymbol(e)}static numberSymbols(e){return new NumberSymbols(e)}static timePattern(e){return new TimePattern(e)}static timePatterns(e){return new TimePatterns(e)}static typeFace(e){return new TypeFace(e)}static typeFaces(e){return new TypeFaces(e)}}const af=ul.signature.id;class signature_Signature extends XFAObject{constructor(e){super(af,"signature",!0)}}class SignatureNamespace{static[hl](e,t){if(Object.hasOwn(SignatureNamespace,e))return SignatureNamespace[e](t)}static signature(e){return new signature_Signature(e)}}const nf=ul.stylesheet.id;class Stylesheet extends XFAObject{constructor(e){super(nf,"stylesheet",!0)}}class StylesheetNamespace{static[hl](e,t){if(Object.hasOwn(StylesheetNamespace,e))return StylesheetNamespace[e](t)}static stylesheet(e){return new Stylesheet(e)}}const sf=ul.xdp.id;class xdp_Xdp extends XFAObject{constructor(e){super(sf,"xdp",!0);this.uuid=e.uuid||"";this.timeStamp=e.timeStamp||"";this.config=null;this.connectionSet=null;this.datasets=null;this.localeSet=null;this.stylesheet=new XFAObjectArray;this.template=null}[Ko](e){const t=ul[e[Uo]];return t&&e[Lo]===t.id}}class XdpNamespace{static[hl](e,t){if(Object.hasOwn(XdpNamespace,e))return XdpNamespace[e](t)}static xdp(e){return new xdp_Xdp(e)}}const rf=ul.xhtml.id,of=Symbol(),lf=new Set(["color","font","font-family","font-size","font-stretch","font-style","font-weight","margin","margin-bottom","margin-left","margin-right","margin-top","letter-spacing","line-height","orphans","page-break-after","page-break-before","page-break-inside","tab-interval","tab-stop","text-align","text-decoration","text-indent","vertical-align","widows","kerning-mode","xfa-font-horizontal-scale","xfa-font-vertical-scale","xfa-spacerun","xfa-tab-stops"]),ff=new Map([["page-break-after","breakAfter"],["page-break-before","breakBefore"],["page-break-inside","breakInside"],["kerning-mode",e=>"none"===e?"none":"normal"],["xfa-font-horizontal-scale",e=>`scaleX(${Math.max(0,parseInt(e,10)/100).toFixed(2)})`],["xfa-font-vertical-scale",e=>`scaleY(${Math.max(0,parseInt(e,10)/100).toFixed(2)})`],["xfa-spacerun",""],["xfa-tab-stops",""],["font-size",(e,t)=>measureToString(.99*(e=t.fontSize=Math.abs(getMeasurement(e))))],["letter-spacing",e=>measureToString(getMeasurement(e))],["line-height",e=>measureToString(getMeasurement(e))],["margin",e=>measureToString(getMeasurement(e))],["margin-bottom",e=>measureToString(getMeasurement(e))],["margin-left",e=>measureToString(getMeasurement(e))],["margin-right",e=>measureToString(getMeasurement(e))],["margin-top",e=>measureToString(getMeasurement(e))],["text-indent",e=>measureToString(getMeasurement(e))],["font-family",e=>e],["vertical-align",e=>measureToString(getMeasurement(e))]]),cf=/\s+/g,hf=/[\r\n]+/g,uf=/\r\n?/g;function mapStyle(e,t,a){const n=Object.create(null);if(!e)return n;const s=Object.create(null);for(const[t,a]of e.split(";").map(e=>e.split(":",2))){const e=ff.get(t);if(""===e)continue;let r=a;e&&(r="string"==typeof e?e:e(a,s));t.endsWith("scale")?n.transform=n.transform?`${n[t]} ${r}`:r:n[t.replaceAll(/-([a-zA-Z])/g,(e,t)=>t.toUpperCase())]=r}n.fontFamily&&setFontFamily({typeface:n.fontFamily,weight:n.fontWeight||"normal",posture:n.fontStyle||"normal",size:s.fontSize||0},t,t[xo].fontFinder,n);if(a&&n.verticalAlign&&"0px"!==n.verticalAlign&&n.fontSize){const e=.583,t=.333,a=getMeasurement(n.fontSize);n.fontSize=measureToString(a*e);n.verticalAlign=measureToString(Math.sign(getMeasurement(n.verticalAlign))*a*t)}a&&n.fontSize&&(n.fontSize=`calc(${n.fontSize} * var(--total-scale-factor))`);fixTextIndent(n);return n}const mf=new Set(["body","html"]);class XhtmlObject extends XmlObject{constructor(e,t){super(rf,t);this[of]=!1;this.style=e.style||""}[Zi](e){super[Zi](e);this.style=function checkStyle(e){return e.style?e.style.split(";").filter(e=>!!e.trim()).map(e=>e.split(":",2).map(e=>e.trim())).filter(([t,a])=>{"font-family"===t&&e[xo].usedTypefaces.add(a);return lf.has(t)}).map(e=>e.join(":")).join(";"):""}(this)}[$i](){return!mf.has(this[Uo])}[Go](e,t=!1){if(t)this[of]=!0;else{e=e.replaceAll(hf,"");this.style.includes("xfa-spacerun:yes")||(e=e.replaceAll(cf," "))}e&&(this[so]+=e)}[Vo](e,t=!0){const a=Object.create(null),n={top:NaN,bottom:NaN,left:NaN,right:NaN};let s=null;for(const[e,t]of this.style.split(";").map(e=>e.split(":",2)))switch(e){case"font-family":a.typeface=stripQuotes(t);break;case"font-size":a.size=getMeasurement(t);break;case"font-weight":a.weight=t;break;case"font-style":a.posture=t;break;case"letter-spacing":a.letterSpacing=getMeasurement(t);break;case"margin":const e=t.split(/ \t/).map(e=>getMeasurement(e));switch(e.length){case 1:n.top=n.bottom=n.left=n.right=e[0];break;case 2:n.top=n.bottom=e[0];n.left=n.right=e[1];break;case 3:n.top=e[0];n.bottom=e[2];n.left=n.right=e[1];break;case 4:n.top=e[0];n.left=e[1];n.bottom=e[2];n.right=e[3]}break;case"margin-top":n.top=getMeasurement(t);break;case"margin-bottom":n.bottom=getMeasurement(t);break;case"margin-left":n.left=getMeasurement(t);break;case"margin-right":n.right=getMeasurement(t);break;case"line-height":s=getMeasurement(t)}e.pushData(a,n,s);if(this[so])e.addString(this[so]);else for(const t of this[ko]())"#text"!==t[Uo]?t[Vo](e):e.addString(t[so]);t&&e.popFont()}[ol](e){const t=[];this[oo]={children:t};this[Qi]({});if(0===t.length&&!this[so])return HTMLResult.EMPTY;let a;a=this[of]?this[so]?this[so].replaceAll(uf,"\n"):void 0:this[so]||void 0;return HTMLResult.success({name:this[Uo],attributes:{href:this.href,style:mapStyle(this.style,this,this[of])},children:t,value:a})}}class A extends XhtmlObject{constructor(e){super(e,"a");this.href=fixURL(e.href)||""}}class B extends XhtmlObject{constructor(e){super(e,"b")}[Vo](e){e.pushFont({weight:"bold"});super[Vo](e);e.popFont()}}class Body extends XhtmlObject{constructor(e){super(e,"body")}[ol](e){const t=super[ol](e),{html:a}=t;if(!a)return HTMLResult.EMPTY;a.name="div";a.attributes.class=["xfaRich"];return t}}class Br extends XhtmlObject{constructor(e){super(e,"br")}[rl](){return"\n"}[Vo](e){e.addString("\n")}[ol](e){return HTMLResult.success({name:"br"})}}class Html extends XhtmlObject{constructor(e){super(e,"html")}[ol](e){const t=[];this[oo]={children:t};this[Qi]({});if(0===t.length)return HTMLResult.success({name:"div",attributes:{class:["xfaRich"],style:{}},value:this[so]||""});if(1===t.length){const e=t[0];if(e.attributes?.class.includes("xfaRich"))return HTMLResult.success(e)}return HTMLResult.success({name:"div",attributes:{class:["xfaRich"],style:{}},children:t})}}class I extends XhtmlObject{constructor(e){super(e,"i")}[Vo](e){e.pushFont({posture:"italic"});super[Vo](e);e.popFont()}}class Li extends XhtmlObject{constructor(e){super(e,"li")}}class Ol extends XhtmlObject{constructor(e){super(e,"ol")}}class P extends XhtmlObject{constructor(e){super(e,"p")}[Vo](e){super[Vo](e,!1);e.addString("\n");e.addPara();e.popFont()}[rl](){return this[So]()[ko]().at(-1)===this?super[rl]():super[rl]()+"\n"}}class Span extends XhtmlObject{constructor(e){super(e,"span")}}class Sub extends XhtmlObject{constructor(e){super(e,"sub")}}class Sup extends XhtmlObject{constructor(e){super(e,"sup")}}class Ul extends XhtmlObject{constructor(e){super(e,"ul")}}class XhtmlNamespace{static[hl](e,t){if(Object.hasOwn(XhtmlNamespace,e))return XhtmlNamespace[e](t)}static a(e){return new A(e)}static b(e){return new B(e)}static body(e){return new Body(e)}static br(e){return new Br(e)}static html(e){return new Html(e)}static i(e){return new I(e)}static li(e){return new Li(e)}static ol(e){return new Ol(e)}static p(e){return new P(e)}static span(e){return new Span(e)}static sub(e){return new Sub(e)}static sup(e){return new Sup(e)}static ul(e){return new Ul(e)}}const pf={config:ConfigNamespace,connection:ConnectionSetNamespace,datasets:DatasetsNamespace,localeSet:LocaleSetNamespace,signature:SignatureNamespace,stylesheet:StylesheetNamespace,template:TemplateNamespace,xdp:XdpNamespace,xhtml:XhtmlNamespace};class UnknownNamespace{constructor(e){this.namespaceId=e}[hl](e,t){return new XmlObject(this.namespaceId,e,t)}}class Root extends XFAObject{constructor(e){super(-1,"root",Object.create(null));this.element=null;this[Io]=e}[Wo](e){this.element=e;return!0}[lo](){super[lo]();if(this.element.template instanceof Template){this[Io].set(Qo,this.element);this.element.template[Zo](this[Io]);this.element.template[Io]=this[Io]}}}class Empty extends XFAObject{constructor(){super(-1,"",Object.create(null))}[Wo](e){return!1}}class Builder{constructor(e=null){this._namespaceStack=[];this._nsAgnosticLevel=0;this._namespacePrefixes=new Map;this._namespaces=new Map;this._nextNsId=Math.max(...Object.values(ul).map(({id:e})=>e));this._currentNamespace=e||new UnknownNamespace(++this._nextNsId)}buildRoot(e){return new Root(e)}build({nsPrefix:e,name:t,attributes:a,namespace:n,prefixes:s}){const r=null!==n;if(r){this._namespaceStack.push(this._currentNamespace);this._currentNamespace=this._searchNamespace(n)}s&&this._addNamespacePrefix(s);if(Object.hasOwn(a,Xo)){const e=pf.datasets,t=a[Xo];let n=null;for(const[a,s]of Object.entries(t)){if(this._getNamespaceToUse(a)===e){n={xfa:s};break}}n?a[Xo]=n:delete a[Xo]}const i=this._getNamespaceToUse(e),o=i?.[hl](t,a)||new Empty;o[Mo]()&&this._nsAgnosticLevel++;(r||s||o[Mo]())&&(o[to]={hasNamespace:r,prefixes:s,nsAgnostic:o[Mo]()});return o}isNsAgnostic(){return this._nsAgnosticLevel>0}_searchNamespace(e){let t=this._namespaces.get(e);if(t)return t;for(const[a,{check:n}]of Object.entries(ul))if(n(e)){t=pf[a];if(t){this._namespaces.set(e,t);return t}break}t=new UnknownNamespace(++this._nextNsId);this._namespaces.set(e,t);return t}_addNamespacePrefix(e){for(const{prefix:t,value:a}of e){const e=this._searchNamespace(a);this._namespacePrefixes.getOrInsertComputed(t,makeArr).push(e)}}_getNamespaceToUse(e){if(!e)return this._currentNamespace;const t=this._namespacePrefixes.get(e);if(t?.length>0)return t.at(-1);warn(`Unknown namespace prefix: ${e}.`);return null}clean(e){const{hasNamespace:t,prefixes:a,nsAgnostic:n}=e;t&&(this._currentNamespace=this._namespaceStack.pop());a&&a.forEach(({prefix:e})=>{this._namespacePrefixes.get(e).pop()});n&&this._nsAgnosticLevel--}}class XFAParser extends XMLParserBase{constructor(e=null,t=!1){super();this._builder=new Builder(e);this._stack=[];this._globalData={usedTypefaces:new Set};this._ids=new Map;this._current=this._builder.buildRoot(this._ids);this._errorCode=Bi;this._whiteRegex=/^\s+$/;this._nbsps=/\xa0+/g;this._richText=t}parse(e){this.parseXml(e);if(this._errorCode===Bi){this._current[lo]();return this._current.element}}onText(e){e=e.replace(this._nbsps,e=>e.slice(1)+" ");this._richText||this._current[$i]()?this._current[Go](e,this._richText):this._whiteRegex.test(e)||this._current[Go](e.trim())}onCdata(e){this._current[Go](e)}_mkAttributes(e,t){let a=null,n=null;const s=Object.create({});for(const{name:r,value:i}of e)if("xmlns"===r)a?warn(`XFA - multiple namespace definition in <${t}>`):a=i;else if(r.startsWith("xmlns:")){const e=r.substring(6);n??=[];n.push({prefix:e,value:i})}else{const e=r.indexOf(":");if(-1===e)s[r]=i;else{const t=s[Xo]??=Object.create(null),[a,n]=[r.slice(0,e),r.slice(e+1)];(t[a]||=Object.create(null))[n]=i}}return[a,n,s]}_getNameAndPrefix(e,t){const a=e.indexOf(":");return-1===a?[e,null]:[e.substring(a+1),t?"":e.substring(0,a)]}onBeginElement(e,t,a){const[n,s,r]=this._mkAttributes(t,e),[i,o]=this._getNameAndPrefix(e,this._builder.isNsAgnostic()),l=this._builder.build({nsPrefix:o,name:i,attributes:r,namespace:n,prefixes:s});l[xo]=this._globalData;if(a){l[lo]();this._current[Wo](l)&&l[tl](this._ids);l[Zi](this._builder)}else{this._stack.push(this._current);this._current=l}}onEndElement(e){const t=this._current;if(t[Ro]()&&"string"==typeof t[so]){const e=new XFAParser;e._globalData=this._globalData;const a=e.parse(t[so]);t[so]=null;t[Wo](a)}t[lo]();this._current=this._stack.pop();this._current[Wo](t)&&t[tl](this._ids);t[Zi](this._builder)}onError(e){this._errorCode=e}}class XFAFactory{constructor(e){try{this.root=(new XFAParser).parse(XFAFactory._createDocument(e));const t=new Binder(this.root);this.form=t.bind();this.dataHandler=new DataHandler(this.root,t.getData());this.form[xo].template=this.form}catch(e){warn(`XFA - an error occurred during parsing and binding: ${e}`)}}isValid(){return!(!this.root||!this.form)}_createPagesHelper(){const e=this.form[il]();return new Promise((t,a)=>{const nextIteration=()=>{try{const a=e.next();a.done?t(a.value):setTimeout(nextIteration,0)}catch(e){a(e)}};setTimeout(nextIteration,0)})}async _createPages(){try{this.pages=await this._createPagesHelper();this.dims=this.pages.children.map(e=>{const{width:t,height:a}=e.attributes.style;return[0,0,parseInt(t,10),parseInt(a,10)]})}catch(e){warn(`XFA - an error occurred during layout: ${e}`)}}getBoundingBox(e){return this.dims[e]}async getNumPages(){this.pages||await this._createPages();return this.dims.length}setImages(e){this.form[xo].images=e}setFonts(e){this.form[xo].fontFinder=new FontFinder(e);const t=[];for(let e of this.form[xo].usedTypefaces){e=stripQuotes(e);this.form[xo].fontFinder.find(e)||t.push(e)}return t.length>0?t:null}appendFonts(e,t){this.form[xo].fontFinder.add(e,t)}async getPages(){this.pages||await this._createPages();const e=this.pages;this.pages=null;return e}serializeData(e){return this.dataHandler.serialize(e)}static _createDocument(e){return e["/xdp:xdp"]?Object.values(e).join(""):e["xdp:xdp"]}static getRichTextAsHtml(e){if(!e||"string"!=typeof e)return null;try{let t=new XFAParser(XhtmlNamespace,!0).parse(e);if(!["body","xhtml"].includes(t[Uo])){const e=XhtmlNamespace.body({});e[Ji](t);t=e}const a=t[ol]();if(!a.success)return null;const{html:n}=a,{attributes:s}=n;if(s){s.class&&(s.class=s.class.filter(e=>!e.startsWith("xfa")));s.dir="auto"}return{html:n,str:t[rl]()}}catch(e){warn(`XFA - an error occurred during parsing of rich text: ${e}`)}return null}}class AnnotationFactory{static createGlobals(e){return Promise.all([e.ensureCatalog("acroForm"),e.ensureDoc("xfaDatasets"),e.ensureCatalog("structTreeRoot"),e.ensureCatalog("baseUrl"),e.ensureCatalog("attachments"),e.ensureCatalog("globalColorSpaceCache")]).then(([t,a,n,s,r,i])=>({pdfManager:e,acroForm:t instanceof Dict?t:Dict.empty,xfaDatasets:a,structTreeRoot:n,baseUrl:s,attachments:r,globalColorSpaceCache:i}),e=>{warn(`createGlobals: "${e}".`);return null})}static async create(e,t,a,n,s,r,i,o){const l=s?await this._getPageIndex(e,t,a.pdfManager):null;return a.pdfManager.ensure(this,"_create",[e,t,a,n,s,r,i,l,o])}static _create(e,t,a,n,s=!1,r=null,i=null,o=null,l=null){const f=e.fetchIfRef(t);if(!(f instanceof Dict))return;let c=f.get("Subtype");c=c instanceof Name?c.name:null;if(i&&!i.has(R[c?.toUpperCase()]))return null;const{acroForm:h,pdfManager:u}=a,m=t instanceof Ref?t.toString():`annot_${n.createObjId()}`,p={xref:e,ref:t,dict:f,subtype:c,id:m,annotationGlobals:a,collectFields:s,orphanFields:r,needAppearances:!s&&!0===h.get("NeedAppearances"),pageIndex:o,evaluatorOptions:u.evaluatorOptions,pageRef:l};switch(c){case"Link":return new LinkAnnotation(p);case"Text":return new TextAnnotation(p);case"Widget":let e=getInheritableProperty({dict:f,key:"FT"});e=e instanceof Name?e.name:null;switch(e){case"Tx":return new TextWidgetAnnotation(p);case"Btn":return new ButtonWidgetAnnotation(p);case"Ch":return new ChoiceWidgetAnnotation(p);case"Sig":return new SignatureWidgetAnnotation(p)}warn(`Unimplemented widget field type "${e}", falling back to base field type.`);return new WidgetAnnotation(p);case"Popup":return new PopupAnnotation(p);case"FreeText":return new FreeTextAnnotation(p);case"Line":return new LineAnnotation(p);case"Square":return new SquareAnnotation(p);case"Circle":return new CircleAnnotation(p);case"PolyLine":return new PolylineAnnotation(p);case"Polygon":return new PolygonAnnotation(p);case"Caret":return new CaretAnnotation(p);case"Ink":return new InkAnnotation(p);case"Highlight":return new HighlightAnnotation(p);case"Underline":return new UnderlineAnnotation(p);case"Squiggly":return new SquigglyAnnotation(p);case"StrikeOut":return new StrikeOutAnnotation(p);case"Stamp":return new StampAnnotation(p);case"FileAttachment":return new FileAttachmentAnnotation(p);default:s||warn(c?`Unimplemented annotation type "${c}", falling back to base annotation.`:"Annotation is missing the required /Subtype.");return new Annotation(p)}}static async _getPageIndex(e,t,a){try{const n=await e.fetchIfRefAsync(t);if(!(n instanceof Dict))return-1;const s=n.getRaw("P");if(s instanceof Ref)try{return await a.ensureCatalog("getPageIndex",[s])}catch(e){info(`_getPageIndex -- not a valid page reference: "${e}".`)}if(n.has("Kids"))return-1;const r=await a.ensureDoc("numPages");for(let e=0;ee/255)||t}function getQuadPoints(e,t){const a=e.getArray("QuadPoints");if(!isNumberArray(a,null)||0===a.length||a.length%8>0)return null;const n=new Float32Array(a.length);for(let e=0,s=a.length;et[2]||pt[3]))return null;n.set([u,d,m,d,u,p,m,p],e)}return n}function getTransformMatrix(e,t,n){const s=a.slice();Util.axialAlignedBoundingBox(t,n,s);const[r,i,o,l]=s;if(r===o||i===l)return[1,0,0,1,e[0],e[1]];const f=(e[2]-e[0])/(o-r),c=(e[3]-e[1])/(l-i);return[f,0,0,c,e[0]-r*f,e[1]-i*c]}class Annotation{constructor(e){const{annotationGlobals:t,dict:a,orphanFields:n,ref:s,subtype:r,xref:i}=e,o=n?.get(s);o&&a.set("Parent",o);this.setTitle(a.get("T"));this.setContents(a.get("Contents"));this.setModificationDate(a.get("M"));this.setFlags(a.get("F"));this.setRectangle(a.getArray("Rect"));this.setColor(a.getArray("C"));this.setBorderStyle(a);this.setAppearance(a);this.setOptionalContent(a);const l=a.get("MK");this.setBorderAndBackgroundColors(l);this.setRotation(l,a);this.ref=e.ref instanceof Ref?e.ref:null;this._streams=[];this.appearance&&this._streams.push(this.appearance);const f=!!(this.flags&L),c=!!(this.flags&U);this.data={annotationType:R[r?.toUpperCase()],annotationFlags:this.flags,borderStyle:this.borderStyle,color:this.color,backgroundColor:this.backgroundColor,borderColor:this.borderColor,rotation:this.rotation,contentsObj:this._contents,hasAppearance:!!this.appearance,id:e.id,modificationDate:this.modificationDate,rect:this.rectangle,subtype:r,hasOwnCanvas:!1,noRotate:!!(this.flags&_),noHTML:f&&c,isEditable:!1,structParent:-1};if(t.structTreeRoot){let n=a.get("StructParent");this.data.structParent=n=Number.isInteger(n)&&n>=0?n:-1;t.structTreeRoot.addAnnotationIdToPage(e.pageRef,n)}if(e.collectFields){const t=a.get("Kids");if(Array.isArray(t)){const e=[];for(const a of t)a instanceof Ref&&e.push(a.toString());0!==e.length&&(this.data.kidIds=e)}this.data.actions=collectActions(i,a,ie);this.data.fieldName=this._constructFieldName(a);this.data.pageIndex=e.pageIndex}const h=a.get("IT");h instanceof Name&&(this.data.it=h.name);this._isOffscreenCanvasSupported=e.evaluatorOptions.isOffscreenCanvasSupported;this._fallbackFontDict=null;this._needAppearances=!1}_hasFlag(e,t){return!!(e&t)}_buildFlags(e,t){let{flags:a}=this;if(void 0===e){if(void 0===t)return;return t?a&~E:a&~N|E}if(e){a|=E;return t?a&~z|N:a&~N|z}a&=~(N|z);return t?a&~E:a|E}_isViewable(e){return!this._hasFlag(e,M)&&!this._hasFlag(e,z)}_isPrintable(e){return this._hasFlag(e,E)&&!this._hasFlag(e,N)&&!this._hasFlag(e,M)}mustBeViewed(e,t){const a=e?.get(this.data.id)?.noView;return void 0!==a?!a:this.viewable&&!this._hasFlag(this.flags,N)}mustBePrinted(e){const t=e?.get(this.data.id)?.noPrint;return void 0!==t?!t:this.printable}mustBeViewedWhenEditing(e,t=null){return e?!this.data.isEditable:!t?.has(this.data.id)}get viewable(){return null!==this.data.quadPoints&&(0===this.flags||this._isViewable(this.flags))}get printable(){return null!==this.data.quadPoints&&(0!==this.flags&&this._isPrintable(this.flags))}_parseStringHelper(e){const t="string"==typeof e?stringToPDFString(e):"";return{str:t,dir:t&&"rtl"===bidi(t).dir?"rtl":"ltr"}}setDefaultAppearance(e){const{dict:t,annotationGlobals:a}=e,n=getInheritableProperty({dict:t,key:"DA"})||a.acroForm.get("DA");this._defaultAppearance="string"==typeof n?n:"";this.data.defaultAppearanceData=parseDefaultAppearance(this._defaultAppearance)}setTitle(e){this._title=this._parseStringHelper(e)}setContents(e){this._contents=this._parseStringHelper(e)}setModificationDate(e){this.modificationDate="string"==typeof e?e:null}setFlags(e){this.flags=Number.isInteger(e)&&e>0?e:0;this.flags&M&&"Annotation"!==this.constructor.name&&(this.flags^=M)}hasFlag(e){return this._hasFlag(this.flags,e)}setRectangle(e){this.rectangle=lookupNormalRect(e,[0,0,0,0])}setColor(e){this.color=getRgbColor(e)}setLineEndings(e){this.lineEndings=["None","None"];if(Array.isArray(e)&&2===e.length)for(let t=0;t<2;t++){const a=e[t];if(a instanceof Name)switch(a.name){case"None":continue;case"Square":case"Circle":case"Diamond":case"OpenArrow":case"ClosedArrow":case"Butt":case"ROpenArrow":case"RClosedArrow":case"Slash":this.lineEndings[t]=a.name;continue}warn(`Ignoring invalid lineEnding: ${a}`)}}setRotation(e,t){this.rotation=0;let a=e instanceof Dict?e.get("R")||0:t.get("Rotate")||0;if(Number.isInteger(a)&&0!==a){a%=360;a<0&&(a+=360);a%90==0&&(this.rotation=a)}}setBorderAndBackgroundColors(e){if(e instanceof Dict){this.borderColor=getRgbColor(e.getArray("BC"),null);this.backgroundColor=getRgbColor(e.getArray("BG"),null)}else this.borderColor=this.backgroundColor=null}setBorderStyle(e){this.borderStyle=new AnnotationBorderStyle;if(e instanceof Dict)if(e.has("BS")){const t=e.get("BS");if(t instanceof Dict){const e=t.get("Type");if(!e||isName(e,"Border")){this.borderStyle.setWidth(t.get("W"),this.rectangle);this.borderStyle.setStyle(t.get("S"));this.borderStyle.setDashArray(t.getArray("D"))}}}else if(e.has("Border")){const t=e.getArray("Border");if(Array.isArray(t))if(t.length>=3){this.borderStyle.setHorizontalCornerRadius(t[0]);this.borderStyle.setVerticalCornerRadius(t[1]);this.borderStyle.setWidth(t[2],this.rectangle);4===t.length&&this.borderStyle.setDashArray(t[3],!0)}else 0===t.length&&this.borderStyle.setWidth(0)}else this.borderStyle.setWidth(0)}setAppearance(e){this.appearance=null;const t=e.get("AP");if(!(t instanceof Dict))return;const a=t.get("N");if(a instanceof BaseStream){this.appearance=a;return}if(!(a instanceof Dict))return;const n=e.get("AS");if(!(n instanceof Name&&a.has(n.name)))return;const s=a.get(n.name);s instanceof BaseStream&&(this.appearance=s)}setOptionalContent(e){this.oc=null;const t=e.get("OC");t instanceof Name?warn("setOptionalContent: Support for /Name-entry is not implemented."):t instanceof Dict&&(this.oc=t)}async loadResources(e,t){const a=await t.dict.getAsync("Resources");a&&await ObjectLoader.load(a,e,a.xref);return a}async getOperatorList(e,t,a,n){const{hasOwnCanvas:s,id:r,rect:i}=this.data;let o=this.appearance;const f=!!(s&&a&l);if(f&&(0===this.width||0===this.height)){this.data.hasOwnCanvas=!1;return{opList:new OperatorList,separateForm:!1,separateCanvas:!1}}if(!o){if(!f)return{opList:new OperatorList,separateForm:!1,separateCanvas:!1};o=new StringStream("");o.dict=new Dict}const c=o.dict,h=await this.loadResources(da,o),u=lookupRect(c.getArray("BBox"),[0,0,1,1]),m=lookupMatrix(c.getArray("Matrix"),pa),p=getTransformMatrix(i,u,m),d=new OperatorList;let g;this.oc&&(g=await e.parseMarkedContentProps(this.oc,null));void 0!==g&&d.addOp(xt,["OC",g]);d.addOp(Bt,[r,i,p,m,f]);await e.getOperatorList({stream:o,task:t,resources:h,operatorList:d,fallbackFontDict:this._fallbackFontDict});d.addOp(Dt,[]);void 0!==g&&d.addOp(Ct,[]);this.reset();return{opList:d,separateForm:!1,separateCanvas:f}}async save(e,t,a,n){return null}get overlaysTextContent(){return!1}get hasTextContent(){return!1}async extractTextContent(e,t,a){if(!this.appearance)return;const n=await this.loadResources(ga,this.appearance),s=[],r=[];let i=null;const o={desiredSize:Math.Infinity,ready:!0,enqueue(e,t){for(const t of e.items)if(void 0!==t.str){i||=t.transform.slice(-2);r.push(t.str);if(t.hasEOL){s.push(r.join("").trimEnd());r.length=0}}}};await e.getTextContent({stream:this.appearance,task:t,resources:n,includeMarkedContent:!0,keepWhiteSpace:!0,sink:o,viewBox:a});this.reset();r.length&&s.push(r.join("").trimEnd());if(s.length>1||s[0]){const e=this.appearance.dict,t=lookupRect(e.getArray("BBox"),null),a=lookupMatrix(e.getArray("Matrix"),null);this.data.textPosition=this._transformPoint(i,t,a);this.data.textContent=s}}_transformPoint(e,t,a){const{rect:n}=this.data;t||=[0,0,1,1];a||=[1,0,0,1,0,0];const s=getTransformMatrix(n,t,a);s[4]-=n[0];s[5]-=n[1];const r=e.slice();Util.applyTransform(r,s);Util.applyTransform(r,a);return r}getFieldObject(){return this.data.kidIds?{id:this.data.id,actions:this.data.actions,name:this.data.fieldName,strokeColor:this.data.borderColor,fillColor:this.data.backgroundColor,type:"",kidIds:this.data.kidIds,page:this.data.pageIndex,rotation:this.rotation}:null}reset(){for(const e of this._streams)e.reset()}_constructFieldName(e){if(!e.has("T")&&!e.has("Parent")){warn("Unknown field name, falling back to empty field name.");return""}if(!e.has("Parent"))return stringToPDFString(e.get("T"));const t=[];e.has("T")&&t.unshift(stringToPDFString(e.get("T")));let a=e;const n=new RefSet;e.objId&&n.put(e.objId);for(;a.has("Parent");){a=a.get("Parent");if(!(a instanceof Dict)||a.objId&&n.has(a.objId))break;a.objId&&n.put(a.objId);a.has("T")&&t.unshift(stringToPDFString(a.get("T")))}return t.join(".")}get width(){return this.data.rect[2]-this.data.rect[0]}get height(){return this.data.rect[3]-this.data.rect[1]}}class AnnotationBorderStyle{width=1;rawWidth=1;style=te;dashArray=[3];horizontalCornerRadius=0;verticalCornerRadius=0;setWidth(e,t=[0,0,0,0]){if(e instanceof Name)this.width=0;else if("number"==typeof e){if(e>0){this.rawWidth=e;const a=(t[2]-t[0])/2,n=(t[3]-t[1])/2;if(a>0&&n>0&&(e>a||e>n)){warn(`AnnotationBorderStyle.setWidth - ignoring width: ${e}`);e=1}}this.width=e}}setStyle(e){if(e instanceof Name)switch(e.name){case"S":this.style=te;break;case"D":this.style=ae;break;case"B":this.style=ne;break;case"I":this.style=se;break;case"U":this.style=re}}setDashArray(e,t=!1){if(Array.isArray(e)){let a=!0,n=!0;for(const t of e){if(!(+t>=0)){a=!1;break}t>0&&(n=!1)}if(0===e.length||a&&!n){this.dashArray=e;t&&this.setStyle(Name.get("D"))}else this.width=0}else e&&(this.width=0)}setHorizontalCornerRadius(e){Number.isInteger(e)&&(this.horizontalCornerRadius=e)}setVerticalCornerRadius(e){Number.isInteger(e)&&(this.verticalCornerRadius=e)}}class MarkupAnnotation extends Annotation{constructor(e){super(e);const{dict:t}=e;if(t.has("IRT")){const e=t.getRaw("IRT");this.data.inReplyTo=e instanceof Ref?e.toString():null;const a=t.get("RT");this.data.replyType=a instanceof Name?a.name:D}let a=null;if(this.data.replyType===H){const e=t.get("IRT");this.setTitle(e.get("T"));this.data.titleObj=this._title;this.setContents(e.get("Contents"));this.data.contentsObj=this._contents;if(e.has("CreationDate")){this.setCreationDate(e.get("CreationDate"));this.data.creationDate=this.creationDate}else this.data.creationDate=null;if(e.has("M")){this.setModificationDate(e.get("M"));this.data.modificationDate=this.modificationDate}else this.data.modificationDate=null;a=e.getRaw("Popup");if(e.has("C")){this.setColor(e.getArray("C"));this.data.color=this.color}else this.data.color=null}else{this.data.titleObj=this._title;this.setCreationDate(t.get("CreationDate"));this.data.creationDate=this.creationDate;a=t.getRaw("Popup");t.has("C")||(this.data.color=null)}this.data.popupRef=a instanceof Ref?a.toString():null;t.has("RC")&&(this.data.richText=XFAFactory.getRichTextAsHtml(t.get("RC")))}setCreationDate(e){this.creationDate="string"==typeof e?e:null}_setDefaultAppearance({xref:e,extra:a,strokeColor:n,fillColor:s,blendMode:r,strokeAlpha:i,fillAlpha:o,pointsCallback:l}){const f=this.data.rect=t.slice(),c=["q"];a&&c.push(a);n&&c.push(`${n[0]} ${n[1]} ${n[2]} RG`);s&&c.push(`${s[0]} ${s[1]} ${s[2]} rg`);const h=this.data.quadPoints||Float32Array.from([this.rectangle[0],this.rectangle[3],this.rectangle[2],this.rectangle[3],this.rectangle[0],this.rectangle[1],this.rectangle[2],this.rectangle[1]]);for(let e=0,t=h.length;ethis._decodeFormValue(e)).filter(e=>null!==e);return t.length>0?t:null}return e instanceof Name?e.name:"string"==typeof e?stringToPDFString(e):null}hasFieldFlag(e){return!!(this.data.fieldFlags&e)}_isViewable(e){return!0}mustBeViewed(e,t){return t?this.viewable:super.mustBeViewed(e,t)&&!this._hasFlag(this.flags,z)}getRotationMatrix(e){let t=e?.get(this.data.id)?.rotation;void 0===t&&(t=this.rotation);return 0===t?pa:getRotationMatrix(t,this.width,this.height)}getBorderAndBackgroundAppearances(e){let t=e?.get(this.data.id)?.rotation;void 0===t&&(t=this.rotation);if(!this.backgroundColor&&!this.borderColor)return"";const a=0===t||180===t?`0 0 ${this.width} ${this.height} re`:`0 0 ${this.height} ${this.width} re`;let n="";this.backgroundColor&&(n=`${getPdfColor(this.backgroundColor,!0)} ${a} f `);if(this.borderColor){n+=`${this.borderStyle.width||1} w ${getPdfColor(this.borderColor,!1)} ${a} S `}return n}async getOperatorList(e,t,a,n){if(a&h&&!(this instanceof SignatureWidgetAnnotation)&&!this.data.noHTML&&!this.data.hasOwnCanvas)return{opList:new OperatorList,separateForm:!0,separateCanvas:!1};if(!this._hasText)return super.getOperatorList(e,t,a,n);const s=await this._getAppearance(e,t,a,n);if(this.appearance&&null===s)return super.getOperatorList(e,t,a,n);const r=new OperatorList;if(!this._defaultAppearance||null===s)return{opList:r,separateForm:!1,separateCanvas:!1};const i=!!(this.data.hasOwnCanvas&&a&l),o=[0,0,this.width,this.height],f=getTransformMatrix(this.data.rect,o,[1,0,0,1,0,0]);let c;this.oc&&(c=await e.parseMarkedContentProps(this.oc,null));void 0!==c&&r.addOp(xt,["OC",c]);r.addOp(Bt,[this.data.id,this.data.rect,f,this.getRotationMatrix(n),i]);const u=new StringStream(s);await e.getOperatorList({stream:u,task:t,resources:this._fieldResources.mergedResources,operatorList:r});r.addOp(Dt,[]);void 0!==c&&r.addOp(Ct,[]);return{opList:r,separateForm:!1,separateCanvas:i}}_getMKDict(e){const t=new Dict(null);e&&t.set("R",e);t.setIfArray("BC",getPdfColorArray(this.borderColor));t.setIfArray("BG",getPdfColorArray(this.backgroundColor));return t.size>0?t:null}amendSavedDict(e,t){}setValue(e,t,a,n){const{dict:s,ref:r}=function getParentToUpdate(e,t,a){const n=new RefSet,s=e,r={dict:null,ref:null};for(;e instanceof Dict&&!n.has(t);){n.put(t);if(e.has("T"))break;if(!((t=e.getRaw("Parent"))instanceof Ref))return r;e=a.fetch(t)}if(e instanceof Dict&&e!==s){r.dict=e;r.ref=t}return r}(e,this.ref,a);if(s){if(!n.has(r)){const e=s.clone();e.set("V",t);n.put(r,{data:e});return e}}else e.set("V",t);return null}async save(e,t,a,n){const s=a?.get(this.data.id),r=this._buildFlags(s?.noView,s?.noPrint);let i=s?.value,o=s?.rotation;if(i===this.data.fieldValue||void 0===i){if(!this._hasValueFromXFA&&void 0===o&&void 0===r)return;i||=this.data.fieldValue}if(void 0===o&&!this._hasValueFromXFA&&Array.isArray(i)&&Array.isArray(this.data.fieldValue)&&isArrayEqual(i,this.data.fieldValue)&&void 0===r)return;void 0===o&&(o=this.rotation);let l=null;if(!this._needAppearances){l=await this._getAppearance(e,t,c,a);if(null===l&&void 0===r)return}let f=!1;if(l?.needAppearances){f=!0;l=null}const{xref:h}=e,u=h.fetchIfRef(this.ref);if(!(u instanceof Dict))return;const m=new Dict(h);for(const[e,t]of u.getRawEntries())"AP"!==e&&m.set(e,t);if(void 0!==r){m.set("F",r);if(null===l&&!f){const e=u.getRaw("AP");e&&m.set("AP",e)}}const p={path:this.data.fieldName,value:i},d=this.setValue(m,Array.isArray(i)?i.map(stringToAsciiOrUTF16BE):stringToAsciiOrUTF16BE(i),h,n);this.amendSavedDict(a,d||m);const g=this._getMKDict(o);g&&m.set("MK",g);n.put(this.ref,{data:m,xfa:p,needAppearances:f});if(null!==l){const e=h.getNewTemporaryRef(),t=new Dict(h);m.set("AP",t);t.set("N",e);const s=this._getSaveFieldResources(h),r=new StringStream(l),i=r.dict=new Dict(h);i.setIfName("Subtype","Form");i.set("Resources",s);const f=o%180==0?[0,0,this.width,this.height]:[0,0,this.height,this.width];i.set("BBox",f);const c=this.getRotationMatrix(a);c!==pa&&i.set("Matrix",c);n.put(e,{data:r,xfa:null,needAppearances:!1})}m.set("M",`D:${getModificationDate()}`)}async _getAppearance(e,t,a,n){if(this.data.password)return null;const s=n?.get(this.data.id);let r,o;if(s){r=s.formattedValue||s.value;o=s.rotation}if(void 0===o&&void 0===r&&!this._needAppearances&&(!this._hasValueFromXFA||this.appearance))return null;const l=this.getBorderAndBackgroundAppearances(n);if(void 0===r){r=this.data.fieldValue;if(!r)return`/Tx BMC q ${l}Q EMC`}Array.isArray(r)&&1===r.length&&(r=r[0]);assert("string"==typeof r,"Expected `value` to be a string.");r=r.trimEnd();if(this.data.combo){const e=this.data.options.find(({exportValue:e})=>r===e);r=e?.displayValue||r}if(""===r)return`/Tx BMC q ${l}Q EMC`;void 0===o&&(o=this.rotation);let f,h=-1;if(this.data.multiLine){f=r.split(/\r\n?|\n/).map(e=>e.normalize("NFC"));h=f.length}else f=[r.replace(/\r\n?|\n/,"").normalize("NFC")];let{width:u,height:m}=this;90!==o&&270!==o||([u,m]=[m,u]);this._defaultAppearance||(this.data.defaultAppearanceData=parseDefaultAppearance(this._defaultAppearance="/Helvetica 0 Tf 0 g"));let p,d,g,b=await WidgetAnnotation._getFontData(e,t,this.data.defaultAppearanceData,this._fieldResources.mergedResources);const w=[];let j=!1;for(const e of f){const t=b.encodeString(e);t.length>1&&(j=!0);w.push(t.join(""))}if(j&&a&c)return{needAppearances:!0};if(j&&this._isOffscreenCanvasSupported){const a=this.data.comb?"monospace":"sans-serif",n=new FakeUnicodeFont(e.xref,a),s=n.createFontResources(f.join("")),i=s.getRaw("Font");if(this._fieldResources.mergedResources.has("Font")){const e=this._fieldResources.mergedResources.get("Font");for(const[t,a]of i.getRawEntries())e.set(t,a)}else this._fieldResources.mergedResources.set("Font",i);const o=n.fontName.name;b=await WidgetAnnotation._getFontData(e,t,{fontName:o,fontSize:0},s);for(let e=0,t=w.length;e2)return`/Tx BMC q ${l}BT `+p+` 1 0 0 1 ${numberToString(2)} ${numberToString(v)} Tm (${escapeString(w[0])}) Tj ET Q EMC`;return`/Tx BMC q ${l}BT `+p+` 1 0 0 1 0 0 Tm ${this._renderText(w[0],b,d,u,q,{shift:0},2,v)} ET Q EMC`}static async _getFontData(e,t,a,n){const s=new OperatorList,r={font:null,clone(){return this}},{fontName:i,fontSize:o}=a;await e.handleSetFont(n,[i&&Name.get(i),o],null,s,t,r,null);return r.font}_getTextWidth(e,t){return Math.sumPrecise(t.charsToGlyphs(e).map(e=>e.width))/1e3}_computeFontSize(e,t,a,n,r){let{fontSize:i}=this.data.defaultAppearanceData,o=(i||12)*s,l=Math.round(e/o);if(!i){const roundWithTwoDigits=e=>Math.floor(100*e)/100;if(-1===r){const r=this._getTextWidth(a,n);i=roundWithTwoDigits(Math.min(e/s,t/r));l=1}else{const f=a.split(/\r\n?|\n/),c=[];for(const e of f){const t=n.encodeString(e).join(""),a=n.charsToGlyphs(t),s=n.getCharPositions(t);c.push({line:t,glyphs:a,positions:s})}const isTooBig=a=>{let s=0;for(const r of c){s+=this._splitLine(null,n,a,t,r).length*a;if(s>e)return!0}return!1};l=Math.max(l,r);for(;;){o=e/l;i=roundWithTwoDigits(o/s);if(!isTooBig(i))break;l++}}const{fontName:f,fontColor:c}=this.data.defaultAppearanceData;this._defaultAppearance=function createDefaultAppearance({fontSize:e,fontName:t,fontColor:a}){return`/${escapePDFName(t)} ${e} Tf ${getPdfColor(a,!0)}`}({fontSize:i,fontName:f,fontColor:c})}return[this._defaultAppearance,i,e/l]}_renderText(e,t,a,n,s,r,i,o){let l;if(1===s){l=(n-this._getTextWidth(e,t)*a)/2}else if(2===s){l=n-this._getTextWidth(e,t)*a-i}else l=i;const f=numberToString(l-r.shift);r.shift=l;return`${f} ${o=numberToString(o)} Td (${escapeString(e)}) Tj`}_getSaveFieldResources(e){const{localResources:t,appearanceResources:a,acroFormResources:n}=this._fieldResources,s=this.data.defaultAppearanceData?.fontName;if(!s)return t||Dict.empty;for(const e of[t,a])if(e instanceof Dict){const t=e.get("Font");if(t instanceof Dict&&t.has(s))return e}if(n instanceof Dict){const a=n.get("Font");if(a instanceof Dict&&a.has(s)){const n=new Dict(e);n.set(s,a.getRaw(s));const r=new Dict(e);r.set("Font",n);return Dict.merge({xref:e,dictArray:[r,t],mergeSubDicts:!0})}}return t||Dict.empty}getFieldObject(){return null}}class TextWidgetAnnotation extends WidgetAnnotation{constructor(e){super(e);const{dict:t}=e;if(t.has("PMD")){this.flags|=N;this.data.hidden=!0;warn("Barcodes are not supported")}this.data.hasOwnCanvas=this.data.readOnly&&!this.data.noHTML;this._hasText=!0;"string"!=typeof this.data.fieldValue&&(this.data.fieldValue="");let a=getInheritableProperty({dict:t,key:"Q"});(!Number.isInteger(a)||a<0||a>2)&&(a=null);this.data.textAlignment=a;let n=getInheritableProperty({dict:t,key:"MaxLen"});(!Number.isInteger(n)||n<0)&&(n=0);this.data.maxLen=n;this.data.multiLine=this.hasFieldFlag(K);this.data.comb=this.hasFieldFlag(ee)&&!this.data.multiLine&&!this.data.password&&!this.hasFieldFlag(J)&&0!==this.data.maxLen;this.data.doNotScroll=this.hasFieldFlag(Z);const{data:{actions:s}}=this;if(!s)return;const r=/^AF(Date|Time)_(?:Keystroke|Format)(?:Ex)?\(['"]?([^'"]+)['"]?\);$/;let i=!1;(1===s.Format?.length&&1===s.Keystroke?.length&&r.test(s.Format[0])&&r.test(s.Keystroke[0])||0===s.Format?.length&&1===s.Keystroke?.length&&r.test(s.Keystroke[0])||0===s.Keystroke?.length&&1===s.Format?.length&&r.test(s.Format[0]))&&(i=!0);const o=[];s.Format&&o.push(...s.Format);s.Keystroke&&o.push(...s.Keystroke);if(i){delete s.Keystroke;s.Format=o}for(const e of o){const t=e.match(r);if(!t)continue;const a="Date"===t[1];let n=t[2];const s=parseInt(n,10);isNaN(s)||Math.floor(Math.log10(s))+1!==t[2].length||(n=(a?Ri:Hi)[s]??n);this.data.datetimeFormat=n;if(!i)break;if(a){if(/HH|MM|ss|h/.test(n)){this.data.datetimeType="datetime-local";this.data.timeStep=/ss/.test(n)?1:60}else this.data.datetimeType="date";break}this.data.datetimeType="time";this.data.timeStep=/ss/.test(n)?1:60;break}}get hasTextContent(){return!!this.appearance&&!this._needAppearances}_getCombAppearance(e,t,a,n,s,r,i,o,l,f,c,h,u){const m=s/this.data.maxLen,p=this.getBorderAndBackgroundAppearances(u),d=[],g=t.getCharPositions(a);for(const[e,t]of g)d.push(`(${escapeString(a.substring(e,t))}) Tj`);h&&d.reverse();const b=m*g.length;let w=i;1===c?w+=Math.floor((s-b)/(2*m))*m:2===c&&(w+=s-b);const j=d.join(` ${numberToString(m)} 0 Td `);return`/Tx BMC q ${p}BT `+e+` 1 0 0 1 ${numberToString(w)} ${numberToString(o+l)} Tm ${j} ET Q EMC`}_getMultilineAppearance(e,t,a,n,s,r,i,o,l,f,c,h){const u=[],m=s-2*o,p={shift:0};for(let e=0,r=t.length;en){l.push(e.substring(u,a));u=a;m=d;f=-1;h=-1}else{m+=d;f=a;c=s;h=t}else if(m+d>n)if(-1!==f){l.push(e.substring(u,c));u=c;t=h+1;f=-1;m=0}else{l.push(e.substring(u,a));u=a;m=d}else m+=d}ut?`\\${t}`:"\\s+");new RegExp(`^\\s*${r}\\s*$`).test(this.data.fieldValue)&&(this.data.textContent=this.data.fieldValue.split("\n"))}getFieldObject(){return{id:this.data.id,value:this.data.fieldValue,defaultValue:this.data.defaultFieldValue||"",multiline:this.data.multiLine,password:this.data.password,charLimit:this.data.maxLen,comb:this.data.comb,editable:!this.data.readOnly,hidden:this.data.hidden,name:this.data.fieldName,rect:this.data.rect,actions:this.data.actions,page:this.data.pageIndex,strokeColor:this.data.borderColor,fillColor:this.data.backgroundColor,rotation:this.rotation,datetimeFormat:this.data.datetimeFormat,hasDatetimeHTML:!!this.data.datetimeType,type:"text"}}}class ButtonWidgetAnnotation extends WidgetAnnotation{constructor(e){super(e);this.checkedAppearance=null;this.uncheckedAppearance=null;const t=this.hasFieldFlag(V),a=this.hasFieldFlag($);this.data.checkBox=!t&&!a;this.data.radioButton=t&&!a;this.data.pushButton=a;this.data.isTooltipOnly=!1;if(this.data.checkBox)this._processCheckBox(e);else if(this.data.radioButton)this._processRadioButton(e);else if(this.data.pushButton){this.data.hasOwnCanvas=!0;this.data.noHTML=!1;this._processPushButton(e)}else warn("Invalid field flags for button widget annotation")}async getOperatorList(e,t,a,n){if(this.data.pushButton)return super.getOperatorList(e,t,a,!1,n);let s=null,r=null;if(n){const e=n.get(this.data.id);s=e?e.value:null;r=e?e.rotation:null}if(null===s&&this.appearance)return super.getOperatorList(e,t,a,n);null==s&&(s=this.data.checkBox?this.data.fieldValue===this.data.exportValue:this.data.fieldValue===this.data.buttonValue);const i=s?this.checkedAppearance:this.uncheckedAppearance;if(i){const s=this.appearance,o=lookupMatrix(i.dict.getArray("Matrix"),pa);r&&i.dict.set("Matrix",this.getRotationMatrix(n));this.appearance=i;const l=super.getOperatorList(e,t,a,n);this.appearance=s;i.dict.set("Matrix",o);return l}return{opList:new OperatorList,separateForm:!1,separateCanvas:!1}}async save(e,t,a,n){this.data.checkBox?this._saveCheckbox(e,t,a,n):this.data.radioButton&&this._saveRadioButton(e,t,a,n)}async _saveCheckbox(e,t,a,n){if(!a)return;const s=a.get(this.data.id),r=this._buildFlags(s?.noView,s?.noPrint);let i=s?.rotation,o=s?.value;if(void 0===i&&void 0===r){if(void 0===o)return;if(this.data.fieldValue===this.data.exportValue===o)return}let l=e.xref.fetchIfRef(this.ref);if(!(l instanceof Dict))return;l=l.clone();void 0===i&&(i=this.rotation);void 0===o&&(o=this.data.fieldValue===this.data.exportValue);const f={path:this.data.fieldName,value:o?this.data.exportValue:""},c=Name.get(o?this.data.exportValue:"Off");this.setValue(l,c,e.xref,n);l.set("AS",c);l.set("M",`D:${getModificationDate()}`);void 0!==r&&l.set("F",r);const h=this._getMKDict(i);h&&l.set("MK",h);n.put(this.ref,{data:l,xfa:f,needAppearances:!1})}async _saveRadioButton(e,t,a,n){if(!a)return;const s=a.get(this.data.id),r=this._buildFlags(s?.noView,s?.noPrint);let i=s?.rotation,o=s?.value;if(void 0===i&&void 0===r){if(void 0===o)return;if(this.data.fieldValue===this.data.buttonValue===o)return}let l=e.xref.fetchIfRef(this.ref);if(!(l instanceof Dict))return;l=l.clone();void 0===o&&(o=this.data.fieldValue===this.data.buttonValue);void 0===i&&(i=this.rotation);const f={path:this.data.fieldName,value:o?this.data.buttonValue:""},c=Name.get(o?this.data.buttonValue:"Off");o&&this.setValue(l,c,e.xref,n);l.set("AS",c);l.set("M",`D:${getModificationDate()}`);void 0!==r&&l.set("F",r);const h=this._getMKDict(i);h&&l.set("MK",h);n.put(this.ref,{data:l,xfa:f,needAppearances:!1})}_getDefaultCheckedAppearance(e,t){const{width:a,height:n}=this,s=[0,0,a,n],r=.8*Math.min(a,n);let i,o;if("check"===t){i={width:.755*r,height:.705*r};o="3"}else if("disc"===t){i={width:.791*r,height:.705*r};o="l"}else unreachable(`_getDefaultCheckedAppearance - unsupported type: ${t}`);const l=`q BT /PdfJsZaDb ${r} Tf 0 g ${numberToString((a-i.width)/2)} ${numberToString((n-i.height)/2)} Td (${o}) Tj ET Q`,f=new Dict(e.xref);f.set("FormType",1);f.setIfName("Subtype","Form");f.setIfName("Type","XObject");f.set("BBox",s);f.set("Matrix",[1,0,0,1,0,0]);f.set("Length",l.length);const c=new Dict(e.xref),h=new Dict(e.xref);h.set("PdfJsZaDb",this.fallbackFontDict);c.set("Font",h);f.set("Resources",c);this.checkedAppearance=new StringStream(l);this.checkedAppearance.dict=f;this._streams.push(this.checkedAppearance)}_processCheckBox(e){const t=e.dict.get("AP");if(!(t instanceof Dict))return;const a=t.get("N");if(!(a instanceof Dict))return;const n=this._decodeFormValue(e.dict.get("AS"));"string"==typeof n&&(this.data.fieldValue=n);const s=null!==this.data.fieldValue&&"Off"!==this.data.fieldValue?this.data.fieldValue:"Yes",r=[...a.getKeys()];if(0===r.length)r.push("Off",s);else if(1===r.length)"Off"===r[0]?r.push(s):r.unshift("Off");else if(r.includes(s)){r.length=0;r.push("Off",s)}else{const e=r.find(e=>"Off"!==e);r.length=0;r.push("Off",e)}r.includes(this.data.fieldValue)||(this.data.fieldValue="Off");this.data.exportValue=r[1];const i=a.get(this.data.exportValue);this.checkedAppearance=i instanceof BaseStream?i:null;const o=a.get("Off");this.uncheckedAppearance=o instanceof BaseStream?o:null;this.checkedAppearance?this._streams.push(this.checkedAppearance):this._getDefaultCheckedAppearance(e,"check");this.uncheckedAppearance&&this._streams.push(this.uncheckedAppearance);this._fallbackFontDict=this.fallbackFontDict;null===this.data.defaultFieldValue&&(this.data.defaultFieldValue="Off")}_processRadioButton(e){this.data.buttonValue=null;const t=e.dict.get("Parent");if(t instanceof Dict){this.parent=e.dict.getRaw("Parent");const a=t.get("V");a instanceof Name&&(this.data.fieldValue=this._decodeFormValue(a))}const a=e.dict.get("AP");if(!(a instanceof Dict))return;const n=a.get("N");if(!(n instanceof Dict))return;for(const e of n.getKeys())if("Off"!==e){this.data.buttonValue=e;break}const s=n.get(this.data.buttonValue);this.checkedAppearance=s instanceof BaseStream?s:null;const r=n.get("Off");this.uncheckedAppearance=r instanceof BaseStream?r:null;this.checkedAppearance?this._streams.push(this.checkedAppearance):this._getDefaultCheckedAppearance(e,"disc");this.uncheckedAppearance&&this._streams.push(this.uncheckedAppearance);this._fallbackFontDict=this.fallbackFontDict;null===this.data.defaultFieldValue&&(this.data.defaultFieldValue="Off")}_processPushButton(e){const{dict:t,annotationGlobals:a}=e;if(t.has("A")||t.has("AA")||this.data.alternativeText){this.data.isTooltipOnly=!t.has("A")&&!t.has("AA");Catalog.parseDestDictionary({destDict:t,resultObj:this.data,docBaseUrl:a.baseUrl,docAttachments:a.attachments})}else warn("Push buttons without action dictionaries are not supported")}getFieldObject(){let e,t="button";if(this.data.checkBox){t="checkbox";e=this.data.exportValue}else if(this.data.radioButton){t="radiobutton";e=this.data.buttonValue}return{id:this.data.id,value:this.data.fieldValue||"Off",defaultValue:this.data.defaultFieldValue,exportValues:e,editable:!this.data.readOnly,name:this.data.fieldName,rect:this.data.rect,hidden:this.data.hidden,actions:this.data.actions,page:this.data.pageIndex,strokeColor:this.data.borderColor,fillColor:this.data.backgroundColor,rotation:this.rotation,type:t}}get fallbackFontDict(){const e=new Dict;e.setIfName("BaseFont","ZapfDingbats");e.setIfName("Type","FallbackType");e.setIfName("Subtype","FallbackType");e.setIfName("Encoding","ZapfDingbatsEncoding");return shadow(this,"fallbackFontDict",e)}}class ChoiceWidgetAnnotation extends WidgetAnnotation{constructor(e){super(e);const{dict:t,xref:a}=e;this.indices=t.getArray("I");this.hasIndices=Array.isArray(this.indices)&&this.indices.length>0;this.data.options=[];const n=getInheritableProperty({dict:t,key:"Opt"});if(Array.isArray(n))for(let e=0,t=n.length;e=0&&t0&&(this.data.options=this.data.fieldValue.map(e=>({exportValue:e,displayValue:e})));this.data.combo=this.hasFieldFlag(Y);this.data.multiSelect=this.hasFieldFlag(Q);this._hasText=!0}getFieldObject(){const e=this.data.combo?"combobox":"listbox",t=this.data.fieldValue.length>0?this.data.fieldValue[0]:null;return{id:this.data.id,value:t,defaultValue:this.data.defaultFieldValue,editable:!this.data.readOnly,name:this.data.fieldName,rect:this.data.rect,numItems:this.data.fieldValue.length,multipleSelection:this.data.multiSelect,hidden:this.data.hidden,actions:this.data.actions,items:this.data.options,page:this.data.pageIndex,strokeColor:this.data.borderColor,fillColor:this.data.backgroundColor,rotation:this.rotation,type:e}}amendSavedDict(e,t){if(!this.hasIndices)return;let a=e?.get(this.data.id)?.value;Array.isArray(a)||(a=[a]);const n=[],{options:s}=this.data;for(let e=0,t=0,r=s.length;ea){a=n;t=e}}[m,p]=this._computeFontSize(e,l-4,t,u,-1)}const d=p*s,g=(d-p)/2,b=Math.floor(f/d);let w=0;if(h.length>0){const e=Math.min(...h),t=Math.max(...h);w=Math.max(0,t-b+1);w>e&&(w=e)}const j=Math.min(w+b+1,c),k=["/Tx BMC q",`1 1 ${l} ${f} re W n`];if(h.length){k.push("0.600006 0.756866 0.854904 rg");for(const e of h)w<=e&&ee.trimEnd());const{coords:e,bbox:t,matrix:a}=FakeUnicodeFont.getFirstPositionInfo(this.rectangle,this.rotation,n);this.data.textPosition=this._transformPoint(e,t,a)}if(this._isOffscreenCanvasSupported){const s=e.dict.get("CA"),r=new FakeUnicodeFont(a,"sans-serif");this.appearance=r.createAppearance(this._contents.str,this.rectangle,this.rotation,n,t,s);this._streams.push(this.appearance)}else warn("FreeTextAnnotation: OffscreenCanvas is not supported, annotation may not render correctly.")}}get hasTextContent(){return this._hasAppearance}static createNewDict(e,t,{apRef:a,ap:n}){const{color:s,date:r,fontSize:i,oldAnnotation:o,rect:l,rotation:f,user:c,value:h}=e,u=o||new Dict(t);u.setIfNotExists("Type",Name.get("Annot"));u.setIfNotExists("Subtype",Name.get("FreeText"));u.set(o?"M":"CreationDate",`D:${getModificationDate(r)}`);o&&u.delete("RC");u.setIfArray("Rect",l);const m=`/Helv ${i} Tf ${getPdfColor(s,!0)}`;u.set("DA",m);u.setIfDefined("Contents",stringToAsciiOrUTF16BE(h));u.setIfNotExists("F",4);u.setIfNotExists("Border",[0,0,0]);u.setIfNumber("Rotate",f);u.setIfDefined("T",stringToAsciiOrUTF16BE(c));if(a||n){const e=new Dict(t);u.set("AP",e);e.set("N",a||n)}return u}static async createNewAppearanceStream(e,t,a){const{baseFontRef:n,evaluator:r,task:i}=a,{color:o,fontSize:l,rect:f,rotation:c,value:h}=e;if(!o)return null;const u=new Dict(t),m=new Dict(t);if(n)m.set("Helv",n);else{const e=new Dict(t);e.setIfName("BaseFont","Helvetica");e.setIfName("Type","Font");e.setIfName("Subtype","Type1");e.setIfName("Encoding","WinAnsiEncoding");m.set("Helv",e)}u.set("Font",m);const p=await WidgetAnnotation._getFontData(r,i,{fontName:"Helv",fontSize:l},u),[d,g,b,w]=f;let j=b-d,k=w-g;c%180!=0&&([j,k]=[k,j]);const y=h.split("\n"),q=l/1e3;let v=-1/0;const S=[];for(let e of y){const t=p.encodeString(e);if(t.length>1)return null;e=t.join("");S.push(e);let a=0;const n=p.charsToGlyphs(e);for(const e of n)a+=e.width*q;v=Math.max(v,a)}let x=1;v>j&&(x=j/v);let C=1;const F=s*l,T=1*l,O=F*y.length;O>k&&(C=k/O);const R=l*Math.min(x,C);let H,D,M;switch(c){case 0:M=[1,0,0,1];D=[f[0],f[1],j,k];H=[f[0],f[3]-T];break;case 90:M=[0,1,-1,0];D=[f[1],-f[2],j,k];H=[f[1],-f[0]-T];break;case 180:M=[-1,0,0,-1];D=[-f[2],-f[3],j,k];H=[-f[2],-f[1]-T];break;case 270:M=[0,-1,1,0];D=[-f[3],f[0],j,k];H=[-f[3],f[2]-T]}const N=["q",`${M.join(" ")} 0 0 cm`,`${D.join(" ")} re W n`,"BT",`${getPdfColor(o,!0)}`,`0 Tc /Helv ${numberToString(R)} Tf`];N.push(`${H.join(" ")} Td (${escapeString(S[0])}) Tj`);const E=numberToString(F);for(let e=1,t=S.length;e{e.push(`${n[0]} ${n[1]} m`,`${n[2]} ${n[3]} l`,"S");return[t[0]-o,t[7]-o,t[2]+o,t[3]+o]}})}}}class SquareAnnotation extends MarkupAnnotation{constructor(e){super(e);const{dict:t,xref:a}=e;this.data.hasOwnCanvas=this.data.noRotate;this.data.noHTML=!1;if(!this.appearance){const e=getPdfColorArray(this.color,[0,0,0]),n=t.get("CA"),s=getPdfColorArray(getRgbColor(t.getArray("IC"),null)),r=s?n:null;if(0===this.borderStyle.width&&!s)return;this._setDefaultAppearance({xref:a,extra:`${this.borderStyle.width} w`,strokeColor:e,fillColor:s,strokeAlpha:n,fillAlpha:r,pointsCallback:(e,t)=>{const a=t[4]+this.borderStyle.width/2,n=t[5]+this.borderStyle.width/2,r=t[6]-t[4]-this.borderStyle.width,i=t[3]-t[7]-this.borderStyle.width;e.push(`${a} ${n} ${r} ${i} re`);s?e.push("B"):e.push("S");return[t[0],t[7],t[2],t[3]]}})}}}class CircleAnnotation extends MarkupAnnotation{constructor(e){super(e);const{dict:t,xref:a}=e;if(!this.appearance){const e=getPdfColorArray(this.color,[0,0,0]),n=t.get("CA"),s=getPdfColorArray(getRgbColor(t.getArray("IC"),null)),r=s?n:null;if(0===this.borderStyle.width&&!s)return;const i=4/3*Math.tan(Math.PI/8);this._setDefaultAppearance({xref:a,extra:`${this.borderStyle.width} w`,strokeColor:e,fillColor:s,strokeAlpha:n,fillAlpha:r,pointsCallback:(e,t)=>{const a=t[0]+this.borderStyle.width/2,n=t[1]-this.borderStyle.width/2,r=t[6]-this.borderStyle.width/2,o=t[7]+this.borderStyle.width/2,l=a+(r-a)/2,f=n+(o-n)/2,c=(r-a)/2*i,h=(o-n)/2*i;e.push(`${l} ${o} m`,`${l+c} ${o} ${r} ${f+h} ${r} ${f} c`,`${r} ${f-h} ${l+c} ${n} ${l} ${n} c`,`${l-c} ${n} ${a} ${f-h} ${a} ${f} c`,`${a} ${f+h} ${l-c} ${o} ${l} ${o} c`,"h");s?e.push("B"):e.push("S");return[t[0],t[7],t[2],t[3]]}})}}}class PolylineAnnotation extends MarkupAnnotation{constructor(e){super(e);const{dict:a,xref:n}=e;this.data.hasOwnCanvas=this.data.noRotate;this.data.noHTML=!1;this.data.vertices=null;if(!(this instanceof PolygonAnnotation)){this.setLineEndings(a.getArray("LE"));this.data.lineEndings=this.lineEndings}const s=a.getArray("Vertices");if(!isNumberArray(s,null))return;const r=this.data.vertices=Float32Array.from(s);if(!this.appearance){const e=getPdfColorArray(this.color,[0,0,0]),s=a.get("CA");let i,o=getRgbColor(a.getArray("IC"),null);o&&(o=getPdfColorArray(o));i=o?this.color?o.every((t,a)=>t===e[a])?"f":"B":"f":"S";const l=this.borderStyle.width||1,f=2*l,c=t.slice();for(let e=0,t=r.length;e{for(let t=0,a=r.length;t{for(const t of this.data.inkLists){for(let a=0,n=t.length;a0){const e=new Dict(t);p.set("BS",e);e.set("W",u)}p.setIfArray("C",getPdfColorArray(r));p.setIfNumber("CA",o);if(n||a){const e=new Dict(t);p.set("AP",e);e.set("N",a||n)}return p}static async createNewAppearanceStream(e,t,a){if(e.outlines)return this.createNewAppearanceStreamForHighlight(e,t,a);const{color:n,rect:s,paths:r,thickness:i,opacity:o}=e;if(!n)return null;const l=[`${i} w 1 J 1 j`,`${getPdfColor(n,!1)}`];1!==o&&l.push("/R0 gs");for(const e of r.lines){l.push(`${numberToString(e[4])} ${numberToString(e[5])} m`);for(let t=6,a=e.length;t{e.push(`${t[0]} ${t[1]} m`,`${t[2]} ${t[3]} l`,`${t[6]} ${t[7]} l`,`${t[4]} ${t[5]} l`,"f");return[t[0],t[7],t[2],t[3]]}})}}else this.data.popupRef=null}get overlaysTextContent(){return!0}static createNewDict(e,t,{apRef:a,ap:n}){const{color:s,date:r,oldAnnotation:i,opacity:o,rect:l,rotation:f,user:c,quadPoints:h}=e,u=i||new Dict(t);u.setIfNotExists("Type",Name.get("Annot"));u.setIfNotExists("Subtype",Name.get("Highlight"));u.set(i?"M":"CreationDate",`D:${getModificationDate(r)}`);u.setIfArray("Rect",l);u.setIfNotExists("F",4);u.setIfNotExists("Border",[0,0,0]);u.setIfNumber("Rotate",f);u.setIfArray("QuadPoints",h);u.setIfArray("C",getPdfColorArray(s));u.setIfNumber("CA",o);u.setIfDefined("T",stringToAsciiOrUTF16BE(c));if(a||n){const e=new Dict(t);u.set("AP",e);e.set("N",a||n)}return u}static async createNewAppearanceStream(e,t,a){const{color:n,rect:s,outlines:r,opacity:i}=e;if(!n)return null;const o=[`${getPdfColor(n,!0)}`,"/R0 gs"],l=[];for(const e of r){l.length=0;l.push(`${numberToString(e[0])} ${numberToString(e[1])} m`);for(let t=2,a=e.length;t{e.push(`${t[4]} ${t[5]+1.3} m`,`${t[6]} ${t[7]+1.3} l`,"S");return[t[0],t[7],t[2],t[3]]}})}}else this.data.popupRef=null}get overlaysTextContent(){return!0}}class SquigglyAnnotation extends MarkupAnnotation{constructor(e){super(e);const{dict:t,xref:a}=e;if(this.data.quadPoints=getQuadPoints(t,null)){if(!this.appearance){const e=getPdfColorArray(this.color,[0,0,0]),n=t.get("CA");this._setDefaultAppearance({xref:a,extra:"[] 0 d 1 w",strokeColor:e,strokeAlpha:n,pointsCallback:(e,t)=>{const a=(t[1]-t[5])/6;let n=a,s=t[4];const r=t[5],i=t[6];e.push(`${s} ${r+n} m`);do{s+=2;n=0===n?a:0;e.push(`${s} ${r+n} l`)}while(s{e.push((t[0]+t[4])/2+" "+(t[1]+t[5])/2+" m",(t[2]+t[6])/2+" "+(t[3]+t[7])/2+" l","S");return[t[0],t[7],t[2],t[3]]}})}}else this.data.popupRef=null}get overlaysTextContent(){return!0}}class StampAnnotation extends MarkupAnnotation{#ot=null;constructor(e){super(e);this.data.hasOwnCanvas=this.data.noRotate;this.data.isEditable=!this.data.noHTML;this.data.noHTML=!1}mustBeViewedWhenEditing(e,t=null){if(e){if(!this.data.isEditable)return!0;this.#ot??=this.data.hasOwnCanvas;this.data.hasOwnCanvas=!0;return!0}if(null!==this.#ot){this.data.hasOwnCanvas=this.#ot;this.#ot=null}return!t?.has(this.data.id)}static async createImage(e,t){const{width:a,height:n}=e,s=new OffscreenCanvas(a,n),r=s.getContext("2d",{alpha:!0});r.drawImage(e,0,0);const i=r.getImageData(0,0,a,n).data,o=new Uint32Array(i.buffer),l=o.some(FeatureTest.isLittleEndian?e=>e>>>24!=255:e=>!!(255&~e));if(l){r.fillStyle="white";r.fillRect(0,0,a,n);r.drawImage(e,0,0)}const f=s.convertToBlob({type:"image/jpeg",quality:1}).then(e=>e.bytes()),c=Name.get("XObject"),h=Name.get("Image"),u=new Dict(t);u.set("Type",c);u.set("Subtype",h);u.set("BitsPerComponent",8);u.setIfName("ColorSpace","DeviceRGB");u.setIfName("Filter","DCTDecode");u.set("BBox",[0,0,a,n]);u.set("Width",a);u.set("Height",n);let m=null;if(l){const e=new Uint8Array(o.length);if(FeatureTest.isLittleEndian)for(let t=0,a=o.length;t>>24;else for(let t=0,a=o.length;t=0&&s<=1?s:null}}const df={get r(){return shadow(this,"r",new Uint8Array([7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22,5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21]))},get k(){return shadow(this,"k",new Int32Array([-680876936,-389564586,606105819,-1044525330,-176418897,1200080426,-1473231341,-45705983,1770035416,-1958414417,-42063,-1990404162,1804603682,-40341101,-1502002290,1236535329,-165796510,-1069501632,643717713,-373897302,-701558691,38016083,-660478335,-405537848,568446438,-1019803690,-187363961,1163531501,-1444681467,-51403784,1735328473,-1926607734,-378558,-2022574463,1839030562,-35309556,-1530992060,1272893353,-155497632,-1094730640,681279174,-358537222,-722521979,76029189,-640364487,-421815835,530742520,-995338651,-198630844,1126891415,-1416354905,-57434055,1700485571,-1894986606,-1051523,-2054922799,1873313359,-30611744,-1560198380,1309151649,-145523070,-1120210379,718787259,-343485551]))}};function calculateMD5(e,t,a){let n=1732584193,s=-271733879,r=-1732584194,i=271733878;const o=a+72&-64,l=new Uint8Array(o);let f,c;for(f=0;f>5&255;l[f++]=a>>13&255;l[f++]=a>>21&255;l[f++]=a>>>29&255;f+=3;const u=new Int32Array(16),{k:m,r:p}=df;for(f=0;f>>32-r)|0;a=n}n=n+a|0;s=s+o|0;r=r+h|0;i=i+d|0}return new Uint8Array([255&n,n>>8&255,n>>16&255,n>>>24&255,255&s,s>>8&255,s>>16&255,s>>>24&255,255&r,r>>8&255,r>>16&255,r>>>24&255,255&i,i>>8&255,i>>16&255,i>>>24&255])}function decodeString(e){try{return stringToUTF8String(e)}catch(t){warn(`UTF-8 decoding failed: "${t}".`);return e}}class DatasetXMLParser extends SimpleXMLParser{node=null;onEndElement(e){const t=super.onEndElement(e);if(t&&"xfa:datasets"===e){this.node=t;throw new Error("Aborting DatasetXMLParser.")}}}class DatasetReader{constructor(e){if(e.datasets)this.node=new SimpleXMLParser({hasAttributes:!0}).parseFromString(e.datasets).documentElement;else{const t=new DatasetXMLParser({hasAttributes:!0});try{t.parseFromString(e["xdp:xdp"])}catch{}this.node=t.node}}getValue(e){if(!this.node||!e)return"";const t=this.node.searchNode(parseXFAPath(e),0);if(!t)return"";const a=t.firstChild;return"value"===a?.nodeName?t.children.map(e=>decodeString(e.textContent)):decodeString(t.textContent)}}class SingleIntersector{#ft;minX=1/0;minY=1/0;maxX=-1/0;maxY=-1/0;#ct=null;#ht=[];#ut=[];#mt=-1;#pt=!1;constructor(e){this.#ft=e;const t=e.data.quadPoints;if(t){for(let e=0,a=t.length;e8&&(this.#ct=t)}else[this.minX,this.minY,this.maxX,this.maxY]=e.data.rect}#dt(e,t){if(this.minX>=e||this.maxX<=e||this.minY>=t||this.maxY<=t)return!1;const a=this.#ct;if(!a)return!0;if(this.#mt>=0){const n=this.#mt;if(!(a[n]>=e||a[n+2]<=e||a[n+5]>=t||a[n+1]<=t))return!0;this.#mt=-1}for(let n=0,s=a.length;n=e||a[n+2]<=e||a[n+5]>=t||a[n+1]<=t)){this.#mt=n;return!0}return!1}addGlyph(e,t,a){if(!this.#dt(e,t)){this.disableExtraChars();return!1}if(this.#ut.length>0){this.#ht.push(this.#ut.join(""));this.#ut.length=0}this.#ht.push(a);this.#pt=!0;return!0}addExtraChar(e){this.#pt&&this.#ut.push(e)}disableExtraChars(){if(this.#pt){this.#pt=!1;this.#ut.length=0}}setText(){this.#ft.data.overlaidText=this.#ht.join("")}}const gf=64;class Intersector{#gt=[];#bt=[];#wt;#jt;#kt;#yt;#qt;#vt;constructor(e){let t=1/0,a=1/0,n=-1/0,s=-1/0;const r=this.#gt;for(const i of e){if(!i.data.quadPoints&&!i.data.rect)continue;const e=new SingleIntersector(i);r.push(e);t=Math.min(t,e.minX);a=Math.min(a,e.minY);n=Math.max(n,e.maxX);s=Math.max(s,e.maxY)}this.#wt=t;this.#kt=a;this.#jt=n;this.#yt=s;this.#qt=63/(n-t);this.#vt=63/(s-a);for(const e of r){const t=this.#St(e.minX,e.minY),a=this.#St(e.maxX,e.maxY),n=(a-t)%gf,s=Math.floor((a-t)/gf);for(let a=t;a<=t+s*gf;a+=gf)for(let t=0;t<=n;t++){let n=this.#bt[a+t];n||(this.#bt[a+t]=n=[]);n.push(e)}}}#St(e,t){return Math.floor((e-this.#wt)*this.#qt)+Math.floor((t-this.#kt)*this.#vt)*gf}addGlyph(e,t,a,n){const s=e[4]+t/2,r=e[5]+a/2;if(sthis.#jt||r>this.#yt)return;const i=this.#bt[this.#St(s,r)];if(i)for(const e of i)e.addGlyph(s,r,n)}addExtraChar(e){for(const t of this.#gt)t.addExtraChar(e)}setText(){for(const e of this.#gt)e.setText()}}class Word64{constructor(e,t){this.high=0|e;this.low=0|t}and(e){this.high&=e.high;this.low&=e.low}xor(e){this.high^=e.high;this.low^=e.low}shiftRight(e){if(e>=32){this.low=this.high>>>e-32|0;this.high=0}else{this.low=this.low>>>e|this.high<<32-e;this.high=this.high>>>e|0}}rotateRight(e){let t,a;if(32&e){a=this.low;t=this.high}else{t=this.low;a=this.high}e&=31;this.low=t>>>e|a<<32-e;this.high=a>>>e|t<<32-e}not(){this.high=~this.high;this.low=~this.low}add(e){const t=(this.low>>>0)+(e.low>>>0);let a=(this.high>>>0)+(e.high>>>0);t>4294967295&&(a+=1);this.low=0|t;this.high=0|a}copyTo(e,t){e[t]=this.high>>>24&255;e[t+1]=this.high>>16&255;e[t+2]=this.high>>8&255;e[t+3]=255&this.high;e[t+4]=this.low>>>24&255;e[t+5]=this.low>>16&255;e[t+6]=this.low>>8&255;e[t+7]=255&this.low}assign(e){this.high=e.high;this.low=e.low}}const bf={get k(){return shadow(this,"k",[new Word64(1116352408,3609767458),new Word64(1899447441,602891725),new Word64(3049323471,3964484399),new Word64(3921009573,2173295548),new Word64(961987163,4081628472),new Word64(1508970993,3053834265),new Word64(2453635748,2937671579),new Word64(2870763221,3664609560),new Word64(3624381080,2734883394),new Word64(310598401,1164996542),new Word64(607225278,1323610764),new Word64(1426881987,3590304994),new Word64(1925078388,4068182383),new Word64(2162078206,991336113),new Word64(2614888103,633803317),new Word64(3248222580,3479774868),new Word64(3835390401,2666613458),new Word64(4022224774,944711139),new Word64(264347078,2341262773),new Word64(604807628,2007800933),new Word64(770255983,1495990901),new Word64(1249150122,1856431235),new Word64(1555081692,3175218132),new Word64(1996064986,2198950837),new Word64(2554220882,3999719339),new Word64(2821834349,766784016),new Word64(2952996808,2566594879),new Word64(3210313671,3203337956),new Word64(3336571891,1034457026),new Word64(3584528711,2466948901),new Word64(113926993,3758326383),new Word64(338241895,168717936),new Word64(666307205,1188179964),new Word64(773529912,1546045734),new Word64(1294757372,1522805485),new Word64(1396182291,2643833823),new Word64(1695183700,2343527390),new Word64(1986661051,1014477480),new Word64(2177026350,1206759142),new Word64(2456956037,344077627),new Word64(2730485921,1290863460),new Word64(2820302411,3158454273),new Word64(3259730800,3505952657),new Word64(3345764771,106217008),new Word64(3516065817,3606008344),new Word64(3600352804,1432725776),new Word64(4094571909,1467031594),new Word64(275423344,851169720),new Word64(430227734,3100823752),new Word64(506948616,1363258195),new Word64(659060556,3750685593),new Word64(883997877,3785050280),new Word64(958139571,3318307427),new Word64(1322822218,3812723403),new Word64(1537002063,2003034995),new Word64(1747873779,3602036899),new Word64(1955562222,1575990012),new Word64(2024104815,1125592928),new Word64(2227730452,2716904306),new Word64(2361852424,442776044),new Word64(2428436474,593698344),new Word64(2756734187,3733110249),new Word64(3204031479,2999351573),new Word64(3329325298,3815920427),new Word64(3391569614,3928383900),new Word64(3515267271,566280711),new Word64(3940187606,3454069534),new Word64(4118630271,4000239992),new Word64(116418474,1914138554),new Word64(174292421,2731055270),new Word64(289380356,3203993006),new Word64(460393269,320620315),new Word64(685471733,587496836),new Word64(852142971,1086792851),new Word64(1017036298,365543100),new Word64(1126000580,2618297676),new Word64(1288033470,3409855158),new Word64(1501505948,4234509866),new Word64(1607167915,987167468),new Word64(1816402316,1246189591)])}};function ch(e,t,a,n,s){e.assign(t);e.and(a);s.assign(t);s.not();s.and(n);e.xor(s)}function maj(e,t,a,n,s){e.assign(t);e.and(a);s.assign(t);s.and(n);e.xor(s);s.assign(a);s.and(n);e.xor(s)}function sigma(e,t,a){e.assign(t);e.rotateRight(28);a.assign(t);a.rotateRight(34);e.xor(a);a.assign(t);a.rotateRight(39);e.xor(a)}function sigmaPrime(e,t,a){e.assign(t);e.rotateRight(14);a.assign(t);a.rotateRight(18);e.xor(a);a.assign(t);a.rotateRight(41);e.xor(a)}function littleSigma(e,t,a){e.assign(t);e.rotateRight(1);a.assign(t);a.rotateRight(8);e.xor(a);a.assign(t);a.shiftRight(7);e.xor(a)}function littleSigmaPrime(e,t,a){e.assign(t);e.rotateRight(19);a.assign(t);a.rotateRight(61);e.xor(a);a.assign(t);a.shiftRight(6);e.xor(a)}function calculateSHA512(e,t,a,n=!1){let s,r,i,o,l,f,c,h;if(n){s=new Word64(3418070365,3238371032);r=new Word64(1654270250,914150663);i=new Word64(2438529370,812702999);o=new Word64(355462360,4144912697);l=new Word64(1731405415,4290775857);f=new Word64(2394180231,1750603025);c=new Word64(3675008525,1694076839);h=new Word64(1203062813,3204075428)}else{s=new Word64(1779033703,4089235720);r=new Word64(3144134277,2227873595);i=new Word64(1013904242,4271175723);o=new Word64(2773480762,1595750129);l=new Word64(1359893119,2917565137);f=new Word64(2600822924,725511199);c=new Word64(528734635,4215389547);h=new Word64(1541459225,327033209)}const u=128*Math.ceil((a+17)/128),m=new Uint8Array(u);let p,d;for(p=0;p>>29&255;m[p++]=a>>21&255;m[p++]=a>>13&255;m[p++]=a>>5&255;m[p++]=a<<3&255;const b=new Array(80);for(p=0;p<80;p++)b[p]=new Word64(0,0);const{k:w}=bf;let j=new Word64(0,0),k=new Word64(0,0),y=new Word64(0,0),q=new Word64(0,0),v=new Word64(0,0),S=new Word64(0,0),x=new Word64(0,0),C=new Word64(0,0);const F=new Word64(0,0),T=new Word64(0,0),O=new Word64(0,0),R=new Word64(0,0);let H,D;for(p=0;p>>t|e<<32-t}function calculate_sha256_ch(e,t,a){return e&t^~e&a}function calculate_sha256_maj(e,t,a){return e&t^e&a^t&a}function calculate_sha256_sigma(e){return rotr(e,2)^rotr(e,13)^rotr(e,22)}function calculate_sha256_sigmaPrime(e){return rotr(e,6)^rotr(e,11)^rotr(e,25)}function calculate_sha256_littleSigma(e){return rotr(e,7)^rotr(e,18)^e>>>3}function calculate_sha256_littleSigmaPrime(e){return rotr(e,17)^rotr(e,19)^e>>>10}function calculateSHA256(e,t,a){let n=1779033703,s=3144134277,r=1013904242,i=2773480762,o=1359893119,l=2600822924,f=528734635,c=1541459225;const h=64*Math.ceil((a+9)/64),u=new Uint8Array(h);let m,p;for(m=0;m>>29&255;u[m++]=a>>21&255;u[m++]=a>>13&255;u[m++]=a>>5&255;u[m++]=a<<3&255;const g=new Uint32Array(64),{k:b}=wf;for(m=0;m>24&255,n>>16&255,n>>8&255,255&n,s>>24&255,s>>16&255,s>>8&255,255&s,r>>24&255,r>>16&255,r>>8&255,255&r,i>>24&255,i>>16&255,i>>8&255,255&i,o>>24&255,o>>16&255,o>>8&255,255&o,l>>24&255,l>>16&255,l>>8&255,255&l,f>>24&255,f>>16&255,f>>8&255,255&f,c>>24&255,c>>16&255,c>>8&255,255&c])}class DecryptStream extends DecodeStream{constructor(e,t,a){super(t);this.stream=e;this.dict=e.dict;this.decrypt=a;this.nextChunk=null;this.initialized=!1}readBlock(){let e;if(this.initialized)e=this.nextChunk;else{e=this.stream.getBytes(512);this.initialized=!0}if(!e?.length){this.eof=!0;return}this.nextChunk=this.stream.getBytes(512);const t=this.nextChunk?.length>0;e=(0,this.decrypt)(e,!t);const a=this.bufferLength,n=a+e.length;this.ensureBuffer(n).set(e,a);this.bufferLength=n}getOriginalStream(){return this}}class ARCFourCipher{a=0;b=0;constructor(e){const t=new Uint8Array(256),a=e.length;for(let e=0;e<256;++e)t[e]=e;for(let n=0,s=0;n<256;++n){const r=t[n];s=s+r+e[n%a]&255;t[n]=t[s];t[s]=r}this.s=t}encryptBlock(e){let t=this.a,a=this.b;const n=this.s,s=e.length,r=new Uint8Array(s);for(let i=0;it<128?t<<1:t<<1^27);constructor(){this.buffer=new Uint8Array(16);this.bufferPosition=0}_expandKey(e){unreachable("Cannot call `_expandKey` on the base class")}_decrypt(e,t){let a,n,s;const r=new Uint8Array(16);r.set(e);for(let e=0,a=this._keySize;e<16;++e,++a)r[e]^=t[a];for(let e=this._cyclesOfRepetition-1;e>=1;--e){a=r[13];r[13]=r[9];r[9]=r[5];r[5]=r[1];r[1]=a;a=r[14];n=r[10];r[14]=r[6];r[10]=r[2];r[6]=a;r[2]=n;a=r[15];n=r[11];s=r[7];r[15]=r[3];r[11]=a;r[7]=n;r[3]=s;for(let e=0;e<16;++e)r[e]=this._inv_s[r[e]];for(let a=0,n=16*e;a<16;++a,++n)r[a]^=t[n];for(let e=0;e<16;e+=4){const t=this._mix[r[e]],n=this._mix[r[e+1]],s=this._mix[r[e+2]],i=this._mix[r[e+3]];a=t^n>>>8^n<<24^s>>>16^s<<16^i>>>24^i<<8;r[e]=a>>>24&255;r[e+1]=a>>16&255;r[e+2]=a>>8&255;r[e+3]=255&a}}a=r[13];r[13]=r[9];r[9]=r[5];r[5]=r[1];r[1]=a;a=r[14];n=r[10];r[14]=r[6];r[10]=r[2];r[6]=a;r[2]=n;a=r[15];n=r[11];s=r[7];r[15]=r[3];r[11]=a;r[7]=n;r[3]=s;for(let e=0;e<16;++e){r[e]=this._inv_s[r[e]];r[e]^=t[e]}return r}_encrypt(e,t){const a=this._s;let n,s,r;const i=new Uint8Array(16);i.set(e);for(let e=0;e<16;++e)i[e]^=t[e];for(let e=1;e=n;--a)if(e[a]!==t){t=0;break}o-=t;r[r.length-1]=e.subarray(0,16-t)}}const l=new Uint8Array(o);for(let e=0,t=0,a=r.length;e=256&&(o=255&(27^o))}for(let t=0;t<4;++t){a[e]=n^=a[e-32];e++;a[e]=s^=a[e-32];e++;a[e]=r^=a[e-32];e++;a[e]=i^=a[e-32];e++}}return a}}class PDFBase{_hash(e,t,a){unreachable("Abstract method `_hash` called")}checkOwnerPassword(e,t,a,n){const s=new Uint8Array(e.length+56);s.set(e,0);s.set(t,e.length);s.set(a,e.length+t.length);return isArrayEqual(this._hash(e,s,a),n)}checkUserPassword(e,t,a){const n=new Uint8Array(e.length+8);n.set(e,0);n.set(t,e.length);return isArrayEqual(this._hash(e,n,[]),a)}getOwnerKey(e,t,a,n){const s=new Uint8Array(e.length+56);s.set(e,0);s.set(t,e.length);s.set(a,e.length+t.length);const r=this._hash(e,s,a);return new AES256Cipher(r).decryptBlock(n,!1,new Uint8Array(16))}getUserKey(e,t,a){const n=new Uint8Array(e.length+8);n.set(e,0);n.set(t,e.length);const s=this._hash(e,n,[]);return new AES256Cipher(s).decryptBlock(a,!1,new Uint8Array(16))}}class PDF17 extends PDFBase{_hash(e,t,a){return calculateSHA256(t,0,t.length)}}class PDF20 extends PDFBase{_hash(e,t,a){let n=calculateSHA256(t,0,t.length).subarray(0,32),s=[0],r=0;for(;r<64||s.at(-1)>r-32;){const t=e.length+n.length+a.length,i=new Uint8Array(t);let o=0;i.set(e,o);o+=e.length;i.set(n,o);o+=n.length;i.set(a,o);const l=new Uint8Array(64*t);for(let e=0,a=0;e<64;e++,a+=t)l.set(i,a);s=new AES128Cipher(n.subarray(0,16)).encrypt(l,n.subarray(16,32));const f=Math.sumPrecise(s.slice(0,16))%3;0===f?n=calculateSHA256(s,0,s.length):1===f?n=calculateSHA384(s,0,s.length):2===f&&(n=calculateSHA512(s,0,s.length));r++}return n.subarray(0,32)}}class CipherTransform{constructor(e,t){this.StringCipherConstructor=e;this.StreamCipherConstructor=t}createStream(e,t){const a=new this.StreamCipherConstructor;return new DecryptStream(e,t,function cipherTransformDecryptStream(e,t){return a.decryptBlock(e,t)})}decryptString(e){const t=new this.StringCipherConstructor;let a=stringToBytes(e);a=t.decryptBlock(a,!0);return bytesToString(a)}encryptString(e){const t=new this.StringCipherConstructor;if(t instanceof AESBaseCipher){const a=16-e.length%16;e+=String.fromCharCode(a).repeat(a);const n=new Uint8Array(16);crypto.getRandomValues(n);let s=stringToBytes(e);s=t.encrypt(s,n);const r=new Uint8Array(16+s.length);r.set(n);r.set(s,16);return bytesToString(r)}let a=stringToBytes(e);a=t.encrypt(a);return bytesToString(a)}}class CipherTransformFactory{static get _defaultPasswordBytes(){return shadow(this,"_defaultPasswordBytes",new Uint8Array([40,191,78,94,78,117,138,65,100,0,78,86,255,250,1,8,46,46,0,182,208,104,62,128,47,12,169,254,100,83,105,122]))}#At(e,t,a,n,s,r,i,o,l,f,c,h){if(t){const e=Math.min(127,t.length);t=t.subarray(0,e)}else t=[];const u=6===e?new PDF20:new PDF17;return u.checkUserPassword(t,o,i)?u.getUserKey(t,l,c):t.length&&u.checkOwnerPassword(t,n,r,a)?u.getOwnerKey(t,s,r,f):null}#xt(e,t,a,n,s,r,i,o){const l=40+a.length+e.length,f=new Uint8Array(l);let c,h,u=0;if(t){h=Math.min(32,t.length);for(;u>8&255;f[u++]=s>>16&255;f[u++]=s>>>24&255;f.set(e,u);u+=e.length;if(r>=4&&!o){f.fill(255,u,u+4);u+=4}let m=calculateMD5(f,0,u);const p=i>>3;if(r>=3)for(c=0;c<50;++c)m=calculateMD5(m,0,p);const d=m.subarray(0,p);let g,b;if(r>=3){u=0;f.set(CipherTransformFactory._defaultPasswordBytes,u);u+=32;f.set(e,u);u+=e.length;g=new ARCFourCipher(d);b=g.encryptBlock(calculateMD5(f,0,u));h=d.length;const t=new Uint8Array(h);for(c=1;c<=19;++c){for(let e=0;en[t]===e)?d:null}#Ct(e,t,a,n){const s=new Uint8Array(32);let r=0;const i=Math.min(32,e.length);for(;r>3;if(a>=3)for(o=0;o<50;++o)l=calculateMD5(l,0,l.length);let c,h;if(a>=3){h=t;const e=new Uint8Array(f);for(o=19;o>=0;o--){for(let t=0;t>8&255;r[i++]=e>>16&255;r[i++]=255&t;r[i++]=t>>8&255;if(n){r[i++]=115;r[i++]=65;r[i++]=108;r[i++]=84}return calculateMD5(r,0,i).subarray(0,Math.min(s+5,16))}#Ft(e,t,a,n,s){if(!(t instanceof Name))throw new FormatError("Invalid crypt filter name.");const r=this,i=e.get(t.name),o=i?.get("CFM");if(!o||"None"===o.name)return function(){return new NullCipher};if("V2"===o.name)return function(){return new ARCFourCipher(r.#It(a,n,s,!1))};if("AESV2"===o.name)return function(){return new AES128Cipher(r.#It(a,n,s,!0))};if("AESV3"===o.name)return function(){return new AES256Cipher(s)};throw new FormatError("Unknown crypto method")}constructor(e,t,a){const n=e.get("Filter");if(!isName(n,"Standard"))throw new FormatError("unknown encryption method");this.filterName=n.name;this.dict=e;const s=e.get("V");if(!Number.isInteger(s)||1!==s&&2!==s&&4!==s&&5!==s)throw new FormatError("unsupported encryption algorithm");this.algorithm=s;let r=e.get("Length");if(!r)if(s<=3)r=40;else{const t=e.get("CF"),a=e.get("StmF");if(t instanceof Dict&&a instanceof Name){t.suppressEncryption=!0;const e=t.get(a.name);r=e?.get("Length")||128;r<40&&(r<<=3)}}if(!Number.isInteger(r)||r<40||r%8!=0)throw new FormatError("invalid key length");const i=stringToBytes(e.get("O")),o=stringToBytes(e.get("U")),l=i.subarray(0,32),f=o.subarray(0,32),c=e.get("P"),h=e.get("R"),u=(4===s||5===s)&&!1!==e.get("EncryptMetadata");this.encryptMetadata=u;const m=stringToBytes(t);let p,d;if(a){if(6===h)try{a=utf8StringToString(a)}catch{warn("CipherTransformFactory: Unable to convert UTF8 encoded password.")}p=stringToBytes(a)}if(5!==s)d=this.#xt(m,p,l,f,c,h,r,u);else{const t=i.subarray(32,40),a=i.subarray(40,48),n=o.subarray(0,48),s=o.subarray(32,40),r=o.subarray(40,48),c=stringToBytes(e.get("OE")),u=stringToBytes(e.get("UE")),m=stringToBytes(e.get("Perms"));d=this.#At(h,p,l,t,a,n,f,s,r,c,u,m)}if(!d){if(!a)throw new PasswordException("No password given",Zt);const e=this.#Ct(p,l,h,r);d=this.#xt(m,e,l,f,c,h,r,u)}if(!d)throw new PasswordException("Incorrect Password",ea);if(4===s&&d.length<16){this.encryptionKey=new Uint8Array(16);this.encryptionKey.set(d)}else this.encryptionKey=d;if(s>=4){const t=e.get("CF");t instanceof Dict&&(t.suppressEncryption=!0);this.cf=t;this.stmf=e.get("StmF")||Name.get("Identity");this.strf=e.get("StrF")||Name.get("Identity");this.eff=e.get("EFF")||this.stmf}}createCipherTransform(e,t){if(4===this.algorithm||5===this.algorithm)return new CipherTransform(this.#Ft(this.cf,this.strf,e,t,this.encryptionKey),this.#Ft(this.cf,this.stmf,e,t,this.encryptionKey));const a=this.#It(e,t,this.encryptionKey,!1),cipherConstructor=function(){return new ARCFourCipher(a)};return new CipherTransform(cipherConstructor,cipherConstructor)}}class XRef{constructor(e,t){this.stream=e;this.pdfManager=t;this.entries=[];this._xrefStms=new Set;this._cacheMap=new Map;this._pendingRefs=new RefSet;this._newPersistentRefNum=null;this._newTemporaryRefNum=null;this._persistentRefsCache=null}getNewPersistentRef(e){null===this._newPersistentRefNum&&(this._newPersistentRefNum=this.entries.length||1);const t=this._newPersistentRefNum++;this._cacheMap.set(t,e);return Ref.get(t,0)}getNewTemporaryRef(){if(null===this._newTemporaryRefNum){this._newTemporaryRefNum=this.entries.length||1;if(this._newPersistentRefNum){this._persistentRefsCache=new Map;for(let e=this._newTemporaryRefNum;e0;){const[i,o]=r;if(!Number.isInteger(i)||!Number.isInteger(o))throw new FormatError(`Invalid XRef range fields: ${i}, ${o}`);if(!Number.isInteger(a)||!Number.isInteger(n)||!Number.isInteger(s))throw new FormatError(`Invalid XRef entry fields length: ${i}, ${o}`);for(let r=t.entryNum;r=e.length);){a+=String.fromCharCode(n);n=e[t]}return a}function skipUntil(e,t,a){const n=a.length,s=e.length;let r=0;for(;t=n)break;t++;r++}return r}const e=/\b(endobj|\d+\s+\d+\s+obj|xref|trailer\s*<<)\b/g,t=/\b(startxref|\d+\s+\d+\s+obj)\b/g,a=/^(\d+)\s+(\d+)\s+obj\b/,n=new Uint8Array([116,114,97,105,108,101,114]),s=new Uint8Array([115,116,97,114,116,120,114,101,102]),r=new Uint8Array([47,88,82,101,102]);this.entries.length=0;this._cacheMap.clear();const i=this.stream;i.pos=0;const o=i.getBytes(),l=bytesToString(o),f=o.length;let c=i.start;const h=[],u=[];for(;c=f)break;m=o[c]}while(10!==m&&13!==m);continue}const p=readToken(o,c);let d;if(p.startsWith("xref")&&(4===p.length||/\s/.test(p[4]))){c+=skipUntil(o,c,n);h.push(c);c+=skipUntil(o,c,s)}else if(d=a.exec(p)){const t=0|d[1],a=0|d[2],n=c+p.length;let s,h=!1;if(this.entries[t]){if(this.entries[t].gen===a)try{new Parser({lexer:new Lexer(i.makeSubStream(n))}).getObj();h=!0}catch(e){e instanceof ParserEOFException?warn(`indexObjects -- checking object (${p}): "${e}".`):h=!0}}else h=!0;h&&(this.entries[t]={offset:c-i.start,gen:a,uncompressed:!0});e.lastIndex=n;const m=e.exec(l);if(m){s=e.lastIndex+1-c;if("endobj"!==m[1]){warn(`indexObjects: Found "${m[1]}" inside of another "obj", caused by missing "endobj" -- trying to recover.`);s-=m[1].length+1}}else s=f-c;const g=o.subarray(c,c+s),b=skipUntil(g,0,r);if(b0&&t[3]-t[1]>0)return t;warn(`Empty, or invalid, /${e} entry.`)}return null}get mediaBox(){return shadow(this,"mediaBox",this.getBoundingBox("MediaBox")||jf)}get cropBox(){return shadow(this,"cropBox",this.getBoundingBox("CropBox")||this.mediaBox)}get userUnit(){const e=this.pageDict.get("UserUnit");return shadow(this,"userUnit","number"==typeof e&&e>0?e:1)}get view(){const{cropBox:e,mediaBox:t}=this;if(e!==t&&!isArrayEqual(e,t)){const a=Util.intersect(e,t);if(a&&a[2]-a[0]>0&&a[3]-a[1]>0)return shadow(this,"view",a);warn("Empty /CropBox and /MediaBox intersection.")}return shadow(this,"view",t)}get rotate(){let e=this.#Rt("Rotate")||0;e%90!=0?e=0:e>=360?e%=360:e<0&&(e=(e%360+360)%360);return shadow(this,"rotate",e)}#Ht(e,t){if(!this.evaluatorOptions.ignoreErrors)throw e;warn(`getContentStream - ignoring sub-stream (${t}): "${e}".`)}async getContentStream(){const e=await this.pdfManager.ensure(this,"content");if(e instanceof BaseStream&&!e.isImageStream){if(e.isAsync){const t=await e.asyncGetBytes();if(t)return new Stream(t,0,t.length,e.dict)}return e}if(Array.isArray(e)){const t=[];for(let a=0,n=e.length;a{t&&(e[a]=new Stream(t,0,t.length,n.dict))}))}t.length>0&&await Promise.all(t);return new StreamsSequenceStream(e,this.#Ht.bind(this))}return new NullStream}get xfaData(){return shadow(this,"xfaData",this.xfaFactory?{bbox:this.xfaFactory.getBoundingBox(this.pageIndex)}:null)}async#Bt(e,t,a){const n=[];for(const s of e)if(s.id){const e=Ref.fromString(s.id);if(!e){warn(`A non-linked annotation cannot be modified: ${s.id}`);continue}if(s.deleted){t.put(e,e);if(s.popupRef){const e=Ref.fromString(s.popupRef);e&&t.put(e,e)}continue}if(s.popup?.deleted){const e=Ref.fromString(s.popupRef);e&&t.put(e,e)}a?.put(e);s.ref=e;n.push(this.xref.fetchAsync(e).then(e=>{e instanceof Dict&&(s.oldAnnotation=e.clone())},()=>{warn(`Cannot fetch \`oldAnnotation\` for: ${e}.`)}));delete s.id}await Promise.all(n)}async saveNewAnnotations(e,t,a,n,s){if(this.xfaFactory)throw new Error("XFA: Cannot save new annotations.");const r=this.#Ot(e),i=new RefSetCache,o=new RefSet;await this.#Bt(a,i,o);const l=this.pageDict,f=this.annotations.filter(e=>!(e instanceof Ref&&i.has(e))),c=await AnnotationFactory.saveNewAnnotations(r,this.xref,t,a,n,s);for(const{ref:e}of c.annotations)e instanceof Ref&&!o.has(e)&&f.push(e);const h=l.clone();h.set("Annots",f);s.put(this.ref,{data:h});for(const e of i)s.put(e,{data:null})}async save(e,t,a,n){const s=this.#Ot(e),r=await this._parsedAnnotations,i=[];for(const e of r)i.push(e.save(s,t,a,n).catch(function(e){warn(`save - ignoring annotation data during "${t.name}" task: "${e}".`);return null}));return Promise.all(i)}async loadResources(e){await(this.#Tt??=this.pdfManager.ensure(this,"resources"));await ObjectLoader.load(this.resources,e,this.xref)}async#Dt(e,t){const a=e?.get("Resources");if(!(a instanceof Dict&&a.size))return this.resources;await ObjectLoader.load(a,t,this.xref);return Dict.merge({xref:this.xref,dictArray:[a,this.resources],mergeSubDicts:!0})}async getOperatorList({handler:e,sink:t,task:a,intent:n,cacheKey:s,pageIndex:r=this.pageIndex,annotationStorage:i=null,modifiedIds:c=null}){const p=this.getContentStream(),g=this.loadResources(da),b=this.#Ot(e,r),w=this.xfaFactory?null:getNewAnnotationsMap(i),j=w?.get(this.pageIndex);let k=Promise.resolve(null),y=null;if(j){const e=this.pdfManager.ensureDoc("annotationGlobals");let t;const n=new Set;for(const{bitmapId:e,bitmap:t}of j)!e||t||n.has(e)||n.add(e);const{isOffscreenCanvasSupported:s}=this.evaluatorOptions;if(n.size>0){const e=j.slice();for(const[t,a]of i)t.startsWith(d)&&a.bitmap&&n.has(a.bitmapId)&&e.push(a);t=AnnotationFactory.generateImages(e,this.xref,s)}else t=AnnotationFactory.generateImages(j,this.xref,s);y=new RefSet;k=Promise.all([e,this.#Bt(j,y,null)]).then(([e])=>e?AnnotationFactory.printNewAnnotations(e,b,a,j,t):null)}const q=Promise.all([p,g]).then(async([i])=>{const o=await this.#Dt(i.dict,da),l=new OperatorList(n,t);e.send("StartRenderPage",{transparency:b.hasBlendModes(o,this.nonBlendModesSet),pageIndex:r,cacheKey:s});await b.getOperatorList({stream:i,task:a,resources:o,operatorList:l});return l});let[v,S,x]=await Promise.all([q,this._parsedAnnotations,k]);if(x){S=S.filter(e=>!(e.ref&&y.has(e.ref)));for(let e=0,t=x.length;ee.ref&&isRefsEqual(e.ref,a.refToReplace));if(n>=0){S.splice(n,1,a);x.splice(e--,1);t--}}}S=S.concat(x)}if(0===S.length||n&u){v.flush(!0);return{length:v.totalLength}}const C=!!(n&h),F=!!(n&m),T=!!(n&o),O=!!(n&l),R=!!(n&f),H=[];for(const e of S)(T||O&&e.mustBeViewed(i,C)&&e.mustBeViewedWhenEditing(F,c)||R&&e.mustBePrinted(i))&&H.push(e.getOperatorList(b,a,n,i).catch(function(e){warn(`getOperatorList - ignoring annotation data during "${a.name}" task: "${e}".`);return{opList:null,separateForm:!1,separateCanvas:!1}}));const D=await Promise.all(H);let M=!1,N=!1;for(const{opList:e,separateForm:t,separateCanvas:a}of D){v.addOpList(e);M||=t;N||=a}v.flush(!0,{form:M,canvas:N});return{length:v.totalLength}}async extractTextContent({handler:e,task:t,includeMarkedContent:a,disableNormalization:n,sink:s,intersector:r=null}){const i=this.getContentStream(),o=this.loadResources(ga),l=this.pdfManager.ensureCatalog("lang"),[f,,c]=await Promise.all([i,o,l]),h=await this.#Dt(f.dict,ga);return this.#Ot(e).getTextContent({stream:f,task:t,resources:h,includeMarkedContent:a,disableNormalization:n,sink:s,viewBox:this.view,lang:c,intersector:r})}async getStructTree(){const e=await this.pdfManager.ensureCatalog("structTreeRoot");if(!e)return null;await this._parsedAnnotations;try{const t=await this.pdfManager.ensure(this,"_parseStructTree",[e]);return await this.pdfManager.ensure(t,"serializable")}catch(e){warn(`getStructTree: "${e}".`);return null}}_parseStructTree(e){const t=new StructTreePage(e,this.pageDict);t.parse(this.ref);return t}async getAnnotationsData(e,t,a){const n=await this._parsedAnnotations;if(0===n.length)return n;const s=[],r=[];let i;const c=!!(a&o),h=!!(a&l),u=!!(a&f),m=[];for(const a of n){const n=c||h&&a.viewable;(n||u&&a.printable)&&s.push(a.data);if(a.hasTextContent&&n){i??=this.#Ot(e);r.push(a.extractTextContent(i,t,[-1/0,-1/0,1/0,1/0]).catch(function(e){warn(`getAnnotationsData - ignoring textContent during "${t.name}" task: "${e}".`)}))}else a.overlaysTextContent&&n&&m.push(a)}if(m.length>0){const a=new Intersector(m);r.push(this.extractTextContent({handler:e,task:t,includeMarkedContent:!1,disableNormalization:!1,sink:null,viewBox:this.view,lang:null,intersector:a}).then(()=>{a.setText()}))}await Promise.all(r);return s}get annotations(){const e=this.#Rt("Annots");return shadow(this,"annotations",Array.isArray(e)?e:[])}get _parsedAnnotations(){return shadow(this,"_parsedAnnotations",this.pdfManager.ensure(this,"annotations").then(async e=>{if(0===e.length)return e;const[t,a]=await Promise.all([this.pdfManager.ensureDoc("annotationGlobals"),this.pdfManager.ensureDoc("fieldObjects")]);if(!t)return[];const n=a?.orphanFields,s=[];for(const a of e)s.push(AnnotationFactory.create(this.xref,a,t,this._localIdFactory,!1,n,null,this.ref).catch(function(e){warn(`_parsedAnnotations: "${e}".`);return null}));const r=[];let i,o;for(const e of await Promise.all(s))e&&(e instanceof WidgetAnnotation?(o||=[]).push(e):e instanceof PopupAnnotation?(i||=[]).push(e):r.push(e));o&&r.push(...o);i&&r.push(...i);return r}))}get jsActions(){return shadow(this,"jsActions",collectActions(this.xref,this.pageDict,le))}async collectAnnotationsByType(e,t,a,n,s){const{pageIndex:r}=this;if(Object.hasOwn(this,"_parsedAnnotations")){const e=await this._parsedAnnotations;for(const{data:t}of e)if(!a||a.has(t.annotationType)){t.pageIndex=r;n.push(Promise.resolve(t))}return}const i=await this.pdfManager.ensure(this,"annotations");let o;for(const l of i)n.push(AnnotationFactory.create(this.xref,l,s,this._localIdFactory,!1,null,a,this.ref).then(async a=>{if(!a)return null;a.data.pageIndex=r;if(a.hasTextContent&&a.viewable){o??=this.#Ot(e);await a.extractTextContent(o,t,[-1/0,-1/0,1/0,1/0])}return a.data}).catch(function(e){warn(`collectAnnotationsByType: "${e}".`);return null}))}}const kf=new Uint8Array([37,80,68,70,45]),yf=new Uint8Array([115,116,97,114,116,120,114,101,102]),qf=new Uint8Array([101,110,100,111,98,106]);function find(e,t,a=1024,n=!1){const s=t.length,r=e.peekBytes(a),i=r.length-s;if(i<=0)return!1;if(n){const a=s-1;let n=r.length-1;for(;n>=a;){let i=0;for(;i=s){e.pos+=n-a;return!0}n--}}else{let a=0;for(;a<=i;){let n=0;for(;n=s){e.pos+=a;return!0}a++}}return!1}class PDFDocument{#Mt=new Map;#Nt=null;constructor(e,t){if(t.length<=0)throw new InvalidPDFException("The PDF file is empty, i.e. its size is zero bytes.");this.pdfManager=e;this.stream=t;this.xref=new XRef(t,e);const a={font:0};this._globalIdFactory=class{static getDocId(){return`g_${e.docId}`}static createFontId(){return"f"+ ++a.font}static createObjId(){unreachable("Abstract method `createObjId` called.")}static getPageObjId(){unreachable("Abstract method `getPageObjId` called.")}}}parse(e){this.xref.parse(e);this.catalog=new Catalog(this.pdfManager,this.xref)}get linearization(){let e=null;try{e=Linearization.create(this.stream)}catch(e){if(e instanceof MissingDataException)throw e;info(e)}return shadow(this,"linearization",e)}get startXRef(){const e=this.stream;let t=0;if(this.linearization){e.reset();if(find(e,qf)){e.skip(6);let a=e.peekByte();for(;isWhiteSpace(a);){e.pos++;a=e.peekByte()}t=e.pos-e.start}}else{const a=1024,n=yf.length;let s=!1,r=e.end;for(;!s&&r>0;){r-=a-n;r<0&&(r=0);e.pos=r;s=find(e,yf,a,!0)}if(s){e.skip(9);let a;do{a=e.getByte()}while(isWhiteSpace(a));let n="";for(;a>=32&&a<=57;){n+=String.fromCharCode(a);a=e.getByte()}t=parseInt(n,10);isNaN(t)&&(t=0)}}return shadow(this,"startXRef",t)}checkHeader(){const e=this.stream;e.reset();if(!find(e,kf))return;e.moveStart();e.skip(kf.length);let t,a="";for(;(t=e.getByte())>32&&a.length<7;)a+=String.fromCharCode(t);ua.test(a)?this.#Nt=a:warn(`Invalid PDF header version: ${a}`)}parseStartXRef(){this.xref.setStartXRef(this.startXRef)}get numPages(){let e=0;e=this.catalog.hasActualNumPages?this.catalog.numPages:this.xfaFactory?this.xfaFactory.getNumPages():this.linearization?this.linearization.numPages:this.catalog.numPages;return shadow(this,"numPages",e)}#Pt(e,t=0){return!!Array.isArray(e)&&e.every(e=>{if(!((e=this.xref.fetchIfRef(e))instanceof Dict))return!1;if(e.has("Kids")){if(++t>10){warn("#hasOnlyDocumentSignatures: maximum recursion depth reached");return!1}return this.#Pt(e.get("Kids"),t)}const a=isName(e.get("FT"),"Sig"),n=e.get("Rect"),s=Array.isArray(n)&&n.every(e=>0===e);return a&&s})}get _xfaStreams(){const{acroForm:e}=this.catalog;if(!e)return null;const t=e.get("XFA"),a=new Map(["xdp:xdp","template","datasets","config","connectionSet","localeSet","stylesheet","/xdp:xdp"].map(e=>[e,null]));if(t instanceof BaseStream&&!t.isEmpty){a.set("xdp:xdp",t);return a}if(!Array.isArray(t)||0===t.length)return null;for(let e=0,n=t.length;ef.handleSetFont(n,[Name.get(e),1],null,c,t,u,a,s).catch(e=>{warn(`loadXfaFonts: "${e}".`);return null}),m=[];for(const[e,t]of s){const a=t.get("FontDescriptor");if(!(a instanceof Dict))continue;let n=a.get("FontFamily");n=n.replaceAll(/[ ]+(\d)/g,"$1");const s={fontFamily:n,fontWeight:a.get("FontWeight"),italicAngle:-a.get("ItalicAngle")};validateCSSFont(s)&&m.push(parseFont(e,null,s))}await Promise.all(m);const p=this.xfaFactory.setFonts(h);if(!p)return;r.ignoreErrors=!0;m.length=0;h.length=0;const d=new Set;for(const e of p)getXfaFontName(`${e}-Regular`)||d.add(e);d.size&&p.push("PdfJS-Fallback");for(const e of p)if(!d.has(e))for(const t of[{name:"Regular",fontWeight:400,italicAngle:0},{name:"Bold",fontWeight:700,italicAngle:0},{name:"Italic",fontWeight:400,italicAngle:12},{name:"BoldItalic",fontWeight:700,italicAngle:12}]){const a=`${e}-${t.name}`;m.push(parseFont(a,getXfaFontDict(a),{fontFamily:e,fontWeight:t.fontWeight,italicAngle:t.italicAngle}))}await Promise.all(m);this.xfaFactory.appendFonts(h,d)}loadXfaResources(e,t){return Promise.all([this.#_t(e,t).catch(()=>{}),this.#Et()])}serializeXfaData(e){return this.xfaFactory?this.xfaFactory.serializeData(e):null}get version(){return this.catalog.version||this.#Nt}get formInfo(){const e={hasFields:!1,hasAcroForm:!1,hasXfa:!1,hasSignatures:!1},{acroForm:t}=this.catalog;if(!t)return shadow(this,"formInfo",e);try{const a=t.get("Fields"),n=Array.isArray(a)&&a.length>0;e.hasFields=n;const s=t.get("XFA");e.hasXfa=Array.isArray(s)&&s.length>0||s instanceof BaseStream&&!s.isEmpty;const r=!!(1&t.get("SigFlags")),i=r&&this.#Pt(a);e.hasAcroForm=n&&!i;e.hasSignatures=r}catch(e){if(e instanceof MissingDataException)throw e;warn(`Cannot fetch form information: "${e}".`)}return shadow(this,"formInfo",e)}get documentInfo(){const{catalog:e,formInfo:t,xref:a}=this,n={PDFFormatVersion:this.version,Language:e.lang,EncryptFilterName:a.encrypt?.filterName??null,IsLinearized:!!this.linearization,IsAcroFormPresent:t.hasAcroForm,IsXFAPresent:t.hasXfa,IsCollectionPresent:!!e.collection,IsSignaturesPresent:t.hasSignatures};let s;try{s=a.trailer.get("Info")}catch(e){if(e instanceof MissingDataException)throw e;info("The document information dictionary is invalid.")}if(!(s instanceof Dict))return shadow(this,"documentInfo",n);for(const[e,t]of s){switch(e){case"Title":case"Author":case"Subject":case"Keywords":case"Creator":case"Producer":case"CreationDate":case"ModDate":if("string"==typeof t){n[e]=stringToPDFString(t);continue}break;case"Trapped":if(t instanceof Name){n[e]=t;continue}break;default:let a;switch(typeof t){case"string":a=stringToPDFString(t);break;case"number":case"boolean":a=t;break;default:t instanceof Name&&(a=t)}if(void 0===a){warn(`Bad value, for custom key "${e}", in Info: ${t}.`);continue}n.Custom??=Object.create(null);n.Custom[e]=a;continue}warn(`Bad value, for key "${e}", in Info: ${t}.`)}return shadow(this,"documentInfo",n)}get fingerprints(){const e="\0".repeat(16);function validate(t){return"string"==typeof t&&16===t.length&&t!==e}const t=this.xref.trailer.get("ID");let a,n;if(Array.isArray(t)&&validate(t[0])){a=stringToBytes(t[0]);t[1]!==t[0]&&validate(t[1])&&(n=stringToBytes(t[1]))}else a=calculateMD5(this.stream.getByteRange(0,1024),0,1024);return shadow(this,"fingerprints",[a.toHex(),n?.toHex()??null])}async#zt(e){const{catalog:t,linearization:a,xref:n}=this,s=Ref.get(a.objectNumberFirst,0);try{const e=await n.fetchAsync(s);if(e instanceof Dict){let a=e.getRaw("Type");a instanceof Ref&&(a=await n.fetchAsync(a));if(isName(a,"Page")||!e.has("Type")&&!e.has("Kids")&&e.has("Contents")){t.pageKidsCountCache.has(s)||t.pageKidsCountCache.put(s,1);t.pageIndexCache.has(s)||t.pageIndexCache.put(s,0);return[e,s]}}throw new FormatError("The Linearization dictionary doesn't point to a valid Page dictionary.")}catch(a){warn(`_getLinearizationPage: "${a.message}".`);return t.getPageDict(e)}}getPage(e){const t=this.#Mt.get(e);if(t)return t;const{catalog:a,linearization:n,xfaFactory:s}=this;let r;r=s?Promise.resolve([Dict.empty,null]):n?.pageFirst===e?this.#zt(e):a.getPageDict(e);r=r.then(([t,n])=>new Page({pdfManager:this.pdfManager,xref:this.xref,pageIndex:e,pageDict:t,ref:n,globalIdFactory:this._globalIdFactory,fontCache:a.fontCache,builtInCMapCache:a.builtInCMapCache,standardFontDataCache:a.standardFontDataCache,globalColorSpaceCache:a.globalColorSpaceCache,globalImageCache:a.globalImageCache,systemFontCache:a.systemFontCache,nonBlendModesSet:a.nonBlendModesSet,xfaFactory:s}));this.#Mt.set(e,r);return r}async checkFirstPage(e=!1){if(!e)try{await this.getPage(0)}catch(e){if(e instanceof XRefEntryException){this.#Mt.delete(0);await this.cleanup();throw new XRefParseException}}}async checkLastPage(e=!1){const{catalog:t,pdfManager:a}=this;t.setActualNumPages();let n;try{await Promise.all([a.ensureDoc("xfaFactory"),a.ensureDoc("linearization"),a.ensureCatalog("numPages")]);if(this.xfaFactory)return;n=this.linearization?this.linearization.numPages:t.numPages;if(!Number.isInteger(n))throw new FormatError("Page count is not an integer.");if(n<=1)return;await this.getPage(n-1)}catch(s){this.#Mt.delete(n-1);await this.cleanup();if(s instanceof XRefEntryException&&!e)throw new XRefParseException;warn(`checkLastPage - invalid /Pages tree /Count: ${n}.`);let r;try{r=await t.getAllPageDicts(e)}catch(a){if(a instanceof XRefEntryException&&!e)throw new XRefParseException;t.setActualNumPages(1);return}for(const[e,[n,s]]of r){let r;if(n instanceof Error){r=Promise.reject(n);r.catch(()=>{})}else r=Promise.resolve(new Page({pdfManager:a,xref:this.xref,pageIndex:e,pageDict:n,ref:s,globalIdFactory:this._globalIdFactory,fontCache:t.fontCache,builtInCMapCache:t.builtInCMapCache,standardFontDataCache:t.standardFontDataCache,globalColorSpaceCache:this.globalColorSpaceCache,globalImageCache:t.globalImageCache,systemFontCache:t.systemFontCache,nonBlendModesSet:t.nonBlendModesSet,xfaFactory:null}));this.#Mt.set(e,r)}t.setActualNumPages(r.size)}}async fontFallback(e,t){const{catalog:a,pdfManager:n}=this;for(const s of await Promise.all(a.fontCache))if(s.loadedName===e){s.fallback(t,n.evaluatorOptions);return}}async cleanup(e=!1){return this.catalog?this.catalog.cleanup(e):clearGlobalCaches()}async#Lt(e,t,a,n,s,r,i){const{xref:o}=this;if(!(a instanceof Ref)||r.has(a))return;r.put(a);const l=await o.fetchAsync(a);if(!(l instanceof Dict))return;let f=await l.getAsync("Subtype");f=f instanceof Name?f.name:null;if("Link"===f)return;if(l.has("T")){const t=stringToPDFString(await l.getAsync("T"));e=""===e?t:`${e}.${t}`}else{let a=l;for(;;){a=a.getRaw("Parent")||t;if(a instanceof Ref){if(r.has(a))break;a=await o.fetchAsync(a)}if(!(a instanceof Dict))break;if(a.has("T")){const t=stringToPDFString(await a.getAsync("T"));e=""===e?t:`${e}.${t}`;break}}}t&&!l.has("Parent")&&isName(l.get("Subtype"),"Widget")&&i.put(a,t);n.getOrInsertComputed(e,makeArr).push(AnnotationFactory.create(o,a,s,null,!0,i,null,null).then(e=>e?.getFieldObject()).catch(function(e){warn(`#collectFieldObjects: "${e}".`);return null}));if(!l.has("Kids"))return;const c=await l.getAsync("Kids");if(Array.isArray(c))for(const t of c)await this.#Lt(e,a,t,n,s,r,i)}get fieldObjects(){return shadow(this,"fieldObjects",this.pdfManager.ensureDoc("formInfo").then(async e=>{if(!e.hasFields)return null;const t=await this.annotationGlobals;if(!t)return null;const{acroForm:a}=t,n=new RefSet,s=Object.create(null),r=new Map,i=new RefSetCache;for(const e of a.get("Fields"))await this.#Lt("",null,e,r,t,n,i);const o=[];for(const[e,t]of r)o.push(Promise.all(t).then(t=>{(t=t.filter(e=>!!e)).length>0&&(s[e]=t)}));await Promise.all(o);return{allFields:objectSize(s)>0?s:null,orphanFields:i}}))}get hasJSActions(){return shadow(this,"hasJSActions",this.pdfManager.ensureDoc("_parseHasJSActions"))}async _parseHasJSActions(){const[e,t]=await Promise.all([this.pdfManager.ensureCatalog("jsActions"),this.pdfManager.ensureDoc("fieldObjects")]);return!!e||!!t?.allFields&&Object.values(t.allFields).some(e=>e.some(e=>null!==e.actions))}get calculationOrderIds(){const e=this.catalog.acroForm?.get("CO");if(!Array.isArray(e)||0===e.length)return shadow(this,"calculationOrderIds",null);const t=[];for(const a of e)a instanceof Ref&&t.push(a.toString());return shadow(this,"calculationOrderIds",t.length?t:null)}get annotationGlobals(){return shadow(this,"annotationGlobals",AnnotationFactory.createGlobals(this.pdfManager))}async toJSObject(e,t=!0){throw new Error("Not implemented: toJSObject")}}class BasePdfManager{constructor({docBaseUrl:e,docId:t,enableXfa:a,evaluatorOptions:n,handler:s,password:r}){this._docBaseUrl=function parseDocBaseUrl(e){if(e){const t=createValidAbsoluteUrl(e);if(t)return t.href;warn(`Invalid absolute docBaseUrl: "${e}".`)}return null}(e);this._docId=t;this._password=r;this.enableXfa=a;n.isOffscreenCanvasSupported&&=FeatureTest.isOffscreenCanvasSupported;n.isImageDecoderSupported&&=FeatureTest.isImageDecoderSupported;this.evaluatorOptions=Object.freeze(n);ImageResizer.setOptions(n);JpegStream.setOptions(n);OperatorList.setOptions(n);const i={...n,handler:s};JpxImage.setOptions(i);IccColorSpace.setOptions(i);CmykICCBasedCS.setOptions(i);JBig2CCITTFaxImage.setOptions(i);PDFFunctionFactory.setOptions(i);Pattern.setOptions(i)}get docId(){return this._docId}get password(){return this._password}get docBaseUrl(){return this._docBaseUrl}ensureDoc(e,t){return this.ensure(this.pdfDocument,e,t)}ensureXRef(e,t){return this.ensure(this.pdfDocument.xref,e,t)}ensureCatalog(e,t){return this.ensure(this.pdfDocument.catalog,e,t)}getPage(e){return this.pdfDocument.getPage(e)}fontFallback(e,t){return this.pdfDocument.fontFallback(e,t)}cleanup(e=!1){return this.pdfDocument.cleanup(e)}async ensure(e,t,a){unreachable("Abstract method `ensure` called")}requestRange(e,t){unreachable("Abstract method `requestRange` called")}requestLoadedStream(e=!1){unreachable("Abstract method `requestLoadedStream` called")}sendProgressiveData(e){unreachable("Abstract method `sendProgressiveData` called")}updatePassword(e){this._password=e}terminate(e){unreachable("Abstract method `terminate` called")}}class LocalPdfManager extends BasePdfManager{constructor(e){super(e);const t=new Stream(e.source);this.pdfDocument=new PDFDocument(this,t);this._loadedStreamPromise=Promise.resolve(t)}async ensure(e,t,a){const n=e[t];return"function"==typeof n?n.apply(e,a):n}requestRange(e,t){return Promise.resolve()}requestLoadedStream(e=!1){return this._loadedStreamPromise}terminate(e){}}class NetworkPdfManager extends BasePdfManager{constructor(e){super(e);this.streamManager=new ChunkedStreamManager(e.source,{msgHandler:e.handler,length:e.length,disableAutoFetch:e.disableAutoFetch,rangeChunkSize:e.rangeChunkSize});this.pdfDocument=new PDFDocument(this,this.streamManager.getStream())}async ensure(e,t,a){try{const n=e[t];return"function"==typeof n?await n.apply(e,a):n}catch(n){if(!(n instanceof MissingDataException))throw n;await this.requestRange(n.begin,n.end);return this.ensure(e,t,a)}}requestRange(e,t){return this.streamManager.requestRange(e,t)}requestLoadedStream(e=!1){return this.streamManager.requestAllChunks(e)}sendProgressiveData(e){this.streamManager.onReceiveData({chunk:e})}terminate(e){this.streamManager.abort(e)}}const vf=1,Sf=2,Af=1,xf=2,Cf=3,If=4,Ff=5,Tf=6,Of=7,Rf=8;function onFn(){}function wrapReason(e){if(e instanceof AbortException||e instanceof InvalidPDFException||e instanceof PasswordException||e instanceof ResponseException||e instanceof UnknownErrorException)return e;e instanceof Error||"object"==typeof e&&null!==e||unreachable('wrapReason: Expected "reason" to be a (possibly cloned) Error.');switch(e.name){case"AbortException":return new AbortException(e.message);case"InvalidPDFException":return new InvalidPDFException(e.message);case"PasswordException":return new PasswordException(e.message,e.code);case"ResponseException":return new ResponseException(e.message,e.status,e.missing);case"UnknownErrorException":return new UnknownErrorException(e.message,e.details)}return new UnknownErrorException(e.message,e.toString())}class MessageHandler{#Ut=new AbortController;constructor(e,t,a){this.sourceName=e;this.targetName=t;this.comObj=a;this.callbackId=1;this.streamId=1;this.streamSinks=Object.create(null);this.streamControllers=Object.create(null);this.callbackCapabilities=Object.create(null);this.actionHandler=Object.create(null);a.addEventListener("message",this.#Xt.bind(this),{signal:this.#Ut.signal})}#Xt({data:e}){if(e.targetName!==this.sourceName)return;if(e.stream){this.#Wt(e);return}if(e.callback){const t=e.callbackId,a=this.callbackCapabilities[t];if(!a)throw new Error(`Cannot resolve callback ${t}`);delete this.callbackCapabilities[t];if(e.callback===vf)a.resolve(e.data);else{if(e.callback!==Sf)throw new Error("Unexpected callback case");a.reject(wrapReason(e.reason))}return}const t=this.actionHandler[e.action];if(!t)throw new Error(`Unknown action from worker: ${e.action}`);if(e.callbackId){const a=this.sourceName,n=e.sourceName,s=this.comObj;Promise.try(t,e.data).then(function(t){s.postMessage({sourceName:a,targetName:n,callback:vf,callbackId:e.callbackId,data:t})},function(t){s.postMessage({sourceName:a,targetName:n,callback:Sf,callbackId:e.callbackId,reason:wrapReason(t)})});return}e.streamId?this.#Kt(e):t(e.data)}on(e,t){const a=this.actionHandler;if(a[e])throw new Error(`There is already an actionName called "${e}"`);a[e]=t}send(e,t,a){this.comObj.postMessage({sourceName:this.sourceName,targetName:this.targetName,action:e,data:t},a)}sendWithPromise(e,t,a){const n=this.callbackId++,s=Promise.withResolvers();this.callbackCapabilities[n]=s;try{this.comObj.postMessage({sourceName:this.sourceName,targetName:this.targetName,action:e,callbackId:n,data:t},a)}catch(e){s.reject(e)}return s.promise}sendWithStream(e,t,a,n){const s=this.streamId++,r=this.sourceName,i=this.targetName,o=this.comObj;return new ReadableStream({start:a=>{const l=Promise.withResolvers();this.streamControllers[s]={controller:a,startCall:l,pullCall:null,cancelCall:null,isClosed:!1};o.postMessage({sourceName:r,targetName:i,action:e,streamId:s,data:t,desiredSize:a.desiredSize},n);return l.promise},pull:e=>{const t=Promise.withResolvers();this.streamControllers[s].pullCall=t;o.postMessage({sourceName:r,targetName:i,stream:Tf,streamId:s,desiredSize:e.desiredSize});return t.promise},cancel:e=>{assert(e instanceof Error,"cancel must have a valid reason");const t=Promise.withResolvers();this.streamControllers[s].cancelCall=t;this.streamControllers[s].isClosed=!0;o.postMessage({sourceName:r,targetName:i,stream:Af,streamId:s,reason:wrapReason(e)});return t.promise}},a)}#Kt(e){const t=e.streamId,a=this.sourceName,n=e.sourceName,s=this.comObj,r=this,i=this.actionHandler[e.action],o={enqueue(e,r=1,i){if(this.isCancelled)return;const o=this.desiredSize;this.desiredSize-=r;if(o>0&&this.desiredSize<=0){this.sinkCapability=Promise.withResolvers();this.ready=this.sinkCapability.promise}s.postMessage({sourceName:a,targetName:n,stream:If,streamId:t,chunk:e},i)},close(){if(!this.isCancelled){this.isCancelled=!0;s.postMessage({sourceName:a,targetName:n,stream:Cf,streamId:t});delete r.streamSinks[t]}},error(e){assert(e instanceof Error,"error must have a valid reason");if(!this.isCancelled){this.isCancelled=!0;s.postMessage({sourceName:a,targetName:n,stream:Ff,streamId:t,reason:wrapReason(e)})}},sinkCapability:Promise.withResolvers(),onPull:null,onCancel:null,isCancelled:!1,desiredSize:e.desiredSize,ready:null};o.sinkCapability.resolve();o.ready=o.sinkCapability.promise;this.streamSinks[t]=o;Promise.try(i,e.data,o).then(function(){s.postMessage({sourceName:a,targetName:n,stream:Rf,streamId:t,success:!0})},function(e){s.postMessage({sourceName:a,targetName:n,stream:Rf,streamId:t,reason:wrapReason(e)})})}#Wt(e){const t=e.streamId,a=this.sourceName,n=e.sourceName,s=this.comObj,r=this.streamControllers[t],i=this.streamSinks[t];switch(e.stream){case Rf:e.success?r.startCall.resolve():r.startCall.reject(wrapReason(e.reason));break;case Of:e.success?r.pullCall.resolve():r.pullCall.reject(wrapReason(e.reason));break;case Tf:if(!i){s.postMessage({sourceName:a,targetName:n,stream:Of,streamId:t,success:!0});break}i.desiredSize<=0&&e.desiredSize>0&&i.sinkCapability.resolve();i.desiredSize=e.desiredSize;Promise.try(i.onPull||onFn).then(function(){s.postMessage({sourceName:a,targetName:n,stream:Of,streamId:t,success:!0})},function(e){s.postMessage({sourceName:a,targetName:n,stream:Of,streamId:t,reason:wrapReason(e)})});break;case If:assert(r,"enqueue should have stream controller");if(r.isClosed)break;r.controller.enqueue(e.chunk);break;case Cf:assert(r,"close should have stream controller");if(r.isClosed)break;r.isClosed=!0;r.controller.close();this.#Gt(r,t);break;case Ff:assert(r,"error should have stream controller");r.controller.error(wrapReason(e.reason));this.#Gt(r,t);break;case xf:e.success?r.cancelCall.resolve():r.cancelCall.reject(wrapReason(e.reason));this.#Gt(r,t);break;case Af:if(!i)break;const o=wrapReason(e.reason);Promise.try(i.onCancel||onFn,o).then(function(){s.postMessage({sourceName:a,targetName:n,stream:xf,streamId:t,success:!0})},function(e){s.postMessage({sourceName:a,targetName:n,stream:xf,streamId:t,reason:wrapReason(e)})});i.sinkCapability.reject(o);i.isCancelled=!0;delete this.streamSinks[t];break;default:throw new Error("Unexpected stream case")}}async#Gt(e,t){await Promise.allSettled([e.startCall?.promise,e.pullCall?.promise,e.cancelCall?.promise]);delete this.streamControllers[t]}destroy(){this.#Ut?.abort();this.#Ut=null}}async function writeObject(e,t,a,{encrypt:n=null,encryptRef:s=null}){const r=n&&s!==e?n.createCipherTransform(e.num,e.gen):null;a.push(`${e.num} ${e.gen} obj\n`);await writeValue(t,a,r);a.push("\nendobj\n")}async function writeDict(e,t,a){t.push("<<");for(const[n,s]of e.getRawEntries()){t.push(` /${escapePDFName(n)} `);await writeValue(s,t,a)}t.push(">>")}async function writeValue(e,t,a){if(e instanceof Name)t.push(`/${escapePDFName(e.name)}`);else if(e instanceof Ref)t.push(`${e.num} ${e.gen} R`);else if(Array.isArray(e)||ArrayBuffer.isView(e))await async function writeArray(e,t,a){t.push("[");for(let n=0,s=e.length;n=256&&!o)try{const e=new CompressionStream("deflate"),t=e.writable.getWriter();await t.ready;t.write(n).then(async()=>{await t.ready;await t.close()}).catch(()=>{});n=await new Response(e.readable).bytes();let a,l;if(r){if(!o){a=Array.isArray(r)?[Name.get("FlateDecode"),...r]:[Name.get("FlateDecode"),r];i&&(l=Array.isArray(i)?[null,...i]:[null,i])}}else a=Name.get("FlateDecode");a&&s.set("Filter",a);l&&s.set("DecodeParms",l)}catch(e){info(`writeStream - cannot compress data: "${e}".`)}let l=bytesToString(n);a&&(l=a.encryptString(l));s.set("Length",l.length);await writeDict(s,t,a);t.push(" stream\n",l,"\nendstream")}(e,t,a):null===e?t.push("null"):warn(`Unhandled value in writer: ${typeof e}, please file a bug.`)}function writeInt(e,t,a,n){for(let s=t+a-1;s>a-1;s--){n[s]=255&e;e>>=8}return a+t}function writeString(e,t,a){const n=e.length;for(let s=0;s1&&(r=a.documentElement.searchNode([s.at(-1)],0));r?r.childNodes=Array.isArray(n)?n.map(e=>new SimpleDOMNode("value",e)):[new SimpleDOMNode("#text",n)]:warn(`Node not found for path: ${t}`)}const n=[];a.documentElement.dump(n);return n.join("")}(n.fetchIfRef(t).getString(),a)}const s=new StringStream(e);s.dict=new Dict(n);s.dict.setIfName("Type","EmbeddedFile");a.put(t,{data:s})}function getIndexes(e){const t=[];for(const{ref:a}of e)a.num===t.at(-2)+t.at(-1)?t[t.length-1]+=1:t.push(a.num,1);return t}function computeIDs(e,t,a){if(Array.isArray(t.fileIds)&&t.fileIds.length>0){const n=function computeMD5(e,t){const a=Math.floor(Date.now()/1e3),n=t.filename||"",s=[a.toString(),n,e.toString(),...t.infoMap.values()],r=Math.sumPrecise(s.map(e=>e.length)),i=new Uint8Array(r);let o=0;for(const e of s)o=writeString(e,o,i);return bytesToString(calculateMD5(i,0,i.length))}(e,t);a.set("ID",[t.fileIds[0]||n,n])}}async function incrementalUpdate({originalData:e,xrefInfo:t,changes:a,xref:n=null,hasXfa:s=!1,xfaDatasetsRef:r=null,hasXfaDatasetsEntry:i=!1,needAppearances:o,acroFormRef:l=null,acroForm:f=null,xfaData:c=null,useXrefStream:h=!1}){await async function updateAcroform({xref:e,acroForm:t,acroFormRef:a,hasXfa:n,hasXfaDatasetsEntry:s,xfaDatasetsRef:r,needAppearances:i,changes:o}){!n||s||r||warn("XFA - Cannot save it");if(!i&&(!n||!r||s))return;const l=t.clone();if(n&&!s){const e=t.get("XFA").slice();e.splice(2,0,"datasets");e.splice(3,0,r);l.set("XFA",e)}i&&l.set("NeedAppearances",!0);o.put(a,{data:l})}({xref:n,acroForm:f,acroFormRef:l,hasXfa:s,hasXfaDatasetsEntry:i,xfaDatasetsRef:r,needAppearances:o,changes:a});s&&updateXFA({xfaData:c,xfaDatasetsRef:r,changes:a,xref:n});const u=function getTrailerDict(e,t,a){const n=new Dict(null);n.setIfDefined("Prev",e?.startXRef);const s=e.newRef;if(a){t.put(s,{data:""});n.set("Size",s.num+1);n.setIfName("Type","XRef")}else n.set("Size",s.num);n.setIfDefined("Root",e?.rootRef);n.setIfDefined("Info",e?.infoRef);n.setIfDefined("Encrypt",e?.encryptRef);return n}(t,a,h),m=[],p=await async function writeChanges(e,t,a=[]){const n=[];for(const[s,{data:r,objStreamRef:i,index:o}]of e.items())if(i)n.push({ref:s,data:r,objStreamRef:i,index:o});else if(null!==r&&"string"!=typeof r){await writeObject(s,r,a,t);n.push({ref:s,data:a.join("")});a.length=0}else n.push({ref:s,data:r});return n.sort((e,t)=>e.ref.num-t.ref.num)}(a,n,m);let d=e.length;const g=e.at(-1);if(10!==g&&13!==g){m.push("\n");d+=1}for(const{data:e}of p)null!==e&&m.push(e);await(h?async function getXRefStreamTable(e,t,a,n,s){const r=[];let i=0,o=0;for(const{ref:e,data:n,objStreamRef:s,index:l}of a){let a;i=Math.max(i,t);if(s){a=l;r.push([2,s.num,a])}else if(null!==n){a=Math.min(e.gen,65535);r.push([1,t,a]);t+=n.length}else{a=Math.min(e.gen+1,65535);r.push([0,0,a])}o=Math.max(o,a)}n.set("Index",getIndexes(a));const l=[1,getSizeInBytes(i),getSizeInBytes(o)];n.set("W",l);computeIDs(t,e,n);const f=Math.sumPrecise(l),c=new Uint8Array(f*r.length),h=new Stream(c);h.dict=n;let u=0;for(const[e,t,a]of r){u=writeInt(e,l[0],u,c);u=writeInt(t,l[1],u,c);u=writeInt(a,l[2],u,c)}await writeObject(e.newRef,h,s,{});s.push("startxref\n",t.toString(),"\n%%EOF\n")}(t,d,p,u,m):async function getXRefTable(e,t,a,n,s){s.push("xref\n");const r=getIndexes(a);let i=0;for(const{ref:e,data:n}of a){if(e.num===r[i]){s.push(`${r[i]} ${r[i+1]}\n`);i+=2}if(null!==n){s.push(`${t.toString().padStart(10,"0")} ${Math.min(e.gen,65535).toString().padStart(5,"0")} n\r\n`);t+=n.length}else s.push(`0000000000 ${Math.min(e.gen+1,65535).toString().padStart(5,"0")} f\r\n`)}computeIDs(t,e,n);s.push("trailer\n");await writeDict(n,s,null);s.push("\nstartxref\n",t.toString(),"\n%%EOF\n")}(t,d,p,u,m));const b=e.length+Math.sumPrecise(m.map(e=>e.length)),w=new Uint8Array(b);w.set(e);let j=e.length;for(const e of m)j=writeString(e,j,w);return w}class PageData{constructor(e,t){this.page=e;this.documentData=t;this.annotations=null;this.pointingNamedDestinations=null;t.pagesMap.put(e.ref,this)}}class DocumentData{constructor(e){this.document=e;this.destinations=null;this.pageLabels=null;this.pagesMap=new RefSetCache;this.oldRefMapping=new RefSetCache;this.dedupNamedDestinations=new Map;this.usedNamedDestinations=new Set;this.postponedRefCopies=new RefSetCache;this.usedStructParents=new Set;this.oldStructParentMapping=new Map;this.structTreeRoot=null;this.parentTree=null;this.idTree=null;this.roleMap=null;this.classMap=null;this.namespaces=null;this.structTreeAF=null;this.structTreePronunciationLexicon=[];this.acroForm=null;this.acroFormDefaultAppearance="";this.acroFormDefaultResources=null;this.acroFormQ=0;this.hasSignatureAnnotations=!1;this.fieldToParent=new RefSetCache;this.outline=null;this.embeddedFiles=null}}class XRefWrapper{constructor(e,t){this.entries=e;this._getNewRef=t}fetch(e){return e instanceof Ref?this.entries[e.num]:e}fetchIfRefAsync(e){return Promise.resolve(this.fetch(e))}fetchIfRef(e){return this.fetch(e)}fetchAsync(e){return Promise.resolve(this.fetch(e))}getNewTemporaryRef(){return this._getNewRef()}}class PDFEditor{hasSingleFile=!1;isSingleFile=!1;#Vt=null;currentDocument=null;oldPages=[];newPages=[];xref=[null];xrefWrapper=new XRefWrapper(this.xref,()=>this.newRef);newRefCount=1;namesDict=null;version="1.7";pageLabels=null;namedDestinations=new Map;parentTree=new Map;structTreeKids=[];idTree=new Map;classMap=new Dict;roleMap=new Dict;namespaces=new Map;structTreeAF=[];structTreePronunciationLexicon=[];fields=[];acroFormDefaultAppearance="";acroFormDefaultResources=null;acroFormNeedAppearances=!1;acroFormSigFlags=0;acroFormCalculationOrder=null;acroFormQ=0;outlineItems=null;embeddedFiles=new Map;constructor({useObjectStreams:e=!0,title:t="",author:a=""}={}){[this.rootRef,this.rootDict]=this.newDict;[this.infoRef,this.infoDict]=this.newDict;[this.pagesRef,this.pagesDict]=this.newDict;this.useObjectStreams=e;this.objStreamRefs=e?new Set:null;this.title=t;this.author=a}get newRef(){return Ref.get(this.newRefCount++,0)}get newDict(){const e=this.newRef;return[e,this.xref[e.num]=new Dict]}async#$t(e,t){const a=this.newRef;this.xref[a.num]=await this.#Yt(e,!0,t);return a}cloneDict(e){const t=e.clone();t.xref=this.xrefWrapper;return t}async#Yt(e,t,a){if(e instanceof Ref){const{currentDocument:{oldRefMapping:t}}=this;let n=t.get(e);if(n)return n;const s=e;if("number"==typeof(e=await a.fetchAsync(s)))return e;n=this.newRef;t.put(s,n);this.xref[n.num]=await this.#Yt(e,!0,a);return n}const n=[],{currentDocument:{postponedRefCopies:s}}=this;if(Array.isArray(e)){t&&(e=e.slice());for(let t=0,r=e.length;te[t]=a):n.push(this.#Yt(e[t],!0,a).then(a=>e[t]=a))}await Promise.all(n);return e}let r;if(e instanceof BaseStream){({dict:r}=e=e.getOriginalStream().clone());r.xref=this.xrefWrapper}else if(e instanceof Dict){t&&((e=e.clone()).xref=this.xrefWrapper);r=e}if(r){for(const[e,t]of r.getRawEntries()){const i=t instanceof Ref&&s.get(t);i?i.push(t=>r.set(e,t)):n.push(this.#Yt(t,!0,a).then(t=>r.set(e,t)))}await Promise.all(n)}return e}async#Jt(e,t,a,n,s,r,i,o=new RefSet){const{currentDocument:{pagesMap:l,oldRefMapping:f}}=this,c=t.getRaw("Pg");if(c instanceof Ref&&!l.has(c))return null;let h;const u=h=t.getRaw("K");if(u instanceof Ref){if(o.has(u))return null;h=await a.fetchAsync(u);Array.isArray(h)||(h=[u])}h=Array.isArray(h)?h:[h];const m=[],p=[];for(let t of h){const c=t instanceof Ref?t:null;if(c){if(o.has(c))continue;o.put(c);t=await a.fetchAsync(c)}if("number"==typeof t){m.push(t);continue}if(!(t instanceof Dict))continue;const h=t.getRaw("Pg");if(h instanceof Ref&&!l.has(h))continue;const u=t.get("Type");if(!u||isName(u,"StructElem")){let e=!1;if(c&&n.has(c)){if(!isName(t.get("S"),"Link"))continue;e=!0}const l=await this.#Jt(c,t,a,n,s,r,i,o);if(l){p.push(m.length);m.push(l);c&&f.put(c,l);e&&this.xref[l.num].setIfName("S","Span")}continue}if(isName(u,"OBJR")){if(!c)continue;const t=f.get(c);if(!t)continue;const a=this.xref[t.num].getRaw("Obj");if(a instanceof Ref){const t=this.xref[a.num];if(t instanceof Dict&&!t.has("StructParent")&&e){const a=this.parentTree.size;this.parentTree.set(a,[f,e]);t.set("StructParent",a)}}m.push(t);continue}if(isName(u,"MCR")){const e=await this.#Yt(c||t,!0,a);m.push(e);continue}if(c){const e=await this.#Yt(c,!0,a);m.push(e)}}if(0!==h.length&&0===m.length)return null;const d=this.newRef,g=this.xref[d.num]=this.cloneDict(t);g.delete("ID");g.delete("C");g.delete("K");g.delete("P");g.delete("S");await this.#Yt(g,!1,a);const b=t.get("C");if(b instanceof Name){const e=r.get(b.name);e?g.set("C",Name.get(e)):g.set("C",b)}else if(Array.isArray(b)){const e=[];for(const t of b)if(t instanceof Name){const a=r.get(t.name);a?e.push(Name.get(a)):e.push(t)}g.set("C",e)}const w=t.get("S");if(w instanceof Name){const e=i.get(w.name);e?g.set("S",Name.get(e)):g.set("S",w)}const j=t.get("ID");if("string"==typeof j){const e=stringToPDFString(j,!1),t=s.get(e);t?g.set("ID",stringToAsciiOrUTF16BE(t)):g.set("ID",j)}let k=g.get("A");if(k){Array.isArray(k)||(k=[k]);for(let e of k){e=this.xrefWrapper.fetch(e);if(isName(e.get("O"),"Table")&&e.has("Headers")){const t=this.xrefWrapper.fetch(e.getRaw("Headers"));if(Array.isArray(t))for(let e=0,a=t.length;e1&&g.set("K",m);return d}#Qt({document:e,includePages:t,excludePages:a}){if(!e)return[];let n,s,r,i;for(const e of t||[])Array.isArray(e)?(s||=[]).push(e):(n||=new Set).add(e);for(const e of a||[])Array.isArray(e)?(i||=[]).push(e):(r||=new Set).add(e);const o=[];for(let t=0,a=e.numPages;t=a&&t<=n){e=!0;break}if(e)continue}let e=!1;n&&(e=n.has(t));if(!e&&s)for(const[a,n]of s)if(t>=a&&t<=n){e=!0;break}e||n||s||(e=!0);e&&o.push(t)}return o}#Zt(e){const t=[],a=[];for(let n=0;ne.insertAfter-t.insertAfter);if(a.length>0)for(const t of e){if(!t.document||!t.pageIndices)continue;const e=this.#Qt(t).length;if(t.pageIndices.length0&&e.some(e=>e.document&&e.pageIndices);if(0===t.length&&n){const t=e.slice();let n=-1;for(const t of e)if(t.document&&t.pageIndices)for(const e of t.pageIndices)e>n&&(n=e);let s=0;for(const{i:e,insertAfter:r,count:i}of a){const a=Math.min(Math.max(r,-1)+s,n);for(let e=0;ee<=a)&&(t[e]={...n,pageIndices:n.pageIndices.map(e=>e>a?e+i:e)})}const o=[];for(let e=0;e{if(!e.document||e.pageIndices)return e;const a={...e,pageIndices:r[t]||[]};delete a.insertAfter;return a})}async extractPages(e,t,a,n){e.some(e=>void 0!==e.insertAfter)&&(e=this.#Zt(e));const s=[];let r=0;this.isSingleFile=1===e.length||e.every(t=>t.document===e[0].document);this.hasSingleFile=1===e.length;const i=[];t&&(this.#Vt={handler:a,task:n,newAnnotationsByPage:getNewAnnotationsMap(t),imagesPromises:AnnotationFactory.generateImages(t.values(),this.xrefWrapper,!0)});for(const{document:t,includePages:a,excludePages:n,pageIndices:o}of e){if(!t)continue;o&&(r=-1);const e=new DocumentData(t);i.push(e);s.push(this.#ea(e));let l=0;for(const i of this.#Qt({document:t,includePages:a,excludePages:n})){let a;o&&(a=o[l++]);if(void 0===a)if(-1!==r)a=r++;else for(a=0;void 0!==this.oldPages[a];a++);this.oldPages[a]=null;s.push(t.getPage(i).then(t=>{this.oldPages[a]=new PageData(t,e)}))}}await Promise.all(s);s.length=0;this.#ta(i);this.#aa(i);this.#na();for(const e of this.oldPages)s.push(this.#sa(e));await Promise.all(s);this.#ra();this.#ia(i);for(let e=0,t=this.oldPages.length;ee.destinations=t),t.ensureCatalog("rawPageLabels").then(t=>e.pageLabels=t),t.ensureCatalog("structTreeRoot").then(t=>e.structTreeRoot=t),t.ensureCatalog("acroForm").then(t=>e.acroForm=t),t.ensureCatalog("documentOutlineForEditor").then(t=>e.outline=t),t.ensureCatalog("rawEmbeddedFiles").then(t=>e.embeddedFiles=t)]);const n=e.structTreeRoot;if(n){const t=n.dict,s=t.get("ParentTree");if(s){const t=new NumberTree(s,a);e.parentTree=t.getAll(!0)}const r=t.get("IDTree");if(r){const t=new NameTree(r,a);e.idTree=t.getAll(!0)}e.roleMap=t.get("RoleMap")||null;e.classMap=t.get("ClassMap")||null;let i=t.get("Namespaces")||null;i&&!Array.isArray(i)&&(i=[i]);e.namespaces=i;e.structTreeAF=t.get("AF")||null;e.structTreePronunciationLexicon=t.get("PronunciationLexicon")||null}}async#sa(e){const{page:{xref:t,annotations:a},documentData:{pagesMap:n,destinations:s,usedNamedDestinations:r,fieldToParent:i}}=e;if(!a)return;const o=[];let l=[],f=0,{hasSignatureAnnotations:c}=e.documentData;for(const e of a){const a=f++;o.push(t.fetchIfRefAsync(e).then(async t=>{if(!isName(t.get("Subtype"),"Link")){if(isName(t.get("Subtype"),"Widget")){c||=isName(t.get("FT"),"Sig");const a=t.get("Parent")||null;t.delete("Parent");i.put(e,a)}l[a]=e;return}const o=t.get("A"),f=o instanceof Dict?o.get("D"):t.get("Dest");if(f&&(!Array.isArray(f)||f[0]instanceof Ref&&!n.has(f[0]))){if("string"==typeof f){const t=stringToPDFString(f,!0);if(s.has(t)){l[a]=e;r.add(t)}}}else l[a]=e}))}await Promise.all(o);l=l.filter(e=>!!e);e.annotations=l.length>0?l:null;e.documentData.hasSignatureAnnotations||=c}#ia(e){for(const{postponedRefCopies:t,pagesMap:a}of e)for(const e of a.keys())t.put(e,[])}#la(e){for(const{postponedRefCopies:t,oldRefMapping:a}of e){for(const[e,n]of t.items()){const t=a.get(e);for(const e of n)e(t)}t.clear()}}#ma(e,t,a=new RefSet){if(e instanceof Ref){if(!a.has(e)){a.put(e);this.#ma(this.xref[e.num],t,a)}return}if(Array.isArray(e)){for(const n of e)this.#ma(n,t,a);return}let n;e instanceof BaseStream?({dict:n}=e):e instanceof Dict&&(n=e);if(n){t(n);for(const e of n.getRawValues())this.#ma(e,t,a)}}async#fa(e){let t=0;const{parentTree:a}=this;for(let e=0,n=this.newPages.length;e{const l=e.get("StructParent")??e.get("StructParents");if("number"!=typeof l)return;i.add(l);let f=n.get(l);const c=f instanceof Ref?f:null;if(c){const e=o.fetch(c);Array.isArray(e)&&(f=e)}Array.isArray(f)&&f.every(e=>null===e)&&(f=null);if(!f){e.has("StructParent")?e.delete("StructParent"):e.delete("StructParents");return}let h=r.get(l);if(void 0===h){h=t++;r.set(l,h);a.set(h,[s,f])}e.has("StructParent")?e.set("StructParent",h):e.set("StructParents",h)},c)}const{structTreeKids:n,idTree:s,classMap:r,roleMap:i,namespaces:o,structTreeAF:l,structTreePronunciationLexicon:f}=this;for(const t of e){const{document:{xref:e},oldRefMapping:a,parentTree:c,usedStructParents:h,structTreeRoot:u,idTree:m,classMap:p,roleMap:d,namespaces:g,structTreeAF:b,structTreePronunciationLexicon:w}=t;if(!u)continue;this.currentDocument=t;const j=new RefSet;for(const[e,t]of c||[])!h.has(e)&&t instanceof Ref&&j.put(t);const k=new Map;for(const[e,t]of m||[]){let a=e;if(s.has(e))for(let t=1;;t++){const n=`${e}_${t}`;if(!s.has(n)){k.set(e,n);a=n;break}}s.set(a,t)}const y=new Map;if(p?.size>0)for(let[t,a]of p){a=await this.#Yt(a,!0,e);if(r.has(t))for(let e=1;;e++){const a=`${t}_${e}`;if(!r.has(a)){y.set(t,a);t=a;break}}r.set(t,a)}const q=new Map;if(d?.size>0)for(const[e,t]of d){const a=i.get(e);if(a){if(a!==t)for(let a=1;;a++){const n=`${e}_${a}`;if(!i.has(n)){q.set(e,n);i.set(n,t);break}}}else i.set(e,t)}if(g?.length>0)for(const t of g){const a=await e.fetchIfRefAsync(t);let n=a.get("NS");if(!n||o.has(n))continue;n=stringToPDFString(n,!1);const s=await this.#Yt(a,!0,e);o.set(n,s)}if(b)for(const t of b)l.push(await this.#Yt(t,!0,e));if(w)for(const t of w)f.push(await this.#Yt(t,!0,e));let v=u.dict.get("K");if(v){v=Array.isArray(v)?v:[v];for(let t of v){const a=t instanceof Ref?t:null;if(a&&j.has(a))continue;t=await e.fetchIfRefAsync(t);const s=await this.#Jt(a,t,e,j,k,y,q);s&&n.push(s)}for(const[e,t]of m||[]){const n=t instanceof Ref&&a.get(t),r=k.get(e)||e;n?s.set(r,n):s.delete(r)}}}for(const[e,[t,n]]of a){if(!n){a.delete(e);continue}if(!Array.isArray(n)){const s=t.get(n);void 0===s?a.delete(e):a.set(e,s);continue}const s=n.map(e=>e instanceof Ref&&t.get(e)||null);0===s.length||s.every(e=>null===e)?a.delete(e):a.set(e,s)}this.currentDocument=null}#ta(e){for(const t of e){if(!t.destinations)continue;const{destinations:e,pagesMap:a}=t,n=t.destinations=new Map;for(const[t,s]of Object.entries(e)){const e=s[0],r=e instanceof Ref&&a.get(e);if(r){(r.pointingNamedDestinations||=new Set).add(t);n.set(t,s)}}}}#ra(){const{namedDestinations:e}=this,getUniqueDestinationName=t=>{if(!e.has(t))return t;for(let a=1;;a++){const n=`${t}_${a}`;if(!e.has(n))return n}};for(let t=0,a=this.oldPages.length;t{"string"==typeof n&&e.set(a,t.get(stringToPDFString(n,!0))||n)};for(const t of e){const e=this.xref[t.num];if(!isName(e.get("Subtype"),"Link"))continue;const a=e.get("A");if(a instanceof Dict&&a.has("D")){const e=a.get("D");fixDestination(a,"D",e);continue}const n=e.get("Dest");fixDestination(e,"Dest",n)}}#aa(e){const collect=(e,t,a)=>{for(const n of e){"string"==typeof n.dest&&t?.has(n.dest)&&a.add(n.dest);n.items.length>0&&collect(n.items,t,a)}};for(const t of e){const{outline:e,destinations:a,usedNamedDestinations:n}=t;e?.length&&collect(e,a,n)}}#da(e,t){const{dest:a,action:n,url:s,unsafeUrl:r,attachment:i,setOCGState:o}=e;if(n||s||r||i||o)return!0;if(!a)return!1;if("string"==typeof a){const e=t.dedupNamedDestinations.get(a)||a;return this.namedDestinations.has(e)}return!!(Array.isArray(a)&&a[0]instanceof Ref)&&!!t.oldRefMapping.get(a[0])}#ga(e,t){const a=[];for(const n of e){const e=this.#ga(n.items,t),s=this.#da(n,t);(s||e.length>0)&&a.push({...n,dest:s?n.dest:null,rawDict:s?n.rawDict:null,items:e,_documentData:t})}return a}#ha(e){const t=[];for(const a of e){const{outline:e}=a;e?.length&&t.push(...this.#ga(e,a))}this.outlineItems=t.length>0?t:null}async#ba(e,t){const{dest:a,rawDict:n}=t,s=t._documentData;if(a){if("string"==typeof a){const t=s.dedupNamedDestinations.get(a)||a;e.set("Dest",stringToAsciiOrUTF16BE(t))}else if(Array.isArray(a)){const t=a.slice();t[0]instanceof Ref&&(t[0]=s.oldRefMapping.get(t[0])||t[0]);e.set("Dest",t)}return}const r=n?.get("A");if(r instanceof Dict){this.currentDocument=s;const t=await this.#$t(r,s.document.xref);this.currentDocument=null;e.set("A",t)}}async#wa(){const{outlineItems:e}=this;if(!e?.length)return;const[t,a]=this.newDict;a.setIfName("Type","Outlines");const assignRefs=e=>{for(const t of e){[t._ref]=this.newDict;t.items.length>0&&assignRefs(t.items)}};assignRefs(e);const fillItems=async(e,t)=>{let a=0;for(let n=0;n0&&r.set("Prev",e[n-1]._ref);n0){r.set("First",s.items[0]._ref);r.set("Last",s.items.at(-1)._ref);const e=await fillItems(s.items,s._ref);void 0!==s.count&&r.set("Count",s.count<0?-e:e);a+=void 0!==s.count&&s.count<0?1:e+1}else a+=1;await this.#ba(r,s);const i=(s.bold?2:0)|(s.italic?1:0);0!==i&&r.set("F",i);!s.color||0===s.color[0]&&0===s.color[1]&&0===s.color[2]||r.set("C",[s.color[0]/255,s.color[1]/255,s.color[2]/255])}return a},n=await fillItems(e,t);a.set("First",e[0]._ref);a.set("Last",e.at(-1)._ref);a.set("Count",n);this.rootDict.set("Outlines",t)}async#ca(e){this.#ja(e);this.#ka(e);this.#ya(e);await this.#qa(e);const t=this.fields;for(const a of e){let e=a.acroForm?.get("Fields")||null;!e&&a.fieldToParent.size>0&&(e=this.#va(a.fieldToParent,a.document.xref));if(Array.isArray(e)&&e.length>0){this.currentDocument=a;await this.#Sa(t,e);this.currentDocument=null}}this.#Aa(e)}#ya(e){let t=0,a=null;for(const n of e){const e=n.acroForm?.get("Q");if("number"==typeof e&&0!==e)if(a?.acroFormQ>0)n.acroFormQ=e;else if(0!==t){if(e!==t){a.acroFormQ||=t;n.acroFormQ=e;t=0}}else{t=e;a=n}}t>0&&(this.acroFormQ=t)}#ja(e){let t=0,a=!1;for(const n of e){if(!n.acroForm)continue;const e=n.acroForm.get("SigFlags");"number"==typeof e&&n.hasSignatureAnnotations&&(t|=e);!0===n.acroForm.get("NeedAppearances")&&(a=!0)}this.acroFormSigFlags=t;this.acroFormNeedAppearances=a}#Aa(e){const t=[];for(const a of e){const e=a.acroForm?.get("CO")||null;if(!Array.isArray(e))continue;const{oldRefMapping:n}=a;for(const a of e){const e=a instanceof Ref&&n.get(a);e&&t.push(e)}}this.acroFormCalculationOrder=t.length>0?t:null}#ka(e){let t=null,a=null;for(const n of e){const e=n.acroForm?.get("DA")||null;if(e&&"string"==typeof e)if(a?.acroFormDefaultAppearance)n.acroFormDefaultAppearance=e;else if(t){if(e!==t){a.acroFormDefaultAppearance||=t;n.acroFormDefaultAppearance=e;t=null}}else{t=e;a=n}}t&&(this.acroFormDefaultAppearance=t)}async#qa(e){let t=null,a=null,n=null;for(const s of e){const e=s.acroForm?.get("DR")||null;if(e&&e instanceof Dict)if(n?.acroFormDefaultResources)s.acroFormDefaultResources=e;else if(t){if(!deepCompare(t,e)){n.acroFormDefaultResources||=t;s.acroFormDefaultResources=e;t=null;a=null}}else{t=e;a=s.acroForm.getRaw("DR");n=s}}if(t){this.currentDocument=n;this.acroFormDefaultResources=await this.#Yt(a,!0,n.document.xref);this.currentDocument=null}}#va(e,t){const a=[],n=new RefSet;for(const[s,r]of e){if(!r){a.push(s);continue}let e=r,i=r;for(;;){e=t.fetchIfRef(e)?.getRaw("Parent")||null;if(!e)break;i=e}if(i instanceof Ref&&!n.has(i)){a.push(i);n.put(i)}}return a}async#Sa(e,t){const a=new RefSet,n=[{kids:t,newKids:e,pos:0,oldParentRef:null,parentRef:null,parent:null}],{document:{xref:s},oldRefMapping:r,fieldToParent:i,acroFormDefaultAppearance:o,acroFormDefaultResources:l,acroFormQ:f}=this.currentDocument,c=[],h=[];for(;n.length>0;){const e=n.at(-1),{kids:t,newKids:u,parent:m,pos:p}=e;if(p===t.length){n.pop();if(0===u.length||!m)continue;const t=this.xref[e.parentRef.num]=this.cloneDict(m);t.delete("Parent");t.delete("Kids");await this.#Yt(t,!1,s);t.set("Kids",u);if(n.length>0){const a=n.at(-1);if(!a.parentRef&&a.oldParentRef){const e=a.parentRef=this.newRef;t.set("Parent",e);r.put(a.oldParentRef,e)}a.newKids.push(e.parentRef)}continue}const d=t[e.pos++];if(!(d instanceof Ref)||a.has(d))continue;a.put(d);const g=s.fetchIfRef(d);if(g.has("Kids")){const e=g.get("Kids");if(!Array.isArray(e))continue;n.push({kids:e,newKids:[],pos:0,oldParentRef:d,parentRef:null,parent:g});continue}if(!i.has(d))continue;const b=r.get(d);if(!b)continue;u.push(b);if(!e.parentRef&&e.oldParentRef){e.parentRef=this.newRef;r.put(e.oldParentRef,e.parentRef)}const w=this.xref[b.num];e.parentRef&&w.set("Parent",e.parentRef);o&&isName(w.get("FT"),"Tx")&&!w.has("DA")&&c.push(w);l&&!w.has("Kids")&&w.get("AP")instanceof Dict&&h.push(w);f&&!w.has("Q")&&w.set("Q",f)}for(const e of c){getInheritableProperty({dict:e,key:"DA"})||e.set("DA",o)}const u=new Map;for(const e of h){const t=e.get("AP");for(const[,e]of t){if(!(e instanceof BaseStream))continue;let t=e.dict.getRaw("Resources");if(!t){const t=await u.getOrInsertComputed(l,()=>this.#$t(l,s));e.dict.set("Resources",t);continue}t=s.fetchIfRef(t);for(const[e,a]of l.getRawEntries())if(!t.has(e)){let n=a;a instanceof Ref?n=await this.#Yt(a,!0,s):(a instanceof Dict||a instanceof BaseStream||Array.isArray(a))&&(n=await u.getOrInsertComputed(a,()=>this.#$t(a,s)));t.set(e,n)}}}}async#na(){if(!this.hasSingleFile)return;const{documentData:{document:e,pageLabels:t}}=this.oldPages[0];if(!t)return;const a=e.numPages,n=[],s=new Set(this.oldPages.map(({page:{pageIndex:e}})=>e));let r=null,i=-1;for(let e=0;ee!==f[t])&&m.set(e,a)}const d=t.userUnit;1!==d&&m.set("UserUnit",d);m.setIfDict("Resources",await this.#Yt(c,!0,o));let g=null;if(n){const e=await this.#Yt(n,!0,o);this.#pa(e,r);Array.isArray(e)&&e.length>0&&(g=e)}const b=this.#Vt?.newAnnotationsByPage?.get(e);if(b){const{handler:e,task:a,imagesPromises:n}=this.#Vt,s=new RefSetCache,r=await AnnotationFactory.saveNewAnnotations(t.createAnnotationEvaluator(e),this.xrefWrapper,a,b,n,s);for(const[e,{data:t}]of s.items())this.xref[e.num]=t;g||=[];for(const{ref:e}of r.annotations)g.push(e)}m.setIfArray("Annots",g);if(this.useObjectStreams){const e=this.newRefCount,t=[];for(let a=p;a0;){const{dict:e,kids:t,parentRef:a}=s.pop();if(t.length<=16){e.set("Kids",t);for(const e of t)this.xref[e.num].set("Parent",a);continue}const n=Math.max(16,Math.ceil(t.length/16)),r=[];for(let e=0;ee.localeCompare(t):([e],[t])=>e-t),[n,s]=this.newDict,r=[{dict:s,entries:a,isRoot:!0}],i=t?"Names":"Nums";for(;r.length>0;){const{dict:e,entries:t,isRoot:a}=r.pop();if(t.length<=64){a||e.set("Limits",[t[0][0],t.at(-1)[0]]);e.set(i,t.flat());continue}const n=[],s=Math.max(64,Math.ceil(t.length/64));for(let e=0;e0){const e=this.#Ca(Array.from(this.parentTree.entries()),!1);this.xref[e.num].setIfName("Type","ParentTree");n.set("ParentTree",e);n.set("ParentTreeNextKey",this.parentTree.size)}if(this.idTree.size>0){const e=this.#Ca(Array.from(this.idTree.entries()),!0);this.xref[e.num].setIfName("Type","IDTree");n.set("IDTree",e)}if(this.classMap.size>0){const e=this.newRef;this.xref[e.num]=this.classMap;n.set("ClassMap",e)}if(this.roleMap.size>0){const e=this.newRef;this.xref[e.num]=this.roleMap;n.set("RoleMap",e)}if(this.namespaces.size>0){const e=this.newRef;this.xref[e.num]=Array.from(this.namespaces.values());n.set("Namespaces",e)}if(this.structTreeAF.length>0){const e=this.newRef;this.xref[e.num]=this.structTreeAF;n.set("AF",e)}if(this.structTreePronunciationLexicon.length>0){const e=this.newRef;this.xref[e.num]=this.structTreePronunciationLexicon;n.set("PronunciationLexicon",e)}t.set("StructTreeRoot",a)}#Ra(){if(0===this.fields.length)return;const{rootDict:e}=this,t=this.newRef,a=this.xref[t.num]=new Dict;e.set("AcroForm",t);a.set("Fields",this.fields);this.acroFormNeedAppearances&&a.set("NeedAppearances",!0);this.acroFormSigFlags>0&&a.set("SigFlags",this.acroFormSigFlags);a.setIfArray("CO",this.acroFormCalculationOrder);a.setIfDict("DR",this.acroFormDefaultResources);this.acroFormDefaultAppearance&&a.set("DA",this.acroFormDefaultAppearance);this.acroFormQ>0&&a.set("Q",this.acroFormQ)}async#Ha(){const{rootDict:e}=this;e.setIfName("Type","Catalog");e.setIfName("Version",this.version);this.#Ra();this.#xa();this.#Ia();this.#Fa();this.#Ta();this.#Oa();await this.#wa()}#Ba(){const e=new Map;if(this.isSingleFile){const{xref:{trailer:t}}=this.oldPages[0].documentData.document,a=t.get("Info");for(const[t,n]of a||[])"string"==typeof n&&e.set(t,stringToPDFString(n))}e.delete("ModDate");e.set("CreationDate",getModificationDate());e.set("Creator","PDF.js");e.set("Producer","Firefox");this.author&&e.set("Author",this.author);this.title&&e.set("Title",this.title);for(const[t,a]of e)this.infoDict.set(t,stringToAsciiOrUTF16BE(a));return e}async#Da(){if(!this.isSingleFile)return[null,null,null];const{documentData:e}=this.oldPages[0],{document:{xref:{trailer:t,encrypt:a}}}=e;if(!t.has("Encrypt"))return[null,null,null];const n=t.get("Encrypt");if(!(n instanceof Dict))return[null,null,null];this.currentDocument=e;const s=[await this.#$t(n,t.xref),a,t.get("ID")];this.currentDocument=null;return s}async#Ma(){const e=new RefSetCache;e.put(Ref.get(0,65535),{data:null});for(let t=1,a=this.xref.length;t{this._contentLength=e.contentLength;this._isStreamingSupported=e.isStreamingSupported;this._isRangeSupported=e.isRangeSupported;this._headersCapability.resolve()},this._headersCapability.reject)}async read(){const{value:e,done:t}=await this._reader.read();return t?{value:void 0,done:!0}:{value:e.buffer,done:!1}}cancel(e){this._reader.cancel(e)}}class PDFWorkerStreamRangeReader extends BasePDFStreamRangeReader{_reader=null;constructor(e,t,a){super(e,t,a);const{msgHandler:n}=e._source,s=n.sendWithStream("GetRangeReader",{begin:t,end:a});this._reader=s.getReader()}async read(){const{value:e,done:t}=await this._reader.read();return t?{value:void 0,done:!0}:{value:e.buffer,done:!1}}cancel(e){this._reader.cancel(e)}}class WorkerTask{constructor(e){this.name=e;this.terminated=!1;this._capability=Promise.withResolvers()}get finished(){return this._capability.promise}finish(){this._capability.resolve()}terminate(){this.terminated=!0}ensureNotTerminated(){if(this.terminated)throw new Error("Worker task was terminated")}}class WorkerMessageHandler{static{"undefined"==typeof window&&!e&&"undefined"!=typeof self&&"function"==typeof self.postMessage&&"onmessage"in self&&this.initializeFromPort(self)}static setup(e,t){let a=!1;e.on("test",t=>{if(!a){a=!0;e.send("test",t instanceof Uint8Array)}});e.on("configure",e=>{!function setVerbosityLevel(e){Number.isInteger(e)&&(ta=e)}(e.verbosity)});e.on("GetDocRequest",e=>this.createDocumentHandler(e,t))}static createDocumentHandler(e,t){let a,n=!1,s=null;const r=new Set,i=getVerbosityLevel(),{docId:o,apiVersion:l}=e,f="5.7.284";if(l!==f)throw new Error(`The API version "${l}" does not match the Worker version "${f}".`);const buildMsg=(e,t)=>`The \`${e}.prototype\` contains unexpected enumerable property "${t}", thus breaking e.g. \`for...in\` iteration of ${e}s.`;for(const e in{})throw new Error(buildMsg("Object",e));for(const e in[])throw new Error(buildMsg("Array",e));const c=o+"_worker";let h=new MessageHandler(c,o,t);function ensureNotTerminated(){if(n)throw new Error("Worker was terminated")}function startWorkerTask(e){r.add(e)}function finishWorkerTask(e){e.finish();r.delete(e)}async function loadDocument(e){await a.ensureDoc("checkHeader");await a.ensureDoc("parseStartXRef");await a.ensureDoc("parse",[e]);await a.ensureDoc("checkFirstPage",[e]);await a.ensureDoc("checkLastPage",[e]);const t=await a.ensureDoc("isPureXfa");if(t){const e=new WorkerTask("loadXfaResources");startWorkerTask(e);await a.ensureDoc("loadXfaResources",[h,e]);finishWorkerTask(e)}const[n,s]=await Promise.all([a.ensureDoc("numPages"),a.ensureDoc("fingerprints")]);return{numPages:n,fingerprints:s,htmlForXfa:t?await a.ensureDoc("htmlForXfa"):null}}function setupDoc(e){function onSuccess(e){ensureNotTerminated();h.send("GetDoc",{pdfInfo:e})}function onFailure(e){if(!n)if(e instanceof PasswordException){const t=new WorkerTask(`PasswordException: response ${e.code}`);startWorkerTask(t);h.sendWithPromise("PasswordRequest",e).then(function({password:e}){finishWorkerTask(t);a.updatePassword(e);pdfManagerReady()}).catch(function(){finishWorkerTask(t);h.send("DocException",e)})}else h.send("DocException",wrapReason(e))}function pdfManagerReady(){ensureNotTerminated();loadDocument(!1).then(onSuccess,function(e){ensureNotTerminated();e instanceof XRefParseException?a.requestLoadedStream().then(function(){ensureNotTerminated();loadDocument(!0).then(onSuccess,onFailure)}):onFailure(e)})}ensureNotTerminated();(async function getPdfManager({data:e,password:t,disableAutoFetch:a,rangeChunkSize:n,docBaseUrl:r,enableXfa:i,evaluatorOptions:l}){const f={source:null,disableAutoFetch:a,docBaseUrl:r,docId:o,enableXfa:i,evaluatorOptions:l,handler:h,length:0,password:t,rangeChunkSize:n};if(e){f.source=e;return new LocalPdfManager(f)}const c=new PDFWorkerStream({msgHandler:h}),u=c.getFullReader(),{promise:m,resolve:p,reject:d}=Promise.withResolvers();let g,b=[];s=e=>c.cancelAllRequests(e);u.headersReady.then(()=>{if(u.isRangeSupported){f.source=c;f.length=u.contentLength;f.disableAutoFetch||=u.isStreamingSupported;g=new NetworkPdfManager(f);for(const e of b)g.sendProgressiveData(e);b=null;p(g);s=null}}).catch(e=>{d(e);s=null});(async function readData(){let e=0;for(;;){const{value:t,done:a}=await u.read();ensureNotTerminated();if(a)break;e+=t.byteLength;u.isStreamingSupported||h.send("DocProgress",{loaded:e,total:u.contentLength});g?g.sendProgressiveData(t):b.push(t)}if(!g){f.source=arrayBuffersToBytes(b);b=null;g=new LocalPdfManager(f);p(g)}s=null})().catch(e=>{d(e);s=null});return m})(e).then(function(e){if(n){e.terminate(new AbortException("Worker was terminated."));throw new Error("Worker was terminated")}a=e;a.requestLoadedStream(!0).then(e=>{h.send("DataLoaded",{length:e.bytes.byteLength})})}).then(pdfManagerReady,onFailure)}h.on("GetPage",function(e){return a.getPage(e.pageIndex).then(function(e){return Promise.all([a.ensure(e,"rotate"),a.ensure(e,"ref"),a.ensure(e,"userUnit"),a.ensure(e,"view")]).then(function([e,t,a,n]){return{rotate:e,ref:t,refStr:t?.toString()??null,userUnit:a,view:n}})})});h.on("GetPageIndex",function(e){const t=Ref.get(e.num,e.gen);return a.ensureCatalog("getPageIndex",[t])});h.on("GetDestinations",function(e){return a.ensureCatalog("destinations")});h.on("GetDestination",function(e){return a.ensureCatalog("getDestination",[e.id])});h.on("GetPageLabels",function(e){return a.ensureCatalog("pageLabels")});h.on("GetPageLayout",function(e){return a.ensureCatalog("pageLayout")});h.on("GetPageMode",function(e){return a.ensureCatalog("pageMode")});h.on("GetViewerPreferences",function(e){return a.ensureCatalog("viewerPreferences")});h.on("GetOpenAction",function(e){return a.ensureCatalog("openAction")});h.on("GetAttachments",function(e){return a.ensureCatalog("attachments")});h.on("GetDocJSActions",function(e){return a.ensureCatalog("jsActions")});h.on("GetPageJSActions",function({pageIndex:e}){return a.getPage(e).then(e=>a.ensure(e,"jsActions"))});h.on("GetAnnotationsByType",async function({types:e,pageIndexesToSkip:t}){const[n,s]=await Promise.all([a.ensureDoc("numPages"),a.ensureDoc("annotationGlobals")]);if(!s)return null;const r=[],i=[];let o=null;try{for(let l=0,f=n;lt&&t.collectAnnotationsByType(h,o,e,i,s)||[]))}await Promise.all(r);return(await Promise.all(i)).filter(e=>!!e)}finally{o&&finishWorkerTask(o)}});h.on("GetOutline",function(e){return a.ensureCatalog("documentOutline")});h.on("GetOptionalContentConfig",function(e){return a.ensureCatalog("optionalContentConfig")});h.on("GetPermissions",function(e){return a.ensureCatalog("permissions")});h.on("GetMetadata",function(e){return Promise.all([a.ensureDoc("documentInfo"),a.ensureCatalog("metadata"),a.ensureCatalog("hasStructTree")])});h.on("GetMarkInfo",function(e){return a.ensureCatalog("markInfo")});h.on("GetData",function(e){return a.requestLoadedStream().then(e=>e.bytes)});h.on("GetAnnotations",function({pageIndex:e,intent:t}){return a.getPage(e).then(function(a){const n=new WorkerTask(`GetAnnotations: page ${e}`);startWorkerTask(n);return a.getAnnotationsData(h,n,t).then(e=>{finishWorkerTask(n);return e},e=>{finishWorkerTask(n);throw e})})});h.on("GetFieldObjects",function(e){return a.ensureDoc("fieldObjects").then(e=>e?.allFields||null)});h.on("HasJSActions",function(e){return a.ensureDoc("hasJSActions")});h.on("GetCalculationOrderIds",function(e){return a.ensureDoc("calculationOrderIds")});h.on("ExtractPages",async function({pageInfos:e,annotationStorage:t}){if(!e){warn("extractPages: nothing to extract.");return null}Array.isArray(e)||(e=[e]);let n,s=0;for(const t of e)if(null===t.document)t.document=a.pdfDocument;else if(ArrayBuffer.isView(t.document)){const e=new LocalPdfManager({source:t.document,docId:`${o}_extractPages_${s++}`,handler:h,password:t.password??null,evaluatorOptions:Object.assign({},a.evaluatorOptions)});let n=!1,r=!0;for(;;)try{await e.requestLoadedStream();await e.ensureDoc("checkHeader");await e.ensureDoc("parseStartXRef");await e.ensureDoc("parse",[n]);break}catch(t){if(t instanceof XRefParseException){if(!1===n){n=!0;continue}r=!1;warn("extractPages: XRefParseException.")}else if(t instanceof PasswordException){const a=new WorkerTask(`PasswordException: response ${t.code}`);startWorkerTask(a);try{const{password:a}=await h.sendWithPromise("PasswordRequest",t);e.updatePassword(a)}catch{r=!1;warn("extractPages: invalid password.")}finally{finishWorkerTask(a)}}else{r=!1;warn("extractPages: invalid document.")}if(!r)break}r||(t.document=null);if(await e.ensureDoc("isPureXfa")){t.document=null;warn("extractPages does not support pure XFA documents.")}else t.document=e.pdfDocument}else warn("extractPages: invalid document.");try{const a=new PDFEditor;n=new WorkerTask(`ExtractPages: ${e.length} page(s)`);startWorkerTask(n);return await a.extractPages(e,t,h,n)}catch(e){warn(`extractPages: "${e}".`);return null}finally{n&&finishWorkerTask(n)}});h.on("SaveDocument",async function({isPureXfa:e,numPages:t,annotationStorage:n,filename:s}){const r=[a.requestLoadedStream(),a.ensureCatalog("acroForm"),a.ensureCatalog("acroFormRef"),a.ensureDoc("startXRef"),a.ensureDoc("xref"),a.ensureCatalog("structTreeRoot")],i=new RefSetCache,o=[],l=e?null:getNewAnnotationsMap(n),[f,c,u,m,p,d]=await Promise.all(r),g=p.trailer.getRaw("Root")||null;let b;if(l){d?await d.canUpdateStructTree({pdfManager:a,newAnnotationsByPage:l})&&(b=d):await StructTreeRoot.canCreateStructureTree({catalogRef:g,pdfManager:a,newAnnotationsByPage:l})&&(b=null);const e=AnnotationFactory.generateImages(n.values(),p,a.evaluatorOptions.isOffscreenCanvasSupported),t=void 0===b?o:[];for(const[n,s]of l)t.push(a.getPage(n).then(t=>{const a=new WorkerTask(`Save (editor): page ${n}`);startWorkerTask(a);return t.saveNewAnnotations(h,a,s,e,i).finally(function(){finishWorkerTask(a)})}));null===b?o.push(Promise.all(t).then(async()=>{await StructTreeRoot.createStructureTree({newAnnotationsByPage:l,xref:p,catalogRef:g,pdfManager:a,changes:i})})):b&&o.push(Promise.all(t).then(async()=>{await b.updateStructureTree({newAnnotationsByPage:l,pdfManager:a,changes:i})}))}if(e)o.push(a.ensureDoc("serializeXfaData",[n]));else for(let e=0;ee.needAppearances),y=c instanceof Dict&&c.get("XFA")||null;let q=null,v=!1;if(Array.isArray(y)){for(let e=0,t=y.length;e{p.resetNewTemporaryRef()})});h.on("GetOperatorList",function(e,t){const{pageId:n,pageIndex:s}=e;a.getPage(n).then(function(a){const n=new WorkerTask(`GetOperatorList: page ${s}`);startWorkerTask(n);const r=i>=ce?Date.now():0;a.getOperatorList({handler:h,sink:t,task:n,intent:e.intent,cacheKey:e.cacheKey,annotationStorage:e.annotationStorage,modifiedIds:e.modifiedIds,pageIndex:s}).then(function(e){finishWorkerTask(n);r&&info(`page=${s+1} - getOperatorList: time=${Date.now()-r}ms, len=${e.length}`);t.close()},function(e){finishWorkerTask(n);n.terminated||t.error(e)})})});h.on("GetTextContent",function(e,t){const{pageId:n,pageIndex:s,includeMarkedContent:r,disableNormalization:o}=e;a.getPage(n).then(function(e){const a=new WorkerTask("GetTextContent: page "+s);startWorkerTask(a);const n=i>=ce?Date.now():0;e.extractTextContent({handler:h,task:a,sink:t,includeMarkedContent:r,disableNormalization:o}).then(function(){finishWorkerTask(a);n&&info(`page=${s+1} - getTextContent: time=`+(Date.now()-n)+"ms");t.close()},function(e){finishWorkerTask(a);a.terminated||t.error(e)})})});h.on("GetStructTree",function(e){return a.getPage(e.pageIndex).then(e=>a.ensure(e,"getStructTree"))});h.on("FontFallback",function(e){return a.fontFallback(e.id,h)});h.on("Cleanup",function(e){return a.cleanup(!0)});h.on("Terminate",async function(e){n=!0;const t=[];if(a){a.terminate(new AbortException("Worker was terminated."));const e=a.cleanup();t.push(e);a=null}else clearGlobalCaches();s?.(new AbortException("Worker was terminated."));for(const e of r){t.push(e.finished);e.terminate()}await Promise.all(t);h.destroy();h=null});h.on("Ready",function(t){setupDoc(e);e=null});return c}static initializeFromPort(e){const t=new MessageHandler("worker","main",e);this.setup(t,e);t.send("ready",null)}}globalThis.pdfjsWorker={WorkerMessageHandler};export{WorkerMessageHandler}; \ No newline at end of file diff --git a/web/public/vendor/pdfjs/standard_fonts/FoxitDingbats.pfb b/web/public/vendor/pdfjs/standard_fonts/FoxitDingbats.pfb new file mode 100644 index 00000000..30d52963 Binary files /dev/null and b/web/public/vendor/pdfjs/standard_fonts/FoxitDingbats.pfb differ diff --git a/web/public/vendor/pdfjs/standard_fonts/FoxitFixed.pfb b/web/public/vendor/pdfjs/standard_fonts/FoxitFixed.pfb new file mode 100644 index 00000000..f12dcbce Binary files /dev/null and b/web/public/vendor/pdfjs/standard_fonts/FoxitFixed.pfb differ diff --git a/web/public/vendor/pdfjs/standard_fonts/FoxitFixedBold.pfb b/web/public/vendor/pdfjs/standard_fonts/FoxitFixedBold.pfb new file mode 100644 index 00000000..cf8e24ae Binary files /dev/null and b/web/public/vendor/pdfjs/standard_fonts/FoxitFixedBold.pfb differ diff --git a/web/public/vendor/pdfjs/standard_fonts/FoxitFixedBoldItalic.pfb b/web/public/vendor/pdfjs/standard_fonts/FoxitFixedBoldItalic.pfb new file mode 100644 index 00000000..d2880017 Binary files /dev/null and b/web/public/vendor/pdfjs/standard_fonts/FoxitFixedBoldItalic.pfb differ diff --git a/web/public/vendor/pdfjs/standard_fonts/FoxitFixedItalic.pfb b/web/public/vendor/pdfjs/standard_fonts/FoxitFixedItalic.pfb new file mode 100644 index 00000000..d71697d4 Binary files /dev/null and b/web/public/vendor/pdfjs/standard_fonts/FoxitFixedItalic.pfb differ diff --git a/web/public/vendor/pdfjs/standard_fonts/FoxitSerif.pfb b/web/public/vendor/pdfjs/standard_fonts/FoxitSerif.pfb new file mode 100644 index 00000000..3fa682ef Binary files /dev/null and b/web/public/vendor/pdfjs/standard_fonts/FoxitSerif.pfb differ diff --git a/web/public/vendor/pdfjs/standard_fonts/FoxitSerifBold.pfb b/web/public/vendor/pdfjs/standard_fonts/FoxitSerifBold.pfb new file mode 100644 index 00000000..ff7c6dde Binary files /dev/null and b/web/public/vendor/pdfjs/standard_fonts/FoxitSerifBold.pfb differ diff --git a/web/public/vendor/pdfjs/standard_fonts/FoxitSerifBoldItalic.pfb b/web/public/vendor/pdfjs/standard_fonts/FoxitSerifBoldItalic.pfb new file mode 100644 index 00000000..460231fb Binary files /dev/null and b/web/public/vendor/pdfjs/standard_fonts/FoxitSerifBoldItalic.pfb differ diff --git a/web/public/vendor/pdfjs/standard_fonts/FoxitSerifItalic.pfb b/web/public/vendor/pdfjs/standard_fonts/FoxitSerifItalic.pfb new file mode 100644 index 00000000..d03a7c78 Binary files /dev/null and b/web/public/vendor/pdfjs/standard_fonts/FoxitSerifItalic.pfb differ diff --git a/web/public/vendor/pdfjs/standard_fonts/FoxitSymbol.pfb b/web/public/vendor/pdfjs/standard_fonts/FoxitSymbol.pfb new file mode 100644 index 00000000..c8f9bca7 Binary files /dev/null and b/web/public/vendor/pdfjs/standard_fonts/FoxitSymbol.pfb differ diff --git a/web/public/vendor/pdfjs/standard_fonts/LICENSE_FOXIT b/web/public/vendor/pdfjs/standard_fonts/LICENSE_FOXIT new file mode 100644 index 00000000..8b4ed6dd --- /dev/null +++ b/web/public/vendor/pdfjs/standard_fonts/LICENSE_FOXIT @@ -0,0 +1,27 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/web/public/vendor/pdfjs/standard_fonts/LICENSE_LIBERATION b/web/public/vendor/pdfjs/standard_fonts/LICENSE_LIBERATION new file mode 100644 index 00000000..aba73e8a --- /dev/null +++ b/web/public/vendor/pdfjs/standard_fonts/LICENSE_LIBERATION @@ -0,0 +1,102 @@ +Digitized data copyright (c) 2010 Google Corporation + with Reserved Font Arimo, Tinos and Cousine. +Copyright (c) 2012 Red Hat, Inc. + with Reserved Font Name Liberation. + +This Font Software is licensed under the SIL Open Font License, +Version 1.1. + +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 + +PREAMBLE The goals of the Open Font License (OFL) are to stimulate +worldwide development of collaborative font projects, to support the font +creation efforts of academic and linguistic communities, and to provide +a free and open framework in which fonts may be shared and improved in +partnership with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. +The fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply to +any document created using the fonts or their derivatives. + + + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. +This may include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components +as distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting ? in part or in whole ? +any of the components of the Original Version, by changing formats or +by porting the Font Software to a new environment. + +"Author" refers to any designer, engineer, programmer, technical writer +or other person who contributed to the Font Software. + + +PERMISSION & CONDITIONS + +Permission is hereby granted, free of charge, to any person obtaining a +copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components,in + Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, + redistributed and/or sold with any software, provided that each copy + contains the above copyright notice and this license. These can be + included either as stand-alone text files, human-readable headers or + in the appropriate machine-readable metadata fields within text or + binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font + Name(s) unless explicit written permission is granted by the + corresponding Copyright Holder. This restriction only applies to the + primary font name as presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font + Software shall not be used to promote, endorse or advertise any + Modified Version, except to acknowledge the contribution(s) of the + Copyright Holder(s) and the Author(s) or with their explicit written + permission. + +5) The Font Software, modified or unmodified, in part or in whole, must + be distributed entirely under this license, and must not be distributed + under any other license. The requirement for fonts to remain under + this license does not apply to any document created using the Font + Software. + + + +TERMINATION +This license becomes null and void if any of the above conditions are not met. + + + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER +DEALINGS IN THE FONT SOFTWARE. + diff --git a/web/public/vendor/pdfjs/standard_fonts/LiberationSans-Bold.ttf b/web/public/vendor/pdfjs/standard_fonts/LiberationSans-Bold.ttf new file mode 100644 index 00000000..ee237154 Binary files /dev/null and b/web/public/vendor/pdfjs/standard_fonts/LiberationSans-Bold.ttf differ diff --git a/web/public/vendor/pdfjs/standard_fonts/LiberationSans-BoldItalic.ttf b/web/public/vendor/pdfjs/standard_fonts/LiberationSans-BoldItalic.ttf new file mode 100644 index 00000000..42b5717d Binary files /dev/null and b/web/public/vendor/pdfjs/standard_fonts/LiberationSans-BoldItalic.ttf differ diff --git a/web/public/vendor/pdfjs/standard_fonts/LiberationSans-Italic.ttf b/web/public/vendor/pdfjs/standard_fonts/LiberationSans-Italic.ttf new file mode 100644 index 00000000..0cf61263 Binary files /dev/null and b/web/public/vendor/pdfjs/standard_fonts/LiberationSans-Italic.ttf differ diff --git a/web/public/vendor/pdfjs/standard_fonts/LiberationSans-Regular.ttf b/web/public/vendor/pdfjs/standard_fonts/LiberationSans-Regular.ttf new file mode 100644 index 00000000..366d1489 Binary files /dev/null and b/web/public/vendor/pdfjs/standard_fonts/LiberationSans-Regular.ttf differ diff --git a/web/public/vendor/pdfjs/text_layer_builder.css b/web/public/vendor/pdfjs/text_layer_builder.css new file mode 100644 index 00000000..a2eb5ad6 --- /dev/null +++ b/web/public/vendor/pdfjs/text_layer_builder.css @@ -0,0 +1,144 @@ +/* Copyright 2014 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +.textLayer { + color-scheme: only light; + + position: absolute; + text-align: initial; + inset: 0; + overflow: clip; + opacity: 1; + line-height: 1; + text-size-adjust: none; + forced-color-adjust: none; + transform-origin: 0 0; + caret-color: CanvasText; + z-index: 0; + + &.highlighting { + touch-action: none; + } + + :is(span, br) { + color: transparent; + position: absolute; + white-space: pre; + cursor: text; + transform-origin: 0% 0%; + } + + /* We multiply the font size by --min-font-size, and then scale the text + * elements by 1/--min-font-size. This allows us to effectively ignore the + * minimum font size enforced by the browser, so that the text layer s + * can always match the size of the text in the canvas. */ + --min-font-size: 1; + --text-scale-factor: calc(var(--total-scale-factor) * var(--min-font-size)); + --min-font-size-inv: calc(1 / var(--min-font-size)); + + > :not(.markedContent), + .markedContent span:not(.markedContent) { + z-index: 1; + + --font-height: 0; /* set by text_layer.js */ + font-size: calc(var(--text-scale-factor) * var(--font-height)); + + --scale-x: 1; + --rotate: 0deg; + transform: rotate(var(--rotate)) scaleX(var(--scale-x)) + scale(var(--min-font-size-inv)); + } + + .markedContent { + display: contents; + } + + span[role="img"] { + user-select: none; + cursor: default; + } + + .highlight { + --highlight-bg-color: rgb(180 0 170 / 0.25); + --highlight-selected-bg-color: rgb(0 100 0 / 0.25); + --highlight-backdrop-filter: none; + --highlight-selected-backdrop-filter: none; + + @media screen and (forced-colors: active) { + --highlight-bg-color: transparent; + --highlight-selected-bg-color: transparent; + --highlight-backdrop-filter: var(--hcm-highlight-filter); + --highlight-selected-backdrop-filter: var( + --hcm-highlight-selected-filter + ); + } + + margin: -1px; + padding: 1px; + background-color: var(--highlight-bg-color); + backdrop-filter: var(--highlight-backdrop-filter); + border-radius: 4px; + + &.appended { + position: initial; + } + + &.begin { + border-radius: 4px 0 0 4px; + } + + &.end { + border-radius: 0 4px 4px 0; + } + + &.middle { + border-radius: 0; + } + + &.selected { + background-color: var(--highlight-selected-bg-color); + backdrop-filter: var(--highlight-selected-backdrop-filter); + } + } + + ::selection { + /* stylelint-disable declaration-block-no-duplicate-properties */ + /*#if !MOZCENTRAL*/ + background: rgba(0 0 255 / 0.25); + /*#endif*/ + /* stylelint-enable declaration-block-no-duplicate-properties */ + background: color-mix(in srgb, AccentColor, transparent 75%); + } + + /* Avoids https://github.com/mozilla/pdf.js/issues/13840 in Chrome */ + /*#if !MOZCENTRAL*/ + br::selection { + background: transparent; + } + /*#endif*/ + + .endOfContent { + display: block; + position: absolute; + inset: 100% 0 0; + z-index: 0; + cursor: default; + user-select: none; + } + + &.selecting .endOfContent { + top: 0; + } +} diff --git a/web/public/vendor/pdfjs/wasm/LICENSE_JBIG2 b/web/public/vendor/pdfjs/wasm/LICENSE_JBIG2 new file mode 100644 index 00000000..37a329c6 --- /dev/null +++ b/web/public/vendor/pdfjs/wasm/LICENSE_JBIG2 @@ -0,0 +1,196 @@ +// Copyright 2014 The PDFium Authors +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + 1. Definitions. + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + END OF TERMS AND CONDITIONS + APPENDIX: How to apply the Apache License to your work. + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + Copyright [yyyy] [name of copyright owner] + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + https://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/web/public/vendor/pdfjs/wasm/LICENSE_OPENJPEG b/web/public/vendor/pdfjs/wasm/LICENSE_OPENJPEG new file mode 100644 index 00000000..e8fa4104 --- /dev/null +++ b/web/public/vendor/pdfjs/wasm/LICENSE_OPENJPEG @@ -0,0 +1,39 @@ +/* + * The copyright in this software is being made available under the 2-clauses + * BSD License, included below. This software may be subject to other third + * party and contributor rights, including patent rights, and no such rights + * are granted under this license. + * + * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium + * Copyright (c) 2002-2014, Professor Benoit Macq + * Copyright (c) 2003-2014, Antonin Descampe + * Copyright (c) 2003-2009, Francois-Olivier Devaux + * Copyright (c) 2005, Herve Drolon, FreeImage Team + * Copyright (c) 2002-2003, Yannick Verschueren + * Copyright (c) 2001-2003, David Janssens + * Copyright (c) 2011-2012, Centre National d'Etudes Spatiales (CNES), France + * Copyright (c) 2012, CS Systemes d'Information, France + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/web/public/vendor/pdfjs/wasm/LICENSE_PDFJS_JBIG2 b/web/public/vendor/pdfjs/wasm/LICENSE_PDFJS_JBIG2 new file mode 100644 index 00000000..f1845baf --- /dev/null +++ b/web/public/vendor/pdfjs/wasm/LICENSE_PDFJS_JBIG2 @@ -0,0 +1,13 @@ +Copyright 2026 Mozilla Foundation + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/web/public/vendor/pdfjs/wasm/LICENSE_PDFJS_OPENJPEG b/web/public/vendor/pdfjs/wasm/LICENSE_PDFJS_OPENJPEG new file mode 100644 index 00000000..623929b8 --- /dev/null +++ b/web/public/vendor/pdfjs/wasm/LICENSE_PDFJS_OPENJPEG @@ -0,0 +1,22 @@ +Copyright (c) 2024, Mozilla Foundation + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/web/public/vendor/pdfjs/wasm/LICENSE_PDFJS_QCMS b/web/public/vendor/pdfjs/wasm/LICENSE_PDFJS_QCMS new file mode 100644 index 00000000..7e1aeb34 --- /dev/null +++ b/web/public/vendor/pdfjs/wasm/LICENSE_PDFJS_QCMS @@ -0,0 +1,22 @@ +Copyright (c) 2025, Mozilla Foundation + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/web/public/vendor/pdfjs/wasm/LICENSE_QCMS b/web/public/vendor/pdfjs/wasm/LICENSE_QCMS new file mode 100644 index 00000000..eec8246d --- /dev/null +++ b/web/public/vendor/pdfjs/wasm/LICENSE_QCMS @@ -0,0 +1,21 @@ +qcms +Copyright (C) 2009-2024 Mozilla Corporation +Copyright (C) 1998-2007 Marti Maria + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/web/public/vendor/pdfjs/wasm/jbig2.wasm b/web/public/vendor/pdfjs/wasm/jbig2.wasm new file mode 100644 index 00000000..254e1e48 Binary files /dev/null and b/web/public/vendor/pdfjs/wasm/jbig2.wasm differ diff --git a/web/public/vendor/pdfjs/wasm/jbig2_nowasm_fallback.js b/web/public/vendor/pdfjs/wasm/jbig2_nowasm_fallback.js new file mode 100644 index 00000000..c86528f0 --- /dev/null +++ b/web/public/vendor/pdfjs/wasm/jbig2_nowasm_fallback.js @@ -0,0 +1,15 @@ +/* THIS FILE IS GENERATED - DO NOT EDIT */ +async function JBig2(moduleArg={}){var moduleRtn;var Module=moduleArg;var ENVIRONMENT_IS_WEB=true;var ENVIRONMENT_IS_WORKER=false;var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};var _scriptName=import.meta.url;var scriptDirectory="";var readAsync;if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){try{scriptDirectory=new URL(".",_scriptName).href}catch{}{readAsync=async url=>{var response=await fetch(url,{credentials:"same-origin"});if(response.ok){return response.arrayBuffer()}throw new Error(response.status+" : "+response.url)}}}else{}var out=console.log.bind(console);var err=console.error.bind(console);var wasmBinary;var WebAssembly={Memory:function(opts){this.buffer=new ArrayBuffer(opts["initial"]*65536)},Module:function(binary){},Instance:function(module,info){this.exports=( +// EMSCRIPTEN_START_ASM +function instantiate(ha){var a;var b=new Uint8Array(123);for(var c=25;c>=0;--c){b[48+c]=52+c;b[65+c]=c;b[97+c]=26+c}b[43]=62;b[47]=63;function i(j,k,l){var d,e,c=0,f=k,g=l.length,h=k+(g*3>>2)-(l[g-2]=="=")-(l[g-1]=="=");for(;c>4;if(f>2;if(f>>0;s=s>>>0;if(q+s>a.length)throw"trap: invalid memory.fill";a.fill(u,q,q+s)}function ga(n){var v=new ArrayBuffer(16777216);var w=new Int8Array(v);var x=new Int16Array(v);var y=new Int32Array(v);var z=new Uint8Array(v);var A=new Uint16Array(v);var B=new Uint32Array(v);var C=new Float32Array(v);var D=new Float64Array(v);var E=Math.imul;var F=Math.fround;var G=Math.abs;var H=Math.clz32;var I=Math.min;var J=Math.max;var K=Math.floor;var L=Math.ceil;var M=Math.trunc;var N=Math.sqrt;var O=n.a;var P=O.a;var Q=O.b;var R=O.c;var S=O.d;var T=O.e;var U=O.f;var V=O.g;var W=O.h;var X=O.i;var Y=72736;var Z=0; +// EMSCRIPTEN_START_FUNCS +function yc(a){var b=0,c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,q=0,r=0,s=0,u=0,v=0,C=0,D=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,_=0,$=0,aa=0,ba=0,ca=0,da=0,ea=0,fa=0,ga=0;J=Y-16|0;Y=J;a:{b:{c:{d:{c=y[a+4>>2];b=y[c+8>>2];i=y[c+4>>2];e:{if(b>>>0>i>>>0){break e}if((b|0)==(i|0)){aa=2;break c}R=a+80|0;while(1){if(b>>>0>i>>>0){break e}if(i-b>>>0<11){break c}f:{g:{h:{m=y[R>>2];i:{if(!m){b=na(72);w[b+4|0]=0;y[b>>2]=0;t(b+8|0,0,64);y[J+4>>2]=0;ib(R,b);ac(J+4|0);j:{c=y[a+80>>2];k:{if(ra(y[a+4>>2],c)){break k}if(va(y[a+4>>2],c+4|0)){break k}b=y[a+4>>2];d=y[b+8>>2];g=y[b>>2];l:{m:{if(B[b+4>>2]<=d>>>0){w[J+15|0]=0;break m}d=z[d+g|0];w[J+15|0]=d;if(d>>>0<224){break m}if(ra(b,c+8|0)){break k}b=y[c+8>>2]&536870911;y[c+8>>2]=b;if(b>>>0>64){break k}g=y[a+4>>2];i=y[g+8>>2];d=i+(b+8>>>3|0)|0;if(d>>>0>>0){break l}h=g;g=y[g+4>>2];y[h+8>>2]=d>>>0>>0?d:g;break l}if(va(b,J+15|0)){break k}b=z[J+15|0]>>>5|0;y[c+8>>2]=b}g=z[c+4|0];n:{if(!b){break n}d=y[c>>2];xc(c+12|0,b);b=0;d=(d>>>0>65536?4:d>>>0>256?2:1)-1|0;while(1){if(y[c+8>>2]<=(b|0)){break n}o:{p:{switch(d-1|0){default:if(va(y[a+4>>2],J+15|0)){break k}m=z[J+15|0];y[y[c+12>>2]+(b<<2)>>2]=m;break o;case 0:if(Wa(y[a+4>>2],J+12|0)){break k}m=A[J+12>>1];y[y[c+12>>2]+(b<<2)>>2]=m;break o;case 1:break e;case 2:break p}}if(ra(y[a+4>>2],J+8|0)){break k}m=y[J+8>>2];y[y[c+12>>2]+(b<<2)>>2]=m}b=b+1|0;if(B[c>>2]>m>>>0){continue}break}break k}b=y[a+4>>2];q:{if(!(g&64)){if(va(b,J+15|0)){break k}y[c+24>>2]=z[J+15|0];break q}if(ra(b,c+24|0)){break k}}if(!ra(y[a+4>>2],c+28|0)){break j}}ib(R,0);break i}b=y[a+4>>2];d=y[b+20>>2];y[c+40>>2]=y[b+16>>2];y[c+44>>2]=d;b=y[b+8>>2];y[c+48>>2]=1;y[c+36>>2]=b;y[a+84>>2]=b;m=y[a+80>>2]}r:{while(1){h=0;c=0;f=Y-368|0;Y=f;b=2;s:{t:{u:{v:{w:{x:{y:{z:{A:{B:{C:{D:{E:{F:{G:{H:{I:{J:{K:{L:{M:{N:{O:{P:{Q:{R:{S:{T:{U:{d=z[m+4|0]&63;switch(d-16|0){case 0:break T;case 1:case 2:case 3:case 5:case 8:case 9:case 10:case 11:case 12:case 13:case 14:case 15:case 16:case 17:case 18:case 19:case 21:case 25:case 28:case 29:case 30:case 31:break B;case 4:case 6:case 7:break S;case 20:case 22:case 23:break R;case 24:case 26:case 27:break Q;case 32:break P;case 33:break O;case 34:break N;case 35:break u;case 36:break M;case 37:break L;default:break U}}V:{switch(d|0){case 0:b=0;s=0;F=0;N=0;e=Y-272|0;Y=e;l=1;if(!Wa(y[a+4>>2],e+118|0)){j=na(56);y[j+40>>2]=0;y[j+32>>2]=0;y[j+36>>2]=0;y[j+24>>2]=0;y[j+28>>2]=0;y[j+16>>2]=0;y[j+20>>2]=0;h=A[e+118>>1];c=h&1;w[j|0]=c;d=h>>>10&3;w[j+3|0]=d;g=h&12288;w[j+2|0]=(g|0)!=0;i=(h&254)>>>1&1;w[j+1|0]=i;Q=j+16|0;W:{X:{Y:{if(!c){c=d?2:8;d=j+44|0;while(1){if((b|0)!=(c|0)){g=b+d|0;b=b+1|0;if(!va(y[a+4>>2],g)){continue}break W}break}if(z[j+1|0]!=1){break X}if(!z[j+2|0]){break Y}break X}if(g|!i){break X}}c=j+52|0;b=0;while(1){if((b|0)==4){break X}d=b+c|0;b=b+1|0;if(!va(y[a+4>>2],d)){continue}break}break W}if(ra(y[a+4>>2],j+12|0)){break W}if(ra(y[a+4>>2],j+8|0)|B[j+12>>2]>65535|B[j+8>>2]>65535){break W}b=y[m+8>>2];i=(b|0)>0?b:0;b=0;while(1){if((b|0)==(i|0)){c=1;k=0;b=0;while(1)if((b|0)==(i|0)){Z:{_:{$:{aa:{ba:{ca:{da:{ea:{fa:{if(!(c&1)){break fa}y[j+4>>2]=k;U=Ub(e+104|0,k);n=y[U>>2];ga:{if((n|0)==y[U+4>>2]){break ga}b=y[m+8>>2];q=(b|0)>0?b:0;c=1;k=0;while(1){if((q|0)==(F|0)){break ga}ha:{b=Ma(a,y[y[m+12>>2]+(F<<2)>>2]);if(z[b+4|0]&63){break ha}b=y[b+56>>2];g=y[b+24>>2];d=y[b+28>>2]-g>>2;b=0;while(1){if((b|0)==(d|0)){g=0;i=0;if(c&1){b=d+k|0;g=b>>>0>=d>>>0;i=g?b:0}k=i;c=g;break ha}i=b+k|0;if(!(i>>>0>=b>>>0&c)){break fa}y[n+(i<<2)>>2]=y[g+(b<<2)>>2];b=b+1|0;continue}}F=F+1|0;continue}}bb(Q,U);ia:{if(z[j|0]!=1){break ia}b=h>>>2&3;if((b|0)==2){break Z}d=h>>>4&3;if((d|0)==2){break Z}ja:{ka:{switch(b|0){case 0:c=sa(a,4);b=0;break ja;case 1:c=sa(a,5);b=0;break ja;default:break ka}}b=Ga(a,m,0);if(!b){break Z}c=y[b+68>>2];b=1}y[j+28>>2]=c;la:{ma:{switch(d|0){case 0:c=sa(a,2);break la;case 1:c=sa(a,3);break la;default:break ma}}c=Ga(a,m,b);if(!c){break Z}b=b+1|0;c=y[c+68>>2]}y[j+32>>2]=c;na:{if(!(h&64)){c=sa(a,1);break na}c=Ga(a,m,b);if(!c){break Z}b=b+1|0;c=y[c+68>>2]}y[j+36>>2]=c;if(z[j+1|0]!=1){break ia}oa:{if(!(h&128)){b=sa(a,1);break oa}b=Ga(a,m,b);if(!b){break Z}b=y[b+68>>2]}y[j+40>>2]=b}W=z[j+1|0];V=z[j|0];b=z[j+3|0];c=z[j+2|0];y[e+100>>2]=0;y[e+92>>2]=0;y[e+96>>2]=0;y[e+88>>2]=0;y[e+80>>2]=0;y[e+84>>2]=0;c=c?1024:8192;b=b?(b|0)==1?8192:1024:65536;pa:{if(!(!(h&256)|!s)){if(!(V&1)){d=Sb(e+92|0,y[s+56>>2]);if((b|0)!=y[e+96>>2]-y[d>>2]>>3){break _}}if(!(W&1)){break pa}b=Sb(e+80|0,y[s+56>>2]+12|0);if((c|0)==y[e+84>>2]-y[b>>2]>>3){break pa}break _}if(!(V&1)){kb(e+92|0,b)}if(!(W&1)){break pa}kb(e+80|0,c)}b=y[m+44>>2];d=b;c=y[m+40>>2];y[e+64>>2]=c;y[e+68>>2]=b;g=y[m+36>>2];y[e+72>>2]=g;y[m+52>>2]=2;qa:{if(!(b|c)|z[a+48|0]!=1){break qa}i=y[a+108>>2];b=i;while(1){b=y[b+4>>2];if((i|0)==(b|0)){break qa}if((c|0)!=y[b+8>>2]|(d|0)!=y[b+12>>2]|(g|0)!=y[b+16>>2]){continue}break}c=Ad(y[b+24>>2]);y[e+120>>2]=0;Rb(m+56|0,c);Za(e+120|0);zd(y[a+108>>2],e- -64|0,b+24|0);c=y[a+108>>2];d=y[b>>2];g=y[b+4>>2];y[d+4>>2]=g;y[g>>2]=d;y[c+8>>2]=y[c+8>>2]-1;oc(b);break aa}if(!(V&1)){u=Ra(y[a+4>>2]);y[e+60>>2]=u;I=y[e+92>>2];d=y[e+96>>2];C=y[e+80>>2];g=y[e+84>>2];ba=Fa();y[e+268>>2]=ba;ca=Fa();y[e+252>>2]=ca;da=Fa();y[e+248>>2]=da;ea=Fa();y[e+244>>2]=ea;i=y[j+8>>2]+y[j+4>>2]|0;b=0;while(1){c=b;b=b+1|0;if(i>>>0>1<>>0){continue}break}_=I+3240|0;S=g-C>>3;P=Cd(e+120|0,c&255);O=gb(e+256|0,y[j+8>>2]);G=d-I>>3;X=G>>>0>405;i=0;F=0;while(1){ra:{sa:{ta:{b=y[j+8>>2];if(b>>>0>i>>>0){y[e+240>>2]=0;Aa(ba,u,e+236|0);F=y[e+236>>2]+F|0;if(F>>>0>65535){break ta}K=0;while(1){ua:{l=Aa(ca,u,e+232|0);if(!l|B[j+8>>2]<=i>>>0){break ua}l=1;K=y[e+232>>2]+K|0;if(K>>>0>65535){break ua}if(!(!F|!K)){va:{wa:{xa:{ya:{za:{if(!z[j+1|0]){r=qb();y[e+228>>2]=r;y[r+8>>2]=F;y[r+4>>2]=K;w[r|0]=0;b=z[j+3|0];w[r+1|0]=0;w[r+2|0]=0;w[r+3|0]=b;w[r+16|0]=z[j+44|0];w[r+17|0]=z[j+45|0];w[r+18|0]=z[j+46|0];w[r+19|0]=z[j+47|0];w[r+20|0]=z[j+48|0];w[r+21|0]=z[j+49|0];w[r+22|0]=z[j+50|0];w[r+23|0]=z[j+51|0];Aa:{switch(b|0){case 0:if(xd(r)){D=nc(r,u,I,G,0);break wa}D=mc(r,u,I,G,0);break wa;case 1:if(wd(r)){D=nc(r,u,I,G,1);break wa}D=mc(r,u,I,G,1);break wa;case 2:if(Qb(r)){D=nc(r,u,I,G,2);break wa}D=mc(r,u,I,G,2);break wa;default:break Aa}}if(!Qb(r)){break za}D=xa(K,F);y[e+160>>2]=D;if(!qa(D)){break ya}b=y[r+4>>2];if(!b){break fa}b=b-1|0;L=b>>>3|0;T=(b&7)+1|0;g=0;k=0;n=0;c=0;while(1){Ba:{Ca:{if(B[r+8>>2]>n>>>0){ja(e+192|0,D,n);h=y[e+196>>2];q=y[e+192>>2];if(z[r+1|0]!=1){b=c;break Ca}if(z[u|0]){break ya}if(!X){break fa}b=0;d=ka(u,_);if((d|0)==(c|0)){break Ca}y[e+184>>2]=h;y[e+180>>2]=q;y[e+172>>2]=k;y[e+168>>2]=g;b=y[e+184>>2];y[e+8>>2]=y[e+180>>2];y[e+12>>2]=b;b=y[e+172>>2];y[e>>2]=y[e+168>>2];y[e+4>>2]=b;Pa(e+8|0,e);c=c^d;break Ba}y[e+160>>2]=0;break xa}c=0;H=0;Da:{if(!n){Ea:{while(1){k=7;v=0;if((H|0)==(L|0)){break Ea}Fa:{while(1){if((k|0)>=0){if(z[u|0]){break Fa}if(c>>>0>=G>>>0){break fa}d=ka(u,(c<<3)+I|0);c=d|c<<1&1006;v=d<>>0>>0){break ya}}k=0;v=0;while(1){if((k|0)==(T|0)){break Da}if(z[u|0]){break ya}if(c>>>0>=G>>>0){break fa}d=ka(u,(c<<3)+I|0);c=d|c<<1&1006;v=d<<7-k|v;k=k+1|0;continue}}if(!k){break fa}$=k-1|0;H=z[g|0];k=H>>>1&112;d=0;while(1){Ga:{if((d|0)!=(L|0)){if((d|0)==($|0)){break fa}s=d+1|0;H=z[s+g|0]|H<<8;c=7;v=0;while(1){if((c|0)<0){break Ga}if(z[u|0]){break ya}if(k>>>0>=G>>>0){break fa}M=ka(u,(k<<3)+I|0);k=M|(H>>>c+1&16|k<<1&1006);v=M<>>0>=G>>>0){break fa}g=ka(u,(k<<3)+I|0);k=g|(d>>>8-c&16|k<<1&1006);v=g<<7-c|v;c=c+1|0;continue}break}break Da}if((d|0)==(h|0)){break fa}w[d+q|0]=v;d=s;continue}}if(h>>>0<=L>>>0){break fa}w[q+L|0]=v;c=b}n=n+1|0;g=q;k=h;continue}}Aa(da,u,e+180|0);d=y[e+180>>2];if(d>>>0>=2){g=wa(6);y[e+168>>2]=g;h=wa(8);y[e+160>>2]=h;k=wa(11);y[e+228>>2]=k;l=wa(15);y[e+224>>2]=l;n=wa(15);y[e+220>>2]=n;q=wa(15);y[e+216>>2]=q;s=wa(15);y[e+212>>2]=s;r=wa(1);y[e+208>>2]=r;c=Yb();y[e+204>>2]=c;b=z[j|0];y[c+20>>2]=1;y[c+16>>2]=d;y[c+12>>2]=F;y[c+8>>2]=K;w[c+1|0]=1;w[c|0]=b;b=y[j+4>>2]+i|0;y[c+24>>2]=b;d=Ub(e+192|0,b);b=y[j+4>>2];v=y[j+16>>2];if(b>>>0>y[j+20>>2]-v>>2>>>0){break fa}M=b;b=y[d>>2];vd(v,M,b,y[e+196>>2]-b>>2);v=b+(y[j+4>>2]<<2)|0;D=y[O>>2];b=0;while(1)if((b|0)==(i|0)){bb(c+40|0,d);x[c+4>>1]=0;y[c+52>>2]=0;y[c+56>>2]=1;w[c+3|0]=0;y[c+88>>2]=r;y[c+84>>2]=s;y[c+80>>2]=q;y[c+76>>2]=n;y[c+72>>2]=l;y[c+68>>2]=k;y[c+64>>2]=h;y[c+60>>2]=g;w[c+2|0]=z[j+2|0];w[c+92|0]=z[j+52|0];w[c+93|0]=z[j+53|0];w[c+94|0]=z[j+54|0];w[c+95|0]=z[j+55|0];b=Bd(c,u,C,S,P);y[e+176>>2]=0;oa(e+240|0,b);la(e+176|0);b=y[e+240>>2];ya(d);pc(e+204|0);pa(e+208|0);pa(e+212|0);pa(e+216|0);pa(e+220|0);pa(e+224|0);pa(e+228|0);pa(e+160|0);pa(e+168|0);if(!b){break ta}break va}else{H=b<<2;y[H+v>>2]=y[D+H>>2];b=b+1|0;continue}}b=0;if((d|0)!=1){break va}c=y[j+4>>2];Ac(y[P+36>>2],u,e+192|0);b=y[e+192>>2];if(b>>>0>=c+i>>>0){break ta}c=y[j+4>>2];if(c>>>0>b>>>0){b=y[Q>>2]+(b<<2)|0}else{b=b-c|0;c=y[O>>2];if(b>>>0>=y[O+4>>2]-c>>2>>>0){break fa}b=c+(b<<2)|0}c=y[b>>2];if(!c){break ta}Aa(y[P+28>>2],u,e+168|0);Aa(y[P+32>>2],u,e+160|0);b=db();y[e+228>>2]=b;y[b+8>>2]=F;y[b+4>>2]=K;d=z[j+2|0];y[b+20>>2]=c;w[b|0]=d;y[b+12>>2]=y[e+168>>2];c=y[e+160>>2];w[b+1|0]=0;y[b+16>>2]=c;w[b+24|0]=z[j+52|0];w[b+25|0]=z[j+53|0];w[b+26|0]=z[j+54|0];w[b+27|0]=z[j+55|0];b=cb(b,u,C,S);y[e+224>>2]=0;oa(e+240|0,b);la(e+224|0);b=y[e+240>>2];Ua(e+228|0);if(!b){break ta}break va}D=xa(K,F);y[e+160>>2]=D;Ha:{Ia:{if(!qa(D)){break Ia}n=0;Qa(D,0);h=0;c=0;b=0;while(1){Ja:{Ka:{if(B[r+8>>2]>b>>>0){ja(e+192|0,D,b);g=y[e+196>>2];q=y[e+192>>2];if(z[r+1|0]!=1){d=c;break Ka}if(z[u|0]){break Ia}if(!X){break fa}d=0;k=ka(u,_);if((k|0)==(c|0)){break Ka}y[e+184>>2]=g;y[e+180>>2]=q;y[e+172>>2]=h;y[e+168>>2]=n;d=y[e+184>>2];y[e+24>>2]=y[e+180>>2];y[e+28>>2]=d;d=y[e+172>>2];y[e+16>>2]=y[e+168>>2];y[e+20>>2]=d;c=c^k;D=y[e+160>>2];Pa(e+24|0,e+16|0);break Ja}y[e+160>>2]=0;break Ha}H=0;L=0;if(z[r+2|0]==1){ja(e+192|0,y[r+12>>2],b);L=y[e+192>>2];H=y[e+196>>2]}D=y[e+160>>2];ja(e+192|0,D,w[r+17|0]+b|0);T=y[e+196>>2];$=y[e+192>>2];s=z[n|0]>>>6|0;k=y[D+8>>2];c=!h|(k|0)<2?0:s&1;v=!h|(k|0)<=0?c:s&2|c;s=0;c=0;while(1){if(B[r+4>>2]<=c>>>0){c=d;break Ja}La:{Ma:{if(z[r+2|0]==1){if(ia(y[y[r+12>>2]+8>>2],c,L,H)){break Ma}}k=ia(k,w[r+16|0]+c|0,$,T);if(z[u|0]){break Ia}k=v<<5|k<<4|s;if(k>>>0>=G>>>0){break fa}M=ka(u,(k<<3)+I|0);k=y[D+8>>2];if(!M){break Ma}La(k,c,q,g,1);k=y[D+8>>2];M=1;break La}M=0}v=ia(k,c+2|0,n,h)|v<<1&30;c=c+1|0;s=s<<1&14|M;continue}}b=b+1|0;n=q;h=g;continue}}D=0}la(e+160|0);break wa}D=0}la(e+160|0)}y[e+192>>2]=0;oa(e+240|0,D);la(e+192|0);b=y[e+240>>2];ua(e+228|0);if(!b){break ua}}c=y[O>>2];y[e+240>>2]=0;oa((i<<2)+c|0,b)}i=i+1|0;continue}break}b=l^1;break sa}y[e+200>>2]=0;y[e+192>>2]=0;y[e+196>>2]=0;ud(e+192|0,b+y[j+4>>2]|0);g=y[e+192>>2];i=0;b=0;k=0;Na:{while(1){if(y[j+8>>2]+y[j+4>>2]>>>0>b>>>0){Aa(ea,u,e+160|0);d=y[e+160>>2];c=d+b|0;if(!(c>>>0<=y[j+8>>2]+y[j+4>>2]>>>0&c>>>0>=d>>>0)){break Na}Pb(e+180|0,g,0,b);sd(e+168|0,y[e+180>>2],y[e+184>>2],d,i);k=(i&1?d:0)+k|0;i=(i^-1)&1;b=c;continue}break}if(B[j+12>>2]>>0){break Na}N=lc();d=y[O>>2];b=0;l=0;while(1){c=y[j+4>>2];if(c+y[j+8>>2]>>>0<=b>>>0){break Na}if(!(!(y[g+(b>>>3&536870908)>>2]>>>b&1)|B[j+12>>2]<=l>>>0)){Oa:{if(b>>>0>>0){c=y[y[Q>>2]+(b<<2)>>2];if(!c){i=0;break Oa}i=kc(c);break Oa}c=d+(b-c<<2)|0;i=y[c>>2];y[c>>2]=0}rd(N,i);l=l+1|0}b=b+1|0;continue}}Ob(e+192|0);break ra}b=0}la(e+240|0);if(b){continue}}break}$a(O);qc(P);Ea(e+244|0);Ea(e+248|0);Ea(e+252|0);Ea(e+268|0);y[e+120>>2]=0;Rb(m+56|0,N);Za(e+120|0);if(y[m+56>>2]){ta(y[a+4>>2]);b=y[a+4>>2];c=y[b+8>>2];if(c>>>0<=4294967293){g=b;c=c+2|0;b=y[b+4>>2];y[g+8>>2]=b>>>0>c>>>0?c:b}ua(e+60|0);break ba}ua(e+60|0);break $}g=y[e+80>>2];b=y[e+84>>2];n=y[a+4>>2];q=qd(n);y[e+268>>2]=q;C=gb(e+120|0,y[j+8>>2]);y[e+264>>2]=0;y[e+256>>2]=0;y[e+260>>2]=0;if(!z[j+1|0]){xc(e+256|0,y[j+8>>2])}u=b-g>>3;y[e+252>>2]=0;s=0;d=0;Pa:while(1){if(B[j+8>>2]<=d>>>0){break da}if(za(q,y[j+28>>2],e+160|0)){break ea}s=y[e+160>>2]+s|0;if(s>>>0>65535){break ea}F=0;c=0;i=d;while(1){b=za(q,y[j+32>>2],e+248|0);y[e+244>>2]=b;Qa:{Ra:{Sa:{Ta:{Ua:{switch(b|0){case 0:if(B[j+8>>2]<=i>>>0){break ea}c=y[e+248>>2]+c|0;if(c>>>0>65535){break ea}if(!c|!s){break Qa}if(z[j+1|0]!=1){break Ra}if(za(q,y[j+40>>2],e+240|0)){break ea}oa(e+252|0,0);h=y[e+240>>2];if(h>>>0<2){break Ta}v=wa(6);y[e+236>>2]=v;D=wa(8);y[e+232>>2]=D;H=wa(11);y[e+228>>2]=H;I=wa(15);y[e+224>>2]=I;G=wa(15);y[e+220>>2]=G;K=wa(15);y[e+216>>2]=K;O=wa(15);y[e+212>>2]=O;b=1;P=wa(1);y[e+208>>2]=P;l=Yb();y[e+204>>2]=l;k=z[j|0];y[l+20>>2]=1;y[l+16>>2]=h;y[l+12>>2]=s;y[l+8>>2]=c;w[l+1|0]=1;w[l|0]=k;h=y[j+4>>2]+i|0;y[l+24>>2]=h;r=Ed(e+192|0,h);k=y[j+8>>2]+y[j+4>>2]|0;while(1){h=b;b=b+1|0;if(k>>>0>1<>>0){continue}break};h=h&255;k=y[r>>2];L=y[l+24>>2];b=0;while(1)if((b|0)==(L|0)){bb(l+28|0,r);h=Ub(e+180|0,y[l+24>>2]);b=y[j+4>>2];k=y[j+16>>2];if(b>>>0>y[j+20>>2]-k>>2>>>0){break fa}M=b;b=y[h>>2];vd(k,M,b,y[e+184>>2]-b>>2);L=b+(y[j+4>>2]<<2)|0;k=y[C>>2];b=0;while(1)if((b|0)==(i|0)){bb(l+40|0,h);x[l+4>>1]=0;y[l+52>>2]=0;y[l+56>>2]=1;w[l+3|0]=0;y[l+88>>2]=P;y[l+84>>2]=O;y[l+80>>2]=K;y[l+76>>2]=G;y[l+72>>2]=I;y[l+68>>2]=H;y[l+64>>2]=D;y[l+60>>2]=v;w[l+2|0]=z[j+2|0];w[l+92|0]=z[j+52|0];w[l+93|0]=z[j+53|0];w[l+94|0]=z[j+54|0];w[l+95|0]=z[j+55|0];b=Dd(l,n,g,u);y[e+176>>2]=0;oa(e+252|0,b);la(e+176|0);b=y[e+252>>2];ya(h);ya(r);pc(e+204|0);pa(e+208|0);pa(e+212|0);pa(e+216|0);pa(e+220|0);pa(e+224|0);pa(e+228|0);pa(e+232|0);pa(e+236|0);if(b){break Sa}break ea}else{N=b<<2;y[N+L>>2]=y[k+N>>2];b=b+1|0;continue}}else{N=k+(b<<3)|0;y[N+4>>2]=b;y[N>>2]=h;b=b+1|0;continue};case 1:break Ua;default:break ea}}Va:{if(!z[j+1|0]){if(za(q,y[j+36>>2],e+192|0)){break ea}ta(n);y[e+180>>2]=0;Wa:{if(!y[e+192>>2]){if((F|0)>65535){break Va}b=F+7>>>3|0;c=se(b,0,s);if(Z){break Va}h=y[n+4>>2];k=y[n+8>>2];if(h>>>0>>0){break fa}if(c>>>0>h-k>>>0){break Va}c=0;h=xa(F,s);y[e+248>>2]=0;oa(e+180|0,h);la(e+248|0);v=y[e+180>>2];while(1){if((c|0)==(s|0)){break Wa}ja(e+168|0,v,c);l=y[n+8>>2];h=y[n+4>>2];if(l>>>0>h>>>0|b>>>0>h-l>>>0|b>>>0>B[e+172>>2]){break fa}if(b){if(b){p(y[e+168>>2],y[n>>2]+l|0,b)}l=y[n+8>>2]}h=b+l|0;if(h>>>0>=l>>>0){k=y[n+4>>2];y[n+8>>2]=h>>>0>>0?h:k}c=c+1|0;continue}}b=qb();y[e+248>>2]=b;y[b+8>>2]=s;y[b+4>>2]=F;w[b|0]=1;hb(b,e+180|0,n);ta(n);ua(e+248|0);v=y[e+180>>2]}Xa:{if(!v){break Xa}c=d>>>0>i>>>0?d:i;h=y[C>>2];b=0;k=y[e+256>>2];while(1){if((c|0)==(d|0)){break Xa}l=d<<2;r=l+k|0;F=sb(v,b,0,y[r>>2],s);y[e+248>>2]=0;oa(h+l|0,F);d=d+1|0;la(e+248|0);b=y[r>>2]+b|0;continue}}la(e+180|0)}d=i;continue Pa}la(e+180|0);break ea}if((h|0)!=1){k=y[C>>2];b=y[e+252>>2];break Sa}r=y[j+8>>2]+y[j+4>>2]|0;b=1;l=1;while(1){k=b;b=b+1|0;h=l;l=h+1|0;if(r>>>0>1<>>0){continue}break}y[e+192>>2]=h;h=k&255;l=0;b=0;while(1){if((b|0)!=(h|0)){if(wb(n,e+192|0)){break ea}b=b+1|0;l=y[e+192>>2]|l<<1;continue}break}b=y[j+4>>2];if(b+i>>>0<=l>>>0){break ea}k=y[C>>2];if(b>>>0>l>>>0){b=y[Q>>2]+(l<<2)|0}else{b=l-b|0;if(b>>>0>=y[C+4>>2]-k>>2>>>0){break fa}b=(b<<2)+k|0}h=y[b>>2];if(!h){break ea}b=wa(15);y[e+180>>2]=b;l=wa(1);y[e+236>>2]=l;Ya:{if(za(q,b,e+232|0)){break Ya}if(za(q,b,e+228|0)){break Ya}if(za(q,l,e+244|0)){break Ya}ta(n);r=y[n+8>>2];y[e+192>>2]=r;b=db();y[e+224>>2]=b;y[b+8>>2]=s;y[b+4>>2]=c;l=z[j+2|0];y[b+20>>2]=h;w[b|0]=l;y[b+12>>2]=y[e+232>>2];h=y[e+228>>2];l=0;w[b+1|0]=0;y[b+16>>2]=h;w[b+24|0]=z[j+52|0];w[b+25|0]=z[j+53|0];w[b+26|0]=z[j+54|0];w[b+27|0]=z[j+55|0];h=Ra(n);y[e+220>>2]=h;b=cb(b,h,g,u);y[e+216>>2]=0;oa(e+252|0,b);la(e+216|0);b=y[e+252>>2];if(b){ta(n);l=y[n+8>>2];if(l>>>0<=4294967293){h=l+2|0;l=y[n+4>>2];l=h>>>0>>0?h:l;y[n+8>>2]=l}l=y[e+244>>2]==(l-r|0)}ua(e+220|0);Ua(e+224|0);pa(e+236|0);pa(e+180|0);if(l){break Sa}break ea}pa(e+236|0);pa(e+180|0);break ea}y[e+252>>2]=0;oa((i<<2)+k|0,b);if(z[j+1|0]){break Qa}}y[y[e+256>>2]+(i<<2)>>2]=c}F=c+F|0;i=i+1|0;continue}}}o()}b=0;break ca}i=wa(1);y[e+248>>2]=i;y[e+200>>2]=0;y[e+192>>2]=0;y[e+196>>2]=0;ud(e+192|0,y[j+8>>2]+y[j+4>>2]|0);g=y[e+192>>2];l=0;b=0;k=0;Za:{while(1){if(y[j+8>>2]+y[j+4>>2]>>>0>b>>>0){_a:{$a:{if(za(q,i,e+244|0)){break $a}d=y[e+244>>2];c=d+b|0;if(c>>>0>>0){break $a}if(c>>>0<=y[j+8>>2]+y[j+4>>2]>>>0){break _a}}b=0;break Za}Pb(e+180|0,g,0,b);sd(e+160|0,y[e+180>>2],y[e+184>>2],d,l);k=(l&1?d:0)+k|0;l=(l^-1)&1;b=c;continue}break}b=0;if(B[j+12>>2]>>0){break Za}b=lc();d=y[C>>2];l=0;i=0;while(1){c=y[j+4>>2];if(c+y[j+8>>2]>>>0<=l>>>0){break Za}if(!(!(y[g+(l>>>3&536870908)>>2]>>>l&1)|B[j+12>>2]<=i>>>0)){ab:{if(c>>>0>l>>>0){c=y[y[Q>>2]+(l<<2)>>2];if(!c){c=0;break ab}c=kc(c);break ab}h=d+(l-c<<2)|0;c=y[h>>2];y[h>>2]=0}rd(b,c);i=i+1|0}l=l+1|0;continue}}Ob(e+192|0);pa(e+248|0)}la(e+252|0);Ja(e+256|0);$a(C);Ua(e+268|0);y[e+120>>2]=0;Rb(m+56|0,b);Za(e+120|0);if(!y[m+56>>2]){break $}ta(y[a+4>>2])}if(z[a+48|0]!=1){break aa}fa=e,ga=Ad(y[m+56>>2]),y[fa+120>>2]=ga;b=y[a+108>>2];l=y[b+8>>2];while(1){if(l>>>0>=2){c=y[b>>2];d=y[c>>2];g=y[c+4>>2];y[d+4>>2]=g;y[g>>2]=d;y[b+8>>2]=y[b+8>>2]-1;oc(c);l=l-1|0;b=y[a+108>>2];continue}break}c=b;b=e+120|0;zd(c,e- -64|0,b);Za(b)}l=0;if(!(z[e+119|0]&2)){break _}if(!(V&1)){b=y[m+56>>2];y[e+48>>2]=y[e+92>>2];c=y[e+96>>2];d=y[e+100>>2];y[e+96>>2]=0;y[e+100>>2]=0;y[e+52>>2]=c;y[e+56>>2]=d;y[e+92>>2]=0;c=b;b=e+48|0;pd(c,b);Ja(b)}if(!(W&1)){break _}b=y[m+56>>2];y[e+36>>2]=y[e+80>>2];c=y[e+84>>2];d=y[e+88>>2];y[e+84>>2]=0;y[e+88>>2]=0;y[e+40>>2]=c;y[e+44>>2]=d;y[e+80>>2]=0;c=b+12|0;b=e+36|0;pd(c,b);Ja(b);break _}l=1}Ja(e+80|0);Ja(e+92|0)}ya(U);break W}else{d=Ma(a,y[y[m+12>>2]+(b<<2)>>2]);if(!(z[d+4|0]&63)){g=0;if(c&1){c=y[d+56>>2];c=y[c+28>>2]-y[c+24>>2]>>2;g=c+k|0;n=g;g=c>>>0<=g>>>0;k=g?n:0}else{k=0}s=d;c=g}b=b+1|0;continue}}c=b<<2;b=b+1|0;if(Ma(a,y[c+y[m+12>>2]>>2])){continue}break}}ya(Q);ma(j)}Y=e+272|0;b=l;break u;case 1:case 2:case 3:case 5:break B;case 4:case 6:case 7:break V;default:break K}}b=1;if(z[a+49|0]!=1){break u}if(tb(a,f+332|0)){break u}d=f+14|0;if(Wa(y[a+4>>2],d)){break u}c=y[f+332>>2];if(c-1>>>0>65534){break u}q=y[f+336>>2];if(q-1>>>0>65534){break u}h=Yb();y[f+308>>2]=h;y[h+12>>2]=q;y[h+8>>2]=c;c=A[f+14>>1];g=c&1;w[h|0]=g;w[h+2|0]=c>>>15;y[h+56>>2]=c>>>4&3;y[h+52>>2]=c>>>7&3;w[h+3|0]=c>>>6&1;w[h+4|0]=c>>>9&1;y[h+20>>2]=1<<(c>>>2&3);i=(c&254)>>>1&1;w[h+1|0]=i;k=c>>>10&31;w[h+5|0]=(k>>>0>15?-32:0)|k;bb:{if(g){if(Wa(y[a+4>>2],d)){break v}if(z[h+1|0]!=1){break w}if(!z[h+2|0]){break bb}break w}if(!i|c<<16>>16<0){break w}}c=h+92|0;d=0;while(1){if((d|0)==4){break w}g=c+d|0;d=d+1|0;if(!va(y[a+4>>2],g)){continue}break}break v}g=na(12);y[g+8>>2]=0;y[g>>2]=0;y[g+4>>2]=0;if(va(y[a+4>>2],f+352|0)){break y}b=1;if(va(y[a+4>>2],g+1|0)){break x}if(va(y[a+4>>2],g+2|0)){break x}if(ra(y[a+4>>2],g+4|0)){break x}i=y[g+4>>2];if(i>>>0>65535){break x}b=z[f+352|0];d=b&1;w[g|0]=d;b=b>>>1&3;w[g+8|0]=b;y[m+52>>2]=3;if(d){d=y[a+4>>2];b=wc(g);y[f+24>>2]=b;if(b){y[f+332>>2]=0;hb(b,f+332|0,d);cb:{d=y[f+332>>2];if(!d){break cb}c=vc(i+1|0);b=0;while(1){if(b>>>0>i>>>0){break cb}h=z[g+1|0];h=sb(d,E(h,b),0,h,z[g+2|0]);k=y[c+4>>2];y[f+312>>2]=0;oa((b<<2)+k|0,h);b=b+1|0;la(f+312|0);continue}}la(f+332|0)}ua(f+24|0);y[f+332>>2]=0;Xb(m+60|0,c);Zb(f+332|0);if(!y[m+60>>2]){break y}ta(y[a+4>>2]);b=0;break x}k=b?(b|0)==1?8192:1024:65536;l=ab(k,8);y[f+336>>2]=k;y[f+332>>2]=l;n=Ra(y[a+4>>2]);y[f+16>>2]=n;d=wc(g);y[f+312>>2]=d;if(!d){break z}w[d+1|0]=0;w[d+2|0]=0;w[d+3|0]=b;h=z[g+1|0];w[d+17|0]=0;w[d+16|0]=0-h;if(!b){w[d+22|0]=254;w[d+23|0]=254;w[d+18|0]=253;w[d+19|0]=255;w[d+20|0]=2;w[d+21|0]=254}y[f+356>>2]=0;y[f+40>>2]=0;y[f+32>>2]=l;y[f+36>>2]=k;y[f+28>>2]=n;y[f+24>>2]=f+356;b=Wb(d,f+24|0);y[f+40>>2]=0;while(1){if((b|0)==3){b=Vb(d,f+24|0);continue}break}y[d+44>>2]=0;y[d+48>>2]=0;y[d+36>>2]=0;y[d+40>>2]=0;y[d+28>>2]=0;y[d+32>>2]=0;if(!y[f+356>>2]){break A}c=vc(i+1|0);b=0;while(1){if(b>>>0>i>>>0){break A}d=sb(y[f+356>>2],E(b,h),0,h,z[g+2|0]);k=y[c+4>>2];y[f>>2]=0;oa((b<<2)+k|0,d);b=b+1|0;la(f);continue}}b=1;if(z[a+49|0]!=1){break u}q=na(56);y[q+16>>2]=0;db:{if(tb(a,f+312|0)){break db}if(va(y[a+4>>2],f+14|0)){break db}if(ra(y[a+4>>2],q+32|0)){break db}if(ra(y[a+4>>2],q+36|0)){break db}if(ra(y[a+4>>2],q+40|0)){break db}if(ra(y[a+4>>2],q+44|0)){break db}if(Wa(y[a+4>>2],q+48|0)){break db}if(Wa(y[a+4>>2],q+50|0)){break db}C=y[q+32>>2];if(C-1>>>0>65534){break db}e=y[q+36>>2];if(e-1>>>0>65534){break db}v=y[f+312>>2];if(v-1>>>0>65534){break db}k=y[f+316>>2];if(k-1>>>0>65534){break db}y[q+4>>2]=k;y[q>>2]=v;d=z[f+14|0];F=d&1;w[q+8|0]=F;w[q+20|0]=d>>>7;i=d>>>3|0;K=i&1;w[q+28|0]=K;s=d>>>1&3;w[q+9|0]=s;g=d>>>4|0;y[q+24>>2]=(g&7)==4?4:g&3;if(y[m+8>>2]!=1){break db}g=Ma(a,y[y[m+12>>2]>>2]);if(!g|(z[g+4|0]&63)!=16){break db}g=y[g+60>>2];if(!g){break db}j=y[g>>2];if(!j){break db}y[q+12>>2]=j;y[q+16>>2]=g+4;b=y[y[g+4>>2]>>2];g=y[b+8>>2];w[q+52|0]=g;r=y[b+12>>2];w[q+53|0]=r;y[m+52>>2]=1;eb:{fb:{gb:{if(d&1){d=y[a+4>>2];b=1;while(1){c=b;b=b+1|0;if(j>>>0>1<>>0){continue}break}x[f+80>>1]=0;y[f+36>>2]=0;y[f+48>>2]=0;y[f+52>>2]=0;y[f+56>>2]=0;y[f+60>>2]=0;y[f+64>>2]=0;y[f+68>>2]=0;y[f+72>>2]=0;y[f+84>>2]=0;y[f+88>>2]=0;y[f+92>>2]=0;y[f+96>>2]=0;y[f+100>>2]=0;y[f+32>>2]=e;y[f+28>>2]=C;w[f+24|0]=F;b=c&255;g=gb(f+332|0,b);i=y[g>>2];c=(i+(b<<2)|0)-4|0;hb(f+24|0,c,d);h=0;hb:{if(!y[c>>2]){break hb}ta(d);c=y[d+8>>2];if(c>>>0<=4294967292){c=c+3|0;h=y[d+4>>2];y[d+8>>2]=c>>>0>>0?c:h}b=b-2|0;while(1){if((b|0)>=0){c=i+(b<<2)|0;hb(f+24|0,c,d);h=0;if(!y[c>>2]){break hb}ta(d);h=y[d+8>>2];if(h>>>0<=4294967292){h=h+3|0;l=y[d+4>>2];y[d+8>>2]=h>>>0>>0?h:l}fb(y[c>>2],0,0,0,0,y[c+4>>2],2);b=b-1|0;continue}break}h=uc(q,g)}b=h;$a(g);y[f+356>>2]=0;oa(m- -64|0,b);la(f+356|0);if(!y[m+64>>2]){break eb}ta(y[a+4>>2]);break gb}b=s?(s|0)==1?8192:1024:65536;d=ab(b,8);y[f+4>>2]=b;y[f>>2]=d;Q=Ra(y[a+4>>2]);y[f+308>>2]=Q;n=0;y[f+16>>2]=0;l=b;ib:{if(!(i&1)){break ib}b=xa(C,e);y[f+24>>2]=0;oa(f+16|0,b);b=r&255;O=1-b|0;D=0-(b>>>0>1)|0;b=g&255;P=1-b|0;H=0-(b>>>0>1)|0;la(f+24|0);n=y[f+16>>2];jb:while(1){if(!c&(e|0)==(h|0)){break ib}ja(f+24|0,n,h);b=y[q+44>>2];L=A[q+48>>1];g=se(h,c,L);r=b+g|0;b=Z+(b>>31)|0;V=g>>>0>r>>>0?b+1|0:b;b=y[q+40>>2];N=A[q+50>>1];g=se(h,c,N);I=b+g|0;b=Z+(b>>31)|0;U=g>>>0>I>>>0?b+1|0:b;g=0;i=0;W=y[f+28>>2];_=y[f+24>>2];while(1)if(!i&(g|0)==(C|0)){h=h+1|0;c=h?c:c+1|0;continue jb}else{S=y[n+8>>2];b=se(g,i,N);u=r-b|0;G=V-(Z+(b>>>0>r>>>0)|0)|0;b=G>>8;X=(G&255)<<24|u>>>8;T=se(g,i,L);u=T+I|0;G=Z+U|0;M=S;G=u>>>0>>0?G+1|0:G;S=(G&255)<<24|u>>>8;u=G>>8;La(M,g,_,W,k>>>0<=X>>>0&(b|0)>=0|(b|0)>0|(P>>>0>S>>>0&(H|0)>=(u|0)|(u|0)<(H|0)|((b|0)<=(D|0)&O>>>0>X>>>0|(b|0)<(D|0))|(v>>>0<=S>>>0&(u|0)>=0|(u|0)>0)));g=g+1|0;i=g?i:i+1|0;continue}}}g=d;b=1;while(1){c=b;b=b+1|0;if(j>>>0>1<>>0){continue}break}y[f+48>>2]=0;y[f+52>>2]=0;x[f+80>>1]=0;y[f+56>>2]=0;y[f+60>>2]=0;y[f+64>>2]=0;y[f+68>>2]=0;y[f+72>>2]=0;y[f+84>>2]=0;y[f+88>>2]=0;y[f+92>>2]=0;y[f+96>>2]=0;y[f+100>>2]=0;y[f+32>>2]=e;y[f+28>>2]=C;w[f+24|0]=F;y[f+36>>2]=n;w[f+26|0]=K;w[f+25|0]=0;w[f+27|0]=s;kb:{if(s>>>0>=2){x[f+40>>1]=65282;break kb}x[f+40>>1]=65283;if(s){break kb}x[f+46>>1]=65278;x[f+42>>1]=65533;x[f+44>>1]=65026}b=c&255;c=gb(f+356|0,b);h=y[c>>2];n=b-1|0;d=n;lb:{mb:{while(1){if((d|0)<0){break mb}y[f+352>>2]=0;y[f+348>>2]=0;y[f+340>>2]=g;y[f+344>>2]=l;y[f+336>>2]=Q;y[f+332>>2]=f+352;b=Wb(f+24|0,f+332|0);y[f+348>>2]=0;while(1){if((b|0)==3){b=Vb(f+24|0,f+332|0);continue}break}y[f+68>>2]=0;y[f+72>>2]=0;y[f+60>>2]=0;y[f+64>>2]=0;y[f+52>>2]=0;y[f+56>>2]=0;b=y[f+352>>2];nb:{if(!b){break nb}y[f+352>>2]=0;i=h+(d<<2)|0;oa(i,b);if((d|0)>=(n|0)){break nb}fb(y[i>>2],0,0,0,0,y[i+4>>2],2)}d=d-1|0;la(f+352|0);if(b){continue}break}b=0;break lb}b=uc(q,c)}$a(c);la(f+16|0);y[f+24>>2]=0;oa(m- -64|0,b);la(f+24|0);if(!y[m+64>>2]){break fb}ta(y[a+4>>2]);b=y[a+4>>2];c=y[b+8>>2];if(c>>>0<=4294967293){g=b;c=c+2|0;b=y[b+4>>2];y[g+8>>2]=b>>>0>c>>>0?c:b}ua(f+308|0);Ha(f)}if((z[m+4|0]&63)==20){b=0;break db}ob:{if(z[a+50|0]){break ob}b=y[y[a+24>>2]-4>>2];if(z[b+17|0]!=1){break ob}c=k+y[f+324>>2]|0;d=y[a+32>>2];if((c|0)<=y[d+12>>2]){break ob}eb(d,c,z[b+16|0])}b=y[f+320>>2];g=b;h=b>>31;b=y[f+324>>2];c=b;d=b>>31;b=z[f+328|0];fb(y[a+32>>2],g,h,c,d,y[m+64>>2],(b&7)==4?4:b&3);b=0;oa(m- -64|0,0);break db}ua(f+308|0);Ha(f)}b=1}ma(q);break u}b=1;if(z[a+49|0]!=1){break u}g=a+76|0;d=y[a+76>>2];if(!d){c=qb();y[f+24>>2]=c;pb:{qb:{if(tb(a,a+88|0)){break qb}if(va(y[a+4>>2],f+332|0)){break qb}d=y[a+92>>2];if((d|0)<0){break qb}i=y[a+88>>2];if((i|0)<0){break qb}y[c+8>>2]=d;y[c+4>>2]=i;d=z[f+332|0];i=d&1;w[c|0]=i;h=d>>>1&3;w[c+3|0]=h;w[c+1|0]=d>>>3&1;if(i){break pb}i=c+16|0;d=0;if(!h){while(1){if((d|0)==8){break pb}h=d+i|0;d=d+1|0;if(!va(y[a+4>>2],h)){continue}break qb}}while(1){if((d|0)==2){break pb}h=d+i|0;d=d+1|0;if(!va(y[a+4>>2],h)){continue}break}}ua(f+24|0);break u}w[c+2|0]=0;y[f+24>>2]=0;Va(g,c);ua(f+24|0);d=y[g>>2]}y[m+52>>2]=1;rb:{if(z[d|0]==1){hb(d,m- -64|0,y[a+4>>2]);if(!y[m+64>>2]){Va(g,0);break D}ta(y[a+4>>2]);break rb}if(y[a+60>>2]==y[a+64>>2]){b=z[d+3|0];kb(a+60|0,b?(b|0)==1?8192:1024:65536)}c=a+72|0;h=f;b=y[a+72>>2];if(b){d=b}else{d=Ra(y[a+4>>2]);y[f+24>>2]=0;Va(c,d);ua(f+24|0);d=y[a+72>>2]}y[h+28>>2]=d;d=m- -64|0;y[f+24>>2]=d;h=y[a+64>>2];i=y[a+60>>2];y[f+40>>2]=0;y[f+32>>2]=i;y[f+36>>2]=h-i>>3;i=y[a+76>>2];sb:{if(!b){b=Wb(i,f+24|0);break sb}b=Vb(i,f+24|0)}y[a+56>>2]=b;if((b|0)==3){if((z[m+4|0]&63)==36){break B}tb:{if(z[a+50|0]){break tb}b=y[y[a+24>>2]-4>>2];if(z[b+17|0]!=1){break tb}c=y[a+92>>2]+y[a+100>>2]|0;g=y[a+32>>2];if((c|0)<=y[g+12>>2]){break tb}eb(g,c,z[b+16|0])}b=y[a+76>>2];c=y[b+64>>2]+y[a+96>>2]|0;g=c;i=c>>31;c=y[b+68>>2]+y[a+100>>2]|0;h=b- -64|0;b=z[a+104|0];tc(y[a+32>>2],g,i,c,c>>31,y[d>>2],h,(b&7)==4?4:b&3);break B}b=y[a+76>>2];y[b+44>>2]=0;y[b+48>>2]=0;y[b+36>>2]=0;y[b+40>>2]=0;y[b+28>>2]=0;y[b+32>>2]=0;Va(c,0);y[a+64>>2]=y[a+60>>2];if(!y[d>>2]){y[a+56>>2]=-1;Va(g,0);break D}ta(y[a+4>>2]);b=y[a+4>>2];c=y[b+8>>2];if(c>>>0>4294967293){break rb}d=b;c=c+2|0;b=y[b+4>>2];y[d+8>>2]=b>>>0>c>>>0?c:b}if((z[m+4|0]&63)!=36){ub:{if(z[a+50|0]){break ub}b=y[y[a+24>>2]-4>>2];if(z[b+17|0]!=1){break ub}c=y[a+92>>2]+y[a+100>>2]|0;d=y[a+32>>2];if((c|0)<=y[d+12>>2]){break ub}eb(d,c,z[b+16|0])}b=y[a+76>>2];c=y[b+64>>2]+y[a+96>>2]|0;d=c;i=c>>31;c=y[b+68>>2]+y[a+100>>2]|0;h=b- -64|0;b=z[a+104|0];tc(y[a+32>>2],d,i,c,c>>31,y[m+64>>2],h,(b&7)==4?4:b&3);oa(m- -64|0,0)}b=0;Va(g,0);break u}b=1;if(z[a+49|0]!=1){break u}if(tb(a,f+24|0)){break u}if(va(y[a+4>>2],f+352|0)){break u}i=y[f+24>>2];if(i-1>>>0>65534){break u}g=y[f+28>>2];if(g-1>>>0>65534){break u}d=0;y[f+312>>2]=0;c=db();y[f+356>>2]=c;y[c+8>>2]=g;y[c+4>>2]=i;b=z[f+352|0];h=b&1;w[c|0]=h;w[c+1|0]=b>>>1&1;vb:{wb:{if(h){break wb}h=c+24|0;while(1){if((d|0)==4){break wb}b=1;k=d+h|0;d=d+1|0;if(!va(y[a+4>>2],k)){continue}break}break vb}h=y[m+8>>2];xb:{if((h|0)>0){d=0;b=1;while(1){if((d|0)==(h|0)){break vb}i=Ma(a,y[y[m+12>>2]>>2]);if(!i){break vb}k=(z[i+4|0]&63)-4|0;k=(k<<6|(k&252)>>>2)&255;if(!(1<>>0<=9:0)){d=d+1|0;continue}break}b=i- -64|0;break xb}d=sb(y[a+32>>2],y[f+32>>2],y[f+36>>2],i,g);y[f+332>>2]=0;b=f+312|0;oa(b,d);la(f+332|0)}b=y[b>>2];y[c+12>>2]=0;y[c+16>>2]=0;y[c+20>>2]=b;d=z[c|0];i=d?1024:8192;b=ab(i,8);y[f+336>>2]=i;y[f+332>>2]=b;i=Ra(y[a+4>>2]);y[f>>2]=i;y[m+52>>2]=1;c=cb(c,i,b,d?1024:8192);y[f+16>>2]=0;b=m- -64|0;oa(b,c);la(f+16|0);c=1;yb:{if(!y[m+64>>2]){break yb}ta(y[a+4>>2]);c=y[a+4>>2];d=y[c+8>>2];if(d>>>0<=4294967293){h=c;d=d+2|0;c=y[c+4>>2];y[h+8>>2]=c>>>0>d>>>0?d:c}c=0;if((z[m+4|0]&63)==40){break yb}zb:{if(z[a+50|0]){break zb}c=y[y[a+24>>2]-4>>2];if(z[c+17|0]!=1){break zb}d=g+y[f+36>>2]|0;g=y[a+32>>2];if((d|0)<=y[g+12>>2]){break zb}eb(g,d,z[c+16|0])}c=y[f+32>>2];d=c;i=c>>31;c=y[f+36>>2];g=c;h=c>>31;c=z[f+40|0];fb(y[a+32>>2],d,i,g,h,y[b>>2],(c&7)==4?4:c&3);oa(b,0);c=0}b=c;ua(f);Ha(f+332|0)}Ua(f+356|0);la(f+312|0);break u}c=na(20);y[c+16>>2]=0;y[c+8>>2]=0;y[c+12>>2]=0;y[c>>2]=0;y[c+4>>2]=0;y[f+24>>2]=c;if(ra(y[a+4>>2],c)){break E}if(ra(y[a+4>>2],c+4|0)){break E}if(ra(y[a+4>>2],c+8|0)){break E}if(ra(y[a+4>>2],c+12|0)){break E}if(va(y[a+4>>2],f+356|0)){break E}if(Wa(y[a+4>>2],f+312|0)){break E}w[c+16|0]=z[f+356|0]>>>2&1;g=A[f+312>>1];b=g&32767;x[c+18>>1]=b;w[c+17|0]=g>>>15;d=y[c+4>>2];if((d|0)!=-1){break J}if(g<<16>>16>=0){w[c+17|0]=1}if(z[a+50|0]){break F}d=b;break G}w[a+49|0]=0;break u}c=y[a+4>>2];d=y[c+8>>2];b=d+y[m+28>>2]|0;if(b>>>0>>0){break B}g=c;c=y[c+4>>2];y[g+8>>2]=b>>>0>>0?b:c;break B}c=y[a+4>>2];d=y[c+8>>2];b=d+y[m+28>>2]|0;if(b>>>0>>0){break B}g=c;c=y[c+4>>2];y[g+8>>2]=b>>>0>>0?b:c;break B}y[m+52>>2]=4;k=m+68|0;vb(k,0);g=y[a+4>>2];d=na(44);w[d+1|0]=0;t(d+4|0,0,40);if((va(g,f+356|0)|0)==-1){break I}b=1;i=z[f+356|0];w[d+1|0]=i&1;Ab:{Bb:{if((ra(g,f+332|0)|0)==-1){break Bb}if((ra(g,f+312|0)|0)==-1){break Bb}c=y[f+332>>2];h=y[f+312>>2];if((c|0)>(h|0)){break Bb}l=(i>>>4&7)+1|0;i=(i>>>1&7)+1|0;pb(d,0);y[f+28>>2]=c;w[f+24|0]=1;b=c;while(1){if((Ia(g,i,y[d+8>>2]+(y[d+4>>2]<<3)|0)|0)==-1){break Ab}if((Ia(g,l,y[d+20>>2]+(y[d+4>>2]<<2)|0)|0)==-1){break Ab}n=y[d+20>>2];q=y[d+4>>2]<<2;if(B[n+q>>2]>63){break Ab}y[q+y[d+32>>2]>>2]=b;b=y[n+(y[d+4>>2]<<2)>>2];if((b|0)>31){break Ab}if(z[Na(f+24|0,1<>2];if((h|0)>(b|0)){continue}break}if((Ia(g,i,y[d+8>>2]+(y[d+4>>2]<<3)|0)|0)==-1){break Ab}y[y[d+20>>2]+(y[d+4>>2]<<2)>>2]=32;if((c|0)==-2147483648){break Ab}y[y[d+32>>2]+(y[d+4>>2]<<2)>>2]=c-1;pb(d,1);if((Ia(g,i,y[d+8>>2]+(y[d+4>>2]<<3)|0)|0)==-1){break Ab}y[y[d+20>>2]+(y[d+4>>2]<<2)>>2]=32;y[y[d+32>>2]+(y[d+4>>2]<<2)>>2]=h;pb(d,1);b=y[d+4>>2];if(z[d+1|0]==1){if((Ia(g,i,y[d+8>>2]+(b<<3)|0)|0)==-1){break Ab}b=y[d+4>>2]+1|0;y[d+4>>2]=b}c=y[d+8>>2];if(y[d+12>>2]-c>>3>>>0>>0){break t}c=ob(c,b);w[d|0]=c;y[f+24>>2]=d;b=1;if(!c){break H}b=0;y[f+24>>2]=0;vb(k,d);ta(y[a+4>>2]);break H}w[d|0]=0;y[f+24>>2]=d;break H}break I}if((d|0)!=62){break B}c=y[a+4>>2];d=y[c+8>>2];b=d+y[m+28>>2]|0;if(b>>>0>>0){break B}g=c;c=y[c+4>>2];y[g+8>>2]=b>>>0>>0?b:c;break B}if(!z[a+50|0]){break G}break F}w[d|0]=0;y[f+24>>2]=d;b=1}pa(f+24|0);break u}b=xa(y[c>>2],d);y[f+332>>2]=0;oa(a+32|0,b);la(f+332|0)}if(qa(y[a+32>>2])){break C}y[a+56>>2]=-1}$b(f+24|0)}b=1;break u}Qa(y[a+32>>2],z[c+16|0]);Cb:{Db:{b=y[a+24>>2];h=y[a+28>>2];Eb:{if(b>>>0>>0){y[f+24>>2]=0;y[b>>2]=c;b=b+4|0;break Eb}d=y[a+20>>2];g=b-d|0;k=g>>2;i=k+1|0;if(i>>>0>=1073741824){break Db}b=0;h=h-d|0;l=h>>1;i=h>>>0>=2147483644?1073741823:i>>>0>>0?l:i;if(i){if(i>>>0>=1073741824){break Cb}b=na(i<<2)}y[f+24>>2]=0;h=b+g|0;y[h>>2]=c;c=h-(k<<2)|0;if(g){p(c,d,g)}y[a+28>>2]=(i<<2)+b;b=h+4|0;y[a+24>>2]=b;y[a+20>>2]=c;if(!d){break Eb}ma(d)}w[a+49|0]=1;y[a+24>>2]=b;$b(f+24|0);break B}Ba();o()}jb();o()}b=0;break u}la(f+356|0)}ua(f+312|0);y[f+24>>2]=0;Xb(m+60|0,c);Zb(f+24|0);if(y[m+60>>2]){ta(y[a+4>>2]);b=y[a+4>>2];c=y[b+8>>2];if(c>>>0<=4294967293){d=b;c=c+2|0;b=y[b+4>>2];y[d+8>>2]=b>>>0>c>>>0?c:b}ua(f+16|0);Ha(f+332|0);b=0;break x}ua(f+16|0);Ha(f+332|0)}b=1}ma(g);break u}if(ra(y[a+4>>2],h+16|0)){break v}c=y[a+4>>2];g=y[c>>2];c=y[c+4>>2];if((c|0)<0){break t}d=c>>>27&15;c=((c&134217727)<<5|g>>>27)&-32;if(!d&c>>>0>=2147483648|d){break t}if(!d&B[h+16>>2]>c>>>0){break v}n=0;b=y[m+8>>2];c=(b|0)>0?b:0;d=0;while(1){if((c|0)==(d|0)){i=1;b=0;while(1)if((b|0)==(c|0)){if(!(i&1)){break t}y[h+24>>2]=n;l=Ub(f+356|0,n);k=y[l>>2];Fb:{if((k|0)==y[l+4>>2]){break Fb}b=y[m+8>>2];s=(b|0)>0?b:0;i=1;n=0;c=0;while(1){if((c|0)==(s|0)){break Fb}Gb:{b=Ma(a,y[y[m+12>>2]+(c<<2)>>2]);if(z[b+4|0]&63){break Gb}b=y[b+56>>2];g=y[b+24>>2];d=y[b+28>>2]-g>>2;b=0;while(1){if((b|0)==(d|0)){g=0;if(i&1){b=d+n|0;d=b>>>0>=d>>>0;g=d;n=d?b:0}else{n=0}i=g;break Gb}C=b+n|0;if(!(C>>>0>=b>>>0&i)){break t}y[k+(C<<2)>>2]=y[g+(b<<2)>>2];b=b+1|0;continue}}c=c+1|0;continue}}bb(h+40|0,l);Hb:{Ib:{Jb:{Kb:{if(!z[h|0]){c=y[h+24>>2];b=0;while(1){d=b;b=b+1|0;if(c>>>0>1<>>0){continue}break}break Kb}n=y[h+24>>2];b=0;while(1){if((b|0)!=35){c=b<<3;b=b+1|0;if(!Ia(y[a+4>>2],4,c+(f+24|0)|0)){continue}break Jb}break}if(!ob(f+24|0,35)){break Jb}i=Ed(f+312|0,n);k=y[i>>2];c=0;Lb:while(1){Mb:{Nb:{Ob:{if((c|0)<(n|0)){d=0;y[f+20>>2]=0;w[f+16|0]=1;Pb:while(1){if(wb(y[a+4>>2],f+352|0)){break Nb}g=Tb(f+16|0);if(z[g|0]!=1){break Nb}b=0;C=y[f+20>>2]|y[f+352>>2];s=(C|0)>=0;y[g>>2]=s;j=g;g=s?C:0;y[j+4>>2]=g;if(!s){break t}d=d+1|0;while(1){if((b|0)==35){continue Pb}s=(f+24|0)+(b<<3)|0;if(!(y[s>>2]==(d|0)&(g|0)==y[s+4>>2])){b=b+1|0;continue}break}break}if(b>>>0<=31){y[k+(c<<3)>>2]=b;break Mb}g=y[a+4>>2];Qb:{switch(b-33|0){default:if(Ia(g,2,f+352|0)){break Nb}d=3;break Ob;case 1:if(Ia(g,7,f+352|0)){break Nb}d=11;break Ob;case 0:break Qb}}d=3;if(!Ia(g,3,f+352|0)){break Ob}break Nb}b=y[i+4>>2];if(!ob(k,b-k>>3)){break Nb}y[f+4>>2]=b;y[f>>2]=k;y[f+8>>2]=y[i+8>>2];d=0;y[i+8>>2]=0;y[i>>2]=0;y[i+4>>2]=0;ya(i);if((b|0)==(k|0)){break Ib}ta(y[a+4>>2]);bb(h+28|0,f);ya(f);b=1;if(z[h|0]!=1){break Kb}c=A[f+14>>1];g=c>>>12&3;if((g|0)==2){break Hb}k=c>>>10&3;if((k|0)==2){break Hb}n=c>>>8&3;if((n|0)==2){break Hb}i=c&3;if((i|0)==2){break Hb}s=c>>>6&3;if((s|0)==2){break Hb}Rb:{Sb:{switch(i|0){case 0:i=0;j=sa(a,6);break Rb;case 1:i=0;j=sa(a,7);break Rb;default:break Sb}}C=Ga(a,m,0);if(!C){break Hb}i=1;j=y[C+68>>2]}y[h+60>>2]=j;Tb:{Ub:{switch((c>>>2&3)-1|0){default:j=sa(a,8);break Tb;case 0:j=sa(a,9);break Tb;case 1:j=sa(a,10);break Tb;case 2:break Ub}}C=Ga(a,m,i);if(!C){break Hb}i=i+1|0;j=y[C+68>>2]}y[h+64>>2]=j;Vb:{Wb:{switch((c>>>4&3)-1|0){default:j=sa(a,11);break Vb;case 0:j=sa(a,12);break Vb;case 1:j=sa(a,13);break Vb;case 2:break Wb}}C=Ga(a,m,i);if(!C){break Hb}i=i+1|0;j=y[C+68>>2]}y[h+68>>2]=j;Xb:{Yb:{switch(s|0){case 0:j=sa(a,14);break Xb;case 1:j=sa(a,15);break Xb;default:break Yb}}s=Ga(a,m,i);if(!s){break Hb}i=i+1|0;j=y[s+68>>2]}y[h+72>>2]=j;Zb:{_b:{switch(n|0){case 0:n=sa(a,14);break Zb;case 1:n=sa(a,15);break Zb;default:break _b}}n=Ga(a,m,i);if(!n){break Hb}i=i+1|0;n=y[n+68>>2]}y[h+76>>2]=n;$b:{ac:{switch(k|0){case 0:n=sa(a,14);break $b;case 1:n=sa(a,15);break $b;default:break ac}}k=Ga(a,m,i);if(!k){break Hb}i=i+1|0;n=y[k+68>>2]}y[h+80>>2]=n;bc:{cc:{switch(g|0){case 0:g=sa(a,14);break bc;case 1:g=sa(a,15);break bc;default:break cc}}g=Ga(a,m,i);if(!g){break Hb}i=i+1|0;g=y[g+68>>2]}y[h+84>>2]=g;if(!(c&16384)){fa=h,ga=sa(a,1),y[fa+88>>2]=ga;break Kb}c=Ga(a,m,i);if(!c){break Hb}y[h+88>>2]=y[c+68>>2];break Kb}s=y[f+352>>2]+d|0;if((s|0)<=0){break Mb}g=c+s|0;if((g|0)>(n|0)){break Nb}d=0;b=(b|0)==32&(c|0)>0;c=k+(c<<3)|0;C=c-8|0;while(1)if((d|0)==(s|0)){c=g;continue Lb}else{y[c+(d<<3)>>2]=b?y[C>>2]:0;d=d+1|0;continue}}y[f+8>>2]=0;y[f>>2]=0;y[f+4>>2]=0;ya(i);break Ib}c=c+1|0;continue}}i=0;y[f+312>>2]=0;y[f+316>>2]=0;if(z[h+1|0]==1){b=z[h+2|0];c=b?1024:8192;g=ab(c,8);y[f+24>>2]=0;rc(f+312|0,g);y[f+316>>2]=c;y[f+28>>2]=0;Ha(f+24|0);i=b?1024:8192}y[m+52>>2]=1;dc:{ec:{fc:{gc:{if(z[h|0]==1){b=Dd(h,y[a+4>>2],y[f+312>>2],i);y[f+24>>2]=0;oa(m- -64|0,b);la(f+24|0);if(!y[m+64>>2]){break ec}ta(y[a+4>>2]);break gc}c=Ra(y[a+4>>2]);y[f>>2]=c;b=Cd(f+24|0,d&255);c=Bd(h,c,y[f+312>>2],i,b);y[f+16>>2]=0;oa(m- -64|0,c);la(f+16|0);if(!y[m+64>>2]){break fc}ta(y[a+4>>2]);c=y[a+4>>2];d=y[c+8>>2];if(d>>>0<=4294967293){g=c;d=d+2|0;c=y[c+4>>2];y[g+8>>2]=c>>>0>d>>>0?d:c}qc(b);ua(f)}b=0;if((z[m+4|0]&63)==4){break dc}hc:{if(z[a+50|0]){break hc}b=y[y[a+24>>2]-4>>2];if(z[b+17|0]!=1){break hc}c=q+y[f+344>>2]|0;d=y[a+32>>2];if((c|0)<=y[d+12>>2]){break hc}eb(d,c,z[b+16|0])}b=y[f+340>>2];g=b;h=b>>31;b=y[f+344>>2];c=b;d=b>>31;b=z[f+348|0];fb(y[a+32>>2],g,h,c,d,y[m+64>>2],(b&7)==4?4:b&3);oa(m- -64|0,0);b=0;break dc}qc(b);ua(f)}b=1}Ha(f+312|0);break Hb}y[f+8>>2]=0;y[f>>2]=0;y[f+4>>2]=0}ya(f);b=1}ya(l);break v}else{d=Ma(a,y[y[m+12>>2]+(b<<2)>>2]);if(!(z[d+4|0]&63)){g=0;if(i&1){d=y[d+56>>2];d=y[d+28>>2]-y[d+24>>2]>>2;g=d+n|0;i=g;g=d>>>0<=g>>>0;n=g?i:0}else{n=0}i=g}b=b+1|0;continue}}b=1;g=d<<2;d=d+1|0;if(Ma(a,y[g+y[m+12>>2]>>2])){continue}break}}pc(f+308|0)}Y=f+368|0;break s}o()}if(y[a+56>>2]!=3){break r}b=y[a+4>>2];c=y[b+8>>2];b=y[b+4>>2];if(c>>>0>b>>>0){break e}if((b|0)!=(c|0)){continue}break}y[a+52>>2]=2;break c}ic:{switch(b|0){default:ib(R,0);break i;case 0:break ic;case 2:break d}}g=y[R>>2];b=y[g+28>>2];if((b|0)==-1){break g}c=y[a+84>>2];b=b+c|0;if(b>>>0>=c>>>0){break h}}aa=1;break c}y[a+84>>2]=b;d=y[a+4>>2];c=y[d+4>>2];y[d+8>>2]=b>>>0>>0?b:c;break f}d=y[a+4>>2];b=y[d+8>>2];if(b>>>0>4294967291){break f}b=b+4|0;c=y[d+4>>2];y[d+8>>2]=b>>>0>>0?b:c}b=y[a+12>>2];i=y[a+16>>2];jc:{if(b>>>0>>0){y[R>>2]=0;y[b>>2]=g;b=b+4|0;break jc}c=y[a+8>>2];b=b-c|0;k=b>>2;d=k+1|0;if(d>>>0>=1073741824){break b}i=i-c|0;h=i>>1;d=i>>>0>=2147483644?1073741823:d>>>0>>0?h:d;if(d){if(d>>>0>=1073741824){break a}i=na(d<<2)}else{i=0}y[a+80>>2]=0;h=b+i|0;y[h>>2]=g;g=h-(k<<2)|0;if(b){p(g,c,b)}y[a+16>>2]=i+(d<<2);b=h+4|0;y[a+12>>2]=b;y[a+8>>2]=g;if(c){ma(c)}d=y[a+4>>2]}y[a+12>>2]=b;b=y[d+8>>2];i=y[d+4>>2];if(b>>>0<=i>>>0){continue}break}}o()}ib(R,0)}Y=J+16|0;return aa}Ba();o()}jb();o()}function cb(a,b,c,d){var e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,x=0,A=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0;e=Y-96|0;Y=e;f=y[a+8>>2];g=y[a+4>>2];a:{if(!(f-1>>>0<65535&g-1>>>0<=65534)){n=xa(g,f);break a}b:{c:{d:{e:{f:{g:{h:{i:{j:{k:{if(!z[a|0]){l:{if(z[a+24|0]!=255|z[a+25|0]!=255|(z[a+26|0]!=255|z[a+27|0]!=255)){break l}if(y[a+12>>2]){break l}k=y[a+20>>2];if((g|0)!=y[k+8>>2]){break l}if(!qa(k)){break b}p=y[a+4>>2];f=y[a+8>>2];n=xa(p,f);y[e+44>>2]=n;if(!qa(n)){break d}q=d;A=c;c=y[a+20>>2];G=y[c+8>>2];x=y[c+12>>2];c=y[a+16>>2];if(!((1-x|0)<=(c|0)&(c|0)<(x|0))){y[a+16>>2]=0}K=(f|0)>0?f:0;L=x+1|0;E=x-1|0;H=A+128|0;while(1){if((l|0)!=(K|0)){if(z[a+1|0]==1){if(z[b|0]){break d}if((d|0)==16|d>>>0<16){break k}C=ka(b,H)^C}ja(e+32|0,n,l);i=0;m:{if(!l){u=0;s=0;k=0;break m}ja(e+72|0,n,l-1|0);s=y[e+76>>2];if(!s){break k}u=y[e+72>>2];k=z[u|0]<<4}c=y[a+16>>2];y[e+88>>2]=0;y[e+92>>2]=0;y[e+80>>2]=0;y[e+84>>2]=0;y[e+72>>2]=0;y[e+76>>2]=0;y[e+16>>2]=0;y[e+8>>2]=0;y[e+12>>2]=0;g=l-c|0;if(!((g|0)<=0|(g|0)>(x|0))){ja(e+48|0,y[a+20>>2],g-1|0);c=y[e+52>>2];y[e+76>>2]=c;f=y[e+48>>2];y[e+72>>2]=f;if(!c){break k}c=z[f|0];y[e+8>>2]=c;i=c&192}j=0;c=0;n:{if((g|0)<0){break n}c=0;if((g|0)>=(x|0)){break n}ja(e+48|0,y[a+20>>2],g);c=y[e+52>>2];y[e+84>>2]=c;f=y[e+48>>2];y[e+80>>2]=f;if(!c){break k}c=z[f|0];y[e+12>>2]=c;c=c>>>3&24}if(!((g|0)<-1|(g|0)>=(E|0))){ja(e+48|0,y[a+20>>2],g+1|0);g=y[e+52>>2];y[e+92>>2]=g;f=y[e+48>>2];y[e+88>>2]=f;if(!g){break k}g=z[f|0];y[e+16>>2]=g;j=g>>>6|0}o:{if(!C){h=c|(k&3072|i)|j;r=0;v=y[e+32>>2];I=y[e+36>>2];m=0;p:while(1){if((m|0)>=(p|0)){break o}q:{if(!l){break q}k=k<<8;if((p|0)<=(m+8|0)){break q}c=(m>>>3|0)+1|0;if(c>>>0>=s>>>0){break k}k=z[c+u|0]<<4|k}r:{if((L+y[a+16>>2]|0)<(l|0)){y[e+16>>2]=0;y[e+8>>2]=0;y[e+12>>2]=0;g=0;i=0;c=0;break r}c=(m>>>3|0)+1|0;i=e+8|0;f=0;g=(G|0)<=(m+8|0);while(1){if((f|0)!=24){j=(e+72|0)+f|0;t=y[j+4>>2];s:{if(!t){break s}D=y[i>>2]<<8;y[i>>2]=D;if(g){break s}if(c>>>0>=t>>>0){break k}y[i>>2]=D|z[c+y[j>>2]|0]}i=i+4|0;f=f+8|0;continue}break}g=y[e+12>>2];i=y[e+8>>2];c=y[e+16>>2]}f=0;j=r+p|0;j=(j|0)>0?j:0;D=(j|0)>=8?8:j;j=0;while(1){if((f|0)==(D|0)){c=m>>>3|0;if(c>>>0>=I>>>0){break k}w[c+v|0]=j;r=r-8|0;m=m+8|0;continue p}if(h>>>0>=q>>>0){break k}F=ka(b,A+(h<<3)|0);t=7-f|0;j=F<>>13-f&1|(g>>>10-f&8|(i>>>t&64|(k>>>t&1024|h<<1&6582|F<<9)));f=f+1|0;continue}}}Jb(e+48|0,a,l);i=c|(k&3072|i)|j;c=0;v=y[e+32>>2];I=y[e+36>>2];D=y[e+60>>2];F=y[e+56>>2];r=0;while(1){if((c|0)>=(p|0)){break o}t:{if(!l){g=c+8|0;break t}k=k<<8;g=c+8|0;if((p|0)<=(g|0)){break t}f=(c>>>3|0)+1|0;if(f>>>0>=s>>>0){break k}k=z[f+u|0]<<4|k}j=c>>>3|0;m=j+1|0;f=0;h=e+8|0;while(1){u:{if((f|0)==24){f=0;m=r+p|0;m=(m|0)>0?m:0;J=(m|0)>=8?8:m;N=y[e+16>>2];M=y[e+12>>2];O=y[e+8>>2];m=0;while(1){if((f|0)==(J|0)){break u}t=y[y[a+20>>2]+8>>2];P=c+f|0;h=ia(t,P,F,D);v:{if(z[a+1|0]==1){if(Ib(t,P,h,e+48|0)){break v}}if(z[b|0]){break d}if(i>>>0>=q>>>0){break k}h=ka(b,A+(i<<3)|0)}t=7-f|0;i=N>>>13-f&1|(M>>>10-f&8|(k>>>t&1024|i<<1&6582|h<<9|O>>>t&64));f=f+1|0;m=h<>2];w:{if(!J){break w}N=y[h>>2]<<8;y[h>>2]=N;if((g|0)>=(G|0)){break w}if(m>>>0>=J>>>0){break k}y[h>>2]=N|z[m+y[t>>2]|0]}h=h+4|0;f=f+8|0;continue}break}if(j>>>0>=I>>>0){break k}w[j+v|0]=m;r=r-8|0;c=g;continue}}l=l+1|0;continue}break}y[e+44>>2]=0;break c}n=xa(g,f);y[e+44>>2]=n;if(!qa(n)){break f}Qa(n,0);A=c;u=c+128|0;c=0;while(1){if(B[a+8>>2]>c>>>0){if(z[a+1|0]==1){if(z[b|0]){break f}if((d|0)==16|d>>>0<16){break k}m=ka(b,u)^m}ja(e+32|0,n,c);f=e+72|0;ja(f,n,c-1|0);g=y[e+76>>2];q=y[e+72>>2];dd(f,a,c);f=0;k=y[n+8>>2];f=!g|(k|0)<2?f:z[q|0]>>>6&1;if(!(!g|(k|0)<=0)){f=z[q|0]>>>6&2|f}y[e+8>>2]=f;y[e+12>>2]=0;r=y[a+20>>2];f=y[r+8>>2];k=y[a+12>>2];i=1-k|0;l=y[e+72>>2];j=y[e+76>>2];h=0-k|0;Q=e,R=ia(f,i,l,j)|ia(f,h,l,j)<<1,y[Q+16>>2]=R;l=y[e+80>>2];j=y[e+84>>2];s=ia(f,i,l,j);p=s|ia(f,h,l,j)<<1;s=k^-1;Q=e,R=p|ia(f,s,l,j)<<2,y[Q+20>>2]=R;j=i;k=y[e+88>>2];i=y[e+92>>2];l=ia(f,j,k,i);Q=e,R=l|ia(f,h,k,i)<<1|ia(f,s,k,i)<<2,y[Q+24>>2]=R;f=e+48|0;ja(f,r,w[a+27|0]+(c-y[a+16>>2]|0)|0);k=y[e+52>>2];h=y[e+48>>2];ja(f,n,w[a+25|0]+c|0);f=y[e+52>>2];i=y[e+48>>2];x:{if(!m){l=q;q=g;j=i;i=f;g=k;r=h;f=0;k=y[e+32>>2];s=y[e+36>>2];while(1){if(B[a+4>>2]<=f>>>0){break x}p=e+8|0;h=cd(a,y[n+8>>2],p,f,r,g,j,i);if(z[b|0]){break f}if(d>>>0<=h>>>0){break k}bd(a,n,p,f,ka(b,A+(h<<3)|0),e+72|0,l,q,k,s);f=f+1|0;continue}}Jb(e+48|0,a,c);l=q;q=g;j=i;i=f;g=k;r=h;f=0;k=y[e+32>>2];s=y[e+36>>2];p=y[e+60>>2];C=y[e+56>>2];while(1){if(B[a+4>>2]<=f>>>0){break x}x=y[y[a+20>>2]+8>>2];h=ia(x,f,C,p);y:{if(z[a+1|0]==1){if(Ib(x,f,h,e+48|0)){break y}}h=cd(a,y[n+8>>2],e+8|0,f,r,g,j,i);if(z[b|0]==1){break f}if(d>>>0<=h>>>0){break k}h=ka(b,A+(h<<3)|0)}bd(a,n,e+8|0,f,h,e+72|0,l,q,k,s);f=f+1|0;continue}}c=c+1|0;continue}break}y[e+44>>2]=0;break e}z:{if(y[a+12>>2]){break z}k=y[a+20>>2];if((g|0)!=y[k+8>>2]){break z}if(!qa(k)){break b}q=y[a+4>>2];f=y[a+8>>2];n=xa(q,f);y[e+44>>2]=n;if(!qa(n)){break h}A=d;m=c;c=y[a+20>>2];x=y[c+8>>2];p=y[c+12>>2];c=y[a+16>>2];if(!((1-p|0)<=(c|0)&(c|0)<(p|0))){y[a+16>>2]=0}t=(f|0)>0?f:0;K=p-1|0;L=m- -64|0;while(1){A:{B:{if((l|0)!=(t|0)){if(z[a+1|0]==1){if(z[b|0]){break h}if((d|0)==8|d>>>0<8){break k}G=ka(b,L)^G}ja(e+32|0,n,l);i=0;C:{if(!l){C=0;s=0;k=0;break C}ja(e+72|0,n,l-1|0);s=y[e+76>>2];if(!s){break k}C=y[e+72>>2];k=z[C|0]<<1}c=y[a+16>>2];y[e+88>>2]=0;y[e+92>>2]=0;y[e+80>>2]=0;y[e+84>>2]=0;y[e+72>>2]=0;y[e+76>>2]=0;y[e+16>>2]=0;y[e+8>>2]=0;y[e+12>>2]=0;g=l-c|0;if(!((g|0)<=0|(g|0)>(p|0))){ja(e+48|0,y[a+20>>2],g-1|0);c=y[e+52>>2];y[e+76>>2]=c;f=y[e+48>>2];y[e+72>>2]=f;if(!c){break k}c=z[f|0];y[e+8>>2]=c;i=c>>>2&32}j=0;c=0;D:{if((g|0)<0){break D}c=0;if((g|0)>=(p|0)){break D}ja(e+48|0,y[a+20>>2],g);c=y[e+52>>2];y[e+84>>2]=c;f=y[e+48>>2];y[e+80>>2]=f;if(!c){break k}c=z[f|0];y[e+12>>2]=c;c=c>>>4&12}if(!((g|0)<-1|(g|0)>=(K|0))){ja(e+48|0,y[a+20>>2],g+1|0);g=y[e+52>>2];y[e+92>>2]=g;f=y[e+48>>2];y[e+88>>2]=f;if(!g){break k}g=z[f|0];y[e+16>>2]=g;j=g>>>6|0}if(G){break B}h=c|(k&384|i)|j;f=0;u=y[e+32>>2];E=y[e+36>>2];r=0;E:while(1){if((f|0)>=(q|0)){break A}F:{if(!l){c=f+8|0;break F}k=k<<8;c=f+8|0;if((q|0)<=(c|0)){break F}g=(f>>>3|0)+1|0;if(g>>>0>=s>>>0){break k}k=z[g+C|0]<<1|k}g=f>>>3|0;j=g+1|0;i=e+8|0;f=0;while(1){if((f|0)==24){f=0;i=r+q|0;i=(i|0)>0?i:0;j=(i|0)>=8?8:i;H=y[e+16>>2];v=y[e+12>>2];I=y[e+8>>2];i=0;while(1){if((f|0)==(j|0)){if(g>>>0>=E>>>0){break k}w[g+u|0]=i;r=r-8|0;f=c;continue E}if(h>>>0>=A>>>0){break k}D=ka(b,m+(h<<3)|0);F=7-f|0;i=D<>>13-f&1|(v>>>11-f&4|(I>>>9-f&32|(k>>>F&128|h<<1&794|D<<6)));f=f+1|0;continue}}H=(e+72|0)+f|0;v=y[H+4>>2];G:{if(!v){break G}I=y[i>>2]<<8;y[i>>2]=I;if((c|0)>=(x|0)){break G}if(j>>>0>=v>>>0){break k}y[i>>2]=I|z[j+y[H>>2]|0]}i=i+4|0;f=f+8|0;continue}}}y[e+44>>2]=0;break g}Jb(e+48|0,a,l);i=c|(k&384|i)|j;c=0;E=y[e+32>>2];H=y[e+36>>2];v=y[e+60>>2];I=y[e+56>>2];u=0;while(1){if((c|0)>=(q|0)){break A}H:{if(!l){g=c+8|0;break H}k=k<<8;g=c+8|0;if((q|0)<=(g|0)){break H}f=(c>>>3|0)+1|0;if(f>>>0>=s>>>0){break k}k=z[f+C|0]<<1|k}r=c>>>3|0;j=r+1|0;f=0;h=e+8|0;while(1){I:{if((f|0)==24){f=0;h=q+u|0;h=(h|0)>0?h:0;D=(h|0)>=8?8:h;F=y[e+16>>2];J=y[e+12>>2];N=y[e+8>>2];j=0;while(1){if((f|0)==(D|0)){break I}M=y[y[a+20>>2]+8>>2];O=c+f|0;h=ia(M,O,I,v);J:{if(z[a+1|0]==1){if(Ib(M,O,h,e+48|0)){break J}}if(z[b|0]){break h}if(i>>>0>=A>>>0){break k}h=ka(b,m+(i<<3)|0)}M=7-f|0;i=F>>>13-f&1|(J>>>11-f&4|(N>>>9-f&32|(k>>>M&128|i<<1&794|h<<6)));f=f+1|0;j=h<>2];K:{if(!F){break K}J=y[h>>2]<<8;y[h>>2]=J;if((g|0)>=(x|0)){break K}if(j>>>0>=F>>>0){break k}y[h>>2]=J|z[j+y[D>>2]|0]}h=h+4|0;f=f+8|0;continue}break}if(r>>>0>=H>>>0){break k}w[r+E|0]=j;u=u-8|0;c=g;continue}}l=l+1|0;continue}}n=xa(g,f);y[e+32>>2]=n;if(!qa(n)){break j}Qa(n,0);g=c;t=c- -64|0;while(1){if(B[a+8>>2]>u>>>0){if(z[a+1|0]==1){if(z[b|0]){break j}if((d|0)==8|d>>>0<8){break k}r=ka(b,t)^r}ja(e+8|0,n,u);c=e+72|0;ja(c,n,u-1|0);k=y[e+76>>2];A=y[e+72>>2];dd(c,a,u);h=0;c=y[n+8>>2];h=!k|(c|0)<2?h:z[A|0]>>>6&1;h=!k|(c|0)<=0?h:z[A|0]>>>6&2|h;c=y[y[a+20>>2]+8>>2];m=y[a+12>>2];f=0-m|0;p=y[e+72>>2];C=y[e+76>>2];ia(c,f,p,C);l=ia(c,f,p,C);j=1-m|0;q=y[e+80>>2];s=y[e+84>>2];i=ia(c,j,q,s)|ia(c,f,q,s)<<1|ia(c,m^-1,q,s)<<2;x=y[e+88>>2];G=y[e+92>>2];j=ia(c,j,x,G)|ia(c,f,x,G)<<1;L:{if(!r){m=0;K=y[e+12>>2];L=y[e+8>>2];f=0;while(1){if(B[a+4>>2]<=f>>>0){break L}if(z[b|0]){break j}c=i<<2|l<<5|m<<6|h<<7|j;if(c>>>0>=d>>>0){break k}m=ka(b,g+(c<<3)|0);La(y[n+8>>2],f,L,K,m);l=y[a+12>>2];h=ia(y[n+8>>2],f+2|0,A,k)|h<<1&6;c=y[y[a+20>>2]+8>>2];E=f-l|0;l=ia(c,E+1|0,p,C);E=E+2|0;i=ia(c,E,q,s)|i<<1&6;j=ia(c,E,x,G)|j<<1&2;f=f+1|0;continue}}Jb(e+48|0,a,u);c=0;K=y[e+12>>2];L=y[e+8>>2];E=y[e+60>>2];H=y[e+56>>2];f=0;while(1){if(B[a+4>>2]<=f>>>0){break L}v=y[y[a+20>>2]+8>>2];m=ia(v,f,H,E);M:{if(z[a+1|0]==1){if(Ib(v,f,m,e+48|0)){break M}}if(z[b|0]==1){break j}c=i<<2|l<<5|c<<6|h<<7|j;if(c>>>0>=d>>>0){break k}m=ka(b,g+(c<<3)|0)}La(y[n+8>>2],f,L,K,m);l=y[a+12>>2];h=ia(y[n+8>>2],f+2|0,A,k)|h<<1&6;c=y[y[a+20>>2]+8>>2];v=f-l|0;l=ia(c,v+1|0,p,C);v=v+2|0;i=ia(c,v,q,s)|i<<1&6;j=ia(c,v,x,G)|j<<1&2;f=f+1|0;c=m;continue}}u=u+1|0;continue}break}y[e+32>>2]=0;break i}o()}n=0}la(e+32|0);break a}n=0}la(e+44|0);break a}n=0}la(e+44|0);break a}n=0}la(e+44|0);break a}}Y=e+96|0;return n}function Zc(a,b,c,d,e,f,g,h){var i=0,j=0,k=0,l=0,m=0,n=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,x=0,A=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0;i=Y-32|0;Y=i;j=d-(c>>>0<1048577)|0;l=f-(e>>>0<1048577)|0;a:{if((j|0)==-1&c-1048577>>>0<4292870143|(j|0)!=-1|((l|0)==-1&e-1048577>>>0<4292870143|(l|0)!=-1)){break a}p=y[h>>2];q=y[h+8>>2];j=y[h+4>>2];m=y[h+12>>2];y[i+28>>2]=y[b+8>>2];w[i+24|0]=1;l=Yc(i+24|0,c);v=z[l|0];k=y[i+28>>2];h=y[b+12>>2];y[l>>2]=1;y[l+4>>2]=h;l=Yc(l,e);h=m-j|0;m=y[i+28>>2];x=z[l|0]?(h|0)<(m|0)?h:m:h;l=0-e&f>>31;if((x|0)<=(l|0)){break a}h=q-p|0;m=v?(h|0)<(k|0)?h:k:h;h=0-c&d>>31;if((m|0)<=(h|0)){break a}b:{c:{p=h+p|0;if((p|0)<0){break c}c=!!c&(d|0)>=0|(d|0)>0?c:0;n=c+(m-h|0)|0;D=-1<<0-n;E=-1>>>c|0;v=j+l|0;k=h&31;j=x-l|0;q=c&31;d=!!e&(f|0)>=0|(f|0)>0?e:0;l=c>>>5|0;p=p>>>5|0;if((c^n-1)>>>0<=31){c=D&E;if((h^m-1)>>>0>=32){h=0;j=(j|0)>0?j:0;k=k-q|0;q=32-k|0;m=p<<2;while(1){if((h|0)==(j|0)){break a}f=i+8|0;Ca(f,a,h+v|0);e=y[i+12>>2];n=y[i+8>>2];Ca(f,b,d+h|0);if(!e){break b}f=y[i+12>>2];if(!f){break b}if(e>>>0

>>0){break c}y[i+20>>2]=e-p;y[i+16>>2]=m+n;e=_a(i+16|0);if(!y[i+20>>2]|f>>>0>>0|(f|0)==(l|0)){break c}f=y[i+8>>2]+(l<<2)|0;n=(te(e,24)&16711935|te(e&16711935,8))<>2]>>2];n=n|(te(e&16711935,8)|te(e,24)&16711935)>>>q;e=y[f>>2];e=Oa(g,n,te(e&16711935,8)|te(e,24)&16711935,c);I=f,J=te(e&16711935,8)|te(e,24)&16711935,y[I>>2]=J;h=h+1|0;continue}}h=0;f=(j|0)>0?j:0;if(k>>>0>q>>>0){k=k-q|0;q=p<<2;while(1){if((f|0)==(h|0)){break a}j=i+16|0;Ca(j,a,h+v|0);e=y[i+20>>2];m=y[i+16>>2];Ca(j,b,d+h|0);if(!e){break b}j=y[i+20>>2];if(!j){break b}if((e|0)==(p|0)|e>>>0

>>0|((j|0)==(l|0)|j>>>0>>0)){break c}e=y[i+16>>2]+(l<<2)|0;n=e;j=y[m+q>>2];j=(te(j&16711935,8)|te(j,24)&16711935)<>2];e=Oa(g,j,te(e&16711935,8)|te(e,24)&16711935,c);I=n,J=te(e&16711935,8)|te(e,24)&16711935,y[I>>2]=J;h=h+1|0;continue}}k=q-k|0;q=p<<2;while(1){if((f|0)==(h|0)){break a}j=i+16|0;Ca(j,a,h+v|0);e=y[i+20>>2];m=y[i+16>>2];Ca(j,b,d+h|0);if(!e){break b}j=y[i+20>>2];if(!j){break b}if((e|0)==(p|0)|e>>>0

>>0|((j|0)==(l|0)|j>>>0>>0)){break c}e=y[i+16>>2]+(l<<2)|0;n=e;j=y[m+q>>2];j=(te(j&16711935,8)|te(j,24)&16711935)>>>k|0;e=y[e>>2];e=Oa(g,j,te(e&16711935,8)|te(e,24)&16711935,c);I=n,J=te(e&16711935,8)|te(e,24)&16711935,y[I>>2]=J;h=h+1|0;continue}}x=n&31;A=(y[a+16>>2]>>>2)-(h>>>5)|0;if(k>>>0<=q>>>0){if((k|0)==(q|0)){k=0;e=((n|0)/32|0)-(c+31>>>5|0)|0;if((e|0)<0){break c}A=(j|0)>0?j:0;t=p<<2;d:while(1){if((k|0)==(A|0)){break a}f=i+8|0;Ca(f,a,k+v|0);c=y[i+12>>2];j=y[i+8>>2];Ca(f,b,d+k|0);if(!c){break b}h=y[i+12>>2];if(!h){break b}if(c>>>0

>>0){break c}y[i+20>>2]=c-p;y[i+16>>2]=j+t;if(h>>>0>>0){break c}j=h-l|0;y[i+12>>2]=j;y[i+8>>2]=y[i+8>>2]+(l<<2);if(q){c=_a(i+16|0);f=gc(f);h=te(c,24)&16711935|te(c&16711935,8);c=y[f>>2];c=Oa(g,h,te(c&16711935,8)|te(c,24)&16711935,E);I=f,J=te(c&16711935,8)|te(c,24)&16711935,y[I>>2]=J;j=y[i+12>>2]}if(e>>>0>j>>>0){break c}c=y[i+16>>2];if(B[i+20>>2]>>0){break c}h=y[i+8>>2];u=e<<2;m=h+u|0;n=y[i+20>>2];f=c;while(1)if((h|0)==(m|0)){if(x){if((e|0)==(n|0)|e>>>0>n>>>0|(e|0)==(j|0)){break c}c=y[f+u>>2];f=te(c&16711935,8)|te(c,24)&16711935;c=y[m>>2];c=Oa(g,f,te(c&16711935,8)|te(c,24)&16711935,D);I=m,J=te(c&16711935,8)|te(c,24)&16711935,y[I>>2]=J}k=k+1|0;continue d}else{r=y[c>>2];s=te(r&16711935,8)|te(r,24)&16711935;r=y[h>>2];r=Eb(g,s,te(r&16711935,8)|te(r,24)&16711935);I=h,J=te(r&16711935,8)|te(r,24)&16711935,y[I>>2]=J;c=c+4|0;h=h+4|0;continue}}}m=0;e=((n|0)/32|0)-(c+31>>>5|0)|0;if((e|0)<0){break c}n=q-k|0;u=32-n|0;F=(j|0)>0?j:0;G=p<<2;e:while(1){if((m|0)==(F|0)){break a}f=i+8|0;Ca(f,a,m+v|0);c=y[i+12>>2];h=y[i+8>>2];Ca(f,b,d+m|0);if(!c){break b}f=y[i+12>>2];if(!f){break b}if(c>>>0

>>0){break c}c=c-p|0;y[i+20>>2]=c;y[i+16>>2]=h+G;if(x){if(c>>>0>>0){break c}y[i+20>>2]=A}if(f>>>0>>0){break c}k=f-l|0;y[i+12>>2]=k;y[i+8>>2]=y[i+8>>2]+(l<<2);c=_a(i+16|0);j=te(c&16711935,8)|te(c,24)&16711935;if(q){c=gc(i+8|0);f=y[c>>2];f=Oa(g,j>>>n|0,te(f&16711935,8)|te(f,24)&16711935,E);h=te(f&16711935,8);I=c,J=te(f,24)&16711935|h,y[I>>2]=J;k=y[i+12>>2]}if(e>>>0>k>>>0){break c}c=y[i+16>>2];if(B[i+20>>2]>>0){break c}h=y[i+8>>2];C=e<<2;t=h+C|0;r=y[i+20>>2];f=c;while(1)if((h|0)==(t|0)){if(x){if(e>>>0>r>>>0){break c}h=j<>2];h=(te(c&16711935,8)|te(c,24)&16711935)>>>n|h}if((e|0)==(k|0)){break c}c=y[t>>2];c=Oa(g,h,te(c&16711935,8)|te(c,24)&16711935,D);I=t,J=te(c&16711935,8)|te(c,24)&16711935,y[I>>2]=J}m=m+1|0;continue e}else{H=j<>2];j=te(j&16711935,8)|te(j,24)&16711935;s=y[h>>2];s=Eb(g,H|j>>>n,te(s&16711935,8)|te(s,24)&16711935);I=h,J=te(s&16711935,8)|te(s,24)&16711935,y[I>>2]=J;c=c+4|0;h=h+4|0;continue}}}m=0;e=((n|0)/32|0)-(c+31>>>5|0)|0;if((e|0)<0){break c}n=k-q|0;t=32-n|0;F=(j|0)>0?j:0;G=p<<2;f:while(1){if((m|0)==(F|0)){break a}f=i+8|0;Ca(f,a,m+v|0);c=y[i+12>>2];h=y[i+8>>2];Ca(f,b,d+m|0);if(!c){break b}f=y[i+12>>2];if(!f){break b}if(c>>>0

>>0){break c}c=c-p|0;y[i+20>>2]=c;y[i+16>>2]=h+G;if(x){if(c>>>0>>0){break c}y[i+20>>2]=A}if(f>>>0>>0){break c}k=f-l|0;y[i+12>>2]=k;y[i+8>>2]=y[i+8>>2]+(l<<2);c=i+16|0;f=_a(c);j=te(f&16711935,8)|te(f,24)&16711935;if(q){c=_a(c);f=gc(i+8|0);h=j<>2];c=Oa(g,h|j>>>t,te(c&16711935,8)|te(c,24)&16711935,E);I=f,J=te(c&16711935,8)|te(c,24)&16711935,y[I>>2]=J;k=y[i+12>>2]}if(e>>>0>k>>>0){break c}c=y[i+16>>2];if(B[i+20>>2]>>0){break c}h=y[i+8>>2];C=e<<2;u=h+C|0;r=y[i+20>>2];f=c;while(1)if((h|0)==(u|0)){if(x){if(e>>>0>r>>>0){break c}h=j<>2];h=(te(c&16711935,8)|te(c,24)&16711935)>>>t|h}if((e|0)==(k|0)){break c}c=y[u>>2];c=Oa(g,h,te(c&16711935,8)|te(c,24)&16711935,D);I=u,J=te(c&16711935,8)|te(c,24)&16711935,y[I>>2]=J}m=m+1|0;continue f}else{H=j<>2];j=te(j&16711935,8)|te(j,24)&16711935;s=y[h>>2];s=Eb(g,H|j>>>t,te(s&16711935,8)|te(s,24)&16711935);I=h,J=te(s&16711935,8)|te(s,24)&16711935,y[I>>2]=J;c=c+4|0;h=h+4|0;continue}}}o()}}Y=i+32|0}function Dd(a,b,c,d){var e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,p=0,q=0,r=0,s=0,t=0;f=Y-128|0;Y=f;n=xa(y[a+8>>2],y[a+12>>2]);y[f+124>>2]=n;if(qa(n)){Qa(n,z[a+4|0]);k=qd(b);y[f+116>>2]=k;e=0;a:{if(za(k,y[a+68>>2],f+120|0)){break a}y[f+108>>2]=y[f+120>>2];w[f+104|0]=1;q=Db(f+104|0,y[a+20>>2]);e=q;g=y[f+108>>2];h=0-g|0;y[e>>2]=z[e|0]&(g|0)!=-2147483648;y[e+4>>2]=h;y[f+100>>2]=0;w[f+96|0]=1;b:{c:{d:{e:while(1){if(B[a+16>>2]>r>>>0){if(za(k,y[a+68>>2],f+92|0)){break b}y[f+84>>2]=y[f+92>>2];e=1;w[f+80|0]=1;g=Db(f+80|0,y[a+20>>2]);s=Vc(q,y[g>>2],y[g+4>>2]);y[f+76>>2]=0;w[f+72|0]=1;while(1){f:{if(e&1){if(za(k,y[a+60>>2],f+8|0)){break b}h=Na(f+96|0,y[f+8>>2]);e=y[h+4>>2];y[f+72>>2]=y[h>>2];y[f+76>>2]=e;break f}g:{switch(za(k,y[a+64>>2],f+8|0)|0){case 0:break g;case 1:continue e;default:break b}}Na(Na(f+72|0,y[f+8>>2]),w[a+5|0])}e=1;j=0;g=y[a+20>>2];if((g|0)!=1){while(1){h=e;e=e+1|0;if(g>>>0>1<>>0){continue}break}if(Ia(b,h,f+8|0)){break b}j=z[f+8|0]}e=y[s+4>>2];if(!(y[s>>2]&1)){break b}h=e+j|0;e=h&(e^-1);if((e|0)<0){break b}t=(e|0)>=0?h:0;j=0;y[f+68>>2]=0;w[f+64|0]=1;h:while(1){if(wb(b,f+8|0)){break b}e=Tb(f- -64|0);if(z[e|0]!=1){break b}h=y[f+68>>2]|y[f+8>>2];g=(h|0)>=0;y[e>>2]=g;h=g?h:0;y[e+4>>2]=h;if(!g){break d}j=j+1|0;l=y[a+28>>2];i=y[a+24>>2];e=0;while(1){if((e|0)==(i|0)){continue h}g=l+(e<<3)|0;if(!(y[g>>2]==(j|0)&(h|0)==y[g+4>>2])){e=e+1|0;continue}break}break}i:{j:{k:{if(!z[a+1|0]){w[f+60|0]=0;y[f+56>>2]=0;break k}h=y[b>>2];g=y[b+8>>2];if(g>>>0>=B[b+4>>2]){break b}g=z[g+h|0];h=y[b+12>>2];_b(b);w[f+60|0]=0;y[f+56>>2]=0;if(g>>>7-h&1){break j}}Uc(f+56|0,y[y[a+40>>2]+(e<<2)>>2]);break i}if(za(k,y[a+72>>2],f+52|0)){break c}if(za(k,y[a+76>>2],f+48|0)){break c}if(za(k,y[a+80>>2],f+44|0)){break c}if(za(k,y[a+84>>2],f+40|0)){break c}if(za(k,y[a+88>>2],f+36|0)){break c}ta(b);j=y[y[a+40>>2]+(e<<2)>>2];if(!j){break c}l=y[f+52>>2];h=l>>31;e=y[j+8>>2]+l|0;if(e>>>0>>0?h+1|0:h){break c}i=y[f+48>>2];g=i>>31;h=i+y[j+12>>2]|0;if(h>>>0>>0?g+1|0:g){break c}g=y[b+8>>2];Cb(f+8|0,l,2,y[f+44>>2]);Cb(f+28|0,i,2,y[f+40>>2]);if(!(w[f+32|0]&1)|z[f+12|0]!=1){break c}i=db();y[f+24>>2]=i;y[i+8>>2]=h;y[i+4>>2]=e;e=z[a+2|0];y[i+20>>2]=j;w[i|0]=e;y[i+12>>2]=y[f+8>>2];e=y[f+28>>2];j=0;w[i+1|0]=0;y[i+16>>2]=e;w[i+24|0]=z[a+92|0];w[i+25|0]=z[a+93|0];w[i+26|0]=z[a+94|0];w[i+27|0]=z[a+95|0];e=Ra(b);y[f+20>>2]=e;if(fc(Tc(f+56|0,cb(i,e,c,d)))){ta(b);e=y[b+8>>2];if(e>>>0<=4294967293){h=e+2|0;e=y[b+4>>2];e=e>>>0>h>>>0?h:e;y[b+8>>2]=e}j=y[f+36>>2]==(e-g|0)}ua(f+20|0);Ua(f+24|0);if(!j){break c}}e=f+56|0;if(fc(e)){i=y[Xa(e)+8>>2];e=Xa(e);p=y[a+56>>2];j=y[e+12>>2];l:{m:{l=z[a+3|0];if(!l){e=z[f+72|0];if((p&-2)!=2){break m}if(!(e&1)){break c}g=y[f+76>>2];h=g>>31;e=i-1|0;m=e;e=e+g|0;g=m>>>0>e>>>0?h+1|0:h;g=e>>>0<2147483648&(g|0)<=0|(g|0)<0;y[f+72>>2]=0|g;y[f+76>>2]=g?e:0;if(!g){break c}break l}e=z[f+72|0];n:{switch(p|0){default:if(!(e&1)){break c}break l;case 0:case 2:break n}}if(!(e&1)){break c}g=y[f+76>>2];h=g>>31;e=j-1|0;m=e;e=e+g|0;h=m>>>0>e>>>0?h+1|0:h;g=e>>>0<2147483648&(h|0)<=0|(h|0)<0;y[f+72>>2]=0|g;y[f+76>>2]=g?e:0;if(g){break l}break c}if(!(e&1)){break c}}g=y[f+76>>2];Rc(f+8|0,l,p,g,t,i,j);m=Xa(f+56|0);e=y[f+8>>2];h=e;i=e>>31;e=y[f+12>>2];Hb(m,n,h,i,e,e>>31,y[a+52>>2]);e=y[f+16>>2];if(e){h=g>>31;m=e;e=e+g|0;g=m>>>0>e>>>0?h+1|0:h;h=e;g=e>>>0<2147483648&(g|0)<=0|(g|0)<0;e=0;y[f+72>>2]=e|g;y[f+76>>2]=g?h:0}r=r+1|0}mb(f+56|0);e=0;continue}}break}y[f+124>>2]=0;e=n;break a}o()}mb(f+56|0)}e=0}Ua(f+116|0)}la(f+124|0);Y=f+128|0;return e}function Bd(a,b,c,d,e){var f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,x=0,A=0,C=0,D=0,E=0,F=0;f=Y-112|0;Y=f;k=xa(y[a+8>>2],y[a+12>>2]);y[f+108>>2]=k;g=0;a:{if(!qa(k)){break a}o=y[e>>2];g=0;if(!Aa(o,b,f+104|0)){break a}t=y[e+36>>2];u=y[e+32>>2];v=y[e+28>>2];x=y[e+24>>2];A=y[e+20>>2];C=y[e+16>>2];D=y[e+12>>2];E=y[e+8>>2];F=y[e+4>>2];Qa(k,z[a+4|0]);y[f+100>>2]=y[f+104>>2];w[f+96|0]=1;p=Db(f+96|0,y[a+20>>2]);e=p;h=y[f+100>>2];g=0-h|0;y[e>>2]=z[e|0]&(h|0)!=-2147483648;y[e+4>>2]=g;y[f+92>>2]=0;w[f+88|0]=1;e=y[a+16>>2];b:{c:while(1){if(e>>>0>m>>>0){y[f+84>>2]=0;w[f+80|0]=1;if(!Aa(o,b,f+76|0)){break b}y[f+68>>2]=y[f+76>>2];e=1;w[f+64|0]=1;g=Db(f- -64|0,y[a+20>>2]);q=Vc(p,y[g>>2],y[g+4>>2]);while(1){d:{if(e&1){Aa(F,b,f+4|0);e=Na(f+88|0,y[f+4>>2]);g=y[e+4>>2];y[f+80>>2]=y[e>>2];y[f+84>>2]=g;break d}if(!Aa(E,b,f+4|0)){e=y[a+16>>2];continue c}Na(Na(f+80|0,y[f+4>>2]),w[a+5|0])}e=y[a+16>>2];if(m>>>0>=e>>>0){continue c}y[f+60>>2]=0;if(y[a+20>>2]!=1){Aa(D,b,f+60|0)}e=y[q+4>>2];if(!(y[q>>2]&1)){break b}g=y[f+60>>2];n=e+g|0;if(((e^n)&(g^n))<0){break b}Ac(t,b,f+56|0);e=y[f+56>>2];if(e>>>0>=B[a+24>>2]){break b}e:{f:{g:{h:{i:{if(!z[a+1|0]){y[f+52>>2]=0;w[f+48|0]=0;y[f+44>>2]=0;break i}Aa(C,b,f+52|0);g=y[f+52>>2];w[f+48|0]=0;y[f+44>>2]=0;if(g){break h}}Uc(f+44|0,y[y[a+40>>2]+(e<<2)>>2]);break g}Aa(A,b,f+40|0);Aa(x,b,f+36|0);Aa(v,b,f+32|0);Aa(u,b,f+28|0);h=y[y[a+40>>2]+(e<<2)>>2];if(!h){break f}i=y[f+40>>2];g=i>>31;e=y[h+8>>2]+i|0;if(e>>>0>>0?g+1|0:g){break f}j=y[f+36>>2];g=j>>31;l=j+y[h+12>>2]|0;g=l>>>0>>0?g+1|0:g;if((g|0)==1|g>>>0>1){break f}Cb(f+4|0,i,1,y[f+32>>2]);Cb(f+20|0,j,1,y[f+28>>2]);if(!(w[f+24|0]&1)|z[f+8|0]!=1){break f}g=db();y[f+16>>2]=g;y[g+8>>2]=l;y[g+4>>2]=e;e=z[a+2|0];y[g+20>>2]=h;w[g|0]=e;y[g+12>>2]=y[f+4>>2];e=y[f+20>>2];w[g+1|0]=0;y[g+16>>2]=e;w[g+24|0]=z[a+92|0];w[g+25|0]=z[a+93|0];w[g+26|0]=z[a+94|0];w[g+27|0]=z[a+95|0];Tc(f+44|0,cb(g,b,c,d));Ua(f+16|0)}i=0;e=f+44|0;if(!fc(e)){break e}l=y[Xa(e)+8>>2];e=Xa(e);j=y[a+56>>2];r=y[e+12>>2];j:{k:{l:{s=z[a+3|0];if(!s){e=z[f+80|0];if((j&-2)!=2){break l}if(!(e&1)){break j}g=y[f+84>>2];e=g>>31;h=l-1|0;g=g+h|0;h=h>>>0>g>>>0?e+1|0:e;e=g;g=e>>>0<2147483648&(h|0)<=0|(h|0)<0;y[f+80>>2]=0|g;y[f+84>>2]=g?e:0;if(g){break k}break e}e=z[f+80|0];m:{switch(j|0){default:if(e&1){break k}break e;case 0:case 2:break m}}if(!(e&1)){break j}e=y[f+84>>2];g=e>>31;h=r-1|0;e=e+h|0;g=h>>>0>e>>>0?g+1|0:g;g=e>>>0<2147483648&(g|0)<=0|(g|0)<0;y[f+80>>2]=0|g;y[f+84>>2]=g?e:0;if(g){break k}break e}if(!(e&1)){break e}}g=y[f+84>>2];Rc(f+4|0,s,j,g,n,l,r);i=Xa(f+44|0);e=y[f+4>>2];h=e;j=e>>31;e=y[f+8>>2];Hb(i,k,h,j,e,e>>31,y[a+52>>2]);h=y[f+12>>2];if(h){e=g>>31;g=g+h|0;h=g>>>0>>0?e+1|0:e;e=g;g=e>>>0<2147483648&(h|0)<=0|(h|0)<0;h=0;y[f+80>>2]=g|h;y[f+84>>2]=g?e:0}i=1;m=m+1|0;break e}y[f+80>>2]=0;y[f+84>>2]=0;break e}i=0}mb(f+44|0);e=0;if(i){continue}break}break b}break}y[f+108>>2]=0;g=k;break a}g=0}la(f+108|0);Y=f+112|0;return g}function eb(a,b,c){var d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,q=0,r=0;j=Y-32|0;Y=j;a:{b:{c:{if(!qa(a)){break c}k=y[a+12>>2];if((k|0)>=(b|0)){break c}g=y[a+16>>2];if((268435452/(g|0)|0)<(b|0)){break c}if((k|0)<0){break b}d=g>>31;q=se(g,d,k);k=Z;if((k|0)==1|k>>>0>1){break b}g=se(g,d,b);d=Z;if((d|0)==1|d>>>0>1){break b}d:{if(z[a+4|0]==1){k=y[a>>2];y[a>>2]=0;y[j+20>>2]=0;_c(a,j+20|0);y[j+28>>2]=0;if(!d&g>>>0>2147479550|d){break a}e:{if(!k){g=Ya(g);break e}if(!g){ma(k);g=0;break e}f:{if(g>>>0>4294967239){break f}m=g>>>0<=8?8:g+3&-4;d=m+8|0;g=k;r=g-4|0;i=r;f=y[i>>2];e=f+i|0;h=y[e>>2];g:{h:{i:{if((h|0)!=y[(e+h|0)-4>>2]){h=f+h|0;if(h>>>0>=d+16>>>0){f=y[e+4>>2];e=y[e+8>>2];y[f+8>>2]=e;y[e+4>>2]=f;f=d+i|0;e=h-d|0;y[f>>2]=e;y[(f+(e&-4)|0)-4>>2]=e|1;e=y[f>>2]-8|0;j:{if(e>>>0<=127){n=(e>>>3|0)-1|0;break j}h=H(e);n=((e>>>29-h^4)-(h<<2)|0)+110|0;if(e>>>0<=4095){break j}e=((e>>>30-h^2)-(h<<1)|0)+71|0;n=e>>>0>=63?63:e}e=n;h=e<<4;y[f+4>>2]=h+6080;h=h+6088|0;y[f+8>>2]=y[h>>2];y[h>>2]=f;y[y[f+8>>2]+4>>2]=f;h=y[1779];f=e&31;if((e&63)>>>0>=32){f=1<>>32-f}y[1778]=e|y[1778];y[1779]=f|h;y[i>>2]=d;y[(i+(d&-4)|0)-4>>2]=d;e=1;break g}if(d>>>0>h>>>0){break i}d=y[e+4>>2];f=y[e+8>>2];y[d+8>>2]=f;y[f+4>>2]=d;y[i>>2]=h;y[(i+(h&-4)|0)-4>>2]=h;e=1;break g}if(f>>>0>=d+16>>>0){y[i>>2]=d;y[(i+(d&-4)|0)-4>>2]=d;i=d+i|0;d=f-d|0;y[i>>2]=d;y[(i+(d&-4)|0)-4>>2]=d|1;d=y[i>>2]-8|0;k:{if(d>>>0<=127){e=(d>>>3|0)-1|0;break k}f=H(d);e=((d>>>29-f^4)-(f<<2)|0)+110|0;if(d>>>0<=4095){break k}d=((d>>>30-f^2)-(f<<1)|0)+71|0;e=d>>>0>=63?63:d}f=e;d=f<<4;y[i+4>>2]=d+6080;d=d+6088|0;y[i+8>>2]=y[d>>2];y[d>>2]=i;y[y[i+8>>2]+4>>2]=i;i=y[1779];d=f&31;if((f&63)>>>0>=32){f=1<>>32-d}y[1778]=e|y[1778];y[1779]=f|i;e=1;break g}e=1;if(d>>>0<=f>>>0){break h}}e=0}}if(e){break e}g=Ya(m);if(!g){break f}l=y[r>>2]-8|0;l=l>>>0>m>>>0?m:l;if(l){p(g,k,l)}ma(k);l=g}g=l}if(!g){break a}nb(a,g);Ha(j+28|0);break d}Da(j+20|0,a);k=y[j+20>>2];l=y[j+24>>2];nb(a,ab(g,1));Da(j+12|0,a);if(l>>>0>B[j+16>>2]){break b}if(!l){break d}p(y[j+12>>2],k,l)}y[a+12>>2]=b;Da(j+20|0,a);a=y[j+24>>2];if(q>>>0>a>>>0){break b}c=0-c|0;b=q+y[j+20>>2]|0;a=a-q|0;while(1){if((a|0)<=0){break c}w[b|0]=c;a=a-1|0;b=b+1|0;continue}}Y=j+32|0;return}o()}rb();o()}function nc(a,b,c,d,e){var f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,x=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0;f=Y-48|0;Y=f;v=xa(y[a+4>>2],y[a+8>>2]);y[f+44>>2]=v;a:{b:{if(!qa(v)){break b}c:{m=y[a+4>>2];if(!m){break c}n=y[a+8>>2];L=e?n:n&2147483647;e=e<<1;B=e+2448|0;G=e+2466|0;C=e+2442|0;H=e+2436|0;I=e+2430|0;M=e+2460|0;N=e+2454|0;O=e+2424|0;e=m-1|0;p=e>>>3|0;J=(e&7)+1|0;n=0;m=0;e=0;while(1){d:{e:{if((x|0)!=(L|0)){ja(f+36|0,v,x);r=y[f+40>>2];s=y[f+36>>2];if(z[a+1|0]!=1){t=e;break e}if(z[b|0]){break b}h=A[O>>1];if(h>>>0>=d>>>0){break c}t=0;h=ka(b,(h<<3)+c|0);if((h|0)==(e|0)){break e}y[f+32>>2]=r;y[f+28>>2]=s;y[f+24>>2]=m;y[f+20>>2]=n;t=y[f+32>>2];y[f+8>>2]=y[f+28>>2];y[f+12>>2]=t;t=y[f+24>>2];y[f>>2]=y[f+20>>2];y[f+4>>2]=t;Pa(f+8|0,f);e=e^h;break d}y[f+44>>2]=0;break a}f:{g:{h:{if(x>>>0<=1){i=0;k=0;l=(x|0)!=1;if(!l){if(!m){break c}k=z[n|0]}q=A[I>>1];g=A[H>>1]&k>>>q;while(1){if((i|0)!=(p|0)){if(!l){e=i+1|0;if(e>>>0>=m>>>0){break c}k=z[e+n|0]|k<<8}j=0;e=7;while(1){if((e|0)>=0){if(z[b|0]){break b}if(d>>>0<=g>>>0){break c}u=A[B>>1]&k>>>e+q|(A[C>>1]&g)<<1;h=ka(b,(g<<3)+c|0);g=u|h;j=h<>>0<=g>>>0){break c}u=A[B>>1]&h>>>l-e|(A[C>>1]&g)<<1;i=ka(b,(g<<3)+c|0);g=u|i;j=i<<7-e|j;e=e+1|0;continue}}if(!g|!m|(m-1>>>0

>>0|p>>>0>r>>>0)|g-1>>>0

>>0){break c}k=z[n|0];F=A[I>>1];K=A[N>>1];D=z[i|0]<>1]&k>>>F|D&A[M>>1];u=p+s|0;h=n;l=s;while(1){i:{E=D<<8;if((l|0)==(u|0)){break i}h=h+1|0;k=z[h|0]|k<<8;i=i+1|0;D=E|z[i|0]<=0){if(z[b|0]){break b}if(d>>>0<=g>>>0){break c}E=A[B>>1]&k>>>e+F|(A[G>>1]&D>>>e|(A[C>>1]&g)<<1);q=ka(b,(g<<3)+c|0);g=E|q;j=q<>>0<=g>>>0){break c}h=ka(b,(g<<3)+c|0);i=7-j|0;e=h<>1]&l>>>q-j|(A[G>>1]&E>>>i|(A[C>>1]&g)<<1));j=j+1|0;continue}}if(p>>>0>=r>>>0){break c}w[p+s|0]=j;break f}if(p>>>0>=r>>>0){break c}w[u|0]=e}e=t}x=x+1|0;i=n;g=m;n=s;m=r;continue}}o()}v=0}la(f+44|0);Y=f+48|0;return v}function ic(a,b,c,d,e){var f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0;a:{b:{c:{d:{if(!z[a+1|0]){g=y[a+60>>2];break d}f=y[b+4>>2];if(z[f|0]){break a}h=y[c>>2];if(h>>>0>=B[b+12>>2]){break c}g=y[a+60>>2]^ka(f,y[b+8>>2]+(h<<3)|0);y[a+60>>2]=g}if(g){ed(a,y[y[b>>2]>>2]);break b}f=y[a+24>>2];if(f>>>0<=1){if((f|0)==1){if(!y[a+40>>2]){break c}j=z[y[a+36>>2]]}i=y[c+12>>2]&j>>>y[c+8>>2];h=(f|0)!=1;while(1){if((d|0)!=(k|0)){if(!h){f=k+1|0;if(f>>>0>=B[a+40>>2]){break c}j=z[f+y[a+36>>2]|0]|j<<8}g=7;l=0;while(1){if((g|0)>=0){f=y[b+4>>2];if(z[f|0]){break a}if(B[b+12>>2]<=i>>>0){break c}f=ka(f,y[b+8>>2]+(i<<3)|0);i=f|(y[c+16>>2]&j>>>y[c+8>>2]+g|(y[c+4>>2]&i)<<1);l=f<>2]<=k>>>0){break c}w[y[a+44>>2]+k|0]=l;k=k+1|0;continue}break}f=j<<8;g=0;l=0;while(1){if((e|0)!=(g|0)){h=y[b+4>>2];if(z[h|0]){break a}if(B[b+12>>2]<=i>>>0){break c}h=ka(h,y[b+8>>2]+(i<<3)|0);j=7-g|0;l=h<>2]&f>>>j+y[c+8>>2]|(y[c+4>>2]&i)<<1);g=g+1|0;continue}break}if(B[a+48>>2]<=d>>>0){break c}w[y[a+44>>2]+d|0]=l;break b}if(!y[a+32>>2]|!y[a+40>>2]){break c}j=z[y[a+36>>2]];k=z[y[a+28>>2]]<>2];i=y[c+12>>2]&j>>>y[c+8>>2]|k&y[c+24>>2];f=0;while(1){e:{if((d|0)!=(f|0)){h=f+1|0;if(h>>>0>=B[a+32>>2]|h>>>0>=B[a+40>>2]){break c}k=z[h+y[a+28>>2]|0]<>2]|k<<8;j=z[h+y[a+36>>2]|0]|j<<8;g=7;l=0;while(1){if((g|0)<0){break e}m=y[b+4>>2];if(z[m|0]){break a}if(B[b+12>>2]<=i>>>0){break c}m=ka(m,y[b+8>>2]+(i<<3)|0);i=m|(y[c+16>>2]&j>>>y[c+8>>2]+g|(y[c+28>>2]&k>>>g|(y[c+4>>2]&i)<<1));l=m<>2];if(z[h|0]){break a}if(B[b+12>>2]<=i>>>0){break c}k=ka(h,y[b+8>>2]+(i<<3)|0);h=7-g|0;f=k<>2]&l>>>h+y[c+8>>2]|(y[c+28>>2]&j>>>h|(y[c+4>>2]&i)<<1));g=g+1|0;continue}break}if(B[a+48>>2]<=d>>>0){break c}w[y[a+44>>2]+d|0]=f;break b}if(B[a+48>>2]<=f>>>0){break c}w[y[a+44>>2]+f|0]=l;f=h;continue}}o()}return 1}return 0}function pe(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,x=0,A=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0;c=Y-48|0;Y=c;m=y[b+4>>2];t=y[b+12>>2];u=y[b+8>>2];g=y[y[b>>2]>>2];ja(c+40|0,g,y[a+24>>2]-1|0);ja(c+32|0,g,y[a+24>>2]-2|0);A=u+317736|0;n=y[c+36>>2];p=y[c+32>>2];d=y[a+24>>2];C=t>>>0>39717;q=4;a:{while(1){b:{c:{if(B[a+8>>2]>d>>>0){j=y[c+44>>2];k=y[c+40>>2];ja(c+32|0,g,d);h=y[c+36>>2];r=y[c+32>>2];y[c+40>>2]=r;y[c+44>>2]=h;d:{e:{f:{if(!z[a+1|0]){d=y[a+60>>2];break f}if(z[m|0]){break b}if(!C){break e}d=y[a+60>>2]^ka(m,A);y[a+60>>2]=d}if(d){y[c+20>>2]=j;y[c+16>>2]=k;y[c+24>>2]=r;y[c+28>>2]=h;y[c+8>>2]=r;y[c+12>>2]=h;h=y[c+20>>2];y[c>>2]=y[c+16>>2];y[c+4>>2]=h;Pa(c+8|0,c);break d}i=0;v=0;x=0;if(z[a+2|0]==1){ja(c+32|0,y[a+12>>2],y[a+24>>2]);x=y[c+36>>2];v=y[c+32>>2]}d=c+32|0;ja(d,g,y[a+24>>2]+w[a+17|0]|0);D=y[c+36>>2];E=y[c+32>>2];ja(d,g,y[a+24>>2]+w[a+19|0]|0);F=y[c+36>>2];G=y[c+32>>2];ja(d,g,y[a+24>>2]+w[a+21|0]|0);H=y[c+36>>2];I=y[c+32>>2];ja(d,g,y[a+24>>2]+w[a+23|0]|0);J=y[c+36>>2];K=y[c+32>>2];e=y[g+8>>2];i=!n|(e|0)<2?i:z[p|0]>>>6&1;i=!n|(e|0)<=0?i:z[p|0]>>>6&2|i;f=0;f=!j|(e|0)<3?f:z[k|0]>>>5&1;f=!j|(e|0)<2?f:z[k|0]>>>5&2|f;f=!j|(e|0)<=0?f:z[k|0]>>>5&4|f;s=0;d=0;while(1){if(B[a+4>>2]<=d>>>0){break d}g:{h:{if(z[a+2|0]==1){if(ia(y[y[a+12>>2]+8>>2],d,v,x)){break h}}l=ia(e,w[a+16|0]+d|0,E,D);L=ia(e,w[a+18|0]+d|0,G,F);M=ia(e,w[a+20|0]+d|0,I,H);e=ia(e,w[a+22|0]+d|0,K,J);if(z[m|0]){break b}e=f<<5|i<<12|l<<4|L<<10|M<<11|e<<15|s;if(e>>>0>=t>>>0){break e}l=ka(m,(e<<3)+u|0);e=y[g+8>>2];if(!l){break h}La(e,d,r,h,1);e=y[g+8>>2];l=1;break g}l=0}i=ia(e,d+2|0,p,n)|i<<1&6;f=ia(e,d+3|0,k,j)|f<<1&30;d=d+1|0;s=s<<1&14|l;continue}}o()}h=y[b+16>>2];if(!h){break c}if(!(_[y[y[h>>2]+8>>2]](h)|0)){break c}y[a+24>>2]=y[a+24>>2]+1;q=3}y[a+52>>2]=q;break a}d=y[a+24>>2]+1|0;y[a+24>>2]=d;p=k;n=j;continue}break}q=-1}Y=c+48|0;return q|0} +function mc(a,b,c,d,e){var f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,x=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0;f=Y-48|0;Y=f;i=xa(y[a+4>>2],y[a+8>>2]);y[f+44>>2]=i;a:{b:{if(!qa(i)){break b}Qa(i,0);K=e|2;L=5-e|0;M=4-e|0;k=e>>>1|0;N=k^3;j=e<<1;O=j+2490|0;P=j+2484|0;Q=j+2478|0;R=j+2472|0;S=k^1;T=2-k|0;x=e&1;U=x+1|0;V=j+2424|0;j=0;k=0;while(1){c:{d:{e:{if(B[a+8>>2]>m>>>0){ja(f+36|0,i,m);t=y[f+40>>2];u=y[f+36>>2];if(z[a+1|0]!=1){n=g;break e}if(z[b|0]){break b}h=A[V>>1];if(h>>>0>=d>>>0){break d}n=0;h=ka(b,(h<<3)+c|0);if((h|0)==(g|0)){break e}y[f+32>>2]=t;y[f+28>>2]=u;y[f+24>>2]=k;y[f+20>>2]=j;n=y[f+32>>2];y[f+8>>2]=y[f+28>>2];y[f+12>>2]=n;n=y[f+24>>2];y[f>>2]=y[f+20>>2];y[f+4>>2]=n;g=g^h;i=y[f+44>>2];Pa(f+8|0,f);break c}y[f+44>>2]=0;break a}C=0;D=0;E=0;if(z[a+2|0]==1){ja(f+36|0,y[a+12>>2],m);E=y[f+40>>2];D=y[f+36>>2]}g=f+36|0;i=y[f+44>>2];ja(g,i,w[a+17|0]+m|0);W=y[f+40>>2];X=y[f+36>>2];F=0;G=0;H=0;I=0;J=0;if(!e){ja(g,i,w[a+19|0]+m|0);C=y[f+40>>2];F=y[f+36>>2];ja(g,i,w[a+21|0]+m|0);G=y[f+40>>2];H=y[f+36>>2];ja(g,i,w[a+23|0]+m|0);J=y[f+36>>2];I=y[f+40>>2]}h=y[i+8>>2];p=ia(h,U,r,s)|ia(h,x,r,s)<<1;p=!s|(e|0)!=1|(h|0)<=0?p:z[r|0]>>>5&4|p;q=ia(h,T,j,k)|ia(h,S,j,k)<<1;q=!k|e>>>0>1|(h|0)<=0?q:z[j|0]>>>5&4|q;v=0;g=0;while(1){if(B[a+4>>2]<=g>>>0){g=n;break c}f:{g:{if(z[a+2|0]==1){if(ia(y[y[a+12>>2]+8>>2],g,D,E)){break g}}if(z[b|0]){break b}l=q<>1]|v;if(!e){l=ia(h,w[a+18|0]+g|0,F,C)<<10|ia(h,w[a+20|0]+g|0,H,G)<<11|ia(h,w[a+22|0]+g|0,J,I)<<15|l}if(d>>>0<=l>>>0){break d}l=ka(b,(l<<3)+c|0);h=y[i+8>>2];if(!l){break g}La(h,g,u,t,1);h=y[i+8>>2];l=1;break f}l=0}p=(ia(h,g+K|0,r,s)|p<<1)&A[Q>>1];q=(ia(h,g+N|0,j,k)|q<<1)&A[P>>1];g=g+1|0;v=A[O>>1]&(l|v<<1);continue}}o()}m=m+1|0;r=j;s=k;j=u;k=t;continue}}i=0}la(f+44|0);Y=f+48|0;return i}function ke(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,p=0,q=0,r=0,s=0;k=Y-16|0;Y=k;h=y[b+4>>2];l=y[b+12>>2];m=y[b+8>>2];n=y[y[b>>2]>>2];if(!y[a+48>>2]){Da(k+8|0,n);d=y[k+12>>2];y[a+44>>2]=y[k+8>>2];y[a+48>>2]=d}a:{b:{c:{d=y[a+4>>2];if(!d){break c}r=m+3240|0;d=d-1|0;i=d>>>3|0;p=(d&7)+1|0;c=y[a+24>>2];s=l>>>0>405;while(1){d:{if(B[a+8>>2]<=c>>>0){c=4}else{e:{if(!z[a+1|0]){c=y[a+60>>2];break e}if(z[h|0]){break b}if(!s){break c}c=y[a+60>>2]^ka(h,r);y[a+60>>2]=c}f:{if(c){ed(a,n);break f}c=0;g=0;g:{if(!y[a+24>>2]){h:{while(1){e=7;f=0;if((g|0)==(i|0)){break h}i:{while(1){if((e|0)>=0){if(z[h|0]){break i}if(c>>>0>=l>>>0){break c}d=ka(h,(c<<3)+m|0);c=d|c<<1&1006;f=d<>2]<=g>>>0){break c}w[y[a+44>>2]+g|0]=f;g=g+1|0;continue}break}if(g>>>0>>0){break b}}e=0;f=0;while(1){if((e|0)==(p|0)){break g}if(z[h|0]){break b}if(c>>>0>=l>>>0){break c}d=ka(h,(c<<3)+m|0);c=d|c<<1&1006;f=d<<7-e|f;e=e+1|0;continue}}if(!y[a+40>>2]){break c}g=z[y[a+36>>2]];e=g>>>1&112;d=0;while(1){j:{if((d|0)!=(i|0)){j=d+1|0;if(j>>>0>=B[a+40>>2]){break c}g=z[j+y[a+36>>2]|0]|g<<8;c=7;f=0;while(1){if((c|0)<0){break j}if(z[h|0]){break b}if(e>>>0>=l>>>0){break c}q=ka(h,(e<<3)+m|0);e=q|(g>>>c+1&16|e<<1&1006);f=q<>>0>=l>>>0){break c}j=ka(h,(e<<3)+m|0);e=j|(d>>>8-c&16|e<<1&1006);f=j<<7-c|f;c=c+1|0;continue}break}if(B[a+48>>2]<=i>>>0){break c}w[y[a+44>>2]+i|0]=f;break f}if(B[a+48>>2]<=d>>>0){break c}w[y[a+44>>2]+d|0]=f;d=j;continue}}if(B[a+48>>2]<=i>>>0){break c}w[y[a+44>>2]+i|0]=f}Kb(a,n);d=y[b+16>>2];if(!d){break d}if(!(_[y[y[d>>2]+8>>2]](d)|0)){break d}y[a+24>>2]=y[a+24>>2]+1;c=3}y[a+52>>2]=c;break a}c=y[a+24>>2]+1|0;y[a+24>>2]=c;continue}}o()}c=-1}Y=k+16|0;return c|0}function $d(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,i=0,j=0,k=0,l=0;g=Y-80|0;Y=g;w[g+72|0]=0;y[g+68>>2]=0;i=c+31>>>3&536870908;k=E(i,d);h=yd(k,1);y[g>>2]=0;j=g+68|0;nb(j,h);Ha(g);a:{b:{if(!qa(j)){break b}j=Nb(j);h=g+56|0;y[g+60>>2]=h;y[g+56>>2]=h;y[g+64>>2]=0;y[g+48>>2]=0;y[g+52>>2]=0;l=a;y[g+32>>2]=a;y[g+36>>2]=b;y[g>>2]=c;h=e;y[g+24>>2]=h;y[g+28>>2]=f;y[g+16>>2]=0;y[g+20>>2]=0;y[g+40>>2]=j;y[g+44>>2]=k;y[g+8>>2]=0;y[g+12>>2]=0;y[g+4>>2]=d;y[g+48>>2]=i;a=se(d,0,i);if(Z|a>>>0>k>>>0){break a}e=j;while(1){if((a|0)>0){w[e|0]=0;a=a-1|0;e=e+1|0;continue}break}a=b;b=g+56|0;a=zc(na(116),l,a,b,0);if(f){b=zc(na(116),h,f,b,1);y[g+76>>2]=0;zb(a,b);Ab(g+76|0)}e=c+7>>>3|0;y[g+76>>2]=0;f=g+52|0;zb(f,a);Ab(g+76|0);c:{d:{b=y[g+52>>2];a=y[b>>2];if(!a){break d}if(!yc(a)){break d}y[b+56>>2]=-1;a=0;break c}y[b+52>>2]=0;a=na(20);y[a+16>>2]=0;y[a+8>>2]=0;y[a+12>>2]=0;w[a+4|0]=0;y[a>>2]=0;e:{if((c|d)<0|i>>>0>268435452){break e}h=i<<3;if((h|0)<(c|0)|(!(!i|(d|0)<=0|k)|(2147483616/(h>>>0)|0)<(d|0))){break e}y[a+16>>2]=i;y[a+12>>2]=d;y[a+8>>2]=c;y[g+76>>2]=j;_c(a,g+76|0)}y[g+76>>2]=0;oa(b+32|0,a);la(g+76|0);w[b+50|0]=1;a=id(b)}f:{while(1){g:{a=a&1;h:{b=y[y[g+52>>2]+56>>2];if((b|0)!=4){break h}zb(g+52|0,0);b=-1;if(!a){break h}i:{b=E(y[g+48>>2],y[g+4>>2]);if(b>>>0>B[g+44>>2]){break i}a=y[g+40>>2];if(a&3){break i}c=(b&-4)+a|0;b=4;while(1){if((a|0)==(c|0)){break h}y[a>>2]=y[a>>2]^-1;a=a+4|0;continue}}o()}j:{switch(b-3|0){case 0:break j;case 1:break g;default:break f}}a=id(y[g+52>>2]);continue}break}X(Nb(g+68|0)|0,e|0,i|0,d|0)}Ab(f);if(!y[g+64>>2]){break b}a=y[g+60>>2];b=y[a>>2];c=y[y[g+56>>2]+4>>2];y[b+4>>2]=c;y[c>>2]=b;y[g+64>>2]=0;while(1){if((g+56|0)==(a|0)){break b}b=y[a+4>>2];oc(a);a=b;continue}}ub(g+68|0);Y=g+80|0;return}o()}function Ya(a){var b=0,c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0;a:{b:{while(1){if(a>>>0>4294967239){break b}b=y[1779];c=b;f=y[1778];a=a>>>0<=8?8:a+3&-4;c:{if(a>>>0<=127){d=(a>>>3|0)-1|0;break c}e=H(a);d=((a>>>29-e^4)-(e<<2)|0)+110|0;if(a>>>0<=4095){break c}e=((a>>>30-e^2)-(e<<1)|0)+71|0;d=e>>>0>=63?63:e}g=d;d=g&31;if((g&63)>>>0>=32){e=0;b=b>>>d|0}else{e=b>>>d|0;b=((1<>>d}if(b|e){while(1){d=e;d:{if(d|b){c=d-1|0;e=c+1|0;f=c;c=b-1|0;f=(c|0)!=-1?e:f;e=H(d^f);e=(e|0)==32?H(b^c)+32|0:e;c=63-e|0;Z=0-(e>>>0>63)|0;break d}Z=0;c=64}f=c;c=f&31;if((f&63)>>>0>=32){e=0;h=d>>>c|0}else{e=d>>>c|0;h=((1<>>c}g=f+g|0;b=g<<4;d=y[b+6088>>2];c=b+6080|0;e:{if((d|0)!=(c|0)){b=dc(d,a);if(b){break a}b=y[d+4>>2];f=y[d+8>>2];y[b+8>>2]=f;y[f+4>>2]=b;y[d+8>>2]=c;y[d+4>>2]=y[c+4>>2];y[c+4>>2]=d;y[y[d+4>>2]+8>>2]=d;g=g+1|0;b=(e&1)<<31|h>>>1;e=e>>>1|0;break e}j=y[1779];f=g&63;b=f;d=b&31;if(b>>>0>=32){b=0;c=-1>>>d|0}else{b=-1>>>d|0;c=b|(1<>>0>=32){b=c<>>32-d|b<>>0>=32){b=-1<>>32-b}k=c&-2;c=f&31;if(f>>>0>=32){f=0;c=b>>>c|0}else{f=b>>>c|0;c=((1<>>c}b=c|i;Z=d|f;y[1778]=y[1778]&b;y[1779]=Z&j;b=h^1}if(b|e){continue}break}f=y[1778];c=y[1779]}e=H(c);d=63-((e|0)==32?H(f)+32|0:e)|0;f:{if(!(c|f)){g=0;break f}b=d<<4;g=y[b+6088>>2];if(!c&f>>>0<1073741824){break f}e=98;c=b+6080|0;if((c|0)==(g|0)){break f}while(1){b=dc(g,a);if(b){break a}g=y[g+8>>2];if((c|0)==(g|0)){break f}b=e;e=b-1|0;if(b){continue}break}}if(Fd(a+48|0)){continue}break}if(!g){break b}e=(d<<4)+6080|0;if((e|0)==(g|0)){break b}while(1){b=dc(g,a);if(b){break a}g=y[g+8>>2];if((e|0)!=(g|0)){continue}break}}b=0}return b}function Ld(a,b){a=a|0;b=+b;var c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0;c=a<<3;o=c+7136|0;i=D[c+7168>>3];if(i!=0){l=D[o>>3];m=J(l,b);b=m-l;if(b<0x10000000000000000&b>=0){d=~~b>>>0;if(G(b)>=1){c=~~(b>0?I(K(b*2.3283064365386963e-10),4294967295):L((b-+(~~b>>>0>>>0))*2.3283064365386963e-10))>>>0}else{c=0}}else{c=0}if(i<0x10000000000000000&i>=0){h=~~i>>>0;if(G(i)>=1){e=~~(i>0?I(K(i*2.3283064365386963e-10),4294967295):L((i-+(~~i>>>0>>>0))*2.3283064365386963e-10))>>>0}else{e=0}}else{e=0}a:{b:{c:{d:{e:{f:{g:{h:{i:{j:{k:{if(c){if(!h){break k}if(!e){break j}f=H(e)-H(c)|0;if(f>>>0<=31){break i}break c}if((e|0)==1|e>>>0>1){break c}Z=0;c=(d>>>0)/(h>>>0)|0;break a}if(!d){break h}if(!e|e-1&e){break g}c=c>>>ue(e)|0;Z=0;break a}if(!(h-1&h)){break f}k=(H(h)+33|0)-H(c)|0;j=0-k|0;break d}k=f+1|0;j=63-f|0;break d}Z=0;c=(c>>>0)/(e>>>0)|0;break a}f=H(e)-H(c)|0;if(f>>>0<31){break e}break c}if((h|0)==1){break b}h=ue(h);e=h&31;if((h&63)>>>0>=32){c=c>>>e|0}else{f=c>>>e|0;c=((1<>>e}Z=f;break a}k=f+1|0;j=63-f|0}f=k&63;g=f&31;if(f>>>0>=32){f=0;n=c>>>g|0}else{f=c>>>g|0;n=((1<>>g}j=j&63;g=j&31;if(j>>>0>=32){c=d<>>32-g|c<>>31;f=n<<1|c>>>31;g=s-(p+(f>>>0>j>>>0)|0)>>31;q=g&h;n=f-q|0;f=p-((e&g)+(f>>>0>>0)|0)|0;c=c<<1|d>>>31;d=r|d<<1;r=g&1;k=k-1|0;if(k){continue}break}}Z=c<<1|d>>>31;c=r|d<<1;break a}d=0;c=0}Z=c;c=d}d=Z;c=c+1|0;d=c?d:d+1|0;l=(+(c>>>0)+ +(d>>>0)*4294967296)*i+l;m=l-m}D[o>>3]=l;Q(a|0,+m)|0;a=(a|0)==2?27:(a|0)==1?26:14;c=a-1|0;l:{if(y[1780]>>>c&1){y[1782]=y[1782]|1<>2];if(c){_[c|0](a)}}}function ne(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,x=0,A=0,C=0,D=0,E=0,F=0;c=Y-48|0;Y=c;n=y[b+4>>2];t=y[b+12>>2];u=y[b+8>>2];i=y[y[b>>2]>>2];ja(c+40|0,i,y[a+24>>2]-1|0);ja(c+32|0,i,y[a+24>>2]-2|0);C=u+15528|0;l=y[c+36>>2];m=y[c+32>>2];e=y[a+24>>2];D=t>>>0>1941;p=4;a:{while(1){b:{c:{if(B[a+8>>2]>e>>>0){j=y[c+44>>2];k=y[c+40>>2];ja(c+32|0,i,e);h=y[c+36>>2];q=y[c+32>>2];y[c+40>>2]=q;y[c+44>>2]=h;d:{e:{f:{if(!z[a+1|0]){e=y[a+60>>2];break f}if(z[n|0]){break b}if(!D){break e}e=y[a+60>>2]^ka(n,C);y[a+60>>2]=e}if(e){y[c+20>>2]=j;y[c+16>>2]=k;y[c+24>>2]=q;y[c+28>>2]=h;y[c+8>>2]=q;y[c+12>>2]=h;h=y[c+20>>2];y[c>>2]=y[c+16>>2];y[c+4>>2]=h;Pa(c+8|0,c);break d}f=0;v=0;x=0;if(z[a+2|0]==1){ja(c+32|0,y[a+12>>2],y[a+24>>2]);x=y[c+32>>2];v=y[c+36>>2]}ja(c+32|0,i,y[a+24>>2]+w[a+17|0]|0);E=y[c+36>>2];F=y[c+32>>2];d=y[i+8>>2];f=!l|(d|0)<3?f:z[m|0]>>>5&1;f=!l|(d|0)<2?f:z[m|0]>>>5&2|f;f=!l|(d|0)<=0?f:z[m|0]>>>5&4|f;g=0;g=!j|(d|0)<3?g:z[k|0]>>>5&1;g=!j|(d|0)<2?g:z[k|0]>>>5&2|g;g=!j|(d|0)<=0?g:z[k|0]>>>5&4|g;s=0;e=0;while(1){if(B[a+4>>2]<=e>>>0){break d}g:{h:{if(z[a+2|0]==1){if(ia(y[y[a+12>>2]+8>>2],e,x,v)){break h}}d=ia(d,w[a+16|0]+e|0,F,E);if(z[n|0]){break b}d=g<<4|f<<9|d<<3|s;if(d>>>0>=t>>>0){break e}r=ka(n,(d<<3)+u|0);d=y[i+8>>2];if(!r){break h}La(d,e,q,h,1);d=y[i+8>>2];r=1;break g}r=0}A=e+3|0;f=ia(d,A,m,l)|f<<1&14;g=ia(d,A,k,j)|g<<1&30;e=e+1|0;s=s<<1&6|r;continue}}o()}h=y[b+16>>2];if(!h){break c}if(!(_[y[y[h>>2]+8>>2]](h)|0)){break c}y[a+24>>2]=y[a+24>>2]+1;p=3}y[a+52>>2]=p;break a}e=y[a+24>>2]+1|0;y[a+24>>2]=e;m=k;l=j;continue}break}p=-1}Y=c+48|0;return p|0}function le(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,x=0,A=0,C=0,D=0,E=0;c=Y-48|0;Y=c;l=y[b+4>>2];s=y[b+12>>2];t=y[b+8>>2];i=y[y[b>>2]>>2];ja(c+40|0,i,y[a+24>>2]-1|0);ja(c+32|0,i,y[a+24>>2]-2|0);A=t+1832|0;m=y[c+36>>2];n=y[c+32>>2];d=y[a+24>>2];C=s>>>0>229;a:{while(1){b:{c:{if(B[a+8>>2]<=d>>>0){d=4}else{j=y[c+44>>2];k=y[c+40>>2];ja(c+32|0,i,d);f=y[c+36>>2];p=y[c+32>>2];y[c+40>>2]=p;y[c+44>>2]=f;d:{e:{f:{if(!z[a+1|0]){d=y[a+60>>2];break f}if(z[l|0]){break b}if(!C){break e}d=y[a+60>>2]^ka(l,A);y[a+60>>2]=d}if(d){y[c+20>>2]=j;y[c+16>>2]=k;y[c+24>>2]=p;y[c+28>>2]=f;y[c+8>>2]=p;y[c+12>>2]=f;f=y[c+20>>2];y[c>>2]=y[c+16>>2];y[c+4>>2]=f;Pa(c+8|0,c);break d}g=0;u=0;v=0;if(z[a+2|0]==1){ja(c+32|0,y[a+12>>2],y[a+24>>2]);v=y[c+32>>2];u=y[c+36>>2]}ja(c+32|0,i,y[a+24>>2]+w[a+17|0]|0);D=y[c+36>>2];E=y[c+32>>2];e=y[i+8>>2];g=!m|(e|0)<2?g:z[n|0]>>>6&1;g=!m|(e|0)<=0?g:z[n|0]>>>6&2|g;h=0;h=!j|(e|0)<2?h:z[k|0]>>>6&1;h=!j|(e|0)<=0?h:z[k|0]>>>6&2|h;r=0;d=0;while(1){if(B[a+4>>2]<=d>>>0){break d}g:{h:{if(z[a+2|0]==1){if(ia(y[y[a+12>>2]+8>>2],d,v,u)){break h}}e=ia(e,w[a+16|0]+d|0,E,D);if(z[l|0]){break b}e=h<<3|g<<7|e<<2|r;if(e>>>0>=s>>>0){break e}q=ka(l,(e<<3)+t|0);e=y[i+8>>2];if(!q){break h}La(e,d,p,f,1);e=y[i+8>>2];q=1;break g}q=0}x=d+2|0;g=ia(e,x,n,m)|g<<1&6;h=ia(e,x,k,j)|h<<1&14;d=d+1|0;r=r<<1&2|q;continue}}o()}f=y[b+16>>2];if(!f){break c}if(!(_[y[y[f>>2]+8>>2]](f)|0)){break c}y[a+24>>2]=y[a+24>>2]+1;d=3}y[a+52>>2]=d;break a}d=y[a+24>>2]+1|0;y[a+24>>2]=d;n=k;m=j;continue}break}d=-1}Y=c+48|0;return d|0}function ec(a,b,c,d,e,f,g,h){var i=0,j=0,k=0,l=0,m=0,n=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,A=0,C=0,D=0,E=0;k=((b&536870911)<<3|a>>>29)&-8;m=e;n=g;p=d;q=f;r=1;e=-1;a:{b:{c:while(1){j=r&1;u=j?326:325;v=j?1392:1056;w=j?325:326;x=j?1056:1392;d:while(1){A=e+1|0;t=e>>>3|0;C=t+q|0;D=1<<((e^-1)&7);d=y[c>>2];E=(e|0)<0;while(1){if(d>>>0>=k>>>0){break b}if(E){f=1}else{if(n>>>0<=t>>>0){break a}f=(z[C|0]&D)!=0}d=h;g=d;l=f^1;i=cc(q,n,d,A,l);s=d;e:{if((i|0)>=(d|0)){break e}f:{if((f|0)==(j|0)){l=f;break f}i=cc(q,n,h,i+1|0,f)}s=h;if((d|0)<=(i|0)){break e}g=i;s=cc(q,n,d,i+1|0,l)}d=s;g:{f=0;h:{if(Ka(a,b,c)){break h}if(B[c>>2]>=k>>>0){break b}i=Ka(a,b,c);if(B[c>>2]>=k>>>0){break b}l=Ka(a,b,c);f=l?1:-1;if(i){break h}if(l){d=0;while(1){f=bc(x,w,a,b,c);d=f+d|0;if((f|0)>63){continue}break}d=(e>>>31|0)+d|0;if((d|0)<0){break b}f=d+e|0;if(!j){lb(p,m,h,e,f)}d=0;while(1){e=bc(v,u,a,b,c);d=e+d|0;if((e|0)>63){continue}break}if((d|0)<0){break b}e=d+f|0;if(j){lb(p,m,h,f,e)}if((e|0)<(h|0)){continue d}break b}if(B[c>>2]>=k>>>0){break b}if(Ka(a,b,c)){if(!j){lb(p,m,h,e,d)}e=d;if((d|0)<(h|0)){continue d}break b}if(B[c>>2]>=k>>>0){break b}d=Ka(a,b,c);if(B[c>>2]>=k>>>0){break b}i=Ka(a,b,c);f=i?2:-2;if(d){break h}d=y[c>>2];if(!i){break g}if(d>>>0>=k>>>0){break b}f=Ka(a,b,c)?3:-3}d=f+g|0;if(!j){lb(p,m,h,e,d)}if((d|0)<=(e|0)|(d|0)>=(h|0)){break b}r=r^1;e=d;continue c}if(d>>>0>=k>>>0){break b}f=Ka(a,b,c);d=y[c>>2];if(f){d=d+3|0;y[c>>2]=d;continue}break}break}break}y[c>>2]=d+5}return}o()}function uc(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,p=0,q=0,r=0,s=0,t=0,u=0;f=Y-32|0;Y=f;m=xa(y[a>>2],y[a+4>>2]);y[f+28>>2]=m;a:{if(!qa(m)){break a}Qa(m,z[a+20|0]);b:{while(1){if(!k&l>>>0>=B[a+36>>2]|k){y[f+28>>2]=0;d=m;break a}d=y[b>>2];g=y[b+4>>2];e=0;y[f+24>>2]=0;y[f+16>>2]=0;y[f+20>>2]=0;c=g-d|0;h=c>>2;c:{d:{if((d|0)==(g|0)){j=0;break d}if(h>>>0>=536870912){break c}j=na(c<<1);y[f+16>>2]=j;e=(h<<3)+j|0;y[f+24>>2]=e;c=j;while(1){if((c|0)!=(e|0)){y[c>>2]=0;y[c+4>>2]=0;c=c+8|0;continue}break}y[f+20>>2]=e}if(h>>>0>e-j>>3>>>0){break b}c=j;while(1){if((d|0)==(g|0)){e:{c=y[a+44>>2];d=se(l,k,A[a+48>>1]);e=c+d|0;q=e;c=Z+(c>>31)|0;r=d>>>0>e>>>0?c+1|0:c;c=y[a+40>>2];d=se(l,k,A[a+50>>1]);e=c+d|0;s=e;c=Z+(c>>31)|0;t=d>>>0>e>>>0?c+1|0:c;g=0;e=0;f:while(1){if(!e&B[a+32>>2]<=g>>>0|e){break e}i=y[b>>2];p=y[b+4>>2]-i>>2;c=0;d=0;while(1){h=c&255;if(h>>>0>=p>>>0){c=y[a+12>>2]-1|0;h=y[y[y[a+16>>2]>>2]+((c>>>0>>0?c:d)<<2)>>2];d=se(g,e,A[a+48>>1]);c=d+s|0;i=Z+t|0;d=c>>>0>>0?i+1|0:i;i=se(g,e,A[a+50>>1]);p=q-i|0;u=(d&255)<<24|c>>>8;n=d>>8;c=r-(Z+(i>>>0>q>>>0)|0)|0;d=c>>8;Hb(h,m,u,n,(c&255)<<24|p>>>8,d,y[a+24>>2]);g=g+1|0;e=g?e:e+1|0;continue f}else{n=(h<<3)+j|0;d=d|ia(y[y[i+(h<<2)>>2]+8>>2],g,y[n>>2],y[n+4>>2])<>2],l);e=y[f+12>>2];y[c>>2]=y[f+8>>2];y[c+4>>2]=e;c=c+8|0;d=d+4|0;continue}break}l=l+1|0;k=l?k:k+1|0;ya(f+16|0);continue}break}Ba()}o()}la(f+28|0);Y=f+32|0;return d}function sb(a,b,c,d,e){var f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,q=0,r=0,s=0,t=0,u=0;f=Y-16|0;Y=f;a:{j=xa(d,e);if(!qa(j)){break a}if(!qa(a)|(b|0)<0|(c|0)<0|y[a+8>>2]<=(b|0)){break a}g=y[a+12>>2];if((g|0)<=(c|0)){break a}b:{if(!(b&7)){d=y[a+16>>2];b=b>>>3|0;if(d>>>0<=b>>>0){break b}d=d-b|0;e=y[j+16>>2];d=d>>>0>>0?d:e;if(!d){break b}e=0;h=g-c|0;i=y[j+12>>2];h=(h|0)<(i|0)?h:i;h=(h|0)>0?h:0;while(1){if((e|0)==(h|0)){break a}ja(f+8|0,a,c+e|0);i=y[f+12>>2];if(i>>>0>>0|d>>>0>i-b>>>0){break b}i=y[f+8>>2];ja(f,j,e);if(d>>>0>B[f+4>>2]){break b}if(d){p(y[f>>2],b+i|0,d)}e=e+1|0;continue}}h=b>>>5|0;e=y[a+16>>2]>>>2|0;if(h>>>0>=e>>>0){break b}d=y[j+16>>2]>>>2|0;if(!d){break b}e=e-h|0;i=d>>>0>e>>>0?e:d;q=b&31;d=g-c|0;e=y[j+12>>2];d=(d|0)<(e|0)?d:e;r=(d|0)>0?d:0;s=b^-1;c:while(1){if((l|0)==(r|0)){break a}d=f+8|0;ja(d,a,c+l|0);e=y[f+8>>2];if(e&3){break b}g=y[f+12>>2]>>>2|0;if(g>>>0>>0){break b}y[f+12>>2]=g-h;y[f+8>>2]=e+(h<<2);Ca(f,j,l);if(B[f+4>>2]>>0){break b}e=y[f>>2];g=_a(d);d=y[f+8>>2];n=y[f+12>>2];k=i>>>0>n>>>0?n:i;if(k>>>0>B[f+12>>2]){break b}k=(k<<2)+e|0;g=te(g,24)&16711935|te(g&16711935,8);while(1)if((e|0)==(k|0)){if(i>>>0>n>>>0){d=g<>2]=u}l=l+1|0;continue c}else{m=g<>2];g=te(g&16711935,8)|te(g,24)&16711935;m=m|g>>>1>>>s;t=e,u=te(m&16711935,8)|te(m,24)&16711935,y[t>>2]=u;d=d+4|0;e=e+4|0;continue}}}o()}Y=f+16|0;return j}function je(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,x=0,A=0;c=Y-48|0;Y=c;i=y[b+4>>2];q=y[b+12>>2];r=y[b+8>>2];h=y[y[b>>2]>>2];ja(c+40|0,h,y[a+24>>2]-1|0);u=r+3240|0;d=y[a+24>>2];v=q>>>0>405;j=4;a:{while(1){b:{c:{if(B[a+8>>2]>d>>>0){k=y[c+44>>2];l=y[c+40>>2];ja(c+32|0,h,d);f=y[c+36>>2];m=y[c+32>>2];y[c+40>>2]=m;y[c+44>>2]=f;d:{e:{f:{if(!z[a+1|0]){d=y[a+60>>2];break f}if(z[i|0]){break b}if(!v){break e}d=y[a+60>>2]^ka(i,u);y[a+60>>2]=d}if(d){y[c+20>>2]=k;y[c+16>>2]=l;y[c+24>>2]=m;y[c+28>>2]=f;y[c+8>>2]=m;y[c+12>>2]=f;f=y[c+20>>2];y[c>>2]=y[c+16>>2];y[c+4>>2]=f;Pa(c+8|0,c);break d}g=0;s=0;t=0;if(z[a+2|0]==1){ja(c+32|0,y[a+12>>2],y[a+24>>2]);t=y[c+36>>2];s=y[c+32>>2]}ja(c+32|0,h,y[a+24>>2]+w[a+17|0]|0);x=y[c+36>>2];A=y[c+32>>2];e=y[h+8>>2];g=!k|(e|0)<2?g:z[l|0]>>>6&1;g=!k|(e|0)<=0?g:z[l|0]>>>6&2|g;p=0;d=0;while(1){if(B[a+4>>2]<=d>>>0){break d}g:{h:{if(z[a+2|0]==1){if(ia(y[y[a+12>>2]+8>>2],d,s,t)){break h}}e=ia(e,w[a+16|0]+d|0,A,x);if(z[i|0]){break b}e=g<<5|e<<4|p;if(e>>>0>=q>>>0){break e}n=ka(i,(e<<3)+r|0);e=y[h+8>>2];if(!n){break h}La(e,d,m,f,1);e=y[h+8>>2];n=1;break g}n=0}g=ia(e,d+2|0,l,k)|g<<1&30;d=d+1|0;p=p<<1&14|n;continue}}o()}f=y[b+16>>2];if(!f){break c}if(!(_[y[y[f>>2]+8>>2]](f)|0)){break c}y[a+24>>2]=y[a+24>>2]+1;j=3}y[a+52>>2]=j;break a}d=y[a+24>>2]+1|0;y[a+24>>2]=d;continue}break}j=-1}Y=c+48|0;return j|0}function Hd(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0;c=y[b+56>>2];d=y[b+60>>2];f=b+48|0;Hc(c,d,f);h=((d&536870911)<<3|c>>>29)&-8;if(h>>>0<=B[b+48>>2]){y[a>>2]=0;y[a+4>>2]=0;return}Ic(y[b+64>>2],y[b+68>>2]);i=b- -64|0;c=y[b+44>>2];a:{if((c|0)<0){e=y[b+64>>2];c=y[b+68>>2]-e|0;d=0;l=c;g=y[b+76>>2];c=y[b+80>>2]-g|0;ec(y[b+56>>2],y[b+60>>2],f,e|d,l,d|g,c,y[b+4>>2]);Gc(b+76|0,i);break a}if(!c){d=y[b+64>>2];c=y[b+68>>2]-d|0;Fc(y[b+56>>2],y[b+60>>2],f,d,c,y[b+4>>2]);break a}e=Ka(y[b+56>>2],y[b+60>>2],f);g=y[b+64>>2];c=y[b+68>>2]-g|0;d=y[b+56>>2];j=y[b+60>>2];b:{if(e){Fc(d,j,f,g,c,y[b+4>>2]);break b}l=d;d=0;e=c;k=y[b+76>>2];c=y[b+80>>2]-k|0;ec(l,j,f,d|g,e,d|k,c,y[b+4>>2])}Gc(b+76|0,i)}if(z[b+53|0]==1){Hc(y[b+56>>2],y[b+60>>2],f)}c:{d:{e:{if(z[b+52|0]!=1){break e}d=y[f>>2];if(h>>>0<=d>>>0){break e}e=d+7&-8;i=y[b+56>>2];g=y[b+60>>2];while(1){c=d>>>3|0;h=c+i|0;j=1<<((d^-1)&7);k=c>>>0>>0;c=1;f:{while(1){if(!c|d>>>0>=e>>>0){break f}if(!k){break d}if(z[h|0]&j){c=0;w[b+52|0]=0;continue}break}d=d+1|0;continue}break}if(!c){break e}y[f>>2]=e}c=y[b+64>>2];if(!z[b+54|0]){d=y[b+68>>2]-c|0;break c}if(c&3){break d}d=y[b+68>>2]-c|0;e=c+(d&-4)|0;b=c;while(1){if((b|0)==(e|0)){break c}y[b>>2]=y[b>>2]^-1;b=b+4|0;continue}}o()}y[a+4>>2]=d;y[a>>2]=c}function hd(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,i=0;d=Y-32|0;Y=d;i=y[a+24>>2];y[d+16>>2]=0;a:{b:{switch(z[a+3|0]){case 0:c=xd(a)?3:4;break a;case 1:c=wd(a)?5:6;break a;case 2:c=Qb(a)?7:8;break a;default:break b}}c=Qb(a)?9:10}e=Y-48|0;Y=e;y[e+24>>2]=0;if(c){y[e+16>>2]=0;y[e+12>>2]=c;y[e+8>>2]=2600;g=e+8|0;y[e+24>>2]=g}h=e+8|0;c:{if((h|0)==(d|0)){break c}c=y[d+16>>2];if((g|0)==(h|0)){if((d|0)==(c|0)){f=e+32|0;_[y[y[g>>2]+12>>2]](g,f);c=y[e+24>>2];_[y[y[c>>2]+16>>2]](c);y[e+24>>2]=0;c=y[d+16>>2];_[y[y[c>>2]+12>>2]](c,h);c=y[d+16>>2];_[y[y[c>>2]+16>>2]](c);y[d+16>>2]=0;y[e+24>>2]=h;_[y[y[e+32>>2]+12>>2]](f,d);_[y[y[e+32>>2]+16>>2]](f);y[d+16>>2]=d;break c}_[y[y[g>>2]+12>>2]](g,d);c=y[e+24>>2];_[y[y[c>>2]+16>>2]](c);y[e+24>>2]=y[d+16>>2];y[d+16>>2]=d;break c}if((d|0)==(c|0)){f=e+8|0;_[y[y[c>>2]+12>>2]](c,f);c=y[d+16>>2];_[y[y[c>>2]+16>>2]](c);y[d+16>>2]=y[e+24>>2];y[e+24>>2]=f;break c}y[e+24>>2]=c;y[d+16>>2]=g}gd(e+8|0);Y=e+48|0;c=y[y[b>>2]>>2];f=y[d+16>>2];y[d+28>>2]=b;if(f){f=_[y[y[f>>2]+24>>2]](f,a,d+28|0)|0;y[a+64>>2]=0;y[a+52>>2]=f;b=y[c+8>>2];y[a+68>>2]=i;y[a+72>>2]=b;y[a+76>>2]=y[a+24>>2];if((f|0)==4){y[a+24>>2]=0}gd(d);Y=d+32|0;return f}a=Fb(4);y[a>>2]=5228;P(a|0,5240,11);o()}function hb(a,b,c){var d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,q=0,r=0,s=0,t=0,u=0;d=Y-32|0;Y=d;h=xa(y[a+4>>2],y[a+8>>2]);y[d+8>>2]=h;Da(d,h);a:{l=y[d+4>>2];if(!l){oa(b,0);y[a+52>>2]=-1;break a}b:{f=y[h+16>>2];if((f|0)<0|!f){break b}j=y[c+12>>2]+(y[c+8>>2]<<3)|0;i=y[a+8>>2];n=y[a+4>>2];q=y[c>>2];r=y[c+4>>2];k=y[d>>2];y[d+20>>2]=0;y[d+12>>2]=0;y[d+16>>2]=0;g=d+12|0;y[d+24>>2]=g;jc(g,f);e=y[d+16>>2];g=f+e|0;while(1){if((e|0)!=(g|0)){w[e|0]=255;e=e+1|0;continue}break}w[d+28|0]=1;y[d+16>>2]=g;fd(d+24|0);m=y[d+12>>2];g=y[d+16>>2];y[d+24>>2]=j;j=0;s=(i|0)>0?i:0;e=0;g=g-m|0;t=e|m;while(1){if((j|0)!=(s|0)){i=k;e=f;if(l>>>0>>0){break b}while(1){if((e|0)>0){w[i|0]=255;e=e-1|0;i=i+1|0;continue}break}ec(q,r,d+24|0,k|u,f,t,g,n);if(f>>>0>g>>>0){break b}if(f){p(m,k,f)}j=j+1|0;l=l-f|0;k=f+k|0;continue}break}f=y[d+24>>2];g=d+12|0;ya(g);y[c+12>>2]=f&7;y[c+8>>2]=f>>>3;Da(g,h);e=y[d+12>>2];c=e+y[d+16>>2]|0;while(1)if((c|0)==(e|0)){y[a+64>>2]=0;y[a+52>>2]=4;c=y[h+8>>2];y[a+68>>2]=0;y[a+72>>2]=c;y[a+76>>2]=y[h+12>>2];y[d+8>>2]=0;oa(b,h);break a}else{w[e|0]=z[e|0]^-1;e=e+1|0;continue}}o()}la(d+8|0);Y=d+32|0}function ae(a,b,c,d,e,f,g,h,i,j){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;i=i|0;j=j|0;var k=0,l=0;k=Y-16|0;Y=k;l=i?i:c;a:{if((l|0)<=0){break a}j=j?j:d;if((j|0)<=0|(j|l)>>>0>65535){break a}i=na(88);y[i+40>>2]=0;y[i+32>>2]=-1;y[i+36>>2]=0;y[i+20>>2]=1;y[i+24>>2]=1;y[i+16>>2]=j;y[i+12>>2]=l;y[i+8>>2]=j;y[i+4>>2]=l;y[i+56>>2]=a;y[i+60>>2]=b;w[i+54|0]=(h|0)!=0;w[i+53|0]=(f|0)!=0;w[i+52|0]=(g|0)!=0;y[i+48>>2]=0;y[i+44>>2]=e;y[i>>2]=2e3;a=l+31>>>3&16380;y[i+28>>2]=a;od(i- -64|0,a);od(i+76|0,y[i+28>>2]);e=c+7>>>3|0;W(E(e,d)|0);a=0;while(1){b:{if((a|0)==(d|0)){break b}c:{j=y[i+32>>2];b=a+1|0;if((j|0)==(b|0)){j=y[i+40>>2];c=y[i+36>>2];break c}d:{if(!((j|0)>=0&(a|0)>=(j|0))){j=0;if(!(_[y[y[i>>2]+12>>2]](i)|0)){break b}c=0;break d}c=1}while(1){if(!c){y[i+32>>2]=j;c=1;continue}e:{if((a|0)>(j|0)){_[y[y[i>>2]+16>>2]](k+8|0,i);j=y[i+32>>2]+1|0;break e}_[y[y[i>>2]+16>>2]](k+8|0,i);j=y[k+12>>2];c=y[k+8>>2];y[i+36>>2]=c;y[i+40>>2]=j;y[i+32>>2]=y[i+32>>2]+1;break c}c=0;continue}}if(!j){break b}V(c|0,e|0,E(a,e)|0);a=b;continue}break}_[y[y[i>>2]+4>>2]](i)}Y=k+16|0}function Fd(a){var b=0,c=0,d=0,e=0,f=0,g=0,h=0;b=y[1508];c=a+7&-8;a=c+7|0;d=a>>>0<7?1:d;e=a&-8;a=e+b|0;a:{b:{if(!(a>>>0>>0?d+1|0:d)){if(a>>>0<=$()<<16>>>0){break b}if(R(a|0)|0){break b}}y[1516]=48;b=-1;break a}y[1508]=a}d=b;if((d|0)!=-1){a=d+c|0;y[a-4>>2]=16;f=a-16|0;y[f>>2]=16;b=y[1776];if(b){e=y[b+8>>2]}else{e=0}c:{d:{if((e|0)==(d|0)){e=y[d-4>>2]&-2;g=d-e|0;h=y[g-4>>2];y[b+8>>2]=a;b=h&-2;a=g-b|0;if(y[(a+y[a>>2]|0)-4>>2]&1){f=y[a+4>>2];g=y[a+8>>2];y[f+8>>2]=g;y[g+4>>2]=f;b=(b+(c+e|0)|0)-16|0;y[a>>2]=b;break c}a=d-16|0;break d}y[d>>2]=16;y[d+8>>2]=a;y[d+4>>2]=b;y[d+12>>2]=16;y[1776]=d;a=d+16|0}b=f-a|0;y[a>>2]=b}y[((b&-4)+a|0)-4>>2]=b|1;c=y[a>>2]-8|0;e:{if(c>>>0<=127){b=(c>>>3|0)-1|0;break e}e=H(c);b=((c>>>29-e^4)-(e<<2)|0)+110|0;if(c>>>0<=4095){break e}b=((c>>>30-e^2)-(e<<1)|0)+71|0;b=b>>>0>=63?63:b}c=b<<4;y[a+4>>2]=c+6080;c=c+6088|0;y[a+8>>2]=y[c>>2];y[c>>2]=a;y[y[a+8>>2]+4>>2]=a;c=y[1779];a=b&31;if((b&63)>>>0>=32){b=1<>>32-a}y[1778]=e|y[1778];y[1779]=b|c}return(d|0)!=-1}function Bb(a,b){var c=0,d=0,e=0;c=Y+-64|0;Y=c;d=y[a>>2];e=y[d-8>>2];d=y[d-4>>2];a:{if(y[d+4>>2]==y[b+4>>2]){a=e?0:a;break a}e=a+e|0;if((e|0)<=(a|0)){y[c+16>>2]=0;y[c+20>>2]=0;y[c+12>>2]=b;y[c+8>>2]=a;y[c+4>>2]=d;y[c+24>>2]=0;y[c+28>>2]=0;y[c+32>>2]=0;y[c+36>>2]=0;y[c+40>>2]=0;y[c+44>>2]=0;y[c+48>>2]=0;y[c+60>>2]=0;y[c+52>>2]=1;y[c+56>>2]=16777216;_[y[y[d>>2]+20>>2]](d,c+4|0,e,e,1,0);if(y[c+28>>2]){break a}}y[c+16>>2]=0;y[c+20>>2]=0;y[c+12>>2]=5336;y[c+8>>2]=a;y[c+4>>2]=b;y[c+24>>2]=0;y[c+28>>2]=0;y[c+32>>2]=0;y[c+36>>2]=0;y[c+40>>2]=0;y[c+44>>2]=0;y[c+48>>2]=0;y[c+52>>2]=0;a=0;w[c+55|0]=0;w[c+56|0]=0;w[c+57|0]=0;w[c+58|0]=0;y[c+60>>2]=0;w[c+59|0]=1;_[y[y[d>>2]+24>>2]](d,c+4|0,e,1,0);b:{switch(y[c+40>>2]){case 0:a=y[c+44>>2]==1?y[c+32>>2]==1?y[c+36>>2]==1?y[c+24>>2]:0:0:0;break a;case 1:break b;default:break a}}if(y[c+28>>2]!=1){if(y[c+44>>2]|y[c+32>>2]!=1|y[c+36>>2]!=1){break a}}a=y[c+20>>2]}Y=c- -64|0;return a}function ud(a,b){var c=0,d=0,e=0,f=0,g=0,h=0;d=Y-32|0;Y=d;a:{b:{c:{d:{e:{c=y[a+4>>2];if(c>>>0>>0){g=b-c|0;f=y[a+8>>2];e=f<<5;if(!(g>>>0>e>>>0|c>>>0>e-g>>>0)){y[a+4>>2]=b;e=c&31;b=y[a>>2]+(c>>>3&536870908)|0;break b}y[d+16>>2]=0;y[d+8>>2]=0;y[d+12>>2]=0;if((b|0)<0){break e}c=2147483647;if(e>>>0<=1073741822){e=f<<6;b=b+31&-32;c=b>>>0>>0?e:b;if((c|0)<0){break e}}e=(c-1>>>5|0)+1|0;b=na(e<<2);y[d+24>>2]=0;y[d+28>>2]=0;y[d+8>>2]=b;y[d+20>>2]=0;y[d+16>>2]=e;y[d+24>>2]=0;Ob(d+20|0);e=y[a+4>>2];y[d+12>>2]=e+g;if((e|0)<=0){break d}f=y[a>>2];h=e>>>5|0;c=h<<2;if(!(!h|!c)){p(b,f,c)}b=(h<<2)+b|0;e=e&31;if(!e){break d}f=y[c+f>>2];c=y[b>>2];y[b>>2]=(f^c)&-1>>>32-e^c;break c}y[a+4>>2]=b;break a}Ba();o()}e=0}c=y[a>>2];y[a>>2]=y[d+8>>2];y[d+8>>2]=c;c=y[a+4>>2];y[a+4>>2]=y[d+12>>2];y[d+12>>2]=c;c=y[a+8>>2];y[a+8>>2]=y[d+16>>2];y[d+16>>2]=c;Ob(d+8|0)}Xc(b,e,g);Pb(d+20|0,b,e,g)}Y=d+32|0}function cc(a,b,c,d,e){var f=0,g=0,h=0,i=0;a:{b:{if((c|0)<=(d|0)){break b}h=e-1|0;c:{f=(d|0)/8|0;g=f<<3;if((g|0)!=(d|0)){if(b>>>0<=f>>>0){break a}f=(z[a+f|0]^h)&255>>>d-g;if(f){break c}f=(d+7|0)/8|0}g=(c+7|0)/8|0;d:{if((c|0)<57){break d}i=g-8|0;if((i|0)<=(f|0)){break d}d=e?1718:1726;while(1){if((f|0)>=(i|0)){break d}if(b>>>0>>0|b-f>>>0<=7){break a}e=a+f|0;if((z[d|0]|z[d+1|0]<<8|(z[d+2|0]<<16|z[d+3|0]<<24))!=(z[e|0]|z[e+1|0]<<8|(z[e+2|0]<<16|z[e+3|0]<<24))|(z[e+4|0]|z[e+5|0]<<8|(z[e+6|0]<<16|z[e+7|0]<<24))!=(z[d+4|0]|z[d+5|0]<<8|(z[d+6|0]<<16|z[d+7|0]<<24))){break d}f=f+8|0;continue}}b=b>>>0>>0?f:b;d=(f|0)>(g|0)?f:g;e=h&255;while(1){if((d|0)==(f|0)){break b}if((b|0)==(f|0)){break a}g=z[a+f|0];if((g|0)!=(e|0)){a=z[((g^h)&255)+1734|0]+(f<<3)|0;return(a|0)>(c|0)?c:a}else{f=f+1|0;continue}}}c=g+z[f+1734|0]|0}return c}o()}function dc(a,b){var c=0,d=0,e=0,f=0,g=0;e=a+4|0;d=e+7&-8;c=y[a>>2];if(d+b>>>0<=(c+a|0)-4>>>0){f=y[a+4>>2];g=y[a+8>>2];y[f+8>>2]=g;y[g+4>>2]=f;if((d|0)!=(e|0)){d=d-e|0;f=a-(y[a-4>>2]&-2)|0;e=d+y[f>>2]|0;y[f>>2]=e;y[(f+(e&-4)|0)-4>>2]=e;a=a+d|0;c=c-d|0;y[a>>2]=c}a:{if(b+24>>>0<=c>>>0){e=a+b|0;c=(c-b|0)-8|0;y[e+8>>2]=c;g=e+8|0;y[(g+(c&-4)|0)-4>>2]=c|1;d=y[e+8>>2]-8|0;b:{if(d>>>0<=127){c=(d>>>3|0)-1|0;break b}f=H(d);c=((d>>>29-f^4)-(f<<2)|0)+110|0;if(d>>>0<=4095){break b}c=((d>>>30-f^2)-(f<<1)|0)+71|0;c=c>>>0>=63?63:c}d=c<<4;y[e+12>>2]=d+6080;d=d+6088|0;y[e+16>>2]=y[d>>2];y[d>>2]=g;y[y[e+16>>2]+4>>2]=g;d=y[1778];f=y[1779];e=c&31;if((c&63)>>>0>=32){c=1<>>32-e}y[1778]=g|d;y[1779]=c|f;c=b+8|0;y[a>>2]=c;b=(c&-4)+a|0;break a}b=a+c|0}y[b-4>>2]=c;a=a+4|0}else{a=0}return a}function ma(a){a=a|0;var b=0,c=0,d=0,e=0,f=0;if(a){b=a-4|0;f=y[b>>2];c=f;d=b;e=y[a-8>>2];a=e&-2;if((a|0)!=(e|0)){d=b-a|0;c=y[d+4>>2];e=y[d+8>>2];y[c+8>>2]=e;y[e+4>>2]=c;c=a+f|0}a=b+f|0;b=y[a>>2];if((b|0)!=y[(a+b|0)-4>>2]){f=y[a+4>>2];a=y[a+8>>2];y[f+8>>2]=a;y[a+4>>2]=f;c=b+c|0}y[d>>2]=c;y[((c&-4)+d|0)-4>>2]=c|1;b=y[d>>2]-8|0;a:{if(b>>>0<=127){a=(b>>>3|0)-1|0;break a}c=H(b);a=((b>>>29-c^4)-(c<<2)|0)+110|0;if(b>>>0<=4095){break a}a=((b>>>30-c^2)-(c<<1)|0)+71|0;a=a>>>0>=63?63:a}b=a<<4;y[d+4>>2]=b+6080;b=b+6088|0;y[d+8>>2]=y[b>>2];y[b>>2]=d;y[y[d+8>>2]+4>>2]=d;b=y[1778];c=y[1779];d=a&31;if((a&63)>>>0>=32){a=1<>>32-d}y[1778]=e|b;y[1779]=a|c}}function ob(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0;d=Y-32|0;Y=d;g=(b<<3)+a|0;b=a;while(1){if((b|0)==(g|0)){a:{b=f+1|0;i=md(d+20|0,b);j=md(d+8|0,b);h=y[i>>2];b=a;while(1)if((b|0)==(g|0)){y[h>>2]=0;k=((f|0)>0?f:0)+1|0;l=y[j>>2];c=1;while(1){if((c|0)==(k|0)){c=k;break a}b=c<<2;e=b-4|0;y[d+4>>2]=y[e+l>>2];w[d|0]=1;if(z[Tb(Na(d,y[e+h>>2]))|0]!=1){break a}e=y[d+4>>2];y[b+l>>2]=e;b=a;while(1){if((b|0)!=(g|0)){if(y[b>>2]==(c|0)){y[b+4>>2]=e;e=e+1|0}b=b+8|0;continue}break}c=c+1|0;continue}}else{c=(y[b>>2]<<2)+h|0;y[c>>2]=y[c>>2]+1;b=b+8|0;continue}}}else{c=y[b>>2];f=(c|0)>(f|0)?c:f;b=b+8|0;continue}break}ya(j);ya(i);Y=d+32|0;return(c|0)>(f|0)}function za(a,b,c){var d=0,e=0,f=0,g=0,h=0,i=0,j=0;e=Y-16|0;Y=e;y[e+12>>2]=0;w[e+8|0]=1;h=-1;a:{b:while(1){if((wb(y[a>>2],e+4|0)|0)==-1){break a}d=Tb(e+8|0);if(z[d|0]!=1){break a}g=y[e+12>>2]|y[e+4>>2];f=(g|0)>=0;y[d>>2]=f;g=f?g:0;y[d+4>>2]=g;if(f){i=i+1|0;f=y[b+4>>2];d=0;while(1){if((d|0)==(f|0)){continue b}j=y[b+8>>2]+(d<<3)|0;if(y[j>>2]!=(i|0)|y[j+4>>2]!=(g|0)){d=d+1|0;continue}else{if((f-1|0)==(d|0)){h=1;if(w[b+1|0]&1){break a}}h=-1;f=y[a>>2];a=d<<2;if((Ia(f,y[a+y[b+20>>2]>>2],e+4|0)|0)==-1){break a}h=0;f=y[a+y[b+32>>2]>>2];a=y[e+4>>2];y[c>>2]=f+((y[b+4>>2]+(z[b+1|0]?-3:-2)|0)==(d|0)?0-a|0:a);break a}}}break}o()}Y=e+16|0;return h}function lb(a,b,c,d,e){var f=0,g=0,h=0;a:{d=(d|0)>0?d:0;f=(e|0)>=0?(c|0)<(e|0)?c:e:0;if((d|0)>=(f|0)){break a}b:{h=d>>>3|0;if(h>>>0>=b>>>0){break b}e=d&7;c=a+h|0;g=f-1|0;f=g>>>3|0;if((f|0)==(h|0)){a=g&7;while(1){if(a>>>0>>0){break a}w[c|0]=z[c|0]+(-1<<7-e);e=e+1|0;continue}}d=z[c|0];while(1)if((e|0)==8){w[c|0]=d;if(b>>>0<=f>>>0){break b}d=(g&7)+1|0;g=a+f|0;c=z[g|0];e=0;while(1)if((d|0)==(e|0)){w[g|0]=c;c=h+1|0;if(c>>>0>=f>>>0){break a}e=f+(h^-1)|0;if(e>>>0>b-c>>>0){break b}c=a+c|0;while(1){if((e|0)<=0){break a}w[c|0]=0;e=e-1|0;c=c+1|0;continue}}else{c=(-1<<7-e)+c|0;e=e+1|0;continue}}else{d=(-1<<7-e)+d|0;e=e+1|0;continue}}o()}}function ka(a,b){var c=0,d=0,e=0,f=0;c=y[b+4>>2];if(c>>>0<47){c=E(c,6);e=A[c+2080>>1];d=y[a+8>>2]-e|0;y[a+8>>2]=d;c=c+2080|0;f=y[a+4>>2];a:{if(d>>>0>f>>>16>>>0){if(d&32768){return z[b|0]}if(d>>>0>>0){d=z[b|0]^1;e=z[c+3|0];if(z[c+4|0]==1){w[b|0]=d&1}y[b+4>>2]=e;b=d&1;break a}y[b+4>>2]=z[c+2|0];b=z[b|0];break a}y[a+4>>2]=f-(d<<16);b:{if(d>>>0>>0){y[b+4>>2]=z[c+2|0];b=z[b|0];break b}d=z[b|0]^1;f=z[c+3|0];if(z[c+4|0]==1){w[b|0]=d&1}y[b+4>>2]=f;b=d&1}y[a+8>>2]=e}c=y[a+12>>2];while(1){if(!c){Dc(a);c=y[a+12>>2]}c=c-1|0;y[a+12>>2]=c;d=y[a+8>>2];y[a+8>>2]=d<<1;y[a+4>>2]=y[a+4>>2]<<1;if(!(d&16384)){continue}break}return b}o()}function xc(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,i=0;a:{b:{c=y[a+4>>2];e=y[a>>2];g=c-e|0;h=g>>2;c:{if(h>>>0>>0){f=b-h|0;d=y[a+8>>2];if(f>>>0<=d-c>>2>>>0){b=(f<<2)+c|0;while(1){if((b|0)!=(c|0)){y[c>>2]=0;c=c+4|0;continue}break}y[a+4>>2]=b;return}if(b>>>0>=1073741824){break b}c=d-e|0;d=c>>1;d=c>>>0>=2147483644?1073741823:b>>>0>>0?d:b;if(d>>>0>=1073741824){break a}i=na(d<<2);b=i+g|0;f=b+(f<<2)|0;c=b;while(1){if((c|0)!=(f|0)){y[c>>2]=0;c=c+4|0;continue}break}b=b-(h<<2)|0;if(g){p(b,e,g)}y[a+8>>2]=(d<<2)+i;y[a+4>>2]=f;y[a>>2]=b;if(!e){break c}ma(e);return}if(b>>>0>=h>>>0){break c}y[a+4>>2]=(b<<2)+e}return}Ba();o()}jb();o()}function Td(a,b,c,d,e){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;a:{if(Sa(a,y[b+8>>2],e)){if(y[b+28>>2]==1|y[b+4>>2]!=(c|0)){break a}y[b+28>>2]=d;return}if(Sa(a,y[b>>2],e)){if(!(y[b+16>>2]!=(c|0)&y[b+20>>2]!=(c|0))){if((d|0)!=1){break a}y[b+32>>2]=1;return}y[b+32>>2]=d;b:{if(y[b+44>>2]==4){break b}x[b+52>>1]=0;a=y[a+8>>2];_[y[y[a>>2]+20>>2]](a,b,c,c,1,e);if(z[b+53|0]==1){y[b+44>>2]=3;if(!z[b+52|0]){break b}break a}y[b+44>>2]=4}y[b+20>>2]=c;y[b+40>>2]=y[b+40>>2]+1;if(y[b+36>>2]!=1|y[b+24>>2]!=2){break a}w[b+54|0]=1;return}a=y[a+8>>2];_[y[y[a>>2]+24>>2]](a,b,c,d,e)}}function kb(a,b){var c=0,d=0,e=0,f=0,g=0;a:{c=y[a+4>>2];d=y[a>>2];e=c-d>>3;if(e>>>0>>0){g=b-e|0;f=y[a+8>>2];if(g>>>0<=f-c>>3>>>0){b=(g<<3)+c|0;while(1){if((b|0)!=(c|0)){y[c+4>>2]=0;w[c|0]=0;c=c+8|0;continue}break}y[a+4>>2]=b;return}d=Bc(d,f,b);b=y[a+4>>2]-y[a>>2]|0;e=0;if(d){e=yb(d)}b=b+e|0;g=b+(g<<3)|0;c=b;while(1){if((c|0)!=(g|0)){y[c+4>>2]=0;w[c|0]=0;c=c+8|0;continue}break}f=b;b=y[a>>2];c=y[a+4>>2]-b|0;f=f-c|0;if(c){p(f,b,c)}y[a+4>>2]=g;y[a>>2]=f;y[a+8>>2]=(d<<3)+e;if(!b){break a}ma(b);return}if(b>>>0>=e>>>0){break a}y[a+4>>2]=d+(b<<3)}}function ad(a,b){var c=0,d=0,e=0,f=0,g=0,h=0;a:{e=y[a+4>>2];c=y[a>>2];f=e-c|0;d=f>>3;b:{if(d>>>0>>0){d=b-d|0;g=y[a+8>>2];if(d>>>0<=g-e>>3>>>0){ld(a,d);return}if(b>>>0>=536870912){break a}h=d<<3;e=f;c=g-c|0;d=c>>2;f=c>>>0>=2147483640?536870911:b>>>0>>0?d:b;g=yb(f);c=e+g|0;d=h+c|0;b=c;while(1){if((b|0)!=(d|0)){y[b>>2]=0;y[b+4>>2]=0;b=b+8|0;continue}break}e=c;b=y[a>>2];c=y[a+4>>2]-b|0;e=e-c|0;if(c){p(e,b,c)}y[a+4>>2]=d;y[a>>2]=e;y[a+8>>2]=g+(f<<3);if(!b){break b}ma(b);return}if(b>>>0>=d>>>0){break b}y[a+4>>2]=c+(b<<3)}return}Ba();o()}function Gb(a,b){var c=0,d=0,e=0,f=0,g=0,h=0;a:{e=y[a+4>>2];c=y[a>>2];f=e-c|0;d=f>>2;b:{if(d>>>0>>0){d=b-d|0;g=y[a+8>>2];if(d>>>0<=g-e>>2>>>0){kd(a,d);return}if(b>>>0>=1073741824){break a}h=d<<2;e=f;c=g-c|0;d=c>>1;f=c>>>0>=2147483644?1073741823:b>>>0>>0?d:b;g=Lb(f);c=e+g|0;d=h+c|0;b=c;while(1){if((b|0)!=(d|0)){y[b>>2]=0;b=b+4|0;continue}break}e=c;b=y[a>>2];c=y[a+4>>2]-b|0;e=e-c|0;if(c){p(e,b,c)}y[a+4>>2]=d;y[a>>2]=e;y[a+8>>2]=g+(f<<2);if(!b){break b}ma(b);return}if(b>>>0>=d>>>0){break b}y[a+4>>2]=c+(b<<2)}return}Ba();o()}function Rc(a,b,c,d,e,f,g){y[a+8>>2]=0;a:{b:{if(!(b&255)){c:{switch(c|0){case 1:y[a+4>>2]=e;y[a>>2]=d;y[a+8>>2]=f-1;return;case 3:y[a+4>>2]=e;break a;case 0:y[a>>2]=d;y[a+8>>2]=f-1;y[a+4>>2]=(e-g|0)+1;return;case 2:break c;default:break b}}y[a+4>>2]=(e-g|0)+1;break a}d:{switch(c|0){case 1:y[a+4>>2]=d;y[a>>2]=e;y[a+8>>2]=g-1;return;case 3:y[a+4>>2]=d;y[a+8>>2]=g-1;y[a>>2]=(e-f|0)+1;return;case 0:y[a>>2]=e;y[a+4>>2]=(d-g|0)+1;return;case 2:break d;default:break b}}y[a+4>>2]=(d-g|0)+1;y[a>>2]=(e-f|0)+1}return}y[a>>2]=(d-f|0)+1}function bc(a,b,c,d,e){var f=0,g=0,h=0,i=0,j=0,k=0;k=((d&536870911)<<3|c>>>29)&-8;a:{b:while(1){c:{if(b>>>0<=f>>>0){break c}g=-1;i=z[a+f|0];if((i|0)==255){break a}h=y[e>>2];if(h>>>0>=k>>>0){break a}g=h>>>3|0;if(g>>>0>=d>>>0){break c}g=z[c+g|0];y[e>>2]=h+1;j=g>>>((h^-1)&7)&1|j<<1;f=f+1|0;h=f+E(i,3)|0;while(1){if((f|0)>=(h|0)){continue b}if(b>>>0<=f>>>0){break c}if(z[a+f|0]==(j|0)){c=f+1|0;if(c>>>0>=b>>>0){break c}d=b;b=f+2|0;if(d>>>0<=b>>>0){break c}g=z[a+c|0]|z[a+b|0]<<8;break a}else{f=f+3|0;continue}}}break}o()}return g}function Ia(a,b,c){var d=0,e=0,f=0,g=0;e=-1;g=y[a>>2];a:{f=y[a+8>>2];d=y[a+4>>2];if(f>>>0>=d>>>0){break a}b:{if((d|0)==536870912|d>>>0>536870912){break b}f=y[a+12>>2]+(f<<3)|0;if(f>>>0>(((d&536870911)<<3|g>>>29)&-8)>>>0){break a}y[c>>2]=0;g=y[a>>2];d=y[a+4>>2];if((d|0)==536870912|d>>>0>536870912){break b}d=((d&536870911)<<3|g>>>29)&-8;e=d>>>0>>0?d-f|0:b;while(1){if(!e){e=0;break a}b=y[a+8>>2];if(b>>>0>=B[a+4>>2]){break b}y[c>>2]=z[b+y[a>>2]|0]>>>7-y[a+12>>2]&1|y[c>>2]<<1;_b(a);e=e-1|0;continue}}o()}return e}function Aa(a,b,c){var d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0;f=Y-16|0;Y=f;i=ka(b,y[a>>2]+8|0);d=i|2;while(1){a:{if((e|0)==5){e=5;break a}g=ka(b,y[a>>2]+(d<<3)|0);d=g|d<<1;if(!g){break a}e=e+1|0;continue}break}g=0;e=e<<3;h=y[e+2364>>2];h=(h|0)>0?h:0;k=e+2364|0;e=0;while(1){if((e|0)!=(h|0)){j=ka(b,y[a>>2]+(d<<3)|0);d=j|d<<1;d=(d|0)>255?d&255|256:d;g=g<<1|j;e=e+1|0;continue}break}w[f+8|0]=1;y[f+12>>2]=y[k+4>>2];d=0;e=0;if(z[Na(f+8|0,g)|0]){a=y[f+12>>2];b=a>>31;d=i?b-(a^b)|0:a;e=!i|(d|0)!=0}y[c>>2]=d;Y=f+16|0;return e}function Sb(a,b){var c=0,d=0,e=0,f=0,g=0;a:{if((a|0)!=(b|0)){f=y[b+4>>2];d=y[b>>2];b=f-d|0;c=y[a>>2];if(b>>>0<=y[a+8>>2]-c>>>0){e=y[a+4>>2];g=e-c|0;if(b>>>0>g>>>0){if((c|0)!=(e|0)){if(g){p(c,d,g)}e=y[a+4>>2]}b=d+g|0;d=f-b|0;if(!(!d|(b|0)==(f|0))){p(e,b,d)}y[a+4>>2]=d+e;return a}if(!(!b|(d|0)==(f|0))){p(c,d,b)}y[a+4>>2]=b+c;return a}jd(a);e=Bc(y[a>>2],y[a+8>>2],b>>3);if(e>>>0>=536870912){break a}c=yb(e);y[a+4>>2]=c;y[a>>2]=c;y[a+8>>2]=c+(e<<3);if(!(!b|(d|0)==(f|0))){p(c,d,b)}y[a+4>>2]=b+c}return a}Ba();o()}function me(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,g=0,h=0;d=Y-16|0;Y=d;f=y[y[b>>2]>>2];if(!y[a+48>>2]){Da(d+8|0,f);c=y[d+12>>2];y[a+44>>2]=y[d+8>>2];y[a+48>>2]=c}c=y[a+4>>2];if(c){c=c-1|0;g=c>>>3|0;h=(c&7)+1|0;c=y[a+24>>2];while(1){a:{b:{if(B[a+8>>2]<=c>>>0){c=4}else{if(!ic(a,b,2560,g,h)){c=-1;break b}Kb(a,f);e=y[b+16>>2];c=y[a+24>>2];if(!e|(c>>>0)%50){break a}e=_[y[y[e>>2]+8>>2]](e)|0;c=y[a+24>>2];if(!e){break a}y[a+24>>2]=c+1;c=3}y[a+52>>2]=c}Y=d+16|0;return c|0}c=c+1|0;y[a+24>>2]=c;continue}}o()}function qe(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,g=0,h=0;d=Y-16|0;Y=d;e=y[y[b>>2]>>2];if(!y[a+48>>2]){Da(d+8|0,e);c=y[d+12>>2];y[a+44>>2]=y[d+8>>2];y[a+48>>2]=c}c=y[a+4>>2];if(c){f=y[a+8>>2]&2147483647;c=c-1|0;g=c>>>3|0;h=(c&7)+1|0;c=y[a+24>>2];while(1){a:{b:{if(c>>>0>=f>>>0){c=4}else{if(!ic(a,b,2496,g,h)){c=-1;break b}Kb(a,e);c=y[b+16>>2];if(!c){break a}if(!(_[y[y[c>>2]+8>>2]](c)|0)){break a}y[a+24>>2]=y[a+24>>2]+1;c=3}y[a+52>>2]=c}Y=d+16|0;return c|0}c=y[a+24>>2]+1|0;y[a+24>>2]=c;continue}}o()}function zc(a,b,c,d,e){var f=0,g=0;y[a>>2]=0;f=na(24);y[f+8>>2]=0;y[f+12>>2]=0;g=b;b=(c|0)==268435456|c>>>0<268435456;y[f>>2]=b?g:0;y[f+4>>2]=b?c:0;y[f+16>>2]=0;y[f+20>>2]=0;y[a+8>>2]=0;y[a+12>>2]=0;y[a+4>>2]=f;y[a+16>>2]=0;y[a+20>>2]=0;y[a+24>>2]=0;y[a+28>>2]=0;y[a+32>>2]=0;c=na(64);y[a+36>>2]=c;b=c- -64|0;y[a+44>>2]=b;f=0;while(1){if((f|0)!=64){y[c+f>>2]=0;f=f+4|0;continue}break}y[a+52>>2]=10;w[a+49|0]=0;w[a+50|0]=0;w[a+48|0]=e;y[a+40>>2]=b;t(a+56|0,0,49);w[a+112|0]=0;y[a+108>>2]=d;return a}function Wc(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,i=0;a:{c=y[a+4>>2];d=y[a+8>>2];b:{if(c>>>0>>0){e=y[b>>2];y[b>>2]=0;y[c>>2]=e;b=c+4|0;break b}f=y[a>>2];e=c-f|0;h=e>>2;c=h+1|0;if(c>>>0>=1073741824){break a}d=d-f|0;g=d>>1;g=d>>>0>=2147483644?1073741823:c>>>0>>0?g:c;c:{if(!g){c=e;d=0;break c}d=Lb(g);f=y[a>>2];c=y[a+4>>2]-f|0;h=c>>2}i=y[b>>2];y[b>>2]=0;b=d+e|0;y[b>>2]=i;e=b-(h<<2)|0;if(c){p(e,f,c)}y[a+8>>2]=(g<<2)+d;b=b+4|0;y[a+4>>2]=b;y[a>>2]=e;if(!f){break b}ma(f)}y[a+4>>2]=b;return}Ba();o()}function zb(a,b){var c=0;c=y[a>>2];y[a>>2]=b;if(c){ac(c+80|0);ua(c+76|0);ua(c+72|0);Ja(c+60|0);b=y[c+36>>2];if(b){a=y[c+40>>2];while(1){if((a|0)!=(b|0)){a=pa(a-4|0);continue}break}y[c+40>>2]=b;ma(y[c+36>>2])}la(c+32|0);b=y[c+20>>2];if(b){a=y[c+24>>2];while(1){if((a|0)!=(b|0)){a=$b(a-4|0);continue}break}y[c+24>>2]=b;ma(y[c+20>>2])}b=y[c+8>>2];if(b){a=y[c+12>>2];while(1){if((a|0)!=(b|0)){a=ac(a-4|0);continue}break}y[c+12>>2]=b;ma(y[c+8>>2])}a=y[c+4>>2];y[c+4>>2]=0;if(a){ma(a)}ma(Ab(c))}}function oe(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,g=0;d=Y-16|0;Y=d;e=y[y[b>>2]>>2];if(!y[a+48>>2]){Da(d+8|0,e);c=y[d+12>>2];y[a+44>>2]=y[d+8>>2];y[a+48>>2]=c}c=y[a+4>>2];if(c){c=c-1|0;f=c>>>3|0;g=(c&7)+1|0;c=y[a+24>>2];while(1){a:{b:{if(B[a+8>>2]<=c>>>0){c=4}else{if(!ic(a,b,2528,f,g)){c=-1;break b}Kb(a,e);c=y[b+16>>2];if(!c){break a}if(!(_[y[y[c>>2]+8>>2]](c)|0)){break a}y[a+24>>2]=y[a+24>>2]+1;c=3}y[a+52>>2]=c}Y=d+16|0;return c|0}c=y[a+24>>2]+1|0;y[a+24>>2]=c;continue}}o()}function Qd(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0;if(y[b+4>>2]==5604){b=y[a+12>>2];a:{if(b){a=5592;if(Bb(b,5480)){break a}}a=5600}y[c>>2]=a;return 1}e=y[a+8>>2];b:{c:{if(!(e&24)){d=Bb(b,5432);if(!d){break c}c=1;if(!Sa(a,b,(y[d+8>>2]&24)!=0)){break c}break b}c=1;if(Sa(a,b,1)){break b}}c=0;b=Bb(b,5532);if(!b){break b}d=y[b+8>>2];if(d&(e^-1)&7|(d^-1)&e&96|y[y[a+12>>2]+4>>2]!=y[y[b+12>>2]+4>>2]){break b}c=y[y[a+16>>2]+4>>2]==y[y[b+16>>2]+4>>2]}return c|0}function Wb(a,b){var c=0,d=0,e=0,f=0,g=0;e=Y-16|0;Y=e;a:{b:{f=y[a+4>>2];if(f-1>>>0<=65534){g=y[a+8>>2];if(g-1>>>0<65535){break b}}y[a+52>>2]=4;a=4;break a}y[a+52>>2]=2;c=y[b>>2];d=y[c>>2];if(!d){d=xa(f,g);y[e+12>>2]=0;oa(c,d);la(e+12|0);d=y[c>>2]}if(!qa(d)){oa(c,0);y[a+52>>2]=-1;a=-1;break a}Qa(y[c>>2],0);y[a+60>>2]=0;x[a+56>>1]=1;y[a+24>>2]=0;y[a+28>>2]=0;y[a+32>>2]=0;y[a+36>>2]=0;y[a+40>>2]=0;y[a+44>>2]=0;y[a+48>>2]=0;a=hd(a,b)}Y=e+16|0;return a}function Ba(){var a=0,b=0,c=0,d=0;c=Fb(8);y[c>>2]=5828;a=4883;b=0;a:{if(!z[4883]){break a}b:{c:{while(1){a=a+1|0;if(!(a&3)){break c}if(z[a|0]){continue}break}break b}while(1){b=a;a=a+4|0;d=y[b>>2];if(((16843008-d|d)&-2139062144)==-2139062144){continue}break}while(1){a=b;b=a+1|0;if(z[a|0]){continue}break}}b=a-4883|0}a=na(b+13|0);y[a+8>>2]=0;y[a+4>>2]=b;y[a>>2]=b;a=a+12|0;b=b+1|0;if(b){p(a,4883,b)}y[c>>2]=5876;y[c+4>>2]=a;P(c|0,5888,1);o()}function Gc(a,b){var c=0,d=0,e=0,f=0,g=0;a:{if((a|0)!=(b|0)){f=y[b+4>>2];e=y[b>>2];d=f-e|0;c=y[a+8>>2];b=y[a>>2];if(d>>>0<=c-b>>>0){g=y[a+4>>2];c=g-b|0;if(c>>>0>>0){if(!(!c|(b|0)==(g|0))){p(b,e,c)}Ec(a,c+e|0,f);return}if(!(!d|(e|0)==(f|0))){p(b,e,d)}y[a+4>>2]=b+d;return}if(b){y[a+4>>2]=b;ma(b);y[a+8>>2]=0;y[a>>2]=0;y[a+4>>2]=0;c=0}if((d|0)<0){break a}b=c<<1;jc(a,c>>>0>=1073741823?2147483647:b>>>0>d>>>0?b:d);Ec(a,e,f)}return}Ba();o()}function Dc(a){var b=0,c=0,d=0,e=0,f=0;e=255;b=y[a+16>>2];a:{if(z[a+1|0]==255){f=8;d=y[b+8>>2];c=d+1|0;if(c>>>0>=B[b+4>>2]){break a}c=z[c+y[b>>2]|0];if(c>>>0>143){break a}Cc(b);w[a+1|0]=c;y[a+4>>2]=(y[a+4>>2]-(c<<9)|0)+65024;b=y[a+16>>2];d=y[b+8>>2];f=7;break a}Cc(b);f=8;b=y[a+16>>2];d=y[b+8>>2];c=y[b>>2];e=B[b+4>>2]>d>>>0?z[c+d|0]:e;w[a+1|0]=e;y[a+4>>2]=(y[a+4>>2]-(e<<8)|0)+65280}y[a+12>>2]=f;if(B[b+4>>2]<=d>>>0){w[a|0]=1}}function Ib(a,b,c,d){var e=0,f=0,g=0,h=0,i=0;f=b-1|0;g=y[d>>2];e=y[d+4>>2];a:{if((ia(a,f,g,e)|0)!=(c|0)){break a}if((ia(a,b,g,e)|0)!=(c|0)){break a}h=b+1|0;if((ia(a,h,g,e)|0)!=(c|0)){break a}g=y[d+8>>2];e=y[d+12>>2];if((ia(a,f,g,e)|0)!=(c|0)){break a}if((ia(a,h,g,e)|0)!=(c|0)){break a}e=f;f=y[d+16>>2];d=y[d+20>>2];if((ia(a,e,f,d)|0)!=(c|0)){break a}if((ia(a,b,f,d)|0)!=(c|0)){break a}i=(ia(a,h,f,d)|0)==(c|0)}return i}function ja(a,b,c){var d=0,e=0;d=Y-16|0;Y=d;a:{b:{c:{if(!(!(!qa(b)|(c|0)<0)&y[b+12>>2]>(c|0))){w[d+8|0]=0;break c}e=y[b+16>>2];if((e|0)<0){break b}c=se(e,0,c);if(Z){break b}y[d+8>>2]=c;e=1}w[d+12|0]=e;break a}o()}d:{e:{if(!z[d+12|0]){y[a>>2]=0;y[a+4>>2]=0;break e}Da(d,b);e=y[d+4>>2];c=y[d+8>>2];if(e>>>0>>0){break d}b=y[b+16>>2];if(b>>>0>e-c>>>0){break d}e=y[d>>2];y[a+4>>2]=b;y[a>>2]=c+e}Y=d+16|0;return}o()}function nd(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0;d=a+8|0;t(d,0,36);b=E(b,12);c=y[b+3072>>2];y[a+4>>2]=c;w[a+1|0]=z[b+3064|0];ad(d,c);Gb(a+20|0,c);Gb(a+32|0,c);b=y[b+3068>>2];d=b+(c<<3)|0;g=y[a+32>>2];h=y[a+20>>2];c=y[a+8>>2];while(1){if((b|0)!=(d|0)){y[c+(e<<3)>>2]=z[b|0];f=e<<2;y[f+h>>2]=z[b+1|0];y[g+f>>2]=y[b+4>>2];b=b+8|0;e=e+1|0;continue}break}i=a,j=ob(c,y[a+12>>2]-c>>3),w[i|0]=j;return a}function bd(a,b,c,d,e,f,g,h,i,j){var k=0,l=0;La(y[b+8>>2],d,i,j,e);i=y[a+12>>2];j=y[c>>2];b=ia(y[b+8>>2],d+2|0,g,h);y[c+4>>2]=e;y[c>>2]=b|j<<1&2;e=y[c+8>>2];a=y[a+20>>2];b=(d-i|0)+2|0;k=c,l=ia(y[a+8>>2],b,y[f>>2],y[f+4>>2])|e<<1&2,y[k+8>>2]=l;d=y[c+12>>2];k=c,l=ia(y[a+8>>2],b,y[f+8>>2],y[f+12>>2])|d<<1&6,y[k+12>>2]=l;d=y[c+16>>2];k=c,l=ia(y[a+8>>2],b,y[f+16>>2],y[f+20>>2])|d<<1&6,y[k+16>>2]=l}function Fc(a,b,c,d,e,f){var g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0;l=((b&536870911)<<3|a>>>29)&-8;h=1;while(1){a:{if(B[c>>2]>=l>>>0){break a}i=h&1;n=i?325:326;g=i?1056:1392;j=0;while(1){k=bc(g,n,a,b,c);if((k|0)<0){while(1){if(B[c>>2]>=l>>>0){break a}if(!Ka(a,b,c)){continue}break}break a}j=j+k|0;if(k>>>0>63){continue}break}g=j+m|0;if(!i){lb(d,e,f,m,g)}h=h^1;m=g;if((f|0)>(g|0)){continue}}break}}function Pd(a,b,c,d,e){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;a:{if(Sa(a,y[b+8>>2],e)){if(y[b+28>>2]==1|y[b+4>>2]!=(c|0)){break a}y[b+28>>2]=d;return}if(!Sa(a,y[b>>2],e)){break a}if(!(y[b+16>>2]!=(c|0)&y[b+20>>2]!=(c|0))){if((d|0)!=1){break a}y[b+32>>2]=1;return}y[b+20>>2]=c;y[b+32>>2]=d;y[b+40>>2]=y[b+40>>2]+1;if(!(y[b+36>>2]!=1|y[b+24>>2]!=2)){w[b+54|0]=1}y[b+44>>2]=4}}function Vd(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0;d=Y+-64|0;Y=d;a:{e=1;b:{if(y[a+4>>2]==y[b+4>>2]){break b}b=Bb(b,5384);e=0;if(!b){break b}e=y[c>>2];if(!e){break a}t(d+8|0,0,56);w[d+59|0]=1;y[d+16>>2]=-1;y[d+12>>2]=a;y[d+4>>2]=b;y[d+52>>2]=1;_[y[y[b>>2]+28>>2]](b,d+4|0,e,1);a=y[d+28>>2];if((a|0)==1){y[c>>2]=y[d+20>>2]}e=(a|0)==1}Y=d- -64|0;return e|0}Qc();o()}function Pc(a,b,c,d){w[a+53|0]=1;a:{if(y[a+4>>2]!=(c|0)){break a}w[a+52|0]=1;c=y[a+16>>2];b:{if(!c){y[a+36>>2]=1;y[a+24>>2]=d;y[a+16>>2]=b;if((d|0)!=1){break a}if(y[a+48>>2]==1){break b}break a}if((b|0)==(c|0)){c=y[a+24>>2];if((c|0)==2){y[a+24>>2]=d;c=d}if(y[a+48>>2]!=1){break a}if((c|0)==1){break b}break a}y[a+36>>2]=y[a+36>>2]+1}w[a+54|0]=1}}function Cd(a,b){var c=0,d=0,e=0;d=a,e=Fa(),y[d>>2]=e;d=a,e=Fa(),y[d+4>>2]=e;d=a,e=Fa(),y[d+8>>2]=e;d=a,e=Fa(),y[d+12>>2]=e;d=a,e=Fa(),y[d+16>>2]=e;d=a,e=Fa(),y[d+20>>2]=e;d=a,e=Fa(),y[d+24>>2]=e;d=a,e=Fa(),y[d+28>>2]=e;d=a,e=Fa(),y[d+32>>2]=e;c=na(16);w[c+12|0]=b;y[c+8>>2]=0;y[c>>2]=0;y[c+4>>2]=0;kb(c,1<>2]=c;return a}function kc(a){var b=0,c=0,d=0,e=0;b=Y-16|0;Y=b;c=na(20);w[c+4|0]=0;y[c>>2]=0;y[c+8>>2]=y[a+8>>2];d=y[a+12>>2];y[c+12>>2]=d;e=y[a+16>>2];y[c+16>>2]=e;Da(b+8|0,a);a:{b:{if(!y[b+12>>2]){break b}nb(c,td(e,d));Da(b,c);a=y[b+8>>2];e=0;d=y[b+12>>2];if(d>>>0>B[b+4>>2]){break a}if(!d|!(d|e)){break b}p(y[b>>2],a,d)}Y=b+16|0;return c}o()}function sd(a,b,c,d,e){var f=0,g=0,h=0,i=0;a:{if(!d){break a}if(e&1){f=b;g=d;if(c){e=32-c|0;f=d>>>0>>0?d:e;y[b>>2]=y[b>>2]|-1<>>e-f;g=d-f|0;f=b+4|0}i=g>>>5|0;e=i;h=f;while(1){if(e){y[h>>2]=-1;e=e-1|0;h=h+4|0;continue}break}e=g&31;if(!e){break a}f=(i<<2)+f|0;y[f>>2]=y[f>>2]|-1>>>32-e;break a}Xc(b,c,d)}Pb(a,b,c,d)}function ed(a,b){var c=0,d=0,e=0;c=Y-32|0;Y=c;a:{d=y[a+40>>2];if(d){e=y[a+48>>2];y[c+24>>2]=y[a+44>>2];y[c+28>>2]=e;b=y[b+16>>2];if(b>>>0>d>>>0){break a}a=y[a+36>>2];y[c+20>>2]=b;y[c+16>>2]=a;a=y[c+28>>2];y[c+8>>2]=y[c+24>>2];y[c+12>>2]=a;a=y[c+20>>2];y[c>>2]=y[c+16>>2];y[c+4>>2]=a;Pa(c+8|0,c)}Y=c+32|0;return}o()}function ra(a,b){var c=0,d=0,e=0,f=0,g=0;a:{d=y[a+8>>2];f=d+3|0;c=y[a+4>>2];if(f>>>0>>0){if(c>>>0<=d>>>0){break a}g=d+1|0;if(g>>>0>=c>>>0){break a}e=c;c=d+2|0;if(e>>>0<=c>>>0){break a}e=b;b=y[a>>2];y[e>>2]=z[b+g|0]<<16|z[b+d|0]<<24|z[b+c|0]<<8|z[b+f|0];y[a+8>>2]=y[a+8>>2]+4;a=0}else{a=-1}return a}o()}function dd(a,b,c){var d=0;d=Y-32|0;Y=d;ja(d+24|0,y[b+20>>2],(y[b+16>>2]^-1)+c|0);y[a>>2]=y[d+24>>2];y[a+4>>2]=y[d+28>>2];ja(d+16|0,y[b+20>>2],c-y[b+16>>2]|0);y[a+8>>2]=y[d+16>>2];y[a+12>>2]=y[d+20>>2];ja(d+8|0,y[b+20>>2],(c-y[b+16>>2]|0)+1|0);y[a+16>>2]=y[d+8>>2];y[a+20>>2]=y[d+12>>2];Y=d+32|0}function xa(a,b){var c=0,d=0;c=na(20);y[c+16>>2]=0;y[c+8>>2]=0;y[c+12>>2]=0;w[c+4|0]=0;y[c>>2]=0;a:{if(a-2147483617>>>0<2147483680|(b|0)<=0){break a}d=a+31&2147483616;if(2147483616/(d>>>0)>>>0>>0){break a}y[c+12>>2]=b;y[c+8>>2]=a;a=d>>>3|0;y[c+16>>2]=a;nb(c,td(a,b))}return c}function Sa(a,b,c){var d=0;if(!c){return y[a+4>>2]==y[b+4>>2]}if((a|0)==(b|0)){return 1}c=y[a+4>>2];a=z[c|0];b=y[b+4>>2];d=z[b|0];a:{if(!a|(d|0)!=(a|0)){break a}while(1){d=z[b+1|0];a=z[c+1|0];if(!a){break a}b=b+1|0;c=c+1|0;if((a|0)==(d|0)){continue}break}}return(a|0)==(d|0)}function Pa(a,b){var c=0,d=0,e=0;a:{c=y[a+4>>2];b:{if(!c){break b}if(!y[b+4>>2]){a=y[a>>2];while(1){if((c|0)<=0){break b}w[a|0]=0;c=c-1|0;a=a+1|0;continue}}e=y[b>>2];d=y[b+4>>2];c=y[a>>2];b=0;if(1&B[a+4>>2]>>0){break a}if(!(b|d)|!d){break b}p(c,e,d)}return}o()}function Ad(a){var b=0,c=0,d=0,e=0,f=0,g=0;d=Y-16|0;Y=d;e=lc();f=e+24|0;g=y[a+28>>2];c=y[a+24>>2];while(1){if((c|0)!=(g|0)){b=y[c>>2];if(b){b=kc(b)}else{b=0}y[d+12>>2]=b;b=d+12|0;Wc(f,b);c=c+4|0;la(b);continue}break}c=Sb(e,a);Sb(c+12|0,a+12|0);Y=d+16|0;return c}function Jb(a,b,c){var d=0;d=Y-32|0;Y=d;ja(d+24|0,y[b+20>>2],c-1|0);y[a>>2]=y[d+24>>2];y[a+4>>2]=y[d+28>>2];ja(d+16|0,y[b+20>>2],c);y[a+8>>2]=y[d+16>>2];y[a+12>>2]=y[d+20>>2];ja(d+8|0,y[b+20>>2],c+1|0);y[a+16>>2]=y[d+8>>2];y[a+20>>2]=y[d+12>>2];Y=d+32|0}function Oc(a,b,c){var d=0;d=y[a+36>>2];if(!d){y[a+24>>2]=c;y[a+16>>2]=b;y[a+36>>2]=1;y[a+20>>2]=y[a+56>>2];return}a:{if(!(y[a+20>>2]!=y[a+56>>2]|y[a+16>>2]!=(b|0))){if(y[a+24>>2]!=2){break a}y[a+24>>2]=c;return}w[a+54|0]=1;y[a+24>>2]=2;y[a+36>>2]=d+1}}function Ra(a){var b=0,c=0,d=0,e=0;b=na(20);y[b+16>>2]=a;w[b|0]=0;c=255;d=y[a>>2];e=y[a+4>>2];a=y[a+8>>2];if(e>>>0>a>>>0){c=z[a+d|0]}w[b+1|0]=c;y[b+4>>2]=((c^-1)&255)<<16;Dc(b);y[b+8>>2]=32768;y[b+4>>2]=y[b+4>>2]<<7;y[b+12>>2]=y[b+12>>2]-7;return b}function Xc(a,b,c){var d=0,e=0;if(b){d=32-b|0;e=c>>>0>>0?c:d;y[a>>2]=y[a>>2]&(-1<>>d-e^-1);c=c-e|0;a=a+4|0}e=c>>>5|0;b=e;d=a;while(1){if(b){y[d>>2]=0;b=b-1|0;d=d+4|0;continue}break}b=c&31;if(b){a=(e<<2)+a|0;y[a>>2]=y[a>>2]&(-1>>>32-b^-1)}}function tb(a,b){var c=0;c=1;a:{if(ra(y[a+4>>2],b)){break a}if(ra(y[a+4>>2],b+4|0)){break a}if(ra(y[a+4>>2],b+8|0)){break a}if(ra(y[a+4>>2],b+12|0)){break a}if(va(y[a+4>>2],b+16|0)|(y[b>>2]>4096|y[b+4>>2]>4096)&z[a+112|0]==1){break a}c=0}return c}function zd(a,b,c){var d=0,e=0;d=na(32);e=y[b+12>>2];y[d+16>>2]=y[b+8>>2];y[d+20>>2]=e;e=y[b+4>>2];y[d+8>>2]=y[b>>2];y[d+12>>2]=e;b=y[c>>2];y[c>>2]=0;y[d>>2]=a;y[d+24>>2]=b;b=y[a+4>>2];y[d+4>>2]=b;y[b>>2]=d;y[a+4>>2]=d;y[a+8>>2]=y[a+8>>2]+1}function Ga(a,b,c){var d=0,e=0,f=0,g=0;d=y[b+8>>2];g=(d|0)>0?d:0;d=0;while(1){a:{if((d|0)==(g|0)){e=0;break a}e=Ma(a,y[y[b+12>>2]+(d<<2)>>2]);if(!(!e|(z[e+4|0]&63)!=53)){if((c|0)==(f|0)){break a}f=f+1|0}d=d+1|0;continue}break}return e}function Ub(a,b){var c=0;y[a+8>>2]=0;y[a>>2]=0;y[a+4>>2]=0;a:{if(b){if(b>>>0>=1073741824){break a}c=b<<2;b=na(c);y[a>>2]=b;c=b+c|0;y[a+8>>2]=c;while(1){if((b|0)!=(c|0)){y[b>>2]=0;b=b+4|0;continue}break}y[a+4>>2]=c}return a}Ba();o()}function gb(a,b){var c=0;y[a+8>>2]=0;y[a>>2]=0;y[a+4>>2]=0;a:{if(b){if(b>>>0>=1073741824){break a}c=Lb(b);y[a>>2]=c;b=(b<<2)+c|0;y[a+8>>2]=b;while(1){if((b|0)!=(c|0)){y[c>>2]=0;c=c+4|0;continue}break}y[a+4>>2]=b}return a}Ba();o()}function od(a,b){var c=0,d=0;c=Y-16|0;Y=c;y[a+8>>2]=0;y[a>>2]=0;y[a+4>>2]=0;y[c+8>>2]=a;if(b){jc(a,b);d=b;b=y[a+4>>2];d=d+b|0;while(1){if((b|0)!=(d|0)){w[b|0]=0;b=b+1|0;continue}break}y[a+4>>2]=d}w[c+12|0]=1;fd(c+8|0);Y=c+16|0}function Kc(a,b){var c=0,d=0,e=0;c=Y-16|0;Y=c;d=z[a+4|0];if((d|0)==255){a=Fb(4);y[a>>2]=5288;P(a|0,5300,2);o()}y[c+4>>2]=c+3;e=y[b+4>>2];y[c+8>>2]=y[b>>2];y[c+12>>2]=e;a=_[y[(c+8|0)+(d<<2)>>2]](c+4|0,a)|0;Y=c+16|0;return a}function qb(){var a=0;a=na(80);x[a+56>>1]=0;y[a+12>>2]=0;y[a+24>>2]=0;y[a+28>>2]=0;y[a+32>>2]=0;y[a+36>>2]=0;y[a+40>>2]=0;y[a+44>>2]=0;y[a+48>>2]=0;y[a+60>>2]=0;y[a+64>>2]=0;y[a+68>>2]=0;y[a+72>>2]=0;y[a+76>>2]=0;return a}function Yb(){var a=0;a=na(96);y[a+44>>2]=0;y[a+48>>2]=0;y[a+36>>2]=0;y[a+40>>2]=0;y[a+28>>2]=0;y[a+32>>2]=0;y[a+60>>2]=0;y[a+64>>2]=0;y[a+68>>2]=0;y[a+72>>2]=0;y[a+76>>2]=0;y[a+80>>2]=0;y[a+84>>2]=0;y[a+88>>2]=0;return a}function La(a,b,c,d,e){a:{if(!(!d|(b|0)<0|(a|0)<=(b|0))){a=b>>>3|0;b=1<<((b^-1)&7);if(e){if(a>>>0>=d>>>0){break a}a=a+c|0;w[a|0]=b|z[a|0];return}if(a>>>0>=d>>>0){break a}a=a+c|0;w[a|0]=z[a|0]&(b^-1)}return}o()}function id(a){var b=0,c=0,d=0;y[a+56>>2]=2;c=1;b=4;d=y[a+52>>2];a:{b:{if((d|0)==5){break b}if((d|0)>=3){y[a+52>>2]=5;break b}b=yc(a);c=!b;if(y[a+56>>2]==3){break a}y[a+52>>2]=5;b=b?-1:4}y[a+56>>2]=b}return c}function se(a,b,c){var d=0,e=0,f=0,g=0,h=0;e=c>>>16|0;d=a>>>16|0;h=E(e,d);f=c&65535;a=a&65535;g=E(f,a);d=(g>>>16|0)+E(d,f)|0;a=(d&65535)+E(a,e)|0;Z=h+E(b,c)+(d>>>16)+(a>>>16)|0;return g&65535|a<<16}function Ma(a,b){var c=0,d=0;c=y[a>>2];a:{if(c){c=Ma(c,b);if(c){break a}}d=y[a+12>>2];a=y[a+8>>2];while(1){if((a|0)==(d|0)){return 0}c=y[a>>2];a=a+4|0;if(y[c>>2]!=(b|0)){continue}break}}return c}function xd(a){var b=0;a:{if(z[a+16|0]!=3|z[a+17|0]!=255|(z[a+18|0]!=253|z[a+19|0]!=255)){break a}if(z[a+20|0]!=2|z[a+21|0]!=254|(z[a+22|0]!=254|z[a+23|0]!=254)){break a}b=z[a+2|0]^1}return b&1}function Wa(a,b){var c=0,d=0,e=0;a:{c=y[a+8>>2];e=c+1|0;d=y[a+4>>2];if(e>>>0>>0){if(c>>>0>=d>>>0){break a}d=b;b=y[a>>2];x[d>>1]=z[b+c|0]<<8|z[b+e|0];y[a+8>>2]=c+2;a=0}else{a=-1}return a}o()}function Kb(a,b){var c=0;c=y[a+40>>2];y[a+28>>2]=y[a+36>>2];y[a+32>>2]=c;c=y[a+48>>2];b=y[b+16>>2];if(c>>>0>>0){o()}y[a+40>>2]=c;y[a+48>>2]=c-b;c=y[a+44>>2];y[a+36>>2]=c;y[a+44>>2]=b+c}function cd(a,b,c,d,e,f,g,h){return y[c+16>>2]|y[c+12>>2]<<3|y[c+8>>2]<<6|ia(y[y[a+20>>2]+8>>2],w[a+26|0]+(d-y[a+12>>2]|0)|0,e,f)<<8|y[c+4>>2]<<9|y[c>>2]<<10|ia(b,w[a+24|0]+d|0,g,h)<<12}function sa(a,b){var c=0,d=0,e=0;d=Y-16|0;Y=d;e=b<<2;c=y[e+y[a+36>>2]>>2];if(!c){b=nd(na(44),b);c=y[a+36>>2];y[d+12>>2]=0;vb(c+e|0,b);pa(d+12|0);c=y[y[a+36>>2]+e>>2]}Y=d+16|0;return c}function bb(a,b){var c=0;c=y[a>>2];if(c){y[a+4>>2]=c;ma(c);y[a+8>>2]=0;y[a>>2]=0;y[a+4>>2]=0}y[a>>2]=y[b>>2];y[a+4>>2]=y[b+4>>2];y[a+8>>2]=y[b+8>>2];y[b+8>>2]=0;y[b>>2]=0;y[b+4>>2]=0}function Hc(a,b,c){var d=0,e=0;e=((b&536870911)<<3|a>>>29)&-8;d=y[c>>2];a:{while(1){if(B[c>>2]>=e>>>0){break a}if(!Ka(a,b,c)){continue}break}if(y[c>>2]-d>>>0>11){break a}y[c>>2]=d}}function Ed(a,b){var c=0;y[a+8>>2]=0;y[a>>2]=0;y[a+4>>2]=0;a:{if(b){if(b>>>0>=536870912){break a}c=yb(b);y[a+4>>2]=c;y[a>>2]=c;y[a+8>>2]=(b<<3)+c;ld(a,b)}return a}Ba();o()}function Jc(a,b){var c=0,d=0,e=0;c=Y-16|0;Y=c;d=z[a+4|0];if((d|0)!=255){e=y[b+4>>2];y[c+8>>2]=y[b>>2];y[c+12>>2]=e;_[y[(c+8|0)+(d<<2)>>2]](c+7|0,a)}w[a+4|0]=255;Y=c+16|0}function Tc(a,b){var c=0;c=Y-16|0;Y=c;y[c+8>>2]=0;a:{if(z[a+4|0]==1){oa(a,b);break a}mb(a);w[a+4|0]=1;y[a>>2]=b}y[c+12>>2]=0;la(c+12|0);la(c+8|0);Y=c+16|0;return a}function Db(a,b){var c=0;if(z[a|0]==1){c=y[a+4>>2];b=se(c,c>>31,b);c=Z;c=!(b- -2147483648>>>0<2147483648?c+1|0:c);b=c?b:0}else{b=0}y[a>>2]=c;y[a+4>>2]=b;return a}function Ac(a,b,c){var d=0,e=0,f=0;d=1;while(1){e=z[a+12|0];if(e>>>0<=(f&255)>>>0){y[c>>2]=(-1<>2]+(d<<3)|0)|d<<1;f=f+1|0;continue}break}}function Hb(a,b,c,d,e,f,g){var h=0,i=0;h=Y-16|0;Y=h;if(qa(a)){i=y[a+12>>2];y[h+8>>2]=y[a+8>>2];y[h+12>>2]=i;y[h>>2]=0;y[h+4>>2]=0;Zc(a,b,c,d,e,f,g,h)}Y=h+16|0}function Cb(a,b,c,d){var e=0;e=Y-16|0;Y=e;y[e+12>>2]=d;d=1;w[e+8|0]=1;a:{if(!z[Na(e+8|0,b>>c)|0]){d=0;w[a|0]=0;break a}y[a>>2]=y[e+12>>2]}w[a+4|0]=d;Y=e+16|0}function qc(a){var b=0;b=y[a+36>>2];y[a+36>>2]=0;if(b){ma(Ja(b))}Ea(a+32|0);Ea(a+28|0);Ea(a+24|0);Ea(a+20|0);Ea(a+16|0);Ea(a+12|0);Ea(a+8|0);Ea(a+4|0);Ea(a)}function Qa(a,b){var c=0,d=0;c=Y-16|0;Y=c;Da(c+8|0,a);d=0-b|0;a=y[c+12>>2];b=y[c+8>>2];while(1){if((a|0)>0){w[b|0]=d;a=a-1|0;b=b+1|0;continue}break}Y=c+16|0}function md(a,b){var c=0;y[a+8>>2]=0;y[a>>2]=0;y[a+4>>2]=0;if(b>>>0>=1073741824){Ba();o()}c=Lb(b);y[a+4>>2]=c;y[a>>2]=c;y[a+8>>2]=(b<<2)+c;kd(a,b);return a}function pb(a,b){var c=0;c=y[a+4>>2];if(b){c=c+1|0;y[a+4>>2]=c}b=y[a+12>>2]-y[a+8>>2]>>3;if(b>>>0<=c>>>0){b=b+16|0;ad(a+8|0,b);Gb(a+20|0,b);Gb(a+32|0,b)}}function jc(a,b){var c=0;a:{if((b|0)>=0){if(b>>>0>2147479550){break a}c=Ya(b);if(!c){break a}y[a+4>>2]=c;y[a>>2]=c;y[a+8>>2]=b+c;return}Ba();o()}rb();o()}function _c(a,b){var c=0;a:{if(!z[a+4|0]){c=y[b>>2];if((c|0)==y[a>>2]){break a}y[b>>2]=0;y[a>>2]=c;return}ub(a);c=y[b>>2];y[b>>2]=0;w[a+4|0]=0;y[a>>2]=c}}function Ud(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;if(Sa(a,y[b+8>>2],f)){Pc(b,c,d,e);return}a=y[a+8>>2];_[y[y[a>>2]+20>>2]](a,b,c,d,e,f)}function Eb(a,b,c){a:{switch(a|0){case 1:return b&c;case 2:return b^c;case 3:return b^c^-1;default:o();case 0:b=b|c;break;case 4:break a}}return b}function wc(a){var b=0,c=0,d=0;c=E(z[a+1|0],y[a+4>>2]+1|0);if(c>>>0<=65535){d=z[a+2|0];b=qb();a=z[a|0];y[b+8>>2]=d;y[b+4>>2]=c;w[b|0]=a}return b}function Tb(a){var b=0,c=0,d=0;a:{if(z[a|0]!=1){break a}b=y[a+4>>2];if((b|0)<0){break a}c=b>>>0<1073741824;d=b<<1}y[a>>2]=c;y[a+4>>2]=d;return a}function Vc(a,b,c){var d=0;if(!(b&1)|z[a|0]!=1){b=0;c=0}else{b=y[a+4>>2];d=b;b=b+c|0;c=((d^b)&(b^c))>=0;b=c?b:0}y[a>>2]=c;y[a+4>>2]=b;return a}function ld(a,b){var c=0;c=y[a+4>>2];b=c+(b<<3)|0;while(1){if((b|0)==(c|0)){y[a+4>>2]=b}else{y[c>>2]=0;y[c+4>>2]=0;c=c+8|0;continue}break}}function fe(a,b,c){a=a|0;b=b|0;c=c|0;var d=0;d=y[a+4>>2];a=y[a+8>>2];b=(a>>1)+b|0;c=y[c>>2];if(a&1){d=y[d+y[b>>2]>>2]}return _[d|0](b,c)|0}function Da(a,b){var c=0,d=0;d=Nb(b);a:{c=y[b+16>>2];if((c|0)>=0){b=y[b+12>>2];b=se(b,b>>31,c);if(!Z){break a}}o()}y[a+4>>2]=b;y[a>>2]=d}function Sd(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;if(y[a+4>>2]==y[y[b+8>>2]+4>>2]){Oc(b,c,d);return}a=y[a+8>>2];_[y[y[a>>2]+28>>2]](a,b,c,d)}function vd(a,b,c,d){if(b>>>0<=d>>>0){b=(b<<2)+a|0;while(1){if((a|0)!=(b|0)){y[c>>2]=y[a>>2];c=c+4|0;a=a+4|0;continue}break}return}o()}function ia(a,b,c,d){a:{if(!d|(b|0)<0|(a|0)<=(b|0)){a=0}else{a=b>>>3|0;if(a>>>0>=d>>>0){break a}a=z[a+c|0]>>>((b^-1)&7)&1}return a}o()}function wb(a,b){var c=0,d=0;d=y[a>>2];c=y[a+8>>2];if(c>>>0>2]){y[b>>2]=z[d+c|0]>>>7-y[a+12>>2]&1;_b(a);a=0}else{a=-1}return a}function Yc(a,b){var c=0,d=0;if(z[a|0]==1){c=y[a+4>>2];d=c-b|0;c=((c^d)&(b^c))>=0;b=c?d:0}else{b=0;c=0}y[a>>2]=c;y[a+4>>2]=b;return a}function Na(a,b){var c=0,d=0;if(z[a|0]==1){c=y[a+4>>2];d=c+b|0;c=((d^c)&(b^d))>=0;b=c?d:0}else{b=0;c=0}y[a>>2]=c;y[a+4>>2]=b;return a}function nb(a,b){var c=0;c=Y-16|0;Y=c;a:{if(z[a+4|0]==1){rc(a,b);break a}ub(a);w[a+4|0]=1;y[a>>2]=b}y[c+12>>2]=0;Ha(c+12|0);Y=c+16|0}function $a(a){var b=0,c=0;b=y[a>>2];if(b){c=y[a+4>>2];while(1){if((c|0)!=(b|0)){c=la(c-4|0);continue}break}y[a+4>>2]=b;ma(y[a>>2])}}function va(a,b){var c=0,d=0;d=y[a>>2];c=y[a+8>>2];if(c>>>0>2]){w[b|0]=z[d+c|0];y[a+8>>2]=y[a+8>>2]+1;a=0}else{a=-1}return a}function re(){var a=0,b=0,c=0;while(1){b=a<<4;c=b+6080|0;y[b+6084>>2]=c;y[b+6088>>2]=c;a=a+1|0;if((a|0)!=64){continue}break}Fd(48)}function Sc(a){a=a|0;var b=0,c=0,d=0;y[a>>2]=5828;b=y[a+4>>2];c=b-4|0;d=y[c>>2]-1|0;y[c>>2]=d;if((d|0)<0){ma(b-12|0)}return a|0}function kd(a,b){var c=0;c=y[a+4>>2];b=c+(b<<2)|0;while(1){if((b|0)==(c|0)){y[a+4>>2]=b}else{y[c>>2]=0;c=c+4|0;continue}break}}function Ca(a,b,c){var d=0;d=Y-16|0;Y=d;ja(d+8|0,b,c);b=y[d+8>>2];if(b&3){o()}c=y[d+12>>2];y[a>>2]=b;y[a+4>>2]=c>>>2;Y=d+16|0}function Ec(a,b,c){var d=0;d=y[a+4>>2];while(1){if((b|0)!=(c|0)){w[d|0]=z[b|0];d=d+1|0;b=b+1|0;continue}break}y[a+4>>2]=d}function Pb(a,b,c,d){a:{if((d|0)>=0){d=c+d|0;c=d>>>5|0;break a}d=c+d|0;c=(d-31|0)/32|0}y[a+4>>2]=d&31;y[a>>2]=(c<<2)+b}function Ka(a,b,c){var d=0;d=c;c=y[c>>2];y[d>>2]=c+1;d=b;b=c>>>3|0;if(d>>>0<=b>>>0){o()}return z[a+b|0]>>>((c^-1)&7)&1}function ta(a){var b=0,c=0;if(y[a+12>>2]){b=y[a+8>>2]+1|0;if(b){c=y[a+4>>2];y[a+8>>2]=b>>>0>>0?b:c}y[a+12>>2]=0}}function yd(a,b){var c=0;c=se(b,0,a);if(Z|c>>>0>2147479550){a=0}else{b=E(a,b);a=Ya(b);if(b?a:0){t(a,0,b)}}return a}function ib(a,b){var c=0;c=y[a>>2];y[a>>2]=b;if(c){pa(c+68|0);la(c- -64|0);Zb(c+60|0);Za(c+56|0);Ja(c+12|0);ma(c)}}function Bc(a,b,c){if(c>>>0>=536870912){Ba();o()}a=b-a|0;b=a>>2;return a>>>0>=2147483640?536870911:b>>>0>c>>>0?b:c}function pd(a,b){jd(a);y[a>>2]=y[b>>2];y[a+4>>2]=y[b+4>>2];y[a+8>>2]=y[b+8>>2];y[b+8>>2]=0;y[b>>2]=0;y[b+4>>2]=0}function gd(a){var b=0;a:{b=a;a=y[a+16>>2];if((b|0)==(a|0)){b=16}else{if(!a){break a}b=20}_[y[b+y[a>>2]>>2]](a)}}function Vb(a,b){var c=0;c=y[a+52>>2];if((c|0)==3){if(A[a+56>>1]!=1){y[a+52>>2]=-1;return-1}c=hd(a,b)}return c}function ie(a){a=a|0;var b=0,c=0;b=na(12);y[b>>2]=2600;c=y[a+8>>2];y[b+4>>2]=y[a+4>>2];y[b+8>>2]=c;return b|0}function _b(a){var b=0;b=y[a+12>>2];a:{if((b|0)==7){y[a+8>>2]=y[a+8>>2]+1;b=0;break a}b=b+1|0}y[a+12>>2]=b}function _a(a){var b=0,c=0;b=y[a+4>>2];if(!b){o()}c=y[a>>2];y[a>>2]=c+4;y[a+4>>2]=b-1;return y[c>>2]}function Uc(a,b){a:{b:{if(!z[a+4|0]){if(y[a>>2]!=(b|0)){break b}break a}mb(a);w[a+4|0]=0}y[a>>2]=b}}function Od(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;if(Sa(a,y[b+8>>2],f)){Pc(b,c,d,e)}}function gc(a){var b=0,c=0;b=y[a+4>>2];if(!b){o()}c=y[a>>2];y[a>>2]=c+4;y[a+4>>2]=b-1;return c}function he(a,b){a=a|0;b=b|0;var c=0;y[b>>2]=2600;c=y[a+8>>2];y[b+4>>2]=y[a+4>>2];y[b+8>>2]=c}function rd(a,b){var c=0;c=Y-16|0;Y=c;y[c+12>>2]=b;b=a+24|0;a=c+12|0;Wc(b,a);la(a);Y=c+16|0}function jd(a){var b=0;b=y[a>>2];if(b){y[a+4>>2]=b;ma(b);y[a+8>>2]=0;y[a>>2]=0;y[a+4>>2]=0}}function Cc(a){var b=0,c=0;b=y[a+8>>2]+1|0;if(b){c=a;a=y[a+4>>2];y[c+8>>2]=a>>>0>b>>>0?b:a}}function tc(a,b,c,d,e,f,g,h){a:{if(!qa(a)){break a}if(!qa(f)){break a}Zc(f,a,b,c,d,e,h,g)}}function vb(a,b){var c=0;c=y[a>>2];y[a>>2]=b;if(c){ya(c+32|0);ya(c+20|0);ya(c+8|0);ma(c)}}function te(a,b){var c=0,d=0;c=b&31;d=(-1<>>c|0;c=a;a=0-b&31;return d|(c&-1>>>a)<>2]==y[y[b+8>>2]+4>>2]){Oc(b,c,d)}}function Jd(a){a=a|0;var b=0;b=y[a+60>>2];a=y[a+48>>2]+7>>>3|0;return(a>>>0>b>>>0?b:a)|0}function Ic(a,b){b=b-a|0;while(1){if((b|0)>0){w[a|0]=255;b=b-1|0;a=a+1|0;continue}break}}function Rb(a,b){var c=0;c=y[a>>2];y[a>>2]=b;if(c){$a(c+24|0);Ja(c+12|0);ma(Ja(c))}}function Fa(){var a=0;a=na(12);y[a+8>>2]=0;y[a>>2]=0;y[a+4>>2]=0;kb(a,512);return a}function Nc(a){a=a|0;y[a+36>>2]=0;y[a+40>>2]=0;ya(a+76|0);ya(a- -64|0);return a|0}function td(a,b){if(4294967295/(b>>>0)>>>0<=a>>>0){rb();o()}return ab(E(a,b),1)}function Lc(a,b){var c=0;return(z[a+16|0]!=(b|0)|z[a+17|0]!=255?c:z[a+2|0]^1)&1}function pc(a){var b=0;b=y[a>>2];y[a>>2]=0;if(b){ya(b+40|0);ya(b+28|0);ma(b)}}function Id(a){a=a|0;Ic(y[a+76>>2],y[a+80>>2]);y[a+48>>2]=0;return 1}function Xb(a,b){var c=0;c=y[a>>2];y[a>>2]=b;if(c){$a(c+4|0);ma(c)}}function Ja(a){var b=0;b=y[a>>2];if(b){y[a+4>>2]=b;ma(b)}return a}function oa(a,b){var c=0;c=y[a>>2];y[a>>2]=b;if(c){ub(c);ma(c)}}function $b(a){var b=0;b=y[a>>2];y[a>>2]=0;if(b){ma(b)}return a}function vc(a){var b=0;b=na(16);y[b>>2]=a;gb(b+4|0,a);return b}function ee(a,b){a=a|0;b=b|0;return(y[b+4>>2]==2888?a+4|0:0)|0}function Lb(a){if(a>>>0>=1073741824){jb();o()}return na(a<<2)}function yb(a){if(a>>>0>=536870912){jb();o()}return na(a<<3)}function jb(){var a=0;a=Fb(4);y[a>>2]=5724;P(a|0,5780,2);o()}function Ea(a){var b=0;b=y[a>>2];y[a>>2]=0;if(b){ma(Ja(b))}}function na(a){a=Ya(a>>>0<=1?1:a);if(!a){Qc();o()}return a}function ya(a){var b=0;b=y[a>>2];if(b){y[a+4>>2]=b;ma(b)}}function Va(a,b){var c=0;c=y[a>>2];y[a>>2]=b;if(c){ma(c)}}function fb(a,b,c,d,e,f,g){if(qa(a)){Hb(f,a,b,c,d,e,g)}}function Ua(a){var b=0;b=y[a>>2];y[a>>2]=0;if(b){ma(b)}}function ue(a){if(a){return 31-H(a-1^a)|0}return 32} +function db(){var a=0;a=na(28);y[a+20>>2]=0;return a}function ab(a,b){a=yd(a,b);if(!a){rb();o()}return a}function rc(a,b){var c=0;c=y[a>>2];y[a>>2]=b;ma(c)}function qd(a){var b=0;b=na(4);y[b>>2]=a;return b}function lc(){var a=0;a=na(36);t(a,0,36);return a}function rb(){y[(Y-16|0)+12>>2]=6036;sc();o()}function Wd(a,b,c){a=a|0;b=b|0;c=c|0;return 0}function Oa(a,b,c,d){return(Eb(a,b,c)^c)&d^c}function Mb(a,b){a=a|0;b=b|0;return y[b>>2]}function ge(a){a=a|0;_[y[y[a>>2]+4>>2]](a)}function fd(a){if(!z[a+4|0]){ya(y[a>>2])}}function Zd(a){a=a|0;T();S(a+128|0);o()}function Ob(a){a=y[a>>2];if(a){ma(a)}}function Md(a){a=a|0;return y[a+4>>2]}function Gd(a){a=a|0;return Ya(a)|0}function Fb(a){return Ya(a|80)+80|0}function wa(a){return nd(na(44),a)}function ce(a,b){a=a|0;b=b|0;Ha(b)}function be(a,b){a=a|0;b=b|0;la(b)}function qa(a){return(Nb(a)|0)!=0}function fc(a){return(Xa(a)|0)!=0}function de(a){a=a|0;return 2868}function Yd(a){a=a|0;return 4890}function Xd(a){a=a|0;return 4864}function Xa(a){return Kc(a,4944)}function Nd(a){a=a|0;return 4913}function Nb(a){return Kc(a,2416)}function xb(a){a=a|0;return a|0}function pa(a){vb(a,0);return a}function oc(a){Za(a+24|0);ma(a)}function la(a){oa(a,0);return a}function ac(a){ib(a,0);return a}function Ab(a){zb(a,0);return a}function Mc(a){a=a|0;ma(Sc(a))}function Kd(a){a=a|0;ma(Nc(a))}function wd(a){return Lc(a,3)}function _d(a){a=a|0;sc();o()}function Qb(a){return Lc(a,2)}function $c(a,b){a=a|0;b=b|0}function Ta(a){a=a|0;ma(a)}function ub(a){Jc(a,4856)}function mb(a){Jc(a,4936)}function ua(a){Va(a,0)}function Zb(a){Xb(a,0)}function Za(a){Rb(a,0)}function Qc(){sc();o()}function Ha(a){rc(a,0)}function sc(){U();o()}function hc(a){a=a|0} +// EMSCRIPTEN_END_FUNCS +a=z;m(n);var _=[null,Sc,xb,qe,pe,oe,ne,me,le,ke,je,xb,Nc,Kd,Jd,Id,Hd,Mb,Mb,xb,Ta,ie,he,hc,ge,fe,ee,de,$c,ce,$c,be,Mb,Mb,Zd,_d,Ta,Yd,Ta,Xd,xb,Ta,hc,hc,Wd,Ta,Qd,Ta,Nd,Mc,Md,Mc,Ta,Vd,Od,Pd,Rd,Ta,Ud,Td,Sd];function $(){return v.byteLength>>16}function ea(fa){fa=fa|0;var aa=$()|0;var ba=aa+fa|0;if(aa{Module["instantiateWasm"](info,(inst,mod)=>{resolve(receiveInstance(inst,mod))})})}wasmBinaryFile??=findWasmBinary();var result=instantiateSync(wasmBinaryFile,info);return receiveInstance(result[0])}class ExitStatus{name="ExitStatus";constructor(status){this.message=`Program terminated with exit(${status})`;this.status=status}}var HEAP16;var HEAP32;var HEAP8;var HEAPF32;var HEAPF64;var HEAPU16;var HEAPU32;var HEAPU8;var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};var onPostRuns=[];var addOnPostRun=cb=>onPostRuns.push(cb);var onPreRuns=[];var addOnPreRun=cb=>onPreRuns.push(cb);var noExitRuntime=true;class ExceptionInfo{constructor(excPtr){this.excPtr=excPtr;this.ptr=excPtr-24}set_type(type){HEAPU32[this.ptr+4>>2]=type}get_type(){return HEAPU32[this.ptr+4>>2]}set_destructor(destructor){HEAPU32[this.ptr+8>>2]=destructor}get_destructor(){return HEAPU32[this.ptr+8>>2]}set_caught(caught){caught=caught?1:0;HEAP8[this.ptr+12]=caught}get_caught(){return HEAP8[this.ptr+12]!=0}set_rethrown(rethrown){rethrown=rethrown?1:0;HEAP8[this.ptr+13]=rethrown}get_rethrown(){return HEAP8[this.ptr+13]!=0}init(type,destructor){this.set_adjusted_ptr(0);this.set_type(type);this.set_destructor(destructor)}set_adjusted_ptr(adjustedPtr){HEAPU32[this.ptr+16>>2]=adjustedPtr}get_adjusted_ptr(){return HEAPU32[this.ptr+16>>2]}}var uncaughtExceptionCount=0;var ___cxa_throw=(ptr,type,destructor)=>{var info=new ExceptionInfo(ptr);info.init(type,destructor);uncaughtExceptionCount++;abort()};var __abort_js=()=>abort("");var runtimeKeepaliveCounter=0;var __emscripten_runtime_keepalive_clear=()=>{noExitRuntime=false;runtimeKeepaliveCounter=0};var timers={};var handleException=e=>{if(e instanceof ExitStatus||e=="unwind"){return EXITSTATUS}quit_(1,e)};var keepRuntimeAlive=()=>noExitRuntime||runtimeKeepaliveCounter>0;var _proc_exit=code=>{EXITSTATUS=code;if(!keepRuntimeAlive()){Module["onExit"]?.(code);ABORT=true}quit_(code,new ExitStatus(code))};var exitJS=(status,implicit)=>{EXITSTATUS=status;_proc_exit(status)};var _exit=exitJS;var maybeExit=()=>{if(!keepRuntimeAlive()){try{_exit(EXITSTATUS)}catch(e){handleException(e)}}};var callUserCallback=func=>{if(ABORT){return}try{return func()}catch(e){handleException(e)}finally{maybeExit()}};var _emscripten_get_now=()=>performance.now();var __setitimer_js=(which,timeout_ms)=>{if(timers[which]){clearTimeout(timers[which].id);delete timers[which]}if(!timeout_ms)return 0;var id=setTimeout(()=>{delete timers[which];callUserCallback(()=>__emscripten_timeout(which,_emscripten_get_now()))},timeout_ms);timers[which]={id,timeout_ms};return 0};function _createImageData(size){Module.imageData=new Uint8Array(size)}var getHeapMax=()=>2147483648;var alignMemory=(size,alignment)=>Math.ceil(size/alignment)*alignment;var growMemory=size=>{var oldHeapSize=wasmMemory.buffer.byteLength;var pages=(size-oldHeapSize+65535)/65536|0;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignMemory(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};function _setImageData(array_ptr,pitch8,pitch32,height){if(pitch32===pitch8){Module.imageData=new Uint8ClampedArray(HEAPU8.subarray(array_ptr,array_ptr+pitch32*height));return}const destSize=pitch8*height;const imageData=Module.imageData=new Uint8ClampedArray(destSize);for(let srcStart=array_ptr,destStart=0;destStart{HEAP8.set(array,buffer)};{if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(Module["print"])out=Module["print"];if(Module["printErr"])err=Module["printErr"];if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].shift()()}}}Module["writeArrayToMemory"]=writeArrayToMemory;var _malloc,_free,_jbig2_decode,_ccitt_decode,__emscripten_timeout,memory,__indirect_function_table,wasmMemory;function assignWasmExports(wasmExports){_malloc=Module["_malloc"]=wasmExports["l"];_free=Module["_free"]=wasmExports["m"];_jbig2_decode=Module["_jbig2_decode"]=wasmExports["n"];_ccitt_decode=Module["_ccitt_decode"]=wasmExports["o"];__emscripten_timeout=wasmExports["p"];memory=wasmMemory=wasmExports["j"];__indirect_function_table=wasmExports["__indirect_function_table"]}var wasmImports={a:___cxa_throw,f:__abort_js,e:__emscripten_runtime_keepalive_clear,b:__setitimer_js,h:_createImageData,c:_emscripten_resize_heap,d:_proc_exit,i:_setImageData,g:_setLineData};function run(){preRun();function doRun(){Module["calledRun"]=true;if(ABORT)return;initRuntime();readyPromiseResolve?.(Module);Module["onRuntimeInitialized"]?.();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(()=>{setTimeout(()=>Module["setStatus"](""),1);doRun()},1)}else{doRun()}}var wasmExports;wasmExports=createWasm();run();if(runtimeInitialized){moduleRtn=Module}else{moduleRtn=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject})} +;return moduleRtn}export default JBig2; diff --git a/web/public/vendor/pdfjs/wasm/openjpeg.wasm b/web/public/vendor/pdfjs/wasm/openjpeg.wasm new file mode 100644 index 00000000..5acce73f Binary files /dev/null and b/web/public/vendor/pdfjs/wasm/openjpeg.wasm differ diff --git a/web/public/vendor/pdfjs/wasm/openjpeg_nowasm_fallback.js b/web/public/vendor/pdfjs/wasm/openjpeg_nowasm_fallback.js new file mode 100644 index 00000000..0df96aed --- /dev/null +++ b/web/public/vendor/pdfjs/wasm/openjpeg_nowasm_fallback.js @@ -0,0 +1,17 @@ +/* THIS FILE IS GENERATED - DO NOT EDIT */ +async function OpenJPEG(moduleArg={}){var moduleRtn;var Module=moduleArg;var ENVIRONMENT_IS_WEB=true;var ENVIRONMENT_IS_WORKER=false;var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};var _scriptName=import.meta.url;var scriptDirectory="";var readAsync;if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){try{scriptDirectory=new URL(".",_scriptName).href}catch{}{readAsync=async url=>{var response=await fetch(url,{credentials:"same-origin"});if(response.ok){return response.arrayBuffer()}throw new Error(response.status+" : "+response.url)}}}else{}var out=console.log.bind(console);var err=console.error.bind(console);var wasmBinary;var WebAssembly={Memory:function(opts){this.buffer=new ArrayBuffer(opts["initial"]*65536)},Module:function(binary){},Instance:function(module,info){this.exports=( +// EMSCRIPTEN_START_ASM +function instantiate(Aa){var a;var b=new Uint8Array(123);for(var c=25;c>=0;--c){b[48+c]=52+c;b[65+c]=c;b[97+c]=26+c}b[43]=62;b[47]=63;function i(j,k,l){var d,e,c=0,f=k,g=l.length,h=k+(g*3>>2)-(l[g-2]=="=")-(l[g-1]=="=");for(;c>4;if(f>2;if(f>>0;A=A>>>0;if(z+A>a.length)throw"trap: invalid memory.fill";a.fill(v,z,z+A)}function B(z,C,A){a.copyWithin(z,C,C+A)}function D(){throw new Error("abort")}function za(n){var E=new ArrayBuffer(16777216);var F=new Int8Array(E);var G=new Int16Array(E);var H=new Int32Array(E);var I=new Uint8Array(E);var J=new Uint16Array(E);var K=new Uint32Array(E);var L=new Float32Array(E);var M=new Float64Array(E);var N=Math.imul;var O=Math.fround;var P=Math.abs;var Q=Math.clz32;var R=Math.min;var S=Math.max;var T=Math.floor;var U=Math.ceil;var V=Math.trunc;var W=Math.sqrt;var X=n.a;var Y=X.a;var Z=X.b;var _=X.c;var $=X.d;var aa=X.e;var ba=X.f;var ca=X.g;var da=X.h;var ea=X.i;var fa=X.j;var ga=X.k;var ha=X.l;var ia=X.m;var ja=X.n;var ka=X.o;var la=X.p;var ma=X.q;var na=94240;var oa=0;var pa=0;var qa=0; +// EMSCRIPTEN_START_FUNCS +function Zc(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,z=0,A=0,C=0,D=0,E=0,M=0,P=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,aa=0,ba=0,ca=0,da=0,ea=0,fa=0,ga=O(0),ha=0,ia=0,ja=0,ka=0,la=0,ma=0,oa=0,pa=0,qa=0,sa=0,ta=0,ua=0,va=0,wa=0;Y=na-96|0;na=Y;C=H[a+8>>2];a:{b:{c:{if(!H[a>>2]){g=N(H[C+16>>2]-H[C+8>>2]|0,H[C+20>>2]-H[C+12>>2]|0)<<2;c=Ia(g);H[C+60>>2]=c;if(!c){Ba(H[a+32>>2],1,8023,0);d=a+28|0;break b}if(!g){break c}y(c,0,g);break c}c=H[C+60>>2];if(!c){break c}Ca(c);H[C+60>>2]=0}if(!H[H[a+28>>2]>>2]){break a}oa=H[a+16>>2];c=H[oa+28>>2]+N(H[oa+24>>2],152)|0;va=H[c-152>>2];wa=H[c-144>>2];pa=H[a+20>>2];qa=H[a+12>>2];ua=H[a+4>>2];d=a+28|0;d:{q=H[b+4>>2];f=0;e:{if((q|0)<=0){break e}k=H[b>>2];c=0;f:{while(1){g=k+N(c,12)|0;if(!H[g>>2]){break f}c=c+1|0;if((q|0)!=(c|0)){continue}break}f=0;break e}f=H[g+4>>2]}if(f){break d}f=Ea(1,156);if(!f){Ba(H[a+32>>2],1,6313,0);break b}H[f+140>>2]=0;c=0;k=H[b+4>>2];g:{if((k|0)==2147483647){break g}g=H[b>>2];if((k|0)>0){while(1){q=g+N(c,12)|0;if(!H[q>>2]){k=H[q+8>>2];if(k){ra[k|0](H[q+4>>2]);g=H[b>>2]}b=g+N(c,12)|0;H[b+8>>2]=15;H[b+4>>2]=f;c=1;break g}c=c+1|0;if((k|0)!=(c|0)){continue}break}}g=Ha(g,N(k,12)+12|0);c=0;if(!g){break g}H[b>>2]=g;c=H[b+4>>2];g=g+N(c,12)|0;H[g+8>>2]=15;H[g+4>>2]=f;H[g>>2]=0;H[b+4>>2]=c+1;c=1}if(c){break d}Ba(H[a+32>>2],1,8338,0);b=H[f+116>>2];if(b){Ca(b);H[f+116>>2]=0}b=H[f+120>>2];if(b){Ca(b);H[f+120>>2]=0}Ca(H[f+148>>2]);Ca(f);break b}H[f+144>>2]=H[a+24>>2];E=H[a+40>>2];_=H[a+36>>2];S=H[a+32>>2];j=H[pa+808>>2];b=H[qa+16>>2];h:{V=H[pa+16>>2];i:{if(V&64){k=na-304|0;na=k;j:{if(j){if(_){Ba(S,1,3219,0);break j}Ba(S,1,3219,0);break j}i=H[f+116>>2];c=H[C+20>>2]-H[C+12>>2]|0;b=H[C+16>>2]-H[C+8>>2]|0;g=N(c,b);k:{l:{if(g>>>0>K[f+132>>2]){Ca(i);j=g<<2;i=Ia(j);H[f+116>>2]=i;if(!i){i=0;break j}H[f+132>>2]=g;break l}if(!i){break k}j=g<<2}if(!j){break k}y(i,0,j)}i=H[f+120>>2];m:{if(K[f+136>>2]>2639){break m}Ca(i);i=Ia(10560);H[f+120>>2]=i;if(i){break m}i=0;break j}H[f+136>>2]=2640;y(i,0,10560);H[f+128>>2]=c;H[f+124>>2]=b;n=H[C+24>>2];if(!n){i=1;break j}q=H[C+28>>2];i=1;n:{o:{p:{q:{j=H[C+52>>2];r:{if(j){g=j&3;c=H[C+4>>2];i=0;s:{if(j>>>0>=4){b=j&-4;while(1){o=c+(i<<3)|0;h=H[o+28>>2]+(H[o+20>>2]+(H[o+12>>2]+(H[o+4>>2]+h|0)|0)|0)|0;i=i+4|0;z=z+4|0;if((b|0)!=(z|0)){continue}break}if(!g){break s}}while(1){h=H[(c+(i<<3)|0)+4>>2]+h|0;i=i+1|0;l=l+1|0;if((g|0)!=(l|0)){continue}break}}if(!H[f+144>>2]&(j|0)==1){break o}if(K[f+152>>2]>=h>>>0){break r}z=Ha(H[f+148>>2],h);if(z){break q}i=0;break j}if(!H[f+144>>2]){break j}}z=H[f+148>>2];if(z){break p}i=0;break j}H[f+152>>2]=h;H[f+148>>2]=z}if(!H[C+52>>2]){h=0;break n}j=H[C+4>>2];h=0;i=0;while(1){g=i<<3;c=g+j|0;b=H[c+4>>2];if(b){B(h+z|0,H[c>>2],b)}j=H[C+4>>2];h=H[(g+j|0)+4>>2]+h|0;i=i+1|0;if(i>>>0>2]){continue}break}break n}z=H[H[C+4>>2]>>2]}i=0;j=0;c=H[C+40>>2];g=0;t:{if(!c){break t}b=H[C>>2];j=H[b+8>>2];g=0;if((c|0)==1){break t}g=H[b+32>>2]}c=n-q|0;j=g+j|0;u:{if(!j){l=0;break u}i=1;b=H[C>>2];p=H[b>>2];l=0;if((j|0)==1){i=0;break u}l=H[b+24>>2]}P=c+1|0;ha=H[f+116>>2];s=H[f+120>>2];X=H[C+12>>2];x=H[C+20>>2];ca=H[C+8>>2];D=H[C+16>>2];v:{w:{x:{y:{z:{A:{B:{C:{if(!(!i|l)){if(!_){break C}Ba(S,2,10806,0);j=1;break B}if(j>>>0<4){break B}if(_){H[k+112>>2]=j;Ba(S,1,9590,k+112|0);break v}H[k+96>>2]=j;Ba(S,1,9590,k+96|0);i=0;break j}Ba(S,2,10806,0);i=H[C+24>>2];if(i>>>0>30){break A}e=1;if(i>>>0>=P>>>0){break y}break w}i=H[C+24>>2];if(i>>>0<=30){break z}if(!_){break A}H[k+32>>2]=H[C+24>>2];Ba(S,1,12302,k+32|0);break v}H[k>>2]=i;Ba(S,1,12302,k);i=0;break j}if(i>>>0

>>0){break x}if(j>>>0<2){e=j;break y}if((i|0)!=(P|0)){e=j;break y}e=1;if(I[26384]){break y}if(!_){F[26384]=1;H[k+64>>2]=j;Ba(S,2,10299,k- -64|0);break y}if(!I[26384]){F[26384]=1;H[k+80>>2]=j;Ba(S,2,10299,k+80|0)}}if(!(!(p>>>0<2|h>>>0

>>0)&l+p>>>0<=h>>>0)){if(_){i=0;Ba(S,1,9532,0);break j}i=0;Ba(S,1,9532,0);break j}U=p+z|0;b=I[U-1|0];i=b<<4|I[U-2|0]&15;if(!(!(i>>>0<2|(b|0)==255)&(i|0)<=(p|0))){if(_){i=0;Ba(S,1,15305,0);break j}i=0;Ba(S,1,15305,0);break j}sa=H[C+28>>2];H[k+272>>2]=0;H[k+280>>2]=0;H[k+264>>2]=0;H[k+268>>2]=0;H[k+296>>2]=0;H[k+300>>2]=0;H[k+284>>2]=0;H[k+288>>2]=0;b=i-1|0;H[k+276>>2]=b;o=(p+z|0)-i|0;H[k+256>>2]=o;q=I[o|0];c=8;H[k+272>>2]=8;j=o+1|0;H[k+256>>2]=j;g=i-2|0;H[k+276>>2]=g;n=(b|0)==1?q|15:q;b=0;q=b;H[k+264>>2]=n;H[k+268>>2]=b;H[k+280>>2]=!b&(n|0)==255;u=o&3;D:{E:{if((u|0)==3){break E}v=0;if(!((n|0)!=255|(b|0)!=0|I[j|0]<=143)){break D}b=255;b=i>>>0>=3?I[j|0]:b;h=i-3|0;H[k+276>>2]=h;o=!q&(n|0)==255;c=o?15:16;H[k+272>>2]=c;R=j+(i>>>0>2)|0;H[k+256>>2]=R;b=(g|0)==1?b|15:b;g=0;H[k+280>>2]=!g&(b|0)==255;g=b;j=n;b=o?7:8;o=b&31;if((b&63)>>>0>=32){m=j<>>32-o|q<>2]=n;H[k+268>>2]=b;if((u|0)==2){break E}o=255;v=0;if(!((g|0)!=255|(r|0)!=0|I[R|0]<=143)){break D}o=i>>>0>=4?I[R|0]:o;j=i-4|0;H[k+276>>2]=j;R=R+(i>>>0>3)|0;H[k+256>>2]=R;b=(h|0)==1?o|15:o;o=0;m=o;H[k+280>>2]=!m&(b|0)==255;o=!r&(g|0)==255;c=(o?7:8)+c|0;H[k+272>>2]=c;g=b;h=n;b=o?7:8;r=b&31;if((b&63)>>>0>=32){o=h<>>32-r|q<>2]=n;H[k+268>>2]=b;if((u|0)==1){break E}v=0;if(!((g|0)!=255|(m|0)!=0|I[R|0]<=143)){break D}b=255;b=i>>>0>=5?I[R|0]:b;H[k+276>>2]=i-5;H[k+256>>2]=R+(i>>>0>4);o=0;b=(j|0)==1?b|15:b;H[k+280>>2]=1&(b|0)==255;g=!m&(g|0)==255;c=(g?7:8)+c|0;H[k+272>>2]=c;j=n;g=g?7:8;h=g&31;if((g&63)>>>0>=32){E=j<>>32-h|q<>2]=n;H[k+268>>2]=b}b=n;c=64-c|0;g=c&31;if((c&63)>>>0>=32){w=b<>>32-g|q<>2]=b;H[k+268>>2]=w;v=1}if(!v){if(_){i=0;Ba(S,1,11470,0);break j}i=0;Ba(S,1,11470,0);break j}A=D-ca|0;o=0;m=i;r=i-2|0;H[k+244>>2]=r;c=p+z|0;b=c-3|0;H[k+224>>2]=b;g=I[c-2|0];c=g>>>0>143;H[k+248>>2]=c;q=0;n=g>>>4|0;H[k+232>>2]=n;H[k+236>>2]=0;h=(n&7)==7?3:4;H[k+240>>2]=h;g=(b&3)+1|0;R=g>>>0>>0?g:r;F:{G:{if(r){while(1){i=c;u=b;b=b-1|0;H[k+224>>2]=b;g=I[u|0];c=g>>>0>143;H[k+248>>2]=c;j=h&31;if((h&63)>>>0>=32){E=g<>>32-j;j=g<>2]=n;q=q|E;H[k+236>>2]=q;h=(i&1?(g&127)==127?7:8:8)+h|0;H[k+240>>2]=h;o=o+1|0;if((R|0)!=(o|0)){continue}break}j=r-R|0;H[k+244>>2]=j;if(h>>>0>32){break F}if((j|0)>=4){o=H[u-4>>2];H[k+224>>2]=u-5;H[k+244>>2]=j-4;break G}if((j|0)<=0){o=0;break G}H:{if((R|0)==(m-3|0)){i=24;o=0;break H}E=j&1;r=j&2147483646;i=24;o=0;g=0;while(1){u=b-1|0;H[k+224>>2]=u;R=I[b|0];b=b-2|0;H[k+224>>2]=b;H[k+244>>2]=j-1;u=I[u|0];j=j-2|0;H[k+244>>2]=j;o=R<>2]=b-1;b=I[b|0];H[k+244>>2]=j-1;o=b<>2]=r-R}u=o&255;H[k+248>>2]=u>>>0>143;g=c?(o&2130706432)==2130706432?7:8:8;c=g+(o>>>0<=2415919103?8:(o&8323072)==8323072?7:8)|0;i=o>>>16&255;b=c+(i>>>0<=143?8:(o&32512)==32512?7:8)|0;j=o>>>8&255;H[k+240>>2]=b+((j>>>0<=143?8:(o&127)==127?7:8)+h|0);b=i<>>24|j<>>0>=32){o=b<>>32-c;b=b<>2]=b|n;H[k+236>>2]=o|q}Zb(k+192|0,z,p-m|0,255);b=0;I:{if(e>>>0<2){break I}Zb(k+160|0,U,l,0);b=0;if((e|0)==2){break I}n=0;q=0;o=0;H[k+152>>2]=1;H[k+144>>2]=0;H[k+136>>2]=0;H[k+140>>2]=0;c=l;i=c-1|0;H[k+148>>2]=i;b=(p+z|0)+c|0;g=b-1|0;H[k+128>>2]=g;R=g&3;J:{if((c|0)<=0){b=g;break J}b=b-2|0;H[k+128>>2]=b;n=I[g|0]}H[k+136>>2]=n;H[k+140>>2]=0;p=n>>>0>143;H[k+152>>2]=p;l=(n&127)==127?7:8;H[k+144>>2]=l;K:{if(!R){break K}u=c-2|0;H[k+148>>2]=u;L:{if((c|0)<2){g=b;break L}g=b-1|0;H[k+128>>2]=g;o=I[b|0]}p=o>>>0>143;H[k+152>>2]=p;b=l&31;if((l&63)>>>0>=32){m=o<>>32-b;b=o<>2]=q;H[k+140>>2]=m;l=(n>>>0<=143?8:(o&127)==127?7:8)+l|0;H[k+144>>2]=l;if((R|0)==1){b=g;n=q;q=m;c=i;i=u;break K}h=c-3|0;H[k+148>>2]=h;M:{if((c|0)<3){j=g;break M}j=g-1|0;H[k+128>>2]=j;aa=I[g|0]}p=aa>>>0>143;H[k+152>>2]=p;b=l&31;if((l&63)>>>0>=32){E=aa<>>32-b;b=aa<>2]=n;H[k+140>>2]=b;l=(o>>>0<=143?8:(aa&127)==127?7:8)+l|0;H[k+144>>2]=l;if((R|0)==2){b=j;c=u;i=h;break K}i=c-4|0;H[k+148>>2]=i;o=0;N:{if((c|0)<4){b=j;break N}b=j-1|0;H[k+128>>2]=b;o=I[j|0]}p=o>>>0>143;H[k+152>>2]=p;c=l&31;if((l&63)>>>0>=32){w=o<>>32-c;c=o<>2]=n;H[k+140>>2]=c;l=(aa>>>0<=143?8:(o&127)==127?7:8)+l|0;H[k+144>>2]=l;c=h}if(l>>>0<=32){O:{if((c|0)>=5){g=H[b-3>>2];H[k+148>>2]=c-5;H[k+128>>2]=b-4;break O}g=0;if((c|0)<2){break O}c=24;while(1){j=b-1|0;H[k+128>>2]=j;b=I[b|0];o=i-1|0;H[k+148>>2]=o;g=b<>>0>1;b=j;c=c-8|0;i=o;if(h){continue}break}}h=g&255;H[k+152>>2]=h>>>0>143;i=p?(g&2130706432)==2130706432?7:8:8;c=i+(g>>>0<=2415919103?8:(g&8323072)==8323072?7:8)|0;o=g>>>16&255;b=c+(o>>>0<=143?8:(g&32512)==32512?7:8)|0;j=g>>>8&255;H[k+144>>2]=b+((j>>>0<=143?8:(g&127)==127?7:8)+l|0);b=o<>>24|j<>>0>=32){m=b<>>32-c;b=b<>2]=b|n;H[k+140>>2]=m|q}b=1}aa=b;$=x-X|0;ta=P+1|0;F[s+2112|0]=0;u=s+2112|0;g=Ya(k+256|0);if((A|0)>0){P=sa-1|0;l=s;j=u;b=0;c=ha;z=0;while(1){R=z;h=J[(b<<8|(lb(k+224|0)&127)<<1)+16656>>1];P:{if(b){break P}b=g-2|0;h=(b|0)==-1?h:0;if((g|0)>1){g=b;break P}g=Ya(k+256|0)}q=H[k+236>>2];n=H[k+232>>2];i=H[k+240>>2];x=h>>>4|0;p=H[l>>2]|(x&3|h>>>2&48)<>2]=p;E=h&16;b=h>>>5&7|E>>>4;o=i;i=h&7;z=o-i|0;n=((1<>>i;q=q>>>i|0;o=n;i=0;if((A|0)>(R|2)){i=J[(b<<8|(n&127)<<1)+16656>>1];Q:{if(b){break Q}b=g-2|0;i=(b|0)==-1?i:0;if((g|0)>1){g=b;break Q}g=Ya(k+256|0)}b=i&7;z=z-b|0;n=((1<>>b;q=q>>>b|0;o=n;b=i>>>4&1|i>>>5&7}H[l>>2]=p|(i<<2&768|i&48)<>>2&2|h>>>3&1;R:{if((p|0)!=3){break R}m=g-2|0;p=(m|0)==-1?4:3;if((g|0)>1){g=m;break R}g=Ya(k+256|0)}S:{if(!p){H[k+120>>2]=1;H[k+124>>2]=1;o=0;break S}if(p>>>0<=2){m=I[(o&7)+20804|0];w=m>>>2&7;r=m&3;m=(((-1<>>r)+(m>>>5|0)|0)+1|0;o=(p|0)==1;H[k+124>>2]=o?1:m;H[k+120>>2]=o?m:1;o=r+w|0;break S}v=o;o=I[(o&7)+20804|0];U=o&3;m=v>>>U|0;if((p|0)==3){X=(o>>>5|0)+1|0;if((U|0)==3){H[k+124>>2]=m&1|2;o=o>>>2&7;H[k+120>>2]=X+((-1<>>1);o=o+4|0;break S}r=I[(m&7)+20804|0];p=r&3;m=m>>>p|0;w=o>>>2&7;H[k+120>>2]=X+(m&(-1<>>2&7;H[k+124>>2]=(((-1<>>w)+(r>>>5|0)|0)+1;o=o+(p+(w+U|0)|0)|0;break S}r=I[(m&7)+20804|0];p=r&3;m=m>>>p|0;w=o>>>2&7;H[k+120>>2]=((m&(-1<>>5|0)|0)+3;o=r>>>2&7;H[k+124>>2]=(((-1<>>w)+(r>>>5|0)|0)+3;o=o+(w+(p+U|0)|0)|0}T:{X=H[k+120>>2];if(X>>>0<=ta>>>0){w=H[k+124>>2];if(w>>>0<=ta>>>0){break T}}if(_){i=0;Ba(S,1,15756,0);break j}i=0;Ba(S,1,15756,0);break j}H[k+240>>2]=z-o;m=o&31;if((o&63)>>>0>=32){o=0;q=q>>>m|0}else{o=q>>>m|0;q=((1<>>m}H[k+232>>2]=q;H[k+236>>2]=o;o=i&240|x&15;z=R+4|0;q=(z|0)<=(A|0)?255:255>>>(z-A<<1)|0;x=($|0)>1?q:q&85;if(o&(x^-1)){if(_){i=0;Ba(S,1,12194,0);break j}i=0;Ba(S,1,12194,0);break j}U:{V:{if(E){n=Ma(k+192|0);p=X+(h<<19>>31)|0;H[k+208>>2]=H[k+208>>2]-p;m=H[k+204>>2];q=H[k+200>>2];r=p&31;if((p&63)>>>0>=32){o=0;q=m>>>r|0}else{o=m>>>r|0;q=((1<>>r}H[k+200>>2]=q;H[k+204>>2]=o;T=(n&(-1<>>8&1)<>2]=T}W:{if(h&32){n=Ma(k+192|0);p=X+(h<<18>>31)|0;H[k+208>>2]=H[k+208>>2]-p;m=H[k+204>>2];q=H[k+200>>2];r=p&31;if((p&63)>>>0>=32){o=0;q=m>>>r|0}else{o=m>>>r|0;q=((1<>>r}H[k+200>>2]=q;H[k+204>>2]=o;q=n&(-1<>>9&1)<>2]=q+2<>>0>q>>>0?n:q)|128;break W}if(!(x&2)){break W}H[(A<<2)+c>>2]=0}p=c+4|0;X:{Y:{if(h&64){n=Ma(k+192|0);r=X+(h<<17>>31)|0;H[k+208>>2]=H[k+208>>2]-r;m=H[k+204>>2];q=H[k+200>>2];E=r&31;if((r&63)>>>0>=32){o=0;q=m>>>E|0}else{o=m>>>E|0;q=((1<>>E}H[k+200>>2]=q;H[k+204>>2]=o;q=(n&(-1<>>10&1)<>2]=q}F[j+1|0]=0;Z:{if(h&128){n=Ma(k+192|0);r=X-(h>>>15|0)|0;H[k+208>>2]=H[k+208>>2]-r;m=H[k+204>>2];q=H[k+200>>2];E=r&31;if((r&63)>>>0>=32){o=0;q=m>>>E|0}else{o=m>>>E|0;q=((1<>>E}H[k+200>>2]=q;H[k+204>>2]=o;q=n&(-1<>>11&1)<>2]=q+2<>2]=0}m=c+8|0;_:{$:{if(i&16){n=Ma(k+192|0);p=w+(i<<19>>31)|0;H[k+208>>2]=H[k+208>>2]-p;h=H[k+204>>2];q=H[k+200>>2];r=p&31;if((p&63)>>>0>=32){o=0;q=h>>>r|0}else{o=h>>>r|0;q=((1<>>r}H[k+200>>2]=q;H[k+204>>2]=o;p=(n&(-1<>>8&1)<>2]=p}aa:{if(i&32){n=Ma(k+192|0);p=w+(i<<18>>31)|0;H[k+208>>2]=H[k+208>>2]-p;h=H[k+204>>2];q=H[k+200>>2];r=p&31;if((p&63)>>>0>=32){o=0;q=h>>>r|0}else{o=h>>>r|0;q=((1<>>r}H[k+200>>2]=q;H[k+204>>2]=o;q=n&(-1<>>9&1)<>2]=q+2<>>0>q>>>0?n:q)|128;break aa}if(!(x&32)){break aa}H[m+(A<<2)>>2]=0}m=c+12|0;ba:{ca:{if(i&64){n=Ma(k+192|0);p=w+(i<<17>>31)|0;H[k+208>>2]=H[k+208>>2]-p;h=H[k+204>>2];q=H[k+200>>2];r=p&31;if((p&63)>>>0>=32){o=0;q=h>>>r|0}else{o=h>>>r|0;q=((1<>>r}H[k+200>>2]=q;H[k+204>>2]=o;p=(n&(-1<>>10&1)<>2]=p}j=j+2|0;F[j|0]=0;da:{if(i&128){n=Ma(k+192|0);p=w-(i>>>15|0)|0;H[k+208>>2]=H[k+208>>2]-p;h=H[k+204>>2];q=H[k+200>>2];r=p&31;if((p&63)>>>0>=32){o=0;q=h>>>r|0}else{o=h>>>r|0;q=((1<>>r}H[k+200>>2]=q;H[k+204>>2]=o;i=n&(-1<>>11&1)<>2]=i+2<>>0<128){break da}H[m+(A<<2)>>2]=0}Z=Z^16;l=(R&4)+l|0;c=c+16|0;if((z|0)<(A|0)){continue}break}}ia=V&8;ja=s+1584|0;ka=s+1056|0;fa=s+528|0;if(($|0)>=3){la=N(A,12);ma=A<<3;da=sa-1|0;b=sa-2|0;t=3<>>1&2147483644)+4|0;r=2;while(1){R=r;T=I[u|0];F[u|0]=0;Z=Z&-17^2;ea:{if((A|0)<=0){r=r+2|0;break ea}b=R&4?fa:s;r=R+2|0;j=ha+(N(A,R)<<2)|0;p=0;c=u;E=0;while(1){V=p;P=T&255;i=I[c+1|0]>>>5&4|(P>>>7|E);h=J[(i<<8|(lb(k+224|0)&127)<<1)+18704>>1];fa:{if(i){break fa}i=g-2|0;h=(i|0)==-1?h:0;if((g|0)>1){g=i;break fa}g=Ya(k+256|0)}q=H[k+236>>2];n=H[k+232>>2];i=H[k+240>>2];l=H[b>>2]|(h>>>4&3|h>>>2&48)<>2]=l;U=h&64;X=h&128;E=U>>>5|X>>>6;o=i;i=h&7;x=o-i|0;n=((1<>>i;q=q>>>i|0;p=n;i=0;if((A|0)>(V|2)){o=I[c+2|0]>>>5&4|I[c+1|0]>>>7|E;i=J[(o<<8|(n&127)<<1)+18704>>1];ga:{if(o){break ga}o=g-2|0;i=(o|0)==-1?i:0;if((g|0)>1){g=o;break ga}g=Ya(k+256|0)}o=i&7;x=x-o|0;E=(i>>>5|i>>>6)&2;n=((1<>>o;p=n;q=q>>>o|0}H[b>>2]=l|(i<<2&768|i&48)<>>2&2|h>>>3&1;switch(z|0){case 0:break ha;case 3:break ia;default:break ja}}o=I[(p&7)+20804|0];w=o>>>2&7;v=p;p=o&3;l=(((-1<>>p)+(o>>>5|0)|0)+1|0;m=(z|0)==1;o=m?1:l;l=m?l:1;z=p+w|0;break ha}v=I[(p&7)+20804|0];m=v&3;o=p>>>m|0;w=I[(o&7)+20804|0];l=w&3;p=v>>>2&7;M=p+(l+m|0)|0;m=w>>>2&7;z=M+m|0;o=o>>>l|0;l=((o&(-1<>>5|0)|0)+1|0;o=(((-1<>>p)+(w>>>5|0)|0)+1|0}H[k+240>>2]=x-z;m=z&31;if((z&63)>>>0>=32){w=0;q=q>>>m|0}else{w=q>>>m|0;q=((1<>>m}H[k+232>>2]=q;H[k+236>>2]=w;p=h&240;if(p-1&p){n=P&127;q=I[c+1|0]&127;n=n>>>0>q>>>0?n:q;q=n-2|0;l=(n>>>0>=q>>>0?q:0)+l|0}m=i&240;if(m-1&m){n=I[c+1|0]&127;q=I[c+2|0]&127;q=n>>>0>q>>>0?n:q;o=(q>>>0>2?q-2|0:0)+o|0}if(!(l>>>0<=ta>>>0&o>>>0<=ta>>>0)){if(_){i=0;Ba(S,1,15856,0);break j}i=0;Ba(S,1,15856,0);break j}T=I[c+2|0];F[c+1|0]=0;F[c+2|0]=0;n=m|p>>>4;p=V+4|0;q=(p|0)<=(A|0)?255:255>>>(p-A<<1)|0;P=(r|0)>($|0)?q&85:q;if(n&(P^-1)){if(_){i=0;Ba(S,1,12194,0);break j}i=0;Ba(S,1,12194,0);break j}ka:{la:{if(h&16){n=Ma(k+192|0);x=(h<<19>>31)+l|0;H[k+208>>2]=H[k+208>>2]-x;m=H[k+204>>2];q=H[k+200>>2];z=x&31;if((x&63)>>>0>=32){w=0;q=m>>>z|0}else{w=m>>>z|0;q=((1<>>z}H[k+200>>2]=q;H[k+204>>2]=w;x=(n&(-1<>>8&1)<>2]=x}ma:{if(h&32){n=Ma(k+192|0);x=(h<<18>>31)+l|0;H[k+208>>2]=H[k+208>>2]-x;m=H[k+204>>2];q=H[k+200>>2];z=x&31;if((x&63)>>>0>=32){w=0;q=m>>>z|0}else{w=m>>>z|0;q=((1<>>z}H[k+200>>2]=q;H[k+204>>2]=w;q=n&(-1<>>9&1)<>2]=q+2<>>0>q>>>0?n:q)|128;break ma}if(!(P&2)){break ma}H[(A<<2)+j>>2]=0}x=j+4|0;na:{oa:{if(U){n=Ma(k+192|0);z=(h<<17>>31)+l|0;H[k+208>>2]=H[k+208>>2]-z;m=H[k+204>>2];q=H[k+200>>2];U=z&31;if((z&63)>>>0>=32){w=0;q=m>>>U|0}else{w=m>>>U|0;q=((1<>>U}H[k+200>>2]=q;H[k+204>>2]=w;m=(n&(-1<>>10&1)<>2]=m}pa:{if(X){n=Ma(k+192|0);l=l-(h>>>15|0)|0;H[k+208>>2]=H[k+208>>2]-l;m=H[k+204>>2];q=H[k+200>>2];z=l&31;if((l&63)>>>0>=32){w=0;q=m>>>z|0}else{w=m>>>z|0;q=((1<>>z}H[k+200>>2]=q;H[k+204>>2]=w;q=n&(-1<>>11&1)<>2]=q+2<>2]=0}m=j+8|0;qa:{ra:{if(i&16){n=Ma(k+192|0);l=(i<<19>>31)+o|0;H[k+208>>2]=H[k+208>>2]-l;h=H[k+204>>2];q=H[k+200>>2];x=l&31;if((l&63)>>>0>=32){w=0;q=h>>>x|0}else{w=h>>>x|0;q=((1<>>x}H[k+200>>2]=q;H[k+204>>2]=w;l=(n&(-1<>>8&1)<>2]=l}sa:{if(i&32){n=Ma(k+192|0);l=(i<<18>>31)+o|0;H[k+208>>2]=H[k+208>>2]-l;h=H[k+204>>2];q=H[k+200>>2];x=l&31;if((l&63)>>>0>=32){w=0;q=h>>>x|0}else{w=h>>>x|0;q=((1<>>x}H[k+200>>2]=q;H[k+204>>2]=w;q=n&(-1<>>9&1)<>2]=q+2<>>0>q>>>0?n:q)|128;break sa}if(!(P&32)){break sa}H[m+(A<<2)>>2]=0}m=j+12|0;ta:{ua:{if(i&64){n=Ma(k+192|0);l=(i<<17>>31)+o|0;H[k+208>>2]=H[k+208>>2]-l;h=H[k+204>>2];q=H[k+200>>2];x=l&31;if((l&63)>>>0>=32){w=0;q=h>>>x|0}else{w=h>>>x|0;q=((1<>>x}H[k+200>>2]=q;H[k+204>>2]=w;l=(n&(-1<>>10&1)<>2]=l}c=c+2|0;va:{if(i&128){n=Ma(k+192|0);h=o-(i>>>15|0)|0;H[k+208>>2]=H[k+208>>2]-h;o=H[k+204>>2];q=H[k+200>>2];l=h&31;if((h&63)>>>0>=32){w=0;o=o>>>l|0}else{w=o>>>l|0;o=((1<>>l}H[k+200>>2]=o;H[k+204>>2]=w;i=n&(-1<>>11&1)<>2]=i+2<>>0<128){break va}H[m+(A<<2)>>2]=0}Z=Z^16;b=(V&4)+b|0;j=j+16|0;if((p|0)<(A|0)){continue}break}}wa:{if(!(R&2)|e>>>0<2){break wa}m=r&4;xa:{ya:{za:{Aa:{Ba:{if(aa){x=m?s:fa;z=0;if((A|0)<=0){break Ba}q=ha+(N(A,R-2|0)<<2)|0;while(1){i=lb(k+128|0);h=0;j=H[x>>2];if(j){h=q+(z<<2)|0;o=0;c=15;while(1){Ca:{if(!(c&j)){break Ca}n=c&286331153;if(n&j){H[h>>2]=W|H[h>>2]^((i^-1)&1)<>>1|0}if(j&n<<1){b=(A<<2)+h|0;H[b>>2]=W|H[b>>2]^((i^-1)&1)<>>1|0}if(j&n<<2){b=h+ma|0;H[b>>2]=W|H[b>>2]^((i^-1)&1)<>>1|0}if(!(j&n<<3)){break Ca}b=h+la|0;H[b>>2]=W|H[b>>2]^((i^-1)&1)<>>1|0}h=h+4|0;c=c<<4;o=o+1|0;if((o|0)!=8){continue}break}h=xe(j)}x=x+4|0;H[k+144>>2]=H[k+144>>2]-h;c=H[k+140>>2];b=H[k+136>>2];i=h&31;if((h&63)>>>0>=32){w=0;b=c>>>i|0}else{w=c>>>i|0;b=((1<>>i}H[k+136>>2]=b;H[k+140>>2]=w;z=z+8|0;if((A|0)>(z|0)){continue}break}}l=0;i=0;ba=m?ka:ja;h=ba;x=m?s:fa;c=x;if((A|0)>0){break za}b=!m;break Aa}ba=m?ka:ja;b=!m}if(R>>>0<=5){break wa}q=b?s:fa;if((A|0)<=0){break xa}c=b?ka:ja;break ya}while(1){b=i>>>28|0;i=H[c>>2];b=i|(b|i<<4|i>>>4);H[h>>2]=b;b=b|H[c+4>>2]<<28;H[h>>2]=(b>>>1&2004318071|b<<1&-286331154|b)&(i^-1);h=h+4|0;c=c+4|0;l=l+8|0;if((A|0)>(l|0)){continue}break}if(R>>>0<6){break wa}q=m?fa:s;c=m?ja:ka}b=0;o=0;h=x;m=c;i=c;c=q;while(1){j=h+4|0;n=H[i>>2];h=H[h>>2];if(!ia){n=n|(h|(h<<4|b>>>28|h>>>4|H[j>>2]<<28))<<3&-2004318072}H[i>>2]=n&(H[c>>2]^-1);c=c+4|0;i=i+4|0;b=h;h=j;o=o+8|0;if((A|0)>(o|0)){continue}break}if((A|0)<=0){break xa}U=ha+(N(A,R-6|0)<<2)|0;T=0;b=q;while(1){j=0;c=H[m>>2];if(c){X=A-T|0;R=(T<<2)+U|0;i=0;E=0;while(1){n=i;i=Ma(k+160|0);o=E+4|0;P=(A|0)>(o+T|0)?o:X;Da:{if((P|0)<=(E|0)){h=0;break Da}M=H[b>>2]^-1;w=E<<2;z=R+w|0;h=0;o=E;p=15<>>1|0}V=v<<1;if(V&c){if(i&1){j=j|V;c=M&116<<(o<<2)|c}h=h+1|0;i=i>>>1|0}V=v<<2;if(V&c){if(i&1){j=j|V;c=M&232<<(o<<2)|c}h=h+1|0;i=i>>>1|0}V=v<<3;if(!(V&c)){break Ea}if(i&1){j=j|V;c=M&192<<(o<<2)|c}h=h+1|0;i=i>>>1|0}l=l<<4;o=o+1|0;if((P|0)>(o|0)){continue}break}if(!(j>>>w&65535)){break Da}while(1){Fa:{if(!(j&p)){break Fa}l=p&286331153;if(l&j){H[z>>2]=t|(H[z>>2]|i<<31);h=h+1|0;i=i>>>1|0}if(l<<1&j){o=(A<<2)+z|0;H[o>>2]=t|(H[o>>2]|i<<31);h=h+1|0;i=i>>>1|0}if(l<<2&j){o=z+ma|0;H[o>>2]=t|(H[o>>2]|i<<31);h=h+1|0;i=i>>>1|0}if(!(l<<3&j)){break Fa}o=z+la|0;H[o>>2]=t|(H[o>>2]|i<<31);h=h+1|0;i=i>>>1|0}p=p<<4;z=z+4|0;E=E+1|0;if((P|0)>(E|0)){continue}break}}H[k+176>>2]=H[k+176>>2]-h;o=H[k+172>>2];i=H[k+168>>2];l=h&31;if((h&63)>>>0>=32){w=0;i=o>>>l|0}else{w=o>>>l|0;i=((1<>>l}H[k+168>>2]=i;H[k+172>>2]=w;i=1;E=4;if(!(n&1)){continue}break}H[m+4>>2]=H[m+4>>2]|(j>>>27&14|j>>>29|j>>>28)&(H[b+4>>2]^-1)}n=H[b>>2]|j;o=n>>>3&286331153;i=o>>>4|o<<4|o;if(T){c=ba-4|0;H[c>>2]=H[c>>2]|(H[x-4>>2]^-1)&o<<28}H[ba>>2]=H[ba>>2]|i&(H[x>>2]^-1);H[ba+4>>2]=H[ba+4>>2]|(H[x+4>>2]^-1)&n>>>31;m=m+4|0;b=b+4|0;ba=ba+4|0;x=x+4|0;T=T+8|0;if((A|0)>(T|0)){continue}break}}if(!ea){break wa}y(q,0,ea)}if((r|0)<($|0)){continue}break}}Ga:{if(e>>>0<2){break Ga}e=($&3)-1|0;Ha:{if(aa&e>>>0<2){if((A|0)<=0){break Ha}p=1<>2];if(u){h=j+(E<<2)|0;c=15;o=0;while(1){Ia:{if(!(c&u)){break Ia}m=c&286331153;if(m&u){H[h>>2]=p|H[h>>2]^((i^-1)&1)<>>1|0}if(u&m<<1){b=(A<<2)+h|0;H[b>>2]=p|H[b>>2]^((i^-1)&1)<>>1|0}if(u&m<<2){b=h+g|0;H[b>>2]=p|H[b>>2]^((i^-1)&1)<>>1|0}if(!(u&m<<3)){break Ia}b=h+n|0;H[b>>2]=p|H[b>>2]^((i^-1)&1)<>>1|0}h=h+4|0;c=c<<4;o=o+1|0;if((o|0)!=8){continue}break}h=xe(u)}q=q+4|0;H[k+144>>2]=H[k+144>>2]-h;c=H[k+140>>2];b=H[k+136>>2];i=h&31;if((h&63)>>>0>=32){w=0;b=c>>>i|0}else{w=c>>>i|0;b=((1<>>i}H[k+136>>2]=b;H[k+140>>2]=w;E=E+8|0;if((A|0)>(E|0)){continue}break}}if((A|0)<=0|e>>>0>1){break Ha}b=$&4;h=b?fa:s;c=b?ja:ka;l=0;i=0;while(1){b=i>>>28|0;i=H[h>>2];b=i|(b|i<<4|i>>>4);H[c>>2]=b;b=b|H[h+4>>2]<<28;H[c>>2]=(b>>>1&2004318071|b<<1&-286331154|b)&(i^-1);c=c+4|0;h=h+4|0;l=l+8|0;if((A|0)>(l|0)){continue}break}}b=($|0)>6?($-($+1&3)|0)-3|0:0;if(($|0)<=(b|0)){break Ga}r=N(A,12);R=A<<3;ea=3<>>0>=3){Z=-1;if((g|0)<5){break La}if(X){break Ja}g=b&4;h=g?fa:s;i=g?ja:ka;c=0;if(!ia){c=g?s:fa;l=0;j=0;while(1){g=j>>>28|0;Z=-1;j=H[c>>2];H[i>>2]=(H[i>>2]|(j|(g|j<<4|j>>>4|H[c+4>>2]<<28))<<3&-2004318072)&(H[h>>2]^-1);h=h+4|0;i=i+4|0;c=c+4|0;l=l+8|0;if((A|0)>(l|0)){continue}break}break Ka}while(1){Z=-1;H[i>>2]=H[i>>2]&(H[h>>2]^-1);h=h+4|0;i=i+4|0;c=c+8|0;if((A|0)>(c|0)){continue}break}break Ka}Z=H[(c<<2)+20812>>2]}if(X){break Ja}}c=b&4;q=c?fa:s;g=c?ja:ka;x=c?s:fa;T=c?ka:ja;V=ha+(N(b,A)<<2)|0;p=0;while(1){j=0;c=H[g>>2]&Z;if(c){aa=A-p|0;u=V+(p<<2)|0;i=0;e=0;while(1){n=i;i=Ma(k+160|0);o=e+4|0;w=(A|0)>(o+p|0)?o:aa;Ma:{if((w|0)<=(e|0)){h=0;break Ma}E=e<<2;z=E+u|0;v=(H[q>>2]^-1)&Z;h=0;o=e;P=15<>>1|0}m=U<<1;if(m&c){if(i&1){j=j|m;c=v&116<<(o<<2)|c}h=h+1|0;i=i>>>1|0}m=U<<2;if(m&c){if(i&1){j=j|m;c=v&232<<(o<<2)|c}h=h+1|0;i=i>>>1|0}m=U<<3;if(!(m&c)){break Na}if(i&1){j=j|m;c=v&192<<(o<<2)|c}h=h+1|0;i=i>>>1|0}l=l<<4;o=o+1|0;if((w|0)>(o|0)){continue}break}if(!(j>>>E&65535)){break Ma}while(1){Oa:{if(!(j&P)){break Oa}m=P&286331153;if(m&j){H[z>>2]=ea|(H[z>>2]|i<<31);h=h+1|0;i=i>>>1|0}if(m<<1&j){o=(A<<2)+z|0;H[o>>2]=ea|(H[o>>2]|i<<31);h=h+1|0;i=i>>>1|0}if(m<<2&j){o=z+R|0;H[o>>2]=ea|(H[o>>2]|i<<31);h=h+1|0;i=i>>>1|0}if(!(m<<3&j)){break Oa}o=r+z|0;H[o>>2]=ea|(H[o>>2]|i<<31);h=h+1|0;i=i>>>1|0}P=P<<4;z=z+4|0;e=e+1|0;if((w|0)>(e|0)){continue}break}}H[k+176>>2]=H[k+176>>2]-h;o=H[k+172>>2];i=H[k+168>>2];e=h&31;if((h&63)>>>0>=32){w=0;i=o>>>e|0}else{w=o>>>e|0;i=((1<>>e}H[k+168>>2]=i;H[k+172>>2]=w;i=1;e=4;if(!(n&1)){continue}break}H[g+4>>2]=H[g+4>>2]|(j>>>27&14|j>>>29|j>>>28)&(H[q+4>>2]^-1)}n=H[q>>2]|j;o=n>>>3&286331153;i=o>>>4|o<<4|o;if(p){c=T-4|0;H[c>>2]=H[c>>2]|(H[x-4>>2]^-1)&o<<28}H[T>>2]=H[T>>2]|i&(H[x>>2]^-1);H[T+4>>2]=H[T+4>>2]|(H[x+4>>2]^-1)&n>>>31;g=g+4|0;q=q+4|0;T=T+4|0;x=x+4|0;p=p+8|0;if((A|0)>(p|0)){continue}break}}b=b+4|0;if(($|0)>(b|0)){continue}break}}i=1;if(($|0)<=0|(A|0)<=0){break j}n=A&2147483644;o=A&3;q=ca-D>>>0>4294967292;b=0;while(1){i=ha+(N(b,A)<<2)|0;h=0;Pa:{if(!q){while(1){g=H[i>>2];c=g&2147483647;H[i>>2]=(g|0)<0?0-c|0:c;g=H[i+4>>2];c=g&2147483647;H[i+4>>2]=(g|0)<0?0-c|0:c;g=H[i+8>>2];c=g&2147483647;H[i+8>>2]=(g|0)<0?0-c|0:c;g=H[i+12>>2];c=g&2147483647;H[i+12>>2]=(g|0)<0?0-c|0:c;i=i+16|0;h=h+4|0;if((n|0)!=(h|0)){continue}break}if(!o){break Pa}}h=0;while(1){g=H[i>>2];c=g&2147483647;H[i>>2]=(g|0)<0?0-c|0:c;i=i+4|0;h=h+1|0;if((o|0)!=(h|0)){continue}break}}i=1;b=b+1|0;if(($|0)!=(b|0)){continue}break}break j}if(!_){break w}H[k+52>>2]=H[C+24>>2];H[k+48>>2]=P;Ba(S,1,9686,k+48|0);break v}H[k+20>>2]=i;H[k+16>>2]=P;Ba(S,1,9686,k+16|0);i=0;break j}i=0}na=k+304|0;if(i){break i}break b}H[f+108>>2]=(b<<9)+22336;c=0;b=H[f+116>>2];Qa:{Ra:{m=H[C+16>>2]-H[C+8>>2]|0;e=H[C+20>>2]-H[C+12>>2]|0;g=N(m,e);Sa:{Ta:{Ua:{if(g>>>0>K[f+132>>2]){Ca(b);b=Ia(g<<2);H[f+116>>2]=b;if(!b){break Sa}H[f+132>>2]=g;break Ua}if(!b){break Ta}}g=g<<2;if(!g){break Ta}y(b,0,g)}b=H[f+120>>2];l=m+2|0;o=e+3>>>2|0;g=N(l,o+2|0);if(g>>>0<=K[f+136>>2]){z=g<<2;break Ra}Ca(b);z=g<<2;b=Ia(z);H[f+120>>2]=b;if(b){break Ra}}b=0;break Qa}H[f+136>>2]=g;if(z){y(b,0,z)}Va:{if(!l){break Va}k=l&7;n=H[f+120>>2];b=n;q=m+1|0;Wa:{if(q>>>0>=7){g=l&-8;while(1){H[b+24>>2]=1226833920;H[b+28>>2]=1226833920;H[b+16>>2]=1226833920;H[b+20>>2]=1226833920;H[b+8>>2]=1226833920;H[b+12>>2]=1226833920;H[b>>2]=1226833920;H[b+4>>2]=1226833920;b=b+32|0;c=c+8|0;if((g|0)!=(c|0)){continue}break}if(!k){break Wa}}c=0;while(1){H[b>>2]=1226833920;b=b+4|0;c=c+1|0;if((k|0)!=(c|0)){continue}break}}k=l&7;b=n+(N(l,o+1|0)<<2)|0;Xa:{if(q>>>0>=7){g=l&-8;c=0;while(1){H[b+24>>2]=1226833920;H[b+28>>2]=1226833920;H[b+16>>2]=1226833920;H[b+20>>2]=1226833920;H[b+8>>2]=1226833920;H[b+12>>2]=1226833920;H[b>>2]=1226833920;H[b+4>>2]=1226833920;b=b+32|0;c=c+8|0;if((g|0)!=(c|0)){continue}break}if(!k){break Xa}}c=0;while(1){H[b>>2]=1226833920;b=b+4|0;c=c+1|0;if((k|0)!=(c|0)){continue}break}}b=e&3;if(!b){break Va}k=(b|0)==1?1224736768:(b|0)==2?1207959552:1073741824;g=l&7;b=n+(N(l,o)<<2)|0;if(q>>>0>=7){c=l&-8;z=0;while(1){H[b+28>>2]=k;H[b+24>>2]=k;H[b+20>>2]=k;H[b+16>>2]=k;H[b+12>>2]=k;H[b+8>>2]=k;H[b+4>>2]=k;H[b>>2]=k;b=b+32|0;z=z+8|0;if((c|0)!=(z|0)){continue}break}if(!g){break Va}}z=0;while(1){H[b>>2]=k;b=b+4|0;z=z+1|0;if((g|0)!=(z|0)){continue}break}}H[f+128>>2]=e;H[f+124>>2]=m;b=1}if(!b){break b}z=j+H[C+28>>2]|0;if((z|0)>=31){if(!_){break h}H[Y+16>>2]=z;Ba(S,2,8716,Y+16|0);break b}Yb(f);Xa(f,18,46);Xa(f,17,3);Xa(f,0,4);if(H[C+64>>2]){break i}c=H[C+52>>2];Ya:{if(!(c>>>0<=1&(!H[f+144>>2]|(c|0)!=1))){k=c&3;b=H[C+4>>2];g=0;Za:{if(c-1>>>0>=3){c=c&-4;while(1){q=(t<<3)+b|0;g=H[q+28>>2]+(H[q+20>>2]+(H[q+12>>2]+(H[q+4>>2]+g|0)|0)|0)|0;t=t+4|0;h=h+4|0;if((c|0)!=(h|0)){continue}break}if(!k){break Za}}while(1){g=H[((t<<3)+b|0)+4>>2]+g|0;t=t+1|0;i=i+1|0;if((k|0)!=(i|0)){continue}break}}ia=H[f+148>>2];c=g+2|0;if(c>>>0>K[f+152>>2]){b=Ha(ia,c);if(!b){break b}H[f+148>>2]=b;b=b+g|0;F[b|0]=0;F[b+1|0]=0;H[f+152>>2]=c;ia=H[f+148>>2];if(!H[C+52>>2]){break Ya}b=H[C+4>>2]}g=0;t=0;while(1){k=t<<3;c=k+b|0;b=H[c+4>>2];if(b){B(g+ia|0,H[c>>2],b)}b=H[C+4>>2];g=H[(k+b|0)+4>>2]+g|0;t=t+1|0;if(t>>>0>2]){continue}break}break Ya}if((c|0)!=1){break i}ia=H[H[C+4>>2]>>2]}b=H[C+60>>2];if(b){Z=H[f+116>>2];H[f+116>>2]=b}if(H[C+44>>2]){P=V&8;ca=f+28|0;ha=!(V&2);la=2;while(1){k=U+ia|0;ma=H[C>>2]+N(X,24)|0;c=H[ma>>2];ea=V&(la>>>0<2&(H[C+28>>2]-4|0)>=(z|0));_a:{if(ea){H[f+20>>2]=k;b=c+k|0;H[f+24>>2]=b;G[f+112>>1]=I[b|0]|I[b+1|0]<<8;F[b|0]=255;F[H[f+24>>2]+1|0]=255;H[f+8>>2]=0;H[f>>2]=0;H[f+16>>2]=k;break _a}H[f+20>>2]=k;b=c+k|0;H[f+24>>2]=b;G[f+112>>1]=I[b|0]|I[b+1|0]<<8;F[b|0]=255;F[H[f+24>>2]+1|0]=255;H[f+104>>2]=f+28;H[f+16>>2]=k;H[f+12>>2]=0;b=c?I[k|0]<<16:16711680;H[f>>2]=b;i=1;c=k+1|0;g=I[k+1|0];$a:{if(I[k|0]==255){if(g>>>0>=144){H[f+12>>2]=1;b=b|65280;break $a}H[f+16>>2]=c;i=0;b=b+(g<<9)|0;break $a}H[f+16>>2]=c;b=b|g<<8}H[f+8>>2]=i;H[f+4>>2]=32768;H[f>>2]=b<<7}R=H[ma>>2];ab:{if(!H[ma+8>>2]|(z|0)<=0){break ab}aa=ea|ha;ba=0;while(1){bb:{cb:{db:{eb:{switch(la-1|0){default:if(!ea){break db}b=1<>>1|b;e=H[f+124>>2];d=e<<2;b=(d+H[f+120>>2]|0)+12|0;g=H[f+116>>2];l=0;c=H[f+128>>2];if(c>>>0>=4){if(!e){break bb}o=N(e,12);n=e<<3;j=0-m|0;while(1){c=0;while(1){k=b;b=H[b>>2];fb:{if(!b){break fb}if(!(!(b&495)|b&2097168)){b=H[f>>2];i=H[f+8>>2];gb:{if(i){break gb}i=(b|0)!=255;q=H[f+16>>2];b=I[q|0];hb:{if(i){i=8}else{if(b>>>0>143){break hb}i=7}H[f>>2]=b;H[f+16>>2]=q+1;break gb}i=8;b=255}i=i-1|0;H[f+8>>2]=i;ib:{if(!(b>>>i&1)){break ib}jb:{if(i){break jb}i=(b|0)!=255;q=H[f+16>>2];b=I[q|0];kb:{if(i){i=8}else{if(b>>>0>143){break kb}i=7}H[f>>2]=b;H[f+16>>2]=q+1;break jb}i=8;b=255}i=i-1|0;H[f+8>>2]=i;q=b>>>i&1;H[g>>2]=q?j:m;i=H[f+124>>2];b=k-4|0;H[b>>2]=H[b>>2]|32;H[k+4>>2]=H[k+4>>2]|8;H[k>>2]=H[k>>2]|q<<19|16;if(P){break ib}b=k+(-2-i<<2)|0;H[b+4>>2]=H[b+4>>2]|32768;H[b>>2]=H[b>>2]|q<<31|65536;b=b-4|0;H[b>>2]=H[b>>2]|131072}b=H[k>>2]|2097152;H[k>>2]=b}if(!(!(b&3960)|b&16777344)){b=H[f>>2];i=H[f+8>>2];lb:{if(i){break lb}i=(b|0)!=255;q=H[f+16>>2];b=I[q|0];mb:{if(i){i=8}else{if(b>>>0>143){break mb}i=7}H[f>>2]=b;H[f+16>>2]=q+1;break lb}i=8;b=255}i=i-1|0;H[f+8>>2]=i;if(b>>>i&1){nb:{if(i){break nb}i=(b|0)!=255;q=H[f+16>>2];b=I[q|0];ob:{if(i){i=8}else{if(b>>>0>143){break ob}i=7}H[f>>2]=b;H[f+16>>2]=q+1;break nb}i=8;b=255}i=i-1|0;H[f+8>>2]=i;i=b>>>i&1;H[d+g>>2]=i?j:m;b=k-4|0;H[b>>2]=H[b>>2]|256;H[k+4>>2]=H[k+4>>2]|64;b=H[k>>2]|i<<22|128}else{b=H[k>>2]}b=b|16777216;H[k>>2]=b}if(!(!(b&31680)|b&134218752)){b=H[f>>2];i=H[f+8>>2];pb:{if(i){break pb}i=(b|0)!=255;q=H[f+16>>2];b=I[q|0];qb:{if(i){i=8}else{if(b>>>0>143){break qb}i=7}H[f>>2]=b;H[f+16>>2]=q+1;break pb}i=8;b=255}i=i-1|0;H[f+8>>2]=i;if(b>>>i&1){rb:{if(i){break rb}i=(b|0)!=255;q=H[f+16>>2];b=I[q|0];sb:{if(i){i=8}else{if(b>>>0>143){break sb}i=7}H[f>>2]=b;H[f+16>>2]=q+1;break rb}i=8;b=255}i=i-1|0;H[f+8>>2]=i;i=b>>>i&1;H[g+n>>2]=i?j:m;b=k-4|0;H[b>>2]=H[b>>2]|2048;H[k+4>>2]=H[k+4>>2]|512;b=H[k>>2]|i<<25|1024}else{b=H[k>>2]}b=b|134217728;H[k>>2]=b}if(!(b&253440)|b&1073750016){break fb}b=H[f>>2];i=H[f+8>>2];tb:{if(i){break tb}i=(b|0)!=255;q=H[f+16>>2];b=I[q|0];ub:{if(i){i=8}else{if(b>>>0>143){break ub}i=7}H[f>>2]=b;H[f+16>>2]=q+1;break tb}i=8;b=255}i=i-1|0;H[f+8>>2]=i;if(b>>>i&1){vb:{if(i){break vb}i=(b|0)!=255;q=H[f+16>>2];b=I[q|0];wb:{if(i){i=8}else{if(b>>>0>143){break wb}i=7}H[f>>2]=b;H[f+16>>2]=q+1;break vb}i=8;b=255}i=i-1|0;H[f+8>>2]=i;q=b>>>i&1;H[g+o>>2]=q?j:m;i=H[f+124>>2];b=k-4|0;H[b>>2]=H[b>>2]|16384;H[k+4>>2]=H[k+4>>2]|4096;H[k>>2]=H[k>>2]|q<<28|8192;b=k+(i<<2)|0;H[b+4>>2]=H[b+4>>2]|4;H[b+12>>2]=H[b+12>>2]|1;H[b+8>>2]=H[b+8>>2]|q<<18|2}H[k>>2]=H[k>>2]|1073741824}g=g+4|0;b=k+4|0;c=c+1|0;if((e|0)!=(c|0)){continue}break}g=g+o|0;b=k+12|0;l=l+4|0;c=H[f+128>>2];if(l>>>0<(c&-4)>>>0){continue}break}}if(!e|c>>>0<=l>>>0){break cb}p=0;n=0-m|0;i=c;while(1){xb:{if((i|0)==(l|0)){i=l;break xb}d=b-4|0;j=H[b>>2];t=0;while(1){o=N(t,3);k=j>>>o|0;if(!(k&2097168|!(k&495))){c=H[f>>2];h=H[f+8>>2];yb:{if(h){break yb}i=(c|0)!=255;k=H[f+16>>2];c=I[k|0];zb:{if(i){h=8}else{if(c>>>0>143){break zb}h=7}H[f>>2]=c;H[f+16>>2]=k+1;break yb}h=8;c=255}h=h-1|0;H[f+8>>2]=h;Ab:{if(!(c>>>h&1)){break Ab}q=(N(e,t)<<2)+g|0;Bb:{if(h){break Bb}i=(c|0)!=255;k=H[f+16>>2];c=I[k|0];Cb:{if(i){h=8}else{if(c>>>0>143){break Cb}h=7}H[f>>2]=c;H[f+16>>2]=k+1;break Bb}h=8;c=255}k=h-1|0;H[f+8>>2]=k;i=c>>>k&1;H[q>>2]=i?n:m;k=H[f+124>>2];H[d>>2]=H[d>>2]|32<>2]=H[b>>2]|(i<<19|16)<>2]=H[b+4>>2]|8<>2]=H[c+4>>2]|32768;H[c>>2]=H[c>>2]|i<<31|65536;c=c-4|0;H[c>>2]=H[c>>2]|131072}if((t|0)!=3){break Ab}c=(k<<2)+b|0;H[c+4>>2]=H[c+4>>2]|4;H[c+12>>2]=H[c+12>>2]|1;H[c+8>>2]=H[c+8>>2]|i<<18|2}j=H[b>>2]|2097152<>2]=j;c=H[f+128>>2]}i=c;t=t+1|0;if(t>>>0>>0){continue}break}}g=g+4|0;b=b+4|0;p=p+1|0;if((e|0)!=(p|0)){continue}break};break cb;case 1:m=0;x=0;Db:{Eb:{Fb:{W=H[f+124>>2];if(!((W|0)!=64|H[f+128>>2]!=64)){b=1<>>1|b;u=0-s|0;q=f+100|0;k=f+96|0;w=f+28|0;g=H[f+120>>2]+268|0;e=H[f+8>>2];b=H[f+4>>2];d=H[f>>2];i=H[f+104>>2];c=H[f+116>>2];if(V&8){break Fb}while(1){r=0;while(1){o=c;j=g;g=H[g>>2];Gb:{Hb:{Ib:{if(!g){i=H[k>>2];g=H[i>>2];b=b-g|0;Jb:{if(d>>>16>>>0>>0){n=H[i+4>>2];c=b>>>0>>0;H[k>>2]=H[i+(c?8:12)>>2];while(1){Kb:{if(e){break Kb}i=H[f+16>>2];b=i+1|0;h=I[i+1|0];if(I[i|0]==255){if(h>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break Kb}H[f+16>>2]=b;d=(h<<9)+d|0;e=7;break Kb}H[f+16>>2]=b;e=8;d=(h<<8)+d|0}e=e-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;c=c?n:!n;break Jb}d=d-(g<<16)|0;if(!(b&32768)){n=H[i+4>>2];c=b>>>0>>0;H[k>>2]=H[i+(c?12:8)>>2];while(1){Lb:{if(e){break Lb}i=H[f+16>>2];g=i+1|0;h=I[i+1|0];if(I[i|0]==255){if(h>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break Lb}H[f+16>>2]=g;d=(h<<9)+d|0;e=7;break Lb}H[f+16>>2]=g;e=8;d=(h<<8)+d|0}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}c=c?!n:n;break Jb}c=H[i+4>>2]}if(!c){i=k;break Gb}c=H[q>>2];g=H[c>>2];b=b-g|0;Mb:{if(d>>>16>>>0>>0){h=H[c+4>>2];i=b>>>0>>0;c=H[(i?8:12)+c>>2];H[q>>2]=c;while(1){Nb:{if(e){break Nb}n=H[f+16>>2];b=n+1|0;m=I[n+1|0];if(I[n|0]==255){if(m>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break Nb}H[f+16>>2]=b;d=(m<<9)+d|0;e=7;break Nb}H[f+16>>2]=b;e=8;d=(m<<8)+d|0}e=e-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;n=i?h:!h;break Mb}d=d-(g<<16)|0;if(!(b&32768)){h=H[c+4>>2];g=b>>>0>>0;c=H[(g?12:8)+c>>2];H[q>>2]=c;while(1){Ob:{if(e){break Ob}n=H[f+16>>2];i=n+1|0;m=I[n+1|0];if(I[n|0]==255){if(m>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break Ob}H[f+16>>2]=i;d=(m<<9)+d|0;e=7;break Ob}H[f+16>>2]=i;e=8;d=(m<<8)+d|0}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}n=g?!h:h;break Mb}n=H[c+4>>2]}g=H[c>>2];b=b-g|0;Pb:{if(d>>>16>>>0>>0){h=H[c+4>>2];i=c;c=b>>>0>>0;H[q>>2]=H[i+(c?8:12)>>2];while(1){Qb:{if(e){break Qb}i=H[f+16>>2];b=i+1|0;m=I[i+1|0];if(I[i|0]==255){if(m>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break Qb}H[f+16>>2]=b;d=(m<<9)+d|0;e=7;break Qb}H[f+16>>2]=b;e=8;d=(m<<8)+d|0}e=e-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;c=c?h:!h;break Pb}d=d-(g<<16)|0;if(!(b&32768)){h=H[c+4>>2];i=c;c=b>>>0>>0;H[q>>2]=H[i+(c?12:8)>>2];while(1){Rb:{if(e){break Rb}i=H[f+16>>2];g=i+1|0;m=I[i+1|0];if(I[i|0]==255){if(m>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break Rb}H[f+16>>2]=g;d=(m<<9)+d|0;e=7;break Rb}H[f+16>>2]=g;e=8;d=(m<<8)+d|0}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}c=c?!h:h;break Pb}c=H[c+4>>2]}g=0;i=q;Sb:{Tb:{Ub:{Vb:{Wb:{switch(c|n<<1){case 0:m=j-4|0;i=H[j+4>>2]>>>17&4|H[m>>2]>>>19&1;c=w+(I[i+24384|0]<<2)|0;n=H[c>>2];g=H[n>>2];b=b-g|0;Xb:{if(d>>>16>>>0>>0){h=H[n+4>>2];v=c;c=b>>>0>>0;H[v>>2]=H[n+(c?8:12)>>2];while(1){Yb:{if(e){break Yb}n=H[f+16>>2];b=n+1|0;l=I[n+1|0];if(I[n|0]==255){if(l>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break Yb}H[f+16>>2]=b;d=(l<<9)+d|0;e=7;break Yb}H[f+16>>2]=b;e=8;d=(l<<8)+d|0}e=e-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;n=c?h:!h;break Xb}d=d-(g<<16)|0;if(!(b&32768)){h=H[n+4>>2];v=c;c=b>>>0>>0;H[v>>2]=H[n+(c?12:8)>>2];while(1){Zb:{if(e){break Zb}n=H[f+16>>2];g=n+1|0;l=I[n+1|0];if(I[n|0]==255){if(l>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break Zb}H[f+16>>2]=g;d=(l<<9)+d|0;e=7;break Zb}H[f+16>>2]=g;e=8;d=(l<<8)+d|0}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}n=c?!h:h;break Xb}n=H[n+4>>2]}g=I[i+24640|0];H[o>>2]=(n|0)==(g|0)?s:u;H[m>>2]=H[m>>2]|32;H[j+4>>2]=H[j+4>>2]|8;c=j-268|0;H[c>>2]=H[c>>2]|131072;c=j-260|0;H[c>>2]=H[c>>2]|32768;c=j-264|0;i=c;v=H[c>>2];c=g^n;H[i>>2]=v|c<<31|65536;i=c<<19;t=H[f+108>>2];c=w+(I[t+2|0]<<2)|0;n=H[c>>2];g=H[n>>2];b=b-g|0;_b:{if(d>>>16>>>0>>0){h=H[n+4>>2];v=c;c=b>>>0>>0;H[v>>2]=H[n+(c?8:12)>>2];while(1){$b:{if(e){break $b}n=H[f+16>>2];b=n+1|0;m=I[n+1|0];if(I[n|0]==255){if(m>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break $b}H[f+16>>2]=b;d=(m<<9)+d|0;e=7;break $b}H[f+16>>2]=b;e=8;d=(m<<8)+d|0}e=e-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;c=c?h:!h;break _b}d=d-(g<<16)|0;if(!(b&32768)){h=H[n+4>>2];v=c;c=b>>>0>>0;H[v>>2]=H[n+(c?12:8)>>2];while(1){ac:{if(e){break ac}n=H[f+16>>2];g=n+1|0;m=I[n+1|0];if(I[n|0]==255){if(m>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break ac}H[f+16>>2]=g;d=(m<<9)+d|0;e=7;break ac}H[f+16>>2]=g;e=8;d=(m<<8)+d|0}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}c=c?!h:h;break _b}c=H[n+4>>2]}g=i|16;if(!c){break Vb}break;case 1:break Wb;case 2:break Ub;case 3:break Sb;default:break Hb}}m=j-4|0;n=H[j+4>>2]>>>20&4|(H[m>>2]>>>22&1|(g>>>15&16|(g>>>19&64|g>>>3&170)));i=w+(I[n+24384|0]<<2)|0;l=H[i>>2];c=H[l>>2];b=b-c|0;bc:{if(d>>>16>>>0>>0){h=H[l+4>>2];v=i;i=b>>>0>>0;H[v>>2]=H[l+(i?8:12)>>2];while(1){cc:{if(e){break cc}e=H[f+16>>2];b=e+1|0;l=I[e+1|0];if(I[e|0]==255){if(l>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break cc}H[f+16>>2]=b;d=(l<<9)+d|0;e=7;break cc}H[f+16>>2]=b;e=8;d=(l<<8)+d|0}e=e-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;i=i?h:!h;break bc}d=d-(c<<16)|0;if(!(b&32768)){h=H[l+4>>2];c=b>>>0>>0;H[i>>2]=H[l+(c?12:8)>>2];while(1){dc:{if(e){break dc}e=H[f+16>>2];i=e+1|0;l=I[e+1|0];if(I[e|0]==255){if(l>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break dc}H[f+16>>2]=i;d=(l<<9)+d|0;e=7;break dc}H[f+16>>2]=i;e=8;d=(l<<8)+d|0}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}i=c?!h:h;break bc}i=H[l+4>>2]}c=I[n+24640|0];H[o+256>>2]=(i|0)==(c|0)?s:u;H[m>>2]=H[m>>2]|256;H[j+4>>2]=H[j+4>>2]|64;t=H[f+108>>2];g=(c^i)<<22|g|128}i=w+(I[(g>>>6&495)+t|0]<<2)|0;n=H[i>>2];c=H[n>>2];b=b-c|0;ec:{if(d>>>16>>>0>>0){h=H[n+4>>2];v=i;i=b>>>0>>0;H[v>>2]=H[n+(i?8:12)>>2];while(1){fc:{if(e){break fc}n=H[f+16>>2];b=n+1|0;m=I[n+1|0];if(I[n|0]==255){if(m>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break fc}H[f+16>>2]=b;d=(m<<9)+d|0;e=7;break fc}H[f+16>>2]=b;e=8;d=(m<<8)+d|0}e=e-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;c=i?h:!h;break ec}d=d-(c<<16)|0;if(!(b&32768)){h=H[n+4>>2];c=b>>>0>>0;H[i>>2]=H[n+(c?12:8)>>2];while(1){gc:{if(e){break gc}n=H[f+16>>2];i=n+1|0;m=I[n+1|0];if(I[n|0]==255){if(m>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break gc}H[f+16>>2]=i;d=(m<<9)+d|0;e=7;break gc}H[f+16>>2]=i;e=8;d=(m<<8)+d|0}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}c=c?!h:h;break ec}c=H[n+4>>2]}if(!c){break Tb}}m=j-4|0;n=H[j+4>>2]>>>23&4|(H[m>>2]>>>25&1|(g>>>18&16|(g>>>22&64|g>>>6&170)));i=w+(I[n+24384|0]<<2)|0;l=H[i>>2];c=H[l>>2];b=b-c|0;hc:{if(d>>>16>>>0>>0){h=H[l+4>>2];v=i;i=b>>>0>>0;H[v>>2]=H[l+(i?8:12)>>2];while(1){ic:{if(e){break ic}e=H[f+16>>2];b=e+1|0;l=I[e+1|0];if(I[e|0]==255){if(l>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break ic}H[f+16>>2]=b;d=(l<<9)+d|0;e=7;break ic}H[f+16>>2]=b;e=8;d=(l<<8)+d|0}e=e-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;i=i?h:!h;break hc}d=d-(c<<16)|0;if(!(b&32768)){h=H[l+4>>2];c=b>>>0>>0;H[i>>2]=H[l+(c?12:8)>>2];while(1){jc:{if(e){break jc}e=H[f+16>>2];i=e+1|0;l=I[e+1|0];if(I[e|0]==255){if(l>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break jc}H[f+16>>2]=i;d=(l<<9)+d|0;e=7;break jc}H[f+16>>2]=i;e=8;d=(l<<8)+d|0}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}i=c?!h:h;break hc}i=H[l+4>>2]}c=I[n+24640|0];H[o+512>>2]=(i|0)==(c|0)?s:u;H[m>>2]=H[m>>2]|2048;H[j+4>>2]=H[j+4>>2]|512;g=(c^i)<<25|g|1024;t=H[f+108>>2]}i=w+(I[(g>>>9&495)+t|0]<<2)|0;m=H[i>>2];c=H[m>>2];b=b-c|0;kc:{if(d>>>16>>>0>>0){h=H[m+4>>2];n=b>>>0>>0;H[i>>2]=H[m+(n?8:12)>>2];while(1){lc:{if(e){break lc}e=H[f+16>>2];b=e+1|0;m=I[e+1|0];if(I[e|0]==255){if(m>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break lc}H[f+16>>2]=b;d=(m<<9)+d|0;e=7;break lc}H[f+16>>2]=b;e=8;d=(m<<8)+d|0}e=e-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;c=n?h:!h;break kc}d=d-(c<<16)|0;if(!(b&32768)){h=H[m+4>>2];c=b>>>0>>0;H[i>>2]=H[m+(c?12:8)>>2];while(1){mc:{if(e){break mc}e=H[f+16>>2];n=e+1|0;m=I[e+1|0];if(I[e|0]==255){if(m>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break mc}H[f+16>>2]=n;d=(m<<9)+d|0;e=7;break mc}H[f+16>>2]=n;e=8;d=(m<<8)+d|0}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}c=c?!h:h;break kc}c=H[m+4>>2]}if(!c){break Hb}}m=j-4|0;p=H[j+4>>2]>>>26&4|(H[m>>2]>>>28&1|(g>>>21&16|(g>>>25&64|g>>>9&170)));i=w+(I[p+24384|0]<<2)|0;t=H[i>>2];c=H[t>>2];b=b-c|0;break Ib}nc:{if(g&2097168){break nc}i=w+(I[H[f+108>>2]+(g&495)|0]<<2)|0;m=H[i>>2];c=H[m>>2];b=b-c|0;oc:{if(d>>>16>>>0>>0){h=H[m+4>>2];n=b>>>0>>0;H[i>>2]=H[m+(n?8:12)>>2];while(1){pc:{if(e){break pc}e=H[f+16>>2];b=e+1|0;m=I[e+1|0];if(I[e|0]==255){if(m>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break pc}H[f+16>>2]=b;d=(m<<9)+d|0;e=7;break pc}H[f+16>>2]=b;e=8;d=(m<<8)+d|0}e=e-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;c=n?h:!h;break oc}d=d-(c<<16)|0;if(!(b&32768)){h=H[m+4>>2];c=b>>>0>>0;H[i>>2]=H[m+(c?12:8)>>2];while(1){qc:{if(e){break qc}e=H[f+16>>2];n=e+1|0;m=I[e+1|0];if(I[e|0]==255){if(m>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break qc}H[f+16>>2]=n;d=(m<<9)+d|0;e=7;break qc}H[f+16>>2]=n;e=8;d=(m<<8)+d|0}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}c=c?!h:h;break oc}c=H[m+4>>2]}if(!c){break nc}l=j-4|0;h=H[j+4>>2]>>>17&4|(H[l>>2]>>>19&1|(g>>>14&16|(g>>>16&64|g&170)));i=w+(I[h+24384|0]<<2)|0;p=H[i>>2];c=H[p>>2];b=b-c|0;rc:{if(d>>>16>>>0>>0){m=H[p+4>>2];n=b>>>0>>0;H[i>>2]=H[p+(n?8:12)>>2];while(1){sc:{if(e){break sc}e=H[f+16>>2];b=e+1|0;p=I[e+1|0];if(I[e|0]==255){if(p>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break sc}H[f+16>>2]=b;d=(p<<9)+d|0;e=7;break sc}H[f+16>>2]=b;e=8;d=(p<<8)+d|0}e=e-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;m=n?m:!m;break rc}d=d-(c<<16)|0;if(!(b&32768)){m=H[p+4>>2];c=b>>>0>>0;H[i>>2]=H[p+(c?12:8)>>2];while(1){tc:{if(e){break tc}e=H[f+16>>2];n=e+1|0;p=I[e+1|0];if(I[e|0]==255){if(p>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break tc}H[f+16>>2]=n;d=(p<<9)+d|0;e=7;break tc}H[f+16>>2]=n;e=8;d=(p<<8)+d|0}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}m=c?!m:m;break rc}m=H[p+4>>2]}n=I[h+24640|0];H[o>>2]=(m|0)==(n|0)?s:u;H[l>>2]=H[l>>2]|32;H[j+4>>2]=H[j+4>>2]|8;c=j-268|0;H[c>>2]=H[c>>2]|131072;c=j-260|0;H[c>>2]=H[c>>2]|32768;c=j-264|0;v=c;M=H[c>>2];c=m^n;H[v>>2]=M|c<<31|65536;g=c<<19|g|16}uc:{if(g&16777344){break uc}h=g>>>3|0;i=w+(I[H[f+108>>2]+(h&495)|0]<<2)|0;l=H[i>>2];c=H[l>>2];b=b-c|0;vc:{if(d>>>16>>>0>>0){m=H[l+4>>2];n=b>>>0>>0;H[i>>2]=H[l+(n?8:12)>>2];while(1){wc:{if(e){break wc}e=H[f+16>>2];b=e+1|0;l=I[e+1|0];if(I[e|0]==255){if(l>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break wc}H[f+16>>2]=b;d=(l<<9)+d|0;e=7;break wc}H[f+16>>2]=b;e=8;d=(l<<8)+d|0}e=e-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;c=n?m:!m;break vc}d=d-(c<<16)|0;if(!(b&32768)){m=H[l+4>>2];c=b>>>0>>0;H[i>>2]=H[l+(c?12:8)>>2];while(1){xc:{if(e){break xc}e=H[f+16>>2];n=e+1|0;l=I[e+1|0];if(I[e|0]==255){if(l>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break xc}H[f+16>>2]=n;d=(l<<9)+d|0;e=7;break xc}H[f+16>>2]=n;e=8;d=(l<<8)+d|0}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}c=c?!m:m;break vc}c=H[l+4>>2]}if(!c){break uc}l=j-4|0;h=H[j+4>>2]>>>20&4|(H[l>>2]>>>22&1|(g>>>15&16|(g>>>19&64|h&170)));i=w+(I[h+24384|0]<<2)|0;p=H[i>>2];c=H[p>>2];b=b-c|0;yc:{if(d>>>16>>>0>>0){m=H[p+4>>2];n=b>>>0>>0;H[i>>2]=H[p+(n?8:12)>>2];while(1){zc:{if(e){break zc}e=H[f+16>>2];b=e+1|0;p=I[e+1|0];if(I[e|0]==255){if(p>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break zc}H[f+16>>2]=b;d=(p<<9)+d|0;e=7;break zc}H[f+16>>2]=b;e=8;d=(p<<8)+d|0}e=e-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;n=n?m:!m;break yc}d=d-(c<<16)|0;if(!(b&32768)){m=H[p+4>>2];c=b>>>0>>0;H[i>>2]=H[p+(c?12:8)>>2];while(1){Ac:{if(e){break Ac}e=H[f+16>>2];n=e+1|0;p=I[e+1|0];if(I[e|0]==255){if(p>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break Ac}H[f+16>>2]=n;d=(p<<9)+d|0;e=7;break Ac}H[f+16>>2]=n;e=8;d=(p<<8)+d|0}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}n=c?!m:m;break yc}n=H[p+4>>2]}c=I[h+24640|0];H[o+256>>2]=(n|0)==(c|0)?s:u;H[l>>2]=H[l>>2]|256;H[j+4>>2]=H[j+4>>2]|64;g=(c^n)<<22|g|128}Bc:{if(g&134218752){break Bc}h=g>>>6|0;i=w+(I[H[f+108>>2]+(h&495)|0]<<2)|0;l=H[i>>2];c=H[l>>2];b=b-c|0;Cc:{if(d>>>16>>>0>>0){m=H[l+4>>2];n=b>>>0>>0;H[i>>2]=H[l+(n?8:12)>>2];while(1){Dc:{if(e){break Dc}e=H[f+16>>2];b=e+1|0;l=I[e+1|0];if(I[e|0]==255){if(l>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break Dc}H[f+16>>2]=b;d=(l<<9)+d|0;e=7;break Dc}H[f+16>>2]=b;e=8;d=(l<<8)+d|0}e=e-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;c=n?m:!m;break Cc}d=d-(c<<16)|0;if(!(b&32768)){m=H[l+4>>2];c=b>>>0>>0;H[i>>2]=H[l+(c?12:8)>>2];while(1){Ec:{if(e){break Ec}e=H[f+16>>2];n=e+1|0;l=I[e+1|0];if(I[e|0]==255){if(l>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break Ec}H[f+16>>2]=n;d=(l<<9)+d|0;e=7;break Ec}H[f+16>>2]=n;e=8;d=(l<<8)+d|0}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}c=c?!m:m;break Cc}c=H[l+4>>2]}if(!c){break Bc}l=j-4|0;h=H[j+4>>2]>>>23&4|(H[l>>2]>>>25&1|(g>>>18&16|(g>>>22&64|h&170)));i=w+(I[h+24384|0]<<2)|0;p=H[i>>2];c=H[p>>2];b=b-c|0;Fc:{if(d>>>16>>>0>>0){m=H[p+4>>2];n=b>>>0>>0;H[i>>2]=H[p+(n?8:12)>>2];while(1){Gc:{if(e){break Gc}e=H[f+16>>2];b=e+1|0;p=I[e+1|0];if(I[e|0]==255){if(p>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break Gc}H[f+16>>2]=b;d=(p<<9)+d|0;e=7;break Gc}H[f+16>>2]=b;e=8;d=(p<<8)+d|0}e=e-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;n=n?m:!m;break Fc}d=d-(c<<16)|0;if(!(b&32768)){m=H[p+4>>2];c=b>>>0>>0;H[i>>2]=H[p+(c?12:8)>>2];while(1){Hc:{if(e){break Hc}e=H[f+16>>2];n=e+1|0;p=I[e+1|0];if(I[e|0]==255){if(p>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break Hc}H[f+16>>2]=n;d=(p<<9)+d|0;e=7;break Hc}H[f+16>>2]=n;e=8;d=(p<<8)+d|0}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}n=c?!m:m;break Fc}n=H[p+4>>2]}c=I[h+24640|0];H[o+512>>2]=(n|0)==(c|0)?s:u;H[l>>2]=H[l>>2]|2048;H[j+4>>2]=H[j+4>>2]|512;g=(c^n)<<25|g|1024}if(g&1073750016){break Hb}h=g>>>9|0;i=w+(I[H[f+108>>2]+(h&495)|0]<<2)|0;l=H[i>>2];c=H[l>>2];b=b-c|0;Ic:{if(d>>>16>>>0>>0){m=H[l+4>>2];n=b>>>0>>0;H[i>>2]=H[l+(n?8:12)>>2];while(1){Jc:{if(e){break Jc}e=H[f+16>>2];b=e+1|0;l=I[e+1|0];if(I[e|0]==255){if(l>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break Jc}H[f+16>>2]=b;d=(l<<9)+d|0;e=7;break Jc}H[f+16>>2]=b;e=8;d=(l<<8)+d|0}e=e-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;c=n?m:!m;break Ic}d=d-(c<<16)|0;if(!(b&32768)){m=H[l+4>>2];c=b>>>0>>0;H[i>>2]=H[l+(c?12:8)>>2];while(1){Kc:{if(e){break Kc}e=H[f+16>>2];n=e+1|0;l=I[e+1|0];if(I[e|0]==255){if(l>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break Kc}H[f+16>>2]=n;d=(l<<9)+d|0;e=7;break Kc}H[f+16>>2]=n;e=8;d=(l<<8)+d|0}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}c=c?!m:m;break Ic}c=H[l+4>>2]}if(!c){break Hb}m=j-4|0;p=H[j+4>>2]>>>26&4|(H[m>>2]>>>28&1|(g>>>21&16|(g>>>25&64|h&170)));i=w+(I[p+24384|0]<<2)|0;t=H[i>>2];c=H[t>>2];b=b-c|0}Lc:{if(d>>>16>>>0>>0){h=H[t+4>>2];n=b>>>0>>0;H[i>>2]=H[(n?8:12)+t>>2];while(1){Mc:{if(e){break Mc}e=H[f+16>>2];b=e+1|0;l=I[e+1|0];if(I[e|0]==255){if(l>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break Mc}H[f+16>>2]=b;d=(l<<9)+d|0;e=7;break Mc}H[f+16>>2]=b;e=8;d=(l<<8)+d|0}e=e-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;n=n?h:!h;break Lc}d=d-(c<<16)|0;if(!(b&32768)){h=H[t+4>>2];c=b>>>0>>0;H[i>>2]=H[(c?12:8)+t>>2];while(1){Nc:{if(e){break Nc}e=H[f+16>>2];n=e+1|0;l=I[e+1|0];if(I[e|0]==255){if(l>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break Nc}H[f+16>>2]=n;d=(l<<9)+d|0;e=7;break Nc}H[f+16>>2]=n;e=8;d=(l<<8)+d|0}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}n=c?!h:h;break Lc}n=H[t+4>>2]}c=I[p+24640|0];H[o+768>>2]=(n|0)==(c|0)?s:u;H[m>>2]=H[m>>2]|16384;H[j+4>>2]=H[j+4>>2]|4096;H[j+260>>2]=H[j+260>>2]|4;H[j+268>>2]=H[j+268>>2]|1;c=c^n;H[j+264>>2]=H[j+264>>2]|c<<18|2;g=c<<28|g|8192}H[j>>2]=g&-1226833921}g=j+4|0;c=o+4|0;r=r+1|0;if((r|0)!=64){continue}break}g=j+12|0;c=o+772|0;n=x>>>0<60;x=x+4|0;if(n){continue}break}break Eb}b=1<>>1|b;k=H[f+120>>2];c=(k+(W<<2)|0)+12|0;g=H[f+128>>2];e=H[f+8>>2];b=H[f+4>>2];d=H[f>>2];i=H[f+104>>2];o=H[f+116>>2];if(V&8){Oc:{if(g>>>0<4){break Oc}if(W){n=f+100|0;q=f+96|0;r=N(W,12);u=W<<3;v=0-M|0;D=f+28|0;while(1){w=0;while(1){j=c;c=H[c>>2];Pc:{Qc:{Rc:{if(c){Sc:{if(c&2097168){break Sc}i=D+(I[H[f+108>>2]+(c&495)|0]<<2)|0;l=H[i>>2];g=H[l>>2];b=b-g|0;Tc:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;k=H[l+4>>2];if(b&32768){break Tc}h=H[l+4>>2];g=b>>>0>>0;H[i>>2]=H[l+(g?12:8)>>2];while(1){Uc:{if(e){break Uc}e=H[f+16>>2];k=e+1|0;l=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=k;e=8;d=(l<<8)+d|0;break Uc}if(l>>>0<=143){H[f+16>>2]=k;d=(l<<9)+d|0;e=7;break Uc}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}k=g?!h:h;break Tc}h=H[l+4>>2];k=b>>>0>>0;H[i>>2]=H[l+(k?8:12)>>2];while(1){Vc:{if(e){break Vc}e=H[f+16>>2];b=e+1|0;l=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=b;e=8;d=(l<<8)+d|0;break Vc}if(l>>>0<=143){H[f+16>>2]=b;d=(l<<9)+d|0;e=7;break Vc}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;k=k?h:!h}if(!k){break Sc}p=j-4|0;h=H[j+4>>2]>>>17&4|(H[p>>2]>>>19&1|(c>>>14&16|(c>>>16&64|c&170)));i=D+(I[h+24384|0]<<2)|0;s=H[i>>2];g=H[s>>2];b=b-g|0;Wc:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;k=H[s+4>>2];if(b&32768){break Wc}l=H[s+4>>2];g=b>>>0>>0;H[i>>2]=H[s+(g?12:8)>>2];while(1){Xc:{if(e){break Xc}e=H[f+16>>2];k=e+1|0;s=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=k;e=8;d=(s<<8)+d|0;break Xc}if(s>>>0<=143){H[f+16>>2]=k;d=(s<<9)+d|0;e=7;break Xc}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}k=g?!l:l;break Wc}l=H[s+4>>2];k=b>>>0>>0;H[i>>2]=H[s+(k?8:12)>>2];while(1){Yc:{if(e){break Yc}e=H[f+16>>2];b=e+1|0;s=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=b;e=8;d=(s<<8)+d|0;break Yc}if(s>>>0<=143){H[f+16>>2]=b;d=(s<<9)+d|0;e=7;break Yc}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;k=k?l:!l}g=I[h+24640|0];H[o>>2]=(k|0)==(g|0)?M:v;H[p>>2]=H[p>>2]|32;H[j+4>>2]=H[j+4>>2]|8;c=(g^k)<<19|c|16}Zc:{if(c&16777344){break Zc}h=c>>>3|0;i=D+(I[H[f+108>>2]+(h&495)|0]<<2)|0;p=H[i>>2];g=H[p>>2];b=b-g|0;_c:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;k=H[p+4>>2];if(b&32768){break _c}l=H[p+4>>2];g=b>>>0>>0;H[i>>2]=H[p+(g?12:8)>>2];while(1){$c:{if(e){break $c}e=H[f+16>>2];k=e+1|0;p=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=k;e=8;d=(p<<8)+d|0;break $c}if(p>>>0<=143){H[f+16>>2]=k;d=(p<<9)+d|0;e=7;break $c}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}k=g?!l:l;break _c}l=H[p+4>>2];k=b>>>0>>0;H[i>>2]=H[p+(k?8:12)>>2];while(1){ad:{if(e){break ad}e=H[f+16>>2];b=e+1|0;p=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=b;e=8;d=(p<<8)+d|0;break ad}if(p>>>0<=143){H[f+16>>2]=b;d=(p<<9)+d|0;e=7;break ad}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;k=k?l:!l}if(!k){break Zc}p=j-4|0;h=H[j+4>>2]>>>20&4|(H[p>>2]>>>22&1|(c>>>15&16|(c>>>19&64|h&170)));i=D+(I[h+24384|0]<<2)|0;s=H[i>>2];g=H[s>>2];b=b-g|0;t=(W<<2)+o|0;bd:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;k=H[s+4>>2];if(b&32768){break bd}l=H[s+4>>2];g=b>>>0>>0;H[i>>2]=H[s+(g?12:8)>>2];while(1){cd:{if(e){break cd}e=H[f+16>>2];k=e+1|0;s=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=k;e=8;d=(s<<8)+d|0;break cd}if(s>>>0<=143){H[f+16>>2]=k;d=(s<<9)+d|0;e=7;break cd}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}k=g?!l:l;break bd}l=H[s+4>>2];k=b>>>0>>0;H[i>>2]=H[s+(k?8:12)>>2];while(1){dd:{if(e){break dd}e=H[f+16>>2];b=e+1|0;s=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=b;e=8;d=(s<<8)+d|0;break dd}if(s>>>0<=143){H[f+16>>2]=b;d=(s<<9)+d|0;e=7;break dd}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;k=k?l:!l}g=I[h+24640|0];H[t>>2]=(k|0)==(g|0)?M:v;H[p>>2]=H[p>>2]|256;H[j+4>>2]=H[j+4>>2]|64;c=(g^k)<<22|c|128}ed:{if(c&134218752){break ed}h=c>>>6|0;i=D+(I[H[f+108>>2]+(h&495)|0]<<2)|0;p=H[i>>2];g=H[p>>2];b=b-g|0;fd:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;k=H[p+4>>2];if(b&32768){break fd}l=H[p+4>>2];g=b>>>0>>0;H[i>>2]=H[p+(g?12:8)>>2];while(1){gd:{if(e){break gd}e=H[f+16>>2];k=e+1|0;p=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=k;e=8;d=(p<<8)+d|0;break gd}if(p>>>0<=143){H[f+16>>2]=k;d=(p<<9)+d|0;e=7;break gd}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}k=g?!l:l;break fd}l=H[p+4>>2];k=b>>>0>>0;H[i>>2]=H[p+(k?8:12)>>2];while(1){hd:{if(e){break hd}e=H[f+16>>2];b=e+1|0;p=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=b;e=8;d=(p<<8)+d|0;break hd}if(p>>>0<=143){H[f+16>>2]=b;d=(p<<9)+d|0;e=7;break hd}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;k=k?l:!l}if(!k){break ed}p=j-4|0;h=H[j+4>>2]>>>23&4|(H[p>>2]>>>25&1|(c>>>18&16|(c>>>22&64|h&170)));i=D+(I[h+24384|0]<<2)|0;s=H[i>>2];g=H[s>>2];b=b-g|0;t=o+u|0;id:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;k=H[s+4>>2];if(b&32768){break id}l=H[s+4>>2];g=b>>>0>>0;H[i>>2]=H[s+(g?12:8)>>2];while(1){jd:{if(e){break jd}e=H[f+16>>2];k=e+1|0;s=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=k;e=8;d=(s<<8)+d|0;break jd}if(s>>>0<=143){H[f+16>>2]=k;d=(s<<9)+d|0;e=7;break jd}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}k=g?!l:l;break id}l=H[s+4>>2];k=b>>>0>>0;H[i>>2]=H[s+(k?8:12)>>2];while(1){kd:{if(e){break kd}e=H[f+16>>2];b=e+1|0;s=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=b;e=8;d=(s<<8)+d|0;break kd}if(s>>>0<=143){H[f+16>>2]=b;d=(s<<9)+d|0;e=7;break kd}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;k=k?l:!l}g=I[h+24640|0];H[t>>2]=(k|0)==(g|0)?M:v;H[p>>2]=H[p>>2]|2048;H[j+4>>2]=H[j+4>>2]|512;c=(g^k)<<25|c|1024}if(c&1073750016){break Qc}h=c>>>9|0;i=D+(I[H[f+108>>2]+(h&495)|0]<<2)|0;p=H[i>>2];g=H[p>>2];b=b-g|0;ld:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;k=H[p+4>>2];if(b&32768){break ld}l=H[p+4>>2];g=b>>>0>>0;H[i>>2]=H[p+(g?12:8)>>2];while(1){md:{if(e){break md}e=H[f+16>>2];k=e+1|0;p=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=k;e=8;d=(p<<8)+d|0;break md}if(p>>>0<=143){H[f+16>>2]=k;d=(p<<9)+d|0;e=7;break md}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}k=g?!l:l;break ld}l=H[p+4>>2];k=b>>>0>>0;H[i>>2]=H[p+(k?8:12)>>2];while(1){nd:{if(e){break nd}e=H[f+16>>2];b=e+1|0;p=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=b;e=8;d=(p<<8)+d|0;break nd}if(p>>>0<=143){H[f+16>>2]=b;d=(p<<9)+d|0;e=7;break nd}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;k=k?l:!l}if(!k){break Qc}p=j-4|0;T=H[j+4>>2]>>>26&4|(H[p>>2]>>>28&1|(c>>>21&16|(c>>>25&64|h&170)));i=D+(I[T+24384|0]<<2)|0;t=H[i>>2];g=H[t>>2];b=b-g|0;break Rc}k=H[q>>2];c=H[k>>2];b=b-c|0;od:{if(d>>>16>>>0>=c>>>0){d=d-(c<<16)|0;g=H[k+4>>2];if(b&32768){break od}i=H[k+4>>2];c=b>>>0>>0;H[q>>2]=H[k+(c?12:8)>>2];while(1){pd:{if(e){break pd}k=H[f+16>>2];g=k+1|0;h=I[k+1|0];if(I[k|0]!=255){H[f+16>>2]=g;e=8;d=(h<<8)+d|0;break pd}if(h>>>0<=143){H[f+16>>2]=g;d=(h<<9)+d|0;e=7;break pd}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}g=c?!i:i;break od}i=H[k+4>>2];g=b>>>0>>0;H[q>>2]=H[k+(g?8:12)>>2];while(1){qd:{if(e){break qd}k=H[f+16>>2];b=k+1|0;h=I[k+1|0];if(I[k|0]!=255){H[f+16>>2]=b;e=8;d=(h<<8)+d|0;break qd}if(h>>>0<=143){H[f+16>>2]=b;d=(h<<9)+d|0;e=7;break qd}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;g=g?i:!i}if(!g){i=q;break Pc}g=H[n>>2];c=H[g>>2];b=b-c|0;rd:{if(d>>>16>>>0>=c>>>0){d=d-(c<<16)|0;k=H[g+4>>2];if(b&32768){break rd}h=H[g+4>>2];c=b>>>0>>0;g=H[(c?12:8)+g>>2];H[n>>2]=g;while(1){sd:{if(e){break sd}i=H[f+16>>2];k=i+1|0;l=I[i+1|0];if(I[i|0]!=255){H[f+16>>2]=k;e=8;d=(l<<8)+d|0;break sd}if(l>>>0<=143){H[f+16>>2]=k;d=(l<<9)+d|0;e=7;break sd}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}k=c?!h:h;break rd}h=H[g+4>>2];k=b>>>0>>0;g=H[(k?8:12)+g>>2];H[n>>2]=g;while(1){td:{if(e){break td}i=H[f+16>>2];b=i+1|0;l=I[i+1|0];if(I[i|0]!=255){H[f+16>>2]=b;e=8;d=(l<<8)+d|0;break td}if(l>>>0<=143){H[f+16>>2]=b;d=(l<<9)+d|0;e=7;break td}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;k=k?h:!h}c=H[g>>2];b=b-c|0;ud:{if(d>>>16>>>0>=c>>>0){d=d-(c<<16)|0;i=H[g+4>>2];if(b&32768){break ud}h=H[g+4>>2];c=b>>>0>>0;H[n>>2]=H[(c?12:8)+g>>2];while(1){vd:{if(e){break vd}i=H[f+16>>2];g=i+1|0;l=I[i+1|0];if(I[i|0]!=255){H[f+16>>2]=g;e=8;d=(l<<8)+d|0;break vd}if(l>>>0<=143){H[f+16>>2]=g;d=(l<<9)+d|0;e=7;break vd}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}i=c?!h:h;break ud}h=H[g+4>>2];i=g;g=b>>>0>>0;H[n>>2]=H[i+(g?8:12)>>2];while(1){wd:{if(e){break wd}i=H[f+16>>2];b=i+1|0;l=I[i+1|0];if(I[i|0]!=255){H[f+16>>2]=b;e=8;d=(l<<8)+d|0;break wd}if(l>>>0<=143){H[f+16>>2]=b;d=(l<<9)+d|0;e=7;break wd}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;i=g?h:!h}g=i;c=0;i=n;xd:{yd:{zd:{Ad:{Bd:{switch(g|k<<1){case 0:l=j-4|0;k=H[j+4>>2]>>>17&4|H[l>>2]>>>19&1;g=D+(I[k+24384|0]<<2)|0;i=H[g>>2];c=H[i>>2];b=b-c|0;Cd:{if(d>>>16>>>0>=c>>>0){d=d-(c<<16)|0;s=H[i+4>>2];if(b&32768){break Cd}h=H[i+4>>2];c=b>>>0>>0;H[g>>2]=H[i+(c?12:8)>>2];while(1){Dd:{if(e){break Dd}i=H[f+16>>2];g=i+1|0;p=I[i+1|0];if(I[i|0]!=255){H[f+16>>2]=g;e=8;d=(p<<8)+d|0;break Dd}if(p>>>0<=143){H[f+16>>2]=g;d=(p<<9)+d|0;e=7;break Dd}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}s=c?!h:h;break Cd}h=H[i+4>>2];s=g;g=b>>>0>>0;H[s>>2]=H[i+(g?8:12)>>2];while(1){Ed:{if(e){break Ed}i=H[f+16>>2];b=i+1|0;p=I[i+1|0];if(I[i|0]!=255){H[f+16>>2]=b;e=8;d=(p<<8)+d|0;break Ed}if(p>>>0<=143){H[f+16>>2]=b;d=(p<<9)+d|0;e=7;break Ed}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;s=g?h:!h}g=s;c=I[k+24640|0];H[o>>2]=(g|0)==(c|0)?M:v;H[l>>2]=H[l>>2]|32;H[j+4>>2]=H[j+4>>2]|8;k=(c^g)<<19;t=H[f+108>>2];g=D+(I[t+2|0]<<2)|0;i=H[g>>2];c=H[i>>2];b=b-c|0;Fd:{if(d>>>16>>>0>=c>>>0){d=d-(c<<16)|0;s=H[i+4>>2];if(b&32768){break Fd}h=H[i+4>>2];c=b>>>0>>0;H[g>>2]=H[i+(c?12:8)>>2];while(1){Gd:{if(e){break Gd}i=H[f+16>>2];g=i+1|0;l=I[i+1|0];if(I[i|0]!=255){H[f+16>>2]=g;e=8;d=(l<<8)+d|0;break Gd}if(l>>>0<=143){H[f+16>>2]=g;d=(l<<9)+d|0;e=7;break Gd}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}s=c?!h:h;break Fd}h=H[i+4>>2];s=g;g=b>>>0>>0;H[s>>2]=H[i+(g?8:12)>>2];while(1){Hd:{if(e){break Hd}i=H[f+16>>2];b=i+1|0;l=I[i+1|0];if(I[i|0]!=255){H[f+16>>2]=b;e=8;d=(l<<8)+d|0;break Hd}if(l>>>0<=143){H[f+16>>2]=b;d=(l<<9)+d|0;e=7;break Hd}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;s=g?h:!h}g=s;c=k|16;if(!g){break Ad}break;case 1:break Bd;case 2:break zd;case 3:break xd;default:break Qc}}l=j-4|0;i=H[j+4>>2]>>>20&4|(H[l>>2]>>>22&1|(c>>>15&16|(c>>>19&64|c>>>3&170)));k=D+(I[i+24384|0]<<2)|0;p=H[k>>2];g=H[p>>2];b=b-g|0;t=(W<<2)+o|0;Id:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;s=H[p+4>>2];if(b&32768){break Id}h=H[p+4>>2];g=b>>>0>>0;H[k>>2]=H[p+(g?12:8)>>2];while(1){Jd:{if(e){break Jd}e=H[f+16>>2];k=e+1|0;p=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=k;e=8;d=(p<<8)+d|0;break Jd}if(p>>>0<=143){H[f+16>>2]=k;d=(p<<9)+d|0;e=7;break Jd}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}s=g?!h:h;break Id}h=H[p+4>>2];s=k;k=b>>>0>>0;H[s>>2]=H[p+(k?8:12)>>2];while(1){Kd:{if(e){break Kd}e=H[f+16>>2];b=e+1|0;p=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=b;e=8;d=(p<<8)+d|0;break Kd}if(p>>>0<=143){H[f+16>>2]=b;d=(p<<9)+d|0;e=7;break Kd}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;s=k?h:!h}k=s;g=I[i+24640|0];H[t>>2]=(k|0)==(g|0)?M:v;H[l>>2]=H[l>>2]|256;H[j+4>>2]=H[j+4>>2]|64;t=H[f+108>>2];c=(g^k)<<22|c|128}k=D+(I[(c>>>6&495)+t|0]<<2)|0;i=H[k>>2];g=H[i>>2];b=b-g|0;Ld:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;s=H[i+4>>2];if(b&32768){break Ld}h=H[i+4>>2];g=b>>>0>>0;H[k>>2]=H[i+(g?12:8)>>2];while(1){Md:{if(e){break Md}i=H[f+16>>2];k=i+1|0;l=I[i+1|0];if(I[i|0]!=255){H[f+16>>2]=k;e=8;d=(l<<8)+d|0;break Md}if(l>>>0<=143){H[f+16>>2]=k;d=(l<<9)+d|0;e=7;break Md}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}s=g?!h:h;break Ld}h=H[i+4>>2];s=k;k=b>>>0>>0;H[s>>2]=H[i+(k?8:12)>>2];while(1){Nd:{if(e){break Nd}i=H[f+16>>2];b=i+1|0;l=I[i+1|0];if(I[i|0]!=255){H[f+16>>2]=b;e=8;d=(l<<8)+d|0;break Nd}if(l>>>0<=143){H[f+16>>2]=b;d=(l<<9)+d|0;e=7;break Nd}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;s=k?h:!h}if(!s){break yd}}l=j-4|0;i=H[j+4>>2]>>>23&4|(H[l>>2]>>>25&1|(c>>>18&16|(c>>>22&64|c>>>6&170)));k=D+(I[i+24384|0]<<2)|0;p=H[k>>2];g=H[p>>2];b=b-g|0;t=o+u|0;Od:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;s=H[p+4>>2];if(b&32768){break Od}h=H[p+4>>2];g=b>>>0>>0;H[k>>2]=H[p+(g?12:8)>>2];while(1){Pd:{if(e){break Pd}e=H[f+16>>2];k=e+1|0;p=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=k;e=8;d=(p<<8)+d|0;break Pd}if(p>>>0<=143){H[f+16>>2]=k;d=(p<<9)+d|0;e=7;break Pd}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}s=g?!h:h;break Od}h=H[p+4>>2];s=k;k=b>>>0>>0;H[s>>2]=H[p+(k?8:12)>>2];while(1){Qd:{if(e){break Qd}e=H[f+16>>2];b=e+1|0;p=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=b;e=8;d=(p<<8)+d|0;break Qd}if(p>>>0<=143){H[f+16>>2]=b;d=(p<<9)+d|0;e=7;break Qd}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;s=k?h:!h}k=s;g=I[i+24640|0];H[t>>2]=(k|0)==(g|0)?M:v;H[l>>2]=H[l>>2]|2048;H[j+4>>2]=H[j+4>>2]|512;c=(g^k)<<25|c|1024;t=H[f+108>>2]}i=D+(I[(c>>>9&495)+t|0]<<2)|0;l=H[i>>2];g=H[l>>2];b=b-g|0;Rd:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;k=H[l+4>>2];if(b&32768){break Rd}h=H[l+4>>2];g=b>>>0>>0;H[i>>2]=H[l+(g?12:8)>>2];while(1){Sd:{if(e){break Sd}e=H[f+16>>2];k=e+1|0;l=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=k;e=8;d=(l<<8)+d|0;break Sd}if(l>>>0<=143){H[f+16>>2]=k;d=(l<<9)+d|0;e=7;break Sd}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}k=g?!h:h;break Rd}h=H[l+4>>2];k=b>>>0>>0;H[i>>2]=H[l+(k?8:12)>>2];while(1){Td:{if(e){break Td}e=H[f+16>>2];b=e+1|0;l=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=b;e=8;d=(l<<8)+d|0;break Td}if(l>>>0<=143){H[f+16>>2]=b;d=(l<<9)+d|0;e=7;break Td}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;k=k?h:!h}if(!k){break Qc}}p=j-4|0;T=H[j+4>>2]>>>26&4|(H[p>>2]>>>28&1|(c>>>21&16|(c>>>25&64|c>>>9&170)));i=D+(I[T+24384|0]<<2)|0;t=H[i>>2];g=H[t>>2];b=b-g|0}s=o+r|0;Ud:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;k=H[t+4>>2];if(b&32768){break Ud}h=H[t+4>>2];g=b>>>0>>0;H[i>>2]=H[(g?12:8)+t>>2];while(1){Vd:{if(e){break Vd}e=H[f+16>>2];k=e+1|0;l=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=k;e=8;d=(l<<8)+d|0;break Vd}if(l>>>0<=143){H[f+16>>2]=k;d=(l<<9)+d|0;e=7;break Vd}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}k=g?!h:h;break Ud}h=H[t+4>>2];k=b>>>0>>0;H[i>>2]=H[(k?8:12)+t>>2];while(1){Wd:{if(e){break Wd}e=H[f+16>>2];b=e+1|0;l=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=b;e=8;d=(l<<8)+d|0;break Wd}if(l>>>0<=143){H[f+16>>2]=b;d=(l<<9)+d|0;e=7;break Wd}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;k=k?h:!h}g=I[T+24640|0];H[s>>2]=(k|0)==(g|0)?M:v;H[p>>2]=H[p>>2]|16384;H[j+4>>2]=H[j+4>>2]|4096;h=j+(H[f+124>>2]<<2)|0;H[h+4>>2]=H[h+4>>2]|4;H[h+12>>2]=H[h+12>>2]|1;g=g^k;H[h+8>>2]=H[h+8>>2]|g<<18|2;c=g<<28|c|8192}H[j>>2]=c&-1226833921}c=j+4|0;o=o+4|0;w=w+1|0;if((W|0)!=(w|0)){continue}break}c=j+12|0;o=o+r|0;x=x+4|0;g=H[f+128>>2];if(x>>>0<(g&-4)>>>0){continue}break}break Oc}x=g&-4;c=(k+(x<<1)|0)+12|0}H[f+8>>2]=e;H[f+4>>2]=b;H[f>>2]=d;H[f+104>>2]=i;if(!W|g>>>0<=x>>>0){break Db}while(1){e=0;if(H[f+128>>2]!=(x|0)){while(1){Wb(f,c,(N(e,W)<<2)+o|0,M,e,1);e=e+1|0;if(e>>>0>2]-x>>>0){continue}break}}H[c>>2]=H[c>>2]&-1226833921;o=o+4|0;c=c+4|0;m=m+1|0;if((W|0)!=(m|0)){continue}break}break Db}Xd:{if(g>>>0<4){break Xd}if(W){n=f+100|0;q=f+96|0;r=N(W,12);u=W<<3;v=0-M|0;D=f+28|0;while(1){w=0;while(1){j=c;c=H[c>>2];Yd:{Zd:{_d:{if(c){$d:{if(c&2097168){break $d}i=D+(I[H[f+108>>2]+(c&495)|0]<<2)|0;l=H[i>>2];g=H[l>>2];b=b-g|0;ae:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;k=H[l+4>>2];if(b&32768){break ae}h=H[l+4>>2];g=b>>>0>>0;H[i>>2]=H[l+(g?12:8)>>2];while(1){be:{if(e){break be}e=H[f+16>>2];k=e+1|0;l=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=k;e=8;d=(l<<8)+d|0;break be}if(l>>>0<=143){H[f+16>>2]=k;d=(l<<9)+d|0;e=7;break be}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}k=g?!h:h;break ae}h=H[l+4>>2];k=b>>>0>>0;H[i>>2]=H[l+(k?8:12)>>2];while(1){ce:{if(e){break ce}e=H[f+16>>2];b=e+1|0;l=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=b;e=8;d=(l<<8)+d|0;break ce}if(l>>>0<=143){H[f+16>>2]=b;d=(l<<9)+d|0;e=7;break ce}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;k=k?h:!h}if(!k){break $d}p=j-4|0;h=H[j+4>>2]>>>17&4|(H[p>>2]>>>19&1|(c>>>14&16|(c>>>16&64|c&170)));i=D+(I[h+24384|0]<<2)|0;s=H[i>>2];g=H[s>>2];b=b-g|0;de:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;k=H[s+4>>2];if(b&32768){break de}l=H[s+4>>2];g=b>>>0>>0;H[i>>2]=H[s+(g?12:8)>>2];while(1){ee:{if(e){break ee}e=H[f+16>>2];k=e+1|0;s=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=k;e=8;d=(s<<8)+d|0;break ee}if(s>>>0<=143){H[f+16>>2]=k;d=(s<<9)+d|0;e=7;break ee}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}k=g?!l:l;break de}l=H[s+4>>2];k=b>>>0>>0;H[i>>2]=H[s+(k?8:12)>>2];while(1){fe:{if(e){break fe}e=H[f+16>>2];b=e+1|0;s=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=b;e=8;d=(s<<8)+d|0;break fe}if(s>>>0<=143){H[f+16>>2]=b;d=(s<<9)+d|0;e=7;break fe}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;k=k?l:!l}g=I[h+24640|0];H[o>>2]=(k|0)==(g|0)?M:v;H[p>>2]=H[p>>2]|32;H[j+4>>2]=H[j+4>>2]|8;h=j+(-2-H[f+124>>2]<<2)|0;H[h+4>>2]=H[h+4>>2]|32768;k=g^k;H[h>>2]=H[h>>2]|k<<31|65536;g=h-4|0;H[g>>2]=H[g>>2]|131072;c=k<<19|c|16}ge:{if(c&16777344){break ge}h=c>>>3|0;i=D+(I[H[f+108>>2]+(h&495)|0]<<2)|0;p=H[i>>2];g=H[p>>2];b=b-g|0;he:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;k=H[p+4>>2];if(b&32768){break he}l=H[p+4>>2];g=b>>>0>>0;H[i>>2]=H[p+(g?12:8)>>2];while(1){ie:{if(e){break ie}e=H[f+16>>2];k=e+1|0;p=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=k;e=8;d=(p<<8)+d|0;break ie}if(p>>>0<=143){H[f+16>>2]=k;d=(p<<9)+d|0;e=7;break ie}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}k=g?!l:l;break he}l=H[p+4>>2];k=b>>>0>>0;H[i>>2]=H[p+(k?8:12)>>2];while(1){je:{if(e){break je}e=H[f+16>>2];b=e+1|0;p=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=b;e=8;d=(p<<8)+d|0;break je}if(p>>>0<=143){H[f+16>>2]=b;d=(p<<9)+d|0;e=7;break je}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;k=k?l:!l}if(!k){break ge}p=j-4|0;h=H[j+4>>2]>>>20&4|(H[p>>2]>>>22&1|(c>>>15&16|(c>>>19&64|h&170)));i=D+(I[h+24384|0]<<2)|0;s=H[i>>2];g=H[s>>2];b=b-g|0;t=(W<<2)+o|0;ke:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;k=H[s+4>>2];if(b&32768){break ke}l=H[s+4>>2];g=b>>>0>>0;H[i>>2]=H[s+(g?12:8)>>2];while(1){le:{if(e){break le}e=H[f+16>>2];k=e+1|0;s=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=k;e=8;d=(s<<8)+d|0;break le}if(s>>>0<=143){H[f+16>>2]=k;d=(s<<9)+d|0;e=7;break le}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}k=g?!l:l;break ke}l=H[s+4>>2];k=b>>>0>>0;H[i>>2]=H[s+(k?8:12)>>2];while(1){me:{if(e){break me}e=H[f+16>>2];b=e+1|0;s=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=b;e=8;d=(s<<8)+d|0;break me}if(s>>>0<=143){H[f+16>>2]=b;d=(s<<9)+d|0;e=7;break me}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;k=k?l:!l}g=I[h+24640|0];H[t>>2]=(k|0)==(g|0)?M:v;H[p>>2]=H[p>>2]|256;H[j+4>>2]=H[j+4>>2]|64;c=(g^k)<<22|c|128}ne:{if(c&134218752){break ne}h=c>>>6|0;i=D+(I[H[f+108>>2]+(h&495)|0]<<2)|0;p=H[i>>2];g=H[p>>2];b=b-g|0;oe:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;k=H[p+4>>2];if(b&32768){break oe}l=H[p+4>>2];g=b>>>0>>0;H[i>>2]=H[p+(g?12:8)>>2];while(1){pe:{if(e){break pe}e=H[f+16>>2];k=e+1|0;p=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=k;e=8;d=(p<<8)+d|0;break pe}if(p>>>0<=143){H[f+16>>2]=k;d=(p<<9)+d|0;e=7;break pe}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}k=g?!l:l;break oe}l=H[p+4>>2];k=b>>>0>>0;H[i>>2]=H[p+(k?8:12)>>2];while(1){qe:{if(e){break qe}e=H[f+16>>2];b=e+1|0;p=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=b;e=8;d=(p<<8)+d|0;break qe}if(p>>>0<=143){H[f+16>>2]=b;d=(p<<9)+d|0;e=7;break qe}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;k=k?l:!l}if(!k){break ne}p=j-4|0;h=H[j+4>>2]>>>23&4|(H[p>>2]>>>25&1|(c>>>18&16|(c>>>22&64|h&170)));i=D+(I[h+24384|0]<<2)|0;s=H[i>>2];g=H[s>>2];b=b-g|0;t=o+u|0;re:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;k=H[s+4>>2];if(b&32768){break re}l=H[s+4>>2];g=b>>>0>>0;H[i>>2]=H[s+(g?12:8)>>2];while(1){se:{if(e){break se}e=H[f+16>>2];k=e+1|0;s=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=k;e=8;d=(s<<8)+d|0;break se}if(s>>>0<=143){H[f+16>>2]=k;d=(s<<9)+d|0;e=7;break se}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}k=g?!l:l;break re}l=H[s+4>>2];k=b>>>0>>0;H[i>>2]=H[s+(k?8:12)>>2];while(1){te:{if(e){break te}e=H[f+16>>2];b=e+1|0;s=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=b;e=8;d=(s<<8)+d|0;break te}if(s>>>0<=143){H[f+16>>2]=b;d=(s<<9)+d|0;e=7;break te}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;k=k?l:!l}g=I[h+24640|0];H[t>>2]=(k|0)==(g|0)?M:v;H[p>>2]=H[p>>2]|2048;H[j+4>>2]=H[j+4>>2]|512;c=(g^k)<<25|c|1024}if(c&1073750016){break Zd}h=c>>>9|0;i=D+(I[H[f+108>>2]+(h&495)|0]<<2)|0;p=H[i>>2];g=H[p>>2];b=b-g|0;ue:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;k=H[p+4>>2];if(b&32768){break ue}l=H[p+4>>2];g=b>>>0>>0;H[i>>2]=H[p+(g?12:8)>>2];while(1){ve:{if(e){break ve}e=H[f+16>>2];k=e+1|0;p=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=k;e=8;d=(p<<8)+d|0;break ve}if(p>>>0<=143){H[f+16>>2]=k;d=(p<<9)+d|0;e=7;break ve}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}k=g?!l:l;break ue}l=H[p+4>>2];k=b>>>0>>0;H[i>>2]=H[p+(k?8:12)>>2];while(1){we:{if(e){break we}e=H[f+16>>2];b=e+1|0;p=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=b;e=8;d=(p<<8)+d|0;break we}if(p>>>0<=143){H[f+16>>2]=b;d=(p<<9)+d|0;e=7;break we}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;k=k?l:!l}if(!k){break Zd}p=j-4|0;T=H[j+4>>2]>>>26&4|(H[p>>2]>>>28&1|(c>>>21&16|(c>>>25&64|h&170)));i=D+(I[T+24384|0]<<2)|0;t=H[i>>2];g=H[t>>2];b=b-g|0;break _d}k=H[q>>2];c=H[k>>2];b=b-c|0;xe:{if(d>>>16>>>0>=c>>>0){d=d-(c<<16)|0;g=H[k+4>>2];if(b&32768){break xe}i=H[k+4>>2];c=b>>>0>>0;H[q>>2]=H[k+(c?12:8)>>2];while(1){ye:{if(e){break ye}k=H[f+16>>2];g=k+1|0;h=I[k+1|0];if(I[k|0]!=255){H[f+16>>2]=g;e=8;d=(h<<8)+d|0;break ye}if(h>>>0<=143){H[f+16>>2]=g;d=(h<<9)+d|0;e=7;break ye}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}g=c?!i:i;break xe}i=H[k+4>>2];g=b>>>0>>0;H[q>>2]=H[k+(g?8:12)>>2];while(1){ze:{if(e){break ze}k=H[f+16>>2];b=k+1|0;h=I[k+1|0];if(I[k|0]!=255){H[f+16>>2]=b;e=8;d=(h<<8)+d|0;break ze}if(h>>>0<=143){H[f+16>>2]=b;d=(h<<9)+d|0;e=7;break ze}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;g=g?i:!i}if(!g){i=q;break Yd}g=H[n>>2];c=H[g>>2];b=b-c|0;Ae:{if(d>>>16>>>0>=c>>>0){d=d-(c<<16)|0;k=H[g+4>>2];if(b&32768){break Ae}h=H[g+4>>2];c=b>>>0>>0;g=H[(c?12:8)+g>>2];H[n>>2]=g;while(1){Be:{if(e){break Be}i=H[f+16>>2];k=i+1|0;l=I[i+1|0];if(I[i|0]!=255){H[f+16>>2]=k;e=8;d=(l<<8)+d|0;break Be}if(l>>>0<=143){H[f+16>>2]=k;d=(l<<9)+d|0;e=7;break Be}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}k=c?!h:h;break Ae}h=H[g+4>>2];k=b>>>0>>0;g=H[(k?8:12)+g>>2];H[n>>2]=g;while(1){Ce:{if(e){break Ce}i=H[f+16>>2];b=i+1|0;l=I[i+1|0];if(I[i|0]!=255){H[f+16>>2]=b;e=8;d=(l<<8)+d|0;break Ce}if(l>>>0<=143){H[f+16>>2]=b;d=(l<<9)+d|0;e=7;break Ce}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;k=k?h:!h}c=H[g>>2];b=b-c|0;De:{if(d>>>16>>>0>=c>>>0){d=d-(c<<16)|0;i=H[g+4>>2];if(b&32768){break De}h=H[g+4>>2];c=b>>>0>>0;H[n>>2]=H[(c?12:8)+g>>2];while(1){Ee:{if(e){break Ee}i=H[f+16>>2];g=i+1|0;l=I[i+1|0];if(I[i|0]!=255){H[f+16>>2]=g;e=8;d=(l<<8)+d|0;break Ee}if(l>>>0<=143){H[f+16>>2]=g;d=(l<<9)+d|0;e=7;break Ee}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}i=c?!h:h;break De}h=H[g+4>>2];i=g;g=b>>>0>>0;H[n>>2]=H[i+(g?8:12)>>2];while(1){Fe:{if(e){break Fe}i=H[f+16>>2];b=i+1|0;l=I[i+1|0];if(I[i|0]!=255){H[f+16>>2]=b;e=8;d=(l<<8)+d|0;break Fe}if(l>>>0<=143){H[f+16>>2]=b;d=(l<<9)+d|0;e=7;break Fe}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;i=g?h:!h}g=i;c=0;i=n;Ge:{He:{Ie:{Je:{Ke:{switch(g|k<<1){case 0:l=j-4|0;k=H[j+4>>2]>>>17&4|H[l>>2]>>>19&1;g=D+(I[k+24384|0]<<2)|0;i=H[g>>2];c=H[i>>2];b=b-c|0;Le:{if(d>>>16>>>0>=c>>>0){d=d-(c<<16)|0;s=H[i+4>>2];if(b&32768){break Le}h=H[i+4>>2];c=b>>>0>>0;H[g>>2]=H[i+(c?12:8)>>2];while(1){Me:{if(e){break Me}i=H[f+16>>2];g=i+1|0;p=I[i+1|0];if(I[i|0]!=255){H[f+16>>2]=g;e=8;d=(p<<8)+d|0;break Me}if(p>>>0<=143){H[f+16>>2]=g;d=(p<<9)+d|0;e=7;break Me}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}s=c?!h:h;break Le}h=H[i+4>>2];s=g;g=b>>>0>>0;H[s>>2]=H[i+(g?8:12)>>2];while(1){Ne:{if(e){break Ne}i=H[f+16>>2];b=i+1|0;p=I[i+1|0];if(I[i|0]!=255){H[f+16>>2]=b;e=8;d=(p<<8)+d|0;break Ne}if(p>>>0<=143){H[f+16>>2]=b;d=(p<<9)+d|0;e=7;break Ne}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;s=g?h:!h}g=s;c=I[k+24640|0];H[o>>2]=(g|0)==(c|0)?M:v;H[l>>2]=H[l>>2]|32;H[j+4>>2]=H[j+4>>2]|8;k=j+(-2-H[f+124>>2]<<2)|0;H[k+4>>2]=H[k+4>>2]|32768;g=c^g;H[k>>2]=H[k>>2]|g<<31|65536;c=k-4|0;H[c>>2]=H[c>>2]|131072;k=g<<19;t=H[f+108>>2];g=D+(I[t+2|0]<<2)|0;i=H[g>>2];c=H[i>>2];b=b-c|0;Oe:{if(d>>>16>>>0>=c>>>0){d=d-(c<<16)|0;s=H[i+4>>2];if(b&32768){break Oe}h=H[i+4>>2];c=b>>>0>>0;H[g>>2]=H[i+(c?12:8)>>2];while(1){Pe:{if(e){break Pe}i=H[f+16>>2];g=i+1|0;l=I[i+1|0];if(I[i|0]!=255){H[f+16>>2]=g;e=8;d=(l<<8)+d|0;break Pe}if(l>>>0<=143){H[f+16>>2]=g;d=(l<<9)+d|0;e=7;break Pe}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}s=c?!h:h;break Oe}h=H[i+4>>2];s=g;g=b>>>0>>0;H[s>>2]=H[i+(g?8:12)>>2];while(1){Qe:{if(e){break Qe}i=H[f+16>>2];b=i+1|0;l=I[i+1|0];if(I[i|0]!=255){H[f+16>>2]=b;e=8;d=(l<<8)+d|0;break Qe}if(l>>>0<=143){H[f+16>>2]=b;d=(l<<9)+d|0;e=7;break Qe}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;s=g?h:!h}g=s;c=k|16;if(!g){break Je}break;case 1:break Ke;case 2:break Ie;case 3:break Ge;default:break Zd}}l=j-4|0;i=H[j+4>>2]>>>20&4|(H[l>>2]>>>22&1|(c>>>15&16|(c>>>19&64|c>>>3&170)));k=D+(I[i+24384|0]<<2)|0;p=H[k>>2];g=H[p>>2];b=b-g|0;t=(W<<2)+o|0;Re:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;s=H[p+4>>2];if(b&32768){break Re}h=H[p+4>>2];g=b>>>0>>0;H[k>>2]=H[p+(g?12:8)>>2];while(1){Se:{if(e){break Se}e=H[f+16>>2];k=e+1|0;p=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=k;e=8;d=(p<<8)+d|0;break Se}if(p>>>0<=143){H[f+16>>2]=k;d=(p<<9)+d|0;e=7;break Se}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}s=g?!h:h;break Re}h=H[p+4>>2];s=k;k=b>>>0>>0;H[s>>2]=H[p+(k?8:12)>>2];while(1){Te:{if(e){break Te}e=H[f+16>>2];b=e+1|0;p=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=b;e=8;d=(p<<8)+d|0;break Te}if(p>>>0<=143){H[f+16>>2]=b;d=(p<<9)+d|0;e=7;break Te}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;s=k?h:!h}k=s;g=I[i+24640|0];H[t>>2]=(k|0)==(g|0)?M:v;H[l>>2]=H[l>>2]|256;H[j+4>>2]=H[j+4>>2]|64;t=H[f+108>>2];c=(g^k)<<22|c|128}k=D+(I[(c>>>6&495)+t|0]<<2)|0;i=H[k>>2];g=H[i>>2];b=b-g|0;Ue:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;s=H[i+4>>2];if(b&32768){break Ue}h=H[i+4>>2];g=b>>>0>>0;H[k>>2]=H[i+(g?12:8)>>2];while(1){Ve:{if(e){break Ve}i=H[f+16>>2];k=i+1|0;l=I[i+1|0];if(I[i|0]!=255){H[f+16>>2]=k;e=8;d=(l<<8)+d|0;break Ve}if(l>>>0<=143){H[f+16>>2]=k;d=(l<<9)+d|0;e=7;break Ve}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}s=g?!h:h;break Ue}h=H[i+4>>2];s=k;k=b>>>0>>0;H[s>>2]=H[i+(k?8:12)>>2];while(1){We:{if(e){break We}i=H[f+16>>2];b=i+1|0;l=I[i+1|0];if(I[i|0]!=255){H[f+16>>2]=b;e=8;d=(l<<8)+d|0;break We}if(l>>>0<=143){H[f+16>>2]=b;d=(l<<9)+d|0;e=7;break We}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;s=k?h:!h}if(!s){break He}}l=j-4|0;i=H[j+4>>2]>>>23&4|(H[l>>2]>>>25&1|(c>>>18&16|(c>>>22&64|c>>>6&170)));k=D+(I[i+24384|0]<<2)|0;p=H[k>>2];g=H[p>>2];b=b-g|0;t=o+u|0;Xe:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;s=H[p+4>>2];if(b&32768){break Xe}h=H[p+4>>2];g=b>>>0>>0;H[k>>2]=H[p+(g?12:8)>>2];while(1){Ye:{if(e){break Ye}e=H[f+16>>2];k=e+1|0;p=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=k;e=8;d=(p<<8)+d|0;break Ye}if(p>>>0<=143){H[f+16>>2]=k;d=(p<<9)+d|0;e=7;break Ye}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}s=g?!h:h;break Xe}h=H[p+4>>2];s=k;k=b>>>0>>0;H[s>>2]=H[p+(k?8:12)>>2];while(1){Ze:{if(e){break Ze}e=H[f+16>>2];b=e+1|0;p=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=b;e=8;d=(p<<8)+d|0;break Ze}if(p>>>0<=143){H[f+16>>2]=b;d=(p<<9)+d|0;e=7;break Ze}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;s=k?h:!h}k=s;g=I[i+24640|0];H[t>>2]=(k|0)==(g|0)?M:v;H[l>>2]=H[l>>2]|2048;H[j+4>>2]=H[j+4>>2]|512;c=(g^k)<<25|c|1024;t=H[f+108>>2]}i=D+(I[(c>>>9&495)+t|0]<<2)|0;l=H[i>>2];g=H[l>>2];b=b-g|0;_e:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;k=H[l+4>>2];if(b&32768){break _e}h=H[l+4>>2];g=b>>>0>>0;H[i>>2]=H[l+(g?12:8)>>2];while(1){$e:{if(e){break $e}e=H[f+16>>2];k=e+1|0;l=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=k;e=8;d=(l<<8)+d|0;break $e}if(l>>>0<=143){H[f+16>>2]=k;d=(l<<9)+d|0;e=7;break $e}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}k=g?!h:h;break _e}h=H[l+4>>2];k=b>>>0>>0;H[i>>2]=H[l+(k?8:12)>>2];while(1){af:{if(e){break af}e=H[f+16>>2];b=e+1|0;l=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=b;e=8;d=(l<<8)+d|0;break af}if(l>>>0<=143){H[f+16>>2]=b;d=(l<<9)+d|0;e=7;break af}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;k=k?h:!h}if(!k){break Zd}}p=j-4|0;T=H[j+4>>2]>>>26&4|(H[p>>2]>>>28&1|(c>>>21&16|(c>>>25&64|c>>>9&170)));i=D+(I[T+24384|0]<<2)|0;t=H[i>>2];g=H[t>>2];b=b-g|0}s=o+r|0;bf:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;k=H[t+4>>2];if(b&32768){break bf}h=H[t+4>>2];g=b>>>0>>0;H[i>>2]=H[(g?12:8)+t>>2];while(1){cf:{if(e){break cf}e=H[f+16>>2];k=e+1|0;l=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=k;e=8;d=(l<<8)+d|0;break cf}if(l>>>0<=143){H[f+16>>2]=k;d=(l<<9)+d|0;e=7;break cf}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}k=g?!h:h;break bf}h=H[t+4>>2];k=b>>>0>>0;H[i>>2]=H[(k?8:12)+t>>2];while(1){df:{if(e){break df}e=H[f+16>>2];b=e+1|0;l=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=b;e=8;d=(l<<8)+d|0;break df}if(l>>>0<=143){H[f+16>>2]=b;d=(l<<9)+d|0;e=7;break df}H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8}e=e-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;k=k?h:!h}g=I[T+24640|0];H[s>>2]=(k|0)==(g|0)?M:v;H[p>>2]=H[p>>2]|16384;H[j+4>>2]=H[j+4>>2]|4096;h=j+(H[f+124>>2]<<2)|0;H[h+4>>2]=H[h+4>>2]|4;H[h+12>>2]=H[h+12>>2]|1;g=g^k;H[h+8>>2]=H[h+8>>2]|g<<18|2;c=g<<28|c|8192}H[j>>2]=c&-1226833921}c=j+4|0;o=o+4|0;w=w+1|0;if((W|0)!=(w|0)){continue}break}c=j+12|0;o=o+r|0;x=x+4|0;g=H[f+128>>2];if(x>>>0<(g&-4)>>>0){continue}break}break Xd}x=g&-4;c=(k+(x<<1)|0)+12|0}H[f+8>>2]=e;H[f+4>>2]=b;H[f>>2]=d;H[f+104>>2]=i;if(!W|g>>>0<=x>>>0){break Db}while(1){e=0;if(H[f+128>>2]!=(x|0)){while(1){Wb(f,c,(N(e,W)<<2)+o|0,M,e,0);e=e+1|0;if(e>>>0>2]-x>>>0){continue}break}}H[c>>2]=H[c>>2]&-1226833921;o=o+4|0;c=c+4|0;m=m+1|0;if((W|0)!=(m|0)){continue}break}break Db}while(1){r=0;while(1){o=c;j=g;g=H[g>>2];ef:{ff:{gf:{if(!g){i=H[k>>2];g=H[i>>2];b=b-g|0;hf:{if(d>>>16>>>0>>0){n=H[i+4>>2];c=b>>>0>>0;H[k>>2]=H[i+(c?8:12)>>2];while(1){jf:{if(e){break jf}i=H[f+16>>2];b=i+1|0;h=I[i+1|0];if(I[i|0]==255){if(h>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break jf}H[f+16>>2]=b;d=(h<<9)+d|0;e=7;break jf}H[f+16>>2]=b;e=8;d=(h<<8)+d|0}e=e-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;c=c?n:!n;break hf}d=d-(g<<16)|0;if(!(b&32768)){n=H[i+4>>2];c=b>>>0>>0;H[k>>2]=H[i+(c?12:8)>>2];while(1){kf:{if(e){break kf}i=H[f+16>>2];g=i+1|0;h=I[i+1|0];if(I[i|0]==255){if(h>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break kf}H[f+16>>2]=g;d=(h<<9)+d|0;e=7;break kf}H[f+16>>2]=g;e=8;d=(h<<8)+d|0}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}c=c?!n:n;break hf}c=H[i+4>>2]}if(!c){i=k;break ef}c=H[q>>2];g=H[c>>2];b=b-g|0;lf:{if(d>>>16>>>0>>0){h=H[c+4>>2];i=b>>>0>>0;c=H[(i?8:12)+c>>2];H[q>>2]=c;while(1){mf:{if(e){break mf}n=H[f+16>>2];b=n+1|0;m=I[n+1|0];if(I[n|0]==255){if(m>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break mf}H[f+16>>2]=b;d=(m<<9)+d|0;e=7;break mf}H[f+16>>2]=b;e=8;d=(m<<8)+d|0}e=e-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;n=i?h:!h;break lf}d=d-(g<<16)|0;if(!(b&32768)){h=H[c+4>>2];g=b>>>0>>0;c=H[(g?12:8)+c>>2];H[q>>2]=c;while(1){nf:{if(e){break nf}n=H[f+16>>2];i=n+1|0;m=I[n+1|0];if(I[n|0]==255){if(m>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break nf}H[f+16>>2]=i;d=(m<<9)+d|0;e=7;break nf}H[f+16>>2]=i;e=8;d=(m<<8)+d|0}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}n=g?!h:h;break lf}n=H[c+4>>2]}g=H[c>>2];b=b-g|0;of:{if(d>>>16>>>0>>0){h=H[c+4>>2];i=c;c=b>>>0>>0;H[q>>2]=H[i+(c?8:12)>>2];while(1){pf:{if(e){break pf}i=H[f+16>>2];b=i+1|0;m=I[i+1|0];if(I[i|0]==255){if(m>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break pf}H[f+16>>2]=b;d=(m<<9)+d|0;e=7;break pf}H[f+16>>2]=b;e=8;d=(m<<8)+d|0}e=e-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;c=c?h:!h;break of}d=d-(g<<16)|0;if(!(b&32768)){h=H[c+4>>2];i=c;c=b>>>0>>0;H[q>>2]=H[i+(c?12:8)>>2];while(1){qf:{if(e){break qf}i=H[f+16>>2];g=i+1|0;m=I[i+1|0];if(I[i|0]==255){if(m>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break qf}H[f+16>>2]=g;d=(m<<9)+d|0;e=7;break qf}H[f+16>>2]=g;e=8;d=(m<<8)+d|0}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}c=c?!h:h;break of}c=H[c+4>>2]}g=0;i=q;rf:{sf:{tf:{uf:{vf:{switch(c|n<<1){case 0:m=j-4|0;i=H[j+4>>2]>>>17&4|H[m>>2]>>>19&1;c=w+(I[i+24384|0]<<2)|0;n=H[c>>2];g=H[n>>2];b=b-g|0;wf:{if(d>>>16>>>0>>0){h=H[n+4>>2];v=c;c=b>>>0>>0;H[v>>2]=H[n+(c?8:12)>>2];while(1){xf:{if(e){break xf}n=H[f+16>>2];b=n+1|0;l=I[n+1|0];if(I[n|0]==255){if(l>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break xf}H[f+16>>2]=b;d=(l<<9)+d|0;e=7;break xf}H[f+16>>2]=b;e=8;d=(l<<8)+d|0}e=e-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;g=c?h:!h;break wf}d=d-(g<<16)|0;if(!(b&32768)){h=H[n+4>>2];v=c;c=b>>>0>>0;H[v>>2]=H[n+(c?12:8)>>2];while(1){yf:{if(e){break yf}n=H[f+16>>2];g=n+1|0;l=I[n+1|0];if(I[n|0]==255){if(l>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break yf}H[f+16>>2]=g;d=(l<<9)+d|0;e=7;break yf}H[f+16>>2]=g;e=8;d=(l<<8)+d|0}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}g=c?!h:h;break wf}g=H[n+4>>2]}c=I[i+24640|0];H[o>>2]=(g|0)==(c|0)?s:u;H[m>>2]=H[m>>2]|32;H[j+4>>2]=H[j+4>>2]|8;i=(c^g)<<19;t=H[f+108>>2];c=w+(I[t+2|0]<<2)|0;n=H[c>>2];g=H[n>>2];b=b-g|0;zf:{if(d>>>16>>>0>>0){h=H[n+4>>2];v=c;c=b>>>0>>0;H[v>>2]=H[n+(c?8:12)>>2];while(1){Af:{if(e){break Af}n=H[f+16>>2];b=n+1|0;m=I[n+1|0];if(I[n|0]==255){if(m>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break Af}H[f+16>>2]=b;d=(m<<9)+d|0;e=7;break Af}H[f+16>>2]=b;e=8;d=(m<<8)+d|0}e=e-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;c=c?h:!h;break zf}d=d-(g<<16)|0;if(!(b&32768)){h=H[n+4>>2];v=c;c=b>>>0>>0;H[v>>2]=H[n+(c?12:8)>>2];while(1){Bf:{if(e){break Bf}n=H[f+16>>2];g=n+1|0;m=I[n+1|0];if(I[n|0]==255){if(m>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break Bf}H[f+16>>2]=g;d=(m<<9)+d|0;e=7;break Bf}H[f+16>>2]=g;e=8;d=(m<<8)+d|0}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}c=c?!h:h;break zf}c=H[n+4>>2]}g=i|16;if(!c){break uf}break;case 1:break vf;case 2:break tf;case 3:break rf;default:break ff}}m=j-4|0;n=H[j+4>>2]>>>20&4|(H[m>>2]>>>22&1|(g>>>15&16|(g>>>19&64|g>>>3&170)));i=w+(I[n+24384|0]<<2)|0;l=H[i>>2];c=H[l>>2];b=b-c|0;Cf:{if(d>>>16>>>0>>0){h=H[l+4>>2];v=i;i=b>>>0>>0;H[v>>2]=H[l+(i?8:12)>>2];while(1){Df:{if(e){break Df}e=H[f+16>>2];b=e+1|0;l=I[e+1|0];if(I[e|0]==255){if(l>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break Df}H[f+16>>2]=b;d=(l<<9)+d|0;e=7;break Df}H[f+16>>2]=b;e=8;d=(l<<8)+d|0}e=e-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;i=i?h:!h;break Cf}d=d-(c<<16)|0;if(!(b&32768)){h=H[l+4>>2];c=b>>>0>>0;H[i>>2]=H[l+(c?12:8)>>2];while(1){Ef:{if(e){break Ef}e=H[f+16>>2];i=e+1|0;l=I[e+1|0];if(I[e|0]==255){if(l>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break Ef}H[f+16>>2]=i;d=(l<<9)+d|0;e=7;break Ef}H[f+16>>2]=i;e=8;d=(l<<8)+d|0}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}i=c?!h:h;break Cf}i=H[l+4>>2]}c=I[n+24640|0];H[o+256>>2]=(i|0)==(c|0)?s:u;H[m>>2]=H[m>>2]|256;H[j+4>>2]=H[j+4>>2]|64;t=H[f+108>>2];g=(c^i)<<22|g|128}i=w+(I[(g>>>6&495)+t|0]<<2)|0;n=H[i>>2];c=H[n>>2];b=b-c|0;Ff:{if(d>>>16>>>0>>0){h=H[n+4>>2];v=i;i=b>>>0>>0;H[v>>2]=H[n+(i?8:12)>>2];while(1){Gf:{if(e){break Gf}n=H[f+16>>2];b=n+1|0;m=I[n+1|0];if(I[n|0]==255){if(m>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break Gf}H[f+16>>2]=b;d=(m<<9)+d|0;e=7;break Gf}H[f+16>>2]=b;e=8;d=(m<<8)+d|0}e=e-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;c=i?h:!h;break Ff}d=d-(c<<16)|0;if(!(b&32768)){h=H[n+4>>2];c=b>>>0>>0;H[i>>2]=H[n+(c?12:8)>>2];while(1){Hf:{if(e){break Hf}n=H[f+16>>2];i=n+1|0;m=I[n+1|0];if(I[n|0]==255){if(m>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break Hf}H[f+16>>2]=i;d=(m<<9)+d|0;e=7;break Hf}H[f+16>>2]=i;e=8;d=(m<<8)+d|0}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}c=c?!h:h;break Ff}c=H[n+4>>2]}if(!c){break sf}}m=j-4|0;n=H[j+4>>2]>>>23&4|(H[m>>2]>>>25&1|(g>>>18&16|(g>>>22&64|g>>>6&170)));i=w+(I[n+24384|0]<<2)|0;l=H[i>>2];c=H[l>>2];b=b-c|0;If:{if(d>>>16>>>0>>0){h=H[l+4>>2];v=i;i=b>>>0>>0;H[v>>2]=H[l+(i?8:12)>>2];while(1){Jf:{if(e){break Jf}e=H[f+16>>2];b=e+1|0;l=I[e+1|0];if(I[e|0]==255){if(l>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break Jf}H[f+16>>2]=b;d=(l<<9)+d|0;e=7;break Jf}H[f+16>>2]=b;e=8;d=(l<<8)+d|0}e=e-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;i=i?h:!h;break If}d=d-(c<<16)|0;if(!(b&32768)){h=H[l+4>>2];c=b>>>0>>0;H[i>>2]=H[l+(c?12:8)>>2];while(1){Kf:{if(e){break Kf}e=H[f+16>>2];i=e+1|0;l=I[e+1|0];if(I[e|0]==255){if(l>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break Kf}H[f+16>>2]=i;d=(l<<9)+d|0;e=7;break Kf}H[f+16>>2]=i;e=8;d=(l<<8)+d|0}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}i=c?!h:h;break If}i=H[l+4>>2]}c=I[n+24640|0];H[o+512>>2]=(i|0)==(c|0)?s:u;H[m>>2]=H[m>>2]|2048;H[j+4>>2]=H[j+4>>2]|512;g=(c^i)<<25|g|1024;t=H[f+108>>2]}i=w+(I[(g>>>9&495)+t|0]<<2)|0;m=H[i>>2];c=H[m>>2];b=b-c|0;Lf:{if(d>>>16>>>0>>0){h=H[m+4>>2];n=b>>>0>>0;H[i>>2]=H[m+(n?8:12)>>2];while(1){Mf:{if(e){break Mf}e=H[f+16>>2];b=e+1|0;m=I[e+1|0];if(I[e|0]==255){if(m>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break Mf}H[f+16>>2]=b;d=(m<<9)+d|0;e=7;break Mf}H[f+16>>2]=b;e=8;d=(m<<8)+d|0}e=e-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;c=n?h:!h;break Lf}d=d-(c<<16)|0;if(!(b&32768)){h=H[m+4>>2];c=b>>>0>>0;H[i>>2]=H[m+(c?12:8)>>2];while(1){Nf:{if(e){break Nf}e=H[f+16>>2];n=e+1|0;m=I[e+1|0];if(I[e|0]==255){if(m>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break Nf}H[f+16>>2]=n;d=(m<<9)+d|0;e=7;break Nf}H[f+16>>2]=n;e=8;d=(m<<8)+d|0}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}c=c?!h:h;break Lf}c=H[m+4>>2]}if(!c){break ff}}m=j-4|0;p=H[j+4>>2]>>>26&4|(H[m>>2]>>>28&1|(g>>>21&16|(g>>>25&64|g>>>9&170)));i=w+(I[p+24384|0]<<2)|0;t=H[i>>2];c=H[t>>2];b=b-c|0;break gf}Of:{if(g&2097168){break Of}i=w+(I[H[f+108>>2]+(g&495)|0]<<2)|0;m=H[i>>2];c=H[m>>2];b=b-c|0;Pf:{if(d>>>16>>>0>>0){h=H[m+4>>2];n=b>>>0>>0;H[i>>2]=H[m+(n?8:12)>>2];while(1){Qf:{if(e){break Qf}e=H[f+16>>2];b=e+1|0;m=I[e+1|0];if(I[e|0]==255){if(m>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break Qf}H[f+16>>2]=b;d=(m<<9)+d|0;e=7;break Qf}H[f+16>>2]=b;e=8;d=(m<<8)+d|0}e=e-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;c=n?h:!h;break Pf}d=d-(c<<16)|0;if(!(b&32768)){h=H[m+4>>2];c=b>>>0>>0;H[i>>2]=H[m+(c?12:8)>>2];while(1){Rf:{if(e){break Rf}e=H[f+16>>2];n=e+1|0;m=I[e+1|0];if(I[e|0]==255){if(m>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break Rf}H[f+16>>2]=n;d=(m<<9)+d|0;e=7;break Rf}H[f+16>>2]=n;e=8;d=(m<<8)+d|0}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}c=c?!h:h;break Pf}c=H[m+4>>2]}if(!c){break Of}l=j-4|0;h=H[j+4>>2]>>>17&4|(H[l>>2]>>>19&1|(g>>>14&16|(g>>>16&64|g&170)));i=w+(I[h+24384|0]<<2)|0;p=H[i>>2];c=H[p>>2];b=b-c|0;Sf:{if(d>>>16>>>0>>0){m=H[p+4>>2];n=b>>>0>>0;H[i>>2]=H[p+(n?8:12)>>2];while(1){Tf:{if(e){break Tf}e=H[f+16>>2];b=e+1|0;p=I[e+1|0];if(I[e|0]==255){if(p>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break Tf}H[f+16>>2]=b;d=(p<<9)+d|0;e=7;break Tf}H[f+16>>2]=b;e=8;d=(p<<8)+d|0}e=e-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;n=n?m:!m;break Sf}d=d-(c<<16)|0;if(!(b&32768)){m=H[p+4>>2];c=b>>>0>>0;H[i>>2]=H[p+(c?12:8)>>2];while(1){Uf:{if(e){break Uf}e=H[f+16>>2];n=e+1|0;p=I[e+1|0];if(I[e|0]==255){if(p>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break Uf}H[f+16>>2]=n;d=(p<<9)+d|0;e=7;break Uf}H[f+16>>2]=n;e=8;d=(p<<8)+d|0}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}n=c?!m:m;break Sf}n=H[p+4>>2]}c=I[h+24640|0];H[o>>2]=(n|0)==(c|0)?s:u;H[l>>2]=H[l>>2]|32;H[j+4>>2]=H[j+4>>2]|8;g=(c^n)<<19|g|16}Vf:{if(g&16777344){break Vf}h=g>>>3|0;i=w+(I[H[f+108>>2]+(h&495)|0]<<2)|0;l=H[i>>2];c=H[l>>2];b=b-c|0;Wf:{if(d>>>16>>>0>>0){m=H[l+4>>2];n=b>>>0>>0;H[i>>2]=H[l+(n?8:12)>>2];while(1){Xf:{if(e){break Xf}e=H[f+16>>2];b=e+1|0;l=I[e+1|0];if(I[e|0]==255){if(l>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break Xf}H[f+16>>2]=b;d=(l<<9)+d|0;e=7;break Xf}H[f+16>>2]=b;e=8;d=(l<<8)+d|0}e=e-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;c=n?m:!m;break Wf}d=d-(c<<16)|0;if(!(b&32768)){m=H[l+4>>2];c=b>>>0>>0;H[i>>2]=H[l+(c?12:8)>>2];while(1){Yf:{if(e){break Yf}e=H[f+16>>2];n=e+1|0;l=I[e+1|0];if(I[e|0]==255){if(l>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break Yf}H[f+16>>2]=n;d=(l<<9)+d|0;e=7;break Yf}H[f+16>>2]=n;e=8;d=(l<<8)+d|0}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}c=c?!m:m;break Wf}c=H[l+4>>2]}if(!c){break Vf}l=j-4|0;h=H[j+4>>2]>>>20&4|(H[l>>2]>>>22&1|(g>>>15&16|(g>>>19&64|h&170)));i=w+(I[h+24384|0]<<2)|0;p=H[i>>2];c=H[p>>2];b=b-c|0;Zf:{if(d>>>16>>>0>>0){m=H[p+4>>2];n=b>>>0>>0;H[i>>2]=H[p+(n?8:12)>>2];while(1){_f:{if(e){break _f}e=H[f+16>>2];b=e+1|0;p=I[e+1|0];if(I[e|0]==255){if(p>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break _f}H[f+16>>2]=b;d=(p<<9)+d|0;e=7;break _f}H[f+16>>2]=b;e=8;d=(p<<8)+d|0}e=e-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;n=n?m:!m;break Zf}d=d-(c<<16)|0;if(!(b&32768)){m=H[p+4>>2];c=b>>>0>>0;H[i>>2]=H[p+(c?12:8)>>2];while(1){$f:{if(e){break $f}e=H[f+16>>2];n=e+1|0;p=I[e+1|0];if(I[e|0]==255){if(p>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break $f}H[f+16>>2]=n;d=(p<<9)+d|0;e=7;break $f}H[f+16>>2]=n;e=8;d=(p<<8)+d|0}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}n=c?!m:m;break Zf}n=H[p+4>>2]}c=I[h+24640|0];H[o+256>>2]=(n|0)==(c|0)?s:u;H[l>>2]=H[l>>2]|256;H[j+4>>2]=H[j+4>>2]|64;g=(c^n)<<22|g|128}ag:{if(g&134218752){break ag}h=g>>>6|0;i=w+(I[H[f+108>>2]+(h&495)|0]<<2)|0;l=H[i>>2];c=H[l>>2];b=b-c|0;bg:{if(d>>>16>>>0>>0){m=H[l+4>>2];n=b>>>0>>0;H[i>>2]=H[l+(n?8:12)>>2];while(1){cg:{if(e){break cg}e=H[f+16>>2];b=e+1|0;l=I[e+1|0];if(I[e|0]==255){if(l>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break cg}H[f+16>>2]=b;d=(l<<9)+d|0;e=7;break cg}H[f+16>>2]=b;e=8;d=(l<<8)+d|0}e=e-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;c=n?m:!m;break bg}d=d-(c<<16)|0;if(!(b&32768)){m=H[l+4>>2];c=b>>>0>>0;H[i>>2]=H[l+(c?12:8)>>2];while(1){dg:{if(e){break dg}e=H[f+16>>2];n=e+1|0;l=I[e+1|0];if(I[e|0]==255){if(l>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break dg}H[f+16>>2]=n;d=(l<<9)+d|0;e=7;break dg}H[f+16>>2]=n;e=8;d=(l<<8)+d|0}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}c=c?!m:m;break bg}c=H[l+4>>2]}if(!c){break ag}l=j-4|0;h=H[j+4>>2]>>>23&4|(H[l>>2]>>>25&1|(g>>>18&16|(g>>>22&64|h&170)));i=w+(I[h+24384|0]<<2)|0;p=H[i>>2];c=H[p>>2];b=b-c|0;eg:{if(d>>>16>>>0>>0){m=H[p+4>>2];n=b>>>0>>0;H[i>>2]=H[p+(n?8:12)>>2];while(1){fg:{if(e){break fg}e=H[f+16>>2];b=e+1|0;p=I[e+1|0];if(I[e|0]==255){if(p>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break fg}H[f+16>>2]=b;d=(p<<9)+d|0;e=7;break fg}H[f+16>>2]=b;e=8;d=(p<<8)+d|0}e=e-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;n=n?m:!m;break eg}d=d-(c<<16)|0;if(!(b&32768)){m=H[p+4>>2];c=b>>>0>>0;H[i>>2]=H[p+(c?12:8)>>2];while(1){gg:{if(e){break gg}e=H[f+16>>2];n=e+1|0;p=I[e+1|0];if(I[e|0]==255){if(p>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break gg}H[f+16>>2]=n;d=(p<<9)+d|0;e=7;break gg}H[f+16>>2]=n;e=8;d=(p<<8)+d|0}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}n=c?!m:m;break eg}n=H[p+4>>2]}c=I[h+24640|0];H[o+512>>2]=(n|0)==(c|0)?s:u;H[l>>2]=H[l>>2]|2048;H[j+4>>2]=H[j+4>>2]|512;g=(c^n)<<25|g|1024}if(g&1073750016){break ff}h=g>>>9|0;i=w+(I[H[f+108>>2]+(h&495)|0]<<2)|0;l=H[i>>2];c=H[l>>2];b=b-c|0;hg:{if(d>>>16>>>0>>0){m=H[l+4>>2];n=b>>>0>>0;H[i>>2]=H[l+(n?8:12)>>2];while(1){ig:{if(e){break ig}e=H[f+16>>2];b=e+1|0;l=I[e+1|0];if(I[e|0]==255){if(l>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break ig}H[f+16>>2]=b;d=(l<<9)+d|0;e=7;break ig}H[f+16>>2]=b;e=8;d=(l<<8)+d|0}e=e-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;c=n?m:!m;break hg}d=d-(c<<16)|0;if(!(b&32768)){m=H[l+4>>2];c=b>>>0>>0;H[i>>2]=H[l+(c?12:8)>>2];while(1){jg:{if(e){break jg}e=H[f+16>>2];n=e+1|0;l=I[e+1|0];if(I[e|0]==255){if(l>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break jg}H[f+16>>2]=n;d=(l<<9)+d|0;e=7;break jg}H[f+16>>2]=n;e=8;d=(l<<8)+d|0}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}c=c?!m:m;break hg}c=H[l+4>>2]}if(!c){break ff}m=j-4|0;p=H[j+4>>2]>>>26&4|(H[m>>2]>>>28&1|(g>>>21&16|(g>>>25&64|h&170)));i=w+(I[p+24384|0]<<2)|0;t=H[i>>2];c=H[t>>2];b=b-c|0}kg:{if(d>>>16>>>0>>0){h=H[t+4>>2];n=b>>>0>>0;H[i>>2]=H[(n?8:12)+t>>2];while(1){lg:{if(e){break lg}e=H[f+16>>2];b=e+1|0;l=I[e+1|0];if(I[e|0]==255){if(l>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break lg}H[f+16>>2]=b;d=(l<<9)+d|0;e=7;break lg}H[f+16>>2]=b;e=8;d=(l<<8)+d|0}e=e-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;n=n?h:!h;break kg}d=d-(c<<16)|0;if(!(b&32768)){h=H[t+4>>2];c=b>>>0>>0;H[i>>2]=H[(c?12:8)+t>>2];while(1){mg:{if(e){break mg}e=H[f+16>>2];n=e+1|0;l=I[e+1|0];if(I[e|0]==255){if(l>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;d=d+65280|0;e=8;break mg}H[f+16>>2]=n;d=(l<<9)+d|0;e=7;break mg}H[f+16>>2]=n;e=8;d=(l<<8)+d|0}e=e-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}n=c?!h:h;break kg}n=H[t+4>>2]}c=I[p+24640|0];H[o+768>>2]=(n|0)==(c|0)?s:u;H[m>>2]=H[m>>2]|16384;H[j+4>>2]=H[j+4>>2]|4096;H[j+260>>2]=H[j+260>>2]|4;H[j+268>>2]=H[j+268>>2]|1;c=c^n;H[j+264>>2]=H[j+264>>2]|c<<18|2;g=c<<28|g|8192}H[j>>2]=g&-1226833921}g=j+4|0;c=o+4|0;r=r+1|0;if((r|0)!=64){continue}break}g=j+12|0;c=o+772|0;n=x>>>0<60;x=x+4|0;if(n){continue}break}}H[f+8>>2]=e;H[f+4>>2]=b;H[f>>2]=d;H[f+104>>2]=i}ng:{if(!(V&32)){break ng}H[f+104>>2]=f+100;g=H[f+100>>2];b=H[g>>2];d=H[f+4>>2]-b|0;H[f+4>>2]=d;e=H[f>>2];og:{if(e>>>16>>>0>>0){H[f+4>>2]=b;g=H[(b>>>0>d>>>0?8:12)+g>>2];H[f+100>>2]=g;d=H[f+8>>2];while(1){pg:{if(d){break pg}k=H[f+16>>2];c=k+1|0;i=I[k+1|0];if(I[k|0]==255){if(i>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;e=e+65280|0;d=8;break pg}H[f+16>>2]=c;e=(i<<9)+e|0;d=7;break pg}H[f+16>>2]=c;d=8;e=(i<<8)+e|0}d=d-1|0;H[f+8>>2]=d;e=e<<1;H[f>>2]=e;b=b<<1;H[f+4>>2]=b;if(b>>>0<32768){continue}break}d=b;break og}e=e-(b<<16)|0;H[f>>2]=e;if(d&32768){break og}g=H[(b>>>0>d>>>0?12:8)+g>>2];H[f+100>>2]=g;b=H[f+8>>2];while(1){qg:{if(b){break qg}c=H[f+16>>2];b=c+1|0;k=I[c+1|0];if(I[c|0]==255){if(k>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;e=e+65280|0;b=8;break qg}H[f+16>>2]=b;e=(k<<9)+e|0;b=7;break qg}H[f+16>>2]=b;b=8;e=(k<<8)+e|0}b=b-1|0;H[f+8>>2]=b;e=e<<1;H[f>>2]=e;d=d<<1;H[f+4>>2]=d;if(d>>>0<32768){continue}break}}b=H[g>>2];d=d-b|0;H[f+4>>2]=d;rg:{if(e>>>16>>>0>>0){H[f+4>>2]=b;g=H[(b>>>0>d>>>0?8:12)+g>>2];H[f+100>>2]=g;d=H[f+8>>2];while(1){sg:{if(d){break sg}k=H[f+16>>2];c=k+1|0;i=I[k+1|0];if(I[k|0]==255){if(i>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;e=e+65280|0;d=8;break sg}H[f+16>>2]=c;e=(i<<9)+e|0;d=7;break sg}H[f+16>>2]=c;d=8;e=(i<<8)+e|0}d=d-1|0;H[f+8>>2]=d;e=e<<1;H[f>>2]=e;b=b<<1;H[f+4>>2]=b;if(b>>>0<32768){continue}break}d=b;break rg}e=e-(b<<16)|0;H[f>>2]=e;if(d&32768){break rg}g=H[(b>>>0>d>>>0?12:8)+g>>2];H[f+100>>2]=g;b=H[f+8>>2];while(1){tg:{if(b){break tg}c=H[f+16>>2];b=c+1|0;k=I[c+1|0];if(I[c|0]==255){if(k>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;e=e+65280|0;b=8;break tg}H[f+16>>2]=b;e=(k<<9)+e|0;b=7;break tg}H[f+16>>2]=b;b=8;e=(k<<8)+e|0}b=b-1|0;H[f+8>>2]=b;e=e<<1;H[f>>2]=e;d=d<<1;H[f+4>>2]=d;if(d>>>0<32768){continue}break}}b=H[g>>2];d=d-b|0;H[f+4>>2]=d;ug:{if(e>>>16>>>0>>0){H[f+4>>2]=b;g=H[(b>>>0>d>>>0?8:12)+g>>2];H[f+100>>2]=g;d=H[f+8>>2];while(1){vg:{if(d){break vg}k=H[f+16>>2];c=k+1|0;i=I[k+1|0];if(I[k|0]==255){if(i>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;e=e+65280|0;d=8;break vg}H[f+16>>2]=c;e=(i<<9)+e|0;d=7;break vg}H[f+16>>2]=c;d=8;e=(i<<8)+e|0}d=d-1|0;H[f+8>>2]=d;e=e<<1;H[f>>2]=e;b=b<<1;H[f+4>>2]=b;if(b>>>0<32768){continue}break}d=b;break ug}e=e-(b<<16)|0;H[f>>2]=e;if(d&32768){break ug}g=H[(b>>>0>d>>>0?12:8)+g>>2];H[f+100>>2]=g;b=H[f+8>>2];while(1){wg:{if(b){break wg}c=H[f+16>>2];b=c+1|0;k=I[c+1|0];if(I[c|0]==255){if(k>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;e=e+65280|0;b=8;break wg}H[f+16>>2]=b;e=(k<<9)+e|0;b=7;break wg}H[f+16>>2]=b;b=8;e=(k<<8)+e|0}b=b-1|0;H[f+8>>2]=b;e=e<<1;H[f>>2]=e;d=d<<1;H[f+4>>2]=d;if(d>>>0<32768){continue}break}}b=H[g>>2];d=d-b|0;H[f+4>>2]=d;if(e>>>16>>>0>>0){H[f+4>>2]=b;H[f+100>>2]=H[(b>>>0>d>>>0?8:12)+g>>2];d=H[f+8>>2];while(1){xg:{if(d){break xg}g=H[f+16>>2];c=g+1|0;k=I[g+1|0];if(I[g|0]==255){if(k>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;e=e+65280|0;d=8;break xg}H[f+16>>2]=c;e=(k<<9)+e|0;d=7;break xg}H[f+16>>2]=c;d=8;e=(k<<8)+e|0}d=d-1|0;H[f+8>>2]=d;e=e<<1;H[f>>2]=e;b=b<<1;H[f+4>>2]=b;if(b>>>0<32768){continue}break}break ng}c=e-(b<<16)|0;H[f>>2]=c;if(d&32768){break ng}H[f+100>>2]=H[(b>>>0>d>>>0?12:8)+g>>2];e=H[f+8>>2];while(1){yg:{if(e){break yg}g=H[f+16>>2];b=g+1|0;k=I[g+1|0];if(I[g|0]==255){if(k>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;c=c+65280|0;e=8;break yg}H[f+16>>2]=b;c=(k<<9)+c|0;e=7;break yg}H[f+16>>2]=b;e=8;c=(k<<8)+c|0}e=e-1|0;H[f+8>>2]=e;c=c<<1;H[f>>2]=c;d=d<<1;H[f+4>>2]=d;if(d>>>0<32768){continue}break}}break cb;case 0:break eb}}if(ea){r=1<>>1|0;u=H[f+124>>2];d=u<<2;b=(d+H[f+120>>2]|0)+12|0;g=H[f+116>>2];j=0;c=H[f+128>>2];if(c>>>0>=4){if(!u){break bb}h=N(u,12);n=u<<3;m=0-r|0;while(1){c=0;while(1){k=b;b=H[b>>2];zg:{if(!b){break zg}if((b&2097168)==16){b=H[f>>2];e=H[f+8>>2];Ag:{if(e){break Ag}q=(b|0)!=255;i=H[f+16>>2];b=I[i|0];Bg:{if(q){e=8}else{if(b>>>0>143){break Bg}e=7}H[f>>2]=b;H[f+16>>2]=i+1;break Ag}e=8;b=255}i=e-1|0;H[f+8>>2]=i;i=b>>>i&1;b=H[g>>2];H[g>>2]=((i|0)==(b>>>31|0)?m:r)+b;b=H[k>>2]|1048576;H[k>>2]=b}if((b&16777344)==128){b=H[f>>2];e=H[f+8>>2];Cg:{if(e){break Cg}q=(b|0)!=255;i=H[f+16>>2];b=I[i|0];Dg:{if(q){e=8}else{if(b>>>0>143){break Dg}e=7}H[f>>2]=b;H[f+16>>2]=i+1;break Cg}e=8;b=255}q=e-1|0;H[f+8>>2]=q;i=d+g|0;o=H[i>>2];H[i>>2]=o+((b>>>q&1)==(o>>>31|0)?m:r);b=H[k>>2]|8388608;H[k>>2]=b}if((b&134218752)==1024){b=H[f>>2];e=H[f+8>>2];Eg:{if(e){break Eg}q=(b|0)!=255;i=H[f+16>>2];b=I[i|0];Fg:{if(q){e=8}else{if(b>>>0>143){break Fg}e=7}H[f>>2]=b;H[f+16>>2]=i+1;break Eg}e=8;b=255}q=e-1|0;H[f+8>>2]=q;i=g+n|0;o=H[i>>2];H[i>>2]=o+((b>>>q&1)==(o>>>31|0)?m:r);b=H[k>>2]|67108864;H[k>>2]=b}if((b&1073750016)!=8192){break zg}b=H[f>>2];e=H[f+8>>2];Gg:{if(e){break Gg}q=(b|0)!=255;i=H[f+16>>2];b=I[i|0];Hg:{if(q){e=8}else{if(b>>>0>143){break Hg}e=7}H[f>>2]=b;H[f+16>>2]=i+1;break Gg}e=8;b=255}q=e-1|0;H[f+8>>2]=q;i=h+g|0;o=H[i>>2];H[i>>2]=o+((b>>>q&1)==(o>>>31|0)?m:r);H[k>>2]=H[k>>2]|536870912}g=g+4|0;b=k+4|0;c=c+1|0;if((u|0)!=(c|0)){continue}break}g=h+g|0;b=k+12|0;j=j+4|0;c=H[f+128>>2];if(j>>>0<(c&-4)>>>0){continue}break}}if(!u|c>>>0<=j>>>0){break cb}p=0;q=0-r|0;d=c;while(1){Ig:{if((d|0)==(j|0)){d=j;break Ig}e=H[b>>2];t=0;while(1){d=N(t,3);if((2097168<>2];l=H[f+8>>2];Jg:{if(l){break Jg}i=(c|0)!=255;k=H[f+16>>2];c=I[k|0];Kg:{if(i){l=8}else{if(c>>>0>143){break Kg}l=7}H[f>>2]=c;H[f+16>>2]=k+1;break Jg}l=8;c=255}k=l-1|0;H[f+8>>2]=k;k=c>>>k&1;c=H[n>>2];H[n>>2]=((k|0)==(c>>>31|0)?q:r)+c;e=H[b>>2]|1048576<>2]=e;c=H[f+128>>2]}t=t+1|0;d=c;if(t>>>0>>0){continue}break}}g=g+4|0;b=b+4|0;p=p+1|0;if((u|0)!=(p|0)){continue}break}break cb}k=H[f+120>>2];d=H[f+116>>2];w=H[f+124>>2];c=H[f+128>>2];if(!((w|0)!=64|(c|0)!=64)){c=k+268|0;x=0;p=1<>>1|0;m=0-p|0;t=H[f+8>>2];g=H[f+4>>2];b=H[f>>2];j=H[f+104>>2];while(1){l=0;while(1){q=d;i=c;d=H[c>>2];if(d){k=c;if((d&2097168)==16){j=ca+((d&1048576?16:d&495?15:14)<<2)|0;o=H[j>>2];c=H[o>>2];g=g-c|0;Lg:{if(b>>>16>>>0>>0){e=H[o+4>>2];n=c>>>0>g>>>0;H[j>>2]=H[o+(n?8:12)>>2];while(1){Mg:{if(t){break Mg}o=H[f+16>>2];g=o+1|0;h=I[o+1|0];if(I[o|0]==255){if(h>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;b=b+65280|0;t=8;break Mg}H[f+16>>2]=g;b=(h<<9)+b|0;t=7;break Mg}H[f+16>>2]=g;t=8;b=(h<<8)+b|0}t=t-1|0;b=b<<1;c=c<<1;if(c>>>0<32768){continue}break}g=c;n=n?e:!e;break Lg}b=b-(c<<16)|0;if(!(g&32768)){e=H[o+4>>2];c=c>>>0>g>>>0;H[j>>2]=H[o+(c?12:8)>>2];while(1){Ng:{if(t){break Ng}o=H[f+16>>2];n=o+1|0;h=I[o+1|0];if(I[o|0]==255){if(h>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;b=b+65280|0;t=8;break Ng}H[f+16>>2]=n;b=(h<<9)+b|0;t=7;break Ng}H[f+16>>2]=n;t=8;b=(h<<8)+b|0}t=t-1|0;b=b<<1;g=g<<1;if(g>>>0<32768){continue}break}n=c?!e:e;break Lg}n=H[o+4>>2]}c=H[q>>2];H[q>>2]=((n|0)==(c>>>31|0)?m:p)+c;d=d|1048576}if((d&16777344)==128){j=ca+((d&8388608?16:d&3960?15:14)<<2)|0;o=H[j>>2];c=H[o>>2];g=g-c|0;Og:{if(b>>>16>>>0>>0){e=H[o+4>>2];n=c>>>0>g>>>0;H[j>>2]=H[o+(n?8:12)>>2];while(1){Pg:{if(t){break Pg}o=H[f+16>>2];g=o+1|0;h=I[o+1|0];if(I[o|0]==255){if(h>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;b=b+65280|0;t=8;break Pg}H[f+16>>2]=g;b=(h<<9)+b|0;t=7;break Pg}H[f+16>>2]=g;t=8;b=(h<<8)+b|0}t=t-1|0;b=b<<1;c=c<<1;if(c>>>0<32768){continue}break}g=c;n=n?e:!e;break Og}b=b-(c<<16)|0;if(!(g&32768)){e=H[o+4>>2];c=c>>>0>g>>>0;H[j>>2]=H[o+(c?12:8)>>2];while(1){Qg:{if(t){break Qg}o=H[f+16>>2];n=o+1|0;h=I[o+1|0];if(I[o|0]==255){if(h>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;b=b+65280|0;t=8;break Qg}H[f+16>>2]=n;b=(h<<9)+b|0;t=7;break Qg}H[f+16>>2]=n;t=8;b=(h<<8)+b|0}t=t-1|0;b=b<<1;g=g<<1;if(g>>>0<32768){continue}break}n=c?!e:e;break Og}n=H[o+4>>2]}c=H[q+256>>2];H[q+256>>2]=((n|0)==(c>>>31|0)?m:p)+c;d=d|8388608}if((d&134218752)==1024){j=ca+((d&67108864?16:d&31680?15:14)<<2)|0;o=H[j>>2];c=H[o>>2];g=g-c|0;Rg:{if(b>>>16>>>0>>0){e=H[o+4>>2];n=c>>>0>g>>>0;H[j>>2]=H[o+(n?8:12)>>2];while(1){Sg:{if(t){break Sg}o=H[f+16>>2];g=o+1|0;h=I[o+1|0];if(I[o|0]==255){if(h>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;b=b+65280|0;t=8;break Sg}H[f+16>>2]=g;b=(h<<9)+b|0;t=7;break Sg}H[f+16>>2]=g;t=8;b=(h<<8)+b|0}t=t-1|0;b=b<<1;c=c<<1;if(c>>>0<32768){continue}break}g=c;n=n?e:!e;break Rg}b=b-(c<<16)|0;if(!(g&32768)){e=H[o+4>>2];c=c>>>0>g>>>0;H[j>>2]=H[o+(c?12:8)>>2];while(1){Tg:{if(t){break Tg}o=H[f+16>>2];n=o+1|0;h=I[o+1|0];if(I[o|0]==255){if(h>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;b=b+65280|0;t=8;break Tg}H[f+16>>2]=n;b=(h<<9)+b|0;t=7;break Tg}H[f+16>>2]=n;t=8;b=(h<<8)+b|0}t=t-1|0;b=b<<1;g=g<<1;if(g>>>0<32768){continue}break}n=c?!e:e;break Rg}n=H[o+4>>2]}c=H[q+512>>2];H[q+512>>2]=((n|0)==(c>>>31|0)?m:p)+c;d=d|67108864}if((d&1073750016)==8192){j=ca+((d&536870912?16:d&253440?15:14)<<2)|0;o=H[j>>2];c=H[o>>2];g=g-c|0;Ug:{if(b>>>16>>>0>>0){e=H[o+4>>2];n=c>>>0>g>>>0;H[j>>2]=H[o+(n?8:12)>>2];while(1){Vg:{if(t){break Vg}o=H[f+16>>2];g=o+1|0;h=I[o+1|0];if(I[o|0]==255){if(h>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;b=b+65280|0;t=8;break Vg}H[f+16>>2]=g;b=(h<<9)+b|0;t=7;break Vg}H[f+16>>2]=g;t=8;b=(h<<8)+b|0}t=t-1|0;b=b<<1;c=c<<1;if(c>>>0<32768){continue}break}g=c;n=n?e:!e;break Ug}b=b-(c<<16)|0;if(!(g&32768)){e=H[o+4>>2];c=c>>>0>g>>>0;H[j>>2]=H[o+(c?12:8)>>2];while(1){Wg:{if(t){break Wg}o=H[f+16>>2];n=o+1|0;h=I[o+1|0];if(I[o|0]==255){if(h>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;b=b+65280|0;t=8;break Wg}H[f+16>>2]=n;b=(h<<9)+b|0;t=7;break Wg}H[f+16>>2]=n;t=8;b=(h<<8)+b|0}t=t-1|0;b=b<<1;g=g<<1;if(g>>>0<32768){continue}break}n=c?!e:e;break Ug}n=H[o+4>>2]}c=H[q+768>>2];H[q+768>>2]=((n|0)==(c>>>31|0)?m:p)+c;d=d|536870912}H[k>>2]=d}c=i+4|0;d=q+4|0;l=l+1|0;if((l|0)!=64){continue}break}c=i+12|0;d=q+772|0;k=x>>>0<60;x=x+4|0;if(k){continue}break}H[f+8>>2]=t;H[f+4>>2]=g;H[f>>2]=b;H[f+104>>2]=j;break cb}s=1<>>1|0;e=w<<2;h=(e+k|0)+12|0;t=H[f+8>>2];g=H[f+4>>2];b=H[f>>2];j=H[f+104>>2];o=0;Xg:{if(c>>>0<4){break Xg}if(w){p=N(w,12);n=w<<3;r=0-s|0;while(1){l=0;while(1){k=h;i=H[h>>2];if(i){if((i&2097168)==16){j=ca+((i&1048576?16:i&495?15:14)<<2)|0;h=H[j>>2];c=H[h>>2];g=g-c|0;Yg:{if(b>>>16>>>0>=c>>>0){b=b-(c<<16)|0;q=H[h+4>>2];if(g&32768){break Yg}m=H[h+4>>2];c=c>>>0>g>>>0;H[j>>2]=H[h+(c?12:8)>>2];while(1){Zg:{if(t){break Zg}h=H[f+16>>2];q=h+1|0;u=I[h+1|0];if(I[h|0]!=255){H[f+16>>2]=q;t=8;b=(u<<8)+b|0;break Zg}if(u>>>0<=143){H[f+16>>2]=q;b=(u<<9)+b|0;t=7;break Zg}H[f+12>>2]=H[f+12>>2]+1;b=b+65280|0;t=8}t=t-1|0;b=b<<1;g=g<<1;if(g>>>0<32768){continue}break}q=c?!m:m;break Yg}m=H[h+4>>2];q=c>>>0>g>>>0;H[j>>2]=H[h+(q?8:12)>>2];while(1){_g:{if(t){break _g}h=H[f+16>>2];g=h+1|0;u=I[h+1|0];if(I[h|0]!=255){H[f+16>>2]=g;t=8;b=(u<<8)+b|0;break _g}if(u>>>0<=143){H[f+16>>2]=g;b=(u<<9)+b|0;t=7;break _g}H[f+12>>2]=H[f+12>>2]+1;b=b+65280|0;t=8}t=t-1|0;b=b<<1;c=c<<1;if(c>>>0<32768){continue}break}g=c;q=q?m:!m}c=H[d>>2];H[d>>2]=((q|0)==(c>>>31|0)?r:s)+c;i=i|1048576}if((i&16777344)==128){j=ca+((i&8388608?16:i&3960?15:14)<<2)|0;h=H[j>>2];c=H[h>>2];g=g-c|0;$g:{if(b>>>16>>>0>=c>>>0){b=b-(c<<16)|0;q=H[h+4>>2];if(g&32768){break $g}m=H[h+4>>2];c=c>>>0>g>>>0;H[j>>2]=H[h+(c?12:8)>>2];while(1){ah:{if(t){break ah}h=H[f+16>>2];q=h+1|0;u=I[h+1|0];if(I[h|0]!=255){H[f+16>>2]=q;t=8;b=(u<<8)+b|0;break ah}if(u>>>0<=143){H[f+16>>2]=q;b=(u<<9)+b|0;t=7;break ah}H[f+12>>2]=H[f+12>>2]+1;b=b+65280|0;t=8}t=t-1|0;b=b<<1;g=g<<1;if(g>>>0<32768){continue}break}q=c?!m:m;break $g}m=H[h+4>>2];q=c>>>0>g>>>0;H[j>>2]=H[h+(q?8:12)>>2];while(1){bh:{if(t){break bh}h=H[f+16>>2];g=h+1|0;u=I[h+1|0];if(I[h|0]!=255){H[f+16>>2]=g;t=8;b=(u<<8)+b|0;break bh}if(u>>>0<=143){H[f+16>>2]=g;b=(u<<9)+b|0;t=7;break bh}H[f+12>>2]=H[f+12>>2]+1;b=b+65280|0;t=8}t=t-1|0;b=b<<1;c=c<<1;if(c>>>0<32768){continue}break}g=c;q=q?m:!m}h=q;c=d+e|0;q=H[c>>2];H[c>>2]=q+((h|0)==(q>>>31|0)?r:s);i=i|8388608}if((i&134218752)==1024){j=ca+((i&67108864?16:i&31680?15:14)<<2)|0;h=H[j>>2];c=H[h>>2];g=g-c|0;ch:{if(b>>>16>>>0>=c>>>0){b=b-(c<<16)|0;q=H[h+4>>2];if(g&32768){break ch}m=H[h+4>>2];c=c>>>0>g>>>0;H[j>>2]=H[h+(c?12:8)>>2];while(1){dh:{if(t){break dh}h=H[f+16>>2];q=h+1|0;u=I[h+1|0];if(I[h|0]!=255){H[f+16>>2]=q;t=8;b=(u<<8)+b|0;break dh}if(u>>>0<=143){H[f+16>>2]=q;b=(u<<9)+b|0;t=7;break dh}H[f+12>>2]=H[f+12>>2]+1;b=b+65280|0;t=8}t=t-1|0;b=b<<1;g=g<<1;if(g>>>0<32768){continue}break}q=c?!m:m;break ch}m=H[h+4>>2];q=c>>>0>g>>>0;H[j>>2]=H[h+(q?8:12)>>2];while(1){eh:{if(t){break eh}h=H[f+16>>2];g=h+1|0;u=I[h+1|0];if(I[h|0]!=255){H[f+16>>2]=g;t=8;b=(u<<8)+b|0;break eh}if(u>>>0<=143){H[f+16>>2]=g;b=(u<<9)+b|0;t=7;break eh}H[f+12>>2]=H[f+12>>2]+1;b=b+65280|0;t=8}t=t-1|0;b=b<<1;c=c<<1;if(c>>>0<32768){continue}break}g=c;q=q?m:!m}h=q;c=d+n|0;q=H[c>>2];H[c>>2]=q+((h|0)==(q>>>31|0)?r:s);i=i|67108864}if((i&1073750016)==8192){j=ca+((i&536870912?16:i&253440?15:14)<<2)|0;h=H[j>>2];c=H[h>>2];g=g-c|0;fh:{if(b>>>16>>>0>=c>>>0){b=b-(c<<16)|0;q=H[h+4>>2];if(g&32768){break fh}m=H[h+4>>2];c=c>>>0>g>>>0;H[j>>2]=H[h+(c?12:8)>>2];while(1){gh:{if(t){break gh}h=H[f+16>>2];q=h+1|0;u=I[h+1|0];if(I[h|0]!=255){H[f+16>>2]=q;t=8;b=(u<<8)+b|0;break gh}if(u>>>0<=143){H[f+16>>2]=q;b=(u<<9)+b|0;t=7;break gh}H[f+12>>2]=H[f+12>>2]+1;b=b+65280|0;t=8}t=t-1|0;b=b<<1;g=g<<1;if(g>>>0<32768){continue}break}q=c?!m:m;break fh}m=H[h+4>>2];q=c>>>0>g>>>0;H[j>>2]=H[h+(q?8:12)>>2];while(1){hh:{if(t){break hh}h=H[f+16>>2];g=h+1|0;u=I[h+1|0];if(I[h|0]!=255){H[f+16>>2]=g;t=8;b=(u<<8)+b|0;break hh}if(u>>>0<=143){H[f+16>>2]=g;b=(u<<9)+b|0;t=7;break hh}H[f+12>>2]=H[f+12>>2]+1;b=b+65280|0;t=8}t=t-1|0;b=b<<1;c=c<<1;if(c>>>0<32768){continue}break}g=c;q=q?m:!m}h=q;c=d+p|0;q=H[c>>2];H[c>>2]=q+((h|0)==(q>>>31|0)?r:s);i=i|536870912}H[k>>2]=i}h=k+4|0;d=d+4|0;l=l+1|0;if((w|0)!=(l|0)){continue}break}h=k+12|0;d=d+p|0;o=o+4|0;c=H[f+128>>2];if(o>>>0<(c&-4)>>>0){continue}break}break Xg}o=c&-4;h=(k+(o<<1)|0)+12|0}H[f+8>>2]=t;H[f+4>>2]=g;H[f>>2]=b;H[f+104>>2]=j;if(!w|c>>>0<=o>>>0){break cb}x=0;k=0-s|0;b=c;while(1){ih:{if((b|0)==(o|0)){b=o;break ih}t=H[h>>2];e=0;while(1){l=N(e,3);if((2097168<>>l|0;i=ca+((b&1048576?16:b&495?15:14)<<2)|0;H[f+104>>2]=i;q=H[i>>2];b=H[q>>2];c=H[f+4>>2]-b|0;H[f+4>>2]=c;g=H[f>>2];jh:{if(g>>>16>>>0>>0){n=H[q+4>>2];H[f+4>>2]=b;c=b>>>0>c>>>0;H[i>>2]=H[q+(c?8:12)>>2];t=H[f+8>>2];while(1){kh:{if(t){break kh}q=H[f+16>>2];i=q+1|0;m=I[q+1|0];if(I[q|0]==255){if(m>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;g=g+65280|0;t=8;break kh}H[f+16>>2]=i;g=(m<<9)+g|0;t=7;break kh}H[f+16>>2]=i;t=8;g=(m<<8)+g|0}t=t-1|0;H[f+8>>2]=t;g=g<<1;H[f>>2]=g;b=b<<1;H[f+4>>2]=b;if(b>>>0<32768){continue}break}c=c?n:!n;break jh}g=g-(b<<16)|0;H[f>>2]=g;if(!(c&32768)){n=H[q+4>>2];b=b>>>0>c>>>0;H[i>>2]=H[q+(b?12:8)>>2];t=H[f+8>>2];while(1){lh:{if(t){break lh}q=H[f+16>>2];i=q+1|0;m=I[q+1|0];if(I[q|0]==255){if(m>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;g=g+65280|0;t=8;break lh}H[f+16>>2]=i;g=(m<<9)+g|0;t=7;break lh}H[f+16>>2]=i;t=8;g=(m<<8)+g|0}t=t-1|0;H[f+8>>2]=t;g=g<<1;H[f>>2]=g;c=c<<1;H[f+4>>2]=c;if(c>>>0<32768){continue}break}c=b?!n:n;break jh}c=H[q+4>>2]}b=H[j>>2];H[j>>2]=((c|0)==(b>>>31|0)?k:s)+b;t=H[h>>2]|1048576<>2]=t;c=H[f+128>>2]}e=e+1|0;b=c;if(e>>>0>>0){continue}break}}h=h+4|0;d=d+4|0;x=x+1|0;if((w|0)!=(x|0)){continue}break}break cb}i=0;x=0;p=0;mh:{nh:{oh:{M=H[f+124>>2];if(!((M|0)!=64|H[f+128>>2]!=64)){b=1<>>1|b;k=0-i|0;s=f+28|0;g=H[f+120>>2]+268|0;h=H[f+8>>2];c=H[f+4>>2];j=H[f>>2];l=H[f+104>>2];b=H[f+116>>2];if(V&8){break oh}while(1){p=0;while(1){q=b;n=g;g=H[g>>2];if(g){ph:{if(g&2097168){break ph}b=g&495;if(!b){break ph}l=s+(I[b+H[f+108>>2]|0]<<2)|0;o=H[l>>2];b=H[o>>2];c=c-b|0;qh:{if(j>>>16>>>0>>0){e=H[o+4>>2];d=b>>>0>c>>>0;H[l>>2]=H[o+(d?8:12)>>2];while(1){rh:{if(h){break rh}o=H[f+16>>2];c=o+1|0;m=I[o+1|0];if(I[o|0]==255){if(m>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8;break rh}H[f+16>>2]=c;j=(m<<9)+j|0;h=7;break rh}H[f+16>>2]=c;h=8;j=(m<<8)+j|0}h=h-1|0;j=j<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;b=d?e:!e;break qh}j=j-(b<<16)|0;if(!(c&32768)){e=H[o+4>>2];b=b>>>0>c>>>0;H[l>>2]=H[o+(b?12:8)>>2];while(1){sh:{if(h){break sh}o=H[f+16>>2];d=o+1|0;m=I[o+1|0];if(I[o|0]==255){if(m>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8;break sh}H[f+16>>2]=d;j=(m<<9)+j|0;h=7;break sh}H[f+16>>2]=d;h=8;j=(m<<8)+j|0}h=h-1|0;j=j<<1;c=c<<1;if(c>>>0<32768){continue}break}b=b?!e:e;break qh}b=H[o+4>>2]}if(b){u=n-4|0;d=H[n+4>>2]>>>17&4|(H[u>>2]>>>19&1|(g>>>14&16|(g>>>16&64|g&170)));l=s+(I[d+24384|0]<<2)|0;e=H[l>>2];b=H[e>>2];c=c-b|0;o=I[d+24640|0];th:{if(j>>>16>>>0>>0){m=H[e+4>>2];d=b>>>0>c>>>0;H[l>>2]=H[e+(d?8:12)>>2];while(1){uh:{if(h){break uh}e=H[f+16>>2];c=e+1|0;r=I[e+1|0];if(I[e|0]==255){if(r>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8;break uh}H[f+16>>2]=c;j=(r<<9)+j|0;h=7;break uh}H[f+16>>2]=c;h=8;j=(r<<8)+j|0}h=h-1|0;j=j<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;d=d?m:!m;break th}j=j-(b<<16)|0;if(!(c&32768)){m=H[e+4>>2];b=b>>>0>c>>>0;H[l>>2]=H[e+(b?12:8)>>2];while(1){vh:{if(h){break vh}e=H[f+16>>2];d=e+1|0;r=I[e+1|0];if(I[e|0]==255){if(r>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8;break vh}H[f+16>>2]=d;j=(r<<9)+j|0;h=7;break vh}H[f+16>>2]=d;h=8;j=(r<<8)+j|0}h=h-1|0;j=j<<1;c=c<<1;if(c>>>0<32768){continue}break}d=b?!m:m;break th}d=H[e+4>>2]}H[q>>2]=(o|0)==(d|0)?i:k;H[u>>2]=H[u>>2]|32;H[n+4>>2]=H[n+4>>2]|8;b=n-268|0;H[b>>2]=H[b>>2]|131072;b=n-260|0;H[b>>2]=H[b>>2]|32768;b=n-264|0;u=b;e=H[b>>2];b=d^o;H[u>>2]=e|b<<31|65536;g=b<<19|g|16}g=g|2097152}if(!(!(g&3960)|g&16777344)){o=g>>>3|0;l=s+(I[H[f+108>>2]+(o&495)|0]<<2)|0;e=H[l>>2];b=H[e>>2];c=c-b|0;wh:{if(j>>>16>>>0>>0){m=H[e+4>>2];d=b>>>0>c>>>0;H[l>>2]=H[e+(d?8:12)>>2];while(1){xh:{if(h){break xh}e=H[f+16>>2];c=e+1|0;u=I[e+1|0];if(I[e|0]==255){if(u>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8;break xh}H[f+16>>2]=c;j=(u<<9)+j|0;h=7;break xh}H[f+16>>2]=c;h=8;j=(u<<8)+j|0}h=h-1|0;j=j<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;b=d?m:!m;break wh}j=j-(b<<16)|0;if(!(c&32768)){m=H[e+4>>2];b=b>>>0>c>>>0;H[l>>2]=H[e+(b?12:8)>>2];while(1){yh:{if(h){break yh}e=H[f+16>>2];d=e+1|0;u=I[e+1|0];if(I[e|0]==255){if(u>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8;break yh}H[f+16>>2]=d;j=(u<<9)+j|0;h=7;break yh}H[f+16>>2]=d;h=8;j=(u<<8)+j|0}h=h-1|0;j=j<<1;c=c<<1;if(c>>>0<32768){continue}break}b=b?!m:m;break wh}b=H[e+4>>2]}if(b){u=n-4|0;d=H[n+4>>2]>>>20&4|(H[u>>2]>>>22&1|(g>>>15&16|(g>>>19&64|o&170)));l=s+(I[d+24384|0]<<2)|0;e=H[l>>2];b=H[e>>2];c=c-b|0;o=I[d+24640|0];zh:{if(j>>>16>>>0>>0){m=H[e+4>>2];d=b>>>0>c>>>0;H[l>>2]=H[e+(d?8:12)>>2];while(1){Ah:{if(h){break Ah}e=H[f+16>>2];c=e+1|0;r=I[e+1|0];if(I[e|0]==255){if(r>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8;break Ah}H[f+16>>2]=c;j=(r<<9)+j|0;h=7;break Ah}H[f+16>>2]=c;h=8;j=(r<<8)+j|0}h=h-1|0;j=j<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;b=d?m:!m;break zh}j=j-(b<<16)|0;if(!(c&32768)){m=H[e+4>>2];b=b>>>0>c>>>0;H[l>>2]=H[e+(b?12:8)>>2];while(1){Bh:{if(h){break Bh}e=H[f+16>>2];d=e+1|0;r=I[e+1|0];if(I[e|0]==255){if(r>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8;break Bh}H[f+16>>2]=d;j=(r<<9)+j|0;h=7;break Bh}H[f+16>>2]=d;h=8;j=(r<<8)+j|0}h=h-1|0;j=j<<1;c=c<<1;if(c>>>0<32768){continue}break}b=b?!m:m;break zh}b=H[e+4>>2]}H[q+256>>2]=(o|0)==(b|0)?i:k;H[u>>2]=H[u>>2]|256;H[n+4>>2]=H[n+4>>2]|64;g=(b^o)<<22|g|128}g=g|16777216}if(!(!(g&31680)|g&134218752)){o=g>>>6|0;l=s+(I[H[f+108>>2]+(o&495)|0]<<2)|0;e=H[l>>2];b=H[e>>2];c=c-b|0;Ch:{if(j>>>16>>>0>>0){m=H[e+4>>2];d=b>>>0>c>>>0;H[l>>2]=H[e+(d?8:12)>>2];while(1){Dh:{if(h){break Dh}e=H[f+16>>2];c=e+1|0;u=I[e+1|0];if(I[e|0]==255){if(u>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8;break Dh}H[f+16>>2]=c;j=(u<<9)+j|0;h=7;break Dh}H[f+16>>2]=c;h=8;j=(u<<8)+j|0}h=h-1|0;j=j<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;b=d?m:!m;break Ch}j=j-(b<<16)|0;if(!(c&32768)){m=H[e+4>>2];b=b>>>0>c>>>0;H[l>>2]=H[e+(b?12:8)>>2];while(1){Eh:{if(h){break Eh}e=H[f+16>>2];d=e+1|0;u=I[e+1|0];if(I[e|0]==255){if(u>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8;break Eh}H[f+16>>2]=d;j=(u<<9)+j|0;h=7;break Eh}H[f+16>>2]=d;h=8;j=(u<<8)+j|0}h=h-1|0;j=j<<1;c=c<<1;if(c>>>0<32768){continue}break}b=b?!m:m;break Ch}b=H[e+4>>2]}if(b){u=n-4|0;d=H[n+4>>2]>>>23&4|(H[u>>2]>>>25&1|(g>>>18&16|(g>>>22&64|o&170)));l=s+(I[d+24384|0]<<2)|0;e=H[l>>2];b=H[e>>2];c=c-b|0;o=I[d+24640|0];Fh:{if(j>>>16>>>0>>0){m=H[e+4>>2];d=b>>>0>c>>>0;H[l>>2]=H[e+(d?8:12)>>2];while(1){Gh:{if(h){break Gh}e=H[f+16>>2];c=e+1|0;r=I[e+1|0];if(I[e|0]==255){if(r>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8;break Gh}H[f+16>>2]=c;j=(r<<9)+j|0;h=7;break Gh}H[f+16>>2]=c;h=8;j=(r<<8)+j|0}h=h-1|0;j=j<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;b=d?m:!m;break Fh}j=j-(b<<16)|0;if(!(c&32768)){m=H[e+4>>2];b=b>>>0>c>>>0;H[l>>2]=H[e+(b?12:8)>>2];while(1){Hh:{if(h){break Hh}e=H[f+16>>2];d=e+1|0;r=I[e+1|0];if(I[e|0]==255){if(r>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8;break Hh}H[f+16>>2]=d;j=(r<<9)+j|0;h=7;break Hh}H[f+16>>2]=d;h=8;j=(r<<8)+j|0}h=h-1|0;j=j<<1;c=c<<1;if(c>>>0<32768){continue}break}b=b?!m:m;break Fh}b=H[e+4>>2]}H[q+512>>2]=(o|0)==(b|0)?i:k;H[u>>2]=H[u>>2]|2048;H[n+4>>2]=H[n+4>>2]|512;g=(b^o)<<25|g|1024}g=g|134217728}if(!(!(g&253440)|g&1073750016)){o=g>>>9|0;l=s+(I[H[f+108>>2]+(o&495)|0]<<2)|0;e=H[l>>2];b=H[e>>2];c=c-b|0;Ih:{if(j>>>16>>>0>>0){m=H[e+4>>2];d=b>>>0>c>>>0;H[l>>2]=H[e+(d?8:12)>>2];while(1){Jh:{if(h){break Jh}e=H[f+16>>2];c=e+1|0;u=I[e+1|0];if(I[e|0]==255){if(u>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8;break Jh}H[f+16>>2]=c;j=(u<<9)+j|0;h=7;break Jh}H[f+16>>2]=c;h=8;j=(u<<8)+j|0}h=h-1|0;j=j<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;b=d?m:!m;break Ih}j=j-(b<<16)|0;if(!(c&32768)){m=H[e+4>>2];b=b>>>0>c>>>0;H[l>>2]=H[e+(b?12:8)>>2];while(1){Kh:{if(h){break Kh}e=H[f+16>>2];d=e+1|0;u=I[e+1|0];if(I[e|0]==255){if(u>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8;break Kh}H[f+16>>2]=d;j=(u<<9)+j|0;h=7;break Kh}H[f+16>>2]=d;h=8;j=(u<<8)+j|0}h=h-1|0;j=j<<1;c=c<<1;if(c>>>0<32768){continue}break}b=b?!m:m;break Ih}b=H[e+4>>2]}if(b){u=n-4|0;d=H[n+4>>2]>>>26&4|(H[u>>2]>>>28&1|(g>>>21&16|(g>>>25&64|o&170)));l=s+(I[d+24384|0]<<2)|0;e=H[l>>2];b=H[e>>2];c=c-b|0;o=I[d+24640|0];Lh:{if(j>>>16>>>0>>0){m=H[e+4>>2];d=b>>>0>c>>>0;H[l>>2]=H[e+(d?8:12)>>2];while(1){Mh:{if(h){break Mh}e=H[f+16>>2];c=e+1|0;r=I[e+1|0];if(I[e|0]==255){if(r>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8;break Mh}H[f+16>>2]=c;j=(r<<9)+j|0;h=7;break Mh}H[f+16>>2]=c;h=8;j=(r<<8)+j|0}h=h-1|0;j=j<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;b=d?m:!m;break Lh}j=j-(b<<16)|0;if(!(c&32768)){m=H[e+4>>2];b=b>>>0>c>>>0;H[l>>2]=H[e+(b?12:8)>>2];while(1){Nh:{if(h){break Nh}e=H[f+16>>2];d=e+1|0;r=I[e+1|0];if(I[e|0]==255){if(r>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8;break Nh}H[f+16>>2]=d;j=(r<<9)+j|0;h=7;break Nh}H[f+16>>2]=d;h=8;j=(r<<8)+j|0}h=h-1|0;j=j<<1;c=c<<1;if(c>>>0<32768){continue}break}b=b?!m:m;break Lh}b=H[e+4>>2]}H[q+768>>2]=(o|0)==(b|0)?i:k;H[u>>2]=H[u>>2]|16384;H[n+4>>2]=H[n+4>>2]|4096;H[n+260>>2]=H[n+260>>2]|4;H[n+268>>2]=H[n+268>>2]|1;b=b^o;H[n+264>>2]=H[n+264>>2]|b<<18|2;g=b<<28|g|8192}g=g|1073741824}H[n>>2]=g}g=n+4|0;b=q+4|0;p=p+1|0;if((p|0)!=64){continue}break}g=n+12|0;b=q+772|0;q=x>>>0<60;x=x+4|0;if(q){continue}break}break nh}b=1<>>1|b;q=H[f+120>>2];g=(q+(M<<2)|0)+12|0;b=H[f+128>>2];h=H[f+8>>2];c=H[f+4>>2];j=H[f>>2];l=H[f+104>>2];o=H[f+116>>2];Oh:{if(V&8){Ph:{if(b>>>0<4){break Ph}if(M){w=N(M,12);u=M<<3;q=0-k|0;D=f+28|0;while(1){x=0;while(1){n=g;g=H[g>>2];if(g){Qh:{if(g&2097168){break Qh}b=g&495;if(!b){break Qh}l=D+(I[b+H[f+108>>2]|0]<<2)|0;e=H[l>>2];b=H[e>>2];c=c-b|0;Rh:{if(j>>>16>>>0>=b>>>0){j=j-(b<<16)|0;s=H[e+4>>2];if(c&32768){break Rh}m=H[e+4>>2];b=b>>>0>c>>>0;H[l>>2]=H[e+(b?12:8)>>2];while(1){Sh:{if(h){break Sh}e=H[f+16>>2];d=e+1|0;r=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=d;h=8;j=(r<<8)+j|0;break Sh}if(r>>>0<=143){H[f+16>>2]=d;j=(r<<9)+j|0;h=7;break Sh}H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8}h=h-1|0;j=j<<1;c=c<<1;if(c>>>0<32768){continue}break}s=b?!m:m;break Rh}m=H[e+4>>2];d=b>>>0>c>>>0;H[l>>2]=H[e+(d?8:12)>>2];while(1){Th:{if(h){break Th}e=H[f+16>>2];c=e+1|0;r=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=c;h=8;j=(r<<8)+j|0;break Th}if(r>>>0<=143){H[f+16>>2]=c;j=(r<<9)+j|0;h=7;break Th}H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8}h=h-1|0;j=j<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;s=d?m:!m}if(s){r=n-4|0;d=H[n+4>>2]>>>17&4|(H[r>>2]>>>19&1|(g>>>14&16|(g>>>16&64|g&170)));l=D+(I[d+24384|0]<<2)|0;s=H[l>>2];b=H[s>>2];c=c-b|0;e=I[d+24640|0];Uh:{if(j>>>16>>>0>=b>>>0){j=j-(b<<16)|0;v=H[s+4>>2];if(c&32768){break Uh}m=H[s+4>>2];b=b>>>0>c>>>0;H[l>>2]=H[s+(b?12:8)>>2];while(1){Vh:{if(h){break Vh}h=H[f+16>>2];d=h+1|0;s=I[h+1|0];if(I[h|0]!=255){H[f+16>>2]=d;h=8;j=(s<<8)+j|0;break Vh}if(s>>>0<=143){H[f+16>>2]=d;j=(s<<9)+j|0;h=7;break Vh}H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8}h=h-1|0;j=j<<1;c=c<<1;if(c>>>0<32768){continue}break}v=b?!m:m;break Uh}m=H[s+4>>2];d=b>>>0>c>>>0;H[l>>2]=H[s+(d?8:12)>>2];while(1){Wh:{if(h){break Wh}h=H[f+16>>2];c=h+1|0;s=I[h+1|0];if(I[h|0]!=255){H[f+16>>2]=c;h=8;j=(s<<8)+j|0;break Wh}if(s>>>0<=143){H[f+16>>2]=c;j=(s<<9)+j|0;h=7;break Wh}H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8}h=h-1|0;j=j<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;v=d?m:!m}b=v;H[o>>2]=(e|0)==(b|0)?k:q;H[r>>2]=H[r>>2]|32;H[n+4>>2]=H[n+4>>2]|8;g=(b^e)<<19|g|16}g=g|2097152}if(!(!(g&3960)|g&16777344)){e=g>>>3|0;l=D+(I[H[f+108>>2]+(e&495)|0]<<2)|0;r=H[l>>2];b=H[r>>2];c=c-b|0;Xh:{if(j>>>16>>>0>=b>>>0){j=j-(b<<16)|0;s=H[r+4>>2];if(c&32768){break Xh}m=H[r+4>>2];b=b>>>0>c>>>0;H[l>>2]=H[r+(b?12:8)>>2];while(1){Yh:{if(h){break Yh}h=H[f+16>>2];d=h+1|0;r=I[h+1|0];if(I[h|0]!=255){H[f+16>>2]=d;h=8;j=(r<<8)+j|0;break Yh}if(r>>>0<=143){H[f+16>>2]=d;j=(r<<9)+j|0;h=7;break Yh}H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8}h=h-1|0;j=j<<1;c=c<<1;if(c>>>0<32768){continue}break}s=b?!m:m;break Xh}m=H[r+4>>2];d=b>>>0>c>>>0;H[l>>2]=H[r+(d?8:12)>>2];while(1){Zh:{if(h){break Zh}h=H[f+16>>2];c=h+1|0;r=I[h+1|0];if(I[h|0]!=255){H[f+16>>2]=c;h=8;j=(r<<8)+j|0;break Zh}if(r>>>0<=143){H[f+16>>2]=c;j=(r<<9)+j|0;h=7;break Zh}H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8}h=h-1|0;j=j<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;s=d?m:!m}if(s){s=n-4|0;d=H[n+4>>2]>>>20&4|(H[s>>2]>>>22&1|(g>>>15&16|(g>>>19&64|e&170)));l=D+(I[d+24384|0]<<2)|0;v=H[l>>2];b=H[v>>2];c=c-b|0;m=(M<<2)+o|0;e=I[d+24640|0];_h:{if(j>>>16>>>0>=b>>>0){j=j-(b<<16)|0;d=H[v+4>>2];if(c&32768){break _h}r=H[v+4>>2];b=b>>>0>c>>>0;H[l>>2]=H[v+(b?12:8)>>2];while(1){$h:{if(h){break $h}h=H[f+16>>2];d=h+1|0;v=I[h+1|0];if(I[h|0]!=255){H[f+16>>2]=d;h=8;j=(v<<8)+j|0;break $h}if(v>>>0<=143){H[f+16>>2]=d;j=(v<<9)+j|0;h=7;break $h}H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8}h=h-1|0;j=j<<1;c=c<<1;if(c>>>0<32768){continue}break}d=b?!r:r;break _h}r=H[v+4>>2];d=b>>>0>c>>>0;H[l>>2]=H[v+(d?8:12)>>2];while(1){ai:{if(h){break ai}h=H[f+16>>2];c=h+1|0;v=I[h+1|0];if(I[h|0]!=255){H[f+16>>2]=c;h=8;j=(v<<8)+j|0;break ai}if(v>>>0<=143){H[f+16>>2]=c;j=(v<<9)+j|0;h=7;break ai}H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8}h=h-1|0;j=j<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;d=d?r:!r}b=d;H[m>>2]=(e|0)==(b|0)?k:q;H[s>>2]=H[s>>2]|256;H[n+4>>2]=H[n+4>>2]|64;g=(b^e)<<22|g|128}g=g|16777216}if(!(!(g&31680)|g&134218752)){e=g>>>6|0;l=D+(I[H[f+108>>2]+(e&495)|0]<<2)|0;r=H[l>>2];b=H[r>>2];c=c-b|0;bi:{if(j>>>16>>>0>=b>>>0){j=j-(b<<16)|0;s=H[r+4>>2];if(c&32768){break bi}m=H[r+4>>2];b=b>>>0>c>>>0;H[l>>2]=H[r+(b?12:8)>>2];while(1){ci:{if(h){break ci}h=H[f+16>>2];d=h+1|0;r=I[h+1|0];if(I[h|0]!=255){H[f+16>>2]=d;h=8;j=(r<<8)+j|0;break ci}if(r>>>0<=143){H[f+16>>2]=d;j=(r<<9)+j|0;h=7;break ci}H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8}h=h-1|0;j=j<<1;c=c<<1;if(c>>>0<32768){continue}break}s=b?!m:m;break bi}m=H[r+4>>2];d=b>>>0>c>>>0;H[l>>2]=H[r+(d?8:12)>>2];while(1){di:{if(h){break di}h=H[f+16>>2];c=h+1|0;r=I[h+1|0];if(I[h|0]!=255){H[f+16>>2]=c;h=8;j=(r<<8)+j|0;break di}if(r>>>0<=143){H[f+16>>2]=c;j=(r<<9)+j|0;h=7;break di}H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8}h=h-1|0;j=j<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;s=d?m:!m}if(s){s=n-4|0;d=H[n+4>>2]>>>23&4|(H[s>>2]>>>25&1|(g>>>18&16|(g>>>22&64|e&170)));l=D+(I[d+24384|0]<<2)|0;v=H[l>>2];b=H[v>>2];c=c-b|0;m=o+u|0;e=I[d+24640|0];ei:{if(j>>>16>>>0>=b>>>0){j=j-(b<<16)|0;d=H[v+4>>2];if(c&32768){break ei}r=H[v+4>>2];b=b>>>0>c>>>0;H[l>>2]=H[v+(b?12:8)>>2];while(1){fi:{if(h){break fi}h=H[f+16>>2];d=h+1|0;v=I[h+1|0];if(I[h|0]!=255){H[f+16>>2]=d;h=8;j=(v<<8)+j|0;break fi}if(v>>>0<=143){H[f+16>>2]=d;j=(v<<9)+j|0;h=7;break fi}H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8}h=h-1|0;j=j<<1;c=c<<1;if(c>>>0<32768){continue}break}d=b?!r:r;break ei}r=H[v+4>>2];d=b>>>0>c>>>0;H[l>>2]=H[v+(d?8:12)>>2];while(1){gi:{if(h){break gi}h=H[f+16>>2];c=h+1|0;v=I[h+1|0];if(I[h|0]!=255){H[f+16>>2]=c;h=8;j=(v<<8)+j|0;break gi}if(v>>>0<=143){H[f+16>>2]=c;j=(v<<9)+j|0;h=7;break gi}H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8}h=h-1|0;j=j<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;d=d?r:!r}b=d;H[m>>2]=(e|0)==(b|0)?k:q;H[s>>2]=H[s>>2]|2048;H[n+4>>2]=H[n+4>>2]|512;g=(b^e)<<25|g|1024}g=g|134217728}if(!(!(g&253440)|g&1073750016)){e=g>>>9|0;l=D+(I[H[f+108>>2]+(e&495)|0]<<2)|0;r=H[l>>2];b=H[r>>2];c=c-b|0;hi:{if(j>>>16>>>0>=b>>>0){j=j-(b<<16)|0;s=H[r+4>>2];if(c&32768){break hi}m=H[r+4>>2];b=b>>>0>c>>>0;H[l>>2]=H[r+(b?12:8)>>2];while(1){ii:{if(h){break ii}h=H[f+16>>2];d=h+1|0;r=I[h+1|0];if(I[h|0]!=255){H[f+16>>2]=d;h=8;j=(r<<8)+j|0;break ii}if(r>>>0<=143){H[f+16>>2]=d;j=(r<<9)+j|0;h=7;break ii}H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8}h=h-1|0;j=j<<1;c=c<<1;if(c>>>0<32768){continue}break}s=b?!m:m;break hi}m=H[r+4>>2];d=b>>>0>c>>>0;H[l>>2]=H[r+(d?8:12)>>2];while(1){ji:{if(h){break ji}h=H[f+16>>2];c=h+1|0;r=I[h+1|0];if(I[h|0]!=255){H[f+16>>2]=c;h=8;j=(r<<8)+j|0;break ji}if(r>>>0<=143){H[f+16>>2]=c;j=(r<<9)+j|0;h=7;break ji}H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8}h=h-1|0;j=j<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;s=d?m:!m}if(s){s=n-4|0;d=H[n+4>>2]>>>26&4|(H[s>>2]>>>28&1|(g>>>21&16|(g>>>25&64|e&170)));l=D+(I[d+24384|0]<<2)|0;v=H[l>>2];b=H[v>>2];c=c-b|0;m=o+w|0;e=I[d+24640|0];ki:{if(j>>>16>>>0>=b>>>0){j=j-(b<<16)|0;d=H[v+4>>2];if(c&32768){break ki}r=H[v+4>>2];b=b>>>0>c>>>0;H[l>>2]=H[v+(b?12:8)>>2];while(1){li:{if(h){break li}h=H[f+16>>2];d=h+1|0;v=I[h+1|0];if(I[h|0]!=255){H[f+16>>2]=d;h=8;j=(v<<8)+j|0;break li}if(v>>>0<=143){H[f+16>>2]=d;j=(v<<9)+j|0;h=7;break li}H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8}h=h-1|0;j=j<<1;c=c<<1;if(c>>>0<32768){continue}break}d=b?!r:r;break ki}r=H[v+4>>2];d=b>>>0>c>>>0;H[l>>2]=H[v+(d?8:12)>>2];while(1){mi:{if(h){break mi}h=H[f+16>>2];c=h+1|0;v=I[h+1|0];if(I[h|0]!=255){H[f+16>>2]=c;h=8;j=(v<<8)+j|0;break mi}if(v>>>0<=143){H[f+16>>2]=c;j=(v<<9)+j|0;h=7;break mi}H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8}h=h-1|0;j=j<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;d=d?r:!r}b=d;H[m>>2]=(e|0)==(b|0)?k:q;H[s>>2]=H[s>>2]|16384;H[n+4>>2]=H[n+4>>2]|4096;d=n+(H[f+124>>2]<<2)|0;H[d+4>>2]=H[d+4>>2]|4;H[d+12>>2]=H[d+12>>2]|1;b=b^e;H[d+8>>2]=H[d+8>>2]|b<<18|2;g=b<<28|g|8192}g=g|1073741824}H[n>>2]=g}g=n+4|0;o=o+4|0;x=x+1|0;if((M|0)!=(x|0)){continue}break}g=n+12|0;o=o+w|0;i=i+4|0;b=H[f+128>>2];if(i>>>0<(b&-4)>>>0){continue}break}break Ph}i=b&-4;g=(q+(i<<1)|0)+12|0}H[f+8>>2]=h;H[f+4>>2]=c;H[f>>2]=j;H[f+104>>2]=l;if(!M|b>>>0<=i>>>0){break Oh}while(1){c=(b|0)==(i|0);h=0;b=i;if(!c){while(1){Xb(f,g,(N(h,M)<<2)+o|0,k,h,H[f+124>>2]+2|0,1);h=h+1|0;b=H[f+128>>2];if(h>>>0>>0){continue}break}}g=g+4|0;o=o+4|0;p=p+1|0;if((M|0)!=(p|0)){continue}break}break Oh}ni:{if(b>>>0<4){break ni}if(M){w=N(M,12);u=M<<3;q=0-k|0;D=f+28|0;while(1){x=0;while(1){n=g;g=H[g>>2];if(g){oi:{if(g&2097168){break oi}b=g&495;if(!b){break oi}l=D+(I[b+H[f+108>>2]|0]<<2)|0;e=H[l>>2];b=H[e>>2];c=c-b|0;pi:{if(j>>>16>>>0>=b>>>0){j=j-(b<<16)|0;s=H[e+4>>2];if(c&32768){break pi}m=H[e+4>>2];b=b>>>0>c>>>0;H[l>>2]=H[e+(b?12:8)>>2];while(1){qi:{if(h){break qi}e=H[f+16>>2];d=e+1|0;r=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=d;h=8;j=(r<<8)+j|0;break qi}if(r>>>0<=143){H[f+16>>2]=d;j=(r<<9)+j|0;h=7;break qi}H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8}h=h-1|0;j=j<<1;c=c<<1;if(c>>>0<32768){continue}break}s=b?!m:m;break pi}m=H[e+4>>2];d=b>>>0>c>>>0;H[l>>2]=H[e+(d?8:12)>>2];while(1){ri:{if(h){break ri}e=H[f+16>>2];c=e+1|0;r=I[e+1|0];if(I[e|0]!=255){H[f+16>>2]=c;h=8;j=(r<<8)+j|0;break ri}if(r>>>0<=143){H[f+16>>2]=c;j=(r<<9)+j|0;h=7;break ri}H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8}h=h-1|0;j=j<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;s=d?m:!m}if(s){r=n-4|0;d=H[n+4>>2]>>>17&4|(H[r>>2]>>>19&1|(g>>>14&16|(g>>>16&64|g&170)));l=D+(I[d+24384|0]<<2)|0;s=H[l>>2];b=H[s>>2];c=c-b|0;e=I[d+24640|0];si:{if(j>>>16>>>0>=b>>>0){j=j-(b<<16)|0;d=H[s+4>>2];if(c&32768){break si}m=H[s+4>>2];b=b>>>0>c>>>0;H[l>>2]=H[s+(b?12:8)>>2];while(1){ti:{if(h){break ti}h=H[f+16>>2];d=h+1|0;s=I[h+1|0];if(I[h|0]!=255){H[f+16>>2]=d;h=8;j=(s<<8)+j|0;break ti}if(s>>>0<=143){H[f+16>>2]=d;j=(s<<9)+j|0;h=7;break ti}H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8}h=h-1|0;j=j<<1;c=c<<1;if(c>>>0<32768){continue}break}d=b?!m:m;break si}m=H[s+4>>2];d=b>>>0>c>>>0;H[l>>2]=H[s+(d?8:12)>>2];while(1){ui:{if(h){break ui}h=H[f+16>>2];c=h+1|0;s=I[h+1|0];if(I[h|0]!=255){H[f+16>>2]=c;h=8;j=(s<<8)+j|0;break ui}if(s>>>0<=143){H[f+16>>2]=c;j=(s<<9)+j|0;h=7;break ui}H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8}h=h-1|0;j=j<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;d=d?m:!m}H[o>>2]=(e|0)==(d|0)?k:q;H[r>>2]=H[r>>2]|32;H[n+4>>2]=H[n+4>>2]|8;b=n+(-2-H[f+124>>2]<<2)|0;H[b+4>>2]=H[b+4>>2]|32768;d=d^e;H[b>>2]=H[b>>2]|d<<31|65536;b=b-4|0;H[b>>2]=H[b>>2]|131072;g=d<<19|g|16}g=g|2097152}if(!(!(g&3960)|g&16777344)){e=g>>>3|0;l=D+(I[H[f+108>>2]+(e&495)|0]<<2)|0;r=H[l>>2];b=H[r>>2];c=c-b|0;vi:{if(j>>>16>>>0>=b>>>0){j=j-(b<<16)|0;s=H[r+4>>2];if(c&32768){break vi}m=H[r+4>>2];b=b>>>0>c>>>0;H[l>>2]=H[r+(b?12:8)>>2];while(1){wi:{if(h){break wi}h=H[f+16>>2];d=h+1|0;r=I[h+1|0];if(I[h|0]!=255){H[f+16>>2]=d;h=8;j=(r<<8)+j|0;break wi}if(r>>>0<=143){H[f+16>>2]=d;j=(r<<9)+j|0;h=7;break wi}H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8}h=h-1|0;j=j<<1;c=c<<1;if(c>>>0<32768){continue}break}s=b?!m:m;break vi}m=H[r+4>>2];d=b>>>0>c>>>0;H[l>>2]=H[r+(d?8:12)>>2];while(1){xi:{if(h){break xi}h=H[f+16>>2];c=h+1|0;r=I[h+1|0];if(I[h|0]!=255){H[f+16>>2]=c;h=8;j=(r<<8)+j|0;break xi}if(r>>>0<=143){H[f+16>>2]=c;j=(r<<9)+j|0;h=7;break xi}H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8}h=h-1|0;j=j<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;s=d?m:!m}if(s){s=n-4|0;d=H[n+4>>2]>>>20&4|(H[s>>2]>>>22&1|(g>>>15&16|(g>>>19&64|e&170)));l=D+(I[d+24384|0]<<2)|0;v=H[l>>2];b=H[v>>2];c=c-b|0;m=(M<<2)+o|0;e=I[d+24640|0];yi:{if(j>>>16>>>0>=b>>>0){j=j-(b<<16)|0;d=H[v+4>>2];if(c&32768){break yi}r=H[v+4>>2];b=b>>>0>c>>>0;H[l>>2]=H[v+(b?12:8)>>2];while(1){zi:{if(h){break zi}h=H[f+16>>2];d=h+1|0;v=I[h+1|0];if(I[h|0]!=255){H[f+16>>2]=d;h=8;j=(v<<8)+j|0;break zi}if(v>>>0<=143){H[f+16>>2]=d;j=(v<<9)+j|0;h=7;break zi}H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8}h=h-1|0;j=j<<1;c=c<<1;if(c>>>0<32768){continue}break}d=b?!r:r;break yi}r=H[v+4>>2];d=b>>>0>c>>>0;H[l>>2]=H[v+(d?8:12)>>2];while(1){Ai:{if(h){break Ai}h=H[f+16>>2];c=h+1|0;v=I[h+1|0];if(I[h|0]!=255){H[f+16>>2]=c;h=8;j=(v<<8)+j|0;break Ai}if(v>>>0<=143){H[f+16>>2]=c;j=(v<<9)+j|0;h=7;break Ai}H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8}h=h-1|0;j=j<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;d=d?r:!r}b=d;H[m>>2]=(e|0)==(b|0)?k:q;H[s>>2]=H[s>>2]|256;H[n+4>>2]=H[n+4>>2]|64;g=(b^e)<<22|g|128}g=g|16777216}if(!(!(g&31680)|g&134218752)){e=g>>>6|0;l=D+(I[H[f+108>>2]+(e&495)|0]<<2)|0;r=H[l>>2];b=H[r>>2];c=c-b|0;Bi:{if(j>>>16>>>0>=b>>>0){j=j-(b<<16)|0;s=H[r+4>>2];if(c&32768){break Bi}m=H[r+4>>2];b=b>>>0>c>>>0;H[l>>2]=H[r+(b?12:8)>>2];while(1){Ci:{if(h){break Ci}h=H[f+16>>2];d=h+1|0;r=I[h+1|0];if(I[h|0]!=255){H[f+16>>2]=d;h=8;j=(r<<8)+j|0;break Ci}if(r>>>0<=143){H[f+16>>2]=d;j=(r<<9)+j|0;h=7;break Ci}H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8}h=h-1|0;j=j<<1;c=c<<1;if(c>>>0<32768){continue}break}s=b?!m:m;break Bi}m=H[r+4>>2];d=b>>>0>c>>>0;H[l>>2]=H[r+(d?8:12)>>2];while(1){Di:{if(h){break Di}h=H[f+16>>2];c=h+1|0;r=I[h+1|0];if(I[h|0]!=255){H[f+16>>2]=c;h=8;j=(r<<8)+j|0;break Di}if(r>>>0<=143){H[f+16>>2]=c;j=(r<<9)+j|0;h=7;break Di}H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8}h=h-1|0;j=j<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;s=d?m:!m}if(s){s=n-4|0;d=H[n+4>>2]>>>23&4|(H[s>>2]>>>25&1|(g>>>18&16|(g>>>22&64|e&170)));l=D+(I[d+24384|0]<<2)|0;v=H[l>>2];b=H[v>>2];c=c-b|0;m=o+u|0;e=I[d+24640|0];Ei:{if(j>>>16>>>0>=b>>>0){j=j-(b<<16)|0;d=H[v+4>>2];if(c&32768){break Ei}r=H[v+4>>2];b=b>>>0>c>>>0;H[l>>2]=H[v+(b?12:8)>>2];while(1){Fi:{if(h){break Fi}h=H[f+16>>2];d=h+1|0;v=I[h+1|0];if(I[h|0]!=255){H[f+16>>2]=d;h=8;j=(v<<8)+j|0;break Fi}if(v>>>0<=143){H[f+16>>2]=d;j=(v<<9)+j|0;h=7;break Fi}H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8}h=h-1|0;j=j<<1;c=c<<1;if(c>>>0<32768){continue}break}d=b?!r:r;break Ei}r=H[v+4>>2];d=b>>>0>c>>>0;H[l>>2]=H[v+(d?8:12)>>2];while(1){Gi:{if(h){break Gi}h=H[f+16>>2];c=h+1|0;v=I[h+1|0];if(I[h|0]!=255){H[f+16>>2]=c;h=8;j=(v<<8)+j|0;break Gi}if(v>>>0<=143){H[f+16>>2]=c;j=(v<<9)+j|0;h=7;break Gi}H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8}h=h-1|0;j=j<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;d=d?r:!r}b=d;H[m>>2]=(e|0)==(b|0)?k:q;H[s>>2]=H[s>>2]|2048;H[n+4>>2]=H[n+4>>2]|512;g=(b^e)<<25|g|1024}g=g|134217728}if(!(!(g&253440)|g&1073750016)){e=g>>>9|0;l=D+(I[H[f+108>>2]+(e&495)|0]<<2)|0;r=H[l>>2];b=H[r>>2];c=c-b|0;Hi:{if(j>>>16>>>0>=b>>>0){j=j-(b<<16)|0;s=H[r+4>>2];if(c&32768){break Hi}m=H[r+4>>2];b=b>>>0>c>>>0;H[l>>2]=H[r+(b?12:8)>>2];while(1){Ii:{if(h){break Ii}h=H[f+16>>2];d=h+1|0;r=I[h+1|0];if(I[h|0]!=255){H[f+16>>2]=d;h=8;j=(r<<8)+j|0;break Ii}if(r>>>0<=143){H[f+16>>2]=d;j=(r<<9)+j|0;h=7;break Ii}H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8}h=h-1|0;j=j<<1;c=c<<1;if(c>>>0<32768){continue}break}s=b?!m:m;break Hi}m=H[r+4>>2];d=b>>>0>c>>>0;H[l>>2]=H[r+(d?8:12)>>2];while(1){Ji:{if(h){break Ji}h=H[f+16>>2];c=h+1|0;r=I[h+1|0];if(I[h|0]!=255){H[f+16>>2]=c;h=8;j=(r<<8)+j|0;break Ji}if(r>>>0<=143){H[f+16>>2]=c;j=(r<<9)+j|0;h=7;break Ji}H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8}h=h-1|0;j=j<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;s=d?m:!m}if(s){s=n-4|0;d=H[n+4>>2]>>>26&4|(H[s>>2]>>>28&1|(g>>>21&16|(g>>>25&64|e&170)));l=D+(I[d+24384|0]<<2)|0;v=H[l>>2];b=H[v>>2];c=c-b|0;m=o+w|0;e=I[d+24640|0];Ki:{if(j>>>16>>>0>=b>>>0){j=j-(b<<16)|0;d=H[v+4>>2];if(c&32768){break Ki}r=H[v+4>>2];b=b>>>0>c>>>0;H[l>>2]=H[v+(b?12:8)>>2];while(1){Li:{if(h){break Li}h=H[f+16>>2];d=h+1|0;v=I[h+1|0];if(I[h|0]!=255){H[f+16>>2]=d;h=8;j=(v<<8)+j|0;break Li}if(v>>>0<=143){H[f+16>>2]=d;j=(v<<9)+j|0;h=7;break Li}H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8}h=h-1|0;j=j<<1;c=c<<1;if(c>>>0<32768){continue}break}d=b?!r:r;break Ki}r=H[v+4>>2];d=b>>>0>c>>>0;H[l>>2]=H[v+(d?8:12)>>2];while(1){Mi:{if(h){break Mi}h=H[f+16>>2];c=h+1|0;v=I[h+1|0];if(I[h|0]!=255){H[f+16>>2]=c;h=8;j=(v<<8)+j|0;break Mi}if(v>>>0<=143){H[f+16>>2]=c;j=(v<<9)+j|0;h=7;break Mi}H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8}h=h-1|0;j=j<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;d=d?r:!r}b=d;H[m>>2]=(e|0)==(b|0)?k:q;H[s>>2]=H[s>>2]|16384;H[n+4>>2]=H[n+4>>2]|4096;d=n+(H[f+124>>2]<<2)|0;H[d+4>>2]=H[d+4>>2]|4;H[d+12>>2]=H[d+12>>2]|1;b=b^e;H[d+8>>2]=H[d+8>>2]|b<<18|2;g=b<<28|g|8192}g=g|1073741824}H[n>>2]=g}g=n+4|0;o=o+4|0;x=x+1|0;if((M|0)!=(x|0)){continue}break}g=n+12|0;o=o+w|0;i=i+4|0;b=H[f+128>>2];if(i>>>0<(b&-4)>>>0){continue}break}break ni}i=b&-4;g=(q+(i<<1)|0)+12|0}H[f+8>>2]=h;H[f+4>>2]=c;H[f>>2]=j;H[f+104>>2]=l;if(!M|b>>>0<=i>>>0){break Oh}while(1){c=(b|0)==(i|0);h=0;b=i;if(!c){while(1){Xb(f,g,(N(h,M)<<2)+o|0,k,h,H[f+124>>2]+2|0,0);h=h+1|0;b=H[f+128>>2];if(h>>>0>>0){continue}break}}g=g+4|0;o=o+4|0;p=p+1|0;if((M|0)!=(p|0)){continue}break}}break mh}while(1){p=0;while(1){q=b;n=g;g=H[g>>2];if(g){Ni:{if(g&2097168){break Ni}b=g&495;if(!b){break Ni}l=s+(I[b+H[f+108>>2]|0]<<2)|0;o=H[l>>2];b=H[o>>2];c=c-b|0;Oi:{if(j>>>16>>>0>>0){e=H[o+4>>2];d=b>>>0>c>>>0;H[l>>2]=H[o+(d?8:12)>>2];while(1){Pi:{if(h){break Pi}o=H[f+16>>2];c=o+1|0;m=I[o+1|0];if(I[o|0]==255){if(m>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8;break Pi}H[f+16>>2]=c;j=(m<<9)+j|0;h=7;break Pi}H[f+16>>2]=c;h=8;j=(m<<8)+j|0}h=h-1|0;j=j<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;b=d?e:!e;break Oi}j=j-(b<<16)|0;if(!(c&32768)){e=H[o+4>>2];b=b>>>0>c>>>0;H[l>>2]=H[o+(b?12:8)>>2];while(1){Qi:{if(h){break Qi}o=H[f+16>>2];d=o+1|0;m=I[o+1|0];if(I[o|0]==255){if(m>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8;break Qi}H[f+16>>2]=d;j=(m<<9)+j|0;h=7;break Qi}H[f+16>>2]=d;h=8;j=(m<<8)+j|0}h=h-1|0;j=j<<1;c=c<<1;if(c>>>0<32768){continue}break}b=b?!e:e;break Oi}b=H[o+4>>2]}if(b){u=n-4|0;d=H[n+4>>2]>>>17&4|(H[u>>2]>>>19&1|(g>>>14&16|(g>>>16&64|g&170)));l=s+(I[d+24384|0]<<2)|0;e=H[l>>2];b=H[e>>2];c=c-b|0;o=I[d+24640|0];Ri:{if(j>>>16>>>0>>0){m=H[e+4>>2];d=b>>>0>c>>>0;H[l>>2]=H[e+(d?8:12)>>2];while(1){Si:{if(h){break Si}e=H[f+16>>2];c=e+1|0;r=I[e+1|0];if(I[e|0]==255){if(r>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8;break Si}H[f+16>>2]=c;j=(r<<9)+j|0;h=7;break Si}H[f+16>>2]=c;h=8;j=(r<<8)+j|0}h=h-1|0;j=j<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;b=d?m:!m;break Ri}j=j-(b<<16)|0;if(!(c&32768)){m=H[e+4>>2];b=b>>>0>c>>>0;H[l>>2]=H[e+(b?12:8)>>2];while(1){Ti:{if(h){break Ti}e=H[f+16>>2];d=e+1|0;r=I[e+1|0];if(I[e|0]==255){if(r>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8;break Ti}H[f+16>>2]=d;j=(r<<9)+j|0;h=7;break Ti}H[f+16>>2]=d;h=8;j=(r<<8)+j|0}h=h-1|0;j=j<<1;c=c<<1;if(c>>>0<32768){continue}break}b=b?!m:m;break Ri}b=H[e+4>>2]}H[q>>2]=(o|0)==(b|0)?i:k;H[u>>2]=H[u>>2]|32;H[n+4>>2]=H[n+4>>2]|8;g=(b^o)<<19|g|16}g=g|2097152}if(!(!(g&3960)|g&16777344)){o=g>>>3|0;l=s+(I[H[f+108>>2]+(o&495)|0]<<2)|0;e=H[l>>2];b=H[e>>2];c=c-b|0;Ui:{if(j>>>16>>>0>>0){m=H[e+4>>2];d=b>>>0>c>>>0;H[l>>2]=H[e+(d?8:12)>>2];while(1){Vi:{if(h){break Vi}e=H[f+16>>2];c=e+1|0;u=I[e+1|0];if(I[e|0]==255){if(u>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8;break Vi}H[f+16>>2]=c;j=(u<<9)+j|0;h=7;break Vi}H[f+16>>2]=c;h=8;j=(u<<8)+j|0}h=h-1|0;j=j<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;b=d?m:!m;break Ui}j=j-(b<<16)|0;if(!(c&32768)){m=H[e+4>>2];b=b>>>0>c>>>0;H[l>>2]=H[e+(b?12:8)>>2];while(1){Wi:{if(h){break Wi}e=H[f+16>>2];d=e+1|0;u=I[e+1|0];if(I[e|0]==255){if(u>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8;break Wi}H[f+16>>2]=d;j=(u<<9)+j|0;h=7;break Wi}H[f+16>>2]=d;h=8;j=(u<<8)+j|0}h=h-1|0;j=j<<1;c=c<<1;if(c>>>0<32768){continue}break}b=b?!m:m;break Ui}b=H[e+4>>2]}if(b){u=n-4|0;d=H[n+4>>2]>>>20&4|(H[u>>2]>>>22&1|(g>>>15&16|(g>>>19&64|o&170)));l=s+(I[d+24384|0]<<2)|0;e=H[l>>2];b=H[e>>2];c=c-b|0;o=I[d+24640|0];Xi:{if(j>>>16>>>0>>0){m=H[e+4>>2];d=b>>>0>c>>>0;H[l>>2]=H[e+(d?8:12)>>2];while(1){Yi:{if(h){break Yi}e=H[f+16>>2];c=e+1|0;r=I[e+1|0];if(I[e|0]==255){if(r>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8;break Yi}H[f+16>>2]=c;j=(r<<9)+j|0;h=7;break Yi}H[f+16>>2]=c;h=8;j=(r<<8)+j|0}h=h-1|0;j=j<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;b=d?m:!m;break Xi}j=j-(b<<16)|0;if(!(c&32768)){m=H[e+4>>2];b=b>>>0>c>>>0;H[l>>2]=H[e+(b?12:8)>>2];while(1){Zi:{if(h){break Zi}e=H[f+16>>2];d=e+1|0;r=I[e+1|0];if(I[e|0]==255){if(r>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8;break Zi}H[f+16>>2]=d;j=(r<<9)+j|0;h=7;break Zi}H[f+16>>2]=d;h=8;j=(r<<8)+j|0}h=h-1|0;j=j<<1;c=c<<1;if(c>>>0<32768){continue}break}b=b?!m:m;break Xi}b=H[e+4>>2]}H[q+256>>2]=(o|0)==(b|0)?i:k;H[u>>2]=H[u>>2]|256;H[n+4>>2]=H[n+4>>2]|64;g=(b^o)<<22|g|128}g=g|16777216}if(!(!(g&31680)|g&134218752)){o=g>>>6|0;l=s+(I[H[f+108>>2]+(o&495)|0]<<2)|0;e=H[l>>2];b=H[e>>2];c=c-b|0;_i:{if(j>>>16>>>0>>0){m=H[e+4>>2];d=b>>>0>c>>>0;H[l>>2]=H[e+(d?8:12)>>2];while(1){$i:{if(h){break $i}e=H[f+16>>2];c=e+1|0;u=I[e+1|0];if(I[e|0]==255){if(u>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8;break $i}H[f+16>>2]=c;j=(u<<9)+j|0;h=7;break $i}H[f+16>>2]=c;h=8;j=(u<<8)+j|0}h=h-1|0;j=j<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;b=d?m:!m;break _i}j=j-(b<<16)|0;if(!(c&32768)){m=H[e+4>>2];b=b>>>0>c>>>0;H[l>>2]=H[e+(b?12:8)>>2];while(1){aj:{if(h){break aj}e=H[f+16>>2];d=e+1|0;u=I[e+1|0];if(I[e|0]==255){if(u>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8;break aj}H[f+16>>2]=d;j=(u<<9)+j|0;h=7;break aj}H[f+16>>2]=d;h=8;j=(u<<8)+j|0}h=h-1|0;j=j<<1;c=c<<1;if(c>>>0<32768){continue}break}b=b?!m:m;break _i}b=H[e+4>>2]}if(b){u=n-4|0;d=H[n+4>>2]>>>23&4|(H[u>>2]>>>25&1|(g>>>18&16|(g>>>22&64|o&170)));l=s+(I[d+24384|0]<<2)|0;e=H[l>>2];b=H[e>>2];c=c-b|0;o=I[d+24640|0];bj:{if(j>>>16>>>0>>0){m=H[e+4>>2];d=b>>>0>c>>>0;H[l>>2]=H[e+(d?8:12)>>2];while(1){cj:{if(h){break cj}e=H[f+16>>2];c=e+1|0;r=I[e+1|0];if(I[e|0]==255){if(r>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8;break cj}H[f+16>>2]=c;j=(r<<9)+j|0;h=7;break cj}H[f+16>>2]=c;h=8;j=(r<<8)+j|0}h=h-1|0;j=j<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;b=d?m:!m;break bj}j=j-(b<<16)|0;if(!(c&32768)){m=H[e+4>>2];b=b>>>0>c>>>0;H[l>>2]=H[e+(b?12:8)>>2];while(1){dj:{if(h){break dj}e=H[f+16>>2];d=e+1|0;r=I[e+1|0];if(I[e|0]==255){if(r>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8;break dj}H[f+16>>2]=d;j=(r<<9)+j|0;h=7;break dj}H[f+16>>2]=d;h=8;j=(r<<8)+j|0}h=h-1|0;j=j<<1;c=c<<1;if(c>>>0<32768){continue}break}b=b?!m:m;break bj}b=H[e+4>>2]}H[q+512>>2]=(o|0)==(b|0)?i:k;H[u>>2]=H[u>>2]|2048;H[n+4>>2]=H[n+4>>2]|512;g=(b^o)<<25|g|1024}g=g|134217728}if(!(!(g&253440)|g&1073750016)){o=g>>>9|0;l=s+(I[H[f+108>>2]+(o&495)|0]<<2)|0;e=H[l>>2];b=H[e>>2];c=c-b|0;ej:{if(j>>>16>>>0>>0){m=H[e+4>>2];d=b>>>0>c>>>0;H[l>>2]=H[e+(d?8:12)>>2];while(1){fj:{if(h){break fj}e=H[f+16>>2];c=e+1|0;u=I[e+1|0];if(I[e|0]==255){if(u>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8;break fj}H[f+16>>2]=c;j=(u<<9)+j|0;h=7;break fj}H[f+16>>2]=c;h=8;j=(u<<8)+j|0}h=h-1|0;j=j<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;b=d?m:!m;break ej}j=j-(b<<16)|0;if(!(c&32768)){m=H[e+4>>2];b=b>>>0>c>>>0;H[l>>2]=H[e+(b?12:8)>>2];while(1){gj:{if(h){break gj}e=H[f+16>>2];d=e+1|0;u=I[e+1|0];if(I[e|0]==255){if(u>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8;break gj}H[f+16>>2]=d;j=(u<<9)+j|0;h=7;break gj}H[f+16>>2]=d;h=8;j=(u<<8)+j|0}h=h-1|0;j=j<<1;c=c<<1;if(c>>>0<32768){continue}break}b=b?!m:m;break ej}b=H[e+4>>2]}if(b){u=n-4|0;d=H[n+4>>2]>>>26&4|(H[u>>2]>>>28&1|(g>>>21&16|(g>>>25&64|o&170)));l=s+(I[d+24384|0]<<2)|0;e=H[l>>2];b=H[e>>2];c=c-b|0;o=I[d+24640|0];hj:{if(j>>>16>>>0>>0){m=H[e+4>>2];d=b>>>0>c>>>0;H[l>>2]=H[e+(d?8:12)>>2];while(1){ij:{if(h){break ij}e=H[f+16>>2];c=e+1|0;r=I[e+1|0];if(I[e|0]==255){if(r>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8;break ij}H[f+16>>2]=c;j=(r<<9)+j|0;h=7;break ij}H[f+16>>2]=c;h=8;j=(r<<8)+j|0}h=h-1|0;j=j<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;b=d?m:!m;break hj}j=j-(b<<16)|0;if(!(c&32768)){m=H[e+4>>2];b=b>>>0>c>>>0;H[l>>2]=H[e+(b?12:8)>>2];while(1){jj:{if(h){break jj}e=H[f+16>>2];d=e+1|0;r=I[e+1|0];if(I[e|0]==255){if(r>>>0>=144){H[f+12>>2]=H[f+12>>2]+1;j=j+65280|0;h=8;break jj}H[f+16>>2]=d;j=(r<<9)+j|0;h=7;break jj}H[f+16>>2]=d;h=8;j=(r<<8)+j|0}h=h-1|0;j=j<<1;c=c<<1;if(c>>>0<32768){continue}break}b=b?!m:m;break hj}b=H[e+4>>2]}H[q+768>>2]=(o|0)==(b|0)?i:k;H[u>>2]=H[u>>2]|16384;H[n+4>>2]=H[n+4>>2]|4096;H[n+260>>2]=H[n+260>>2]|4;H[n+268>>2]=H[n+268>>2]|1;b=b^o;H[n+264>>2]=H[n+264>>2]|b<<18|2;g=b<<28|g|8192}g=g|1073741824}H[n>>2]=g}g=n+4|0;b=q+4|0;p=p+1|0;if((p|0)!=64){continue}break}g=n+12|0;b=q+772|0;q=x>>>0<60;x=x+4|0;if(q){continue}break}}H[f+8>>2]=h;H[f+4>>2]=c;H[f>>2]=j;H[f+104>>2]=l}}if(aa){break bb}Yb(f);Xa(f,18,46);Xa(f,17,3);Xa(f,0,4)}b=la+1|0;c=(b|0)==3;la=c?0:b;z=z-c|0;ba=ba+1|0;if(ba>>>0>=K[ma+8>>2]){break ab}if((z|0)>0){continue}break}}U=R+U|0;c=H[f+24>>2];b=J[f+112>>1];F[c|0]=b;F[c+1|0]=b>>>8;X=X+1|0;if(X>>>0>2]){continue}break}}kj:{if(!E){break kj}lj:{c=H[f+24>>2];g=H[f+16>>2];if(c>>>0>g+2>>>0){if(!_){break lj}g=H[f+16>>2];c=H[f+24>>2];b=H[f+20>>2];H[Y+56>>2]=c-b;H[Y+52>>2]=g-b;H[Y+48>>2]=(c-g|0)-2;Ba(S,2,15235,Y+48|0);break kj}b=H[f+12>>2];if(b>>>0<3){break kj}if(_){H[Y+80>>2]=H[f+12>>2];Ba(S,2,7107,Y+80|0);break kj}H[Y+64>>2]=b;Ba(S,2,7107,Y- -64|0);break kj}b=H[f+20>>2];H[Y+40>>2]=c-b;H[Y+36>>2]=g-b;H[Y+32>>2]=(c-g|0)-2;Ba(S,2,15235,Y+32|0)}if(!H[C+60>>2]){break i}H[f+116>>2]=Z}k=H[qa+4>>2];g=H[C+12>>2];l=H[C+8>>2]-H[qa>>2]|0;c=H[qa+16>>2];if(c&1){b=H[oa+28>>2]+N(ua,152)|0;l=(H[b-144>>2]+l|0)-H[b-152>>2]|0}i=g-k|0;if(c&2){b=H[oa+28>>2]+N(ua,152)|0;i=(H[b-140>>2]+i|0)-H[b-148>>2]|0}d=H[C+60>>2];t=d?d:H[f+116>>2];p=H[f+128>>2];u=H[f+124>>2];n=H[pa+808>>2];mj:{if(!n){break mj}b=!p|!u;if((n|0)<=30){if(b){break mj}e=0;while(1){k=(N(e,u)<<2)+t|0;b=0;while(1){g=k+(b<<2)|0;q=H[g>>2];c=q>>31;c=(c^q)-c|0;if(c>>>n|0){c=c>>>H[pa+808>>2]|0;H[g>>2]=(q|0)<0?0-c|0:c}b=b+1|0;if((u|0)!=(b|0)){continue}break}e=e+1|0;if((p|0)!=(e|0)){continue}break}break mj}if(b){break mj}b=N(p,u)<<2;if(!b){break mj}y(t,0,b)}if(d){b=N(p,u);if(H[pa+20>>2]==1){if(!b){break a}f=0;if((b|0)!=1){c=b&1;b=b&-2;g=0;while(1){k=(f<<2)+t|0;H[k>>2]=H[k>>2]/2;H[k+4>>2]=H[k+4>>2]/2;f=f+2|0;g=g+2|0;if((b|0)!=(g|0)){continue}break}if(!c){break a}}b=(f<<2)+t|0;H[b>>2]=H[b>>2]/2;break a}if(!b){break a}ga=O(L[qa+32>>2]*O(.5));g=b&3;if(b>>>0>=4){c=b&-4;b=0;while(1){L[t>>2]=ga*O(H[t>>2]);L[t+4>>2]=ga*O(H[t+4>>2]);L[t+8>>2]=ga*O(H[t+8>>2]);L[t+12>>2]=ga*O(H[t+12>>2]);t=t+16|0;b=b+4|0;if((c|0)!=(b|0)){continue}break}if(!g){break a}}b=0;while(1){L[t>>2]=ga*O(H[t>>2]);t=t+4|0;b=b+1|0;if((g|0)!=(b|0)){continue}break}break a}m=wa-va|0;if(H[pa+20>>2]==1){if(!p){break a}j=(H[oa+36>>2]+(N(i,m)<<2)|0)+(l<<2)|0;o=u&-4;i=0;while(1){b=0;if(o){d=j+(N(i,m)<<2)|0;n=(N(i,u)<<2)+t|0;while(1){q=b<<2;e=q+n|0;k=H[e+4>>2];g=H[e+8>>2];c=H[e+12>>2];q=d+q|0;H[q>>2]=H[e>>2]/2;H[q+12>>2]=(c|0)/2;H[q+8>>2]=(g|0)/2;H[q+4>>2]=(k|0)/2;b=b+4|0;if(o>>>0>b>>>0){continue}break}}nj:{if(b>>>0>=u>>>0){break nj}c=b+1|0;k=j+(N(i,m)<<2)|0;g=(N(i,u)<<2)+t|0;if(u-b&1){b=b<<2;H[b+k>>2]=H[b+g>>2]/2;b=c}if((c|0)==(u|0)){break nj}while(1){c=b<<2;H[c+k>>2]=H[c+g>>2]/2;c=c+4|0;H[c+k>>2]=H[c+g>>2]/2;b=b+2|0;if((u|0)!=(b|0)){continue}break}}i=i+1|0;if((p|0)!=(i|0)){continue}break}break a}if(!p|!u){break a}ga=O(L[qa+32>>2]*O(.5));i=(H[oa+36>>2]+(N(i,m)<<2)|0)+(l<<2)|0;g=u&-4;k=u&3;h=0;c=u-1>>>0<3;while(1){b=i;f=0;oj:{if(!c){while(1){L[b>>2]=ga*O(H[t>>2]);L[b+4>>2]=ga*O(H[t+4>>2]);L[b+8>>2]=ga*O(H[t+8>>2]);L[b+12>>2]=ga*O(H[t+12>>2]);b=b+16|0;t=t+16|0;f=f+4|0;if((g|0)!=(f|0)){continue}break}if(!k){break oj}}f=0;while(1){L[b>>2]=ga*O(H[t>>2]);b=b+4|0;t=t+4|0;f=f+1|0;if((k|0)!=(f|0)){continue}break}}i=(m<<2)+i|0;h=h+1|0;if((p|0)!=(h|0)){continue}break}break a}H[Y>>2]=z;Ba(S,2,8716,Y)}H[H[d>>2]>>2]=0}Ca(a);na=Y+96|0} +function ib(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,z=0,A=0,C=0,D=0,E=0,J=0,M=0,Q=0,R=0,S=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,aa=0,ba=0,ca=O(0),da=0,ea=0,fa=0,ga=0,ha=0,ia=0,ja=0,ka=0,la=0,ma=0,oa=0,pa=0,ra=0,sa=0,ta=0,ua=0,va=0,wa=0,xa=0,ya=0,za=O(0),Aa=0,Ga=0,Ka=0,La=0,Ma=0,Na=0,Pa=O(0),Ra=0,Ua=0,Wa=0,Xa=0,Ya=0,_a=0,$a=0,ab=0,bb=0,cb=0,fb=0,gb=0,hb=0,ib=0,kb=0,lb=0,mb=0,pb=0,qb=0,rb=0,sb=0,tb=0;Aa=na-16|0;na=Aa;a:{if(!(I[a+8|0]&128)|H[a+228>>2]!=(b|0)){break a}ua=H[a+180>>2]+N(b,5644)|0;E=H[ua+5596>>2];if(!E){jb(ua);break a}g=H[a+100>>2];if(!g){g=H[a+96>>2]}q=H[g>>2];p=H[g+4>>2];k=H[g+8>>2];v=H[g+12>>2];g=H[a+60>>2];h=H[a+64>>2];i=H[ua+5600>>2];ra=na-16|0;na=ra;D=H[a+232>>2];H[D+36>>2]=b;j=H[H[D+28>>2]+76>>2];H[D+64>>2]=1;H[D+60>>2]=v;H[D+56>>2]=k;H[D+52>>2]=p;H[D+48>>2]=q;H[D+32>>2]=j+N(b,5644);Ca(H[D+68>>2]);p=0;H[D+68>>2]=0;b:{if(g){p=Ea(4,H[H[D+24>>2]+16>>2]);if(!p){break b}q=g&3;c:{if(g>>>0>=4){k=g&-4;b=0;while(1){g=h+(X<<2)|0;H[(H[g>>2]<<2)+p>>2]=1;H[(H[g+4>>2]<<2)+p>>2]=1;H[(H[g+8>>2]<<2)+p>>2]=1;H[(H[g+12>>2]<<2)+p>>2]=1;X=X+4|0;b=b+4|0;if((k|0)!=(b|0)){continue}break}if(!q){break c}}while(1){H[(H[h+(X<<2)>>2]<<2)+p>>2]=1;X=X+1|0;s=s+1|0;if((q|0)!=(s|0)){continue}break}}H[D+68>>2]=p}d:{A=H[D+24>>2];m=H[A+16>>2];e:{if(!m){break e}X=0;f:{while(1){g:{if(H[(X<<2)+p>>2]?0:p){break g}h=H[A+24>>2]+N(X,52)|0;b=H[h+4>>2];k=b-1|0;q=H[D+60>>2];v=k+q|0;g=0-!b|0;j=ve(v,q>>>0>v>>>0?g+1|0:g,b,0);h=H[h>>2];l=h-1|0;v=H[D+56>>2];s=l+v|0;q=0-!h|0;r=ve(s,s>>>0>>0?q+1|0:q,h,0);t=k;k=H[D+52>>2];v=t+k|0;k=ve(v,k>>>0>v>>>0?g+1|0:g,b,0);b=H[H[H[D+20>>2]>>2]+20>>2]+N(X,76)|0;g=H[b+20>>2]-H[b+24>>2]|0;if(g>>>0>31){break g}v=H[b+12>>2];s=H[b+8>>2];t=H[b+4>>2];V=l;l=H[D+48>>2];x=V+l|0;h=ve(x,l>>>0>x>>>0?q+1|0:q,h,0);b=h-H[b>>2]|0;h:{if((b>>>0<=h>>>0?b:0)>>>g|0){break h}b=k-t|0;if((b>>>0<=k>>>0?b:0)>>>g|0){break h}b=s-r|0;if((b>>>0<=s>>>0?b:0)>>>g|0){break h}b=v-j|0;if(!((b>>>0<=v>>>0?b:0)>>>g|0)){break g}}H[D+64>>2]=0;break f}X=X+1|0;if((m|0)!=(X|0)){continue}break}if(!H[D+64>>2]){break f}s=0;while(1){b=H[H[H[D+20>>2]>>2]+20>>2]+N(s,76)|0;g=H[b+28>>2]+N(H[b+24>>2],152)|0;h=H[g-148>>2];q=H[g-140>>2];p=H[g-152>>2];g=H[g-144>>2];k=H[D+68>>2];i:{if(H[k+(s<<2)>>2]?0:k){break i}k=q-h|0;g=g-p|0;re(k,0,g);if(!(!qa|(h|0)==(q|0))){X=0;Ba(f,1,2982,0);break b}g=N(g,k);if(g>>>0>=1073741824){X=0;Ba(f,1,2982,0);break b}g=g<<2;H[b+44>>2]=g;j:{k:{l:{h=H[b+36>>2];if(h){if(g>>>0<=K[b+48>>2]){break i}if(H[b+40>>2]){break l}}g=Ia(g);H[b+36>>2]=g;h=g;g=H[b+44>>2];if(!(g?h:1)){break k}H[b+40>>2]=1;H[b+48>>2]=g;break i}Ca(h);g=Ia(H[b+44>>2]);H[b+36>>2]=g;if(g){break j}H[b+48>>2]=0;H[b+40>>2]=0;H[b+44>>2]=0}X=0;Ba(f,1,2982,0);break b}H[b+40>>2]=1;H[b+48>>2]=H[b+44>>2]}s=s+1|0;A=H[D+24>>2];if(s>>>0>2]){continue}break}break e}M=H[A+24>>2];W=H[H[H[D+20>>2]>>2]+20>>2];b=0;while(1){m:{if(H[(b<<2)+p>>2]?0:p){break m}g=W+N(b,76)|0;q=H[g>>2];k=M+N(b,52)|0;h=H[k>>2];l=h-1|0;v=H[D+48>>2];j=l+v|0;s=0-!h|0;v=ve(j,j>>>0>>0?s+1|0:s,h,0);q=q>>>0>v>>>0?q:v;H[g+56>>2]=q;v=H[g+4>>2];k=H[k+4>>2];r=k-1|0;t=H[D+52>>2];x=r+t|0;j=0-!k|0;t=ve(x,t>>>0>x>>>0?j+1|0:j,k,0);v=v>>>0>t>>>0?v:t;H[g+60>>2]=v;t=H[g+8>>2];V=l;l=H[D+56>>2];x=V+l|0;h=ve(x,l>>>0>x>>>0?s+1|0:s,h,0);h=h>>>0>t>>>0?t:h;H[g+64>>2]=h;s=H[g+12>>2];l=H[D+60>>2];r=r+l|0;k=ve(r,l>>>0>r>>>0?j+1|0:j,k,0);k=k>>>0>s>>>0?s:k;H[g+68>>2]=k;if(h>>>0>>0|k>>>0>>0){break d}r=H[g+20>>2];if(!r){break m}t=k-1|0;J=0-!k|0;Z=h-1|0;R=0-!h|0;x=v-1|0;S=0-!v|0;Y=q-1|0;z=0-!q|0;$=H[g+28>>2];k=0;h=0;while(1){v=$+N(k,152)|0;q=r+(k^-1)|0;g=q&31;if((q&63)>>>0>=32){l=1<>>32-g}g=t+s|0;j=l+J|0;w=g>>>0>>0?j+1|0:j;j=q&31;if((q&63)>>>0>=32){g=w>>>j|0}else{g=((1<>>j}H[v+148>>2]=g;g=l+R|0;V=g+1|0;j=g;g=s+Z|0;w=g>>>0>>0?V:j;j=q&31;if((q&63)>>>0>=32){g=w>>>j|0}else{g=((1<>>j}H[v+144>>2]=g;g=l+S|0;V=g+1|0;j=g;g=s+x|0;w=g>>>0>>0?V:j;j=q&31;if((q&63)>>>0>=32){g=w>>>j|0}else{g=((1<>>j}H[v+140>>2]=g;g=l+z|0;j=s+Y|0;s=j>>>0>>0?g+1|0:g;g=q&31;if((q&63)>>>0>=32){g=s>>>g|0}else{g=((1<>>g}H[v+136>>2]=g;k=k+1|0;h=k?h:h+1|0;if(h|(k|0)!=(r|0)){continue}break}}b=b+1|0;if((m|0)!=(b|0)){continue}break}}X=0;H[ra+8>>2]=0;b=H[D+28>>2];Y=Ea(1,8);if(Y){H[Y+4>>2]=b;H[Y>>2]=A}if(!Y){break b}Z=H[H[D+20>>2]>>2];x=na-144|0;na=x;l=H[D+36>>2];b=N(l,5644);h=H[Y+4>>2];R=b+H[h+76>>2]|0;_=H[R+420>>2];k=0;q=0;t=na-32|0;na=t;J=b+H[h+76>>2]|0;$=H[J+420>>2];S=H[Y>>2];m=S;w=H[m+16>>2];v=Fa(N(w,528));n:{if(!v){break n}b=Fa(w<<2);o:{if(!b){b=v;break o}p=H[h+76>>2]+N(l,5644)|0;M=H[p+420>>2];s=M+1|0;g=Ea(s,240);p:{if(g){q:{if(s){r=H[m+16>>2];s=g;while(1){H[s+236>>2]=f;j=Ea(r,16);H[s+200>>2]=j;if(!j){break q}W=H[m+16>>2];H[s+196>>2]=W;r=0;j=0;if(W){while(1){r=H[s+200>>2]+(j<<4)|0;W=H[p+5584>>2]+N(j,1080)|0;z=Ea(H[W+4>>2],16);H[r+12>>2]=z;if(!z){break q}H[r+8>>2]=H[W+4>>2];j=j+1|0;r=H[m+16>>2];if(j>>>0>>0){continue}break}}s=s+240|0;j=(o|0)==(M|0);o=o+1|0;if(!j){continue}break}}break p}p=H[g+4>>2];if(p){Ca(p);H[g+4>>2]=0}s=g;p=0;while(1){j=H[s+200>>2];if(j){r=0;o=H[s+196>>2];if(o){while(1){W=H[j+12>>2];if(W){Ca(W);H[j+12>>2]=0;o=H[s+196>>2]}j=j+16|0;r=r+1|0;if(r>>>0>>0){continue}break}j=H[s+200>>2]}Ca(j);H[s+200>>2]=0}s=s+240|0;j=(p|0)==(M|0);p=p+1|0;if(!j){continue}break}Ca(g)}g=0}if(g){r:{if(!w){break r}j=w&7;p=v;if(w>>>0>=8){r=w&-8;while(1){s=(n<<2)+b|0;H[s+28>>2]=p+3696;H[s+24>>2]=p+3168;H[s+20>>2]=p+2640;H[s+16>>2]=p+2112;H[s+12>>2]=p+1584;H[s+8>>2]=p+1056;H[s+4>>2]=p+528;H[s>>2]=p;n=n+8|0;p=p+4224|0;k=k+8|0;if((r|0)!=(k|0)){continue}break}if(!j){break r}}while(1){H[(n<<2)+b>>2]=p;n=n+1|0;p=p+528|0;u=u+1|0;if((j|0)!=(u|0)){continue}break}}u=b;r=0;s=H[(H[h+76>>2]+N(l,5644)|0)+5584>>2];p=H[m+24>>2];b=H[h+24>>2];k=(l>>>0)/(b>>>0)|0;b=H[h+4>>2]+N(H[h+12>>2],l-N(b,k)|0)|0;n=H[m>>2];H[t+20>>2]=b>>>0>n>>>0?b:n;n=b+H[h+12>>2]|0;b=b>>>0>n>>>0?-1:n;n=H[m+8>>2];H[t+16>>2]=b>>>0>>0?b:n;b=H[h+8>>2]+N(k,H[h+16>>2])|0;k=H[m+4>>2];H[t+12>>2]=b>>>0>k>>>0?b:k;h=b+H[h+16>>2]|0;b=b>>>0>h>>>0?-1:h;h=H[m+12>>2];H[t+8>>2]=b>>>0>>0?b:h;H[t+24>>2]=0;H[t+28>>2]=0;H[t+4>>2]=2147483647;H[t>>2]=2147483647;if(H[m+16>>2]){while(1){h=u?H[u+(r<<2)>>2]:0;b=H[p+4>>2];M=b-1|0;k=H[t+8>>2];j=M+k|0;n=0-!b|0;j=ve(j,k>>>0>j>>>0?n+1|0:n,b,0);k=H[p>>2];W=k-1|0;o=H[t+16>>2];z=W+o|0;l=0-!k|0;o=ve(z,o>>>0>z>>>0?l+1|0:l,k,0);V=M;M=H[t+12>>2];z=V+M|0;n=ve(z,z>>>0>>0?n+1|0:n,b,0);b=H[t+20>>2];M=b+W|0;k=ve(M,b>>>0>M>>>0?l+1|0:l,k,0);b=H[s+4>>2];if(b>>>0>K[t+28>>2]){H[t+28>>2]=b;b=H[s+4>>2]}if(b){da=s+944|0;ka=s+812|0;ba=j-1|0;oa=0-!j|0;aa=o-1|0;la=0-!o|0;ga=n-1|0;ia=0-!n|0;C=k-1|0;ha=0-!k|0;o=0;while(1){k=o<<2;M=H[k+da>>2];W=H[k+ka>>2];n=0;if(h){H[h+4>>2]=M;H[h>>2]=W;n=h+8|0}b=b-1|0;h=W+b|0;s:{if(h>>>0>31){break s}k=H[p>>2];if(k>>>0>-1>>>h>>>0){break s}j=H[t+4>>2];h=k<>2]=h>>>0>j>>>0?j:h}h=b+M|0;t:{if(h>>>0>31){break t}k=H[p+4>>2];if(k>>>0>-1>>>h>>>0){break t}j=H[t>>2];h=k<>2]=h>>>0>j>>>0?j:h}h=0;k=b&31;if((b&63)>>>0>=32){l=1<>>32-k}ea=j;j=ba+ea|0;k=l;l=oa+k|0;A=j>>>0>>0?l+1|0:l;z=b&31;l=M&31;if((M&63)>>>0>=32){l=1<>>32-l}if((b&63)>>>0>=32){A=A>>>z|0}else{A=((1<>>z}j=V+A|0;ma=j-1|0;l=(j>>>0>>0?l+1|0:l)-!j|0;j=M&31;z=k+ia|0;V=ga+ea|0;z=V>>>0>>0?z+1|0:z;if((M&63)>>>0>=32){j=l>>>j|0}else{j=((1<>>j}l=j;j=b&31;if((b&63)>>>0>=32){j=z>>>j|0}else{j=((1<>>j}z=(j|0)!=(A|0)?l-(j>>>M|0)&-1>>>M:0;j=k+la|0;l=j+1|0;V=j;j=aa+ea|0;A=j>>>0>>0?l:V;M=b&31;l=W&31;if((W&63)>>>0>=32){l=1<>>32-l}if((b&63)>>>0>=32){A=A>>>M|0}else{A=((1<>>M}j=V+A|0;ma=j-1|0;l=(j>>>0>>0?l+1|0:l)-!j|0;j=W&31;k=k+ha|0;ea=C+ea|0;M=ea>>>0>>0?k+1|0:k;k=b&31;if((W&63)>>>0>=32){j=l>>>j|0}else{j=((1<>>j}if((b&63)>>>0>=32){k=M>>>k|0}else{k=((1<>>k}k=(k|0)!=(A|0)?j-(k>>>W|0)&-1>>>W:0;if(n){H[n+4>>2]=z;H[n>>2]=k;h=n+8|0}k=N(k,z);if(k>>>0>K[t+24>>2]){H[t+24>>2]=k}o=o+1|0;if(o>>>0>2]){continue}break}}p=p+52|0;s=s+1080|0;r=r+1|0;if(r>>>0>2]){continue}break}}M=$+1|0;r=H[t+28>>2];l=H[t+24>>2];H[g+4>>2]=0;b=H[J+8>>2]+1|0;W=N(l,w);o=N(W,r);re(b,0,o);u:{if(!qa){b=N(b,o);H[g+8>>2]=b;b=Ea(b,2);H[g+4>>2]=b;if(b){break u}}Ca(v);Ca(u);b=H[g+4>>2];if(b){Ca(b);H[g+4>>2]=0}if(!M){b=g;break o}b=0;k=g;while(1){p=H[k+200>>2];if(p){m=0;n=H[k+196>>2];if(n){while(1){h=H[p+12>>2];if(h){Ca(h);H[p+12>>2]=0;n=H[k+196>>2]}p=p+16|0;m=m+1|0;if(n>>>0>m>>>0){continue}break}p=H[k+200>>2]}Ca(p);H[k+200>>2]=0}k=k+240|0;h=(b|0)==($|0);b=b+1|0;if(!h){continue}break}b=g;break o}j=H[m+24>>2];z=H[t+20>>2];H[g+204>>2]=z;ea=H[t+12>>2];H[g+208>>2]=ea;ba=H[t+16>>2];H[g+212>>2]=ba;aa=H[t+8>>2];H[g+216>>2]=aa;H[g+12>>2]=o;H[g+16>>2]=W;H[g+20>>2]=l;q=1;H[g+24>>2]=1;if(w){k=H[g+200>>2];s=0;b=j;while(1){p=H[u+(s<<2)>>2];H[k>>2]=H[b>>2];H[k+4>>2]=H[b+4>>2];h=H[k+8>>2];v:{if(!h){break v}m=H[k+12>>2];if((h|0)!=1){ga=h&1;h=h&-2;n=0;while(1){H[m>>2]=H[p>>2];H[m+4>>2]=H[p+4>>2];H[m+8>>2]=H[p+8>>2];H[m+12>>2]=H[p+12>>2];H[m+16>>2]=H[p+16>>2];H[m+20>>2]=H[p+20>>2];H[m+24>>2]=H[p+24>>2];H[m+28>>2]=H[p+28>>2];m=m+32|0;p=p+32|0;n=n+2|0;if((h|0)!=(n|0)){continue}break}if(!ga){break v}}H[m>>2]=H[p>>2];H[m+4>>2]=H[p+4>>2];H[m+8>>2]=H[p+8>>2];H[m+12>>2]=H[p+12>>2]}b=b+52|0;k=k+16|0;s=s+1|0;if((w|0)!=(s|0)){continue}break}}if(M>>>0>1){h=g;while(1){H[h+456>>2]=aa;H[h+452>>2]=ba;H[h+448>>2]=ea;H[h+444>>2]=z;H[h+264>>2]=1;H[h+260>>2]=l;H[h+256>>2]=W;H[h+252>>2]=o;if(w){k=H[h+440>>2];s=0;b=j;while(1){p=H[u+(s<<2)>>2];H[k>>2]=H[b>>2];H[k+4>>2]=H[b+4>>2];n=H[k+8>>2];w:{if(!n){break w}m=H[k+12>>2];if((n|0)!=1){M=n&1;ga=n&-2;n=0;while(1){H[m>>2]=H[p>>2];H[m+4>>2]=H[p+4>>2];H[m+8>>2]=H[p+8>>2];H[m+12>>2]=H[p+12>>2];H[m+16>>2]=H[p+16>>2];H[m+20>>2]=H[p+20>>2];H[m+24>>2]=H[p+24>>2];H[m+28>>2]=H[p+28>>2];m=m+32|0;p=p+32|0;n=n+2|0;if((ga|0)!=(n|0)){continue}break}if(!M){break w}}H[m>>2]=H[p>>2];H[m+4>>2]=H[p+4>>2];H[m+8>>2]=H[p+8>>2];H[m+12>>2]=H[p+12>>2]}b=b+52|0;k=k+16|0;s=s+1|0;if((w|0)!=(s|0)){continue}break}}b=H[h+8>>2];H[h+244>>2]=H[h+4>>2];H[h+248>>2]=b;b=(q|0)!=($|0);h=h+240|0;q=q+1|0;if(b){continue}break}}Ca(v);Ca(u);b=H[J+420>>2];x:{if(I[J+5640|0]&4){if((b|0)==-1){break x}m=J+424|0;h=H[J+8>>2];n=0;p=g;while(1){q=H[m+36>>2];H[p+44>>2]=1;H[p+84>>2]=q;H[p+48>>2]=H[m>>2];q=H[m+4>>2];H[p+68>>2]=0;H[p+72>>2]=0;H[p+52>>2]=q;H[p+60>>2]=H[m+12>>2];H[p+64>>2]=H[m+16>>2];q=H[m+8>>2];H[p+76>>2]=l;H[p+56>>2]=h>>>0>q>>>0?q:h;m=m+148|0;p=p+240|0;q=(b|0)==(n|0);n=n+1|0;if(!q){continue}break}break x}if((b|0)==-1){break x}h=H[J+8>>2];q=H[J+4>>2];p=g;if(b){k=b+1&-2;u=0;while(1){H[p+68>>2]=0;H[p+72>>2]=0;H[p+52>>2]=0;H[p+44>>2]=1;H[p+48>>2]=0;H[p+84>>2]=q;H[p+60>>2]=r;H[p+324>>2]=q;H[p+76>>2]=l;H[p+56>>2]=h;H[p+308>>2]=0;H[p+312>>2]=0;H[p+292>>2]=0;H[p+284>>2]=1;H[p+288>>2]=0;H[p+300>>2]=r;H[p+296>>2]=h;H[p+316>>2]=l;H[p+64>>2]=H[p+196>>2];H[p+304>>2]=H[p+436>>2];p=p+480|0;u=u+2|0;if((k|0)!=(u|0)){continue}break}if(b&1){break x}}H[p+68>>2]=0;H[p+72>>2]=0;H[p+52>>2]=0;H[p+44>>2]=1;H[p+48>>2]=0;H[p+84>>2]=q;H[p+60>>2]=r;H[p+76>>2]=l;H[p+56>>2]=h;H[p+64>>2]=H[p+196>>2]}q=g;break n}Ca(v)}Ca(b)}na=t+32|0;g=q;y:{z:{if(!g){break z}M=_+1|0;s=E;v=g;A:{B:{while(1){if(H[v+84>>2]==-1){break A}l=Fa(H[S+16>>2]<<2);if(!l){break A}b=H[S+16>>2]<<2;if(b){y(l,1,b)}if(Vb(v)){while(1){k=H[Z+20>>2];C:{D:{if(K[v+40>>2]>=K[R+12>>2]){break D}b=H[v+32>>2];h=N(H[v+28>>2],76)+k|0;if(b>>>0>=K[h+24>>2]){break D}h=H[h+28>>2]+N(b,152)|0;if(!H[h+24>>2]){break D}q=h+28|0;j=0;E:{while(1){p=q+N(j,36)|0;b=H[p+20>>2]+N(H[v+36>>2],40)|0;if(!xb(D,H[v+28>>2],H[v+32>>2],H[p+16>>2],H[b>>2],H[b+4>>2],H[b+8>>2],H[b+12>>2])){j=j+1|0;if(j>>>0>2]){continue}break E}break}H[l+(H[v+28>>2]<<2)>>2]=0;H[x+136>>2]=0;if(!Ub(H[Y+4>>2],H[Z+20>>2],R,v,x+140|0,s,x+136|0,i,f)){break B}j=H[v+32>>2];o=H[v+28>>2];m=H[x+136>>2];if(H[x+140>>2]){H[x+136>>2]=0;W=H[(H[Z+20>>2]+N(o,76)|0)+28>>2]+N(j,152)|0;k=H[W+24>>2];if(k){w=i-m|0;J=i+s|0;o=W+28|0;t=0;r=0;z=m+s|0;u=z;while(1){F:{if(H[o+8>>2]==H[o>>2]|H[o+12>>2]==H[o+4>>2]){break F}b=H[o+20>>2]+N(H[v+36>>2],40)|0;$=N(H[b+20>>2],H[b+16>>2]);if(!$){break F}k=H[b+24>>2];h=0;while(1){p=H[k+36>>2];if(p){G:{if(r|H[k+64>>2]){H[k+52>>2]=0;j=1;b=64;break G}j=H[k>>2];b=H[k+40>>2];H:{if(b){j=N(b,24)+j|0;if(H[j-20>>2]!=H[j-12>>2]){j=j-24|0;break H}b=b+1|0}else{b=1}H[k+40>>2]=b}b=H[j+20>>2];I:{J:{if(b>>>0>(u^-1)>>>0){break J}q=j+20|0;while(1){if(J>>>0>>0){break J}n=H[k+4>>2];r=H[k+52>>2];if((r|0)!=H[k+56>>2]){q=p}else{b=r<<1|1;n=Ha(n,b<<3);if(!n){Ba(f,1,1024,0);break B}H[k+56>>2]=b;H[k+4>>2]=n;r=H[k+52>>2];b=H[q>>2];q=H[k+36>>2]}p=(r<<3)+n|0;H[p+4>>2]=b;H[p>>2]=u;H[k+52>>2]=r+1;H[j>>2]=H[j>>2]+b;n=H[j+16>>2];r=n+H[j+4>>2]|0;H[j+4>>2]=r;p=q-n|0;H[k+36>>2]=p;H[j+8>>2]=r;u=b+u|0;r=0;if((n|0)==(q|0)){break I}H[k+40>>2]=H[k+40>>2]+1;q=j+44|0;b=H[j+44>>2];j=j+24|0;if((u^-1)>>>0>=b>>>0){continue}break}}q=H[v+28>>2];p=H[v+32>>2];n=H[v+36>>2];if(H[H[Y+4>>2]+104>>2]){H[x+120>>2]=q;H[x+116>>2]=p;H[x+112>>2]=t;H[x+108>>2]=n;H[x+104>>2]=h;H[x+100>>2]=w;H[x+96>>2]=b;Ba(f,1,14693,x+96|0);break B}H[x+88>>2]=q;H[x+84>>2]=p;H[x+80>>2]=t;H[x+76>>2]=n;H[x+72>>2]=h;H[x+68>>2]=w;H[x+64>>2]=b;Ba(f,2,14693,x- -64|0);H[k+52>>2]=0;H[k+64>>2]=1;r=1}j=H[k+40>>2];b=44}H[b+k>>2]=j}k=k+68|0;h=h+1|0;if(($|0)!=(h|0)){continue}break}k=H[W+24>>2]}o=o+36|0;t=t+1|0;if(t>>>0>>0){continue}break}j=H[v+32>>2];o=H[v+28>>2];b=r?w:u-z|0}else{b=0}m=b+m|0}h=H[S+24>>2]+N(o,52)|0;b=H[h+36>>2];H[h+36>>2]=b>>>0>>0?j:b;break C}k=H[Z+20>>2]}H[x+136>>2]=0;if(!Ub(H[Y+4>>2],k,R,v,x+140|0,s,x+136|0,i,f)){break B}o=H[v+28>>2];m=H[x+136>>2];if(!H[x+140>>2]){break C}J=H[v+32>>2];b=H[(H[Z+20>>2]+N(o,76)|0)+28>>2]+N(J,152)|0;z=H[b+24>>2];if(!z){break C}w=i-m|0;n=b+28|0;W=H[v+36>>2];j=0;r=0;K:{L:{while(1){M:{if(H[n+8>>2]==H[n>>2]|H[n+12>>2]==H[n+4>>2]){break M}b=H[n+20>>2]+N(W,40)|0;$=N(H[b+20>>2],H[b+16>>2]);if(!$){break M}p=H[b+24>>2];t=0;while(1){b=H[p+36>>2];if(b){k=H[p>>2];h=H[p+40>>2];N:{if(h){k=N(h,24)+k|0;if(H[k-20>>2]!=H[k-12>>2]){k=k-24|0;break N}h=h+1|0}else{h=1}H[p+40>>2]=h}u=H[k+20>>2];j=u+j|0;if(j>>>0>>0|j>>>0>w>>>0){break K}while(1){O:{u=H[k+16>>2];H[k+4>>2]=u+H[k+4>>2];q=b-u|0;if((b|0)==(u|0)){break O}h=h+1|0;H[p+40>>2]=h;u=H[k+44>>2];j=u+j|0;if(j>>>0>>0){break L}k=k+24|0;b=q;if(j>>>0<=w>>>0){continue}break L}break}H[p+36>>2]=q}p=p+68|0;t=t+1|0;if(($|0)!=(t|0)){continue}break}}n=n+36|0;r=r+1|0;if((z|0)!=(r|0)){continue}break}m=j+m|0;break C}H[p+36>>2]=q}if(!H[H[Y+4>>2]+104>>2]){H[x+24>>2]=o;H[x+20>>2]=J;H[x+16>>2]=r;H[x+12>>2]=W;H[x+8>>2]=t;H[x+4>>2]=w;H[x>>2]=u;Ba(f,2,14608,x);o=H[v+28>>2];m=m+w|0;break C}H[x+56>>2]=o;H[x+52>>2]=J;H[x+48>>2]=r;H[x+44>>2]=W;H[x+40>>2]=t;H[x+36>>2]=w;H[x+32>>2]=u;Ba(f,1,14608,x+32|0);break B}P:{if(!H[l+(o<<2)>>2]){break P}b=H[S+24>>2]+N(o,52)|0;if(H[b+36>>2]){break P}H[b+36>>2]=H[(H[Z+20>>2]+N(o,76)|0)+24>>2]-1}i=i-m|0;s=m+s|0;if(Vb(v)){continue}break}}Ca(l);v=v+240|0;Q=Q+1|0;if(Q>>>0<=K[R+420>>2]){continue}break}yb(g,M);H[ra+8>>2]=s-E;b=1;break y}yb(g,M);Ca(l);break z}yb(g,M)}b=0}na=x+144|0;db(Y);if(!b){break b}X=H[H[D+32>>2]+5584>>2];j=H[H[D+20>>2]>>2];C=H[j+20>>2];H[ra+12>>2]=1;s=0;b=H[D+32>>2];l=H[X+16>>2]>>>4&1&H[b+12>>2]==H[b+8>>2];A=H[j+16>>2];Q:{if(!A){break Q}while(1){b=H[D+68>>2];if(!(H[b+(s<<2)>>2]?0:b)){u=ra+12|0;A=0;b=H[C+24>>2];R:{if(!b){break R}m=H[D+44>>2];while(1){q=H[C+28>>2]+N(A,152)|0;p=H[q+24>>2];if(p){o=q+28|0;b=H[q+20>>2];v=H[q+16>>2];r=0;while(1){if(N(b,v)){n=o+N(r,36)|0;k=0;while(1){g=H[n+20>>2]+N(k,40)|0;i=xb(D,H[C+16>>2],A,H[n+16>>2],H[g>>2],H[g+4>>2],H[g+8>>2],H[g+12>>2]);h=H[g+16>>2];p=H[g+20>>2];b=N(h,p);S:{if(i){if(!b){break S}h=0;while(1){i=H[g+24>>2]+N(h,68)|0;T:{if(!xb(D,H[C+16>>2],A,H[n+16>>2],H[i+8>>2],H[i+12>>2],H[i+16>>2],H[i+20>>2])){b=H[i+60>>2];if(!b){break T}Ca(b);H[i+60>>2]=0;break T}if(!H[D+64>>2]){if(H[i+60>>2]|H[i+16>>2]==H[i+8>>2]|H[i+20>>2]==H[i+12>>2]){break T}}b=Ea(1,44);if(!b){H[ra+12>>2]=0;break R}p=H[D+64>>2];H[b+36>>2]=0;H[b+28>>2]=u;H[b+20>>2]=X;H[b+16>>2]=C;H[b+12>>2]=n;H[b+8>>2]=i;H[b+4>>2]=A;H[b>>2]=p;H[b+40>>2]=l;H[b+32>>2]=f;H[b+24>>2]=H[m+4>>2]>1;eb(m,14,b);if(!H[ra+12>>2]){break R}}h=h+1|0;if(h>>>0>2],H[g+16>>2])>>>0){continue}break}break S}if(!b){break S}v=0;while(1){b=H[g+24>>2]+N(v,68)|0;i=H[b+60>>2];if(i){Ca(i);H[b+60>>2]=0;p=H[g+20>>2];h=H[g+16>>2]}v=v+1|0;if(v>>>0>>0){continue}break}}k=k+1|0;b=H[q+20>>2];v=H[q+16>>2];if(k>>>0>>0){continue}break}p=H[q+24>>2]}r=r+1|0;if(r>>>0

>>0){continue}break}b=H[C+24>>2]}A=A+1|0;if(A>>>0>>0){continue}break}}if(!H[ra+12>>2]){break Q}A=H[j+16>>2]}X=X+1080|0;C=C+76|0;s=s+1|0;if(A>>>0>s>>>0){continue}break}}X=0;Sa(H[D+44>>2]);if(!H[ra+12>>2]){break b}U:{if(H[D+64>>2]){break U}s=H[D+24>>2];if(!H[s+16>>2]){break U}C=0;while(1){b=H[H[H[D+20>>2]>>2]+20>>2]+N(C,76)|0;g=H[b+28>>2]+N(H[(H[s+24>>2]+N(C,52)|0)+36>>2],152)|0;i=H[g+136>>2];h=H[g+144>>2];q=H[g+140>>2];g=H[g+148>>2];Ca(H[b+52>>2]);H[b+52>>2]=0;V:{p=H[D+68>>2];if((h|0)==(i|0)|(g|0)==(q|0)|(H[p+(C<<2)>>2]?0:p)){break V}g=g-q|0;i=h-i|0;re(g,0,i);if(qa){Ba(f,1,2982,0);break b}g=N(g,i);if(g>>>0>=1073741824){Ba(f,1,2982,0);break b}i=b;b=Ia(g<<2);H[i+52>>2]=b;if(b){break V}Ba(f,1,2982,0);break b}C=C+1|0;s=H[D+24>>2];if(C>>>0>2]){continue}break}}s=H[D+32>>2];v=H[H[D+20>>2]>>2];if(H[v+16>>2]){C=H[v+20>>2];s=H[s+5584>>2];A=H[H[D+24>>2]+24>>2];p=0;while(1){W:{b=H[D+68>>2];if(H[b+(p<<2)>>2]?0:b){break W}j=H[A+36>>2]+1|0;if(H[s+20>>2]==1){Y=j;b=0;_=na-32|0;na=_;X:{Y:{if(H[D+64>>2]){g=1;if((j|0)==1){break X}k=H[C+28>>2];b=k+N(H[C+24>>2],152)|0;h=H[b-144>>2];q=H[b-152>>2];if((h|0)==(q|0)){break X}n=j-1|0;b=0;m=H[D+44>>2];l=H[m+4>>2];i=k;Z:{if((j|0)!=2){j=n&1;r=n&-2;g=0;while(1){u=H[i+160>>2]-H[i+152>>2]|0;b=b>>>0>u>>>0?b:u;u=H[i+164>>2]-H[i+156>>2]|0;b=b>>>0>u>>>0?b:u;u=H[i+312>>2]-H[i+304>>2]|0;b=b>>>0>u>>>0?b:u;u=H[i+316>>2]-H[i+308>>2]|0;b=b>>>0>u>>>0?b:u;i=i+304|0;g=g+2|0;if((r|0)!=(g|0)){continue}break}if(!j){break Z}}g=H[i+160>>2]-H[i+152>>2]|0;b=b>>>0>g>>>0?b:g;g=H[i+164>>2]-H[i+156>>2]|0;b=b>>>0>g>>>0?b:g}g=0;if(b>>>0>134217727){break X}i=H[k+4>>2];u=H[k+12>>2];t=H[k>>2];E=H[k+8>>2];o=b<<5;j=ob(o);H[_+16>>2]=j;if(!j){break X}r=h-q|0;g=u-i|0;q=E-t|0;H[_>>2]=j;while(1){u=H[C+36>>2];h=g;H[_+8>>2]=g;b=q;H[_+24>>2]=b;g=H[k+156>>2];i=H[k+164>>2];q=H[k+160>>2];t=H[k+152>>2];H[_+28>>2]=(t|0)%2;q=q-t|0;H[_+20>>2]=q-b;x=(l|0)<2;g=i-g|0;_:{if(!(!x&g>>>0>1)){i=0;if(!g){break _}while(1){$b(_+16|0,u+(N(i,r)<<2)|0);i=i+1|0;if((i|0)!=(g|0)){continue}break}break _}t=g>>>0>>0?g:l;Y=t-1|0;E=(g>>>0)/(t>>>0)|0;b=0;while(1){i=Fa(36);if(!i){break Y}w=H[_+28>>2];H[i+8>>2]=H[_+24>>2];H[i+12>>2]=w;w=H[_+20>>2];H[i>>2]=H[_+16>>2];H[i+4>>2]=w;H[i+24>>2]=u;H[i+20>>2]=r;H[i+16>>2]=q;H[i+28>>2]=N(b,E);w=(b|0)==(Y|0);b=b+1|0;H[i+32>>2]=w?g:N(E,b);w=ob(o);H[i>>2]=w;if(!w){g=0;Sa(m);Ca(i);Ca(j);break X}eb(m,10,i);if((b|0)!=(t|0)){continue}break}Sa(m)}H[_+4>>2]=g-h;H[_+12>>2]=H[k+156>>2]%2;$:{if(!(!x&q>>>0>1)){b=8;i=0;if(q>>>0>=8){while(1){nb(_,u+(i<<2)|0,r,8);i=b;b=b+8|0;if(q>>>0>=b>>>0){continue}break}}if(i>>>0>=q>>>0){break $}nb(_,u+(i<<2)|0,r,q-i|0);break $}h=l>>>0>q>>>0?q:l;E=h-1|0;t=(q>>>0)/(h>>>0)|0;b=0;while(1){i=Fa(36);if(!i){break Y}x=H[_+12>>2];H[i+8>>2]=H[_+8>>2];H[i+12>>2]=x;x=H[_+4>>2];H[i>>2]=H[_>>2];H[i+4>>2]=x;H[i+24>>2]=u;H[i+20>>2]=r;H[i+16>>2]=g;H[i+28>>2]=N(b,t);x=(b|0)==(E|0);b=b+1|0;H[i+32>>2]=x?q:N(t,b);x=ob(o);H[i>>2]=x;if(!x){g=0;Sa(m);Ca(i);Ca(j);break X}eb(m,11,i);if((b|0)!=(h|0)){continue}break}Sa(m)}k=k+152|0;n=n-1|0;if(n){continue}break}g=1;Ca(j);break X}g=1;k=H[C+28>>2];ma=k+N(Y,152)|0;gb=ma-152|0;if(H[gb>>2]==H[ma-144>>2]){break X}hb=ma-148|0;if(H[hb>>2]==H[ma-140>>2]){break X}h=H[k+4>>2];q=H[k+12>>2];n=H[k>>2];j=H[k+8>>2];w=H[C+68>>2];M=H[C+64>>2];W=H[C+60>>2];J=H[C+56>>2];oa=_b(C,Y);if(!oa){g=0;break X}aa:{ba:{if((Y|0)!=1){i=k;ca:{if((Y|0)!=2){g=Y-1|0;m=g&1;l=g&-2;g=0;while(1){r=H[i+160>>2]-H[i+152>>2]|0;b=b>>>0>r>>>0?b:r;r=H[i+164>>2]-H[i+156>>2]|0;b=b>>>0>r>>>0?b:r;r=H[i+312>>2]-H[i+304>>2]|0;b=b>>>0>r>>>0?b:r;r=H[i+316>>2]-H[i+308>>2]|0;b=b>>>0>r>>>0?b:r;i=i+304|0;g=g+2|0;if((l|0)!=(g|0)){continue}break}if(!m){break ca}}g=H[i+160>>2]-H[i+152>>2]|0;b=b>>>0>g>>>0?b:g;g=H[i+164>>2]-H[i+156>>2]|0;b=b>>>0>g>>>0?b:g}if(b>>>0>=268435456){break aa}o=ob(b<<4);if(!o){break aa}da:{if(!Y){break da}u=q-h|0;m=j-n|0;ib=o+28|0;$=o+24|0;Ra=o+16|0;ha=o+4|0;sa=1;ea:while(1){b=H[k+156>>2];ja=(b|0)%2|0;g=H[k+152>>2];pa=(g|0)%2|0;Z=H[k+164>>2]-b|0;ka=Z-u|0;R=H[k+160>>2]-g|0;da=R-m|0;i=J;g=i;t=W;q=t;b=M;fa=b;h=w;n=h;j=H[C+20>>2];fa:{if((j|0)==(sa|0)){break fa}r=j-sa|0;q=0;g=0;if(i){b=r&31;if((r&63)>>>0>=32){l=-1<>>32-b}b=i+(g^-1)|0;g=l^-1;i=b>>>0>>0?g+1|0:g;g=r&31;if((r&63)>>>0>=32){g=i>>>g|0}else{g=((1<>>g}}if(W){b=r&31;if((r&63)>>>0>=32){l=-1<>>32-b}b=W+(i^-1)|0;i=l^-1;h=b>>>0>>0?i+1|0:i;i=r&31;if((r&63)>>>0>=32){q=h>>>i|0}else{q=((1<>>i}}h=0;b=0;if(M){b=r&31;if((r&63)>>>0>=32){l=-1<>>32-b}b=M+(i^-1)|0;i=l^-1;n=b>>>0>>0?i+1|0:i;i=r&31;if((r&63)>>>0>=32){b=n>>>i|0}else{b=((1<>>i}}if(w){i=r&31;if((r&63)>>>0>=32){l=-1<>>32-i}i=w+(h^-1)|0;h=l^-1;n=i>>>0>>0?h+1|0:h;h=r&31;if((r&63)>>>0>=32){h=n>>>h|0}else{h=((1<>>h}}fa=0;i=0;E=1<>>0>>0){i=r&31;if((r&63)>>>0>=32){l=-1<>>32-i}n=n^-1;i=n+(J-E|0)|0;j=l^-1;j=i>>>0>>0?j+1|0:j;n=r&31;if((r&63)>>>0>=32){i=j>>>n|0}else{i=((1<>>n}}if(E>>>0>>0){n=r&31;if((r&63)>>>0>=32){l=-1<>>32-n}j=j^-1;n=j+(M-E|0)|0;l=l^-1;l=j>>>0>n>>>0?l+1|0:l;j=r&31;if((r&63)>>>0>=32){fa=l>>>j|0}else{fa=((1<>>j}}n=0;t=0;if(E>>>0>>0){j=r&31;if((r&63)>>>0>=32){l=-1<>>32-j}t=t^-1;j=t+(W-E|0)|0;l=l^-1;t=j>>>0>>0?l+1|0:l;l=r&31;if((r&63)>>>0>=32){t=t>>>l|0}else{t=((1<>>l}}if(w>>>0<=E>>>0){break fa}n=r&31;if((r&63)>>>0>=32){l=-1<>>32-n}j=j^-1;n=j+(w-E|0)|0;l=l^-1;l=j>>>0>n>>>0?l+1|0:l;j=r&31;if((r&63)>>>0>=32){n=l>>>j|0}else{n=((1<>>j}}j=H[k+180>>2];l=fa-j|0;l=l>>>0<=fa>>>0?l:0;r=l+2|0;l=l>>>0>r>>>0?-1:r;la=l>>>0>>0?l:da;l=H[k+216>>2];r=b-l|0;b=b>>>0>=r>>>0?r:0;r=b+2|0;b=b>>>0>r>>>0?-1:r;ia=b>>>0>>0?b:m;b=(pa?la:ia)<<1;r=(pa?ia:la)<<1|1;va=b>>>0>r>>>0?b:r;b=va>>>0>>0;j=i-j|0;i=i>>>0>=j>>>0?j:0;j=i-2|0;r=i>>>0>=j>>>0?j:0;i=g-l|0;g=g>>>0>=i>>>0?i:0;i=g-2|0;i=g>>>0>=i>>>0?i:0;g=(pa?r:i)<<1;l=(pa?i:r)<<1|1;x=g>>>0>>0;E=H[k+184>>2];j=q-E|0;q=j>>>0<=q>>>0?j:0;j=q-2|0;q=j>>>0<=q>>>0?j:0;ea=q;S=H[k+220>>2];j=t-S|0;j=j>>>0<=t>>>0?j:0;t=j-2|0;j=j>>>0>=t>>>0?t:0;ba=j;t=h-E|0;h=h>>>0>=t>>>0?t:0;t=h+2|0;h=h>>>0>t>>>0?-1:t;E=h>>>0>>0?h:u;Q=E;h=n-S|0;h=h>>>0<=n>>>0?h:0;n=h+2|0;h=h>>>0>n>>>0?-1:n;V=h>>>0>>0?h:ka;n=V;if(ja){ea=j;ba=q;Q=n;n=E}wa=b?va:R;l=x?g:l;kb=u+V|0;lb=j+u|0;if(Z){ta=o+(i<<3)|0;b=o+(da<<3)|0;xa=b-4|0;g=(i|0)<(da|0);Ua=g?ta+4|0:xa;ya=m-1|0;Wa=(la|0)<(ya|0)?la:ya;x=0;Xa=(m|0)>1|(da|0)>0;h=pa<<2;Ya=(ha-h|0)+(r<<3)|0;_a=h+ta|0;z=(da|0)>(ia|0)?ia:da;aa=i+1|0;$a=m+la|0;La=m+r|0;ab=o+(l<<2)|0;Ma=!m&(da|0)==1;h=o+(m<<3)|0;ga=h-8|0;Ga=h-4|0;Ka=b-8|0;Na=o+(wa<<2)|0;bb=Na-4|0;cb=(o+((g?i:da)<<3)|0)-4|0;while(1){ga:{if(!(x>>>0>>0&q>>>0<=x>>>0|x>>>0>>0&x>>>0>=lb>>>0)){S=x+1|0;break ga}if(R>>>0>va>>>0){H[bb>>2]=0;H[Na>>2]=0}S=x+1|0;Oa(oa,i,x,ia,S,_a,2,0);Oa(oa,La,x,$a,S,Ya,2,0);ha:{ia:{ja:{if(!pa){if(!Xa){break ha}if((i|0)>=(ia|0)){break ia}ka:{la:{if((i|0)>0){b=H[cb>>2];break la}b=H[ha>>2];g=b;if((i|0)<0){break ka}}g=b;b=H[Ua>>2]}H[ta>>2]=H[ta>>2]-((b+g|0)+2>>2);b=aa;h=b;g=i;if((b|0)>=(z|0)){break ja}while(1){b=g;g=h;h=o+(g<<3)|0;H[h>>2]=H[h>>2]-((H[(o+(b<<3)|0)+4>>2]+H[h+4>>2]|0)+2>>2);h=g+1|0;if((z|0)!=(h|0)){continue}break}b=z;break ja}ma:{if(!Ma){b=i;if((ia|0)<=(b|0)){break ma}while(1){g=o+(b<<3)|0;h=H[g+4>>2];t=Ka;na:{oa:{if((b|0)>=0){fa=H[((b|0)<(da|0)?g:Ka)>>2];b=b+1|0;break oa}fa=H[o>>2];if((b|0)!=-1){b=b+1|0;t=o;break na}b=0}if((b|0)>=(da|0)){break na}t=o+(b<<3)|0}H[g+4>>2]=h-((H[t>>2]+fa|0)+2>>2);if((b|0)<(ia|0)){continue}break}break ma}H[o>>2]=H[o>>2]/2;break ha}b=r;if((la|0)<=(b|0)){break ha}while(1){g=o+(b<<3)|0;h=H[g>>2];pa:{if((b|0)<0){t=H[ha>>2];fa=ha;break pa}t=H[((b|0)<(m|0)?g+4|0:Ga)>>2];fa=ha;if(!b){break pa}fa=(b|0)>(m|0)?Ga:g-4|0}H[g>>2]=h+(H[fa>>2]+t>>1);b=b+1|0;if((la|0)!=(b|0)){continue}break}break ha}if((b|0)>=(ia|0)){break ia}while(1){g=o+(b<<3)|0;t=H[g>>2];qa:{ra:{if((b|0)>0){h=H[(o+(((b|0)<(da|0)?b:da)<<3)|0)-4>>2];break ra}h=H[ha>>2];fa=ha;if((b|0)<0){break qa}}fa=xa;if((b|0)>=(da|0)){break qa}fa=(o+(b<<3)|0)+4|0}H[g>>2]=t-((H[fa>>2]+h|0)+2>>2);b=b+1|0;if((ia|0)!=(b|0)){continue}break}}if((r|0)>=(la|0)){break ha}b=r;if((ya|0)>(b|0)){while(1){g=o+(b<<3)|0;b=b+1|0;H[g+4>>2]=H[g+4>>2]+(H[o+(b<<3)>>2]+H[g>>2]>>1);if((b|0)<(Wa|0)){continue}break}}if((b|0)>=(la|0)){break ha}while(1){g=ga;t=b;sa:{ta:{if((b|0)>=0){h=H[((b|0)<(m|0)?o+(b<<3)|0:g)>>2];b=b+1|0;break ta}h=H[o>>2];if((t|0)!=-1){b=t+1|0;g=o;break sa}b=0}if((m|0)<=(b|0)){break sa}g=o+(b<<3)|0}t=o+(t<<3)|0;H[t+4>>2]=H[t+4>>2]+(H[g>>2]+h>>1);if((b|0)<(la|0)){continue}break}}if(!Za(oa,l,x,wa,S,ab,1,0)){break ba}}x=S;if((Z|0)!=(x|0)){continue}break}}k=k+152|0;b=Q<<1;g=n<<1|1;b=b>>>0>g>>>0?b:g;Ua=b>>>0>>0?b:Z;Q=o+(q<<5)|0;b=o+(ka<<5)|0;va=b-4|0;g=(q|0)<(ka|0);Wa=g?Q+28|0:va;ya=b-8|0;Xa=g?Q+24|0:ya;Ga=b-12|0;Ya=g?Q+20|0:Ga;Ka=b-16|0;_a=(q|0)<0?Ra:g?Q+16|0:Ka;xa=u-1|0;$a=(V|0)<(xa|0)?V:xa;i=o+((g?q:ka)<<5)|0;La=(q|0)<=0;ab=La?Ra:i-16|0;Ma=(ka|0)>0;Na=Ma|(u|0)>1;bb=Q+(ja<<4)|0;cb=(o+(4-(ja<<2)<<2)|0)+(j<<5)|0;n=(E|0)<(ka|0)?E:ka;r=q+1|0;g=ea<<1;h=ba<<1|1;mb=g>>>0>>0?g:h;qb=o+(mb<<4)|0;rb=!u&(ka|0)==1;g=o+(u<<5)|0;S=g-20|0;z=g-24|0;ea=g-28|0;ba=g-32|0;aa=g-4|0;ga=g-8|0;da=g-12|0;la=g-16|0;ia=b-20|0;fa=b-24|0;pa=b-28|0;ta=b-32|0;sb=i-4|0;x=i-8|0;tb=i-12|0;while(1){ua:{va:{wa:{xa:{m=l;if(m>>>0>>0){b=wa-m|0;l=m+(b>>>0>=4?4:b)|0;Oa(oa,m,q,l,E,bb,1,8);Oa(oa,m,lb,l,kb,cb,1,8);if(!ja){if(!Na){break ua}if((q|0)>=(E|0)){break va}H[Q>>2]=H[Q>>2]-((H[ab>>2]+H[_a>>2]|0)+2>>2);ya:{if(!La){b=H[tb>>2];h=x;g=sb;break ya}b=H[o+20>>2];if((q|0)<0){break xa}h=$;g=ib}H[Q+4>>2]=H[Q+4>>2]-((H[Ya>>2]+b|0)+2>>2);H[Q+8>>2]=H[Q+8>>2]-((H[h>>2]+H[Xa>>2]|0)+2>>2);b=H[Wa>>2];g=H[g>>2];break wa}if(rb){H[o>>2]=H[o>>2]/2;H[o+4>>2]=H[o+4>>2]/2;H[o+8>>2]=H[o+8>>2]/2;H[o+12>>2]=H[o+12>>2]/2;break ua}g=q;if((E|0)>(g|0)){while(1){b=o+(g<<5)|0;za:{if((g|0)<0){h=H[o>>2];i=Ma|(g|0)!=-1;H[b+16>>2]=H[b+16>>2]-(((i?h:H[ta>>2])+h|0)+2>>2);h=H[ha>>2];H[b+20>>2]=H[b+20>>2]-(((i?h:H[pa>>2])+h|0)+2>>2);h=H[o+8>>2];H[b+24>>2]=H[b+24>>2]-(((i?h:H[fa>>2])+h|0)+2>>2);h=b;t=H[b+28>>2];b=H[o+12>>2];H[h+28>>2]=t-(((i?b:H[ia>>2])+b|0)+2>>2);g=g+1|0;break za}i=g+1|0;Aa:{if((i|0)<(ka|0)){g=o+(i<<5)|0;H[b+16>>2]=H[b+16>>2]-((H[b>>2]+H[g>>2]|0)+2>>2);H[b+20>>2]=H[b+20>>2]-((H[b+4>>2]+H[g+4>>2]|0)+2>>2);H[b+24>>2]=H[b+24>>2]-((H[b+8>>2]+H[g+8>>2]|0)+2>>2);H[b+28>>2]=H[b+28>>2]-((H[b+12>>2]+H[g+12>>2]|0)+2>>2);break Aa}h=H[b+16>>2];if((g|0)>=(ka|0)){H[b+16>>2]=h-((H[ta>>2]<<1)+2>>2);H[b+20>>2]=H[b+20>>2]-((H[pa>>2]<<1)+2>>2);H[b+24>>2]=H[b+24>>2]-((H[fa>>2]<<1)+2>>2);H[b+28>>2]=H[b+28>>2]-((H[ia>>2]<<1)+2>>2);break Aa}H[b+16>>2]=h-((H[b>>2]+H[ta>>2]|0)+2>>2);H[b+20>>2]=H[b+20>>2]-((H[b+4>>2]+H[pa>>2]|0)+2>>2);H[b+24>>2]=H[b+24>>2]-((H[b+8>>2]+H[fa>>2]|0)+2>>2);H[b+28>>2]=H[b+28>>2]-((H[b+12>>2]+H[ia>>2]|0)+2>>2)}g=i}if((E|0)!=(g|0)){continue}break}}g=j;if((V|0)<=(g|0)){break ua}while(1){b=o+(g<<5)|0;Ba:{if((g|0)<0){H[b>>2]=H[b>>2]+(H[o+16>>2]<<1>>1);H[b+4>>2]=H[b+4>>2]+(H[o+20>>2]<<1>>1);H[b+8>>2]=H[b+8>>2]+(H[o+24>>2]<<1>>1);H[b+12>>2]=H[b+12>>2]+(H[o+28>>2]<<1>>1);break Ba}i=H[b>>2];if(!g){h=(g|0)<(u|0);H[b>>2]=i+(H[o+16>>2]+H[(h?b+16|0:la)>>2]>>1);H[b+4>>2]=H[b+4>>2]+(H[o+20>>2]+H[(h?b+20|0:da)>>2]>>1);H[b+8>>2]=H[b+8>>2]+(H[o+24>>2]+H[(h?b+24|0:ga)>>2]>>1);H[b+12>>2]=H[b+12>>2]+(H[o+28>>2]+H[(h?b+28|0:aa)>>2]>>1);break Ba}if((g|0)<=(u|0)){h=(g|0)<(u|0);H[b>>2]=i+(H[b-16>>2]+H[(h?b+16|0:la)>>2]>>1);H[b+4>>2]=H[b+4>>2]+(H[b-12>>2]+H[(h?b+20|0:da)>>2]>>1);H[b+8>>2]=H[b+8>>2]+(H[b-8>>2]+H[(h?b+24|0:ga)>>2]>>1);H[b+12>>2]=H[b+12>>2]+(H[b-4>>2]+H[(h?b+28|0:aa)>>2]>>1);break Ba}H[b>>2]=i+(H[la>>2]<<1>>1);H[b+4>>2]=H[b+4>>2]+(H[da>>2]<<1>>1);H[b+8>>2]=H[b+8>>2]+(H[ga>>2]<<1>>1);H[b+12>>2]=H[b+12>>2]+(H[aa>>2]<<1>>1)}g=g+1|0;if((V|0)!=(g|0)){continue}break}break ua}m=R;u=Z;sa=sa+1|0;if((Y|0)!=(sa|0)){continue ea}break da}H[Q+4>>2]=H[Q+4>>2]-((b<<1)+2>>2);H[Q+8>>2]=H[Q+8>>2]-((H[$>>2]<<1)+2>>2);b=H[ib>>2];g=b}H[Q+12>>2]=H[Q+12>>2]-((b+g|0)+2>>2);i=q;b=r;g=b;if((b|0)<(n|0)){while(1){g=o+(b<<5)|0;i=o+(i<<5)|0;H[g>>2]=H[g>>2]-((H[i+16>>2]+H[g+16>>2]|0)+2>>2);H[g+4>>2]=H[g+4>>2]-((H[i+20>>2]+H[g+20>>2]|0)+2>>2);H[g+8>>2]=H[g+8>>2]-((H[i+24>>2]+H[g+24>>2]|0)+2>>2);H[g+12>>2]=H[g+12>>2]-((H[i+28>>2]+H[g+28>>2]|0)+2>>2);i=b;b=b+1|0;if((n|0)!=(b|0)){continue}break}g=n}if((g|0)>=(E|0)){break va}while(1){b=o+(g<<5)|0;i=(g|0)<(ka|0);Ca:{if((g|0)<=0){h=H[Ra>>2];if((g|0)>=0){H[b>>2]=H[b>>2]-((h+H[(i?b+16|0:Ka)>>2]|0)+2>>2);H[b+4>>2]=H[b+4>>2]-((H[o+20>>2]+H[(i?b+20|0:Ga)>>2]|0)+2>>2);H[b+8>>2]=H[b+8>>2]-((H[o+24>>2]+H[(i?b+24|0:ya)>>2]|0)+2>>2);H[b+12>>2]=H[b+12>>2]-((H[o+28>>2]+H[(i?b+28|0:va)>>2]|0)+2>>2);break Ca}H[b>>2]=H[b>>2]-((h<<1)+2>>2);H[b+4>>2]=H[b+4>>2]-((H[o+20>>2]<<1)+2>>2);H[b+8>>2]=H[b+8>>2]-((H[o+24>>2]<<1)+2>>2);H[b+12>>2]=H[b+12>>2]-((H[o+28>>2]<<1)+2>>2);break Ca}h=o+((i?g:ka)<<5)|0;t=H[h-16>>2];if(!i){H[b>>2]=H[b>>2]-((t+H[Ka>>2]|0)+2>>2);H[b+4>>2]=H[b+4>>2]-((H[h-12>>2]+H[Ga>>2]|0)+2>>2);H[b+8>>2]=H[b+8>>2]-((H[h-8>>2]+H[ya>>2]|0)+2>>2);H[b+12>>2]=H[b+12>>2]-((H[h-4>>2]+H[va>>2]|0)+2>>2);break Ca}H[b>>2]=H[b>>2]-((t+H[b+16>>2]|0)+2>>2);H[b+4>>2]=H[b+4>>2]-((H[h-12>>2]+H[b+20>>2]|0)+2>>2);H[b+8>>2]=H[b+8>>2]-((H[h-8>>2]+H[b+24>>2]|0)+2>>2);H[b+12>>2]=H[b+12>>2]-((H[h-4>>2]+H[b+28>>2]|0)+2>>2)}g=g+1|0;if((E|0)!=(g|0)){continue}break}}if((j|0)>=(V|0)){break ua}g=j;if((xa|0)>(g|0)){while(1){b=o+(g<<5)|0;H[b+16>>2]=H[b+16>>2]+(H[b+32>>2]+H[b>>2]>>1);H[b+20>>2]=H[b+20>>2]+(H[b+36>>2]+H[b+4>>2]>>1);H[b+24>>2]=H[b+24>>2]+(H[b+40>>2]+H[b+8>>2]>>1);H[b+28>>2]=H[b+28>>2]+(H[b+44>>2]+H[b+12>>2]>>1);g=g+1|0;if(($a|0)>(g|0)){continue}break}}if((g|0)>=(V|0)){break ua}while(1){b=o+(g<<5)|0;Da:{Ea:{Fa:{if((g|0)<0){i=H[o>>2];if((g|0)!=-1){break Fa}if((u|0)<=0){H[b+16>>2]=H[b+16>>2]+(i+H[ba>>2]>>1);H[b+20>>2]=H[b+20>>2]+(H[ea>>2]+H[o+4>>2]>>1);H[b+24>>2]=H[b+24>>2]+(H[z>>2]+H[o+8>>2]>>1);t=H[o+12>>2];i=H[S>>2];break Ea}break Fa}i=g+1|0;Ga:{if((i|0)<(u|0)){g=o+(i<<5)|0;H[b+16>>2]=H[b+16>>2]+(H[g>>2]+H[b>>2]>>1);H[b+20>>2]=H[b+20>>2]+(H[g+4>>2]+H[b+4>>2]>>1);H[b+24>>2]=H[b+24>>2]+(H[g+8>>2]+H[b+8>>2]>>1);H[b+28>>2]=H[b+28>>2]+(H[g+12>>2]+H[b+12>>2]>>1);break Ga}if((g|0)>=(u|0)){H[b+16>>2]=H[b+16>>2]+H[ba>>2];H[b+20>>2]=H[b+20>>2]+H[ea>>2];H[b+24>>2]=H[b+24>>2]+H[z>>2];H[b+28>>2]=H[b+28>>2]+H[S>>2];break Ga}H[b+16>>2]=H[b+16>>2]+(H[ba>>2]+H[b>>2]>>1);H[b+20>>2]=H[b+20>>2]+(H[ea>>2]+H[b+4>>2]>>1);H[b+24>>2]=H[b+24>>2]+(H[z>>2]+H[b+8>>2]>>1);H[b+28>>2]=H[b+28>>2]+(H[S>>2]+H[b+12>>2]>>1)}g=i;break Da}H[b+16>>2]=i+H[b+16>>2];H[b+20>>2]=H[b+20>>2]+H[o+4>>2];H[b+24>>2]=H[b+24>>2]+H[o+8>>2];t=H[o+12>>2];i=t}H[b+28>>2]=H[b+28>>2]+(i+t>>1);g=g+1|0}if((V|0)!=(g|0)){continue}break}}if(Za(oa,m,mb,l,Ua,qb,1,4)){continue}break}break}break ba}Ca(o);g=1}b=H[ma-16>>2];i=H[gb>>2];h=H[hb>>2];q=H[ma-8>>2];Oa(oa,b-i|0,H[ma-12>>2]-h|0,q-i|0,H[ma-4>>2]-h|0,H[C+52>>2],1,q-b|0);Va(oa);break X}Va(oa);Ca(o);g=0;break X}Va(oa);g=0;break X}g=0;Sa(m);Ca(j)}na=_+32|0;if(g){break W}break b}h=0;J=na+-64|0;na=J;Ha:{Ia:{if(H[D+64>>2]){r=H[C+28>>2];i=r+N(H[C+24>>2],152)|0;k=H[i-152>>2];o=1;x=H[D+44>>2];M=H[x+4>>2];if((j|0)==1){break Ha}t=j-1|0;b=r;Ja:{if((j|0)!=2){q=t&1;n=t&-2;g=0;while(1){j=H[b+160>>2]-H[b+152>>2]|0;h=h>>>0>j>>>0?h:j;j=H[b+164>>2]-H[b+156>>2]|0;h=h>>>0>j>>>0?h:j;j=H[b+312>>2]-H[b+304>>2]|0;h=h>>>0>j>>>0?h:j;j=H[b+316>>2]-H[b+308>>2]|0;h=h>>>0>j>>>0?h:j;b=b+304|0;g=g+2|0;if((n|0)!=(g|0)){continue}break}if(!q){break Ja}}g=H[b+160>>2]-H[b+152>>2]|0;g=g>>>0>>0?h:g;b=H[b+164>>2]-H[b+156>>2]|0;h=b>>>0>>0?g:b}o=0;if(h>>>0>134217727){break Ha}b=H[i-144>>2];g=H[r+4>>2];i=H[r+12>>2];n=H[r>>2];j=H[r+8>>2];$=h<<5;E=Ia($);H[J+32>>2]=E;if(!E){break Ha}q=i-g|0;i=j-n|0;g=M>>>1|0;ea=g>>>0<=2?2:g;u=b-k|0;aa=u<<5;ga=N(u,28);_=N(u,24);da=N(u,20);ka=u<<4;W=N(u,12);Z=u<<3;H[J>>2]=E;ba=E+32|0;b=H[C+36>>2];while(1){l=q;H[J+8>>2]=l;h=i;H[J+40>>2]=h;Y=H[r+156>>2];w=H[r+164>>2];n=H[r+160>>2];g=H[r+152>>2];H[J+56>>2]=0;H[J+52>>2]=h;H[J+48>>2]=0;R=(g|0)%2|0;H[J+44>>2]=R;i=n-g|0;j=i-h|0;H[J+60>>2]=j;H[J+36>>2]=j;S=(M|0)<2;q=w-Y|0;Ka:{if(!(!S&q>>>0>15)){o=0;k=b;if(q>>>0<8){break Ka}m=i&-2;Q=i&1;j=0;R=(n|0)==(g+1|0);while(1){g=J+32|0;zb(g,k,u,8);Ta(g);if(i){h=0;o=0;La:{if(!R){while(1){g=(h<<2)+k|0;n=E+(h<<5)|0;L[g>>2]=L[n>>2];z=u<<2;L[z+g>>2]=L[n+4>>2];L[g+Z>>2]=L[n+8>>2];L[g+W>>2]=L[n+12>>2];n=h|1;g=(n<<2)+k|0;n=E+(n<<5)|0;L[g>>2]=L[n>>2];L[g+z>>2]=L[n+4>>2];L[g+Z>>2]=L[n+8>>2];L[g+W>>2]=L[n+12>>2];h=h+2|0;o=o+2|0;if((m|0)!=(o|0)){continue}break}if(!Q){break La}}g=(h<<2)+k|0;h=E+(h<<5)|0;L[g>>2]=L[h>>2];L[g+(u<<2)>>2]=L[h+4>>2];L[g+Z>>2]=L[h+8>>2];L[g+W>>2]=L[h+12>>2]}h=0;while(1){g=(h<<2)+k|0;n=E+(h<<5)|0;L[g+ka>>2]=L[n+16>>2];L[g+da>>2]=L[n+20>>2];L[g+_>>2]=L[n+24>>2];L[g+ga>>2]=L[n+28>>2];h=h+1|0;if((i|0)!=(h|0)){continue}break}}k=k+aa|0;g=j+15|0;o=j+8|0;j=o;if(g>>>0>>0){continue}break}break Ka}g=q>>>3|0;m=g>>>0>>0?g:M;Q=(q>>>0)/(m>>>0)&-8;o=q&-8;g=0;k=b;while(1){n=Fa(48);if(!n){break Ia}z=Ia($);H[n>>2]=z;if(!z){o=0;Sa(x);Ca(n);Ca(E);break Ha}H[n+40>>2]=k;H[n+36>>2]=u;H[n+32>>2]=i;H[n+28>>2]=j;H[n+24>>2]=0;H[n+20>>2]=h;H[n+16>>2]=0;H[n+12>>2]=R;H[n+8>>2]=h;H[n+4>>2]=j;V=o-N(g,Q)|0;g=g+1|0;z=(m|0)==(g|0)?V:Q;H[n+44>>2]=z;eb(x,12,n);k=(N(u,z)<<2)+k|0;if((g|0)!=(m|0)){continue}break}Sa(x)}Ma:{if(o>>>0>=q>>>0){break Ma}h=J+32|0;g=q-o|0;zb(h,k,u,g);Ta(h);if(!i){break Ma}R=g&-4;Q=g&3;m=0;z=Y+(o-w|0)>>>0>4294967292;while(1){j=(m<<2)+k|0;o=E+(m<<5)|0;h=0;n=0;g=0;Na:{if(!z){while(1){L[j+(N(h,u)<<2)>>2]=L[o+(h<<2)>>2];g=h|1;L[j+(N(g,u)<<2)>>2]=L[o+(g<<2)>>2];g=h|2;L[j+(N(g,u)<<2)>>2]=L[o+(g<<2)>>2];g=h|3;L[j+(N(g,u)<<2)>>2]=L[o+(g<<2)>>2];h=h+4|0;n=n+4|0;if((R|0)!=(n|0)){continue}break}g=h;if(!Q){break Na}}h=0;while(1){L[j+(N(g,u)<<2)>>2]=L[o+(g<<2)>>2];g=g+1|0;h=h+1|0;if((Q|0)!=(h|0)){continue}break}}m=m+1|0;if((m|0)!=(i|0)){continue}break}}n=q-l|0;H[J+4>>2]=n;g=H[r+156>>2];H[J+28>>2]=n;H[J+24>>2]=0;H[J+20>>2]=l;H[J+16>>2]=0;Q=(g|0)%2|0;H[J+12>>2]=Q;Oa:{if(!(!S&i>>>0>15)){g=b;if(i>>>0<8){break Oa}oa=q&-2;la=q&1;ia=n&-2;ha=n&1;V=l&-2;ma=l&1;h=Q<<5;R=ba-h|0;S=h+E|0;fa=N(l,u)<<2;pa=w-1|0;sa=(pa|0)==(l+Y|0);j=i;while(1){h=0;k=0;Pa:{Qa:{switch(l|0){default:while(1){m=(N(h,u)<<2)+g|0;z=H[m+28>>2];o=S+(h<<6)|0;H[o+24>>2]=H[m+24>>2];H[o+28>>2]=z;z=H[m+20>>2];H[o+16>>2]=H[m+16>>2];H[o+20>>2]=z;z=H[m+12>>2];H[o+8>>2]=H[m+8>>2];H[o+12>>2]=z;z=H[m+4>>2];H[o>>2]=H[m>>2];H[o+4>>2]=z;o=h|1;m=S+(o<<6)|0;o=(N(o,u)<<2)+g|0;z=H[o+28>>2];H[m+24>>2]=H[o+24>>2];H[m+28>>2]=z;z=H[o+20>>2];H[m+16>>2]=H[o+16>>2];H[m+20>>2]=z;z=H[o+12>>2];H[m+8>>2]=H[o+8>>2];H[m+12>>2]=z;z=H[o+4>>2];H[m>>2]=H[o>>2];H[m+4>>2]=z;h=h+2|0;k=k+2|0;if((V|0)!=(k|0)){continue}break};if(!ma){break Pa}break;case 0:break Pa;case 1:break Qa}}k=S+(h<<6)|0;h=(N(h,u)<<2)+g|0;m=H[h+28>>2];H[k+24>>2]=H[h+24>>2];H[k+28>>2]=m;m=H[h+20>>2];H[k+16>>2]=H[h+16>>2];H[k+20>>2]=m;m=H[h+12>>2];H[k+8>>2]=H[h+8>>2];H[k+12>>2]=m;m=H[h+4>>2];H[k>>2]=H[h>>2];H[k+4>>2]=m}Ra:{if((l|0)==(q|0)){break Ra}z=g+fa|0;h=0;m=0;if(!sa){while(1){k=z+(N(h,u)<<2)|0;ja=H[k+28>>2];o=R+(h<<6)|0;H[o+24>>2]=H[k+24>>2];H[o+28>>2]=ja;ja=H[k+20>>2];H[o+16>>2]=H[k+16>>2];H[o+20>>2]=ja;ja=H[k+12>>2];H[o+8>>2]=H[k+8>>2];H[o+12>>2]=ja;ja=H[k+4>>2];H[o>>2]=H[k>>2];H[o+4>>2]=ja;o=h|1;k=R+(o<<6)|0;o=z+(N(o,u)<<2)|0;ja=H[o+28>>2];H[k+24>>2]=H[o+24>>2];H[k+28>>2]=ja;ja=H[o+20>>2];H[k+16>>2]=H[o+16>>2];H[k+20>>2]=ja;ja=H[o+12>>2];H[k+8>>2]=H[o+8>>2];H[k+12>>2]=ja;ja=H[o+4>>2];H[k>>2]=H[o>>2];H[k+4>>2]=ja;h=h+2|0;m=m+2|0;if((ia|0)!=(m|0)){continue}break}if(!ha){break Ra}}k=R+(h<<6)|0;h=z+(N(h,u)<<2)|0;m=H[h+28>>2];H[k+24>>2]=H[h+24>>2];H[k+28>>2]=m;m=H[h+20>>2];H[k+16>>2]=H[h+16>>2];H[k+20>>2]=m;m=H[h+12>>2];H[k+8>>2]=H[h+8>>2];H[k+12>>2]=m;m=H[h+4>>2];H[k>>2]=H[h>>2];H[k+4>>2]=m}Ta(J);Sa:{if(!q){break Sa}h=0;k=0;if((Y|0)!=(pa|0)){while(1){m=E+(h<<5)|0;z=H[m+28>>2];o=(N(h,u)<<2)+g|0;H[o+24>>2]=H[m+24>>2];H[o+28>>2]=z;z=H[m+20>>2];H[o+16>>2]=H[m+16>>2];H[o+20>>2]=z;z=H[m+12>>2];H[o+8>>2]=H[m+8>>2];H[o+12>>2]=z;z=H[m+4>>2];H[o>>2]=H[m>>2];H[o+4>>2]=z;o=h|1;m=(N(o,u)<<2)+g|0;o=E+(o<<5)|0;z=H[o+28>>2];H[m+24>>2]=H[o+24>>2];H[m+28>>2]=z;z=H[o+20>>2];H[m+16>>2]=H[o+16>>2];H[m+20>>2]=z;z=H[o+12>>2];H[m+8>>2]=H[o+8>>2];H[m+12>>2]=z;z=H[o+4>>2];H[m>>2]=H[o>>2];H[m+4>>2]=z;h=h+2|0;k=k+2|0;if((oa|0)!=(k|0)){continue}break}if(!la){break Sa}}k=(N(h,u)<<2)+g|0;h=E+(h<<5)|0;m=H[h+28>>2];H[k+24>>2]=H[h+24>>2];H[k+28>>2]=m;m=H[h+20>>2];H[k+16>>2]=H[h+16>>2];H[k+20>>2]=m;m=H[h+12>>2];H[k+8>>2]=H[h+8>>2];H[k+12>>2]=m;m=H[h+4>>2];H[k>>2]=H[h>>2];H[k+4>>2]=m}g=g+32|0;j=j-8|0;if(j>>>0>7){continue}break}break Oa}g=i>>>3|0;k=g>>>0>>0?g:ea;m=(i>>>0)/(k>>>0)&-8;o=i&-8;j=0;g=b;while(1){h=Fa(48);if(!h){break Ia}R=Ia($);H[h>>2]=R;if(!R){o=0;Sa(x);Ca(h);Ca(E);break Ha}H[h+40>>2]=g;H[h+36>>2]=u;H[h+32>>2]=q;H[h+28>>2]=n;H[h+24>>2]=0;H[h+20>>2]=l;H[h+16>>2]=0;H[h+12>>2]=Q;H[h+8>>2]=l;H[h+4>>2]=n;V=o-N(j,m)|0;j=j+1|0;R=(k|0)==(j|0)?V:m;H[h+44>>2]=R;eb(x,13,h);g=(R<<2)+g|0;if((k|0)!=(j|0)){continue}break}Sa(x)}j=i&7;Ta:{if(!j){break Ta}Q=Q<<5;Ua:{if(!l){break Ua}m=E+Q|0;k=j<<2;h=0;if((l|0)!=1){R=l&1;S=l&-2;o=0;while(1){z=!k;if(!z){B(m+(h<<6)|0,(N(h,u)<<2)+g|0,k)}if(!z){z=h|1;B(m+(z<<6)|0,(N(u,z)<<2)+g|0,k)}h=h+2|0;o=o+2|0;if((S|0)!=(o|0)){continue}break}if(!R){break Ua}}if(!k){break Ua}B(m+(h<<6)|0,(N(h,u)<<2)+g|0,k)}Va:{if((l|0)==(q|0)){break Va}m=ba-Q|0;Q=(N(l,u)<<2)+g|0;k=j<<2;h=0;if((Y|0)!=(w+(l^-1)|0)){l=n&1;n=n&-2;o=0;while(1){R=!k;if(!R){B(m+(h<<6)|0,Q+(N(h,u)<<2)|0,k)}if(!R){R=h|1;B(m+(R<<6)|0,Q+(N(u,R)<<2)|0,k)}h=h+2|0;o=o+2|0;if((n|0)!=(o|0)){continue}break}if(!l){break Va}}if(!k){break Va}B(m+(h<<6)|0,Q+(N(h,u)<<2)|0,k)}Ta(J);if(!q){break Ta}k=j<<2;h=0;if((w|0)!=(Y+1|0)){n=q&1;j=q&-2;o=0;while(1){m=!k;if(!m){B((N(h,u)<<2)+g|0,E+(h<<5)|0,k)}if(!m){m=h|1;B((N(m,u)<<2)+g|0,E+(m<<5)|0,k)}h=h+2|0;o=o+2|0;if((j|0)!=(o|0)){continue}break}if(!n){break Ta}}if(!k){break Ta}B((N(h,u)<<2)+g|0,E+(h<<5)|0,k)}r=r+152|0;t=t-1|0;if(t){continue}break}o=1;Ca(E);break Ha}o=1;q=H[C+28>>2];Q=q+N(j,152)|0;da=Q-152|0;if(H[da>>2]==H[Q-144>>2]){break Ha}ka=Q-148|0;if(H[ka>>2]==H[Q-140>>2]){break Ha}g=H[q+4>>2];i=H[q+12>>2];k=H[q>>2];n=H[q+8>>2];t=H[C+68>>2];E=H[C+64>>2];x=H[C+60>>2];Y=H[C+56>>2];Z=_b(C,j);if(!Z){o=0;break Ha}if((j|0)==1){b=H[Q-16>>2];g=H[da>>2];i=H[ka>>2];h=H[Q-8>>2];Oa(Z,b-g|0,H[Q-12>>2]-i|0,h-g|0,H[Q-4>>2]-i|0,H[C+52>>2],1,h-b|0);Va(Z);break Ha}b=q;Wa:{if((j|0)!=2){h=j-1|0;m=h&1;l=h&-2;h=0;o=0;while(1){r=H[b+160>>2]-H[b+152>>2]|0;h=h>>>0>r>>>0?h:r;r=H[b+164>>2]-H[b+156>>2]|0;h=h>>>0>r>>>0?h:r;r=H[b+312>>2]-H[b+304>>2]|0;h=h>>>0>r>>>0?h:r;r=H[b+316>>2]-H[b+308>>2]|0;h=h>>>0>r>>>0?h:r;b=b+304|0;o=o+2|0;if((l|0)!=(o|0)){continue}break}if(!m){break Wa}}m=H[b+160>>2]-H[b+152>>2]|0;h=h>>>0>m>>>0?h:m;b=H[b+164>>2]-H[b+156>>2]|0;h=b>>>0>>0?h:b}Xa:{if(h>>>0>=134217728){break Xa}R=Ia(h<<5);H[J+32>>2]=R;if(!R){break Xa}H[J>>2]=R;Ya:{if(j){r=i-g|0;b=n-k|0;oa=R+32|0;W=j;la=H[C+20>>2];z=1;ea=0;while(1){H[J+8>>2]=r;H[J+40>>2]=b;i=H[q+164>>2];h=H[q+160>>2];g=H[q+156>>2];k=H[q+152>>2];$=(k|0)%2|0;H[J+44>>2]=$;ia=(g|0)%2|0;H[J+12>>2]=ia;M=h-k|0;ba=M-b|0;H[J+36>>2]=ba;k=i-g|0;ha=k-r|0;H[J+4>>2]=ha;i=Y;o=i;h=x;g=h;j=E;m=j;n=t;u=n;Za:{if(!ea&(z|0)==(la|0)){break Za}w=la-z|0;g=0;o=0;if(i){i=w&31;if((w&63)>>>0>=32){l=-1<>>32-i}i=Y+(h^-1)|0;h=l^-1;n=i>>>0>>0?h+1|0:h;h=w&31;if((w&63)>>>0>=32){o=n>>>h|0}else{o=((1<>>h}}if(x){g=w&31;if((w&63)>>>0>=32){l=-1<>>32-g}g=x+(i^-1)|0;i=l^-1;h=g>>>0>>0?i+1|0:i;i=w&31;if((w&63)>>>0>=32){g=h>>>i|0}else{g=((1<>>i}}n=0;j=0;if(E){i=w&31;if((w&63)>>>0>=32){l=-1<>>32-i}i=E+(h^-1)|0;h=l^-1;j=i>>>0>>0?h+1|0:h;h=w&31;if((w&63)>>>0>=32){j=j>>>h|0}else{j=((1<>>h}}if(t){i=w&31;if((w&63)>>>0>=32){l=-1<>>32-i}i=t+(h^-1)|0;h=l^-1;n=i>>>0>>0?h+1|0:h;h=w&31;if((w&63)>>>0>=32){n=n>>>h|0}else{n=((1<>>h}}m=0;i=0;S=1<>>0>>0){i=w&31;if((w&63)>>>0>=32){l=-1<>>32-i}h=h^-1;i=h+(Y-S|0)|0;l=l^-1;l=h>>>0>i>>>0?l+1|0:l;h=w&31;if((w&63)>>>0>=32){i=l>>>h|0}else{i=((1<>>h}}if(E>>>0>S>>>0){h=w&31;if((w&63)>>>0>=32){l=-1<>>32-h}m=m^-1;h=m+(E-S|0)|0;l=l^-1;l=h>>>0>>0?l+1|0:l;m=w&31;if((w&63)>>>0>=32){m=l>>>m|0}else{m=((1<>>m}}u=0;h=0;if(x>>>0>S>>>0){h=w&31;if((w&63)>>>0>=32){l=-1<>>32-h}aa=aa^-1;h=aa+(x-S|0)|0;l=l^-1;aa=h>>>0>>0?l+1|0:l;l=w&31;if((w&63)>>>0>=32){h=aa>>>l|0}else{h=((1<>>l}}if(t>>>0<=S>>>0){break Za}l=w&31;if((w&63)>>>0>=32){l=-1<>>32-l}aa=u^-1;u=aa+(t-S|0)|0;l=l^-1;S=u>>>0>>0?l+1|0:l;l=w&31;if((w&63)>>>0>=32){u=S>>>l|0}else{u=((1<>>l}}l=H[q+180>>2];w=m-l|0;m=m>>>0>=w>>>0?w:0;w=m+4|0;m=m>>>0>w>>>0?-1:w;ga=m>>>0>>0?m:ba;m=H[q+216>>2];w=j-m|0;j=j>>>0>=w>>>0?w:0;w=j+4|0;j=j>>>0>w>>>0?-1:w;_=b>>>0>j>>>0?j:b;j=($?ga:_)<<1;w=($?_:ga)<<1|1;j=j>>>0>w>>>0?j:w;S=j>>>0>>0;l=i-l|0;i=i>>>0>=l>>>0?l:0;l=i-4|0;ba=i>>>0>=l>>>0?l:0;i=o-m|0;i=i>>>0<=o>>>0?i:0;m=i-4|0;aa=i>>>0>=m>>>0?m:0;V=($?ba:aa)<<1;ma=($?aa:ba)<<1|1;fa=V>>>0>>0;m=H[q+184>>2];i=g-m|0;g=g>>>0>=i>>>0?i:0;i=g-4|0;i=g>>>0>=i>>>0?i:0;o=i;g=H[q+220>>2];l=h-g|0;h=h>>>0>=l>>>0?l:0;l=h-4|0;h=h>>>0>=l>>>0?l:0;l=h;m=n-m|0;n=m>>>0<=n>>>0?m:0;m=n+4|0;n=m>>>0>>0?-1:m;n=n>>>0>>0?n:r;m=n;g=u-g|0;g=g>>>0<=u>>>0?g:0;u=g+4|0;g=g>>>0>u>>>0?-1:u;w=g>>>0>>0?g:ha;u=w;if(ia){l=i;u=m;o=h;m=w}S=S?j:M;j=fa?V:ma;H[J+60>>2]=ga;H[J+56>>2]=ba;H[J+52>>2]=_;H[J+48>>2]=aa;_a:{if(k>>>0<8){b=7;g=0;break _a}g=$<<5;ha=(oa-g|0)+(ba<<6)|0;V=(g+R|0)+(aa<<6)|0;ga=b+ga|0;ma=b+ba|0;fa=r+w|0;pa=h+r|0;sa=R+(j<<5)|0;g=0;while(1){b=g|7;$a:{if(!(g>>>0>>0&b>>>0>=i>>>0|g>>>0>>0&b>>>0>=pa>>>0)){g=g+8|0;break $a}b=k-g|0;ja=b>>>0>=8?8:b;b=0;while(1){$=b+g|0;ba=$+1|0;wa=b<<2;Oa(Z,aa,$,_,ba,wa+V|0,16,0);Oa(Z,ma,$,ga,ba,ha+wa|0,16,0);b=b+1|0;if((ja|0)!=(b|0)){continue}break}Ta(J+32|0);b=g;g=g+8|0;if(!Za(Z,j,b,S,g,sa,8,1)){break Ya}}b=g|7;if(k>>>0>b>>>0){continue}break}}if(!(!(g>>>0>>0&b>>>0>=i>>>0)&(r+w>>>0<=g>>>0|h+r>>>0>b>>>0)|g>>>0>=k>>>0)){b=J+32|0;$=0;ga=k-g|0;if(ga){while(1){ba=g+$|0;aa=ba+1|0;_=H[b+16>>2];ha=$<<2;Oa(Z,_,ba,H[b+20>>2],aa,ha+((H[b>>2]+(H[b+12>>2]<<5)|0)+(_<<6)|0)|0,16,0);_=H[b+24>>2];V=H[b+8>>2];Oa(Z,_+V|0,ba,V+H[b+28>>2]|0,aa,(ha+((H[b>>2]-(H[b+12>>2]<<5)|0)+(_<<6)|0)|0)+32|0,16,0);$=$+1|0;if((ga|0)!=($|0)){continue}break}}Ta(b);if(!Za(Z,j,g,S,k,R+(j<<5)|0,8,1)){break Ya}}H[J+28>>2]=w;H[J+24>>2]=h;H[J+20>>2]=n;H[J+16>>2]=i;if(j>>>0>>0){b=m<<1;g=u<<1|1;b=b>>>0>g>>>0?b:g;g=b>>>0>>0?b:k;b=ia<<5;m=(oa-b|0)+(h<<6)|0;u=(b+R|0)+(i<<6)|0;w=r+w|0;h=h+r|0;b=o<<1;l=l<<1|1;l=b>>>0>>0?b:l;r=R+(l<<5)|0;while(1){b=S-j|0;b=(b>>>0>=8?8:b)+j|0;Oa(Z,j,i,b,n,u,1,16);Oa(Z,j,h,b,w,m,1,16);Ta(J);if(!Za(Z,j,l,b,g,r,1,8)){break Ya}j=j+8|0;if(S>>>0>j>>>0){continue}break}}q=q+152|0;b=M;r=k;z=z+1|0;ea=z?ea:ea+1|0;if(ea|(z|0)!=(W|0)){continue}break}}o=1;b=H[Q-16>>2];g=H[da>>2];i=H[ka>>2];h=H[Q-8>>2];Oa(Z,b-g|0,H[Q-12>>2]-i|0,h-g|0,H[Q-4>>2]-i|0,H[C+52>>2],1,h-b|0);Va(Z);Ca(R);break Ha}Va(Z);Ca(R);o=0;break Ha}Va(Z);o=0;break Ha}o=0;Sa(x);Ca(E)}na=J- -64|0;if(o){break W}break b}s=s+1080|0;A=A+52|0;C=C+76|0;p=p+1|0;if(p>>>0>2]){continue}break}s=H[D+32>>2];v=H[H[D+20>>2]>>2]}k=H[s+16>>2];ab:{if(H[D+68>>2]|!k){break ab}A=H[v+20>>2];g=H[A+28>>2];bb:{cb:{i=H[D+64>>2];if(i){p=H[v+16>>2];if(p>>>0<3){break bb}b=H[A+24>>2];if(!((b|0)==H[A+100>>2]&(b|0)==H[A+176>>2])){Ba(f,1,10089,0);break b}h=H[H[D+24>>2]+24>>2];q=H[h+36>>2];db:{if((q|0)!=H[h+88>>2]|(q|0)!=H[h+140>>2]){break db}h=N(b,152);b=h+g|0;b=N(H[b-140>>2]-H[b-148>>2]|0,H[b-144>>2]-H[b-152>>2]|0);g=h+H[A+104>>2]|0;if((b|0)!=(N(H[g-140>>2]-H[g-148>>2]|0,H[g-144>>2]-H[g-152>>2]|0)|0)){break db}g=h+H[A+180>>2]|0;if((N(H[g-140>>2]-H[g-148>>2]|0,H[g-144>>2]-H[g-152>>2]|0)|0)==(b|0)){break cb}}Ba(f,1,10089,0);break b}p=H[v+16>>2];if(p>>>0<3){break bb}h=H[H[D+24>>2]+24>>2];b=H[h+36>>2];eb:{if((b|0)!=H[h+88>>2]){break eb}h=H[h+140>>2];if((h|0)!=(b|0)){break eb}q=N(b,152);b=g+q|0;b=N(H[b+148>>2]-H[b+140>>2]|0,H[b+144>>2]-H[b+136>>2]|0);g=q+H[A+104>>2]|0;if((b|0)!=(N(H[g+148>>2]-H[g+140>>2]|0,H[g+144>>2]-H[g+136>>2]|0)|0)){break eb}g=H[A+180>>2]+N(h,152)|0;if((N(H[g+148>>2]-H[g+140>>2]|0,H[g+144>>2]-H[g+136>>2]|0)|0)==(b|0)){break cb}}Ba(f,1,10089,0);break b}if((k|0)==2){if(!H[s+5608>>2]){break ab}i=Fa(p<<2);if(!i){break b}n=H[v+16>>2];fb:{if(!n){break fb}gb:{hb:{if(H[D+64>>2]){p=n&3;g=0;if(n>>>0>=4){break hb}C=0;break gb}p=n&3;g=0;ib:{if(n>>>0<4){C=0;break ib}k=n&-4;C=0;h=0;while(1){q=i+(C<<2)|0;H[q>>2]=H[A+52>>2];H[q+4>>2]=H[A+128>>2];H[q+8>>2]=H[A+204>>2];H[q+12>>2]=H[A+280>>2];C=C+4|0;A=A+304|0;h=h+4|0;if((k|0)!=(h|0)){continue}break}if(!p){break fb}}while(1){H[i+(C<<2)>>2]=H[A+52>>2];C=C+1|0;A=A+76|0;g=g+1|0;if((p|0)!=(g|0)){continue}break}break fb}k=n&-4;C=0;h=0;while(1){q=i+(C<<2)|0;H[q>>2]=H[A+36>>2];H[q+4>>2]=H[A+112>>2];H[q+8>>2]=H[A+188>>2];H[q+12>>2]=H[A+264>>2];C=C+4|0;A=A+304|0;h=h+4|0;if((k|0)!=(h|0)){continue}break}if(!p){break fb}}while(1){H[i+(C<<2)>>2]=H[A+36>>2];C=C+1|0;A=A+76|0;g=g+1|0;if((p|0)!=(g|0)){continue}break}}h=H[s+5608>>2];g=i;k=0;s=Fa(n<<3);i=0;jb:{if(!s){break jb}if(!(!b|!n)){i=b;u=s+(n<<2)|0;r=n&-4;m=n&3;o=n-1|0;while(1){v=0;p=0;b=0;t=o>>>0<3;kb:{if(!t){while(1){b=v<<2;L[b+s>>2]=L[H[b+g>>2]>>2];q=b|4;L[q+s>>2]=L[H[g+q>>2]>>2];q=b|8;L[q+s>>2]=L[H[g+q>>2]>>2];b=b|12;L[b+s>>2]=L[H[b+g>>2]>>2];v=v+4|0;p=p+4|0;if((r|0)!=(p|0)){continue}break}b=v;if(!m){break kb}}v=0;while(1){q=b<<2;L[q+s>>2]=L[H[g+q>>2]>>2];b=b+1|0;v=v+1|0;if((m|0)!=(v|0)){continue}break}}q=0;b=h;while(1){E=q<<2;j=E+u|0;H[j>>2]=0;ca=O(0);v=0;p=0;lb:{if(!t){while(1){l=s+(v<<2)|0;ca=O(O(L[b>>2]*L[l>>2])+ca);L[j>>2]=ca;ca=O(O(L[b+4>>2]*L[l+4>>2])+ca);L[j>>2]=ca;ca=O(O(L[b+8>>2]*L[l+8>>2])+ca);L[j>>2]=ca;ca=O(O(L[b+12>>2]*L[l+12>>2])+ca);L[j>>2]=ca;v=v+4|0;b=b+16|0;p=p+4|0;if((r|0)!=(p|0)){continue}break}p=v;if(!m){break lb}}v=0;while(1){ca=O(O(L[b>>2]*L[s+(p<<2)>>2])+ca);L[j>>2]=ca;p=p+1|0;b=b+4|0;v=v+1|0;if((m|0)!=(v|0)){continue}break}}v=g+E|0;p=H[v>>2];H[v>>2]=p+4;L[p>>2]=ca;q=q+1|0;if((n|0)!=(q|0)){continue}break}k=k+1|0;if((i|0)!=(k|0)){continue}break}}Ca(s);i=1}b=i;Ca(g);if(b){break ab}break b}if(H[H[s+5584>>2]+20>>2]==1){if(i){cc(H[A+36>>2],H[A+112>>2],H[A+188>>2],b);break ab}cc(H[A+52>>2],H[A+128>>2],H[A+204>>2],b);break ab}if(i){bc(H[A+36>>2],H[A+112>>2],H[A+188>>2],b);break ab}bc(H[A+52>>2],H[A+128>>2],H[A+204>>2],b);break ab}H[ra>>2]=p;Ba(f,1,10150,ra)}m=H[H[D+20>>2]>>2];if(!H[m+16>>2]){X=1;break b}l=H[D+68>>2];k=H[m+20>>2];b=H[H[D+32>>2]+5584>>2];q=H[H[D+24>>2]+24>>2];i=0;while(1){mb:{if(H[l+(i<<2)>>2]?0:l){break mb}h=H[k+28>>2];g=h+N(H[q+36>>2],152)|0;nb:{if(!H[D+64>>2]){h=H[g+148>>2]-H[g+140>>2]|0;v=H[g+144>>2]-H[g+136>>2]|0;r=0;p=52;break nb}h=h+N(H[k+24>>2],152)|0;v=H[g+8>>2]-H[g>>2]|0;r=H[h-144>>2]-(v+H[h-152>>2]|0)|0;h=H[g+12>>2]-H[g+4>>2]|0;p=36}g=H[q+24>>2];ob:{if(H[q+32>>2]){g=1<>2];if(H[b+20>>2]==1){n=v&-2;j=v&1;A=0;r=r<<2;while(1){p=0;pb:{if((v|0)!=1){while(1){g=H[b+1076>>2]+H[X>>2]|0;H[X>>2]=(g|0)<(s|0)?s:(g|0)<(C|0)?g:C;g=H[b+1076>>2]+H[X+4>>2]|0;H[X+4>>2]=(g|0)<(s|0)?s:(g|0)<(C|0)?g:C;X=X+8|0;p=p+2|0;if((n|0)!=(p|0)){continue}break}if(!j){break pb}}g=H[b+1076>>2]+H[X>>2]|0;H[X>>2]=(g|0)<(s|0)?s:(g|0)<(C|0)?g:C;X=X+4|0}X=r+X|0;A=A+1|0;if((A|0)!=(h|0)){continue}break}break mb}u=s>>31;g=0;while(1){p=0;while(1){ca=L[X>>2];j=C;qb:{if(ca>O(2147483648)){break qb}j=s;if(ca>2];j=n;za=ca;ca=O(T(ca));Pa=O(za-ca);if(!(PaO(.5)){break rb}Pa=ca;ca=O(ca*O(.5));za=O(ca-O(T(ca)))==O(0)?Pa:za}ca=za}t=n>>31;if(O(P(ca))>31)|0;j=j+n|0;n=n>>>0>j>>>0?o+1|0:o;j=j>>>0>>0&(n|0)<=(u|0)|(n|0)<(u|0)?s:j>>>0>>0&(n|0)<=0|(n|0)<0?j:C}H[X>>2]=j;X=X+4|0;p=p+1|0;if((v|0)!=(p|0)){continue}break}X=(r<<2)+X|0;g=g+1|0;if((h|0)!=(g|0)){continue}break}}k=k+76|0;b=b+1080|0;q=q+52|0;X=1;i=i+1|0;if(i>>>0>2]){continue}break}break b}X=0;Ba(f,1,3372,0)}na=ra+16|0;if(!X){jb(ua);H[a+8>>2]=H[a+8>>2]|32768;Ba(f,1,11451,0);break a}sb:{if(!c){break sb}b=0;q=H[a+232>>2];g=Rb(q,1);if(!((g|0)==-1|d>>>0>>0)){tb:{b=1;d=H[q+24>>2];if(!H[d+16>>2]){break tb}h=H[d+24>>2];k=H[H[H[q+20>>2]>>2]+20>>2];while(1){b=H[h+24>>2];i=b&7;v=b>>>3|0;d=H[k+28>>2];b=d+N(H[h+36>>2],152)|0;ub:{if(H[q+64>>2]){g=d+N(H[k+24>>2],152)|0;d=H[b+8>>2]-H[b>>2]|0;p=H[g-144>>2]-(d+H[g-152>>2]|0)|0;g=H[b+12>>2]-H[b+4>>2]|0;b=36;break ub}g=H[b+148>>2]-H[b+140>>2]|0;d=H[b+144>>2]-H[b+136>>2]|0;p=0;b=52}b=H[b+k>>2];vb:{wb:{xb:{yb:{i=v+((i|0)!=0)|0;switch(((i|0)==3?4:i)-1|0){case 0:break xb;case 1:break wb;case 3:break yb;default:break vb}}if(!g){break vb}d=d<<2;if((g|0)!=1){i=g&1;v=g&-2;g=0;while(1){s=!d;if(!s){B(c,b,d)}n=p<<2;b=n+(b+d|0)|0;c=c+d|0;if(!s){B(c,b,d)}c=c+d|0;b=n+(b+d|0)|0;g=g+2|0;if((v|0)!=(g|0)){continue}break}if(!i){break vb}}if(d){B(c,b,d)}c=c+d|0;break vb}i=!g|!d;if(H[h+32>>2]){if(i){break vb}s=d&-8;v=d&7;i=0;n=d-1>>>0<7;while(1){d=0;zb:{if(!n){while(1){F[c|0]=H[b>>2];F[c+1|0]=H[b+4>>2];F[c+2|0]=H[b+8>>2];F[c+3|0]=H[b+12>>2];F[c+4|0]=H[b+16>>2];F[c+5|0]=H[b+20>>2];F[c+6|0]=H[b+24>>2];F[c+7|0]=H[b+28>>2];c=c+8|0;b=b+32|0;d=d+8|0;if((s|0)!=(d|0)){continue}break}if(!v){break zb}}d=0;while(1){F[c|0]=H[b>>2];c=c+1|0;b=b+4|0;d=d+1|0;if((v|0)!=(d|0)){continue}break}}b=(p<<2)+b|0;i=i+1|0;if((i|0)!=(g|0)){continue}break}break vb}if(i){break vb}s=d&-8;v=d&7;i=0;n=d-1>>>0<7;p=p<<2;while(1){d=0;Ab:{if(!n){while(1){F[c|0]=H[b>>2];F[c+1|0]=H[b+4>>2];F[c+2|0]=H[b+8>>2];F[c+3|0]=H[b+12>>2];F[c+4|0]=H[b+16>>2];F[c+5|0]=H[b+20>>2];F[c+6|0]=H[b+24>>2];F[c+7|0]=H[b+28>>2];c=c+8|0;b=b+32|0;d=d+8|0;if((s|0)!=(d|0)){continue}break}if(!v){break Ab}}d=0;while(1){F[c|0]=H[b>>2];c=c+1|0;b=b+4|0;d=d+1|0;if((v|0)!=(d|0)){continue}break}}b=b+p|0;i=i+1|0;if((i|0)!=(g|0)){continue}break}break vb}i=!g|!d;if(H[h+32>>2]){if(i){break vb}s=d&-8;v=d&7;i=0;n=d-1>>>0<7;while(1){d=0;Bb:{if(!n){while(1){G[c>>1]=H[b>>2];G[c+2>>1]=H[b+4>>2];G[c+4>>1]=H[b+8>>2];G[c+6>>1]=H[b+12>>2];G[c+8>>1]=H[b+16>>2];G[c+10>>1]=H[b+20>>2];G[c+12>>1]=H[b+24>>2];G[c+14>>1]=H[b+28>>2];c=c+16|0;b=b+32|0;d=d+8|0;if((s|0)!=(d|0)){continue}break}if(!v){break Bb}}d=0;while(1){G[c>>1]=H[b>>2];c=c+2|0;b=b+4|0;d=d+1|0;if((v|0)!=(d|0)){continue}break}}b=(p<<2)+b|0;i=i+1|0;if((i|0)!=(g|0)){continue}break}break vb}if(i){break vb}s=d&-8;v=d&7;i=0;n=d-1>>>0<7;while(1){d=0;Cb:{if(!n){while(1){G[c>>1]=H[b>>2];G[c+2>>1]=H[b+4>>2];G[c+4>>1]=H[b+8>>2];G[c+6>>1]=H[b+12>>2];G[c+8>>1]=H[b+16>>2];G[c+10>>1]=H[b+20>>2];G[c+12>>1]=H[b+24>>2];G[c+14>>1]=H[b+28>>2];c=c+16|0;b=b+32|0;d=d+8|0;if((s|0)!=(d|0)){continue}break}if(!v){break Cb}}d=0;while(1){G[c>>1]=H[b>>2];c=c+2|0;b=b+4|0;d=d+1|0;if((v|0)!=(d|0)){continue}break}}b=(p<<2)+b|0;i=i+1|0;if((i|0)!=(g|0)){continue}break}}k=k+76|0;h=h+52|0;b=1;pb=pb+1|0;if(pb>>>0>2]+16>>2]){continue}break}}}if(!b){break a}b=H[ua+5596>>2];if(!b){break sb}Ca(b);H[ua+5596>>2]=0;H[ua+5600>>2]=0}F[a+92|0]=I[a+92|0]&254;H[a+8>>2]=H[a+8>>2]&-129;fb=1;c=Qa(e);b=H[a+8>>2];if(!(c|qa)&(b|0)==64|(b|0)==256){break a}if((Ja(e,Aa+10|0,2,f)|0)!=2){Ba(f,H[a+208>>2]?1:2,2472,0);fb=!H[a+208>>2];break a}Da(Aa+10|0,Aa+12|0,2);b=H[Aa+12>>2];if((b|0)==65424){break a}if((b|0)==65497){H[a+8>>2]=256;H[a+228>>2]=0;break a}if(!(Qa(e)|qa)){H[a+8>>2]=64;Ba(f,2,8419,0);break a}fb=0;Ba(f,1,8306,0)}na=Aa+16|0;return fb|0}function cb(a,b,c,d,e,f,g,h,i,j,k){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;i=i|0;j=j|0;k=k|0;var l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,v=0,x=0,z=0,A=0,C=0,D=0,E=0,J=0,M=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=O(0),Y=0,Z=0,_=0,$=0,aa=0,ba=0,ca=0,da=0,ea=0,fa=0,ga=0,ha=0,ia=0,ja=0,ka=0,la=0,ma=0,oa=0,pa=0,sa=0,ta=0,ua=0,va=O(0);t=na-80|0;na=t;H[t+40>>2]=65424;v=N(H[a+132>>2],H[a+128>>2]);a:{b:{c:{l=H[a+8>>2];d:{if((l|0)!=8){j=0;if((l|0)!=256){break a}H[t+40>>2]=65497;break d}if(F[a+92|0]&1){break d}Q=v&-2;V=v&1;D=t+77|0;R=t+76|0;U=t+72|0;l=65424;e:{f:{while(1){g:{h:{i:{j:{k:{l:{m:{n:{p=H[a+84>>2];if(!p){break n}n=H[a+80>>2];if(p>>>0<=n>>>0){break n}l=H[a+88>>2]+(n<<3)|0;p=H[l>>2];l=H[l+4>>2];H[a+80>>2]=n+1;if(!bb(j,p,l,k)){Ba(k,1,5440,0);j=0;break a}if((Ja(j,H[a+16>>2],2,k)|0)!=2){Ba(k,1,2472,0);j=0;break a}Da(H[a+16>>2],t+40|0,2);if(H[t+40>>2]==65424){break m}Ba(k,1,4073,0);j=0;break a}if((l|0)==65427){break l}}while(1){if(!(Qa(j)|qa)){H[a+8>>2]=64;break l}if((Ja(j,H[a+16>>2],2,k)|0)!=2){Ba(k,1,2472,0);j=0;break a}Da(H[a+16>>2],t+36|0,2);if(K[t+36>>2]<=1){Ba(k,1,6048,0);j=0;break a}o:{if(H[t+40>>2]!=32896){break o}if(Qa(j)|qa){break o}H[a+8>>2]=64;break l}m=H[a+8>>2];p:{if(!(m&16)){l=H[t+36>>2];break p}l=H[t+36>>2];n=H[a+24>>2];if(!n){break p}p=l+2|0;if(p>>>0>n>>>0){Ba(k,1,8370,0);j=0;break a}H[a+24>>2]=n-p}p=l-2|0;H[t+36>>2]=p;l=24912;A=H[t+40>>2];while(1){n=l;o=H[l>>2];if(o){l=l+12|0;if((o|0)!=(A|0)){continue}}break}if(!(m&H[n+4>>2])){Ba(k,1,5397,0);j=0;break a}q:{if(K[a+20>>2]>=p>>>0){l=H[a+16>>2];break q}l=Qa(j);m=qa;if((m|0)<0){l=1}else{l=l>>>0

>>0&(m|0)<=0}if(l){Ba(k,1,5797,0);j=0;break a}l=Ha(H[a+16>>2],H[t+36>>2]);if(!l){Ca(H[a+16>>2]);H[a+16>>2]=0;H[a+20>>2]=0;Ba(k,1,4973,0);j=0;break a}H[a+16>>2]=l;p=H[t+36>>2];H[a+20>>2]=p}l=Ja(j,l,p,k);if((l|0)!=H[t+36>>2]){Ba(k,1,2472,0);j=0;break a}n=H[n+8>>2];if(!n){Ba(k,1,11725,0);j=0;break a}if(!(ra[n|0](a,H[a+16>>2],l,k)|0)){H[t+32>>2]=H[t+40>>2];Ba(k,1,13959,t+32|0);j=0;break a}q=H[j+56>>2];m=H[t+36>>2];A=H[a+224>>2];p=H[A+40>>2];x=H[a+228>>2];r=N(x,40);l=p+r|0;z=H[l+20>>2];E=z+1|0;n=H[l+28>>2];r:{if(E>>>0<=n>>>0){l=H[l+24>>2];break r}X=O(O(n>>>0)+O(100));if(X=O(0)){n=~~X>>>0}else{n=0}H[l+28>>2]=n;l=Ha(H[l+24>>2],N(n,24));p=H[A+40>>2];n=r+p|0;if(!l){break k}H[n+24>>2]=l;z=H[n+20>>2];E=z+1|0}l=N(z,24)+l|0;H[l+16>>2]=m+4;n=(q-m|0)-4|0;H[l+8>>2]=n;H[l+12>>2]=n>>31;G[l>>1]=o;l=p+r|0;H[l+20>>2]=E;s:{if((o|0)!=65424){break s}p=H[l+16>>2];t:{if(!p){break t}o=H[l+12>>2];if(o>>>0>=K[l+4>>2]){break t}l=p+N(o,24)|0;H[l>>2]=n;H[l+4>>2]=0}l=(H[j+56>>2]-H[t+36>>2]|0)-4|0;n=H[a+48>>2];p=H[a+52>>2];if((p|0)>0){m=1}else{m=l>>>0<=n>>>0&(p|0)>=0}if(m){break s}H[a+48>>2]=l;H[a+52>>2]=0}if(I[a+92|0]&4){if((rb(j,H[a+24>>2],k)|0)!=H[a+24>>2]|qa){Ba(k,1,2472,0);j=0;break a}H[t+40>>2]=65427;break l}if((Ja(j,H[a+16>>2],2,k)|0)!=2){Ba(k,1,2472,0);j=0;break a}Da(H[a+16>>2],t+40|0,2);if(H[t+40>>2]!=65427){continue}break}}if(!(!(Qa(j)|qa)&H[a+8>>2]==64)){l=I[a+92|0];if(!(l&4)){l=N(H[a+228>>2],5644);n=H[a+180>>2];u:{v:{if(H[a+56>>2]){q=Qa(j);break v}q=H[a+24>>2];if(q>>>0<2){break u}}q=q-2|0;H[a+24>>2]=q}n=l+n|0;if(!q){break j}l=Qa(j);p=qa;if((p|0)<0){l=1}else{l=l>>>0>>0&(p|0)<=0}if(l){if(H[a+208>>2]){Ba(k,1,5842,0);j=0;break a}Ba(k,2,5842,0)}l=H[a+24>>2];if(l>>>0>=4294967294){Ba(k,1,1480,0);j=0;break a}p=H[n+5596>>2];w:{if(p){o=H[n+5600>>2];if(o>>>0>-3-l>>>0){Ba(k,1,1211,0);j=0;break a}l=Ha(p,(l+o|0)+2|0);if(l){H[n+5596>>2]=l;break j}Ca(H[n+5596>>2]);H[n+5596>>2]=0;break w}l=Fa(l+2|0);H[n+5596>>2]=l;if(l){break j}}Ba(k,1,6176,0);j=0;break a}H[a+8>>2]=8;F[a+92|0]=l&250;break i}l=H[t+40>>2];break g}Ca(H[n+24>>2]);a=H[A+40>>2]+N(x,40)|0;H[a+28>>2]=0;H[a+20>>2]=0;H[a+24>>2]=0;Ba(k,1,3863,0);j=0;break a}m=H[j+56>>2];p=m-2|0;A=H[j+60>>2];z=A-(m>>>0<2)|0;r=H[a+224>>2];J=H[r+40>>2];M=H[a+228>>2];x=N(M,40);l=J+x|0;o=H[l+16>>2]+N(H[l+12>>2],24)|0;H[o+8>>2]=p;H[o+12>>2]=z;z=m;m=H[a+24>>2];z=z+m|0;H[o+16>>2]=z;H[o+20>>2]=m>>>0>z>>>0?A+1|0:A;m=H[a+24>>2];z=H[l+20>>2];E=z+1|0;o=H[l+28>>2];x:{if(E>>>0<=o>>>0){l=H[l+24>>2];break x}X=O(O(o>>>0)+O(100));if(X=O(0)){o=~~X>>>0}else{o=0}H[l+28>>2]=o;l=Ha(H[l+24>>2],N(o,24));J=H[r+40>>2];o=x+J|0;if(!l){break f}H[o+24>>2]=l;z=H[o+20>>2];E=z+1|0}l=N(z,24)+l|0;H[l+16>>2]=m+2;H[l+8>>2]=p;H[l+12>>2]=p>>31;G[l>>1]=65427;H[(x+J|0)+20>>2]=E;y:{if(q){q=Ja(j,H[n+5596>>2]+H[n+5600>>2]|0,H[a+24>>2],k);l=8;if((q|0)==H[a+24>>2]){break y}l=64;if((q|0)!=-1){break y}Ba(k,1,2472,0);j=0;break a}q=0;l=H[a+24>>2]?64:8}H[a+8>>2]=l;H[n+5600>>2]=H[n+5600>>2]+q;z:{if(F[a+92|0]&1){break z}l=H[a+44>>2];if(H[a+76>>2]|((l|0)<0|(l|0)!=H[a+228>>2])){break z}if(!Ab(j)){break z}n=H[a+228>>2];p=H[a+180>>2]+N(n,5644)|0;l=H[p+5592>>2];n=H[H[a+224>>2]+40>>2]+N(n,40)|0;if((l|0)!=H[n+4>>2]){break z}p=H[p+5588>>2]+1|0;if(l>>>0<=p>>>0){break z}A:{l=H[n+16>>2]+N(p,24)|0;n=H[l>>2];l=H[l+4>>2];if((n|0)==H[j+56>>2]&(l|0)==H[j+60>>2]){break A}if(bb(j,n,l,k)){break A}Ba(k,1,5440,0);j=0;break a}if((Ja(j,H[a+16>>2],2,k)|0)!=2){Ba(k,1,2472,0);j=0;break a}Da(H[a+16>>2],t+40|0,2);if(H[t+40>>2]==65424){break h}Ba(k,1,4073,0);j=0;break a}l=I[a+92|0];if((l&9)!=1){break i}F[a+92|0]=l|8;p=H[a+228>>2];if(H[(H[a+180>>2]+N(p,5644)|0)+5592>>2]==1){break i}if(!Ab(j)){break i}l=H[j+60>>2];o=l;m=H[j+56>>2];if((l&m)==-1){break i}B:{while(1){l=1;n=t+70|0;if((Ja(j,n,2,k)|0)!=2){break B}Da(n,t- -64|0,2);if(H[t+64>>2]!=65424){break B}q=2472;if((Ja(j,n,2,k)|0)!=2){break c}Da(n,t+60|0,2);if(H[t+60>>2]!=10){q=6048;break c}H[t+60>>2]=8;n=Ja(j,t+70|0,8,k);if((n|0)!=H[t+60>>2]){break c}if((n|0)!=8){q=4047;break c}Da(t+70|0,t+56|0,2);Da(U,t+52|0,4);Da(R,t+48|0,1);Da(D,t+44|0,1);if((p|0)!=H[t+56>>2]){n=H[t+52>>2];if(n>>>0<14){break B}n=n-12|0;H[t+52>>2]=n;n=rb(j,n,k);if(!qa&H[t+52>>2]==(n|0)){continue}break B}break}l=H[t+48>>2]!=H[t+44>>2]}if(!nc(j,m,o,k)){break b}if(l){break i}F[a+92|0]=I[a+92|0]&238|16;C:{if(!v){break C}p=H[a+180>>2];q=0;l=0;if((v|0)!=1){while(1){n=p+N(q,5644)|0;o=H[n+5592>>2];if(o){H[n+5592>>2]=o+1}o=H[n+11236>>2];if(o){H[n+11236>>2]=o+1}q=q+2|0;l=l+2|0;if((Q|0)!=(l|0)){continue}break}if(!V){break C}}l=p+N(q,5644)|0;n=H[l+5592>>2];if(!n){break C}H[l+5592>>2]=n+1}Ba(k,2,9035,0)}if(F[a+92|0]&1){break h}if((Ja(j,H[a+16>>2],2,k)|0)!=2){if(!(!v|(v|0)!=(H[a+228>>2]+1|0))){j=H[a+180>>2];l=0;while(1){n=j+N(l,5644)|0;if(!(H[n+5588>>2]|H[n+5592>>2])){break e}l=l+1|0;if((v|0)!=(l|0)){continue}break}}Ba(k,1,2472,0);j=0;break a}Da(H[a+16>>2],t+40|0,2)}l=H[t+40>>2];if(F[a+92|0]&1){break g}if((l|0)!=65497){continue}}break}if(H[a+8>>2]==256|(l|0)!=65497){break d}H[a+8>>2]=256;H[a+228>>2]=0;break d}Ca(H[o+24>>2]);a=H[r+40>>2]+N(M,40)|0;H[a+28>>2]=0;H[a+20>>2]=0;H[a+24>>2]=0;Ba(k,1,3863,0);j=0;break a}H[t+16>>2]=l;Ba(k,4,11004,t+16|0);H[a+228>>2]=l;H[t+40>>2]=65497;H[a+8>>2]=256}l=H[a+228>>2];j=H[a+180>>2];D:{E:{if(F[a+92|0]&1){break E}F:{G:{if(l>>>0>=v>>>0){break G}q=j+N(l,5644)|0;while(1){if(H[q+5596>>2]){break G}l=l+1|0;H[a+228>>2]=l;q=q+5644|0;if((l|0)!=(v|0)){continue}break}break F}if((l|0)!=(v|0)){break E}}H[i>>2]=0;break D}H:{I:{n=j+N(l,5644)|0;if(H[n+5172>>2]){a=6837}else{if(!(I[n+5640|0]&2)){break H}p=H[n+5160>>2];J:{if(!p){q=0;break J}o=p&3;m=H[n+5164>>2];j=0;q=0;l=0;if(p>>>0>=4){A=p&-4;p=0;while(1){v=m+(l<<3)|0;q=H[v+28>>2]+(H[v+20>>2]+(H[v+12>>2]+(H[v+4>>2]+q|0)|0)|0)|0;l=l+4|0;p=p+4|0;if((A|0)!=(p|0)){continue}break}if(!o){break J}}while(1){q=H[(m+(l<<3)|0)+4>>2]+q|0;l=l+1|0;j=j+1|0;if((o|0)!=(j|0)){continue}break}}j=Fa(q);H[n+5172>>2]=j;if(j){break I}a=4009}Ba(k,1,a,0);Ba(k,1,8059,0);j=0;break a}H[n+5180>>2]=q;q=H[n+5164>>2];j=H[n+5160>>2];if(j){p=0;l=0;while(1){v=l<<3;o=v+q|0;m=H[o>>2];if(m){j=H[o+4>>2];if(j){B(H[n+5172>>2]+p|0,m,j)}j=v+H[n+5164>>2]|0;o=H[j+4>>2];Ca(H[j>>2]);q=H[n+5164>>2];j=v+q|0;H[j>>2]=0;H[j+4>>2]=0;p=o+p|0;j=H[n+5160>>2]}l=l+1|0;if(l>>>0>>0){continue}break}}H[n+5160>>2]=0;Ca(q);H[n+5164>>2]=0;H[n+5168>>2]=H[n+5172>>2];H[n+5176>>2]=H[n+5180>>2]}l=H[a+232>>2];Y=H[l+28>>2];n=H[a+228>>2];E=H[(H[Y+76>>2]+N(n,5644)|0)+5584>>2];j=H[l+24>>2];Z=H[j+24>>2];v=H[Y+24>>2];p=(n>>>0)/(v>>>0)|0;V=H[H[l+20>>2]>>2];o=H[Y+12>>2];l=H[Y+4>>2]+N(o,n-N(p,v)|0)|0;n=H[j>>2];n=l>>>0>n>>>0?l:n;H[V>>2]=n;v=l+o|0;l=l>>>0>v>>>0?-1:v;v=H[j+8>>2];l=l>>>0>>0?l:v;H[V+8>>2]=l;K:{L:{if(!((l|0)>(n|0)&(n|0)>=0)){Ba(k,1,6682,0);break L}q=H[V+20>>2];l=p;p=H[Y+16>>2];l=H[Y+8>>2]+N(l,p)|0;n=H[j+4>>2];n=l>>>0>n>>>0?l:n;H[V+4>>2]=n;p=l+p|0;l=l>>>0>p>>>0?-1:p;j=H[j+12>>2];j=j>>>0>l>>>0?l:j;H[V+12>>2]=j;if(!((j|0)>(n|0)&(n|0)>=0)){Ba(k,1,6644,0);break L}M:{if(H[E+4>>2]){if(H[V+16>>2]){break M}j=1;break K}Ba(k,1,5358,0);break L}N:{O:{while(1){H[Z+36>>2]=0;j=H[Z>>2];n=j>>31;o=j-1|0;l=H[V>>2];p=o+l|0;m=n-!j|0;v=m+(l>>31)|0;ta=q,ua=ue(p,l>>>0>p>>>0?v+1|0:v,j,n),H[ta>>2]=ua;l=H[Z+4>>2];p=l>>31;v=l-1|0;A=H[V+4>>2];r=v+A|0;x=p-!l|0;z=x+(A>>31)|0;ta=q,ua=ue(r,r>>>0>>0?z+1|0:z,l,p),H[ta+4>>2]=ua;z=o;o=H[V+8>>2];A=z+o|0;m=(o>>31)+m|0;ta=q,ua=ue(A,o>>>0>A>>>0?m+1|0:m,j,n),H[ta+8>>2]=ua;j=H[V+12>>2];H[q+16>>2]=fa;n=x+(j>>31)|0;j=j+v|0;n=j>>>0>>0?n+1|0:n;ta=q,ua=ue(j,n,l,p),H[ta+12>>2]=ua;j=H[E+4>>2];H[q+20>>2]=j;l=H[Y+80>>2];H[q+24>>2]=j>>>0>>0?1:j-l|0;Ca(H[q+52>>2]);H[q+68>>2]=0;H[q+60>>2]=0;H[q+64>>2]=0;H[q+52>>2]=0;H[q+56>>2]=0;j=N(j,152);l=H[q+28>>2];P:{if(!l){l=Fa(j);H[q+28>>2]=l;if(!l){break L}H[q+32>>2]=j;if(!j){break P}y(l,0,j);break P}if(j>>>0<=K[q+32>>2]){break P}l=Ha(l,j);if(!l){Ba(k,1,3090,0);Ca(H[q+28>>2]);H[q+28>>2]=0;H[q+32>>2]=0;break L}H[q+28>>2]=l;n=H[q+32>>2];p=j-n|0;if(p){y(l+n|0,0,p)}H[q+32>>2]=j}j=H[q+20>>2];if(j){ha=E+944|0;ia=E+812|0;da=E+28|0;z=H[q+28>>2];_=0;while(1){n=j-1|0;l=n&31;if((n&63)>>>0>=32){l=-1<>>32-l}p=p^-1;m=H[q>>2];o=p+m|0;v=l^-1;l=v+(m>>31)|0;m=m>>>0>o>>>0?l+1|0:l;l=n&31;if((n&63)>>>0>=32){r=m>>l}else{r=((1<>>l}H[z>>2]=r;o=H[q+4>>2];l=o+p|0;m=(o>>31)+v|0;m=l>>>0>>0?m+1|0:m;o=n&31;if((n&63)>>>0>=32){x=m>>o}else{x=((1<>>o}H[z+4>>2]=x;o=H[q+8>>2];l=o+p|0;m=(o>>31)+v|0;m=l>>>0>>0?m+1|0:m;o=n&31;if((n&63)>>>0>=32){m=m>>o}else{m=((1<>>o}H[z+8>>2]=m;o=H[q+12>>2];l=o+p|0;A=(o>>31)+v|0;A=l>>>0>>0?A+1|0:A;o=n&31;if((n&63)>>>0>=32){A=A>>o}else{A=((1<>>o}H[z+12>>2]=A;Q=m>>31;D=_<<2;J=H[D+ia>>2];l=J&31;if((J&63)>>>0>=32){l=1<>>32-l}R=o;o=R+m|0;U=o-1|0;l=l+Q|0;o=(o>>>0>>0?l+1|0:l)-!o|0;l=J&31;if((J&63)>>>0>=32){l=o>>l}else{l=((1<>>l}R=l<>31;Q=H[D+ha>>2];l=Q&31;if((Q&63)>>>0>=32){l=-1<>>32-l}D=o^-1;o=D+A|0;l=(l^-1)+U|0;D=o>>>0>>0?l+1|0:l;l=Q&31;if((Q&63)>>>0>=32){l=D>>l}else{l=((1<>>l}l=l<>Q:0;H[z+20>>2]=o;aa=r&-1<>J:0;H[z+16>>2]=l;re(l,0,o);if(!(!l|!qa)){break O}ba=N(l,o);if(ba>>>0>=107374183){break O}D=N(ba,40);if(_){Q=Q-1|0;J=J-1|0;l=$>>31;m=l;o=l+1|0;l=$+1|0;$=((l?m:o)&1)<<31|l>>>1;l=aa>>31;m=l;o=l+1|0;l=aa+1|0;aa=((l?m:o)&1)<<31|l>>>1;l=3}else{l=1}H[z+24>>2]=l;x=z+28|0;o=j;l=j&31;if((j&63)>>>0>=32){l=1<>>32-l}ga=j;A=l;j=H[E+12>>2];R=j>>>0>>0?j:Q;j=R&31;if((R&63)>>>0>=32){l=-1<>>32-j}ja=m^-1;ka=l^-1;j=H[E+8>>2];U=j>>>0>>0?j:J;j=U&31;if((U&63)>>>0>=32){l=-1<>>32-j}la=m^-1;ma=l^-1;ea=0;while(1){Q:{if(!_){l=H[q+4>>2];j=l+p|0;m=(l>>31)+v|0;m=j>>>0>>0?m+1|0:m;l=n&31;if((n&63)>>>0>=32){S=m>>l}else{S=((1<>>l}l=H[q>>2];j=l+p|0;m=(l>>31)+v|0;m=j>>>0>>0?m+1|0:m;l=n&31;if((n&63)>>>0>=32){C=m>>l}else{C=((1<>>l}j=0;m=p;M=m;l=v;T=l;r=n;break Q}j=ea+1|0;r=j>>>1|0;m=n&31;if((n&63)>>>0>=32){l=r<>>32-m;m=r<>2];m=M+r|0;l=(l^-1)+A|0;T=s>>>0>M>>>0?l+1|0:l;l=T+(r>>31)|0;r=m>>>0>>0?l+1|0:l;l=o&31;if((o&63)>>>0>=32){S=r>>l}else{S=((1<>>l}r=j&1;m=n&31;if((n&63)>>>0>=32){l=r<>>32-m;m=r<>2];r=m+s|0;l=(l^-1)+A|0;l=m>>>0>>0?l+1|0:l;C=l+(s>>31)|0;C=r>>>0>>0?C+1|0:C;s=o&31;if((o&63)>>>0>=32){C=C>>s}else{C=((1<>>s}r=o}s=r;P=H[q+8>>2];W=P>>31;r=H[q+12>>2];H[x+4>>2]=S;H[x>>2]=C;H[x+16>>2]=j;S=(r>>31)+T|0;C=r;r=r+M|0;S=C>>>0>r>>>0?S+1|0:S;M=s&31;if((s&63)>>>0>=32){r=S>>M}else{r=((1<>>M}H[x+12>>2]=r;l=l+W|0;C=l+1|0;r=l;l=m+P|0;r=l>>>0

>>0?C:r;m=s&31;if((s&63)>>>0>=32){l=r>>m}else{l=((1<>>m}H[x+8>>2]=l;ca=1;l=H[da>>2];j=(H[Z+24>>2]+(!H[E+20>>2]|!j?0:(j|0)==3?2:1)|0)-l|0;R:{if((j|0)>=1024){ca=898846567431158e293;if(j>>>0<2047){j=j-1023|0;break R}ca=Infinity;j=(j>>>0>=3069?3069:j)-2046|0;break R}if((j|0)>-1023){break R}ca=2004168360008973e-307;if(j>>>0>4294965304){j=j+969|0;break R}ca=0;j=(j>>>0<=4294964336?-2960:j)+1938|0}oa=+H[da+4>>2]*.00048828125+1;u(0,0);u(1,j+1023<<20);ta=x,va=O(oa*(ca*+w())),L[ta+32>>2]=va;H[x+28>>2]=(l+H[E+804>>2]|0)-1;j=H[x+20>>2];S:{T:{if(!(j|!ba)){j=Fa(D);H[x+20>>2]=j;if(!j){Ba(k,1,2854,0);break L}if(D){y(j,0,D)}H[x+24>>2]=D;break T}if(D>>>0>K[x+24>>2]){j=Ha(j,D);if(!j){Ba(k,1,2854,0);Ca(H[x+20>>2]);H[x+20>>2]=0;H[x+24>>2]=0;break L}H[x+20>>2]=j;l=H[x+24>>2];m=D-l|0;if(m){y(j+l|0,0,m)}H[x+24>>2]=D}if(!ba){break S}}j=H[x+20>>2];M=0;while(1){m=H[z+16>>2];l=(M>>>0)/(m>>>0)|0;m=M-N(l,m)|0;r=(m<>2];S=(r|0)>(s|0)?r:s;H[j>>2]=S;r=(l<>2];C=(r|0)>(s|0)?r:s;H[j+4>>2]=C;m=(m+1<>2];m=(m|0)<(r|0)?m:r;H[j+8>>2]=m;l=(l+1<>2];r=(l|0)<(r|0)?l:r;H[j+12>>2]=r;l=(m>>31)+ma|0;T=l+1|0;s=l;l=m+la|0;s=m>>>0>l>>>0?T:s;S=S>>U;m=U&31;if((U&63)>>>0>=32){l=s>>m}else{l=((1<>>m}s=l-S<>U;H[j+16>>2]=s;l=(r>>31)+ka|0;T=l+1|0;m=l;l=r+ja|0;r=l>>>0>>0?T:m;C=C>>R;m=R&31;if((R&63)>>>0>=32){l=r>>m}else{l=((1<>>m}l=l-C<>R;H[j+20>>2]=l;r=N(l,s);re(r,0,68);if(qa){Ba(k,1,2935,0);break L}l=N(r,68);m=H[j+24>>2];U:{V:{if(!(m|!r)){m=Fa(l);H[j+24>>2]=m;if(!m){break L}if(!l){break V}y(m,0,l);break V}if(l>>>0<=K[j+28>>2]){break U}m=Ha(m,l);if(!m){Ca(H[j+24>>2]);H[j+24>>2]=0;H[j+28>>2]=0;Ba(k,1,2549,0);break L}H[j+24>>2]=m;s=H[j+28>>2];T=l-s|0;if(!T){break V}y(m+s|0,0,T)}H[j+28>>2]=l}l=H[j+20>>2];m=H[j+16>>2];s=H[j+32>>2];W:{if(!s){l=gc(m,l,k);break W}l=ec(s,m,l,k)}H[j+32>>2]=l;l=H[j+20>>2];m=H[j+16>>2];s=H[j+36>>2];X:{if(!s){l=gc(m,l,k);break X}l=ec(s,m,l,k)}H[j+36>>2]=l;if(r){l=0;while(1){W=H[j+16>>2];T=(l>>>0)/(W>>>0)|0;m=H[j+24>>2]+N(l,68)|0;P=H[m>>2];Y:{if(P){pa=H[m+56>>2];sa=H[m+4>>2];s=H[m+48>>2];Ca(H[m+60>>2]);H[m+48>>2]=0;H[m+52>>2]=0;H[m+64>>2]=0;H[m+56>>2]=0;H[m+60>>2]=0;H[m+40>>2]=0;H[m+44>>2]=0;H[m+32>>2]=0;H[m+36>>2]=0;H[m+24>>2]=0;H[m+28>>2]=0;H[m+16>>2]=0;H[m+20>>2]=0;H[m+8>>2]=0;H[m+12>>2]=0;H[m>>2]=P;H[m+48>>2]=s;Z:{if(!s){break Z}s=N(s,24);if(!s){break Z}y(P,0,s)}H[m+56>>2]=pa;H[m+4>>2]=sa;break Y}s=Ea(10,24);H[m>>2]=s;if(!s){break L}H[m+48>>2]=10}s=(l-N(T,W)|0)+S|0;P=s<>2];H[m+8>>2]=(P|0)>(W|0)?P:W;T=C+T|0;P=T<>2];H[m+12>>2]=(P|0)>(W|0)?P:W;s=s+1<>2];H[m+16>>2]=(s|0)<(P|0)?s:P;P=m;m=T+1<>2];H[P+20>>2]=(m|0)<(s|0)?m:s;l=l+1|0;if((r|0)!=(l|0)){continue}break}}j=j+40|0;M=M+1|0;if((M|0)!=(ba|0)){continue}break}}da=da+8|0;x=x+36|0;ea=ea+1|0;if(ea>>>0>2]){continue}break}z=z+152|0;j=n;_=_+1|0;if(_>>>0>2]){continue}break}}Z=Z+52|0;q=q+76|0;E=E+1080|0;fa=fa+1|0;if(fa>>>0>2]){continue}break}j=1;break K}Ba(k,1,2982,0);break L}Ba(k,1,2373,0)}j=0}if(!j){Ba(k,1,3668,0);j=0;break a}j=H[a+228>>2];H[t+4>>2]=N(H[a+128>>2],H[a+132>>2]);H[t>>2]=j+1;Ba(k,4,11825,t);H[b>>2]=H[a+228>>2];H[i>>2]=1;if(c){b=Rb(H[a+232>>2],0);H[c>>2]=b;j=0;if((b|0)==-1){break a}}b=H[H[H[a+232>>2]+20>>2]>>2];H[d>>2]=H[b>>2];H[e>>2]=H[b+4>>2];H[f>>2]=H[b+8>>2];H[g>>2]=H[b+12>>2];H[h>>2]=H[b+16>>2];H[a+8>>2]=H[a+8>>2]|128}j=1;break a}Ba(k,1,q,0)}Ba(k,1,3702,0);j=0}a=j;na=t+80|0;return a|0}function Vb(a){var b=0,c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,L=0,M=0,O=0,P=0,Q=0,R=0,S=0,T=0;a:{b:{c:{d:{e:{f:{g:{h:{i:{j:{switch(H[a+84>>2]){case 0:k:{c=H[a+52>>2];b=H[a+196>>2];if(c>>>0>>0){q=H[a+64>>2];if(q>>>0>>0){break k}}Ba(H[a+236>>2],1,8491,0);break b}if(!H[a+44>>2]){k=H[a+36>>2];b=0;break i}H[a+44>>2]=0;i=H[a+68>>2];b=1;break i;case 1:l:{c=H[a+52>>2];b=H[a+196>>2];if(c>>>0>>0){q=H[a+64>>2];if(q>>>0>>0){break l}}Ba(H[a+236>>2],1,8536,0);break b}if(!H[a+44>>2]){e=H[a+36>>2];b=0;break e}H[a+44>>2]=0;i=H[a+48>>2];b=1;break e;case 2:m:{A=H[a+52>>2];x=H[a+196>>2];if(A>>>0>>0){r=H[a+64>>2];if(r>>>0>>0){break m}}Ba(H[a+236>>2],1,8671,0);break b}if(!H[a+44>>2]){y=H[a+40>>2];break f}H[a+228>>2]=0;H[a+232>>2]=0;H[a+44>>2]=0;j=H[a+200>>2];while(1){O=j+(u<<4)|0;l=H[O+8>>2];if(l){q=H[O+12>>2];b=0;while(1){g=l+(b^-1)|0;d=q+(b<<4)|0;s=g+H[d>>2]|0;n:{if(s>>>0>31){break n}c=H[O>>2];if(c>>>0>-1>>>s>>>0){break n}c=c<>>0>k>>>0?k:c:c;H[a+228>>2]=k}g=g+H[d+4>>2]|0;o:{if(g>>>0>31){break o}c=H[O+4>>2];if(c>>>0>-1>>>g>>>0){break o}c=c<>>0>i>>>0?i:c:c;H[a+232>>2]=i}b=b+1|0;if((l|0)!=(b|0)){continue}break}}u=u+1|0;if((x|0)!=(u|0)){continue}break};if(!k|!i){break d}if(!I[a|0]){H[a+108>>2]=H[a+208>>2];H[a+100>>2]=H[a+204>>2];H[a+112>>2]=H[a+216>>2];H[a+104>>2]=H[a+212>>2]}o=H[a+48>>2];b=1;break f;case 3:p:{A=H[a+52>>2];l=H[a+196>>2];if(A>>>0>>0){P=H[a+64>>2];if(P>>>0>>0){break p}}Ba(H[a+236>>2],1,8626,0);break b}if(!H[a+44>>2]){B=H[a+200>>2];e=H[a+28>>2];y=B+(e<<4)|0;E=H[a+40>>2];break g}H[a+228>>2]=0;H[a+232>>2]=0;H[a+44>>2]=0;B=H[a+200>>2];while(1){x=(p<<4)+B|0;s=H[x+8>>2];if(s){q=H[x+12>>2];b=0;while(1){g=s+(b^-1)|0;d=q+(b<<4)|0;j=g+H[d>>2]|0;q:{if(j>>>0>31){break q}c=H[x>>2];if(c>>>0>-1>>>j>>>0){break q}c=c<>>0>k>>>0?k:c:c;H[a+228>>2]=k}g=g+H[d+4>>2]|0;r:{if(g>>>0>31){break r}c=H[x+4>>2];if(c>>>0>-1>>>g>>>0){break r}c=c<>>0>i>>>0?i:c:c;H[a+232>>2]=i}b=b+1|0;if((s|0)!=(b|0)){continue}break}}p=p+1|0;if((l|0)!=(p|0)){continue}break};if(!k|!i){break d}s:{if(I[a|0]){p=H[a+108>>2];break s}p=H[a+208>>2];H[a+108>>2]=p;H[a+100>>2]=H[a+204>>2];H[a+112>>2]=H[a+216>>2];H[a+104>>2]=H[a+212>>2]}b=1;break g;case 4:break j;default:break d}}t:{p=H[a+52>>2];b=H[a+196>>2];if(p>>>0>>0){r=H[a+64>>2];if(r>>>0>>0){break t}}Ba(H[a+236>>2],1,8581,0);break d}if(!H[a+44>>2]){p=H[a+28>>2];o=H[a+200>>2]+(p<<4)|0;u=H[a+40>>2];b=0;break h}H[a+28>>2]=p;H[a+44>>2]=0;b=1;break h}u:while(1){v:{w:{if(!b){k=k+1|0;break w}H[a+40>>2]=i;if(K[a+56>>2]<=i>>>0){break b}e=H[a+48>>2];b=0;break v}b=1}x:while(1){y:{z:{A:{B:{if(!b){H[a+32>>2]=e;if(K[a+60>>2]<=e>>>0){break B}H[a+28>>2]=c;b=c;o=0;break y}H[a+36>>2]=k;if(K[a+76>>2]<=k>>>0){b=H[a+28>>2];o=1;break y}b=((N(H[a+16>>2],H[a+32>>2])+N(H[a+12>>2],H[a+40>>2])|0)+N(H[a+20>>2],H[a+28>>2])|0)+N(H[a+24>>2],k)|0;if(b>>>0>=K[a+8>>2]){break c}b=H[a+4>>2]+(b<<1)|0;if(J[b>>1]){break A}break a}i=H[a+40>>2]+1|0;break z}b=0;continue u}b=1;continue u}while(1){C:{D:{E:{if(!o){if(b>>>0>=q>>>0){break E}g=H[a+32>>2];d=H[a+200>>2]+(b<<4)|0;if(g>>>0>=K[d+8>>2]){break C}if(!I[a|0]){b=H[d+12>>2]+(g<<4)|0;H[a+76>>2]=N(H[b+12>>2],H[b+8>>2])}k=H[a+72>>2];b=1;continue x}b=b+1|0;H[a+28>>2]=b;break D}e=H[a+32>>2]+1|0;b=0;continue x}o=0;continue}o=1;continue}}}}F:while(1){G:{H:{if(!b){u=u+1|0;H[a+40>>2]=u;break H}if(p>>>0>=r>>>0){break b}H[a+228>>2]=0;H[a+232>>2]=0;o=H[a+200>>2]+(p<<4)|0;s=H[o+8>>2];if(!s){break b}q=H[o+12>>2];k=0;e=0;b=0;while(1){g=s+(b^-1)|0;d=q+(b<<4)|0;j=g+H[d>>2]|0;I:{if(j>>>0>31){break I}c=H[o>>2];if(c>>>0>-1>>>j>>>0){break I}c=c<>>0>e>>>0?e:c:c;H[a+228>>2]=e}g=g+H[d+4>>2]|0;J:{if(g>>>0>31){break J}c=H[o+4>>2];if(c>>>0>-1>>>g>>>0){break J}c=c<>>0>k>>>0?k:c:c;H[a+232>>2]=k}b=b+1|0;if((s|0)!=(b|0)){continue}break}if(!e|!k){break d}K:{if(I[a|0]){k=H[a+108>>2];break K}k=H[a+208>>2];H[a+108>>2]=k;H[a+100>>2]=H[a+204>>2];H[a+112>>2]=H[a+216>>2];H[a+104>>2]=H[a+212>>2]}b=0;break G}b=1}L:while(1){M:{N:{O:{P:{if(!b){H[a+224>>2]=k;if(K[a+112>>2]<=k>>>0){break P}B=H[a+100>>2];b=0;break M}if(K[a+56>>2]<=u>>>0){i=H[a+32>>2];b=1;break M}b=((N(H[a+16>>2],H[a+32>>2])+N(H[a+12>>2],u)|0)+N(H[a+20>>2],p)|0)+N(H[a+24>>2],H[a+36>>2])|0;if(b>>>0>=K[a+8>>2]){break c}b=H[a+4>>2]+(b<<1)|0;if(J[b>>1]){break O}break a}p=p+1|0;H[a+28>>2]=p;break N}b=0;continue F}b=1;continue F}while(1){Q:{R:{S:{T:{if(!b){H[a+220>>2]=B;if(K[a+104>>2]<=B>>>0){break S}i=H[a+48>>2];break T}i=i+1|0}H[a+32>>2]=i;b=H[a+60>>2];d=H[o+8>>2];if((b>>>0>>0?b:d)>>>0>i>>>0){g=H[o>>2];c=g;n=d+(i^-1)|0;m=n;d=m&31;if((m&63)>>>0>=32){b=c<>>32-d;v=g<>>0>=32){b=b>>>d|0}else{b=((1<>>d}if((q|0)!=(b|0)){break Q}b=m&31;if((m&63)>>>0>=32){b=-1>>>b|0}else{b=(1<>>b}c=H[o+4>>2];if((b&c)!=(c|0)){break Q}d=m&31;if((m&63)>>>0>=32){b=c<>>32-d;w=c<>2];j=F+d|0;O=ve(j,d>>>0>j>>>0?h+1|0:h,w,b);b=h;L=H[a+208>>2];d=F+L|0;b=L>>>0>d>>>0?b+1|0:b;s=ve(d,b,w,C);A=v-1|0;j=H[a+212>>2];l=A+j|0;d=f-!v|0;b=d;x=ve(l,l>>>0>>0?b+1|0:b,v,f);D=H[a+204>>2];j=A+D|0;b=D>>>0>j>>>0?b+1|0:b;j=ve(j,b,v,f);z=H[o+12>>2]+(i<<4)|0;M=H[z>>2];t=M+n|0;b=t&31;if((t&63)>>>0>=32){b=-1>>>b|0}else{b=(1<>>b}if((g|0)!=(b&g)){break Q}h=c;P=H[z+4>>2];n=P+n|0;e=n&31;if((n&63)>>>0>=32){b=c<>>32-e;e=c<>>0>=32){c=b>>>l|0}else{c=((1<>>l}if((h|0)!=(c|0)){break Q}l=H[a+224>>2];e=!!(we(l,e,b)|qa);b=n&31;if((n&63)>>>0>=32){h=-1<>>32-b;b=-1<>>0>=32){h=n<>>32-e|b<>2];if((t&63)>>>0>=32){b=g<>>32-n;e=g<>>0>=32){h=-1<>>32-b;b=-1<>>0>=32){h=j<>>32-t|b<>2];if(!n|(!H[z+12>>2]|(j|0)==(x|0))){break Q}if((s|0)==(O|0)){break Q}u=H[a+68>>2];H[a+40>>2]=u;b=d;c=c+A|0;b=c>>>0>>0?b+1|0:b;g=(ve(c,b,v,f)>>>M)-(j>>>M)|0;b=q;c=l+F|0;b=c>>>0>>0?b+1|0:b;S=a,T=N(n,(ve(c,b,w,C)>>>P)-(s>>>P)|0)+g|0,H[S+36>>2]=T;b=1;continue L}c=H[a+220>>2];b=H[a+228>>2];B=c+b-(c>>>0)%(b>>>0)|0;break R}c=H[a+224>>2];b=H[a+232>>2];k=c+b-(c>>>0)%(b>>>0)|0;b=0;continue L}b=0;continue}b=1;continue}}}}U:while(1){V:{W:{if(!b){E=E+1|0;H[a+40>>2]=E;break W}H[a+224>>2]=p;if(K[a+112>>2]<=p>>>0){break b}v=H[a+100>>2];b=0;break V}b=1}X:while(1){Y:{Z:{_:{$:{if(!b){H[a+220>>2]=v;if(K[a+104>>2]<=v>>>0){break $}H[a+28>>2]=A;e=A;b=0;break Y}if(K[a+56>>2]<=E>>>0){u=H[a+32>>2];b=1;break Y}b=((N(H[a+16>>2],H[a+32>>2])+N(H[a+12>>2],E)|0)+N(H[a+20>>2],e)|0)+N(H[a+24>>2],H[a+36>>2])|0;if(b>>>0>=K[a+8>>2]){break c}b=H[a+4>>2]+(b<<1)|0;if(J[b>>1]){break _}break a}c=H[a+224>>2];b=H[a+232>>2];p=c+b-(c>>>0)%(b>>>0)|0;break Z}b=0;continue U}b=1;continue U}while(1){aa:{ba:{ca:{da:{if(!b){if(e>>>0>=P>>>0){break ca}u=H[a+48>>2];H[a+32>>2]=u;y=(e<<4)+B|0;break da}u=u+1|0;H[a+32>>2]=u}b=H[a+60>>2];d=H[y+8>>2];if((b>>>0>>0?b:d)>>>0>u>>>0){g=H[y>>2];c=g;f=d+(u^-1)|0;i=f;d=f&31;if((f&63)>>>0>=32){b=c<>>32-d;k=g<>>0>=32){b=b>>>d|0}else{b=((1<>>d}if((q|0)!=(b|0)){break aa}b=i&31;if((i&63)>>>0>=32){b=-1>>>b|0}else{b=(1<>>b}c=H[y+4>>2];if((b&c)!=(c|0)){break aa}d=i&31;if((i&63)>>>0>=32){b=c<>>32-d;o=c<>2];j=F+d|0;O=ve(j,d>>>0>j>>>0?h+1|0:h,o,b);b=h;w=H[a+208>>2];d=w+F|0;b=w>>>0>d>>>0?b+1|0:b;s=ve(d,b,o,n);C=k-1|0;j=H[a+212>>2];l=C+j|0;d=t-!k|0;b=d;x=ve(l,l>>>0>>0?b+1|0:b,k,t);L=H[a+204>>2];j=C+L|0;b=L>>>0>j>>>0?b+1|0:b;j=ve(j,b,k,t);D=H[y+12>>2]+(u<<4)|0;z=H[D>>2];m=z+f|0;b=m&31;if((m&63)>>>0>=32){b=-1>>>b|0}else{b=(1<>>b}if((g|0)!=(b&g)){break aa}h=c;M=H[D+4>>2];f=M+f|0;r=f&31;if((f&63)>>>0>=32){b=c<>>32-r;r=c<>>0>=32){c=b>>>l|0}else{c=((1<>>l}if((h|0)!=(c|0)){break aa}l=H[a+224>>2];r=!!(we(l,r,b)|qa);b=f&31;if((f&63)>>>0>=32){h=-1<>>32-b;b=-1<>>0>=32){h=f<>>32-r|b<>2];if((m&63)>>>0>=32){b=g<>>32-f;f=g<>>0>=32){h=-1<>>32-b;b=-1<>>0>=32){h=f<>>32-m|b<>2];if(!f|(!H[D+12>>2]|(j|0)==(x|0))){break aa}if((s|0)==(O|0)){break aa}E=H[a+68>>2];H[a+40>>2]=E;b=d;c=c+C|0;b=c>>>0>>0?b+1|0:b;g=(ve(c,b,k,t)>>>z)-(j>>>z)|0;b=q;c=l+F|0;b=c>>>0>>0?b+1|0:b;S=a,T=N(f,(ve(c,b,o,n)>>>M)-(s>>>M)|0)+g|0,H[S+36>>2]=T;b=1;continue X}e=e+1|0;H[a+28>>2]=e;break ba}c=H[a+220>>2];b=H[a+228>>2];v=c+b-(c>>>0)%(b>>>0)|0;b=0;continue X}b=0;continue}b=1;continue}}}}ea:while(1){fa:{ga:{if(!b){y=y+1|0;H[a+40>>2]=y;break ga}H[a+32>>2]=o;if(K[a+60>>2]<=o>>>0){break b}E=H[a+108>>2];b=0;break fa}b=1}ha:while(1){ia:{ja:{ka:{la:{if(!b){H[a+224>>2]=E;if(K[a+112>>2]<=E>>>0){break la}B=H[a+100>>2];b=0;break ia}if(K[a+56>>2]<=y>>>0){p=H[a+28>>2];b=1;break ia}b=((N(H[a+16>>2],H[a+32>>2])+N(H[a+12>>2],y)|0)+N(H[a+20>>2],H[a+28>>2])|0)+N(H[a+24>>2],H[a+36>>2])|0;if(b>>>0>=K[a+8>>2]){break c}b=H[a+4>>2]+(b<<1)|0;if(J[b>>1]){break ka}break a}o=H[a+32>>2]+1|0;break ja}b=0;continue ea}b=1;continue ea}while(1){ma:{na:{oa:{pa:{if(!b){H[a+220>>2]=B;if(K[a+104>>2]<=B>>>0){break oa}H[a+28>>2]=A;p=A;break pa}p=p+1|0;H[a+28>>2]=p}if(p>>>0>>0){m=H[a+32>>2];e=H[a+200>>2]+(p<<4)|0;b=H[e+8>>2];if(m>>>0>=b>>>0){break ma}g=H[e>>2];c=g;f=b+(m^-1)|0;i=f;d=f&31;if((f&63)>>>0>=32){b=c<>>32-d;v=g<>>0>=32){b=b>>>d|0}else{b=((1<>>d}if((q|0)!=(b|0)){break ma}b=i&31;if((i&63)>>>0>=32){b=-1>>>b|0}else{b=(1<>>b}c=H[e+4>>2];if((b&c)!=(c|0)){break ma}d=i&31;if((i&63)>>>0>=32){b=c<>>32-d;w=c<>2];j=F+d|0;O=ve(j,d>>>0>j>>>0?h+1|0:h,w,b);b=h;L=H[a+208>>2];d=F+L|0;b=L>>>0>d>>>0?b+1|0:b;s=ve(d,b,w,n);C=v-1|0;j=H[a+212>>2];l=C+j|0;d=t-!v|0;b=d;x=ve(l,l>>>0>>0?b+1|0:b,v,t);D=H[a+204>>2];j=C+D|0;b=D>>>0>j>>>0?b+1|0:b;j=ve(j,b,v,t);z=H[e+12>>2]+(m<<4)|0;M=H[z>>2];m=M+f|0;b=m&31;if((m&63)>>>0>=32){b=-1>>>b|0}else{b=(1<>>b}if((g|0)!=(b&g)){break ma}h=c;P=H[z+4>>2];f=P+f|0;e=f&31;if((f&63)>>>0>=32){b=c<>>32-e;e=c<>>0>=32){c=b>>>l|0}else{c=((1<>>l}if((h|0)!=(c|0)){break ma}l=H[a+224>>2];e=!!(we(l,e,b)|qa);b=f&31;if((f&63)>>>0>=32){h=-1<>>32-b;b=-1<>>0>=32){h=f<>>32-e|b<>2];if((m&63)>>>0>=32){b=g<>>32-f;f=g<>>0>=32){h=-1<>>32-b;b=-1<>>0>=32){h=f<>>32-m|b<>2];if(!f|(!H[z+12>>2]|(j|0)==(x|0))){break ma}if((s|0)==(O|0)){break ma}y=H[a+68>>2];H[a+40>>2]=y;b=d;c=c+C|0;b=c>>>0>>0?b+1|0:b;g=(ve(c,b,v,t)>>>M)-(j>>>M)|0;b=q;c=l+F|0;b=c>>>0>>0?b+1|0:b;S=a,T=N(f,(ve(c,b,w,n)>>>P)-(s>>>P)|0)+g|0,H[S+36>>2]=T;b=1;continue ha}c=H[a+220>>2];b=H[a+228>>2];B=c+b-(c>>>0)%(b>>>0)|0;break na}c=H[a+224>>2];b=H[a+232>>2];E=c+b-(c>>>0)%(b>>>0)|0;b=0;continue ha}b=0;continue}b=1;continue}}}}qa:while(1){ra:{sa:{if(!b){e=e+1|0;break sa}H[a+32>>2]=i;if(K[a+60>>2]<=i>>>0){break b}k=H[a+68>>2];b=0;break ra}b=1}ta:while(1){ua:{va:{wa:{xa:{if(!b){H[a+40>>2]=k;if(K[a+56>>2]<=k>>>0){break xa}H[a+28>>2]=c;b=c;o=0;break ua}H[a+36>>2]=e;if(K[a+76>>2]<=e>>>0){b=H[a+28>>2];o=1;break ua}b=((N(H[a+16>>2],H[a+32>>2])+N(H[a+12>>2],H[a+40>>2])|0)+N(H[a+20>>2],H[a+28>>2])|0)+N(H[a+24>>2],e)|0;if(b>>>0>=K[a+8>>2]){break c}b=H[a+4>>2]+(b<<1)|0;if(J[b>>1]){break wa}break a}i=H[a+32>>2]+1|0;break va}b=0;continue qa}b=1;continue qa}while(1){ya:{za:{Aa:{if(!o){if(b>>>0>=q>>>0){break Aa}g=H[a+32>>2];d=H[a+200>>2]+(b<<4)|0;if(g>>>0>=K[d+8>>2]){break ya}if(!I[a|0]){b=H[d+12>>2]+(g<<4)|0;H[a+76>>2]=N(H[b+12>>2],H[b+8>>2])}e=H[a+72>>2];b=1;continue ta}b=b+1|0;H[a+28>>2]=b;break za}k=H[a+40>>2]+1|0;b=0;continue ta}o=0;continue}o=1;continue}}}}return 0}Ba(H[a+236>>2],1,1343,0)}return 0}G[b>>1]=1;return 1}function nd(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=O(0),t=0,u=0,v=0,w=O(0),x=0,z=0,A=0,C=O(0),D=O(0),E=O(0),G=0,J=0,K=0,L=0,M=0,Q=O(0),R=0,S=0,T=0;n=na-8320|0;na=n;H[n+64>>2]=0;j=2;g=H[a>>2];a:{b:{if((g|0)==176622093){break b}if((g|0)!=1375686655){if(!((g|0)!=201326592|H[a+4>>2]!=538988650)&H[a+8>>2]==176622093){break b}Y(1101);j=1;break a}j=0}g=Ea(1,96);k=0;c:{if(!g){break c}H[g+76>>2]=1;d:{e:{f:{switch(j|0){case 0:H[g+88>>2]=68;H[g+84>>2]=69;H[g+80>>2]=70;H[g+16>>2]=71;H[g+4>>2]=72;H[g+28>>2]=73;H[g+24>>2]=74;H[g+20>>2]=75;H[g>>2]=76;H[g+92>>2]=77;H[g+44>>2]=78;H[g+40>>2]=79;H[g+36>>2]=80;H[g+32>>2]=81;H[g+12>>2]=82;H[g+8>>2]=83;i=Jb();H[g+48>>2]=i;if(i){break e}break d;case 2:break f;default:break d}}H[g+88>>2]=84;H[g+84>>2]=85;H[g+80>>2]=86;H[g+16>>2]=87;H[g+4>>2]=88;H[g+92>>2]=89;H[g+44>>2]=90;H[g+40>>2]=91;H[g+36>>2]=92;H[g+32>>2]=93;H[g+28>>2]=94;H[g+24>>2]=95;H[g+20>>2]=96;H[g+12>>2]=97;H[g+8>>2]=98;H[g>>2]=99;i=Ea(1,136);g:{if(i){k=Jb();H[i>>2]=k;h:{if(!k){break h}F[i+124|0]=0;H[i+116>>2]=0;H[i+120>>2]=0;H[i+108>>2]=0;H[i+112>>2]=0;k=qb();H[i+4>>2]=k;if(!k){break h}k=qb();H[i+8>>2]=k;if(!k){break h}break g}Fc(i)}i=0}H[g+48>>2]=i;if(!i){break d}}H[g+72>>2]=1;H[g+64>>2]=1;H[g+60>>2]=0;H[g+52>>2]=0;H[g+56>>2]=0;H[g+68>>2]=1;k=g;break c}Ca(g);k=0}g=k;if(g){H[g+60>>2]=0;H[g+72>>2]=100}if(g){H[g+56>>2]=0;H[g+68>>2]=101}if(g){H[g+52>>2]=0;H[g+64>>2]=102}i=n+68|0;if(i){y(i,0,8248);H[i+8248>>2]=0;H[i+8200>>2]=-1;H[i+8204>>2]=-1}if(d){H[n+8316>>2]=H[n+8316>>2]|1}H[n+60>>2]=b;H[n+56>>2]=a;H[n+52>>2]=a;j=1;b=0;i=n+52|0;i:{if(!i){break i}a=Ea(1,72);if(a){j:{H[a+64>>2]=1048576;k=Fa(1048576);H[a+32>>2]=k;if(!k){Ca(a);a=0;break j}H[a+36>>2]=k;H[a+28>>2]=2;H[a+24>>2]=3;H[a+20>>2]=4;H[a+16>>2]=5;H[a+44>>2]=6;H[a+40>>2]=8;H[a+68>>2]=H[a+68>>2]|2}}else{a=0}if(!a){break i}if(a){H[a+4>>2]=0;H[a>>2]=i}if(a){H[a+8>>2]=H[i+8>>2];H[a+12>>2]=0}if(!(!a|!(I[a+68|0]&2))){H[a+16>>2]=64}if(a){H[a+24>>2]=66}if(a){H[a+28>>2]=67}b=a}a=n+68|0;if(!g|!a){a=0}else{k:{if(!H[g+76>>2]){Ba(g+52|0,1,9902,0);a=0;break k}ra[H[g+24>>2]](H[g+48>>2],a);a=1}}if(!a){Y(1153);fb(b);hb(g);break a}if(!b|!g){a=0}else{l:{if(!H[g+76>>2]){Ba(g+52|0,1,9983,0);a=0;break l}a=ra[H[g>>2]](b,H[g+48>>2],n- -64|0,g+52|0)|0}}if(!a){Y(1181);fb(b);hb(g);Ua(H[n+64>>2]);break a}m:{if(!f){break m}if(g){a=ra[H[g+40>>2]](H[g+48>>2],f,g+52|0)|0}else{a=0}if(a){break m}Y(1116);fb(b);hb(g);Ua(H[n+64>>2]);break a}a=H[n+64>>2];n:{if(!H[g+76>>2]|(!g|!b)){a=0}else{a=ra[H[g+4>>2]](H[g+48>>2],b,a,g+52|0)|0}if(a){if(!H[g+76>>2]|(!g|!b)){a=0}else{a=ra[H[g+16>>2]](H[g+48>>2],b,g+52|0)|0}if(a){break n}}Y(1316);hb(g);fb(b);Ua(H[n+64>>2]);break a}fb(b);hb(g);l=H[n+64>>2];a=H[l+28>>2];if(a){Ca(a);l=H[n+64>>2];H[l+28>>2]=0;H[l+32>>2]=0}v=H[l+16>>2];o:{p:{if(!c){if(!(!e|(v|0)!=4)){p=1;v=4;break o}q:{b=H[l+20>>2];if(!((b|0)==3|(v|0)!=3)){a=H[l+24>>2];if(H[a>>2]!=H[a+4>>2]|H[a+52>>2]==1){break q}H[l+20>>2]=3;break p}if(v>>>0>2){break q}H[l+20>>2]=2;break o}r:{switch(b-3|0){case 2:s:{t:{if(v>>>0<4){break t}b=H[l+24>>2];a=H[b>>2];if((a|0)!=H[b+52>>2]|(a|0)!=H[b+104>>2]|(a|0)!=H[b+156>>2]){break t}a=H[b+4>>2];if((a|0)!=H[b+56>>2]|(a|0)!=H[b+108>>2]){break t}if((a|0)==H[b+160>>2]){break s}}H[n+20>>2]=1053;H[n+16>>2]=1373;Ga(26072,8179,n+16|0);break o}f=N(H[b+12>>2],H[b+8>>2]);C=O(O(1)/O((-1<>2]^-1)>>>0));D=O(O(1)/O((-1<>2]^-1)>>>0));w=O(O(1)/O((-1<>2]^-1)>>>0));Q=O(O(1)/O((-1<>2]^-1)>>>0));a=0;while(1){if((a|0)!=(f|0)){c=a<<2;g=c+H[b+148>>2]|0;i=H[g>>2];k=c+H[b+96>>2]|0;j=H[k>>2];m=c+H[b+44>>2]|0;s=O(O(1)-O(C*O(H[c+H[b+200>>2]>>2])));E=O(O(O(O(1)-O(Q*O(H[m>>2])))*O(255))*s);if(O(P(E))>2]=c;E=O(O(O(O(1)-O(w*O(j|0)))*O(255))*s);if(O(P(E))>2]=c;s=O(O(O(O(1)-O(D*O(i|0)))*O(255))*s);if(O(P(s))>2]=c;a=a+1|0;continue}break};Ca(H[b+200>>2]);a=H[l+24>>2];H[a+128>>2]=8;H[a+76>>2]=8;H[a+24>>2]=8;H[a+200>>2]=0;H[l+20>>2]=1;a=H[l+16>>2]-1|0;H[l+16>>2]=a;h=3;while(1){if(a>>>0<=h>>>0){break o}a=H[l+24>>2]+N(h,52)|0;B(a,a+52|0,52);h=h+1|0;a=H[l+16>>2];continue};case 0:break p;case 1:break r;default:break o}}b=H[l+24>>2];a=H[b>>2];u:{v:{if((a|0)!=H[b+52>>2]|(a|0)!=H[b+104>>2]){break v}a=H[b+4>>2];if((a|0)!=H[b+56>>2]){break v}if((a|0)==H[b+108>>2]){break u}}H[n+36>>2]=1115;H[n+32>>2]=1373;Ga(26072,8221,n+32|0);break o}a=H[b+24>>2];c=-1<>2]?0:a;i=H[b+84>>2]?0:a;k=N(H[b+12>>2],H[b+8>>2]);a=0;while(1){if((a|0)!=(k|0)){f=a<<2;j=f+H[b+44>>2]|0;m=f+H[b+148>>2]|0;s=O(H[m>>2]-g|0);h=f+H[b+96>>2]|0;C=O(H[h>>2]-i|0);D=O(H[j>>2]);w=O(O(O(s*O(1.4019900560379028))+O(O(C*O(-3680000008898787e-20))+D))+O(.5));if(O(P(w))>2]=(c|0)<(f|0)?c:(f|0)>0?f:0;w=O(O(O(s*O(-.7141128182411194))+O(O(D*O(1.0003000497817993))+O(C*O(-.34412500262260437))))+O(.5));if(O(P(w))>2]=(c|0)<(f|0)?c:(f|0)>0?f:0;s=O(O(O(s*O(-7999999979801942e-21))+O(O(D*O(.9998229742050171))+O(C*O(1.7720400094985962))))+O(.5));if(O(P(s))>2]=(c|0)<(f|0)?c:(f|0)>0?f:0;a=a+1|0;continue}break}H[l+20>>2]=1;break o}v=c>>>0>v>>>0?v:c;p=1;break o}w:{x:{c=H[l+24>>2];if(H[c>>2]!=1){break x}y:{switch(H[c+52>>2]-1|0){case 1:if(H[c+104>>2]!=2){break x}if(!(H[c+4>>2]!=1|H[c+56>>2]!=2|H[c+108>>2]!=2)){b=H[c+24>>2];h=H[c+148>>2];a=H[c+96>>2];j=H[c+44>>2];G=H[c+60>>2];q=H[c+8>>2];f=H[c+12>>2];c=N(q,f)<<2;g=Ia(c);i=Ia(c);k=Ia(c);if(!(!g|!i|!k)){m=-1<>2]&1;L=f-b|0;K=H[l>>2]&1;x=q-K|0;if(!b){c=k;f=i;b=g;break w}c=k;f=i;b=g;while(1){if((p|0)==(q|0)){break w}Ka(o,m,H[j>>2],0,0,b,f,c);p=p+1|0;c=c+4|0;f=f+4|0;b=b+4|0;j=j+4|0;continue}}Ca(g);Ca(i);Ca(k);break o}if(H[c+4>>2]!=1|H[c+56>>2]!=1|H[c+108>>2]!=1){break x}a=H[c+24>>2];f=H[c+148>>2];b=H[c+96>>2];h=H[c+44>>2];t=H[c+60>>2];g=H[c+8>>2];x=H[c+12>>2];c=N(g,x)<<2;i=Ia(c);k=Ia(c);m=Ia(c);if(!(!i|!k|!m)){o=-1<>2]&1;a=g-z|0;G=a&1;u=a>>>1|0;J=a&-2;a=m;j=k;c=i;while(1){if((q|0)!=(x|0)){if(z){Ka(r,o,H[h>>2],0,0,c,j,a);j=j+4|0;c=c+4|0;h=h+4|0;a=a+4|0}g=0;while(1){if(g>>>0>>0){Ka(r,o,H[h>>2],H[b>>2],H[f>>2],c,j,a);Ka(r,o,H[h+4>>2],H[b>>2],H[f>>2],c+4|0,j+4|0,a+4|0);g=g+2|0;f=f+4|0;b=b+4|0;a=a+8|0;j=j+8|0;c=c+8|0;h=h+8|0;continue}break}z:{if(!G){break z}g=H[h>>2];A:{if((t|0)==(u|0)){Ka(r,o,g,0,0,c,j,a);break A}Ka(r,o,g,H[b>>2],H[f>>2],c,j,a)}a=a+4|0;j=j+4|0;c=c+4|0;h=h+4|0;if(t>>>0<=u>>>0){break z}f=f+4|0;b=b+4|0}q=q+1|0;continue}break}Ca(H[H[l+24>>2]+44>>2]);a=H[l+24>>2];H[a+44>>2]=i;Ca(H[a+96>>2]);a=H[l+24>>2];H[a+96>>2]=k;Ca(H[a+148>>2]);a=H[l+24>>2];H[a+148>>2]=m;b=H[a+8>>2];H[a+112>>2]=b;H[a+60>>2]=b;b=H[a+12>>2];H[a+116>>2]=b;H[a+64>>2]=b;b=H[a>>2];H[a+104>>2]=b;H[a+52>>2]=b;b=H[a+4>>2];H[a+108>>2]=b;H[a+56>>2]=b;H[l+20>>2]=1;break o}Ca(i);Ca(k);Ca(m);break o;case 0:break y;default:break x}}if(H[c+104>>2]!=1|H[c+4>>2]!=1|(H[c+56>>2]!=1|H[c+108>>2]!=1)){break x}b=H[c+24>>2];h=H[c+148>>2];a=H[c+96>>2];j=H[c+44>>2];o=N(H[c+12>>2],H[c+8>>2]);c=o<<2;i=Ia(c);k=Ia(c);m=Ia(c);if(!(!i|!k|!m)){q=-1<>2],H[a>>2],H[h>>2],g,b,f);c=c+1|0;f=f+4|0;b=b+4|0;g=g+4|0;h=h+4|0;a=a+4|0;j=j+4|0;continue}break}Ca(H[H[l+24>>2]+44>>2]);a=H[l+24>>2];H[a+44>>2]=i;Ca(H[a+96>>2]);a=H[l+24>>2];H[a+96>>2]=k;Ca(H[a+148>>2]);H[H[l+24>>2]+148>>2]=m;H[l+20>>2]=1;break o}Ca(i);Ca(k);Ca(m);break o}H[n+4>>2]=463;H[n>>2]=1373;Ga(26072,8264,n);break o}J=x>>>1|0;z=x&-2;R=L&-2;u=q<<2;while(1){if(M>>>0>>0){p=c+u|0;r=f+u|0;q=b+u|0;t=j+u|0;if(K){Ka(o,m,H[j>>2],0,0,b,f,c);Ka(o,m,H[t>>2],H[a>>2],H[h>>2],q,r,p);p=p+4|0;r=r+4|0;q=q+4|0;t=t+4|0;c=c+4|0;f=f+4|0;j=j+4|0;b=b+4|0}A=0;while(1){if(z>>>0>A>>>0){Ka(o,m,H[j>>2],H[a>>2],H[h>>2],b,f,c);Ka(o,m,H[j+4>>2],H[a>>2],H[h>>2],b+4|0,f+4|0,c+4|0);Ka(o,m,H[t>>2],H[a>>2],H[h>>2],q,r,p);Ka(o,m,H[t+4>>2],H[a>>2],H[h>>2],q+4|0,r+4|0,p+4|0);A=A+2|0;h=h+4|0;a=a+4|0;p=p+8|0;r=r+8|0;q=q+8|0;t=t+8|0;c=c+8|0;f=f+8|0;b=b+8|0;j=j+8|0;continue}break}B:{if((x|0)==(z|0)){break B}A=H[j>>2];C:{if((G|0)==(J|0)){Ka(o,m,A,0,0,b,f,c);Ka(o,m,H[t>>2],0,0,q,r,p);break C}Ka(o,m,A,H[a>>2],H[h>>2],b,f,c);Ka(o,m,H[t>>2],H[a>>2],H[h>>2],q,r,p)}c=c+4|0;f=f+4|0;b=b+4|0;j=j+4|0;if(G>>>0<=J>>>0){break B}h=h+4|0;a=a+4|0}M=M+2|0;c=c+u|0;f=f+u|0;b=b+u|0;j=j+u|0;continue}break}D:{if(!(L&1)){break D}if(K){Ka(o,m,H[j>>2],0,0,b,f,c);c=c+4|0;f=f+4|0;j=j+4|0;b=b+4|0}p=0;while(1){if(p>>>0>>0){Ka(o,m,H[j>>2],H[a>>2],H[h>>2],b,f,c);Ka(o,m,H[j+4>>2],H[a>>2],H[h>>2],b+4|0,f+4|0,c+4|0);p=p+2|0;h=h+4|0;a=a+4|0;c=c+8|0;f=f+8|0;b=b+8|0;j=j+8|0;continue}break}if((x|0)==(z|0)){break D}j=H[j>>2];if((G|0)==(J|0)){Ka(o,m,j,0,0,b,f,c);break D}Ka(o,m,j,H[a>>2],H[h>>2],b,f,c)}Ca(H[H[l+24>>2]+44>>2]);a=H[l+24>>2];H[a+44>>2]=g;Ca(H[a+96>>2]);a=H[l+24>>2];H[a+96>>2]=i;Ca(H[a+148>>2]);a=H[l+24>>2];H[a+148>>2]=k;b=H[a+8>>2];H[a+112>>2]=b;H[a+60>>2]=b;b=H[a+12>>2];H[a+116>>2]=b;H[a+64>>2]=b;b=H[a>>2];H[a+104>>2]=b;H[a+52>>2]=b;b=H[a+4>>2];H[a+108>>2]=b;H[a+56>>2]=b;H[l+20>>2]=1;p=0}c=H[n+64>>2];E:{if(d){break E}b=0;while(1){if((b|0)==(v|0)){break E}a=H[c+24>>2]+N(b,52)|0;d=H[a+24>>2];if((d|0)!=8){F:{if(d>>>0<=7){f=N(H[a+12>>2],H[a+8>>2]);g=H[a+44>>2];if(H[a+32>>2]){i=1<>2];k=d>>31<<7|d>>>25;S=m,T=ue(d<<7,k,i,0),H[S>>2]=T;h=h+1|0;continue}}d=-1<>2],0,255),qa,d,0);H[i>>2]=k;h=h+1|0;continue}}d=d-8|0;f=N(H[a+12>>2],H[a+8>>2]);g=H[a+44>>2];h=0;if(H[a+32>>2]){while(1){if((f|0)==(h|0)){break F}i=g+(h<<2)|0;H[i>>2]=H[i>>2]>>d;h=h+1|0;continue}}while(1){if((f|0)==(h|0)){break F}i=g+(h<<2)|0;H[i>>2]=H[i>>2]>>>d;h=h+1|0;continue}}H[a+24>>2]=8}b=b+1|0;continue}}a=H[c+24>>2];b=N(H[a+12>>2],H[a+8>>2]);G:{if(!p){if(H[c+20>>2]==2){if(H[c+16>>2]==1){ma(H[a+44>>2],b|0);break G}if(!e){break G}da(H[a+44>>2],H[a+96>>2],b|0);break G}ca(H[a+44>>2],H[a+96>>2],H[a+148>>2],b|0);break G}H:{switch(v-1|0){case 0:ba(H[a+44>>2],b|0);break G;case 2:aa(H[a+44>>2],H[a+96>>2],H[a+148>>2],b|0);break G;case 3:break H;default:break G}}$(H[a+44>>2],H[a+96>>2],H[a+148>>2],H[a+200>>2],b|0)}Ua(H[n+64>>2]);j=0}na=n+8320|0;return j|0}function ac(a,b,c,d,e,f,g,h,i){var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,z=0,A=0,C=0,D=0,E=0,F=0,G=0,I=0,J=0,L=0,M=0,O=0,P=0;j=H[a>>2];a:{if(j>>>0>>0|b>>>0>=d>>>0|b>>>0>=j>>>0){break a}j=H[a+4>>2];if(j>>>0>>0|c>>>0>=e>>>0|c>>>0>=j>>>0){break a}E=(c>>>0)/K[a+12>>2]|0;s=H[a+8>>2];G=(b>>>0)/(s>>>0)|0;M=(N(s,G)-b|0)+s|0;I=(g|0)!=8;z=c;while(1){k=H[a+12>>2];j=k;j=(c|0)==(z|0)?j-((c>>>0)%(j>>>0)|0)|0:j;t=e-z|0;r=j>>>0>>0?j:t;A=r&-4;u=r&3;O=r&-8;J=r&7;x=r-1|0;P=(g|0)==2&(r|0)==1;L=N(k-j|0,s);C=(N(z-c|0,h)<<2)+f|0;F=G;t=b;while(1){j=(b|0)==(t|0)?M:s;k=d-t|0;q=j>>>0>>0?j:k;k=s-j|0;l=F<<2;j=H[l+(H[a+24>>2]+(N(H[a+16>>2],E)<<2)|0)>>2];b:{c:{d:{e:{f:{g:{if(i){h:{i:{j:{k:{if(j){l=((L<<2)+j|0)+(k<<2)|0;j=t-b|0;if((g|0)==1){break h}m=(N(g,j)<<2)+C|0;if((q|0)==1){break i}if(P){break j}if(q>>>0<=7|I){break k}if(!r){break b}o=q&-4;k=0;while(1){j=0;while(1){H[(j<<5)+m>>2]=H[(j<<2)+l>>2];n=j|1;H[(n<<5)+m>>2]=H[(n<<2)+l>>2];n=j|2;H[(n<<5)+m>>2]=H[(n<<2)+l>>2];n=j|3;H[(n<<5)+m>>2]=H[(n<<2)+l>>2];j=j+4|0;if(o>>>0>j>>>0){continue}break}if(j>>>0>>0){while(1){H[(j<<5)+m>>2]=H[(j<<2)+l>>2];j=j+1|0;if((q|0)!=(j|0)){continue}break}}l=(s<<2)+l|0;m=(h<<2)+m|0;k=k+1|0;if((r|0)!=(k|0)){continue}break}break b}if((g|0)!=1){if(!r){break b}p=q&-4;n=q&3;l=(N(t-b|0,g)<<2)+C|0;o=0;D=q-1>>>0<3;while(1){l:{if(!q){break l}m=0;j=0;k=0;if(!D){while(1){H[(N(g,j)<<2)+l>>2]=0;H[(N(j|1,g)<<2)+l>>2]=0;H[(N(j|2,g)<<2)+l>>2]=0;H[(N(j|3,g)<<2)+l>>2]=0;j=j+4|0;k=k+4|0;if((p|0)!=(k|0)){continue}break}if(!n){break l}}while(1){H[(N(g,j)<<2)+l>>2]=0;j=j+1|0;m=m+1|0;if((n|0)!=(m|0)){continue}break}}l=(h<<2)+l|0;o=o+1|0;if((r|0)!=(o|0)){continue}break}break b}if(!r){break b}l=q<<2;k=(t-b<<2)+C|0;o=0;if(x>>>0>=7){break g}break f}if(!r){break b}D=q&-4;p=q&3;n=0;v=q-1>>>0<3;break c}j=0;k=q&-4;if(k){while(1){H[(j<<3)+m>>2]=H[(j<<2)+l>>2];o=j|1;H[(o<<3)+m>>2]=H[(o<<2)+l>>2];o=j|2;H[(o<<3)+m>>2]=H[(o<<2)+l>>2];o=j|3;H[(o<<3)+m>>2]=H[(o<<2)+l>>2];j=j+4|0;if(k>>>0>j>>>0){continue}break}}if(j>>>0>=q>>>0){break b}o=0;k=j;n=q-j&3;if(n){while(1){H[(k<<3)+m>>2]=H[(k<<2)+l>>2];k=k+1|0;o=o+1|0;if((n|0)!=(o|0)){continue}break}}if(j-q>>>0>4294967292){break b}while(1){H[(k<<3)+m>>2]=H[(k<<2)+l>>2];j=k+1|0;H[(j<<3)+m>>2]=H[(j<<2)+l>>2];j=k+2|0;H[(j<<3)+m>>2]=H[(j<<2)+l>>2];j=k+3|0;H[(j<<3)+m>>2]=H[(j<<2)+l>>2];k=k+4|0;if((q|0)!=(k|0)){continue}break}break b}if(!r){break b}k=0;if(x>>>0>=3){while(1){H[m>>2]=H[l>>2];j=h<<2;m=j+m|0;p=l;l=s<<2;o=p+l|0;H[m>>2]=H[o>>2];m=j+m|0;o=l+o|0;H[m>>2]=H[o>>2];m=j+m|0;o=l+o|0;H[m>>2]=H[o>>2];l=l+o|0;m=j+m|0;k=k+4|0;if((A|0)!=(k|0)){continue}break}if(!u){break b}}j=0;while(1){H[m>>2]=H[l>>2];l=(s<<2)+l|0;m=(h<<2)+m|0;j=j+1|0;if((u|0)!=(j|0)){continue}break}break b}j=(j<<2)+C|0;if((q|0)!=4){if(!r){break b}m=q<<2;o=0;if(x>>>0>=3){break e}break d}if(!r){break b}o=0;if(x>>>0>=3){while(1){k=H[l+12>>2];H[j+8>>2]=H[l+8>>2];H[j+12>>2]=k;k=H[l+4>>2];H[j>>2]=H[l>>2];H[j+4>>2]=k;k=l;l=s<<2;k=k+l|0;n=H[k+12>>2];m=h<<2;j=m+j|0;H[j+8>>2]=H[k+8>>2];H[j+12>>2]=n;n=H[k+4>>2];H[j>>2]=H[k>>2];H[j+4>>2]=n;k=l+k|0;n=H[k+12>>2];j=j+m|0;H[j+8>>2]=H[k+8>>2];H[j+12>>2]=n;n=H[k+4>>2];H[j>>2]=H[k>>2];H[j+4>>2]=n;k=l+k|0;n=H[k+12>>2];j=j+m|0;H[j+8>>2]=H[k+8>>2];H[j+12>>2]=n;n=H[k+4>>2];H[j>>2]=H[k>>2];H[j+4>>2]=n;l=l+k|0;j=j+m|0;o=o+4|0;if((A|0)!=(o|0)){continue}break}if(!u){break b}}m=0;while(1){k=H[l+12>>2];H[j+8>>2]=H[l+8>>2];H[j+12>>2]=k;k=H[l+4>>2];H[j>>2]=H[l>>2];H[j+4>>2]=k;l=(s<<2)+l|0;j=(h<<2)+j|0;m=m+1|0;if((u|0)!=(m|0)){continue}break}break b}if(!j){j=Ea(1,N(H[a+8>>2],H[a+12>>2])<<2);if(!j){return 0}H[l+(H[a+24>>2]+(N(H[a+16>>2],E)<<2)|0)>>2]=j}l=((L<<2)+j|0)+(k<<2)|0;j=t-b|0;m:{n:{o:{p:{q:{r:{if((g|0)!=1){m=(N(g,j)<<2)+C|0;if((q|0)==1){break r}if(q>>>0<=7|I){break q}if(!r){break b}o=q&-4;k=0;while(1){j=0;while(1){H[(j<<2)+l>>2]=H[(j<<5)+m>>2];n=j|1;H[(n<<2)+l>>2]=H[(n<<5)+m>>2];n=j|2;H[(n<<2)+l>>2]=H[(n<<5)+m>>2];n=j|3;H[(n<<2)+l>>2]=H[(n<<5)+m>>2];j=j+4|0;if(o>>>0>j>>>0){continue}break}if(j>>>0>>0){while(1){H[(j<<2)+l>>2]=H[(j<<5)+m>>2];j=j+1|0;if((q|0)!=(j|0)){continue}break}}l=(s<<2)+l|0;m=(h<<2)+m|0;k=k+1|0;if((r|0)!=(k|0)){continue}break}break b}j=(j<<2)+C|0;if((q|0)==4){break p}if(!r){break b}m=q<<2;o=0;if(x>>>0>=3){break o}break n}if(!r){break b}o=0;if(x>>>0>=3){while(1){H[l>>2]=H[m>>2];j=s<<2;l=j+l|0;k=h<<2;m=k+m|0;H[l>>2]=H[m>>2];l=j+l|0;m=k+m|0;H[l>>2]=H[m>>2];l=j+l|0;m=k+m|0;H[l>>2]=H[m>>2];l=j+l|0;m=k+m|0;o=o+4|0;if((A|0)!=(o|0)){continue}break}if(!u){break b}}j=0;while(1){H[l>>2]=H[m>>2];l=(s<<2)+l|0;m=(h<<2)+m|0;j=j+1|0;if((u|0)!=(j|0)){continue}break}break b}if(!r){break b}D=q&-4;p=q&3;n=0;break m}if(!r){break b}o=0;if(x>>>0>=3){while(1){k=H[j+12>>2];H[l+8>>2]=H[j+8>>2];H[l+12>>2]=k;k=H[j+4>>2];H[l>>2]=H[j>>2];H[l+4>>2]=k;m=h<<2;j=m+j|0;n=H[j+12>>2];k=l;l=s<<2;k=k+l|0;H[k+8>>2]=H[j+8>>2];H[k+12>>2]=n;n=H[j+4>>2];H[k>>2]=H[j>>2];H[k+4>>2]=n;j=j+m|0;n=H[j+12>>2];k=l+k|0;H[k+8>>2]=H[j+8>>2];H[k+12>>2]=n;n=H[j+4>>2];H[k>>2]=H[j>>2];H[k+4>>2]=n;j=j+m|0;n=H[j+12>>2];k=l+k|0;H[k+8>>2]=H[j+8>>2];H[k+12>>2]=n;n=H[j+4>>2];H[k>>2]=H[j>>2];H[k+4>>2]=n;j=j+m|0;l=l+k|0;o=o+4|0;if((A|0)!=(o|0)){continue}break}if(!u){break b}}m=0;while(1){k=H[j+12>>2];H[l+8>>2]=H[j+8>>2];H[l+12>>2]=k;k=H[j+4>>2];H[l>>2]=H[j>>2];H[l+4>>2]=k;j=(h<<2)+j|0;l=(s<<2)+l|0;m=m+1|0;if((u|0)!=(m|0)){continue}break}break b}while(1){k=!m;if(!k){B(l,j,m)}p=j;j=h<<2;n=p+j|0;p=l;l=s<<2;p=p+l|0;if(!k){B(p,n,m)}n=j+n|0;p=l+p|0;if(!k){B(p,n,m)}n=j+n|0;p=l+p|0;if(!k){B(p,n,m)}j=j+n|0;l=l+p|0;o=o+4|0;if((A|0)!=(o|0)){continue}break}if(!u){break b}}k=0;while(1){if(m){B(l,j,m)}j=(h<<2)+j|0;l=(s<<2)+l|0;k=k+1|0;if((u|0)!=(k|0)){continue}break}break b}while(1){s:{if(!q){break s}k=0;j=0;o=0;if(q>>>0>=4){while(1){H[(j<<2)+l>>2]=H[(N(g,j)<<2)+m>>2];v=j|1;H[(v<<2)+l>>2]=H[(N(g,v)<<2)+m>>2];v=j|2;H[(v<<2)+l>>2]=H[(N(g,v)<<2)+m>>2];v=j|3;H[(v<<2)+l>>2]=H[(N(g,v)<<2)+m>>2];j=j+4|0;o=o+4|0;if((D|0)!=(o|0)){continue}break}if(!p){break s}}while(1){H[(j<<2)+l>>2]=H[(N(g,j)<<2)+m>>2];j=j+1|0;k=k+1|0;if((p|0)!=(k|0)){continue}break}}l=(s<<2)+l|0;m=(h<<2)+m|0;n=n+1|0;if((r|0)!=(n|0)){continue}break}break b}while(1){j=!l;if(!j){y(k,0,l)}p=k;k=h<<2;m=p+k|0;if(!j){y(m,0,l)}m=k+m|0;if(!j){y(m,0,l)}m=k+m|0;if(!j){y(m,0,l)}m=k+m|0;if(!j){y(m,0,l)}m=k+m|0;if(!j){y(m,0,l)}m=k+m|0;if(!j){y(m,0,l)}m=k+m|0;if(!j){y(m,0,l)}k=k+m|0;o=o+8|0;if((O|0)!=(o|0)){continue}break}if(!J){break b}}j=0;while(1){if(l){y(k,0,l)}k=(h<<2)+k|0;j=j+1|0;if((J|0)!=(j|0)){continue}break}break b}while(1){k=!m;if(!k){B(j,l,m)}p=l;l=s<<2;n=p+l|0;p=j;j=h<<2;p=p+j|0;if(!k){B(p,n,m)}n=l+n|0;p=j+p|0;if(!k){B(p,n,m)}n=l+n|0;p=j+p|0;if(!k){B(p,n,m)}l=l+n|0;j=j+p|0;o=o+4|0;if((A|0)!=(o|0)){continue}break}if(!u){break b}}k=0;while(1){if(m){B(j,l,m)}l=(s<<2)+l|0;j=(h<<2)+j|0;k=k+1|0;if((u|0)!=(k|0)){continue}break}break b}while(1){t:{if(!q){break t}k=0;j=0;o=0;if(!v){while(1){H[(N(g,j)<<2)+m>>2]=H[(j<<2)+l>>2];w=j|1;H[(N(w,g)<<2)+m>>2]=H[(w<<2)+l>>2];w=j|2;H[(N(w,g)<<2)+m>>2]=H[(w<<2)+l>>2];w=j|3;H[(N(w,g)<<2)+m>>2]=H[(w<<2)+l>>2];j=j+4|0;o=o+4|0;if((D|0)!=(o|0)){continue}break}if(!p){break t}}while(1){H[(N(g,j)<<2)+m>>2]=H[(j<<2)+l>>2];j=j+1|0;k=k+1|0;if((p|0)!=(k|0)){continue}break}}l=(s<<2)+l|0;m=(h<<2)+m|0;n=n+1|0;if((r|0)!=(n|0)){continue}break}}F=F+1|0;t=q+t|0;if(t>>>0>>0){continue}break}E=E+1|0;z=r+z|0;if(z>>>0>>0){continue}break}}return 1}function Od(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0;if(!c){return 0}if(!Gb(H[a>>2],b,c,d)){Ba(d,1,6210,0);return 0}n=c;m=d;g=na-240|0;na=g;r=1;o=a;a:{if(H[H[a>>2]+60>>2]|H[a+128>>2]){break a}b:{c=H[a+116>>2];c:{if(!c){a=H[a+120>>2];break c}b=H[n+16>>2];a=H[o+120>>2];if(!(!a|!H[a+12>>2])){b=I[a+18|0]}d:{d=J[c+4>>1];if(d){c=H[c>>2];while(1){f=c+N(e,6)|0;h=J[f>>1];if(h>>>0>=b>>>0){H[g+180>>2]=b;H[g+176>>2]=h;Ba(m,1,13715,g+176|0);r=0;break a}e:{f=J[f+4>>1];if((f+1&65535)>>>0<2){break e}f=f-1|0;if(f>>>0>>0){break e}H[g+164>>2]=b;H[g+160>>2]=f;Ba(m,1,13715,g+160|0);r=0;break a}e=e+1|0;if((d|0)!=(e|0)){continue}break}break d}if(b){break b}break c}while(1){b=b-1|0;e=0;while(1){if(J[c+N(e,6)>>1]!=(b|0)){e=e+1|0;if((d|0)!=(e|0)){continue}break b}break}if(b){continue}break}}f:{if(!a){break f}c=H[a+12>>2];if(!c){break f}g:{b=I[a+18|0];h:{if(b){e=0;h=1;while(1){a=H[n+16>>2];d=J[c+(e<<2)>>1];if(a>>>0<=d>>>0){H[g+148>>2]=a;H[g+144>>2]=d;Ba(m,1,13715,g+144|0);h=0}e=e+1|0;if((b|0)!=(e|0)){continue}break}a=Ea(b,4);if(!a){break h}e=0;while(1){d=c+(e<<2)|0;f=I[d+2|0];i:{if(f>>>0>=2){H[g+68>>2]=f;H[g+64>>2]=e;Ba(m,1,12094,g- -64|0);h=0;break i}d=I[d+3|0];if(d>>>0>=b>>>0){H[g+128>>2]=d;Ba(m,1,12038,g+128|0);h=0;break i}j=(f|0)!=1;i=(d<<2)+a|0;if(!(j|!H[i>>2])){H[g+80>>2]=d;Ba(m,1,11527,g+80|0);h=0;break i}if(!(f|!d)){H[g+100>>2]=d;H[g+96>>2]=e;Ba(m,1,11901,g+96|0);h=0;break i}if(!(j|(d|0)==(e|0))){H[g+120>>2]=d;H[g+116>>2]=e;H[g+112>>2]=e;Ba(m,1,11937,g+112|0);h=0;break i}H[i>>2]=1}e=e+1|0;if((b|0)!=(e|0)){continue}break}h=!h;e=0;while(1){j:{d=e<<2;if(I[(c+d|0)+2|0]?H[d+a>>2]:1){e=e+1|0;if((b|0)!=(e|0)){continue}if(h&1){break j}if(H[n+16>>2]!=1){break g}e=0;while(1){if(H[(e<<2)+a>>2]){e=e+1|0;if((b|0)!=(e|0)){continue}break g}break}d=0;Ba(m,2,9253,0);f=b&3;e=0;if(b>>>0>=4){h=b&252;b=0;while(1){j=c+(e<<2)|0;F[j+3|0]=e;F[j+2|0]=1;j=e|1;i=c+(j<<2)|0;F[i+3|0]=j;F[i+2|0]=1;j=e|2;i=c+(j<<2)|0;F[i+3|0]=j;F[i+2|0]=1;j=e|3;i=c+(j<<2)|0;F[i+3|0]=j;F[i+2|0]=1;e=e+4|0;b=b+4|0;if((h|0)!=(b|0)){continue}break}if(!f){break g}}while(1){b=c+(e<<2)|0;F[b+3|0]=e;F[b+2|0]=1;e=e+1|0;d=d+1|0;if((f|0)!=(d|0)){continue}break}break g}H[g+48>>2]=e;h=1;Ba(m,1,11101,g+48|0);e=e+1|0;if((b|0)!=(e|0)){continue}}break}Ca(a);r=0;break a}a=Ea(b,4);if(a){break g}}r=0;Ba(m,1,12285,0);break a}Ca(a)}a=H[o+120>>2];k:{if(!a){break k}l=H[a+12>>2];if(!l){Ca(H[a+4>>2]);Ca(H[H[o+120>>2]+8>>2]);Ca(H[H[o+120>>2]>>2]);a=H[o+120>>2];b=H[a+12>>2];if(b){Ca(b);a=H[o+120>>2]}Ca(a);H[o+120>>2]=0;break k}j=H[n+24>>2];l:{c=I[a+18|0];m:{if(c){u=H[a>>2];h=H[a+4>>2];i=H[a+8>>2];e=0;n:{while(1){if(H[(j+N(J[l+(e<<2)>>1],52)|0)+44>>2]){e=e+1|0;if((c|0)!=(e|0)){continue}break n}break}H[g+32>>2]=e;Ba(m,1,13877,g+32|0);r=0;break a}f=Fa(N(c,52));if(!f){break m}d=0;while(1){a=l+(d<<2)|0;e=J[a>>1];b=N(I[a+2|0]?I[a+3|0]:d,52)+f|0;a=j+N(e,52)|0;H[b+48>>2]=H[a+48>>2];e=H[a+44>>2];H[b+40>>2]=H[a+40>>2];H[b+44>>2]=e;e=H[a+36>>2];H[b+32>>2]=H[a+32>>2];H[b+36>>2]=e;e=H[a+28>>2];H[b+24>>2]=H[a+24>>2];H[b+28>>2]=e;e=H[a+20>>2];H[b+16>>2]=H[a+16>>2];H[b+20>>2]=e;e=H[a+12>>2];H[b+8>>2]=H[a+8>>2];H[b+12>>2]=e;e=H[a+4>>2];H[b>>2]=H[a>>2];H[b+4>>2]=e;b=N(d,52)+f|0;a=Ia(N(H[a+8>>2],H[a+12>>2])<<2);H[b+44>>2]=a;if(!a){if(d){a=d&65535;while(1){Ca(H[(N(a,52)+f|0)-8>>2]);a=a-1|0;if(a){continue}break}}Ca(f);r=0;Ba(m,1,13825,0);break a}H[b+24>>2]=I[d+i|0];H[b+32>>2]=I[d+h|0];d=d+1|0;if((c|0)!=(d|0)){continue}break}h=J[H[o+120>>2]+16>>1]-1|0;while(1){a=N(q,52)+f|0;d=N(H[a+12>>2],H[a+8>>2]);b=l+(q<<2)|0;e=H[(j+N(J[b>>1],52)|0)+44>>2];o:{if(!I[b+2|0]){if(!d){break o}i=H[a+44>>2];p=d&3;a=0;b=0;if(d>>>0>=4){t=d&-4;k=0;while(1){d=b<<2;H[d+i>>2]=H[d+e>>2];s=d|4;H[s+i>>2]=H[e+s>>2];s=d|8;H[s+i>>2]=H[e+s>>2];d=d|12;H[d+i>>2]=H[d+e>>2];b=b+4|0;k=k+4|0;if((t|0)!=(k|0)){continue}break}if(!p){break o}}while(1){d=b<<2;H[d+i>>2]=H[d+e>>2];b=b+1|0;a=a+1|0;if((p|0)!=(a|0)){continue}break}break o}if(!d){break o}b=I[b+3|0];a=(b<<2)+u|0;i=H[(N(b,52)+f|0)+44>>2];b=0;if((d|0)!=1){t=d&1;s=d&-2;d=0;while(1){k=b<<2;p=H[k+e>>2];H[i+k>>2]=H[a+(N(c,(p|0)>=0?(h|0)>(p|0)?p:h:0)<<2)>>2];k=k|4;p=H[k+e>>2];H[i+k>>2]=H[a+(N(c,(p|0)>=0?(h|0)>(p|0)?p:h:0)<<2)>>2];b=b+2|0;d=d+2|0;if((s|0)!=(d|0)){continue}break}if(!t){break o}}d=b<<2;b=H[d+e>>2];H[d+i>>2]=H[a+(N(c,(b|0)>=0?(b|0)<(h|0)?b:h:0)<<2)>>2]}q=q+1|0;if((c|0)!=(q|0)){continue}break}break l}f=Fa(N(c,52));if(f){break l}}r=0;Ba(m,1,13825,0);break a}a=H[n+16>>2];if(a){e=0;while(1){b=H[(j+N(e,52)|0)+44>>2];if(b){Ca(b)}e=e+1|0;if((a|0)!=(e|0)){continue}break}}Ca(j);H[n+16>>2]=c;H[n+24>>2]=f}e=H[o+116>>2];if(!e){break a}h=H[e>>2];i=J[e+4>>1];if(i){e=0;p=i-2&65535;d=1;while(1){a=H[n+16>>2];q=N(e,6)+h|0;b=J[q>>1];p:{if(a>>>0<=b>>>0){H[g+20>>2]=a;H[g+16>>2]=b;Ba(m,2,7334,g+16|0);break p}c=J[q+4>>1];if((c+1&65535)>>>0<=1){G[(H[n+24>>2]+N(b,52)|0)+48>>1]=J[q+2>>1];break p}c=c-1|0;j=c&65535;if(j>>>0>=a>>>0){H[g+4>>2]=a;H[g>>2]=j;Ba(m,2,7293,g);break p}q:{if(J[q+2>>1]|(b|0)==(j|0)){break q}f=H[n+24>>2];a=f+N(b,52)|0;H[g+232>>2]=H[a+48>>2];l=H[a+44>>2];H[g+224>>2]=H[a+40>>2];H[g+228>>2]=l;l=H[a+36>>2];H[g+216>>2]=H[a+32>>2];H[g+220>>2]=l;l=H[a+28>>2];H[g+208>>2]=H[a+24>>2];H[g+212>>2]=l;l=H[a+20>>2];H[g+200>>2]=H[a+16>>2];H[g+204>>2]=l;l=H[a+12>>2];H[g+192>>2]=H[a+8>>2];H[g+196>>2]=l;l=H[a+4>>2];H[g+184>>2]=H[a>>2];H[g+188>>2]=l;l=N(j,52);f=l+f|0;H[a+48>>2]=H[f+48>>2];k=H[f+44>>2];H[a+40>>2]=H[f+40>>2];H[a+44>>2]=k;k=H[f+36>>2];H[a+32>>2]=H[f+32>>2];H[a+36>>2]=k;k=H[f+28>>2];H[a+24>>2]=H[f+24>>2];H[a+28>>2]=k;k=H[f+20>>2];H[a+16>>2]=H[f+16>>2];H[a+20>>2]=k;k=H[f+12>>2];H[a+8>>2]=H[f+8>>2];H[a+12>>2]=k;k=H[f+4>>2];H[a>>2]=H[f>>2];H[a+4>>2]=k;a=l+H[n+24>>2]|0;H[a+48>>2]=H[g+232>>2];f=H[g+228>>2];H[a+40>>2]=H[g+224>>2];H[a+44>>2]=f;f=H[g+220>>2];H[a+32>>2]=H[g+216>>2];H[a+36>>2]=f;f=H[g+212>>2];H[a+24>>2]=H[g+208>>2];H[a+28>>2]=f;f=H[g+204>>2];H[a+16>>2]=H[g+200>>2];H[a+20>>2]=f;f=H[g+196>>2];H[a+8>>2]=H[g+192>>2];H[a+12>>2]=f;f=H[g+188>>2];H[a>>2]=H[g+184>>2];H[a+4>>2]=f;if(i>>>0<=e+1>>>0){break q}f=d;if(!(e-i&1)){a=c;f=N(d,6)+h|0;l=J[f>>1];r:{if((l|0)!=(b|0)){a=b;if((l|0)!=(j|0)){break r}}G[f>>1]=a}f=d+1|0}if((p|0)==(e&65535)){break q}while(1){a=c;l=N(f,6)+h|0;k=J[l>>1];s:{if((k|0)!=(b|0)){a=b;if((k|0)!=(j|0)){break s}}G[l>>1]=a}a=c;k=J[l+6>>1];t:{if((k|0)!=(b|0)){a=b;if((k|0)!=(j|0)){break t}}G[l+6>>1]=a}f=f+2|0;if((i|0)!=(f&65535)){continue}break}}G[(H[n+24>>2]+N(b,52)|0)+48>>1]=J[q+2>>1]}d=d+1|0;e=e+1|0;if((i|0)!=(e|0)){continue}break}e=H[o+116>>2];h=H[e>>2]}if(h){Ca(h);e=H[o+116>>2]}Ca(e);H[o+116>>2]=0;break a}r=0;Ba(m,1,9499,0)}na=g+240|0;return r|0}function Tc(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=O(0),p=0;k=na-48|0;na=k;H[a+8>>2]=1;a:{b:{d=k+40|0;c:{if((Ja(b,d,2,c)|0)!=2){break c}Da(d,k+44|0,2);if(H[k+44>>2]!=65359){break c}H[a+8>>2]=2;d=H[b+56>>2];e=d-2|0;d=H[b+60>>2]-(d>>>0<2)|0;g=H[a+224>>2];H[g>>2]=e;H[g+4>>2]=d;H[k+16>>2]=e;H[k+20>>2]=d;Ba(c,4,12769,k+16|0);f=H[a+224>>2];j=H[f>>2];e=H[f+24>>2];d=e+1|0;g=H[f+32>>2];if(d>>>0<=g>>>0){g=H[f+28>>2];break b}o=O(O(g>>>0)+O(100));if(o=O(0)){d=~~o>>>0}else{d=0}H[f+32>>2]=d;g=Ha(H[f+28>>2],N(d,24));if(g){H[f+28>>2]=g;e=H[f+24>>2];d=e+1|0;break b}Ca(H[f+28>>2]);H[f+32>>2]=0;H[f+24>>2]=0;H[f+28>>2]=0;Ba(c,1,3899,0)}Ba(c,1,15656,0);a=0;break a}e=N(e,24)+g|0;H[e+16>>2]=2;H[e+8>>2]=j;H[e+12>>2]=j>>31;G[e>>1]=65359;H[f+24>>2]=d;if((Ja(b,H[a+16>>2],2,c)|0)!=2){Ba(c,1,2472,0);a=0;break a}Da(H[a+16>>2],k+40|0,2);d:{e:{g=H[k+40>>2];if((g|0)!=65424){while(1){e=24912;if(g>>>0<=65279){H[k>>2]=g;Ba(c,1,2268,k);a=0;break a}while(1){d=e;f=H[d>>2];if(f){e=d+12|0;if((f|0)!=(g|0)){continue}}break}f:{g:{if(f){break g}h=2;Ba(c,2,3847,0);e=2472;h:{i:{if((Ja(b,H[a+16>>2],2,c)|0)!=2){break i}while(1){Da(H[a+16>>2],k+44|0,2);f=24912;g=H[k+44>>2];if(g>>>0>=65280){while(1){d=f;i=H[d>>2];if(i){f=d+12|0;if((g|0)!=(i|0)){continue}}break}if(!(H[d+4>>2]&H[a+8>>2])){e=5397;break i}if(i){if((i|0)==65424){H[k+40>>2]=65424;break f}j=H[b+56>>2];f=H[a+224>>2];g=H[f+24>>2];e=g+1|0;d=H[f+32>>2];if(e>>>0<=d>>>0){d=H[f+28>>2];break h}o=O(O(d>>>0)+O(100));if(o=O(0)){d=~~o>>>0}else{d=0}H[f+32>>2]=d;d=Ha(H[f+28>>2],N(d,24));if(d){H[f+28>>2]=d;g=H[f+24>>2];e=g+1|0;break h}Ca(H[f+28>>2]);H[f+32>>2]=0;H[f+24>>2]=0;H[f+28>>2]=0;e=3899;break i}h=h+2|0}if((Ja(b,H[a+16>>2],2,c)|0)==2){continue}break}}Ba(c,1,e,0);Ba(c,1,9847,0);a=0;break a}d=N(g,24)+d|0;H[d+16>>2]=h;g=j-h|0;H[d+8>>2]=g;H[d+12>>2]=g>>31;G[d>>1]=0;H[f+24>>2]=e;H[k+40>>2]=i;g=24912;if((i|0)==65424){break f}while(1){d=g;f=H[d>>2];if(!f){break g}g=d+12|0;if((f|0)!=(i|0)){continue}break}}if(!(H[d+4>>2]&H[a+8>>2])){Ba(c,1,5397,0);a=0;break a}if((Ja(b,H[a+16>>2],2,c)|0)!=2){Ba(c,1,2472,0);a=0;break a}Da(H[a+16>>2],k+36|0,2);e=H[k+36>>2];if(e>>>0<=1){Ba(c,1,6074,0);a=0;break a}e=e-2|0;H[k+36>>2]=e;g=H[a+16>>2];if(K[a+20>>2]>>0){g=Ha(g,e);if(!g){Ca(H[a+16>>2]);H[a+16>>2]=0;H[a+20>>2]=0;Ba(c,1,4973,0);a=0;break a}H[a+16>>2]=g;e=H[k+36>>2];H[a+20>>2]=e}e=Ja(b,g,e,c);if((e|0)!=H[k+36>>2]){Ba(c,1,2472,0);a=0;break a}if(!(ra[H[d+8>>2]](a,H[a+16>>2],e,c)|0)){Ba(c,1,2490,0);a=0;break a}j=H[b+56>>2];i=H[k+36>>2];d=H[a+224>>2];g=H[d+24>>2];h=g+1|0;e=H[d+32>>2];j:{if(h>>>0<=e>>>0){e=H[d+28>>2];break j}o=O(O(e>>>0)+O(100));if(o=O(0)){e=~~o>>>0}else{e=0}H[d+32>>2]=e;e=Ha(H[d+28>>2],N(e,24));if(!e){break d}H[d+28>>2]=e;g=H[d+24>>2];h=g+1|0}e=N(g,24)+e|0;H[e+16>>2]=i+4;g=(j-i|0)-4|0;H[e+8>>2]=g;H[e+12>>2]=g>>31;G[e>>1]=f;H[d+24>>2]=h;if((Ja(b,H[a+16>>2],2,c)|0)!=2){Ba(c,1,2472,0);a=0;break a}m=(f|0)==65372?1:m;l=(f|0)==65362?1:l;n=(f|0)==65361?1:n;Da(H[a+16>>2],k+40|0,2);g=H[k+40>>2];if((g|0)!=65424){continue}}break}if(n){break e}}Ba(c,1,4785,0);a=0;break a}if(!l){Ba(c,1,4831,0);a=0;break a}if(!m){Ba(c,1,4877,0);a=0;break a}d=0;e=0;h=0;j=na-16|0;na=j;m=1;k:{if(!(F[a+212|0]&1)){break k}l:{f=H[a+136>>2];if(!f){break l}m:{while(1){g=H[a+140>>2]+(h<<3)|0;l=H[g>>2];if(l){i=H[g+4>>2];g=d-i|0;g=d>>>0>=g>>>0?g:0;if(d>>>0>>0){f=i-d|0;l=d+l|0;while(1){if(f>>>0<4){d=5671;break m}Da(l,j+12|0,4);d=H[j+12>>2];if((d^-1)>>>0>>0){d=5645;break m}i=f-4|0;n=i>>>0>>0;g=n?d-i|0:g;e=d+e|0;f=i-d|0;l=((n?0:d)+l|0)+4|0;if(d>>>0>>0){continue}break}f=H[a+136>>2]}d=g}h=h+1|0;if(h>>>0>>0){continue}break}if(!d){break l}m=0;Ba(c,1,3067,0);break k}m=0;Ba(c,1,d,0);break k}d=Fa(e);H[a+160>>2]=d;if(!d){m=0;Ba(c,1,4337,0);break k}H[a+148>>2]=e;h=H[a+140>>2];n:{f=H[a+136>>2];if(f){e=0;d=0;g=0;while(1){l=g<<3;n=l+h|0;i=H[n>>2];if(i){h=H[a+160>>2]+d|0;f=H[n+4>>2];o:{if(f>>>0<=e>>>0){if(f){B(h,i,f)}d=d+f|0;e=e-f|0;break o}if(e){B(h,i,e)}d=d+e|0;h=f-e|0;e=e+i|0;while(1){if(h>>>0<4){break n}Da(e,j+8|0,4);e=e+4|0;i=H[a+160>>2]+d|0;f=h-4|0;h=H[j+8>>2];if(f>>>0>>0){if(f){B(i,e,f)}d=d+f|0;e=H[j+8>>2]-f|0;break o}if(h){B(i,e,h)}h=H[j+8>>2];d=h+d|0;e=e+h|0;h=f-h|0;if(h){continue}break}e=0}Ca(H[l+H[a+140>>2]>>2]);h=H[a+140>>2];f=l+h|0;H[f>>2]=0;H[f+4>>2]=0;f=H[a+136>>2]}g=g+1|0;if(g>>>0>>0){continue}break}e=H[a+148>>2];d=H[a+160>>2]}H[a+168>>2]=e;H[a+144>>2]=d;H[a+136>>2]=0;Ca(h);H[a+140>>2]=0;break k}m=0;Ba(c,1,5671,0)}na=j+16|0;if(!m){Ba(c,1,8085,0);a=0;break a}Ba(c,4,11754,0);d=H[a+224>>2];e=H[b+56>>2];e=e-2|0;H[d+8>>2]=e;H[d+12>>2]=0;b=0;h=0;i=na-16|0;na=i;g=H[a+68>>2];p:{if(!g){H[a+76>>2]=1;break p}if(H[a+76>>2]){break p}d=H[a+72>>2];j=H[a+224>>2];e=H[j+40>>2];q:{if((g|0)!=1){m=g&1;l=g&-2;while(1){n=(b<<3)+d|0;p=J[n>>1];f=e+N(p,40)|0;H[f>>2]=p;H[f+8>>2]=H[f+8>>2]+1;n=J[n+8>>1];f=e+N(n,40)|0;H[f>>2]=n;H[f+8>>2]=H[f+8>>2]+1;b=b+2|0;h=h+2|0;if((l|0)!=(h|0)){continue}break}if(!m){break q}}f=J[(b<<3)+d>>1];b=e+N(f,40)|0;H[b>>2]=f;H[b+8>>2]=H[b+8>>2]+1}f=H[j+36>>2];r:{if(f){b=0;while(1){if(!H[(e+N(b,40)|0)+8>>2]){H[i>>2]=b;Ba(c,1,9304,i);break r}b=b+1|0;if((f|0)!=(b|0)){continue}break}}f=H[j+8>>2];b=H[j+12>>2];e=0;while(1){s:{l=e<<3;m=H[H[a+224>>2]+40>>2]+N(J[l+d>>1],40)|0;h=H[m+16>>2];if(!h){h=Ea(H[m+8>>2],24);H[m+16>>2]=h;if(!h){break s}g=H[a+68>>2];d=H[a+72>>2]}p=h;h=H[m+4>>2];j=p+N(h,24)|0;H[j>>2]=f;H[j+4>>2]=b;l=H[(d+l|0)+4>>2];f=l+f|0;H[j+16>>2]=f;b=f>>>0>>0?b+1|0:b;H[j+20>>2]=b;H[m+4>>2]=h+1;e=e+1|0;if(g>>>0>e>>>0){continue}break p}break}Ba(c,1,6882,0)}H[a+76>>2]=1;if(!H[a+68>>2]){break p}d=H[H[a+224>>2]+40>>2];b=0;while(1){c=N(J[H[a+72>>2]+(b<<3)>>1],40);d=c+d|0;H[d+8>>2]=0;Ca(H[d+16>>2]);d=H[H[a+224>>2]+40>>2];H[(c+d|0)+16>>2]=0;b=b+1|0;if(b>>>0>2]){continue}break}}na=i+16|0;H[a+8>>2]=8;a=1;break a}Ca(H[d+28>>2]);H[d+32>>2]=0;H[d+24>>2]=0;H[d+28>>2]=0;Ba(c,1,3899,0);a=0}na=k+48|0;return a|0}function ge(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0;f=na-160|0;na=f;a:{if(c>>>0<=35){c=0;Ba(d,1,6095,0);break a}j=H[a+96>>2];c=c-36|0;h=(c>>>0)/3|0;if((N(h,3)|0)!=(c|0)){c=0;Ba(d,1,6095,0);break a}c=f+156|0;Da(b,c,2);G[a+104>>1]=H[f+156>>2];Da(b+2|0,j+8|0,4);Da(b+6|0,j+12|0,4);Da(b+10|0,j,4);Da(b+14|0,j+4|0,4);Da(b+18|0,a+116|0,4);Da(b+22|0,a+120|0,4);Da(b+26|0,a+108|0,4);Da(b+30|0,a+112|0,4);Da(b+34|0,c,2);b:{c:{d:{c=H[f+156>>2];if(c>>>0<=16384){H[j+16>>2]=c;if((c|0)!=(h|0)){H[f+132>>2]=h;H[f+128>>2]=c;Ba(d,1,14980,f+128|0);c=0;break a}c=H[j+4>>2];g=H[j+12>>2];l=H[j+8>>2];e=H[j>>2];if(!(c>>>0>>0&l>>>0>e>>>0)){H[f+120>>2]=g-c;H[f+124>>2]=0-(c>>>0>g>>>0);H[f+112>>2]=l-e;H[f+116>>2]=0-(e>>>0>l>>>0);Ba(d,1,14542,f+112|0);c=0;break a}i=H[a+116>>2];k=H[a+120>>2];if(!(k?i:0)){H[f+4>>2]=k;H[f>>2]=i;Ba(d,1,15094,f);c=0;break a}e:{n=H[a+108>>2];f:{if(n>>>0>e>>>0){break f}i=i+n|0;if(e>>>0>=(i>>>0>>0?-1:i)>>>0){break f}i=H[a+112>>2];if(i>>>0>c>>>0){break f}k=i+k|0;if(c>>>0<(i>>>0>k>>>0?-1:k)>>>0){break e}}c=0;Ba(d,1,2792,0);break a}g:{if(H[a+248>>2]){break g}i=H[a+240>>2];if(!i){break g}k=H[a+244>>2];if(!k){break g}e=l-e|0;c=g-c|0;if((e|0)==(i|0)&(c|0)==(k|0)){break g}H[f+108>>2]=c;H[f+104>>2]=e;H[f+100>>2]=k;H[f+96>>2]=i;Ba(d,1,14006,f+96|0);c=0;break a}e=Ea(h,52);H[j+24>>2]=e;if(!e){break d}h:{if(!H[j+16>>2]){break h}c=f+152|0;Da(b+36|0,c,1);h=H[f+152>>2];k=h>>>7|0;H[e+32>>2]=k;n=(h&127)+1|0;H[e+24>>2]=n;l=H[a+248>>2];Da(b+37|0,c,1);H[e>>2]=H[f+152>>2];Da(b+38|0,c,1);g=H[f+152>>2];H[e+4>>2]=g;c=0;i=H[e>>2];if(i-256>>>0<4294967041){h=0;break b}h=0;if(g-256>>>0<4294967041){break b}g=H[e+24>>2];if(g>>>0>31){break c}H[e+36>>2]=0;H[e+40>>2]=H[a+184>>2];h=1;if(K[j+16>>2]<=1){break h}k=l?0:k;l=l?0:n;b=b+39|0;while(1){Da(b,f+152|0,1);i=H[f+152>>2];g=i>>>7|0;H[e+84>>2]=g;i=(i&127)+1|0;H[e+76>>2]=i;if(!(H[a+248>>2]|(I[a+212|0]&4|(i|0)==(l|0)&(g|0)==(k|0)))){H[f+84>>2]=g;H[f+80>>2]=i;H[f+76>>2]=h;H[f+72>>2]=k;H[f+68>>2]=l;H[f+64>>2]=h;Ba(d,2,14778,f- -64|0)}g=f+152|0;Da(b+1|0,g,1);H[e+52>>2]=H[f+152>>2];Da(b+2|0,g,1);g=H[f+152>>2];H[e+56>>2]=g;i=H[e+52>>2];if(i-256>>>0<4294967041|g-256>>>0<=4294967040){break b}g=H[e+76>>2];if(g>>>0>=32){break c}b=b+3|0;H[e+88>>2]=0;H[e+92>>2]=H[a+184>>2];e=e+52|0;h=h+1|0;if(h>>>0>2]){continue}break}}c=0;h=H[a+116>>2];if(!h){break a}g=H[a+120>>2];if(!g){break a}l=0-!h|0;e=l;p=H[a+108>>2];k=H[j+8>>2]-p|0;i=h-1|0;b=k+i|0;e=k>>>0>b>>>0?e+1|0:e;b=ve(b,e,h,0);H[a+128>>2]=b;n=0-!g|0;e=n;q=H[a+112>>2];o=H[j+12>>2]-q|0;m=o;k=g-1|0;o=o+k|0;e=m>>>0>o>>>0?e+1|0:e;e=ve(o,e,g,0);H[a+132>>2]=e;i:{if(!(!b|!e)){if(b>>>0<=65535/(e>>>0)>>>0){break i}}H[f+20>>2]=e;H[f+16>>2]=b;Ba(d,1,14120,f+16|0);break a}o=N(b,e);j:{if(I[a+92|0]&2){H[a+28>>2]=(H[a+28>>2]-p>>>0)/(h>>>0);H[a+32>>2]=(H[a+32>>2]-q>>>0)/(g>>>0);e=l;b=H[a+36>>2]-p|0;m=b;b=b+i|0;e=m>>>0>b>>>0?e+1|0:e;v=a,w=ve(b,e,h,0),H[v+36>>2]=w;e=n;b=H[a+40>>2]-q|0;m=b;b=b+k|0;e=m>>>0>b>>>0?e+1|0:e;v=a,w=ve(b,e,g,0),H[v+40>>2]=w;break j}H[a+40>>2]=e;H[a+36>>2]=b;H[a+28>>2]=0;H[a+32>>2]=0}b=Ea(o,5644);H[a+180>>2]=b;if(!b){Ba(d,1,3935,0);break a}b=Ea(H[j+16>>2],1080);H[H[a+12>>2]+5584>>2]=b;if(!H[H[a+12>>2]+5584>>2]){Ba(d,1,3935,0);break a}b=Ea(10,20);H[H[a+12>>2]+5616>>2]=b;b=H[a+12>>2];if(!H[b+5616>>2]){Ba(d,1,3935,0);break a}H[b+5624>>2]=10;b=Ea(10,20);H[H[a+12>>2]+5628>>2]=b;b=H[a+12>>2];if(!H[b+5628>>2]){Ba(d,1,3935,0);break a}H[b+5636>>2]=10;e=H[j+16>>2];k:{if(!e){break k}h=H[j+24>>2];b=0;if((e|0)!=1){g=e&1;l=e&-2;e=0;while(1){i=h+N(b,52)|0;if(!H[i+32>>2]){H[(H[H[a+12>>2]+5584>>2]+N(b,1080)|0)+1076>>2]=1<>2]-1}i=b|1;k=h+N(i,52)|0;if(!H[k+32>>2]){H[(H[H[a+12>>2]+5584>>2]+N(i,1080)|0)+1076>>2]=1<>2]-1}b=b+2|0;e=e+2|0;if((l|0)!=(e|0)){continue}break}if(!g){break k}}e=h+N(b,52)|0;if(H[e+32>>2]){break k}H[(H[H[a+12>>2]+5584>>2]+N(b,1080)|0)+1076>>2]=1<>2]-1}if(o){b=H[a+180>>2];e=0;while(1){h=Ea(H[j+16>>2],1080);H[b+5584>>2]=h;if(!h){Ba(d,1,3935,0);break a}b=b+5644|0;e=e+1|0;if(o>>>0>e>>>0){continue}break}}b=N(H[a+132>>2],H[a+128>>2]);H[H[a+224>>2]+36>>2]=b;b=Ea(b,40);d=H[a+224>>2];H[d+40>>2]=b;e=0;l:{if(!b){break l}e=1;if(!H[d+36>>2]){break l}d=0;while(1){m:{e=0;g=N(d,40);b=g+b|0;H[b+20>>2]=0;H[b+28>>2]=100;h=Ea(100,24);l=H[a+224>>2];b=H[l+40>>2];H[(g+b|0)+24>>2]=h;if(!h){break m}e=1;d=d+1|0;if(d>>>0>2]){continue}}break}}if(!e){break a}H[a+8>>2]=4;r=H[j+16>>2];if(r){b=H[a+112>>2];d=H[a+120>>2];c=b+N(d,H[a+132>>2]-1|0)|0;d=c+d|0;c=c>>>0>d>>>0?-1:d;d=H[j+12>>2];c=c>>>0>>0?c:d;l=c-1|0;k=0-!c|0;c=H[a+108>>2];d=H[a+116>>2];a=c+N(d,H[a+128>>2]-1|0)|0;d=a+d|0;a=a>>>0>d>>>0?-1:d;d=H[j+8>>2];a=a>>>0>>0?a:d;i=a-1|0;n=0-!a|0;a=H[j+4>>2];b=a>>>0>>0?b:a;o=b-1|0;p=0-!b|0;a=H[j>>2];b=a>>>0>>0?c:a;q=b-1|0;u=0-!b|0;a=H[j+24>>2];b=0;while(1){e=p;d=H[a+4>>2];c=d+o|0;j=ve(c,c>>>0>>0?e+1|0:e,d,0);H[a+20>>2]=j;e=u;h=H[a>>2];c=h+q|0;s=ve(c,c>>>0>>0?e+1|0:e,h,0);H[a+16>>2]=s;c=H[a+40>>2];g=c&31;if((c&63)>>>0>=32){e=-1<>>32-g}g=m^-1;e=e^-1;m=e;e=k;t=d+l|0;e=t>>>0>>0?e+1|0:e;e=ve(t,e,d,0)-j|0;d=m;j=e;e=e+g|0;d=j>>>0>e>>>0?d+1|0:d;j=e;e=c&31;if((c&63)>>>0>=32){d=d>>>e|0}else{d=((1<>>e}H[a+12>>2]=d;e=n;d=h+i|0;e=d>>>0>>0?e+1|0:e;d=ve(d,e,h,0)-s|0;e=m;d=d+g|0;e=d>>>0>>0?e+1|0:e;h=d;d=c&31;if((c&63)>>>0>=32){c=e>>>d|0}else{c=((1<>>d}H[a+8>>2]=c;a=a+52|0;b=b+1|0;if((r|0)!=(b|0)){continue}break}}c=1;break a}H[f+144>>2]=c;Ba(d,1,7932,f+144|0);c=0;break a}c=0;H[j+16>>2]=0;Ba(d,1,3935,0);break a}H[f+52>>2]=g;H[f+48>>2]=h;Ba(d,1,15402,f+48|0);break a}H[f+40>>2]=g;H[f+36>>2]=i;H[f+32>>2]=h;Ba(d,1,14340,f+32|0)}na=f+160|0;return c|0}function fd(a,b,c,d,e,f){a=a|0;b=+b;c=c|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,t=0,u=0,v=0,w=0,y=0,z=0,A=0,B=0,C=0;n=na-560|0;na=n;H[n+44>>2]=0;x(+b);i=s(1)|0;s(0)|0;a:{if((i|0)<0){r=1;y=1082;b=-b;x(+b);i=s(1)|0;s(0)|0;break a}if(e&2048){r=1;y=1085;break a}r=e&1;y=r?1088:1083;A=!r}b:{if((i&2146435072)==2146435072){d=r+3|0;Na(a,32,c,d,e&-65537);La(a,y,r);f=f&32;La(a,b!=b?f?1207:1435:f?1312:1476,3);Na(a,32,c,d,e^8192);l=(c|0)>(d|0)?c:d;break b}v=n+16|0;c:{d:{e:{b=pc(b,n+44|0);b=b+b;if(b!=0){g=H[n+44>>2];H[n+44>>2]=g-1;t=f|32;if((t|0)!=97){break e}break c}t=f|32;if((t|0)==97){break c}m=H[n+44>>2];break d}m=g-29|0;H[n+44>>2]=m;b=b*268435456}k=(d|0)<0?6:d;p=(n+48|0)+((m|0)>=0?288:0)|0;h=p;while(1){d=b<4294967295&b>=0?~~b>>>0:0;H[h>>2]=d;h=h+4|0;b=(b-+(d>>>0))*1e9;if(b!=0){continue}break}f:{if((m|0)<=0){i=m;g=h;j=p;break f}j=p;i=m;while(1){z=i>>>0>=29?29:i;g=h-4|0;g:{if(j>>>0>g>>>0){break g}d=0;i=0;while(1){w=H[g>>2];o=z&31;if((z&63)>>>0>=32){l=w<>>32-o;w=w<>>0>o>>>0?i+1|0:i;d=ve(o,l,1e9,0);i=qa;B=g,C=o+re(d,i,-1e9)|0,H[B>>2]=C;g=g-4|0;if(j>>>0<=g>>>0){continue}break}if(!l&o>>>0<1e9){break g}j=j-4|0;H[j>>2]=d}while(1){g=h;if(j>>>0>>0){h=g-4|0;if(!H[h>>2]){continue}}break}i=H[n+44>>2]-z|0;H[n+44>>2]=i;h=g;if((i|0)>0){continue}break}}if((i|0)<0){u=((k+25>>>0)/9|0)+1|0;l=(t|0)==102;while(1){d=0-i|0;d=d>>>0>=9?9:d;h:{if(g>>>0<=j>>>0){h=H[j>>2]?0:4;break h}o=1e9>>>d|0;z=-1<>2];H[h>>2]=w+(i>>>d|0);i=N(o,i&z);h=h+4|0;if(h>>>0>>0){continue}break}h=H[j>>2]?0:4;if(!i){break h}H[g>>2]=i;g=g+4|0}i=d+H[n+44>>2]|0;H[n+44>>2]=i;j=h+j|0;d=l?p:j;g=g-d>>2>(u|0)?d+(u<<2)|0:g;if((i|0)<0){continue}break}}i=0;i:{if(g>>>0<=j>>>0){break i}i=N(p-j>>2,9);h=10;d=H[j>>2];if(d>>>0<10){break i}while(1){i=i+1|0;h=N(h,10);if(d>>>0>=h>>>0){continue}break}}d=(k-((t|0)!=102?i:0)|0)-((t|0)==103&(k|0)!=0)|0;if((d|0)<(N(g-p>>2,9)-9|0)){h=(n+48|0)+((m|0)<0?-4092:-3804)|0;d=d+9216|0;m=(d|0)/9|0;l=h+(m<<2)|0;h=10;d=d+N(m,-9)|0;if((d|0)<=7){while(1){h=N(h,10);d=d+1|0;if((d|0)!=8){continue}break}}m=H[l>>2];u=(m>>>0)/(h>>>0)|0;d=N(u,h);o=l+4|0;j:{if((d|0)==(m|0)&(o|0)==(g|0)){break j}m=m-d|0;k:{if(!(u&1)){b=9007199254740992;if(!(F[l-4|0]&1)|((h|0)!=1e9|j>>>0>=l>>>0)){break k}}b=9007199254740994}q=(g|0)==(o|0)?1:1.5;o=h>>>1|0;q=m>>>0>>0?.5:(o|0)==(m|0)?q:1.5;if(!(I[y|0]!=45|A)){q=-q;b=-b}H[l>>2]=d;if(b+q==b){break j}d=d+h|0;H[l>>2]=d;if(d>>>0>=1e9){while(1){H[l>>2]=0;l=l-4|0;if(l>>>0>>0){j=j-4|0;H[j>>2]=0}d=H[l>>2]+1|0;H[l>>2]=d;if(d>>>0>999999999){continue}break}}i=N(p-j>>2,9);h=10;d=H[j>>2];if(d>>>0<10){break j}while(1){i=i+1|0;h=N(h,10);if(d>>>0>=h>>>0){continue}break}}d=l+4|0;g=d>>>0>>0?d:g}while(1){h=g;m=g>>>0<=j>>>0;if(!m){g=g-4|0;if(!H[g>>2]){continue}}break}l:{if((t|0)!=103){d=e&8;break l}d=k?k:1;g=(d|0)>(i|0)&(i|0)>-5;k=(g?i^-1:-1)+d|0;f=(g?-1:-2)+f|0;d=e&8;if(d){break l}g=-9;m:{if(m){break m}l=H[h-4>>2];if(!l){break m}d=10;g=0;if((l>>>0)%10|0){break m}while(1){m=g;g=g+1|0;d=N(d,10);if(!((l>>>0)%(d>>>0)|0)){continue}break}g=m^-1}m=N(h-p>>2,9);if((f&-33)==70){d=0;g=(g+m|0)-9|0;g=(g|0)>0?g:0;k=(g|0)>(k|0)?k:g;break l}d=0;g=((i+m|0)+g|0)-9|0;g=(g|0)>0?g:0;k=(g|0)>(k|0)?k:g}l=-1;t=d|k;if(((t?2147483645:2147483646)|0)<(k|0)){break b}m=(((t|0)!=0)+k|0)+1|0;o=f&-33;n:{if((o|0)==70){if((m^2147483647)<(i|0)){break b}g=(i|0)>0?i:0;break n}g=i>>31;g=$a((g^i)-g|0,0,v);if((v-g|0)<=1){while(1){g=g-1|0;F[g|0]=48;if((v-g|0)<2){continue}break}}u=g-2|0;F[u|0]=f;F[g-1|0]=(i|0)<0?45:43;g=v-u|0;if((g|0)>(m^2147483647)){break b}}f=g+m|0;if((f|0)>(r^2147483647)){break b}f=f+r|0;Na(a,32,c,f,e);La(a,y,r);Na(a,48,c,f,e^65536);o:{p:{q:{if((o|0)==70){d=n+16|9;i=j>>>0>p>>>0?p:j;j=i;while(1){g=$a(H[j>>2],0,d);r:{if((i|0)!=(j|0)){if(n+16>>>0>=g>>>0){break r}while(1){g=g-1|0;F[g|0]=48;if(n+16>>>0>>0){continue}break}break r}if((d|0)!=(g|0)){break r}g=g-1|0;F[g|0]=48}La(a,g,d-g|0);j=j+4|0;if(p>>>0>=j>>>0){continue}break}if(t){La(a,1684,1)}if((k|0)<=0|h>>>0<=j>>>0){break q}while(1){g=$a(H[j>>2],0,d);if(g>>>0>n+16>>>0){while(1){g=g-1|0;F[g|0]=48;if(n+16>>>0>>0){continue}break}}La(a,g,(k|0)>=9?9:k);g=k-9|0;j=j+4|0;if(h>>>0<=j>>>0){break p}i=(k|0)>9;k=g;if(i){continue}break}break p}s:{if((k|0)<0){break s}p=h>>>0>j>>>0?h:j+4|0;i=n+16|9;h=j;while(1){g=$a(H[h>>2],0,i);if((i|0)==(g|0)){g=g-1|0;F[g|0]=48}t:{if((h|0)!=(j|0)){if(n+16>>>0>=g>>>0){break t}while(1){g=g-1|0;F[g|0]=48;if(n+16>>>0>>0){continue}break}break t}La(a,g,1);g=g+1|0;if(!(d|k)){break t}La(a,1684,1)}m=g;g=i-g|0;La(a,m,(g|0)<(k|0)?g:k);k=k-g|0;h=h+4|0;if(p>>>0<=h>>>0){break s}if((k|0)>=0){continue}break}}Na(a,48,k+18|0,18,0);La(a,u,v-u|0);break o}g=k}Na(a,48,g+9|0,9,0)}Na(a,32,c,f,e^8192);l=(c|0)>(f|0)?c:f;break b}j=(f<<26>>31&9)+y|0;u:{if(d>>>0>11){break u}g=12-d|0;q=16;while(1){q=q*16;g=g-1|0;if(g){continue}break}if(I[j|0]==45){b=-(q+(-b-q));break u}b=b+q-q}h=H[n+44>>2];g=h>>31;g=$a((g^h)-g|0,0,v);if((v|0)==(g|0)){g=g-1|0;F[g|0]=48;h=H[n+44>>2]}k=f&32;i=g-2|0;F[i|0]=f+15;F[g-1|0]=(h|0)<0?45:43;g=!(e&8)&(d|0)<=0;h=n+16|0;while(1){f=h;p=P(b)<2147483647?~~b:-2147483648;F[h|0]=k|I[p+26048|0];b=(b-+(p|0))*16;h=h+1|0;if(!(g&b==0|(h-(n+16|0)|0)!=1)){F[f+1|0]=46;h=f+2|0}if(b!=0){continue}break}l=-1;g=v-i|0;if((2147483643-(g+r|0)|0)<(d|0)){break b}f=h;h=n+16|0;f=f-h|0;k=d?(f-2|0)<(d|0)?d+2|0:f:f;p=r|2;d=k+(p+g|0)|0;Na(a,32,c,d,e);La(a,j,p);Na(a,48,c,d,e^65536);La(a,h,f);Na(a,48,k-f|0,0,0);La(a,i,g);Na(a,32,c,d,e^8192);l=(c|0)>(d|0)?c:d}na=n+560|0;return l|0}function sc(a,b,c,d,e,f,g){var h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0;j=na+-64|0;na=j;H[j+60>>2]=b;z=j+41|0;A=j+39|0;t=j+40|0;a:{b:{c:{d:{e:while(1){h=0;f:while(1){k=b;if((o^2147483647)<(h|0)){break d}o=h+o|0;g:{h:{i:{j:{h=b;i=I[h|0];if(i){while(1){k:{b=i&255;l:{if(!b){b=h;break l}if((b|0)!=37){break k}i=h;while(1){if(I[i+1|0]!=37){b=i;break l}h=h+1|0;n=I[i+2|0];b=i+2|0;i=b;if((n|0)==37){continue}break}}h=h-k|0;y=o^2147483647;if((h|0)>(y|0)){break d}if(a){La(a,k,h)}if(h){continue f}H[j+60>>2]=b;h=b+1|0;q=-1;i=F[b+1|0]-48|0;if(!(I[b+2|0]!=36|i>>>0>9)){w=1;q=i;h=b+3|0}H[j+60>>2]=h;l=0;i=F[h|0];b=i-32|0;m:{if(b>>>0>31){n=h;break m}n=h;b=1<>2]=n;l=b|l;i=F[h+1|0];b=i-32|0;if(b>>>0>=32){break m}h=n;b=1<>>0>9)){p:{if(!a){H[(b<<2)+e>>2]=10;b=0;break p}b=H[(b<<3)+d>>2]}p=b;b=n+3|0;i=1;break o}if(w){break j}b=n+1|0;if(!a){H[j+60>>2]=b;w=0;p=0;break n}h=H[c>>2];H[c>>2]=h+4;p=H[h>>2];i=0}w=i;H[j+60>>2]=b;if((p|0)>=0){break n}p=0-p|0;l=l|8192;break n}p=rc(j+60|0);if((p|0)<0){break d}b=H[j+60>>2]}h=0;m=-1;u=0;q:{if(I[b|0]!=46){break q}if(I[b+1|0]==42){i=F[b+2|0]-48|0;r:{if(!(I[b+3|0]!=36|i>>>0>9)){b=b+4|0;s:{if(!a){H[(i<<2)+e>>2]=10;m=0;break s}m=H[(i<<3)+d>>2]}break r}if(w){break j}b=b+2|0;m=0;if(!a){break r}i=H[c>>2];H[c>>2]=i+4;m=H[i>>2]}H[j+60>>2]=b;u=(m|0)>=0;break q}H[j+60>>2]=b+1;m=rc(j+60|0);b=H[j+60>>2];u=1}while(1){x=h;n=28;r=b;i=F[b|0];if(i-123>>>0<4294967238){break c}b=b+1|0;h=I[(i+N(h,58)|0)+25519|0];if((h-1&255)>>>0<8){continue}break}H[j+60>>2]=b;t:{if((h|0)!=27){if(!h){break c}if((q|0)>=0){if(!a){H[(q<<2)+e>>2]=h;continue e}h=(q<<3)+d|0;i=H[h+4>>2];H[j+48>>2]=H[h>>2];H[j+52>>2]=i;break t}if(!a){break g}qc(j+48|0,h,c,g);break t}if((q|0)>=0){break c}h=0;if(!a){continue f}}if(I[a|0]&32){break b}i=l&-65537;l=l&8192?i:l;q=0;v=1072;n=t;u:{v:{w:{x:{y:{z:{A:{B:{C:{D:{E:{F:{G:{H:{I:{J:{K:{r=I[r|0];h=r<<24>>24;h=x?(r&15)==3?h&-45:h:h;switch(h-88|0){case 0:case 32:break G;case 1:case 2:case 3:case 4:case 5:case 6:case 7:case 8:case 10:case 16:case 18:case 19:case 20:case 21:case 25:case 26:case 28:case 30:case 31:break h;case 9:case 13:case 14:case 15:break u;case 11:break B;case 12:case 17:break E;case 22:break I;case 23:break F;case 24:break H;case 27:break A;case 29:break J;default:break K}}L:{switch(h-65|0){case 1:case 3:break h;case 0:case 4:case 5:case 6:break u;case 2:break z;default:break L}}if((h|0)==83){break y}break h}i=H[j+48>>2];r=H[j+52>>2];v=1072;break D}h=0;M:{switch(x|0){case 0:H[H[j+48>>2]>>2]=o;continue f;case 1:H[H[j+48>>2]>>2]=o;continue f;case 2:k=H[j+48>>2];H[k>>2]=o;H[k+4>>2]=o>>31;continue f;case 3:G[H[j+48>>2]>>1]=o;continue f;case 4:F[H[j+48>>2]]=o;continue f;case 6:H[H[j+48>>2]>>2]=o;continue f;case 7:break M;default:continue f}}k=H[j+48>>2];H[k>>2]=o;H[k+4>>2]=o>>31;continue f}m=m>>>0<=8?8:m;l=l|8;h=120}b=t;k=H[j+52>>2];r=k;i=H[j+48>>2];s=i;if(i|k){x=h&32;while(1){b=b-1|0;F[b|0]=x|I[(s&15)+26048|0];s=(k&15)<<28|s>>>4;k=k>>>4|0;if(s|k){continue}break}}k=b;if(!(l&8)|!(i|r)){break C}v=(h>>>4|0)+1072|0;q=2;break C}b=t;k=H[j+52>>2];r=k;i=H[j+48>>2];s=i;if(i|k){while(1){b=b-1|0;F[b|0]=s&7|48;s=(k&7)<<29|s>>>3;k=k>>>3|0;if(s|k){continue}break}}k=b;if(!(l&8)){break C}b=z-b|0;m=(b|0)<(m|0)?m:b;break C}i=H[j+48>>2];b=H[j+52>>2];r=b;if((b|0)<0){h=0-(b+((i|0)!=0)|0)|0;r=h;i=0-i|0;H[j+48>>2]=i;H[j+52>>2]=h;q=1;v=1072;break D}if(l&2048){q=1;v=1073;break D}q=l&1;v=q?1074:1072}k=$a(i,r,t)}if((m|0)<0&u){break d}l=u?l&-65537:l;if(!((i|r)!=0|m)){k=t;m=0;break h}b=!(i|r)+(t-k|0)|0;m=(b|0)<(m|0)?m:b;break h}h=I[j+48|0];break i}h=m>>>0>=2147483647?2147483647:m;l=h;n=(h|0)!=0;b=H[j+48>>2];k=b?b:1686;b=k;N:{O:{P:{Q:{if(!(b&3)|!h){break Q}while(1){if(!I[b|0]){break P}l=l-1|0;n=(l|0)!=0;b=b+1|0;if(!(b&3)){break Q}if(l){continue}break}}if(!n){break O}if(!(!I[b|0]|l>>>0<4)){while(1){n=H[b>>2];if(((16843008-n|n)&-2139062144)!=-2139062144){break P}b=b+4|0;l=l-4|0;if(l>>>0>3){continue}break}}if(!l){break O}}while(1){if(!I[b|0]){break N}b=b+1|0;l=l-1|0;if(l){continue}break}}b=0}b=b?b-k|0:h;n=b+k|0;if((m|0)>=0){l=i;m=b;break h}l=i;m=b;if(I[n|0]){break d}break h}h=H[j+48>>2];if(h|H[j+52>>2]){break x}h=0;break i}if(m){i=H[j+48>>2];break w}h=0;Na(a,32,p,0,l);break v}H[j+12>>2]=0;H[j+8>>2]=h;i=j+8|0;H[j+48>>2]=i;m=-1}h=0;while(1){R:{k=H[i>>2];if(!k){break R}k=oc(j+4|0,k);if((k|0)<0){break b}if(k>>>0>m-h>>>0){break R}i=i+4|0;h=h+k|0;if(m>>>0>h>>>0){continue}}break}n=61;if((h|0)<0){break c}Na(a,32,p,h,l);if(!h){h=0;break v}n=0;i=H[j+48>>2];while(1){k=H[i>>2];if(!k){break v}m=j+4|0;k=oc(m,k);n=k+n|0;if(n>>>0>h>>>0){break v}La(a,m,k);i=i+4|0;if(h>>>0>n>>>0){continue}break}}Na(a,32,p,h,l^8192);h=(h|0)<(p|0)?p:h;continue f}if((m|0)<0&u){break d}n=61;h=ra[f|0](a,M[j+48>>3],p,m,l,h)|0;if((h|0)>=0){continue f}break c}i=I[h+1|0];h=h+1|0;continue}}if(a){break a}if(!w){break g}h=1;while(1){a=H[(h<<2)+e>>2];if(a){qc((h<<3)+d|0,a,c,g);o=1;h=h+1|0;if((h|0)!=10){continue}break a}break}if(h>>>0>=10){o=1;break a}while(1){if(H[(h<<2)+e>>2]){break j}o=1;h=h+1|0;if((h|0)!=10){continue}break}break a}n=28;break c}F[j+39|0]=h;m=1;k=A;l=i}i=n-k|0;m=(i|0)<(m|0)?m:i;if((m|0)>(q^2147483647)){break d}n=61;b=m+q|0;h=(b|0)<(p|0)?p:b;if(y>>>0>>0){break c}Na(a,32,h,b,l);La(a,v,q);Na(a,48,h,b,l^65536);Na(a,48,m,i,0);La(a,k,i);Na(a,32,h,b,l^8192);b=H[j+60>>2];continue}break}break}o=0;break a}n=61}H[6597]=n}o=-1}na=j- -64|0;return o}function Pc(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0;i=na-80|0;na=i;H[i+76>>2]=1;a:{b:{if(H[a+128>>2]!=1|H[a+132>>2]!=1|(H[a+108>>2]|H[a+112>>2])){break b}j=H[a+100>>2];if(H[j>>2]|H[j+4>>2]|(H[j+8>>2]!=H[a+116>>2]|H[j+12>>2]!=H[a+120>>2])){break b}if(!cb(a,i+72|0,0,i+68|0,i- -64|0,i+60|0,i+56|0,i+52|0,i+76|0,b,c)){break a}c:{d:{if(!H[i+76>>2]){break d}if(!ib(a,H[i+72>>2],0,0,b,c)){break d}b=H[a+100>>2];if(H[b+16>>2]){break c}d=1;break a}Ba(c,1,8776,0);break a}e=H[b+24>>2];while(1){b=N(g,52);Ca(H[(b+e|0)+44>>2]);c=H[a+100>>2];e=H[c+24>>2];j=b+e|0;m=H[a+232>>2];d=H[H[H[m+20>>2]>>2]+20>>2]+N(g,76)|0;H[j+44>>2]=H[d+36>>2];H[j+36>>2]=H[(b+H[H[m+24>>2]+24>>2]|0)+36>>2];H[d+36>>2]=0;d=1;g=g+1|0;if(g>>>0>2]){continue}break}break a}H[a+80>>2]=0;H[a+84>>2]=0;Ca(H[a+88>>2]);H[a+88>>2]=0;e:{if(!(H[a+28>>2]|H[a+32>>2]|H[a+36>>2]!=H[a+128>>2])){j=2;if(H[a+40>>2]==H[a+132>>2]){break e}}j=2;if(H[a+76>>2]){break e}if(!Ab(b)){break e}q=H[a+128>>2];j=N(q,H[a+132>>2]);if(j){h=H[H[a+224>>2]+40>>2];f:{g:{if((j|0)==1){j=0;break g}k=j&1;n=j&-2;j=0;while(1){d=h+N(g,40)|0;f=H[d+4>>2];if(f){l=(H[d+16>>2]+N(f,24)|0)-8|0;f=H[l>>2];o=f;p=f>>>0>j>>>0;f=H[l+4>>2];l=p&(f|0)>=(m|0)|(f|0)>(m|0);j=l?o:j;m=l?f:m}f=H[d+44>>2];if(f){f=(H[d+56>>2]+N(f,24)|0)-8|0;d=H[f>>2];o=d;l=d>>>0>j>>>0;d=H[f+4>>2];f=l&(d|0)>=(m|0)|(d|0)>(m|0);j=f?o:j;m=f?d:m}g=g+2|0;e=e+2|0;if((n|0)!=(e|0)){continue}break}if(!k){break f}}d=h+N(g,40)|0;h=H[d+4>>2];if(!h){break f}h=(H[d+16>>2]+N(h,24)|0)-8|0;d=H[h>>2];f=d;k=d>>>0>j>>>0;d=H[h+4>>2];h=k&(d|0)>=(m|0)|(d|0)>(m|0);j=h?f:j;m=h?d:m}j=j+2|0;m=j>>>0<2?m+1|0:m}else{j=2;m=0}g=0;k=H[a+32>>2];p=H[a+40>>2];h:{if(k>>>0>=p>>>0){break h}h=H[a+28>>2];f=H[a+36>>2];if(h>>>0>=f>>>0){break h}n=f-h&3;s=H[H[a+224>>2]+40>>2];t=h-f>>>0>4294967292;while(1){l=s+N(N(k,q),40)|0;d=h;e=0;if(n){while(1){g=H[(l+N(d,40)|0)+4>>2]+g|0;d=d+1|0;e=e+1|0;if((n|0)!=(e|0)){continue}break}}if(!t){while(1){e=l+N(d,40)|0;g=H[e+124>>2]+(H[e+84>>2]+(H[e+44>>2]+(H[e+4>>2]+g|0)|0)|0)|0;d=d+4|0;if((f|0)!=(d|0)){continue}break}}k=k+1|0;if((p|0)!=(k|0)){continue}break}}f=Fa(g<<3);H[a+88>>2]=f;if(!g|!f){break e}g=0;d=H[a+40>>2];n=H[a+32>>2];i:{if(d>>>0<=n>>>0){break i}e=H[a+36>>2];if(e>>>0<=K[a+28>>2]){break i}while(1){f=H[a+28>>2];if(e>>>0>f>>>0){s=H[H[a+224>>2]+40>>2]+N(N(H[a+128>>2],n),40)|0;while(1){h=s+N(f,40)|0;d=H[h+4>>2];if(d){q=d&3;p=H[h+16>>2];h=0;j:{k:{if(d>>>0<4){d=0;break k}t=d&-4;d=0;k=0;while(1){e=p+N(d,24)|0;r=H[e+4>>2];l=g<<3;o=l+H[a+88>>2]|0;H[o>>2]=H[e>>2];H[o+4>>2]=r;r=H[e+28>>2];o=l+H[a+88>>2]|0;H[o+8>>2]=H[e+24>>2];H[o+12>>2]=r;r=H[e+52>>2];o=l+H[a+88>>2]|0;H[o+16>>2]=H[e+48>>2];H[o+20>>2]=r;o=H[e+76>>2];l=l+H[a+88>>2]|0;H[l+24>>2]=H[e+72>>2];H[l+28>>2]=o;d=d+4|0;g=g+4|0;k=k+4|0;if((t|0)!=(k|0)){continue}break}if(!q){break j}}while(1){k=p+N(d,24)|0;l=H[k+4>>2];e=H[a+88>>2]+(g<<3)|0;H[e>>2]=H[k>>2];H[e+4>>2]=l;d=d+1|0;g=g+1|0;h=h+1|0;if((q|0)!=(h|0)){continue}break}}e=H[a+36>>2]}f=f+1|0;if(e>>>0>f>>>0){continue}break}d=H[a+40>>2]}n=n+1|0;if(n>>>0>>0){continue}break}f=H[a+88>>2]}H[a+84>>2]=g;e=na-208|0;na=e;H[e+8>>2]=1;H[e+12>>2]=0;h=g<<3;l:{if(!h){break l}H[e+16>>2]=8;H[e+20>>2]=8;d=2;while(1){g=(e+16|0)+(d<<2)|0;k=H[g-4>>2]+(H[g-8>>2]+8|0)|0;H[g>>2]=k;d=d+1|0;if(h>>>0>k>>>0){continue}break}d=h-8|0;m:{if((d|0)<=0){g=0;h=1;d=1;k=0;break m}g=d+f|0;h=1;d=1;while(1){n:{if((h&3)==3){Bb(f,d,e+16|0);ub(e+8|0,2);d=d+2|0;break n}n=e+16|0;k=d-1|0;o:{if(K[n+(k<<2)>>2]>=g-f>>>0){tb(f,h,H[e+12>>2],d,0,n);break o}Bb(f,d,e+16|0)}if((d|0)==1){sb(e+8|0,1);d=0;break n}sb(e+8|0,k);d=1}h=H[e+8>>2]|1;H[e+8>>2]=h;f=f+8|0;if(g>>>0>f>>>0){continue}break}g=H[e+12>>2];k=(g|0)!=0}tb(f,h,g,d,0,e+16|0);h=H[e+8>>2];if(!(k|((d|0)!=1|(h|0)!=1))){break l}while(1){p:{if((d|0)<=1){h=wc(h,g);ub(e+8|0,h);d=d+h|0;break p}g=e+8|0;sb(g,2);H[e+8>>2]=H[e+8>>2]^7;ub(g,1);n=f-8|0;k=e+16|0;h=d-2|0;tb(n-H[k+(h<<2)>>2]|0,H[e+8>>2],H[e+12>>2],d-1|0,1,k);sb(g,1);d=H[e+8>>2]|1;H[e+8>>2]=d;tb(n,d,H[e+12>>2],h,1,k);d=h}f=f-8|0;g=H[e+12>>2];h=H[e+8>>2];if(g|((d|0)!=1|(h|0)!=1)){continue}break}}na=e+208|0}d=H[a+128>>2];e=0;q:{while(1){r:{if(!(!H[H[a+180>>2]+5596>>2]|((d|0)!=1|H[a+132>>2]!=1))){H[i+72>>2]=0;H[a+228>>2]=0;H[a+8>>2]=H[a+8>>2]|128;d=0;break r}d=0;if(!cb(a,i+72|0,0,i+68|0,i- -64|0,i+60|0,i+56|0,i+52|0,i+76|0,b,c)){break a}if(!H[i+76>>2]){break q}d=H[i+72>>2]}h=d+1|0;f=ib(a,d,0,0,b,c);g=N(H[a+128>>2],H[a+132>>2]);if(!f){H[i+4>>2]=g;H[i>>2]=h;Ba(c,1,7537,i);d=0;break a}H[i+36>>2]=g;H[i+32>>2]=h;Ba(c,4,11795,i+32|0);if(!Hc(H[a+232>>2],H[H[a+100>>2]+24>>2])){d=0;break a}s:{if(!(H[a+128>>2]!=1|H[a+132>>2]!=1)){g=H[a+100>>2];f=H[a+96>>2];if(H[g>>2]!=H[f>>2]|H[g+4>>2]!=H[f+4>>2]|(H[g+8>>2]!=H[f+8>>2]|H[g+12>>2]!=H[f+12>>2])){break s}}d=H[a+180>>2]+N(d,5644)|0;g=H[d+5596>>2];if(!g){break s}Ca(g);H[d+5596>>2]=0;H[d+5600>>2]=0}H[i+16>>2]=h;Ba(c,4,16601,i+16|0);if(!(Qa(b)|qa)&H[a+8>>2]==64){break q}e=e+1|0;d=H[a+128>>2];if((e|0)==(N(d,H[a+132>>2])|0)){break q}h=H[a+84>>2];if(!h|(h|0)!=H[a+80>>2]){continue}break}nc(b,j,m,c)}d=Gc(a,c)}na=i+80|0;return d|0}function Ya(a){var b=0,c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0;d=H[a+32>>2];a:{if(d){break a}b:{h=H[a+16>>2];if((h|0)>=6){b=H[a+8>>2];f=H[a+12>>2];d=h;break b}b=H[a+20>>2];c:{d:{if((b|0)>=5){c=H[a>>2];d=H[c>>2];H[a>>2]=c+4;g=b-4|0;break d}if((b|0)<=0){d=-1;break c}c=H[a>>2];e:{if((b|0)==1){e=-1;b=0;break e}d=0;f:{g:{if((b|0)==2){e=-1;i=b;break g}f=b-1|0;k=f&1;j=f&-2;e=-1;i=b;while(1){f=c;H[a>>2]=c+1;l=I[c|0];c=c+2|0;H[a>>2]=c;H[a+20>>2]=i-1;f=I[f+1|0];i=i-2|0;H[a+20>>2]=i;e=((255<>2]=f;c=I[c|0];H[a+20>>2]=i-1;e=(255<>2]=c+1;d=(255<>2]=g}b=H[a+24>>2];c=d>>>24|0;H[a+24>>2]=(c|0)==255;g=d>>>16&255;k=(g|0)==255;f=d&255;e=(f|0)==255;j=b+e|0;b=d>>>8&255;i=(b|0)==255;j=k+(j+i|0)|0;d=(h-j|0)+32|0;H[a+16>>2]=d;l=H[a+12>>2];b=c|(g|(b|f<<(e?7:8))<<(i?7:8))<<(k?7:8);f=(j-h|0)+32|0;c=f&31;if((f&63)>>>0>=32){i=b<>>32-c;g=b<>2];c=i|l;f=c;H[a+8>>2]=b;H[a+12>>2]=c;if((d|0)>=6){break b}d=0;break a}e=H[a+28>>2];i=H[(e<<2)+20752>>2];h:{if((f|0)<0){d=d-1|0;c=(-1<=11?11:e)+1|0;break h}g=b;h=63-i|0;c=h&31;if((h&63)>>>0>=32){g=f>>>c|0}else{g=((1<>>c}c=(g&(-1<>2]=d;H[a+28>>2]=e;g=b;h=i&31;if((i&63)>>>0>=32){b=b<>>32-h|f<>2]=g;H[a+12>>2]=b;i=H[a+44>>2]|c>>31;j=H[a+40>>2]&-64|c;H[a+40>>2]=j;H[a+44>>2]=i;if((d|0)<6){d=1;break a}b=H[(e<<2)+20752>>2];i:{if((f|0)<0){d=d-1|0;c=(-1<=11?11:e)+1|0;break i}k=g;h=63-b|0;c=h&31;if((h&63)>>>0>=32){k=f>>>c|0}else{k=((1<>>c}c=(k&(-1<>2]=d;H[a+28>>2]=e;k=g;h=b&31;if((b&63)>>>0>=32){b=g<>>32-h|f<>2]=k;H[a+12>>2]=b;b=c>>31<<7|c>>>25|i;h=b;j=j&-8065|c<<7;H[a+40>>2]=j;H[a+44>>2]=b;if((d|0)<6){d=2;break a}b=H[(e<<2)+20752>>2];j:{if((f|0)<0){d=d-1|0;c=(-1<=11?11:e)+1|0;break j}g=k;i=63-b|0;c=i&31;if((i&63)>>>0>=32){g=f>>>c|0}else{g=((1<>>c}c=(g&(-1<>2]=d;H[a+28>>2]=e;l=k;g=b&31;if((b&63)>>>0>=32){i=k<>>32-g|f<>2]=g;f=i;H[a+12>>2]=f;b=c>>31<<14|c>>>18|h;i=b;k=j&-1032193|c<<14;H[a+40>>2]=k;H[a+44>>2]=b;if((d|0)<6){d=3;break a}b=H[(e<<2)+20752>>2];k:{if((f|0)<0){d=d-1|0;c=(-1<=11?11:e)+1|0;break k}j=g;h=63-b|0;c=h&31;if((h&63)>>>0>=32){j=f>>>c|0}else{j=((1<>>c}c=(j&(-1<>2]=d;H[a+28>>2]=e;j=g;h=b&31;if((b&63)>>>0>=32){b=g<>>32-h|f<>2]=g;H[a+12>>2]=b;b=c>>31<<21|c>>>11|i;j=b;k=k&-132120577|c<<21;H[a+40>>2]=k;H[a+44>>2]=b;if((d|0)<6){d=4;break a}b=H[(e<<2)+20752>>2];l:{if((f|0)<0){c=(-1<=11?11:e)+1|0;d=d-1|0;break l}h=g;i=63-b|0;c=i&31;if((i&63)>>>0>=32){i=f>>>c|0}else{i=((1<>>c}c=(i&(-1<>2]=d;H[a+28>>2]=h;i=g;e=b&31;if((b&63)>>>0>=32){b=g<>>32-e|f<>2]=g;f=b;H[a+12>>2]=b;b=j&-4|(c>>31<<28|c>>>4);j=b;k=k&268435455|c<<28;H[a+40>>2]=k;H[a+44>>2]=b;if((d|0)<6){d=5;break a}b=H[(h<<2)+20752>>2];m:{if((f|0)<0){e=(-1<=11?11:h)+1|0;i=d-1|0;break m}i=g;e=63-b|0;c=e&31;if((e&63)>>>0>=32){i=f>>>c|0}else{i=((1<>>c}e=(i&(-1<>2]=i;H[a+28>>2]=h;d=g;c=b&31;if((b&63)>>>0>=32){b=d<>>32-c|f<>2]=g;H[a+12>>2]=b;b=j&-505|e<<3;l=b;H[a+40>>2]=k;H[a+44>>2]=b;d=6;if((i|0)<6){break a}b=H[(h<<2)+20752>>2];n:{if((c|0)<0){e=(-1<=11?11:h)+1|0;d=i-1|0;break n}d=g;e=63-b|0;f=e&31;if((e&63)>>>0>=32){f=c>>>f|0}else{f=((1<>>f}e=(f&(-1<>2]=d;H[a+28>>2]=h;j=g;f=b&31;if((b&63)>>>0>=32){i=g<>>32-f|c<>2]=g;f=i;H[a+12>>2]=f;i=k;b=l&-64513|e<<10;k=b;H[a+40>>2]=i;H[a+44>>2]=b;if((d|0)<6){d=7;break a}b=H[(h<<2)+20752>>2];o:{if((f|0)<0){d=d-1|0;c=(-1<=11?11:h)+1|0;break o}j=g;e=63-b|0;c=e&31;if((e&63)>>>0>=32){j=f>>>c|0}else{j=((1<>>c}c=(j&(-1<>2]=d;H[a+28>>2]=e;d=g;e=b&31;if((b&63)>>>0>=32){b=d<>>32-e|f<>2]=g;H[a+12>>2]=b;H[a+40>>2]=i;H[a+44>>2]=k&-8257537|c<<17;d=8}H[a+32>>2]=d-1;f=H[a+44>>2];b=f>>>7|0;c=H[a+40>>2];H[a+40>>2]=(f&127)<<25|c>>>7;H[a+44>>2]=b;return c&127}function Ub(a,b,c,d,e,f,g,h,i){var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,z=0,A=0;p=na-32|0;na=p;H[p+24>>2]=f;r=H[(N(H[d+28>>2],76)+b|0)+28>>2]+N(H[d+32>>2],152)|0;a:{if(!(H[d+40>>2]|!H[r+24>>2])){k=r+28|0;while(1){b:{if(Qb(k)){break b}b=H[d+36>>2];if(b>>>0>=K[k+24>>2]/40>>>0){Ba(i,1,2836,0);break a}b=H[k+20>>2]+N(b,40)|0;fc(H[b+32>>2]);fc(H[b+36>>2]);o=N(H[b+20>>2],H[b+16>>2]);if(!o){break b}q=o&7;b=H[b+24>>2];if(o>>>0>=8){o=o&-8;j=0;while(1){H[b+516>>2]=0;H[b+520>>2]=0;H[b+448>>2]=0;H[b+452>>2]=0;H[b+380>>2]=0;H[b+384>>2]=0;H[b+312>>2]=0;H[b+316>>2]=0;H[b+244>>2]=0;H[b+248>>2]=0;H[b+176>>2]=0;H[b+180>>2]=0;H[b+108>>2]=0;H[b+112>>2]=0;H[b+40>>2]=0;H[b+44>>2]=0;b=b+544|0;j=j+8|0;if((o|0)!=(j|0)){continue}break}if(!q){break b}}j=0;while(1){H[b+40>>2]=0;H[b+44>>2]=0;b=b+68|0;j=j+1|0;if((q|0)!=(j|0)){continue}break}}k=k+36|0;n=n+1|0;if(n>>>0>2]){continue}break}}q=f;c:{if(!(I[c|0]&2)){break c}if(h>>>0<=5){Ba(i,2,4196,0);break c}if(!(I[f|0]==255&I[f+1|0]==145)){Ba(i,2,4238,0);break c}q=f+6|0;H[p+24>>2]=q}l=Fa(20);if(!l){break a}d:{if(F[a+108|0]&1){q=H[a+40>>2];o=a+44|0;h=a+40|0;break d}if(I[c+5640|0]&2){q=H[c+5168>>2];o=c+5180|0;h=c+5168|0;break d}H[p+28>>2]=(f+h|0)-q;o=p+28|0;h=p+24|0}a=H[o>>2];H[l+12>>2]=0;H[l+16>>2]=0;H[l+8>>2]=q;H[l>>2]=q;H[l+4>>2]=a+q;if(!Ra(l,1)){hc(l);a=ic(l);db(l);a=a+q|0;b=H[h>>2];d=H[o>>2];if(I[c|0]&4){if(b+(d-a|0)>>>0<=1){Ba(i,1,4422,0);break a}if(!(I[a|0]==255&I[a+1|0]==146)){Ba(i,1,4401,0);break a}a=a+2|0}a=a-b|0;H[o>>2]=d-a;H[h>>2]=a+b;H[e>>2]=0;H[g>>2]=H[p+24>>2]-f;x=1;break a}if(H[r+24>>2]){t=r+28|0;while(1){a=H[d+36>>2];b=H[t+20>>2];e:{if(Qb(t)){break e}u=b+N(a,40)|0;z=N(H[u+20>>2],H[u+16>>2]);if(!z){break e}k=H[u+24>>2];v=0;while(1){f:{g:{if(!H[k+40>>2]){a=dc(l,H[u+32>>2],v,H[d+40>>2]+1|0);break g}a=Ra(l,1)}if(!a){H[k+36>>2]=0;break f}if(!H[k+40>>2]){b=0;while(1){a=b;b=b+1|0;if(!dc(l,H[u+36>>2],v,a)){continue}break}b=H[t+28>>2];H[k+32>>2]=3;H[k+24>>2]=b;H[k+28>>2]=(b-a|0)+1}a=1;h:{if(!Ra(l,1)){break h}a=2;if(!Ra(l,1)){break h}a=Ra(l,2);if((a|0)!=3){a=a+3|0;break h}a=Ra(l,5);if((a|0)!=31){a=a+6|0;break h}a=Ra(l,7)+37|0}H[k+36>>2]=a;b=0;while(1){a=b;b=b+1|0;if(Ra(l,1)){continue}break}H[k+32>>2]=a+H[k+32>>2];i:{a=H[k+40>>2];j:{k:{if(!a){a=H[(H[c+5584>>2]+N(H[d+28>>2],1080)|0)+16>>2];if(!H[k+48>>2]){b=Ha(H[k>>2],240);if(!b){break i}H[k>>2]=b;y(b+N(H[k+48>>2],24)|0,0,240);H[k+48>>2]=10}j=H[k>>2];kb(j);b=a&4?1:a&1?10:109;a=0;break k}b=H[k>>2];n=a-1|0;j=b+N(n,24)|0;if(H[j+4>>2]!=H[j+12>>2]){break j}n=H[(H[c+5584>>2]+N(H[d+28>>2],1080)|0)+16>>2];j=H[k+48>>2];if(j>>>0>>0){j=j+10|0;b=Ha(b,N(j,24));if(!b){break i}H[k>>2]=b;y(b+N(H[k+48>>2],24)|0,0,240);H[k+48>>2]=j;b=H[k>>2]}j=N(a,24)+b|0;kb(j);b=1;l:{if(n&4){break l}b=109;if(!(n&1)){break l}b=H[j-12>>2];b=(b|0)==1?2:(b|0)==10?2:1}}n=a;H[j+12>>2]=b}a=H[k+36>>2];if(I[(H[c+5584>>2]+N(H[d+28>>2],1080)|0)+16|0]&64){while(1){m=N(n,24);s=n?a:1;H[(m+H[k>>2]|0)+16>>2]=s;w=H[k+32>>2];j=0;b=a;if(s>>>0>=2){while(1){j=j+1|0;s=b>>>0>3;b=b>>>1|0;if(s){continue}break}}b=j+w|0;if(b>>>0>=33){H[p+16>>2]=b;Ba(i,1,15535,p+16|0);break i}j=Ra(l,b);b=H[k>>2];m=m+b|0;H[m+20>>2]=j;a=a-H[m+16>>2]|0;if((a|0)<=0){break f}j=H[(H[c+5584>>2]+N(H[d+28>>2],1080)|0)+16>>2];m=H[k+48>>2];if(m>>>0>>0){m=m+10|0;b=Ha(b,N(m,24));if(!b){break i}H[k>>2]=b;y(b+N(H[k+48>>2],24)|0,0,240);H[k+48>>2]=m;b=H[k>>2]}n=n+1|0;b=b+N(n,24)|0;kb(b);if(j&4){H[b+12>>2]=1;continue}if(j&1){j=b;b=H[b-12>>2];H[j+12>>2]=(b|0)==1?2:(b|0)==10?2:1}else{H[b+12>>2]=109}continue}}while(1){m=N(n,24);j=m+H[k>>2]|0;b=H[j+12>>2]-H[j+4>>2]|0;b=(a|0)>(b|0)?b:a;H[j+16>>2]=b;s=H[k+32>>2];j=0;if(b>>>0>=2){while(1){j=j+1|0;w=b>>>0>3;b=b>>>1|0;if(w){continue}break}}b=j+s|0;if(b>>>0>=33){H[p>>2]=b;Ba(i,1,15535,p);break i}j=Ra(l,b);b=H[k>>2];m=m+b|0;H[m+20>>2]=j;a=a-H[m+16>>2]|0;if((a|0)<=0){break f}j=H[(H[c+5584>>2]+N(H[d+28>>2],1080)|0)+16>>2];m=H[k+48>>2];if(m>>>0>>0){m=m+10|0;b=Ha(b,N(m,24));if(!b){break i}H[k>>2]=b;y(b+N(H[k+48>>2],24)|0,0,240);H[k+48>>2]=m;b=H[k>>2]}n=n+1|0;b=b+N(n,24)|0;kb(b);if(j&4){H[b+12>>2]=1;continue}if(j&1){j=b;b=H[b-12>>2];H[j+12>>2]=(b|0)==1?2:(b|0)==10?2:1}else{H[b+12>>2]=109}continue}}db(l);break a}k=k+68|0;v=v+1|0;if((z|0)!=(v|0)){continue}break}}t=t+36|0;A=A+1|0;if(A>>>0>2]){continue}break}}if(!hc(l)){db(l);break a}a=ic(l);db(l);b=a+q|0;a=H[h>>2];if(I[c|0]&4){if(a+(H[o>>2]-b|0)>>>0<=1){Ba(i,1,4422,0);break a}if(!(I[b|0]==255&I[b+1|0]==146)){Ba(i,1,4401,0);break a}b=b+2|0}if((a|0)==(b|0)){break a}H[o>>2]=H[o>>2]+(a-b|0);H[h>>2]=b;x=1;H[e>>2]=1;H[g>>2]=H[p+24>>2]-f}na=p+32|0;return x}function Gb(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0;a:{if(!c){break a}b:{e=H[a+184>>2];if(!e){break b}f=H[a+96>>2];if(!f|!H[f+16>>2]|(e|0)!=H[H[f+24>>2]+40>>2]){break b}f=H[c+16>>2];if(!f){break b}n=H[c+24>>2];if(H[n+40>>2]|H[n+44>>2]){break b}i=f&7;c:{if(f>>>0>=8){p=f&-8;while(1){f=N(h,52)+n|0;H[f+404>>2]=e;H[f+352>>2]=e;H[f+300>>2]=e;H[f+248>>2]=e;H[f+196>>2]=e;H[f+144>>2]=e;H[f+92>>2]=e;H[f+40>>2]=e;h=h+8|0;g=g+8|0;if((g|0)!=(p|0)){continue}break}if(!i){break c}}while(1){H[(N(h,52)+n|0)+40>>2]=e;h=h+1|0;j=j+1|0;if((i|0)!=(j|0)){continue}break}}e=0;p=0;n=na-32|0;na=n;v=H[c+16>>2];d:{if(!v){e=1;break d}h=H[c>>2];f=h>>31;q=f;e:{if((f|0)<0){break e}i=H[c+4>>2];f=i>>31;m=f;if((f|0)<0){break e}g=H[c+8>>2];f=g>>31;s=f;if((f|0)<0){break e}j=H[c+12>>2];r=j>>31;if((r|0)<0){break e}f=H[c+24>>2];y=h-1|0;w=q-!h|0;z=i-1|0;x=m-!i|0;A=g-1|0;B=s-!g|0;C=j-1|0;D=r-!j|0;while(1){e=H[f>>2];h=e+y|0;i=e>>>0>h>>>0?w+1|0:w;j=ve(h,i,e,0);H[f+16>>2]=j;i=H[f+4>>2];h=i+z|0;g=i>>>0>h>>>0?x+1|0:x;r=ve(h,g,i,0);H[f+20>>2]=r;h=H[f+40>>2];g=h&31;if((h&63)>>>0>=32){q=1<>>32-g}s=m-1|0;g=q-!m|0;u=g;t=e>>31;l=t+B|0;o=l+1|0;g=l;l=e+A|0;g=ue(l,l>>>0>>0?o:g,e,t);e=u+(g>>31)|0;o=e+1|0;l=e;e=g+s|0;t=g>>>0>e>>>0?o:l;g=h&31;l=(j>>31)+u|0;E=l+1|0;o=l;l=j+s|0;j=j>>>0>l>>>0?E:o;if((h&63)>>>0>=32){g=t>>g}else{g=((1<>>g}e=h&31;if((h&63)>>>0>=32){e=j>>e}else{e=((1<>>e}e=g-e|0;if((e|0)<0){H[n+4>>2]=e;H[n>>2]=p;Ba(d,1,13510,n);e=0;break d}H[f+8>>2]=e;e=i>>31;g=e+D|0;o=g+1|0;l=g;g=i+C|0;j=g>>>0>>0?o:l;i=ue(g,j,i,e);e=(i>>31)+u|0;g=e+1|0;l=e;e=i+s|0;g=i>>>0>e>>>0?g:l;i=h&31;j=q+(r>>31)|0;o=j+1|0;l=j;j=m+r|0;q=j>>>0>>0?o:l;m=j-1|0;if((h&63)>>>0>=32){l=g>>i}else{l=((1<>>i}i=q-!j|0;e=h&31;if((h&63)>>>0>=32){m=i>>e}else{m=((1<>>e}e=l-m|0;if((e|0)<0){H[n+20>>2]=e;H[n+16>>2]=p;Ba(d,1,13579,n+16|0);e=0;break d}H[f+12>>2]=e;f=f+52|0;e=1;p=p+1|0;if((v|0)!=(p|0)){continue}break}break d}Ba(d,1,6720,0)}na=n+32|0;if(e){break b}return 0}e=H[a+100>>2];if(!e){e=Eb();H[a+100>>2]=e;if(!e){break a}}Ec(c,e);if(!Wa(H[a+216>>2],22,d)){break a}h=H[a+216>>2];f=H[h>>2];e=H[h+8>>2];f:{if(f){k=1;g:{if((f|0)!=1){n=f&1;i=f&-2;f=0;while(1){m=0;h:{if(!k){break h}m=0;if(!(ra[H[e>>2]](a,b,d)|0)){break h}m=(ra[H[e+4>>2]](a,b,d)|0)!=0}k=m;e=e+8|0;f=f+2|0;if((i|0)!=(f|0)){continue}break}if(!n){break g}}if(!k){k=0;break g}k=(ra[H[e>>2]](a,b,d)|0)!=0}Pa(h);if(k){break f}Ua(H[a+96>>2]);H[a+96>>2]=0;return 0}Pa(h)}f=0;b=0;d=0;i:{j:{k:{e=H[a+60>>2];if(!e){if(H[c+16>>2]){break k}k=1;break i}h=Fa(N(e,52));if(!h){break j}e=0;if(H[c+16>>2]){b=H[c+24>>2];while(1){e=N(d,52);Ca(H[(e+b|0)+44>>2]);b=H[c+24>>2];H[(e+b|0)+44>>2]=0;d=d+1|0;e=H[c+16>>2];if(d>>>0>>0){continue}break}}if(H[a+60>>2]){d=H[H[a+100>>2]+24>>2];e=0;while(1){b=h+N(e,52)|0;f=N(H[H[a+64>>2]+(e<<2)>>2],52);d=f+d|0;H[b+48>>2]=H[d+48>>2];k=H[d+44>>2];H[b+40>>2]=H[d+40>>2];H[b+44>>2]=k;k=H[d+36>>2];H[b+32>>2]=H[d+32>>2];H[b+36>>2]=k;k=H[d+28>>2];H[b+24>>2]=H[d+24>>2];H[b+28>>2]=k;k=H[d+20>>2];H[b+16>>2]=H[d+16>>2];H[b+20>>2]=k;k=H[d+12>>2];H[b+8>>2]=H[d+8>>2];H[b+12>>2]=k;k=H[d+4>>2];H[b>>2]=H[d>>2];H[b+4>>2]=k;d=H[H[a+100>>2]+24>>2];f=f+d|0;H[b+36>>2]=H[f+36>>2];H[b+44>>2]=H[f+44>>2];H[f+44>>2]=0;e=e+1|0;f=H[a+60>>2];if(e>>>0>>0){continue}break}e=H[c+16>>2]}if(e){b=H[H[a+100>>2]+24>>2];d=0;while(1){e=N(d,52);Ca(H[(e+b|0)+44>>2]);b=H[H[a+100>>2]+24>>2];H[(e+b|0)+44>>2]=0;d=d+1|0;if(d>>>0>2]){continue}break}f=H[a+60>>2]}H[c+16>>2]=f;Ca(H[c+24>>2]);H[c+24>>2]=h;k=1;break i}e=H[c+24>>2];d=H[H[a+100>>2]+24>>2];while(1){f=N(b,52);e=f+e|0;H[e+36>>2]=H[(d+f|0)+36>>2];Ca(H[e+44>>2]);e=H[c+24>>2];m=f+e|0;d=H[H[a+100>>2]+24>>2];f=f+d|0;H[m+44>>2]=H[f+44>>2];H[f+44>>2]=0;b=b+1|0;if(b>>>0>2]){continue}break}k=1;break i}Ua(H[a+96>>2]);H[a+96>>2]=0;k=0}}return k|0}function zb(a,b,c,d){var e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0;j=(c<<2)+b|0;e=H[a>>2];f=H[a+12>>2]<<5;i=e+f|0;l=e-f|0;e=H[a+16>>2];k=H[a+28>>2];h=H[a+20>>2];m=H[a+8>>2];a:{b:{if(i&15|(b&15|d>>>0<8)){if(e>>>0>=h>>>0){break a}c:{switch(d-1|0){case 1:f=e+1|0;if(h-e&1){j=i+(e<<6)|0;e=(e<<2)+b|0;L[j>>2]=L[e>>2];L[j+4>>2]=L[e+(c<<2)>>2];e=f}if((f|0)==(h|0)){break a}while(1){f=i+(e<<6)|0;j=(e<<2)+b|0;L[f>>2]=L[j>>2];g=f;f=c<<2;L[g+4>>2]=L[f+j>>2];j=e+1|0;g=i+(j<<6)|0;j=(j<<2)+b|0;L[g>>2]=L[j>>2];L[g+4>>2]=L[f+j>>2];e=e+2|0;if((h|0)!=(e|0)){continue}break};break a;case 0:break c;default:break b}}f=e;g=h-e&3;if(g){j=0;while(1){L[i+(f<<6)>>2]=L[(f<<2)+b>>2];f=f+1|0;j=j+1|0;if((g|0)!=(j|0)){continue}break}}if(e-h>>>0>4294967292){break a}while(1){L[i+(f<<6)>>2]=L[(f<<2)+b>>2];e=f+1|0;L[i+(e<<6)>>2]=L[(e<<2)+b>>2];e=f+2|0;L[i+(e<<6)>>2]=L[(e<<2)+b>>2];e=f+3|0;L[i+(e<<6)>>2]=L[(e<<2)+b>>2];f=f+4|0;if((h|0)!=(f|0)){continue}break}break a}if(e>>>0>=h>>>0){break a}while(1){f=i+(e<<6)|0;L[f>>2]=L[(e<<2)+b>>2];g=c+e|0;L[f+4>>2]=L[(g<<2)+b>>2];g=c+g|0;L[f+8>>2]=L[(g<<2)+b>>2];g=c+g|0;L[f+12>>2]=L[(g<<2)+b>>2];g=c+g|0;L[f+16>>2]=L[(g<<2)+b>>2];g=c+g<<2;L[f+20>>2]=L[g+b>>2];g=g+j|0;L[f+24>>2]=L[g>>2];L[f+28>>2]=L[g+(c<<2)>>2];e=e+1|0;if((h|0)!=(e|0)){continue}break}break a}while(1){f=i+(e<<6)|0;L[f>>2]=L[(e<<2)+b>>2];g=c+e|0;L[f+4>>2]=L[(g<<2)+b>>2];g=c+g|0;L[f+8>>2]=L[(g<<2)+b>>2];d:{if((d|0)==3){break d}g=c+g|0;L[f+12>>2]=L[(g<<2)+b>>2];if((d|0)==4){break d}g=c+g|0;L[f+16>>2]=L[(g<<2)+b>>2];if((d|0)==5){break d}g=c+g|0;L[f+20>>2]=L[(g<<2)+b>>2];if((d|0)==6){break d}g=j+(g<<2)|0;L[f+24>>2]=L[g>>2];if((d|0)==7){break d}L[f+28>>2]=L[g+(c<<2)>>2]}e=e+1|0;if((h|0)!=(e|0)){continue}break}}b=(m<<2)+b|0;f=b+(c<<2)|0;e=H[a+24>>2];i=l+32|0;e:{if(i&15|(b&15|d>>>0<8)){if(e>>>0>=k>>>0){break e}f:{switch(d-1|0){case 1:a=e+1|0;if(k-e&1){d=i+(e<<6)|0;e=b+(e<<2)|0;L[d>>2]=L[e>>2];L[d+4>>2]=L[e+(c<<2)>>2];e=a}if((a|0)==(k|0)){break e}while(1){a=i+(e<<6)|0;d=b+(e<<2)|0;L[a>>2]=L[d>>2];f=a;a=c<<2;L[f+4>>2]=L[a+d>>2];d=e+1|0;f=i+(d<<6)|0;d=b+(d<<2)|0;L[f>>2]=L[d>>2];L[f+4>>2]=L[a+d>>2];e=e+2|0;if((k|0)!=(e|0)){continue}break};break e;case 0:c=e;a=k-e&3;if(a){f=0;while(1){L[i+(c<<6)>>2]=L[b+(c<<2)>>2];c=c+1|0;f=f+1|0;if((a|0)!=(f|0)){continue}break}}if(e-k>>>0>4294967292){break e}while(1){L[i+(c<<6)>>2]=L[b+(c<<2)>>2];a=c+1|0;L[i+(a<<6)>>2]=L[b+(a<<2)>>2];a=c+2|0;L[i+(a<<6)>>2]=L[b+(a<<2)>>2];a=c+3|0;L[i+(a<<6)>>2]=L[b+(a<<2)>>2];c=c+4|0;if((k|0)!=(c|0)){continue}break};break e;default:break f}}while(1){a=i+(e<<6)|0;L[a>>2]=L[b+(e<<2)>>2];h=c+e|0;L[a+4>>2]=L[b+(h<<2)>>2];h=c+h|0;L[a+8>>2]=L[b+(h<<2)>>2];g:{if((d|0)==3){break g}h=c+h|0;L[a+12>>2]=L[b+(h<<2)>>2];if((d|0)==4){break g}h=c+h|0;L[a+16>>2]=L[b+(h<<2)>>2];if((d|0)==5){break g}h=c+h|0;L[a+20>>2]=L[b+(h<<2)>>2];if((d|0)==6){break g}h=f+(h<<2)|0;L[a+24>>2]=L[h>>2];if((d|0)==7){break g}L[a+28>>2]=L[h+(c<<2)>>2]}e=e+1|0;if((k|0)!=(e|0)){continue}break}break e}if(e>>>0>=k>>>0){break e}while(1){a=i+(e<<6)|0;L[a>>2]=L[b+(e<<2)>>2];d=c+e|0;L[a+4>>2]=L[b+(d<<2)>>2];d=c+d|0;L[a+8>>2]=L[b+(d<<2)>>2];d=c+d|0;L[a+12>>2]=L[b+(d<<2)>>2];d=c+d|0;L[a+16>>2]=L[b+(d<<2)>>2];d=c+d<<2;L[a+20>>2]=L[d+b>>2];d=d+f|0;L[a+24>>2]=L[d>>2];L[a+28>>2]=L[d+(c<<2)>>2];e=e+1|0;if((k|0)!=(e|0)){continue}break}}}function Ib(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0;f=na-176|0;na=f;a:{if(b&384){Ac(5943,11,c);break a}b:{if(!(b&1)){break b}g=H[a+96>>2];if(!g){break b}d=na-80|0;na=d;Ac(1792,13,c);F[d+79|0]=0;F[d+78|0]=9;e=H[g+4>>2];H[d+68>>2]=H[g>>2];H[d+72>>2]=e;e=d+78|0;H[d+64>>2]=e;Ga(c,7520,d- -64|0);i=H[g+12>>2];H[d+52>>2]=H[g+8>>2];H[d+56>>2]=i;H[d+48>>2]=e;Ga(c,7503,d+48|0);H[d+36>>2]=H[g+16>>2];H[d+32>>2]=e;Ga(c,7277,d+32|0);if(!(!H[g+24>>2]|!H[g+16>>2])){while(1){l=d+78|0;H[d+16>>2]=l;H[d+20>>2]=j;Ga(c,1824,d+16|0);i=H[g+24>>2];e=na-48|0;na=e;F[e+46|0]=9;F[e+47|0]=0;F[e+45|0]=9;i=N(j,52)+i|0;k=H[i+4>>2];H[e+36>>2]=H[i>>2];H[e+40>>2]=k;k=e+45|0;H[e+32>>2]=k;Ga(c,7209,e+32|0);H[e+20>>2]=H[i+24>>2];H[e+16>>2]=k;Ga(c,7455,e+16|0);H[e+4>>2]=H[i+32>>2];H[e>>2]=k;Ga(c,7428,e);na=e+48|0;H[d>>2]=l;Ga(c,1702,d);j=j+1|0;if(j>>>0>2]){continue}break}}Ac(1710,2,c);na=d+80|0}if(!(!(b&2)|!H[a+96>>2])){Ac(1931,36,c);d=H[a+112>>2];H[f+160>>2]=H[a+108>>2];H[f+164>>2]=d;Ga(c,2425,f+160|0);d=H[a+120>>2];H[f+144>>2]=H[a+116>>2];H[f+148>>2]=d;Ga(c,2391,f+144|0);d=H[a+132>>2];H[f+128>>2]=H[a+128>>2];H[f+132>>2]=d;Ga(c,2409,f+128|0);Hb(H[a+12>>2],H[H[a+96>>2]+16>>2],c);Ac(1710,2,c)}c:{if(!(b&8)|!H[a+96>>2]){break c}e=N(H[a+128>>2],H[a+132>>2]);if(!e){break c}d=H[a+180>>2];while(1){Hb(d,H[H[a+96>>2]+16>>2],c);d=d+5644|0;h=h+1|0;if((e|0)!=(h|0)){continue}break}}if(!(b&16)){break a}a=H[a+224>>2];Ac(1893,37,c);b=H[a>>2];d=H[a+4>>2];e=H[a+12>>2];H[f+120>>2]=H[a+8>>2];H[f+124>>2]=e;H[f+112>>2]=b;H[f+116>>2]=d;Ga(c,5730,f+112|0);Ac(1875,17,c);if(!(!H[a+28>>2]|!H[a+24>>2])){d=0;while(1){b=H[a+28>>2]+N(d,24)|0;e=J[b>>1];h=H[b+8>>2];g=H[b+12>>2];H[f+96>>2]=H[b+16>>2];H[f+88>>2]=h;H[f+92>>2]=g;H[f+80>>2]=e;Ga(c,7397,f+80|0);d=d+1|0;if(d>>>0>2]){continue}break}}Ac(1708,4,c);e=H[a+40>>2];d:{if(!e){break d}g=H[a+36>>2];if(!g){break d}h=0;d=0;while(1){b=e+N(d,40)|0;j=H[b+4>>2];e:{if(!j){break e}b=H[b+16>>2];if(!b){break e}i=H[b>>2];k=H[b+4>>2];if((k|0)<0){i=1}else{i=!i&(k|0)<=0}if(i|(H[b+8>>2]|H[b+12>>2])){break e}if(xc(1439)){break d}}h=h+j|0;d=d+1|0;if((g|0)!=(d|0)){continue}break}if(!h){break d}Ac(1858,16,c);if(H[a+36>>2]){h=H[a+40>>2];g=0;while(1){b=N(g,40);j=H[(b+h|0)+4>>2];H[f+68>>2]=j;H[f+64>>2]=g;Ga(c,7467,f- -64|0);h=H[a+40>>2];f:{if(!j){break f}d=0;if(!H[(b+h|0)+16>>2]){break f}while(1){e=H[(b+H[a+40>>2]|0)+16>>2]+N(d,24)|0;h=H[e>>2];i=H[e+4>>2];k=H[e+8>>2];l=H[e+12>>2];m=H[e+20>>2];H[f+56>>2]=H[e+16>>2];H[f+60>>2]=m;H[f+48>>2]=k;H[f+52>>2]=l;H[f+40>>2]=h;H[f+44>>2]=i;H[f+32>>2]=d;Ga(c,10938,f+32|0);d=d+1|0;if((j|0)!=(d|0)){continue}break}h=H[a+40>>2]}e=b+h|0;g:{if(!H[e+24>>2]){break g}d=0;if(!H[e+20>>2]){break g}while(1){e=H[(b+h|0)+24>>2]+N(d,24)|0;h=J[e>>1];j=H[e+8>>2];i=H[e+12>>2];H[f+16>>2]=H[e+16>>2];H[f+8>>2]=j;H[f+12>>2]=i;H[f>>2]=h;Ga(c,7397,f);d=d+1|0;h=H[a+40>>2];if(d>>>0>2]){continue}break}}g=g+1|0;if(g>>>0>2]){continue}break}}Ac(1708,4,c)}Ac(1710,2,c)}na=f+176|0}function oe(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0;e=na-128|0;na=e;H[e+120>>2]=0;a:{if((c|0)!=8){Ba(d,1,4047,0);Ba(d,1,4047,0);break a}Da(b,a+228|0,2);Da(b+2|0,e+124|0,4);Da(b+6|0,e+116|0,1);Da(b+7|0,e+120|0,1);c=H[a+228>>2];i=H[a+128>>2];if(c>>>0>=N(i,H[a+132>>2])>>>0){H[e+112>>2]=c;Ba(d,1,7843,e+112|0);break a}f=H[a+180>>2]+N(c,5644)|0;h=(c>>>0)/(i>>>0)|0;b=H[e+116>>2];b:{g=H[a+44>>2];if((g|0)>=0&(c|0)!=(g|0)){break b}g=H[f+5588>>2]+1|0;if((g|0)==(b|0)){break b}H[e+104>>2]=g;H[e+100>>2]=b;H[e+96>>2]=c;Ba(d,1,7867,e+96|0);f=0;break a}H[f+5588>>2]=b;c:{b=H[e+124>>2];if(b-1>>>0<=12){if((b|0)!=12){break c}H[e+64>>2]=12;Ba(d,2,11864,e- -64|0);b=H[e+124>>2]}if(!b){Ba(d,4,10695,0);H[a+56>>2]=1}d:{e:{f:{g:{b=H[f+5592>>2];if(b){g=H[e+116>>2];if(g>>>0>>0){break g}H[e+52>>2]=b;H[e+48>>2]=g;Ba(d,1,5150,e+48|0);H[a+56>>2]=1;f=0;break a}g=H[e+120>>2];if(g){break f}break d}g=H[e+120>>2];if(!g){break e}}b=(I[a+92|0]>>>4&1)+g|0;H[e+120>>2]=b;g=H[e+116>>2];j=H[f+5592>>2];if(g>>>0>j-1>>>0){H[e+20>>2]=j;H[e+16>>2]=g;Ba(d,1,5051,e+16|0);H[a+56>>2]=1;f=0;break a}if(b>>>0<=g>>>0){H[e+36>>2]=b;H[e+32>>2]=g;Ba(d,1,5250,e+32|0);H[a+56>>2]=1;f=0;break a}H[f+5592>>2]=b}if((H[e+116>>2]+1|0)!=(b|0)){break d}F[a+92|0]=I[a+92|0]|1}b=H[e+124>>2];H[a+8>>2]=16;H[a+24>>2]=H[a+56>>2]?0:b-12|0;f=H[a+44>>2];h:{if((f|0)==-1){f=4;b=c-N(h,i)|0;if(!(b>>>0>2]|b>>>0>=K[a+36>>2]|h>>>0>2])){f=h>>>0>=K[a+40>>2]?4:0}F[a+92|0]=I[a+92|0]&251|f;b=H[a+228>>2];break h}b=H[a+228>>2];F[a+92|0]=I[a+92|0]&251|((f|0)!=(b|0)?4:0)}c=H[H[a+224>>2]+40>>2]+N(b,40)|0;H[c>>2]=b;H[c+12>>2]=H[e+116>>2];f=H[e+120>>2];if(!H[a+76>>2]){if(K[c+4>>2]>=f>>>0){f=1;break a}H[e>>2]=b;Ba(d,2,1612,e);H[a+76>>2]=1;f=H[e+120>>2]}c=H[H[a+224>>2]+40>>2];b=H[a+228>>2];h=c+N(b,40)|0;if(f){H[h+4>>2]=f;b=H[e+120>>2];H[h+8>>2]=b;c=H[h+16>>2];if(!c){b=Ea(b,24);H[(H[H[a+224>>2]+40>>2]+N(H[a+228>>2],40)|0)+16>>2]=b;if(b){f=1;break a}f=0;Ba(d,1,6947,0);break a}b=Ha(c,N(b,24));c=H[H[a+224>>2]+40>>2]+N(H[a+228>>2],40)|0;if(!b){Ca(H[c+16>>2]);f=0;H[(H[H[a+224>>2]+40>>2]+N(H[a+228>>2],40)|0)+16>>2]=0;Ba(d,1,6947,0);break a}H[c+16>>2]=b;f=1;break a}i:{g=H[h+16>>2];if(g){break i}H[h+8>>2]=10;g=Ea(10,24);c=H[H[a+224>>2]+40>>2];b=H[a+228>>2];h=c+N(b,40)|0;H[h+16>>2]=g;if(g){break i}f=0;H[h+8>>2]=0;Ba(d,1,6947,0);break a}f=1;b=N(b,40)+c|0;c=H[e+116>>2];if(K[b+8>>2]>c>>>0){break a}h=b;b=c+1|0;H[h+8>>2]=b;b=Ha(g,N(b,24));c=H[H[a+224>>2]+40>>2]+N(H[a+228>>2],40)|0;if(!b){Ca(H[c+16>>2]);f=0;a=H[H[a+224>>2]+40>>2]+N(H[a+228>>2],40)|0;H[a+8>>2]=0;H[a+16>>2]=0;Ba(d,1,6947,0);break a}H[c+16>>2]=b;break a}H[e+80>>2]=b;Ba(d,1,12133,e+80|0);f=0}na=e+128|0;return f|0}function nb(a,b,c,d){var e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0;f=H[a+8>>2];e=f+H[a+4>>2]|0;a:{if(!H[a+12>>2]){if((e|0)<2|(d|0)<=0){break a}q=e&2147483644;m=e&3;r=e&1;s=f+1|0;g=H[a>>2];o=g+(e<<2)|0;t=e-4>>>1|0;a=e-1|0;u=g+(a<<2)|0;v=N(c,f)<<2;l=e>>>0<4;w=N(a>>>1|0,c)<<2;while(1){f=H[b+v>>2];e=H[b>>2]-(f+1>>1)|0;h=0;a=0;if(!l){while(1){j=a+1|0;x=H[(N(j,c)<<2)+b>>2];i=H[(N(a+s|0,c)<<2)+b>>2];p=g+(h<<2)|0;H[p>>2]=e;k=e;e=x-((f+i|0)+2>>2)|0;H[p+4>>2]=(k+e>>1)+f;h=h+2|0;k=(a|0)!=(t|0);f=i;a=j;if(k){continue}break}}H[g+(h<<2)>>2]=e;if(r){a=H[b+w>>2]-(f+1>>1)|0;H[u>>2]=a;e=a+e>>1;a=-8}else{a=-4}H[a+o>>2]=e+f;f=0;a=0;h=0;e=0;b:{if(!l){while(1){H[(N(a,c)<<2)+b>>2]=H[g+(a<<2)>>2];e=a|1;H[(N(e,c)<<2)+b>>2]=H[g+(e<<2)>>2];e=a|2;H[(N(e,c)<<2)+b>>2]=H[g+(e<<2)>>2];e=a|3;H[(N(e,c)<<2)+b>>2]=H[g+(e<<2)>>2];a=a+4|0;h=h+4|0;if((q|0)!=(h|0)){continue}break}e=a;if(!m){break b}}while(1){H[(N(c,e)<<2)+b>>2]=H[g+(e<<2)>>2];e=e+1|0;f=f+1|0;if((m|0)!=(f|0)){continue}break}}b=b+4|0;n=n+1|0;if((n|0)!=(d|0)){continue}break}break a}c:{switch(e-1|0){case 0:if((d|0)<=0){break a}a=d&3;if(d>>>0>=4){d=d&2147483644;c=0;while(1){H[b>>2]=H[b>>2]/2;H[b+4>>2]=H[b+4>>2]/2;H[b+8>>2]=H[b+8>>2]/2;H[b+12>>2]=H[b+12>>2]/2;b=b+16|0;c=c+4|0;if((d|0)!=(c|0)){continue}break}if(!a){break a}}c=0;while(1){H[b>>2]=H[b>>2]/2;b=b+4|0;c=c+1|0;if((a|0)!=(c|0)){continue}break};break a;case 1:if((d|0)<=0){break a}a=H[a>>2];e=0;f=N(c,f)<<2;while(1){i=b+f|0;j=H[b>>2]-(H[i>>2]+1>>1)|0;H[a+4>>2]=j;i=j+H[i>>2]|0;H[a>>2]=i;H[b>>2]=i;H[(c<<2)+b>>2]=H[a+4>>2];b=b+4|0;e=e+1|0;if((e|0)!=(d|0)){continue}break};break a;default:break c}}if((e|0)<3|(d|0)<=0){break a}q=e&2147483644;m=e&3;g=H[a>>2];r=(g+(e<<2)|0)-4|0;a=e-2|0;s=g+(a<<2)|0;o=e&1;i=!o;t=((e-i|0)-4>>>1|0)+1|0;u=N(c,f)<<2;v=a-i>>>0<2;w=N((e>>>1|0)-1|0,c)<<2;x=e-1>>>0<3;while(1){l=b+u|0;f=H[l+(c<<2)>>2];a=H[l>>2];e=H[b>>2]-((f+a|0)+2>>2)|0;H[g>>2]=e+a;h=1;a=1;if(!v){while(1){p=H[(N(a,c)<<2)+b>>2];j=a+1|0;i=H[l+(N(j,c)<<2)>>2];y=g+(h<<2)|0;H[y>>2]=e;k=e;e=p-((f+i|0)+2>>2)|0;H[y+4>>2]=(k+e>>1)+f;h=h+2|0;k=(a|0)!=(t|0);a=j;f=i;if(k){continue}break}}H[g+(h<<2)>>2]=e;d:{if(!o){a=H[b+w>>2]-(f+1>>1)|0;H[s>>2]=(e+a>>1)+f;break d}a=e+f|0}H[r>>2]=a;f=0;a=0;h=0;e=0;e:{if(!x){while(1){H[(N(a,c)<<2)+b>>2]=H[g+(a<<2)>>2];e=a|1;H[(N(e,c)<<2)+b>>2]=H[g+(e<<2)>>2];e=a|2;H[(N(e,c)<<2)+b>>2]=H[g+(e<<2)>>2];e=a|3;H[(N(e,c)<<2)+b>>2]=H[g+(e<<2)>>2];a=a+4|0;h=h+4|0;if((q|0)!=(h|0)){continue}break}e=a;if(!m){break e}}while(1){H[(N(c,e)<<2)+b>>2]=H[g+(e<<2)>>2];e=e+1|0;f=f+1|0;if((m|0)!=(f|0)){continue}break}}b=b+4|0;n=n+1|0;if((n|0)!=(d|0)){continue}break}}}function Xb(a,b,c,d,e,f,g){var h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0;a:{n=N(e,3);h=H[b>>2]>>>n|0;if(h&2097168){break a}h=h&495;if(!h){break a}o=a+28|0;l=o+(I[h+H[a+108>>2]|0]<<2)|0;H[a+104>>2]=l;k=H[l>>2];i=H[k>>2];h=H[a+4>>2]-i|0;H[a+4>>2]=h;j=H[a>>2];b:{if(j>>>16>>>0>>0){m=H[k+4>>2];H[a+4>>2]=i;h=h>>>0>>0;H[l>>2]=H[k+(h?8:12)>>2];k=h?m:!m;h=H[a+8>>2];while(1){c:{if(h){break c}h=H[a+16>>2];m=h+1|0;l=I[h+1|0];if(I[h|0]==255){if(l>>>0>=144){H[a+12>>2]=H[a+12>>2]+1;j=j+65280|0;h=8;break c}H[a+16>>2]=m;j=(l<<9)+j|0;h=7;break c}H[a+16>>2]=m;h=8;j=(l<<8)+j|0}h=h-1|0;H[a+8>>2]=h;j=j<<1;H[a>>2]=j;i=i<<1;H[a+4>>2]=i;if(i>>>0<32768){continue}break}h=i;break b}j=j-(i<<16)|0;H[a>>2]=j;if(!(h&32768)){m=H[k+4>>2];i=h>>>0>>0;H[l>>2]=H[k+(i?12:8)>>2];k=i?!m:m;i=H[a+8>>2];while(1){d:{if(i){break d}i=H[a+16>>2];m=i+1|0;l=I[i+1|0];if(I[i|0]==255){if(l>>>0>=144){H[a+12>>2]=H[a+12>>2]+1;j=j+65280|0;i=8;break d}H[a+16>>2]=m;j=(l<<9)+j|0;i=7;break d}H[a+16>>2]=m;i=8;j=(l<<8)+j|0}i=i-1|0;H[a+8>>2]=i;j=j<<1;H[a>>2]=j;h=h<<1;H[a+4>>2]=h;if(h>>>0<32768){continue}break}break b}k=H[k+4>>2]}e:{if(!k){break e}p=b-4|0;i=H[b>>2];k=H[b+4>>2]>>>n+17&4|(H[p>>2]>>>n+19&1|(i>>>n+16&64|i>>>n&170|i>>>(e?n+12|0:14)&16));m=o+(I[k+24384|0]<<2)|0;H[a+104>>2]=m;l=H[m>>2];i=H[l>>2];h=h-i|0;H[a+4>>2]=h;o=I[k+24640|0];f:{if(j>>>16>>>0>>0){k=H[l+4>>2];H[a+4>>2]=i;h=h>>>0>>0;H[m>>2]=H[l+(h?8:12)>>2];l=h?k:!k;h=H[a+8>>2];while(1){g:{if(h){break g}h=H[a+16>>2];m=h+1|0;k=I[h+1|0];if(I[h|0]==255){if(k>>>0>=144){H[a+12>>2]=H[a+12>>2]+1;j=j+65280|0;h=8;break g}H[a+16>>2]=m;j=(k<<9)+j|0;h=7;break g}H[a+16>>2]=m;h=8;j=(k<<8)+j|0}h=h-1|0;H[a+8>>2]=h;j=j<<1;H[a>>2]=j;i=i<<1;H[a+4>>2]=i;if(i>>>0<32768){continue}break}break f}k=j-(i<<16)|0;H[a>>2]=k;if(!(h&32768)){j=H[l+4>>2];i=h>>>0>>0;H[m>>2]=H[l+(i?12:8)>>2];l=i?!j:j;j=H[a+8>>2];while(1){h:{if(j){break h}j=H[a+16>>2];m=j+1|0;i=I[j+1|0];if(I[j|0]==255){if(i>>>0>=144){H[a+12>>2]=H[a+12>>2]+1;k=k+65280|0;j=8;break h}H[a+16>>2]=m;k=(i<<9)+k|0;j=7;break h}H[a+16>>2]=m;j=8;k=(i<<8)+k|0}j=j-1|0;H[a+8>>2]=j;k=k<<1;H[a>>2]=k;h=h<<1;H[a+4>>2]=h;if(h>>>0<32768){continue}break}break f}l=H[l+4>>2]}H[c>>2]=(l|0)==(o|0)?d:0-d|0;H[p>>2]=H[p>>2]|32<>2]=H[b>>2]|(c<<19|16)<>2]=H[b+4>>2]|8<>2]=H[a+4>>2]|32768;H[a>>2]=H[a>>2]|c<<31|65536;a=a-4|0;H[a>>2]=H[a>>2]|131072}if((e|0)!=3){break e}a=(f<<2)+b|0;H[a+4>>2]=H[a+4>>2]|1;H[a>>2]=H[a>>2]|c<<18|2;a=a-4|0;H[a>>2]=H[a>>2]|4}H[b>>2]=H[b>>2]|2097152<>2]>>>m|0;if(g&2097168){break a}n=a+28|0;k=n+(I[H[a+108>>2]+(g&495)|0]<<2)|0;H[a+104>>2]=k;j=H[k>>2];h=H[j>>2];g=H[a+4>>2]-h|0;H[a+4>>2]=g;i=H[a>>2];b:{if(i>>>16>>>0>>0){l=H[j+4>>2];H[a+4>>2]=h;g=g>>>0>>0;H[k>>2]=H[j+(g?8:12)>>2];j=g?l:!l;g=H[a+8>>2];while(1){c:{if(g){break c}g=H[a+16>>2];l=g+1|0;k=I[g+1|0];if(I[g|0]==255){if(k>>>0>=144){H[a+12>>2]=H[a+12>>2]+1;i=i+65280|0;g=8;break c}H[a+16>>2]=l;i=(k<<9)+i|0;g=7;break c}H[a+16>>2]=l;g=8;i=(k<<8)+i|0}g=g-1|0;H[a+8>>2]=g;i=i<<1;H[a>>2]=i;h=h<<1;H[a+4>>2]=h;if(h>>>0<32768){continue}break}g=h;break b}i=i-(h<<16)|0;H[a>>2]=i;if(!(g&32768)){l=H[j+4>>2];h=g>>>0>>0;H[k>>2]=H[j+(h?12:8)>>2];j=h?!l:l;h=H[a+8>>2];while(1){d:{if(h){break d}h=H[a+16>>2];l=h+1|0;k=I[h+1|0];if(I[h|0]==255){if(k>>>0>=144){H[a+12>>2]=H[a+12>>2]+1;i=i+65280|0;h=8;break d}H[a+16>>2]=l;i=(k<<9)+i|0;h=7;break d}H[a+16>>2]=l;h=8;i=(k<<8)+i|0}h=h-1|0;H[a+8>>2]=h;i=i<<1;H[a>>2]=i;g=g<<1;H[a+4>>2]=g;if(g>>>0<32768){continue}break}break b}j=H[j+4>>2]}if(!j){break a}j=n;n=b-4|0;h=H[b>>2];o=H[b+4>>2]>>>m+17&4|(H[n>>2]>>>m+19&1|(h>>>m+16&64|h>>>m&170|h>>>(e?m+12|0:14)&16));l=j+(I[o+24384|0]<<2)|0;H[a+104>>2]=l;k=H[l>>2];h=H[k>>2];g=g-h|0;H[a+4>>2]=g;e:{if(i>>>16>>>0>>0){j=H[k+4>>2];H[a+4>>2]=h;g=g>>>0>>0;H[l>>2]=H[k+(g?8:12)>>2];k=g?j:!j;g=H[a+8>>2];while(1){f:{if(g){break f}g=H[a+16>>2];l=g+1|0;j=I[g+1|0];if(I[g|0]==255){if(j>>>0>=144){H[a+12>>2]=H[a+12>>2]+1;i=i+65280|0;g=8;break f}H[a+16>>2]=l;i=(j<<9)+i|0;g=7;break f}H[a+16>>2]=l;g=8;i=(j<<8)+i|0}g=g-1|0;H[a+8>>2]=g;i=i<<1;H[a>>2]=i;h=h<<1;H[a+4>>2]=h;if(h>>>0<32768){continue}break}break e}j=i-(h<<16)|0;H[a>>2]=j;if(!(g&32768)){i=H[k+4>>2];h=g>>>0>>0;H[l>>2]=H[k+(h?12:8)>>2];k=h?!i:i;i=H[a+8>>2];while(1){g:{if(i){break g}i=H[a+16>>2];l=i+1|0;h=I[i+1|0];if(I[i|0]==255){if(h>>>0>=144){H[a+12>>2]=H[a+12>>2]+1;j=j+65280|0;i=8;break g}H[a+16>>2]=l;j=(h<<9)+j|0;i=7;break g}H[a+16>>2]=l;i=8;j=(h<<8)+j|0}i=i-1|0;H[a+8>>2]=i;j=j<<1;H[a>>2]=j;g=g<<1;H[a+4>>2]=g;if(g>>>0<32768){continue}break}break e}k=H[k+4>>2]}g=c;c=I[o+24640|0];H[g>>2]=(c|0)==(k|0)?d:0-d|0;H[n>>2]=H[n>>2]|32<>2]=H[b>>2]|(d<<19|16)<>2]=H[b+4>>2]|8<>2]<<2)+b|0;H[c+4>>2]=H[c+4>>2]|32768;H[c>>2]=H[c>>2]|d<<31|65536;c=c-4|0;H[c>>2]=H[c>>2]|131072}if((e|0)!=3){break a}a=(H[a+124>>2]<<2)+b|0;H[a+4>>2]=H[a+4>>2]|4;H[a+12>>2]=H[a+12>>2]|1;H[a+8>>2]=H[a+8>>2]|d<<18|2}}function Kd(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0;e=na-112|0;na=e;j=1024;a:{b:{h=Ea(1,1024);if(h){l=e+92|0;k=e+108|0;while(1){c:{d:{d=e+104|0;e:{if((Ja(b,d,8,c)|0)!=8){break e}Da(d,e+88|0,4);Da(k,l,4);f=8;f:{g:{h:{i:{switch(H[e+88>>2]){case 0:d=Qa(b);g=qa;if((g|0)<0){g=1}else{g=d>>>0<4294967288&(g|0)<=0}if(g){break h}Ba(c,1,8449,0);break e;case 1:break i;default:break f}}d=e+104|0;if((Ja(b,d,8,c)|0)!=8){break e}Da(d,e+100|0,4);if(!H[e+100>>2]){break g}Ba(c,1,8449,0);break e}H[e+88>>2]=d+8;break f}Da(k,e+88|0,4);f=16}d=H[e+92>>2];if((d|0)==1785737827){b=H[a+100>>2];if(b&4){H[a+100>>2]=b|8;break e}Ba(c,1,5702,0);Ca(h);a=0;break a}i=H[e+88>>2];if(!i){Ba(c,1,3268,0);Ca(h);a=0;break a}if(f>>>0>i>>>0){H[e+4>>2]=d;H[e>>2]=i;Ba(c,1,13933,e);break b}j:{k:{l:{m:{n:{o:{p:{q:{r:{s:{if((d|0)<=1668246641){if((d|0)==1651532643){break r}if((d|0)==1667523942){break p}if((d|0)!=1668112752){break s}g=25296;break n}if((d|0)<=1783635999){if((d|0)==1668246642){break o}g=25264;if((d|0)==1768449138){break n}if((d|0)!=1718909296){break s}g=25240;break l}if((d|0)==1885564018){break q}if((d|0)==1783636e3){break m}g=25248;if((d|0)==1785737832){break l}}d=H[a+100>>2];if(d&1){break j}Ba(c,1,2062,0);Ca(h);a=0;break a}g=25280;break n}g=25288;break n}g=25304;break n}g=25272}H[e+76>>2]=d&255;H[e+64>>2]=d>>>24;H[e+72>>2]=d>>>8&255;H[e+68>>2]=d>>>16&255;Ba(c,2,2011,e- -64|0);f=i-f|0;if(I[a+100|0]&4){break k}d=H[e+92>>2];H[e+48>>2]=d>>>24;H[e+60>>2]=d&255;H[e+52>>2]=d>>>16&255;H[e+56>>2]=d>>>8&255;Ba(c,2,6771,e+48|0);H[a+100>>2]=H[a+100>>2]|2147483647;d=rb(b,f,c);if(!qa&(d|0)==(f|0)){continue}Ba(c,1,3748,0);Ca(h);a=0;break a}g=25232}f=i-f|0}d=f;f=Qa(b);i=qa;if((i|0)<0){f=1}else{f=(i|0)<=0&d>>>0>f>>>0}if(f){f=H[e+88>>2];a=H[e+92>>2];m=e,n=Qa(b),H[m+40>>2]=n;H[e+36>>2]=d;H[e+32>>2]=a&255;H[e+20>>2]=a>>>24;H[e+16>>2]=f;H[e+28>>2]=a>>>8&255;H[e+24>>2]=a>>>16&255;Ba(c,1,15680,e+16|0);break b}if(d>>>0<=j>>>0){f=h;break c}j=d;f=Ha(h,d);if(f){break c}Ca(h);Ba(c,1,2193,0);a=0;break a}if(!(d&2)){Ba(c,1,2132,0);Ca(h);a=0;break a}H[a+100>>2]=d|2147483647;d=i-f|0;f=rb(b,d,c);if(!qa&(d|0)==(f|0)){continue}if(!(I[a+100|0]&8)){break d}Ba(c,2,3748,0)}Ca(h);a=1;break a}Ba(c,1,3748,0);Ca(h);a=0;break a}if((Ja(b,f,d,c)|0)!=(d|0)){Ba(c,1,3798,0);Ca(f);a=0;break a}h=f;if(ra[H[g+4>>2]](a,f,d,c)|0){continue}break}Ca(f);a=0;break a}Ba(c,1,4923,0);a=0;break a}Ca(h);a=0}na=e+112|0;return a|0}function Yd(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0;f=na-16|0;na=f;if(H[a+8>>2]==16){i=H[a+180>>2]+N(H[a+228>>2],5644)|0}else{i=H[a+12>>2]}a:{if(c>>>0<=1){Ba(d,1,4721,0);a=0;break a}Da(b,f+12|0,2);if(H[f+12>>2]){Ba(d,2,5897,0);a=1;break a}if(c>>>0<=6){Ba(d,1,4721,0);a=0;break a}Da(b+2|0,f+8|0,1);e=H[i+5628>>2];h=H[i+5632>>2];b:{c:{if(!h){break c}j=H[f+8>>2];a=e;while(1){if((j|0)!=H[a>>2]){a=a+20|0;g=g+1|0;if((h|0)!=(g|0)){continue}break c}break}j=0;if((g|0)!=(h|0)){break b}}if(H[i+5636>>2]==(h|0)){a=h+10|0;H[i+5636>>2]=a;a=Ha(e,N(a,20));if(!a){Ca(H[i+5628>>2]);H[i+5636>>2]=0;H[i+5628>>2]=0;H[i+5632>>2]=0;Ba(d,1,4747,0);a=0;break a}H[i+5628>>2]=a;e=H[i+5632>>2];j=N(H[i+5636>>2]-e|0,20);if(j){y(a+N(e,20)|0,0,j)}h=H[i+5632>>2];e=H[i+5628>>2]}a=e+N(h,20)|0;j=1}H[a>>2]=H[f+8>>2];Da(b+3|0,f+12|0,2);if(H[f+12>>2]){Ba(d,2,5897,0);a=1;break a}Da(b+5|0,f+4|0,2);e=H[f+4>>2];if(e>>>0>=2){Ba(d,2,3130,0);a=1;break a}h=c-7|0;if(e){c=b+7|0;while(1){if(h>>>0<=2){Ba(d,1,4721,0);a=0;break a}Da(c,f+12|0,1);if(H[f+12>>2]!=1){Ba(d,2,5579,0);a=1;break a}Da(c+1|0,f,2);e=H[f>>2];b=e&32767;H[a+4>>2]=b;h=h-3|0;e=(e>>>15|0)+1|0;k=N(e,b)+2|0;if(h>>>0>>0){Ba(d,1,4721,0);a=0;break a}c=c+3|0;g=0;if(b){while(1){Da(c,f+12|0,e);if(H[f+12>>2]!=(g|0)){Ba(d,2,6259,0);a=1;break a}c=c+e|0;g=g+1|0;if(g>>>0>2]){continue}break}}Da(c,f,2);e=H[f>>2];b=e&32767;H[f>>2]=b;if((b|0)!=H[a+4>>2]){Ba(d,2,3306,0);a=1;break a}e=(e>>>15|0)+1|0;l=N(e,b)+3|0;k=h-k|0;if(l>>>0>k>>>0){Ba(d,1,4721,0);a=0;break a}c=c+2|0;g=0;if(b){while(1){Da(c,f+12|0,e);if(H[f+12>>2]!=(g|0)){Ba(d,2,6259,0);a=1;break a}c=c+e|0;g=g+1|0;if(g>>>0>2]){continue}break}}Da(c,f+12|0,3);e=H[f+12>>2];H[a+8>>2]=0;H[a+12>>2]=0;F[a+16|0]=!(e&65536)|I[a+16|0]&254;h=e&255;H[f+8>>2]=h;d:{if(!h){break d}m=H[i+5620>>2];if(m){g=H[i+5616>>2];b=0;while(1){if((h|0)==H[g+8>>2]){H[a+8>>2]=g;break d}g=g+20|0;b=b+1|0;if((m|0)!=(b|0)){continue}break}}Ba(d,1,4721,0);a=0;break a}e=e>>>8&255;H[f+8>>2]=e;e:{if(!e){break e}h=H[i+5620>>2];if(h){g=H[i+5616>>2];b=0;while(1){if((e|0)==H[g+8>>2]){H[a+12>>2]=g;break e}g=g+20|0;b=b+1|0;if((h|0)!=(b|0)){continue}break}}Ba(d,1,4721,0);a=0;break a}h=k-l|0;c=c+3|0;n=n+1|0;if(n>>>0>2]){continue}break}}if(h){Ba(d,1,4721,0);a=0;break a}a=1;if(!j){break a}H[i+5632>>2]=H[i+5632>>2]+1;a=1}na=f+16|0;return a|0}function _c(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0;if(K[a+44>>2]>=8){i=H[a+40>>2];l=8;while(1){k=H[a+12>>2]<<5;e=H[a>>2];g=H[a+36>>2];b=H[a+16>>2];h=H[a+20>>2];a:{if(b>>>0>=h>>>0){break a}j=e+k|0;d=b+1|0;if(h-b&1){c=j+(b<<6)|0;b=(N(b,g)<<2)+i|0;f=H[b+28>>2];H[c+24>>2]=H[b+24>>2];H[c+28>>2]=f;f=H[b+20>>2];H[c+16>>2]=H[b+16>>2];H[c+20>>2]=f;f=H[b+12>>2];H[c+8>>2]=H[b+8>>2];H[c+12>>2]=f;f=H[b+4>>2];H[c>>2]=H[b>>2];H[c+4>>2]=f;b=d}if((d|0)==(h|0)){break a}while(1){d=(N(b,g)<<2)+i|0;f=H[d+28>>2];c=j+(b<<6)|0;H[c+24>>2]=H[d+24>>2];H[c+28>>2]=f;f=H[d+20>>2];H[c+16>>2]=H[d+16>>2];H[c+20>>2]=f;f=H[d+12>>2];H[c+8>>2]=H[d+8>>2];H[c+12>>2]=f;f=H[d+4>>2];H[c>>2]=H[d>>2];H[c+4>>2]=f;d=b+1|0;c=j+(d<<6)|0;d=(N(d,g)<<2)+i|0;f=H[d+28>>2];H[c+24>>2]=H[d+24>>2];H[c+28>>2]=f;f=H[d+20>>2];H[c+16>>2]=H[d+16>>2];H[c+20>>2]=f;f=H[d+12>>2];H[c+8>>2]=H[d+8>>2];H[c+12>>2]=f;f=H[d+4>>2];H[c>>2]=H[d>>2];H[c+4>>2]=f;b=b+2|0;if((h|0)!=(b|0)){continue}break}}b=H[a+24>>2];h=H[a+28>>2];b:{if(b>>>0>=h>>>0){break b}j=(e-k|0)+32|0;k=(N(g,H[a+8>>2])<<2)+i|0;d=b+1|0;if(h-b&1){c=j+(b<<6)|0;b=k+(N(b,g)<<2)|0;e=H[b+28>>2];H[c+24>>2]=H[b+24>>2];H[c+28>>2]=e;e=H[b+20>>2];H[c+16>>2]=H[b+16>>2];H[c+20>>2]=e;e=H[b+12>>2];H[c+8>>2]=H[b+8>>2];H[c+12>>2]=e;e=H[b+4>>2];H[c>>2]=H[b>>2];H[c+4>>2]=e;b=d}if((d|0)==(h|0)){break b}while(1){d=k+(N(b,g)<<2)|0;e=H[d+28>>2];c=j+(b<<6)|0;H[c+24>>2]=H[d+24>>2];H[c+28>>2]=e;e=H[d+20>>2];H[c+16>>2]=H[d+16>>2];H[c+20>>2]=e;e=H[d+12>>2];H[c+8>>2]=H[d+8>>2];H[c+12>>2]=e;e=H[d+4>>2];H[c>>2]=H[d>>2];H[c+4>>2]=e;d=b+1|0;c=j+(d<<6)|0;d=k+(N(d,g)<<2)|0;e=H[d+28>>2];H[c+24>>2]=H[d+24>>2];H[c+28>>2]=e;e=H[d+20>>2];H[c+16>>2]=H[d+16>>2];H[c+20>>2]=e;e=H[d+12>>2];H[c+8>>2]=H[d+8>>2];H[c+12>>2]=e;e=H[d+4>>2];H[c>>2]=H[d>>2];H[c+4>>2]=e;b=b+2|0;if((h|0)!=(b|0)){continue}break}}Ta(a);b=0;if(H[a+32>>2]){while(1){d=H[a>>2]+(b<<5)|0;c=H[d+28>>2];g=(N(H[a+36>>2],b)<<2)+i|0;H[g+24>>2]=H[d+24>>2];H[g+28>>2]=c;c=H[d+20>>2];H[g+16>>2]=H[d+16>>2];H[g+20>>2]=c;c=H[d+12>>2];H[g+8>>2]=H[d+8>>2];H[g+12>>2]=c;c=H[d+4>>2];H[g>>2]=H[d>>2];H[g+4>>2]=c;b=b+1|0;if(b>>>0>2]){continue}break}}i=i+32|0;l=l+8|0;if(l>>>0<=K[a+44>>2]){continue}break}}Ca(H[a>>2]);Ca(a)}function ed(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0;d=b;b=H[b>>2]+7&-8;H[d>>2]=b+16;o=a;f=H[b>>2];a=H[b+4>>2];d=H[b+8>>2];c=H[b+12>>2];p=c;h=na-32|0;na=h;b=c&65535;e=c;c=d;d=e>>>16&32767;g=d;a:{if(d-15361>>>0<=2045){b=b<<4|c>>>28;d=c<<4|a>>>28;e=g-15360|0;a=a&268435455;b:{if((a|0)==134217728&(f|0)!=0|a>>>0>134217728){c=b;d=d+1|0;b=d?c:c+1|0;break b}if(f|(a|0)!=134217728){break b}a=d;d=d+(d&1)|0;b=a>>>0>d>>>0?b+1|0:b}c=b>>>0>1048575;f=c?0:d;a=c?0:b;b=0;c=c+e|0;b=c>>>0>>0?1:b;break a}if(!(!(c|f|(a|b))|((d|0)!=32767|(k|0)!=0))){e=b<<4|c>>>28;f=c<<4|a>>>28;a=e|524288;c=2047;b=0;break a}if(g>>>0>17406){f=0;a=0;c=2047;b=0;break a}e=!(d|k);l=e?15360:15361;k=l-g|0;if((k|0)>112){f=0;a=0;c=0;b=0;break a}d=c;b=e?b:b|65536;if((g|0)!=(l|0)){m=f;c=a;i=d;e=b;l=128-k|0;c:{if(l&64){g=f;e=l+-64|0;c=e&31;if((e&63)>>>0>=32){e=f<>>32-c|a<>>0>=32){g=i<>>32-j|e<>>0>=32){e=0;i=c>>>i|0}else{e=c>>>i|0;i=((1<>>i}i=n|i;e=e|g;n=m;j=l&31;if((l&63)>>>0>=32){g=m<>>32-j|c<>2]=m;H[h+20>>2]=c;H[h+24>>2]=i;H[h+28>>2]=e;m=(H[h+16>>2]|H[h+24>>2]|(H[h+20>>2]|H[h+28>>2]))!=0}d:{if(k&64){c=d;f=k+-64|0;a=f&31;if((f&63)>>>0>=32){e=0;f=b>>>a|0}else{e=b>>>a|0;f=((1<>>a}a=e;d=0;b=0;break d}if(!k){break d}g=d;c=64-k|0;e=c&31;if((c&63)>>>0>=32){c=d<>>32-e|b<>>0>=32){g=0;a=a>>>f|0}else{g=a>>>f|0;a=((1<>>f}f=i|a;a=c|g;e=d;d=k&31;if((k&63)>>>0>=32){c=0;d=b>>>d|0}else{c=b>>>d|0;d=((1<>>d}b=c}H[h>>2]=f;H[h+4>>2]=a;H[h+8>>2]=d;H[h+12>>2]=b;a=H[h+8>>2];d=H[h+4>>2];f=a<<4|d>>>28;a=H[h+12>>2]<<4|a>>>28;c=d&268435455;b=H[h>>2]|m;e:{if((c|0)==134217728&(b|0)!=0|c>>>0>134217728){f=f+1|0;a=f?a:a+1|0;break e}if(b|(c|0)!=134217728){break e}b=a;a=f;f=f+(f&1)|0;a=a>>>0>f>>>0?b+1|0:b}c=a>>>0>1048575;a=c?a^1048576:a;b=0}na=h+32|0;u(0,f|0);u(1,a|(p&-2147483648|c<<20));q=o,r=+w(),M[q>>3]=r}function Hc(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0;q=H[a+24>>2];if(!H[q+16>>2]){return 1}r=H[q+24>>2];o=H[H[H[a+20>>2]>>2]+20>>2];while(1){d=H[r+36>>2];H[b+36>>2]=d;f=H[o+28>>2];c=f+N(d,152)|0;t=H[a+64>>2];a:{if(t){d=f+N(H[o+24>>2],152)|0;p=H[d-144>>2]-H[d-152>>2]|0;f=c+12|0;e=c+4|0;d=H[c+8>>2];g=H[c>>2];c=36;break a}f=c+148|0;e=c+140|0;d=H[c+144>>2];g=H[c+136>>2];p=d-g|0;c=52}u=H[c+o>>2];b:{c:{if(!u){break c}k=H[e>>2];l=H[f>>2];f=d-g|0;e=H[b+40>>2];c=e&31;if((e&63)>>>0>=32){c=-1<>>32-c}m=h^-1;j=H[b+20>>2];h=m+j|0;i=c^-1;c=i;j=h>>>0>>0?c+1|0:c;c=e&31;if((e&63)>>>0>=32){h=j>>>c|0}else{h=((1<>>c}c=H[b+16>>2];j=c+m|0;m=c>>>0>j>>>0?i+1|0:i;i=e&31;c=H[b+8>>2];if((e&63)>>>0>=32){i=m>>>i|0}else{i=((1<>>i}e=c+i|0;d:{if(g>>>0>i>>>0){m=g-i|0;i=0;if(e>>>0>=d>>>0){j=0;d=f;break d}d=e-g|0;j=f-d|0;break d}i=i-g|0;if(e>>>0>=d>>>0){d=f-i|0;m=0;j=0;break d}j=d-e|0;m=0;d=c}f=l-k|0;e=H[b+12>>2];g=e+h|0;e:{if(h>>>0>>0){s=k-h|0;h=0;n=0;if(g>>>0>=l>>>0){break e}n=f;f=g-k|0;n=n-f|0;break e}h=h-k|0;if(g>>>0>=l>>>0){f=f-h|0;s=0;n=0;break e}s=0;f=e;n=l-g|0}k=n;g=0;if((h|i|(j|k)|(d|f))<0){break b}k=N(h,p)+i|0;l=H[b+44>>2];h=N(c,s)+m|0;f:{g:{if(!(k|l|(h|(c|0)!=(p|0))|(c|0)!=(d|0))){if((e|0)!=(f|0)){break g}d=(t?36:52)+o|0;H[b+44>>2]=H[d>>2];H[d>>2]=0;break c}if(l){break f}}re(e,0,c);if(qa|!e){break b}c=N(c,e);if(c>>>0>1073741823){break b}c=Ia(c<<2);H[b+44>>2]=c;if(!c){break b}e=H[b+8>>2];g=H[b+12>>2];if((e|0)==(d|0)&(g|0)==(f|0)){break f}e=N(e,g)<<2;if(!e){break f}y(c,0,e)}if(!f){break c}d=d<<2;g=H[b+44>>2]+(h<<2)|0;c=(k<<2)+u|0;if((f|0)!=1){k=f&1;l=f&2147483646;f=0;while(1){h=!d;if(!h){B(g,c,d)}i=p<<2;c=i+c|0;e=(H[b+8>>2]<<2)+g|0;if(!h){B(e,c,d)}c=c+i|0;g=e+(H[b+8>>2]<<2)|0;f=f+2|0;if((l|0)!=(f|0)){continue}break}if(!k){break c}}if(!d){break c}B(g,c,d)}o=o+76|0;r=r+52|0;b=b+52|0;g=1;v=v+1|0;if(v>>>0>2]){continue}}break}return g}function wb(a){a=a|0;var b=0,c=0,d=0,e=0,f=0,g=0;if(a){a:{if(H[a>>2]){b=H[a+12>>2];if(b){jb(b);Ca(H[a+12>>2]);H[a+12>>2]=0}b=H[a+16>>2];if(b){Ca(b);H[a+16>>2]=0;H[a+20>>2]=0}Ca(H[a+64>>2]);H[a+60>>2]=0;H[a+64>>2]=0;Ca(H[a+72>>2]);H[a+72>>2]=0;Ca(H[a+88>>2]);H[a+88>>2]=0;break a}b=H[a+44>>2];if(b){Ca(b);H[a+44>>2]=0}b=H[a+32>>2];if(b){Ca(b);H[a+32>>2]=0;H[a+36>>2]=0}b=H[a+52>>2];if(!b){break a}Ca(b);H[a+52>>2]=0;H[a+56>>2]=0}Tb(H[a+232>>2]);b=H[a+180>>2];if(b){e=N(H[a+128>>2],H[a+132>>2]);if(e){while(1){jb(b);b=b+5644|0;c=c+1|0;if((e|0)!=(c|0)){continue}break}b=H[a+180>>2]}Ca(b);H[a+180>>2]=0}b=H[a+140>>2];if(b){c=H[a+136>>2];if(c){b=0;while(1){e=H[H[a+140>>2]+(b<<3)>>2];if(e){Ca(e);c=H[a+136>>2]}b=b+1|0;if(c>>>0>b>>>0){continue}break}b=H[a+140>>2]}H[a+136>>2]=0;Ca(b);H[a+140>>2]=0}Ca(H[a+160>>2]);H[a+144>>2]=0;H[a+160>>2]=0;Ca(H[a+124>>2]);H[a+124>>2]=0;if(!(I[a+212|0]&2)){Ca(H[a+192>>2])}y(a+104|0,0,112);pb(H[a+216>>2]);H[a+216>>2]=0;pb(H[a+220>>2]);H[a+216>>2]=0;d=H[a+224>>2];if(d){b=H[d+28>>2];if(b){Ca(b);H[d+28>>2]=0}c=H[d+40>>2];if(c){if(H[d+36>>2]){while(1){e=N(g,40);b=H[(e+c|0)+36>>2];if(b){Ca(b);c=H[d+40>>2];H[(e+c|0)+36>>2]=0}b=H[(c+e|0)+16>>2];if(b){Ca(b);c=H[d+40>>2];H[(e+c|0)+16>>2]=0}b=H[(c+e|0)+24>>2];if(b){Ca(b);c=H[d+40>>2];H[(e+c|0)+24>>2]=0}g=g+1|0;if(g>>>0>2]){continue}break}}Ca(c);H[d+40>>2]=0}Ca(d)}H[a+224>>2]=0;Ua(H[a+96>>2]);H[a+96>>2]=0;Ua(H[a+100>>2]);H[a+100>>2]=0;f=H[a+236>>2];if(f){b:{if(!H[f+8>>2]){break b}if(H[f+12>>2]){H[f+40>>2]=0;while(1){if(H[f+24>>2]>0){continue}break}}H[f+16>>2]=1;Ca(H[f>>2]);c=H[f+28>>2];if(!c){break b}while(1){b=H[c+4>>2];Ca(c);H[f+28>>2]=b;c=b;if(b){continue}break}}d=H[f+36>>2];if(d){g=H[d+4>>2];if((g|0)>0){b=0;while(1){e=H[d>>2]+N(b,12)|0;c=H[e+8>>2];if(c){ra[c|0](H[e+4>>2]);g=H[d+4>>2]}b=b+1|0;if((g|0)>(b|0)){continue}break}}Ca(H[d>>2]);Ca(d)}Ca(f)}H[a+236>>2]=0;Ca(a)}}function ec(a,b,c,d){var e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0;k=na-256|0;na=k;a:{if(!a){a=0;break a}if(!(H[a>>2]==(b|0)&H[a+4>>2]==(c|0))){H[a+4>>2]=c;H[a>>2]=b;H[k>>2]=c;H[k+128>>2]=b;e=c;g=b;while(1){o=h;h=h+1|0;j=h<<2;l=(e+1|0)/2|0;H[j+k>>2]=l;n=j+(k+128|0)|0;j=(g+1|0)/2|0;H[n>>2]=j;i=N(e,g);f=i+f|0;e=l;g=j;if(i>>>0>1){continue}break}H[a+8>>2]=f;b:{c:{d:{if(!f){b=H[a+12>>2];if(!b){break d}Ca(b);H[a+12>>2]=0;break d}f=f<<4;if(f>>>0<=K[a+16>>2]){break b}b=Ha(H[a+12>>2],f);if(b){break c}Ba(d,1,6451,0);b=H[a+12>>2];if(!b){break d}Ca(b);H[a+12>>2]=0}Ca(a);a=0;break a}H[a+12>>2]=b;c=H[a+16>>2];d=f-c|0;if(d){y(b+c|0,0,d)}H[a+16>>2]=f;c=H[a+4>>2];b=H[a>>2]}g=H[a+12>>2];if(o){l=0;e=(N(b,c)<<4)+g|0;f=e;while(1){b=l<<2;h=H[b+k>>2];e:{if((h|0)<=0){break e}j=h-1|0;d=0;f:{g:{b=H[b+(k+128|0)>>2];if((b|0)<=0){i=h&3;if(h>>>0>=4){break g}h=0;break f}while(1){c=f;f=b;while(1){h:{H[g>>2]=e;if((f|0)==1){g=g+16|0;e=e+16|0;break h}H[g+16>>2]=e;e=e+16|0;g=g+32|0;i=(f|0)>2;f=f-2|0;if(i){continue}}break}i=((d|0)==(j|0)|d)&1;f=i?e:c+(b<<4)|0;e=i?e:c;d=d+1|0;if((h|0)!=(d|0)){continue}break}break e}m=h&2147483644;h=0;c=0;while(1){n=(h|0)==(j|0);h=h+4|0;e=n?e:f;f=e;c=c+4|0;if((m|0)!=(c|0)){continue}break}if(i){break f}break e}while(1){c=f;m=((h|0)==(j|0)|h)&1;f=m?e:c+(b<<4)|0;e=m?e:c;h=h+1|0;d=d+1|0;if((i|0)!=(d|0)){continue}break}}l=l+1|0;if((o|0)!=(l|0)){continue}break}}H[g>>2]=0}b=H[a+8>>2];if(!b){break a}c=b&3;e=H[a+12>>2];if(b>>>0>=4){b=b&-4;g=0;while(1){H[e+60>>2]=0;H[e+52>>2]=999;H[e+56>>2]=0;H[e+44>>2]=0;H[e+36>>2]=999;H[e+40>>2]=0;H[e+28>>2]=0;H[e+20>>2]=999;H[e+24>>2]=0;H[e+12>>2]=0;H[e+4>>2]=999;H[e+8>>2]=0;e=e- -64|0;g=g+4|0;if((b|0)!=(g|0)){continue}break}if(!c){break a}}g=0;while(1){H[e+12>>2]=0;H[e+4>>2]=999;H[e+8>>2]=0;e=e+16|0;g=g+1|0;if((c|0)!=(g|0)){continue}break}}na=k+256|0;return a}function Xd(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0,k=0;g=na-16|0;na=g;if(H[a+8>>2]==16){h=H[a+180>>2]+N(H[a+228>>2],5644)|0}else{h=H[a+12>>2]}a:{if(!c){Ba(d,1,4259,0);break a}i=H[a+96>>2];e=1;Da(b,g+8|0,1);f=H[g+8>>2];if(f>>>0>=2){Ba(d,2,9792,0);break a}if((f+1|0)!=(c|0)){e=0;Ba(d,2,4259,0);break a}c=H[i+16>>2];b:{if(!c){break b}d=c&7;e=H[h+5584>>2];if(c>>>0>=8){i=c&-8;c=0;while(1){H[e+8636>>2]=0;H[e+7556>>2]=0;H[e+6476>>2]=0;H[e+5396>>2]=0;H[e+4316>>2]=0;H[e+3236>>2]=0;H[e+2156>>2]=0;H[e+1076>>2]=0;e=e+8640|0;c=c+8|0;if((i|0)!=(c|0)){continue}break}if(!d){break b}}c=0;while(1){H[e+1076>>2]=0;e=e+1080|0;c=c+1|0;if((d|0)!=(c|0)){continue}break}}c=H[h+5608>>2];if(c){Ca(c);H[h+5608>>2]=0;f=H[g+8>>2]}if(!f){e=1;break a}i=0;while(1){b=b+1|0;Da(b,g+12|0,1);c:{if(!H[h+5632>>2]){break c}d=H[h+5628>>2];if(H[d>>2]!=H[g+12>>2]){break c}f=H[d+4>>2];j=H[a+96>>2];if((f|0)!=H[j+16>>2]){break c}c=H[d+8>>2];if(c){e=0;f=N(f,f);if(H[c+16>>2]!=(N(f,H[(H[c>>2]<<2)+24896>>2])|0)){break a}k=Fa(f<<2);H[h+5608>>2]=k;if(!k){break a}ra[H[(H[c>>2]<<2)+25200>>2]](H[c+12>>2],k,f)}c=H[d+12>>2];if(!c){break c}e=0;d=H[j+16>>2];if(H[c+16>>2]!=(N(d,H[(H[c>>2]<<2)+24896>>2])|0)){break a}f=Fa(d<<2);if(!f){break a}ra[H[(H[c>>2]<<2)+25216>>2]](H[c+12>>2],f,d);c=H[j+16>>2];d:{if(!c){break d}j=c&7;e=H[h+5584>>2];e:{if(c>>>0<8){c=f;break e}k=c&-8;d=0;c=f;while(1){H[e+1076>>2]=H[c>>2];H[e+2156>>2]=H[c+4>>2];H[e+3236>>2]=H[c+8>>2];H[e+4316>>2]=H[c+12>>2];H[e+5396>>2]=H[c+16>>2];H[e+6476>>2]=H[c+20>>2];H[e+7556>>2]=H[c+24>>2];H[e+8636>>2]=H[c+28>>2];e=e+8640|0;c=c+32|0;d=d+8|0;if((k|0)!=(d|0)){continue}break}if(!j){break d}}d=0;while(1){H[e+1076>>2]=H[c>>2];e=e+1080|0;c=c+4|0;d=d+1|0;if((j|0)!=(d|0)){continue}break}}Ca(f)}e=1;i=i+1|0;if(i>>>0>2]){continue}break}}na=g+16|0;return e|0}function xb(a,b,c,d,e,f,g,h){var i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0;m=H[H[a+24>>2]+24>>2]+N(b,52)|0;l=H[m+4>>2];k=l-1|0;o=H[a+60>>2];j=k+o|0;p=0-!l|0;i=p;r=H[H[H[a+20>>2]>>2]+20>>2]+N(b,76)|0;n=H[r+12>>2];i=ve(j,j>>>0>>0?i+1|0:i,l,0);q=i>>>0>n>>>0?n:i;j=H[m>>2];m=j-1|0;s=H[a+56>>2];n=m+s|0;o=0-!j|0;i=o;t=H[r+8>>2];i=ve(n,n>>>0>>0?i+1|0:i,j,0);n=i>>>0>t>>>0?t:i;i=p;t=H[r+4>>2];s=H[a+52>>2];k=s+k|0;i=ve(k,k>>>0>>0?i+1|0:i,l,0);k=i>>>0>>0?t:i;i=o;p=H[r>>2];l=m;m=H[a+48>>2];l=l+m|0;i=ve(l,l>>>0>>0?i+1|0:i,j,0);i=i>>>0

>>0?p:i;l=0;p=H[(H[H[a+32>>2]+5584>>2]+N(b,1080)|0)+20>>2];c=H[r+20>>2]+(c?0-c|0:-1)|0;a:{if(!c){a=n;l=i;b=k;break a}m=c-1|0;j=(d&1)<>>0>>0){a=c&31;l=i-j|0;if((c&63)>>>0>=32){i=-1<>>32-a;a=-1<>>0>>0?i+1|0:i;b=a;a=c&31;if((c&63)>>>0>=32){l=i>>>a|0}else{l=((1<>>a}}a=0;b=0;d=d>>>1<>>0>>0){b=c&31;o=k-d|0;if((c&63)>>>0>=32){i=-1<>>32-b;b=-1<>>0>>0?i+1|0:i;k=b;b=c&31;if((c&63)>>>0>=32){b=i>>>b|0}else{b=((1<>>b}}if(j>>>0>>0){a=c&31;k=n-j|0;if((c&63)>>>0>=32){i=-1<>>32-a;a=-1<>>0>>0?i+1|0:i;j=a;a=c&31;if((c&63)>>>0>=32){a=i>>>a|0}else{a=((1<>>a}}if(d>>>0>=q>>>0){q=0;break a}k=q-d|0;d=c&31;if((c&63)>>>0>=32){i=-1<>>32-d;d=-1<>>0>>0?i+1|0:i;j=d;d=c&31;if((c&63)>>>0>=32){q=i>>>d|0}else{q=((1<>>d}}c=(p|0)==1?2:3;d=c+a|0;d=(a>>>0>d>>>0?-1:d)>>>0>e>>>0;a=c+q|0;d=d&(a>>>0>>0?-1:a)>>>0>f>>>0;a=l-c|0;d=d&(a>>>0<=l>>>0?a:0)>>>0>>0;a=b-c|0;return d&(a>>>0<=b>>>0?a:0)>>>0>>0}function lb(a){var b=0,c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0;g=H[a+16>>2];if(g>>>0>=32){return H[a+8>>2]}b=H[a+20>>2];a:{if((b|0)>=4){d=H[a>>2];c=H[d-3>>2];e=b-4|0;H[a+20>>2]=e;H[a>>2]=d-4;break a}if((b|0)<=0){e=b;break a}f=H[a>>2];d=24;b:{if((b|0)==1){break b}l=b&1;k=b&2147483646;while(1){h=f-1|0;H[a>>2]=h;i=I[f|0];f=f-2|0;H[a>>2]=f;H[a+20>>2]=b-1;h=I[h|0];b=b-2|0;H[a+20>>2]=b;c=i<>2]=f-1;f=I[f|0];H[a+20>>2]=b-1;c=f<>2];k=c&255;H[a+24>>2]=k>>>0>143;b=b?(c&2130706432)==2130706432?7:8:8;h=b+(c>>>0<=2415919103?8:(c&8323072)==8323072?7:8)|0;f=c>>>16&255;i=h+(f>>>0<=143?8:(c&32512)==32512?7:8)|0;d=c>>>8&255;l=i+(g+(d>>>0<=143?8:(c&127)==127?7:8)|0)|0;H[a+16>>2]=l;j=H[a+12>>2];b=f<>>24|d<>>0>=32){d=b<>>32-c;b=b<>2];b=d|j;h=b;H[a+8>>2]=g;H[a+12>>2]=b;if(l>>>0<=31){c:{if((e|0)>=4){b=H[a>>2];c=H[b-3>>2];H[a+20>>2]=e-4;H[a>>2]=b-4;break c}if((e|0)<=0){c=0;break c}b=H[a>>2];d:{if((e|0)==1){d=24;c=0;break d}i=e&1;j=e&2147483646;d=24;c=0;f=0;while(1){m=b-1|0;H[a>>2]=m;n=I[b|0];b=b-2|0;H[a>>2]=b;H[a+20>>2]=e-1;m=I[m|0];e=e-2|0;H[a+20>>2]=e;c=n<>2]=b-1;b=I[b|0];H[a+20>>2]=e-1;c=b<>2]=e>>>0>143;k=k>>>0<=143?8:(c&2130706432)==2130706432?7:8;i=k+(c>>>0<=2415919103?8:(c&8323072)==8323072?7:8)|0;f=c>>>16&255;j=i+(f>>>0<=143?8:(c&32512)==32512?7:8)|0;d=c>>>8&255;H[a+16>>2]=j+(l+(d>>>0<=143?8:(c&127)==127?7:8)|0);b=a;a=f<>>24|d<>>0>=32){c=a<>>32-e;a=a<>2]=g;H[b+12>>2]=c|h}return g}function Sc(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0;j=H[a+96>>2];l=N(H[a+128>>2],H[a+132>>2]);a:{if(l){b=H[j+16>>2];m=N(b,1080);k=N(b,b)<<2;e=H[a+12>>2];b=H[a+180>>2];while(1){n=H[b+5584>>2];B(b,e,5644);H[b+5608>>2]=0;H[b+5588>>2]=-1;H[b+5168>>2]=0;H[b+5636>>2]=0;H[b+5616>>2]=0;H[b+5624>>2]=0;H[b+5628>>2]=0;H[b+5584>>2]=n;F[b+5640|0]=I[b+5640|0]&252;b:{if(!H[e+5608>>2]){break b}d=Fa(k);H[b+5608>>2]=d;if(!d){return 0}if(!k){break b}B(d,H[e+5608>>2],k)}d=N(H[e+5624>>2],20);f=Fa(d);H[b+5616>>2]=f;i=0;if(!f){break a}if(d){B(f,H[e+5616>>2],d)}g=H[e+5620>>2];if(g){d=H[e+5616>>2];f=H[b+5616>>2];h=0;while(1){if(H[d+12>>2]){g=Fa(H[d+16>>2]);H[f+12>>2]=g;if(!g){return 0}o=H[d+16>>2];if(o){B(g,H[d+12>>2],o)}g=H[e+5620>>2]}H[b+5624>>2]=H[b+5624>>2]+1;f=f+20|0;d=d+20|0;h=h+1|0;if(h>>>0>>0){continue}break}}d=N(H[e+5636>>2],20);f=Fa(d);H[b+5628>>2]=f;if(!f){break a}if(d){B(f,H[e+5628>>2],d)}i=H[e+5636>>2];H[b+5636>>2]=i;if(i){d=H[e+5628>>2];f=H[b+5628>>2];h=0;while(1){g=H[d+8>>2];if(g){H[f+8>>2]=H[b+5616>>2]+(g-H[e+5616>>2]|0)}g=H[d+12>>2];if(g){H[f+12>>2]=H[b+5616>>2]+(g-H[e+5616>>2]|0)}f=f+20|0;d=d+20|0;h=h+1|0;if((i|0)!=(h|0)){continue}break}}if(m){B(n,H[e+5584>>2],m)}b=b+5644|0;p=p+1|0;if((p|0)!=(l|0)){continue}break}}i=1;e=Ea(1,72);b=0;c:{if(!e){break c}F[e+40|0]=I[e+40|0]&254|1;d=Ea(1,4);H[e+20>>2]=d;b=e;if(d){break c}Ca(b);b=0}H[a+232>>2]=b;if(!b){return 0}f=H[a+236>>2];e=0;H[b+28>>2]=a+104;H[b+24>>2]=j;d=Ea(1,848);H[H[b+20>>2]>>2]=d;d:{if(!d){break d}d=Ea(H[j+16>>2],76);h=H[H[b+20>>2]>>2];H[h+20>>2]=d;if(!d){break d}H[h+16>>2]=H[j+16>>2];e=H[a+188>>2];H[b+44>>2]=f;H[b>>2]=e;e=1}if(e){break a}Tb(H[a+232>>2]);i=0;H[a+232>>2]=0;Ba(c,1,3668,0)}return i|0}function pe(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0;e=na-80|0;na=e;H[e+76>>2]=1;j=H[a+44>>2];d=H[H[a+224>>2]+40>>2];a:{b:{if(!d|!H[d+16>>2]){break b}c:{d=d+N(j,40)|0;if(!H[d+4>>2]){d=H[a+52>>2];f=H[a+48>>2]+2|0;d=f>>>0<2?d+1|0:d;if(bb(b,f,d,c)){break c}d=0;Ba(c,1,5440,0);break a}d=H[d+16>>2];if(!bb(b,H[d>>2],H[d+4>>2],c)){Ba(c,1,5440,0);d=0;break a}if((Ja(b,H[a+16>>2],2,c)|0)!=2){Ba(c,1,2472,0);d=0;break a}Da(H[a+16>>2],e+72|0,2);if(H[e+72>>2]==65424){break c}Ba(c,1,4073,0);d=0;break a}if(H[a+8>>2]!=256){break b}H[a+8>>2]=8}f=N(H[a+132>>2],H[a+128>>2]);d:{if(!f){break d}i=f&7;h=H[a+180>>2];d=0;if(f>>>0>=8){k=f&-8;while(1){f=h+N(g,5644)|0;H[f+45096>>2]=-1;H[f+39452>>2]=-1;H[f+33808>>2]=-1;H[f+28164>>2]=-1;H[f+22520>>2]=-1;H[f+16876>>2]=-1;H[f+11232>>2]=-1;H[f+5588>>2]=-1;g=g+8|0;l=l+8|0;if((k|0)!=(l|0)){continue}break}if(!i){break d}}while(1){H[(h+N(g,5644)|0)+5588>>2]=-1;g=g+1|0;d=d+1|0;if((i|0)!=(d|0)){continue}break}}d=0;if(!cb(a,e+72|0,0,e+68|0,e- -64|0,e+60|0,e+56|0,e+52|0,e+76|0,b,c)){break a}i=j+1|0;while(1){e:{if(!H[e+76>>2]){break e}f=H[e+72>>2];if(!ib(a,f,0,0,b,c)){break a}h=H[a+128>>2];k=H[a+132>>2];g=f+1|0;H[e+32>>2]=g;H[e+36>>2]=N(h,k);Ba(c,4,11795,e+32|0);if(!Hc(H[a+232>>2],H[H[a+100>>2]+24>>2])){break a}d=H[a+180>>2]+N(f,5644)|0;h=H[d+5596>>2];if(h){Ca(h);H[d+5596>>2]=0;H[d+5600>>2]=0}H[e+16>>2]=g;Ba(c,4,16601,e+16|0);if((f|0)==(j|0)){d=H[a+224>>2];f=H[d+8>>2];d=H[d+12>>2];f=f+2|0;d=f>>>0<2?d+1|0:d;if(bb(b,f,d,c)){break e}d=0;Ba(c,1,5440,0);break a}H[e+4>>2]=i;H[e>>2]=g;Ba(c,2,13648,e);d=0;if(cb(a,e+72|0,0,e+68|0,e- -64|0,e+60|0,e+56|0,e+52|0,e+76|0,b,c)){continue}break a}break}d=Gc(a,c)}na=e+80|0;return d|0}function Ma(a){var b=0,c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0;h=H[a+16>>2];if(h>>>0>=32){return H[a+8>>2]}c=H[a+24>>2];a:{if((c|0)>=4){b=H[a>>2];d=H[b>>2];e=c-4|0;H[a+24>>2]=e;H[a>>2]=b+4;break a}d=H[a+28>>2]?-1:0;if((c|0)<=0){e=c;break a}b=H[a>>2];if((c|0)!=1){j=c&1;i=c&2147483646;while(1){e=b;H[a>>2]=b+1;k=I[b|0];b=b+2|0;H[a>>2]=b;H[a+24>>2]=c-1;e=I[e+1|0];c=c-2|0;H[a+24>>2]=c;d=((255<>2]=b+1;b=I[b|0];H[a+24>>2]=c-1;d=(255<>2];i=d>>>24|0;H[a+20>>2]=(i|0)==255;c=d>>>16&255;f=d>>>8&255;b=b?7:8;d=d&255;g=b+((d|0)==255?7:8)|0;k=((f|0)==255?7:8)+g|0;j=(h+((c|0)==255?7:8)|0)+k|0;H[a+16>>2]=j;l=H[a+12>>2];b=d|(f<>>0>=32){d=b<>>32-c;b=b<>2];b=d|l;k=b;H[a+8>>2]=h;H[a+12>>2]=b;if(j>>>0<=31){b:{if((e|0)>=4){b=H[a>>2];d=H[b>>2];H[a+24>>2]=e-4;H[a>>2]=b+4;break b}f=0;d=H[a+28>>2]?-1:0;if((e|0)<=0){break b}b=H[a>>2];if((e|0)!=1){l=e&1;m=e&2147483646;g=0;while(1){c=b;H[a>>2]=b+1;n=I[b|0];b=b+2|0;H[a>>2]=b;H[a+24>>2]=e-1;c=I[c+1|0];e=e-2|0;H[a+24>>2]=e;d=((255<>2]=b+1;b=I[b|0];H[a+24>>2]=e-1;d=(255<>>24|0;H[a+20>>2]=(e|0)==255;c=d>>>16&255;f=d>>>8&255;g=(i|0)==255?7:8;d=d&255;i=g+((d|0)==255?7:8)|0;l=((f|0)==255?7:8)+i|0;H[a+16>>2]=(((c|0)==255?7:8)+j|0)+l;b=a;a=d|(f<>>0>=32){c=a<>>32-e;a=a<>2]=h;H[b+12>>2]=c|k}return h}function Lc(a,b,c,d,e){var f=0,g=0,h=0,i=0,j=0,k=0;h=na-32|0;na=h;if(H[a+8>>2]==16){f=H[a+180>>2]+N(H[a+228>>2],5644)|0}else{f=H[a+12>>2]}a:{if(K[d>>2]<=4){a=0;Ba(e,1,2607,0);break a}f=H[f+5584>>2]+N(b,1080)|0;Da(c,f+4|0,1);g=H[f+4>>2]+1|0;H[f+4>>2]=g;if(g>>>0>=34){H[h+4>>2]=33;H[h>>2]=g;Ba(e,1,7635,h);a=0;break a}j=H[a+184>>2];if(j>>>0>=g>>>0){H[h+24>>2]=g;H[h+20>>2]=j;H[h+16>>2]=b;Ba(e,1,16423,h+16|0);H[a+8>>2]=H[a+8>>2]|32768;a=0;break a}Da(c+1|0,f+8|0,1);H[f+8>>2]=H[f+8>>2]+2;Da(c+2|0,f+12|0,1);a=H[f+12>>2]+2|0;H[f+12>>2]=a;b=H[f+8>>2];if(!(!(b>>>0>10|a>>>0>10)&a+b>>>0<13)){a=0;Ba(e,1,5468,0);break a}Da(c+3|0,f+16|0,1);if(I[f+16|0]&128){a=0;Ba(e,1,6564,0);break a}Da(c+4|0,f+20|0,1);if(K[f+20>>2]>=2){a=0;Ba(e,1,6499,0);break a}b=H[d>>2]-5|0;H[d>>2]=b;a=1;g=H[f+4>>2];if(!(F[f|0]&1)){if(!g){break a}j=g&3;d=f+944|0;e=f+812|0;b=0;c=0;if(g>>>0>=4){k=g&-4;g=0;while(1){f=c<<2;H[f+e>>2]=15;H[d+f>>2]=15;i=f|4;H[i+e>>2]=15;H[d+i>>2]=15;i=f|8;H[i+e>>2]=15;H[d+i>>2]=15;f=f|12;H[f+e>>2]=15;H[d+f>>2]=15;c=c+4|0;g=g+4|0;if((k|0)!=(g|0)){continue}break}if(!j){break a}}while(1){a=c<<2;H[a+e>>2]=15;H[a+d>>2]=15;a=1;c=c+1|0;b=b+1|0;if((j|0)!=(b|0)){continue}break}break a}if(b>>>0>=g>>>0){b:{if(!g){g=0;break b}Da(c+5|0,h+28|0,1);a=H[h+28>>2];H[f+944>>2]=a>>>4;H[f+812>>2]=a&15;g=H[f+4>>2];if(g>>>0>=2){j=f+944|0;k=f+812|0;a=c+6|0;c=1;while(1){Da(a,h+28|0,1);c:{b=H[h+28>>2];if(b>>>0>=16){g=b&15;if(g){break c}}a=0;Ba(e,1,6025,0);break a}i=c<<2;H[i+k>>2]=g;H[j+i>>2]=b>>>4;a=a+1|0;c=c+1|0;g=H[f+4>>2];if(c>>>0>>0){continue}break}}b=H[d>>2]}H[d>>2]=b-g;a=1;break a}a=0;Ba(e,1,2607,0)}na=h+32|0;return a}function gc(a,b,c){var d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0;j=na-256|0;na=j;g=Ea(1,20);a:{if(!g){Ba(c,1,6413,0);g=0;break a}H[g+4>>2]=b;H[g>>2]=a;H[j>>2]=b;H[j+128>>2]=a;while(1){p=h;h=h+1|0;e=h<<2;d=(b+1|0)/2|0;H[e+j>>2]=d;i=e+(j+128|0)|0;e=(a+1|0)/2|0;H[i>>2]=e;i=N(a,b);f=i+f|0;b=d;a=e;if(i>>>0>1){continue}break}H[g+8>>2]=f;if(!f){Ca(g);g=0;break a}d=Ea(f,16);H[g+12>>2]=d;if(!d){Ba(c,1,3564,0);Ca(g);g=0;break a}l=H[g+8>>2];H[g+16>>2]=l<<4;a=d;if(p){f=(N(H[g+4>>2],H[g>>2])<<4)+d|0;b=f;while(1){c=n<<2;e=H[c+j>>2];b:{if((e|0)<=0){break b}o=e-1|0;i=0;c:{d:{c=H[c+(j+128|0)>>2];if((c|0)<=0){k=e&3;if(e>>>0>=4){break d}h=0;break c}while(1){h=f;f=c;while(1){e:{H[a>>2]=b;if((f|0)==1){a=a+16|0;b=b+16|0;break e}H[a+16>>2]=b;b=b+16|0;a=a+32|0;k=(f|0)>2;f=f-2|0;if(k){continue}}break}k=((i|0)==(o|0)|i)&1;f=k?b:h+(c<<4)|0;b=k?b:h;i=i+1|0;if((e|0)!=(i|0)){continue}break}break b}m=e&2147483644;h=0;e=0;while(1){q=(h|0)==(o|0);h=h+4|0;f=q?b:f;b=f;e=e+4|0;if((m|0)!=(e|0)){continue}break}if(k){break c}break b}while(1){e=f;m=((h|0)==(o|0)|h)&1;f=m?b:e+(c<<4)|0;b=m?b:e;h=h+1|0;i=i+1|0;if((k|0)!=(i|0)){continue}break}}n=n+1|0;if((n|0)!=(p|0)){continue}break}}H[a>>2]=0;f:{if(!l){break f}a=l&3;if(l>>>0>=4){c=l&-4;b=0;while(1){H[d+60>>2]=0;H[d+52>>2]=999;H[d+56>>2]=0;H[d+44>>2]=0;H[d+36>>2]=999;H[d+40>>2]=0;H[d+28>>2]=0;H[d+20>>2]=999;H[d+24>>2]=0;H[d+12>>2]=0;H[d+4>>2]=999;H[d+8>>2]=0;d=d- -64|0;b=b+4|0;if((c|0)!=(b|0)){continue}break}if(!a){break f}}b=0;while(1){H[d+12>>2]=0;H[d+4>>2]=999;H[d+8>>2]=0;d=d+16|0;b=b+1|0;if((a|0)!=(b|0)){continue}break}}}na=j+256|0;return g}function Zb(a,b,c,d){var e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0;H[a+8>>2]=0;H[a+12>>2]=0;H[a>>2]=b;H[a+16>>2]=0;H[a+20>>2]=0;H[a+28>>2]=d;h=c-1|0;H[a+24>>2]=h;n=b&3;a:{if((c|0)<=0){e=b;b=d;break a}e=b+1|0;H[a>>2]=e;b=I[b|0]}g=b;i=8;H[a+16>>2]=8;j=(g|0)==255;H[a+20>>2]=j;H[a+8>>2]=g;H[a+12>>2]=0;b:{if((n|0)==3){break b}k=c-2|0;H[a+24>>2]=k;c:{if((c|0)<2){b=e;e=d;break c}b=e+1|0;H[a>>2]=b;e=I[e|0]}j=(e|0)==255;H[a+20>>2]=j;i=(g|0)==255?15:16;H[a+16>>2]=i;g=g|e<<8;H[a+8>>2]=g;H[a+12>>2]=0;if((n|0)==2){e=b;c=h;h=k;break b}o=c-3|0;H[a+24>>2]=o;d:{if((c|0)<3){f=b;b=d;break d}f=b+1|0;H[a>>2]=f;b=I[b|0]}j=(b|0)==255;H[a+20>>2]=j;l=((e|0)==255?7:8)+i|0;H[a+16>>2]=l;e=i&31;if((i&63)>>>0>=32){m=b<>>32-e;e=b<>2]=g;H[a+12>>2]=m;if((n|0)==1){e=f;i=l;c=k;h=o;break b}h=c-4|0;H[a+24>>2]=h;e:{if((c|0)<4){e=f;c=d;break e}e=f+1|0;H[a>>2]=e;c=I[f|0]}j=(c|0)==255;H[a+20>>2]=j;i=l+((b|0)==255?7:8)|0;H[a+16>>2]=i;b=l&31;if((l&63)>>>0>=32){f=c<>>32-b;b=c<>2]=g;H[a+12>>2]=b;c=o}f:{if((c|0)>=5){d=H[e>>2];H[a+24>>2]=c-5;H[a>>2]=e+4;break f}b=0;d=d?-1:0;if((c|0)<2){break f}while(1){c=e+1|0;H[a>>2]=c;e=I[e|0];f=h-1|0;H[a+24>>2]=f;d=(255<>>0>1;e=c;h=f;if(k){continue}break}}b=d>>>24|0;H[a+20>>2]=(b|0)==255;c=d>>>16&255;e=d>>>8&255;h=j?7:8;d=d&255;f=h+((d|0)==255?7:8)|0;k=((e|0)==255?7:8)+f|0;H[a+16>>2]=(((c|0)==255?7:8)+i|0)+k;b=d|(e<>>0>=32){d=a<>>32-b;a=a<>2]=a|g;H[c+12>>2]=d|m}function Ha(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0;if(!b){return 0}a:{if(!a){a=gb(8,b);break a}if(!b){Ca(a);a=0;break a}b:{if(b>>>0>4294967239){break b}h=b>>>0<=8?8:b+3&-4;d=h+8|0;j=a-4|0;f=j;e=H[f>>2];b=e+f|0;c=H[b>>2];c:{d:{e:{if((c|0)!=H[(b+c|0)-4>>2]){c=c+e|0;if(c>>>0>=d+16>>>0){e=H[b+4>>2];b=H[b+8>>2];H[e+8>>2]=b;H[b+4>>2]=e;b=d+f|0;c=c-d|0;H[b>>2]=c;H[(b+(c&-4)|0)-4>>2]=c|1;e=H[b>>2]-8|0;f:{if(e>>>0<=127){g=(e>>>3|0)-1|0;break f}c=Q(e);g=((e>>>29-c^4)-(c<<2)|0)+110|0;if(e>>>0<=4095){break f}c=((e>>>30-c^2)-(c<<1)|0)+71|0;g=c>>>0>=63?63:c}c=g;e=c<<4;H[b+4>>2]=e+26400;e=e+26408|0;H[b+8>>2]=H[e>>2];H[e>>2]=b;H[H[b+8>>2]+4>>2]=b;e=H[6859];b=c&31;if((c&63)>>>0>=32){c=1<>>32-b}H[6858]=g|H[6858];H[6859]=c|e;H[f>>2]=d;H[(f+(d&-4)|0)-4>>2]=d;c=1;break c}if(c>>>0>>0){break e}d=H[b+4>>2];b=H[b+8>>2];H[d+8>>2]=b;H[b+4>>2]=d;H[f>>2]=c;H[(f+(c&-4)|0)-4>>2]=c;c=1;break c}if(e>>>0>=d+16>>>0){H[f>>2]=d;H[(f+(d&-4)|0)-4>>2]=d;b=d+f|0;c=e-d|0;H[b>>2]=c;H[(b+(c&-4)|0)-4>>2]=c|1;d=H[b>>2]-8|0;g:{if(d>>>0<=127){c=(d>>>3|0)-1|0;break g}f=Q(d);c=((d>>>29-f^4)-(f<<2)|0)+110|0;if(d>>>0<=4095){break g}c=((d>>>30-f^2)-(f<<1)|0)+71|0;c=c>>>0>=63?63:c}d=c<<4;H[b+4>>2]=d+26400;d=d+26408|0;H[b+8>>2]=H[d>>2];H[d>>2]=b;H[H[b+8>>2]+4>>2]=b;d=H[6859];b=c&31;if((c&63)>>>0>=32){c=1<>>32-b;b=e}H[6858]=b|H[6858];H[6859]=c|d;c=1;break c}c=1;if(d>>>0<=e>>>0){break d}}c=0}}if(c){break a}b=gb(8,h);if(!b){break b}i=H[j>>2]-8|0;ab(b,a,h>>>0>>0?h:i);Ca(a);i=b}a=i}return a}function ne(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0;f=na-16|0;na=f;H[f+12>>2]=c;k=H[a+96>>2];if(H[a+8>>2]==16){e=H[a+180>>2]+N(H[a+228>>2],5644)|0}else{e=H[a+12>>2]}F[e+5640|0]=I[e+5640|0]|1;a:{if(c>>>0<=4){c=0;Ba(d,1,4565,0);break a}Da(b,e,1);if(K[e>>2]>=8){c=0;Ba(d,1,4531,0);break a}Da(b+1|0,f+8|0,1);c=H[f+8>>2];H[e+4>>2]=c;if((c|0)>=5){Ba(d,1,4490,0);H[e+4>>2]=-1}Da(b+2|0,e+8|0,2);g=H[e+8>>2];if(g-65536>>>0<=4294901760){H[f>>2]=g;Ba(d,1,8111,f);c=0;break a}c=H[a+188>>2];H[e+12>>2]=c?c:g;Da(b+4|0,e+16|0,1);if(K[e+16>>2]>=2){c=0;Ba(d,1,5536,0);break a}g=b+5|0;H[f+12>>2]=H[f+12>>2]-5;c=H[k+16>>2];b:{if(!c){break b}k=c&7;h=H[e>>2]&1;e=H[e+5584>>2];b=0;if(c>>>0>=8){c=c&-8;while(1){i=e+N(b,1080)|0;H[i+7560>>2]=h;H[i+6480>>2]=h;H[i+5400>>2]=h;H[i+4320>>2]=h;H[i+3240>>2]=h;H[i+2160>>2]=h;H[i+1080>>2]=h;H[i>>2]=h;b=b+8|0;j=j+8|0;if((c|0)!=(j|0)){continue}break}if(!k){break b}}while(1){H[e+N(b,1080)>>2]=h;b=b+1|0;l=l+1|0;if((k|0)!=(l|0)){continue}break}}c=0;if(!Lc(a,0,g,f+12|0,d)){Ba(d,1,4565,0);break a}if(H[f+12>>2]){Ba(d,1,4565,0);break a}if(H[a+8>>2]==16){b=H[a+180>>2]+N(H[a+228>>2],5644)|0}else{b=H[a+12>>2]}if(K[H[a+96>>2]+16>>2]>=2){b=H[b+5584>>2];g=H[b+4>>2]<<2;l=b+944|0;e=b+812|0;j=1;c=b;while(1){H[c+1084>>2]=H[b+4>>2];H[c+1088>>2]=H[b+8>>2];H[c+1092>>2]=H[b+12>>2];H[c+1096>>2]=H[b+16>>2];H[c+1100>>2]=H[b+20>>2];d=!g;if(!d){B(c+1892|0,e,g)}if(!d){B(c+2024|0,l,g)}c=c+1080|0;j=j+1|0;if(j>>>0>2]+16>>2]){continue}break}}c=1}na=f+16|0;return c|0}function gb(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0;j=a>>>0<=8?8:a;k=j+48|0;a:{b:{while(1){if(a-1&a|b>>>0>4294967239){break b}c=H[6859];e=c;h=H[6858];b=b>>>0<=8?8:b+3&-4;c:{if(b>>>0<=127){i=(b>>>3|0)-1|0;break c}d=Q(b);i=((b>>>29-d^4)-(d<<2)|0)+110|0;if(b>>>0<=4095){break c}d=((b>>>30-d^2)-(d<<1)|0)+71|0;i=d>>>0>=63?63:d}g=i;f=g&31;if((g&63)>>>0>=32){d=0;c=c>>>f|0}else{d=c>>>f|0;c=((1<>>f}if(c|d){while(1){f=d;d:{if(f|c){e=f-1|0;i=e+1|0;d=e;e=c-1|0;h=(e|0)!=-1?i:d;d=Q(f^h);d=(d|0)==32?Q(c^e)+32|0:d;e=63-d|0;qa=0-(d>>>0>63)|0;break d}qa=0;e=64}h=e;e=h&31;if((h&63)>>>0>=32){d=0;i=f>>>e|0}else{d=f>>>e|0;i=((1<>>e}g=g+h|0;c=g<<4;f=H[c+26408>>2];e=c+26400|0;e:{if((f|0)!=(e|0)){c=Cb(f,j,b);if(c){break a}c=H[f+4>>2];h=H[f+8>>2];H[c+8>>2]=h;H[h+4>>2]=c;H[f+8>>2]=e;H[f+4>>2]=H[e+4>>2];H[e+4>>2]=f;H[H[f+4>>2]+8>>2]=f;g=g+1|0;c=(d&1)<<31|i>>>1;d=d>>>1|0;break e}c=H[6859];l=27432,m=H[6858]&ye(-2,-1,g),H[l>>2]=m;H[6859]=qa&c;c=i^1}if(c|d){continue}break}h=H[6858];e=H[6859]}d=Q(e);f=63-((d|0)==32?Q(h)+32|0:d)|0;f:{if(!(e|h)){g=0;break f}c=f<<4;g=H[c+26408>>2];if(!e&h>>>0<1073741824){break f}d=98;e=c+26400|0;if((e|0)==(g|0)){break f}while(1){c=Cb(g,j,b);if(c){break a}g=H[g+8>>2];if((e|0)==(g|0)){break f}c=d;d=c-1|0;if(c){continue}break}}d=a>>>0>8;a=j;if(zc((d?k:48)+b|0)){continue}break}if(!g){break b}a=(f<<4)+26400|0;if((a|0)==(g|0)){break b}while(1){c=Cb(g,j,b);if(c){break a}g=H[g+8>>2];if((a|0)!=(g|0)){continue}break}}c=0}return c}function ud(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0;e=H[a+48>>2];if(e>>>0>=b>>>0){H[a+48>>2]=e-b;H[a+36>>2]=H[a+36>>2]+b;e=c+H[a+60>>2]|0;d=b+H[a+56>>2]|0;e=d>>>0>>0?e+1|0:e;H[a+56>>2]=d;H[a+60>>2]=e;qa=c;return b|0}if(I[a+68|0]&4){H[a+48>>2]=0;H[a+36>>2]=e+H[a+36>>2];g=H[a+60>>2];c=H[a+56>>2];b=c+e|0;H[a+56>>2]=b;H[a+60>>2]=b>>>0>>0?g+1|0:g;qa=e?0:-1;return(e?e:-1)|0}if(e){H[a+48>>2]=0;H[a+36>>2]=H[a+32>>2];h=b;f=e;b=b-e|0;c=c-(e>>>0>h>>>0)|0}a:{if((c|0)>0){h=1}else{h=!!b&(c|0)>=0}if(h){while(1){h=H[a+12>>2];e=c+g|0;i=b+f|0;e=H[a+60>>2]+(i>>>0>>0?e+1|0:e)|0;j=i;i=i+H[a+56>>2]|0;e=j>>>0>i>>>0?e+1|0:e;if((e|0)==(h|0)&i>>>0>K[a+8>>2]|e>>>0>h>>>0){Ba(d,4,15630,0);H[a+48>>2]=0;H[a+36>>2]=H[a+32>>2];b=g+H[a+60>>2]|0;c=f+H[a+56>>2]|0;b=c>>>0>>0?b+1|0:b;H[a+56>>2]=c;H[a+60>>2]=b;d=H[a+8>>2];f=d-c|0;e=H[a+12>>2];g=e-((c>>>0>d>>>0)+b|0)|0;h=ra[H[a+28>>2]](d,e,H[a>>2])|0;i=H[a+68>>2];if(h){H[a+56>>2]=d;H[a+60>>2]=e}H[a+68>>2]=i|4;a=(c|0)==(d|0)&(b|0)==(e|0);b=a?-1:f;break a}e=ra[H[a+24>>2]](b,c,H[a>>2])|0;h=qa;i=h;if((e&i)==-1){Ba(d,4,15630,0);H[a+68>>2]=H[a+68>>2]|4;e=g+H[a+60>>2]|0;b=f+H[a+56>>2]|0;e=b>>>0>>0?e+1|0:e;H[a+56>>2]=b;H[a+60>>2]=e;a=!(g|f);b=a?-1:f;break a}g=g+i|0;f=e+f|0;g=f>>>0>>0?g+1|0:g;h=b;b=b-e|0;c=c-((e>>>0>h>>>0)+i|0)|0;if(!!b&(c|0)>=0|(c|0)>0){continue}break}}b=g+H[a+60>>2]|0;c=f+H[a+56>>2]|0;b=c>>>0>>0?b+1|0:b;H[a+56>>2]=c;H[a+60>>2]=b;qa=g;return f|0}qa=a?-1:g;return b|0}function yd(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0;e=na-80|0;na=e;a:{if(c>>>0<=2){Ba(d,1,14478,0);break a}if(I[a+124|0]){Ba(d,4,11193,0);g=1;break a}g=1;Da(b,a+40|0,1);Da(b+1|0,a+52|0,1);Da(b+2|0,a+44|0,1);f=b+3|0;b:{c:{d:{e:{f:{h=H[a+40>>2];switch(h-1|0){case 0:break f;case 1:break e;default:break d}}if(c>>>0<=6){H[e+16>>2]=c;Ba(d,1,15155,e+16|0);g=0;break a}if(!((c|0)==7|H[a+48>>2]==14)){H[e+48>>2]=c;Ba(d,2,15155,e+48|0)}Da(f,a+48|0,4);if(H[a+48>>2]!=14){break b}f=Fa(36);if(!f){g=0;Ba(d,1,7993,0);break a}H[f>>2]=14;H[e+64>>2]=0;H[e+56>>2]=0;H[e+72>>2]=0;H[e+60>>2]=0;H[e+68>>2]=0;H[e+76>>2]=0;g=4470064;H[e+52>>2]=4470064;H[f+4>>2]=1145390592;g:{if((c|0)!=7){if((c|0)==35){Da(b+7|0,e+76|0,4);Da(b+11|0,e+72|0,4);Da(b+15|0,e+68|0,4);Da(b+19|0,e- -64|0,4);Da(b+23|0,e+60|0,4);Da(b+27|0,e+56|0,4);Da(b+31|0,e+52|0,4);H[f+4>>2]=0;g=H[e+52>>2];c=H[e+56>>2];d=H[e+64>>2];i=H[e+68>>2];j=H[e+76>>2];h=H[e+72>>2];b=H[e+60>>2];break g}H[e+32>>2]=c;Ba(d,2,15191,e+32|0)}c=0;d=0;h=0;b=0}H[f+24>>2]=b;H[f+16>>2]=i;H[f+8>>2]=j;H[f+32>>2]=g;H[f+28>>2]=c;H[f+20>>2]=d;H[f+12>>2]=h;H[a+112>>2]=0;H[a+108>>2]=f;break b}b=c-3|0;H[a+112>>2]=b;d=Ea(1,b);H[a+108>>2]=d;if(!d){break c}if((c|0)<=3){break b}c=0;while(1){Da(f,e+76|0,1);F[H[a+108>>2]+c|0]=H[e+76>>2];f=f+1|0;c=c+1|0;if((b|0)!=(c|0)){continue}break}break b}if(h>>>0<3){break a}H[e>>2]=h;Ba(d,4,15950,e);break a}g=0;H[a+112>>2]=0;break a}g=1;F[a+124|0]=1}na=e+80|0;return g|0}function Ja(a,b,c,d){var e=0,f=0,g=0,h=0,i=0,j=0;h=H[a+48>>2];if(h>>>0>=c>>>0){if(c){B(b,H[a+36>>2],c)}H[a+36>>2]=H[a+36>>2]+c;H[a+48>>2]=H[a+48>>2]-c;b=H[a+60>>2];d=H[a+56>>2]+c|0;b=d>>>0>>0?b+1|0:b;H[a+56>>2]=d;H[a+60>>2]=b;return c}if(I[a+68|0]&4){if(h){B(b,H[a+36>>2],h)}b=H[a+48>>2];H[a+48>>2]=0;H[a+36>>2]=b+H[a+36>>2];g=H[a+60>>2];c=b;b=H[a+56>>2]+b|0;g=c>>>0>b>>>0?g+1|0:g;H[a+56>>2]=b;H[a+60>>2]=g;return h?h:-1}a:{if(h){if(h){B(b,H[a+36>>2],h)}i=H[a+32>>2];H[a+36>>2]=i;e=H[a+48>>2];H[a+48>>2]=0;f=H[a+60>>2];g=H[a+56>>2]+e|0;f=g>>>0>>0?f+1|0:f;H[a+56>>2]=g;H[a+60>>2]=f;c=c-e|0;b=b+e|0;break a}i=H[a+32>>2];H[a+36>>2]=i}b:{while(1){c:{e=H[a>>2];f=H[a+16>>2];g=H[a+64>>2];d:{if(g>>>0>c>>>0){f=ra[f|0](i,g,e)|0;H[a+48>>2]=f;if((f|0)==-1){break b}if(c>>>0>f>>>0){if(f){B(b,H[a+36>>2],f)}i=H[a+32>>2];H[a+36>>2]=i;e=H[a+48>>2];break d}if(c){B(b,H[a+36>>2],c)}H[a+36>>2]=H[a+36>>2]+c;H[a+48>>2]=H[a+48>>2]-c;b=H[a+60>>2];d=H[a+56>>2]+c|0;b=d>>>0>>0?b+1|0:b;H[a+56>>2]=d;H[a+60>>2]=b;return c+h|0}e=ra[f|0](b,c,e)|0;H[a+48>>2]=e;if((e|0)==-1){break b}if(c>>>0<=e>>>0){break c}i=H[a+32>>2];H[a+36>>2]=i;f=e}H[a+48>>2]=0;g=H[a+60>>2];j=H[a+56>>2]+e|0;g=j>>>0>>0?g+1|0:g;H[a+56>>2]=j;H[a+60>>2]=g;b=b+e|0;c=c-e|0;h=f+h|0;continue}break}H[a+48>>2]=0;H[a+36>>2]=H[a+32>>2];f=H[a+60>>2];b=H[a+56>>2]+e|0;f=b>>>0>>0?f+1|0:f;H[a+56>>2]=b;H[a+60>>2]=f;return e+h|0}Ba(d,4,15630,0);H[a+48>>2]=0;H[a+68>>2]=H[a+68>>2]|4;return h?h:-1}function Ta(a){var b=0,c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0;a:{b:{if(!H[a+12>>2]){k=1;if(H[a+4>>2]>0|H[a+8>>2]>1){break b}break a}e=1;if(H[a+8>>2]>0){break b}if(H[a+4>>2]<2){break a}}b=H[a>>2];f=b+(e<<5)|0;g=H[a+16>>2];h=H[a+20>>2];if(g>>>0>>0){d=g;while(1){c=(d<<6)+f|0;L[c>>2]=L[c>>2]*O(1.2301740646362305);L[c+4>>2]=L[c+4>>2]*O(1.2301740646362305);L[c+8>>2]=L[c+8>>2]*O(1.2301740646362305);L[c+12>>2]=L[c+12>>2]*O(1.2301740646362305);L[c+16>>2]=L[c+16>>2]*O(1.2301740646362305);L[c+20>>2]=L[c+20>>2]*O(1.2301740646362305);L[c+24>>2]=L[c+24>>2]*O(1.2301740646362305);L[c+28>>2]=L[c+28>>2]*O(1.2301740646362305);d=d+1|0;if((h|0)!=(d|0)){continue}break}}i=b+(k<<5)|0;j=H[a+28>>2];c=H[a+24>>2];if(j>>>0>c>>>0){d=c;while(1){b=(d<<6)+i|0;L[b>>2]=L[b>>2]*O(1.625732421875);L[b+4>>2]=L[b+4>>2]*O(1.625732421875);L[b+8>>2]=L[b+8>>2]*O(1.625732421875);L[b+12>>2]=L[b+12>>2]*O(1.625732421875);L[b+16>>2]=L[b+16>>2]*O(1.625732421875);L[b+20>>2]=L[b+20>>2]*O(1.625732421875);L[b+24>>2]=L[b+24>>2]*O(1.625732421875);L[b+28>>2]=L[b+28>>2]*O(1.625732421875);d=d+1|0;if((j|0)!=(d|0)){continue}break}}b=f+32|0;d=H[a+8>>2];a=H[a+4>>2];e=a-e|0;e=(d|0)<(e|0)?d:e;mb(i,b,g,h,e,O(-.4435068666934967));l=i+32|0;d=d-k|0;a=(a|0)<(d|0)?a:d;mb(f,l,c,j,a,O(-.8829110860824585));mb(i,b,g,h,e,O(.05298011749982834));mb(f,l,c,j,a,O(1.5861343145370483))}}function _b(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0;e=H[a+28>>2]+N(b,152)|0;c=H[e-144>>2]-H[e-152>>2]|0;d=H[e-140>>2]-H[e-148>>2]|0;f=d>>>0>=64?64:d;g=c>>>0>=64?64:c;a:{if(!(!c|!d|(!g|!f)|g>>>0>4294967295/(f>>>0)>>>2>>>0)){e=Ea(1,28);H[e+12>>2]=f;H[e+8>>2]=g;H[e+4>>2]=d;H[e>>2]=c;j=d;d=d+f|0;i=j>>>0>d>>>0;d=ve(d-1|0,i-!d|0,f,0);H[e+20>>2]=d;j=c;c=c+g|0;f=j>>>0>c>>>0;c=ve(c-1|0,f-!c|0,g,0);H[e+16>>2]=c;re(d,0,c);b:{if(qa){break b}c=Ea(4,N(c,d));H[e+24>>2]=c;if(!c){break b}break a}Ca(e)}e=0}if(!e){return 0}c:{if(b){while(1){q=N(p,152);g=q+H[a+28>>2]|0;c=H[g+24>>2];if(c){j=g+28|0;d=H[g+20>>2];f=H[g+16>>2];n=0;while(1){if(N(d,f)){i=N(n,36)+j|0;o=0;while(1){k=H[i+20>>2]+N(o,40)|0;c=H[k+20>>2];h=H[k+16>>2];if(N(c,h)){f=0;while(1){l=H[k+24>>2]+N(f,68)|0;r=H[l+60>>2];if(r){h=H[l+8>>2];d=h-H[i>>2]|0;m=H[i+16>>2];if(m&1){c=H[a+28>>2]+q|0;d=(H[c-144>>2]+d|0)-H[c-152>>2]|0}s=H[l+12>>2];c=s-H[i+4>>2]|0;if(m&2){m=H[a+28>>2]+q|0;c=(c+H[m-140>>2]|0)-H[m-148>>2]|0}h=H[l+16>>2]-h|0;if(!Za(e,d,c,d+h|0,(H[l+20>>2]-s|0)+c|0,r,1,h)){break c}h=H[k+16>>2];c=H[k+20>>2]}f=f+1|0;if(f>>>0>>0){continue}break}f=H[g+16>>2];d=H[g+20>>2]}o=o+1|0;if(o>>>0>>0){continue}break}c=H[g+24>>2]}n=n+1|0;if(n>>>0>>0){continue}break}}p=p+1|0;if((p|0)!=(b|0)){continue}break}}return e}Va(e);return 0}function $d(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0;h=na-16|0;na=h;if(H[a+8>>2]==16){e=H[a+180>>2]+N(H[a+228>>2],5644)|0}else{e=H[a+12>>2]}a:{if(c>>>0<=1){Ba(d,1,4132,0);a=0;break a}Da(b,h+12|0,2);b:{if(H[h+12>>2]){Ba(d,2,3608,0);break b}if(c>>>0<=6){Ba(d,1,4132,0);a=0;break a}Da(b+2|0,h+12|0,2);g=H[e+5616>>2];k=I[h+12|0];f=H[e+5620>>2];c:{d:{if(!f){break d}a=g;while(1){if(H[a+8>>2]!=(k|0)){a=a+20|0;i=i+1|0;if((i|0)!=(f|0)){continue}break d}break}if((f|0)!=(i|0)){break c}}if(H[e+5624>>2]==(f|0)){a=f+10|0;H[e+5624>>2]=a;a=Ha(g,N(a,20));g=H[e+5616>>2];if(!a){Ca(g);H[e+5624>>2]=0;H[e+5616>>2]=0;H[e+5620>>2]=0;Ba(d,1,4158,0);a=0;break a}e:{if((a|0)==(g|0)){break e}l=H[e+5632>>2];if(!l){break e}m=H[e+5628>>2];i=0;while(1){f=N(i,20)+m|0;j=H[f+8>>2];if(j){H[f+8>>2]=a+(j-g|0)}j=H[f+12>>2];if(j){H[f+12>>2]=a+(j-g|0)}i=i+1|0;if((l|0)!=(i|0)){continue}break}}H[e+5616>>2]=a;g=H[e+5620>>2];f=N(H[e+5624>>2]-g|0,20);if(f){y(a+N(g,20)|0,0,f)}f=H[e+5620>>2];g=H[e+5616>>2]}H[e+5620>>2]=f+1;a=N(f,20)+g|0}e=H[a+12>>2];if(e){Ca(e);H[a+12>>2]=0;H[a+16>>2]=0}H[a+8>>2]=k;e=H[h+12>>2];H[a>>2]=e>>>10&3;H[a+4>>2]=e>>>8&3;Da(b+4|0,h+12|0,2);if(H[h+12>>2]){Ba(d,2,3023,0);break b}c=c-6|0;e=Fa(c);H[a+12>>2]=e;if(!e){Ba(d,1,4132,0);a=0;break a}if(c){B(e,b+6|0,c)}H[a+16>>2]=c}a=1}na=h+16|0;return a|0}function Tb(a){var b=0,c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0;if(a){b=H[a+20>>2];if(b){g=H[b>>2];if(g){d=H[g+20>>2];if(H[g+16>>2]){i=F[a+40|0]&1?16:17;while(1){c=H[d+28>>2];if(c){b=H[d+32>>2];l=(b>>>0)/152|0;j=0;if(b>>>0>=152){while(1){b=H[c+48>>2];if(b){f=H[c+52>>2];h=(f>>>0)/40|0;e=0;if(f>>>0>=40){while(1){_a(H[b+32>>2]);H[b+32>>2]=0;_a(H[b+36>>2]);H[b+36>>2]=0;ra[i|0](b);b=b+40|0;e=e+1|0;if((h|0)!=(e|0)){continue}break}b=H[c+48>>2]}Ca(b);H[c+48>>2]=0}b=H[c+84>>2];if(b){f=H[c+88>>2];h=(f>>>0)/40|0;e=0;if(f>>>0>=40){while(1){_a(H[b+32>>2]);H[b+32>>2]=0;_a(H[b+36>>2]);H[b+36>>2]=0;ra[i|0](b);b=b+40|0;e=e+1|0;if((h|0)!=(e|0)){continue}break}b=H[c+84>>2]}Ca(b);H[c+84>>2]=0}b=H[c+120>>2];if(b){f=H[c+124>>2];h=(f>>>0)/40|0;e=0;if(f>>>0>=40){while(1){_a(H[b+32>>2]);H[b+32>>2]=0;_a(H[b+36>>2]);H[b+36>>2]=0;ra[i|0](b);b=b+40|0;e=e+1|0;if((h|0)!=(e|0)){continue}break}b=H[c+120>>2]}Ca(b);H[c+120>>2]=0}c=c+152|0;j=j+1|0;if((l|0)!=(j|0)){continue}break}c=H[d+28>>2]}Ca(c);H[d+28>>2]=0}a:{if(!H[d+40>>2]){break a}b=H[d+36>>2];if(!b){break a}Ca(b);H[d+44>>2]=0;H[d+48>>2]=0;H[d+36>>2]=0;H[d+40>>2]=0}Ca(H[d+52>>2]);d=d+76|0;k=k+1|0;if(k>>>0>2]){continue}break}d=H[g+20>>2]}Ca(d);H[g+20>>2]=0;Ca(H[H[a+20>>2]>>2]);b=H[a+20>>2];H[b>>2]=0}Ca(b);H[a+20>>2]=0}Ca(H[a+68>>2]);Ca(a)}}function Rb(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0;e=H[a+24>>2];j=H[e+16>>2];if(!j){return 0}f=H[e+24>>2];e=H[H[H[a+20>>2]>>2]+20>>2];a:{b:{if(!b){b=0;while(1){c=H[f+24>>2];a=H[e+28>>2]+N(H[e+24>>2],152)|0;d=H[a-140>>2];g=H[a-144>>2]-H[a-152>>2]|0;a=H[a-148>>2];h=d-a|0;re(g,0,h);if(!(!qa|(a|0)==(d|0))){break a}a=(c>>>3|0)+((c&7)!=0)|0;c=(a|0)==3?4:a;a=!c;d=N(g,h);re(c,0,d);if(!(!qa|a)){break a}a=-1;c=N(c,d);if(c>>>0>(b^-1)>>>0){break b}e=e+76|0;f=f+52|0;b=b+c|0;a=b;i=i+1|0;if((j|0)!=(i|0)){continue}break}break b}b=0;if(!H[a+64>>2]){while(1){c=H[f+24>>2];a=H[e+28>>2]+N(H[e+24>>2],152)|0;d=H[a-4>>2];g=H[a-8>>2]-H[a-16>>2]|0;a=H[a-12>>2];h=d-a|0;re(g,0,h);if(!(!qa|(a|0)==(d|0))){break a}a=(c>>>3|0)+((c&7)!=0)|0;c=(a|0)==3?4:a;a=!c;d=N(g,h);re(c,0,d);if(!(!qa|a)){break a}a=-1;c=N(c,d);if(c>>>0>(b^-1)>>>0){break b}e=e+76|0;f=f+52|0;b=b+c|0;a=b;i=i+1|0;if((j|0)!=(i|0)){continue}break}break b}while(1){c=H[f+24>>2];a=H[e+28>>2]+N(H[e+24>>2],152)|0;d=H[a-140>>2];g=H[a-144>>2]-H[a-152>>2]|0;a=H[a-148>>2];h=d-a|0;re(g,0,h);if(!(!qa|(a|0)==(d|0))){break a}a=(c>>>3|0)+((c&7)!=0)|0;c=(a|0)==3?4:a;a=!c;d=N(g,h);re(c,0,d);if(!(!qa|a)){break a}a=-1;c=N(c,d);if(c>>>0>(b^-1)>>>0){break b}e=e+76|0;f=f+52|0;b=b+c|0;a=b;i=i+1|0;if((j|0)!=(i|0)){continue}break}}return a}return-1}function $b(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0;c=H[a+8>>2];e=c+H[a+4>>2]|0;a:{if(!H[a+12>>2]){if((e|0)<2){break a}h=(c<<2)+b|0;d=H[h>>2];c=H[b>>2]-(d+1>>1)|0;i=H[a>>2];if(e>>>0>=4){k=(e-4>>>1|0)+1|0;a=1;while(1){f=d;d=a<<2;m=H[d+b>>2];d=H[d+h>>2];j=c;l=i+(g<<2)|0;H[l>>2]=c;c=m-((d+f|0)+2>>2)|0;H[l+4>>2]=f+(j+c>>1);g=g+2|0;f=(a|0)!=(k|0);a=a+1|0;if(f){continue}break}}H[i+(g<<2)>>2]=c;if(e&1){f=e-1|0;a=H[(f<<1)+b>>2]-(d+1>>1)|0;H[i+(f<<2)>>2]=a;c=a+c>>1;j=-8}else{j=-4}a=e<<2;H[j+(a+i|0)>>2]=c+d;if(!a){break a}B(b,i,a);return}b:{switch(e-1|0){case 0:H[b>>2]=H[b>>2]/2;return;case 1:a=H[a>>2];c=(c<<2)+b|0;d=H[b>>2]-(H[c>>2]+1>>1)|0;H[a+4>>2]=d;H[a>>2]=d+H[c>>2];c=H[a+4>>2];H[b>>2]=H[a>>2];H[b+4>>2]=c;return;default:break b}}if((e|0)<3){break a}h=H[a>>2];k=(c<<2)+b|0;d=H[k+4>>2];a=H[k>>2];c=H[b>>2]-((d+a|0)+2>>2)|0;H[h>>2]=c+a;g=1;m=e-2|0;l=e&1;a=!l;if(m-a>>>0>=2){o=((e-a|0)-4>>>1|0)+1|0;a=1;while(1){f=d;p=H[(a<<2)+b>>2];j=a+1|0;d=H[k+(j<<2)>>2];i=c;n=h+(g<<2)|0;H[n>>2]=c;c=p-((d+f|0)+2>>2)|0;H[n+4>>2]=f+(i+c>>1);g=g+2|0;f=(a|0)!=(o|0);a=j;if(f){continue}break}}H[h+(g<<2)>>2]=c;c:{if(!l){g=H[((e<<1)+b|0)-4>>2]-(d+1>>1)|0;H[h+(m<<2)>>2]=(g+c>>1)+d;break c}g=c+d|0}a=e<<2;H[(a+h|0)-4>>2]=g;if(!a){break a}B(b,h,a)}}function Hb(a,b,c){var d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0;d=na-256|0;na=d;if(a){Ac(1806,17,c);H[d+240>>2]=H[a>>2];Ga(c,2348,d+240|0);H[d+224>>2]=H[a+4>>2];Ga(c,2361,d+224|0);H[d+208>>2]=H[a+8>>2];Ga(c,7260,d+208|0);H[d+192>>2]=H[a+16>>2];Ga(c,2319,d+192|0);if((b|0)>0){while(1){e=H[a+5584>>2];H[d+176>>2]=h;Ga(c,1844,d+176|0);e=e+N(h,1080)|0;H[d+160>>2]=H[e>>2];Ga(c,2347,d+160|0);H[d+144>>2]=H[e+4>>2];Ga(c,7374,d+144|0);H[d+128>>2]=H[e+8>>2];Ga(c,7162,d+128|0);H[d+112>>2]=H[e+12>>2];Ga(c,7178,d+112|0);H[d+96>>2]=H[e+16>>2];Ga(c,2330,d+96|0);H[d+80>>2]=H[e+20>>2];Ga(c,7440,d+80|0);Ac(1567,23,c);if(H[e+4>>2]){i=e+944|0;j=e+812|0;f=0;while(1){g=f<<2;k=H[j+g>>2];H[d+68>>2]=H[i+g>>2];H[d+64>>2]=k;Ga(c,1693,d- -64|0);f=f+1|0;if(f>>>0>2]){continue}break}}Bc(c);H[d+48>>2]=H[e+24>>2];Ga(c,7194,d+48|0);H[d+32>>2]=H[e+804>>2];Ga(c,7243,d+32|0);i=1;Ac(1591,20,c);a:{if(H[e+24>>2]!=1){f=H[e+4>>2];if((f|0)<=0){break a}i=N(f,3)-2|0}j=e+28|0;f=0;while(1){g=j+(f<<3)|0;l=d,m=ye(H[g>>2],H[g+4>>2],32),H[l+16>>2]=m;H[d+20>>2]=qa;Ga(c,1693,d+16|0);f=f+1|0;if((i|0)!=(f|0)){continue}break}}Bc(c);H[d>>2]=H[e+808>>2];Ga(c,7226,d);Ac(1707,5,c);h=h+1|0;if((h|0)!=(b|0)){continue}break}}Ac(1708,4,c)}na=d+256|0}function se(a,b,c,d){var e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0;a:{b:{c:{d:{e:{f:{g:{h:{i:{j:{e=b;if(e){if(!c){break j}if(!d){break i}e=Q(d)-Q(e)|0;if(e>>>0<=31){break h}break b}if((d|0)==1|d>>>0>1){break b}b=(a>>>0)/(c>>>0)|0;oa=a-N(b,c)|0;pa=0;qa=0;return b}if(!a){break g}if(!d){break f}f=d-1|0;if(f&d){break f}oa=a;pa=e&f;a=e>>>te(d)|0;qa=0;return a}f=c-1|0;if(!(f&c)){break e}k=(Q(c)+33|0)-Q(e)|0;g=0-k|0;break c}k=e+1|0;g=63-e|0;break c}oa=0;a=(e>>>0)/(d>>>0)|0;pa=e-N(a,d)|0;qa=0;return a}e=Q(d)-Q(e)|0;if(e>>>0<31){break d}break b}oa=a&f;pa=0;if((c|0)==1){break a}c=te(c);d=c&31;if((c&63)>>>0>=32){e=0;a=b>>>d|0}else{e=b>>>d|0;a=((1<>>d}qa=e;return a}k=e+1|0;g=63-e|0}f=a;e=k&63;h=e&31;if((e&63)>>>0>=32){e=0;f=b>>>h|0}else{e=b>>>h|0;f=((1<>>h}h=g&63;g=a;i=h&31;if((h&63)>>>0>=32){j=a<>>32-i|b<>>31;f=f<<1|b>>>31;l=e;i=g-(e+(f>>>0>h>>>0)|0)|0;m=i>>31;j=m;e=f;i=c&j;f=e-i|0;e=l-((d&j)+(e>>>0>>0)|0)|0;j=b<<1|a>>>31;a=n|a<<1;b=j|o;l=m&1;n=l;k=k-1|0;if(k){continue}break}}oa=f;pa=e;j=b<<1|a>>>31;a=l|a<<1;qa=j|o;return a}oa=a;pa=b;a=0;b=0}qa=b;return a}function Kc(a,b,c,d,e){var f=0,g=0,h=0,i=0;h=na-16|0;na=h;if(H[a+8>>2]==16){a=H[a+180>>2]+N(H[a+228>>2],5644)|0}else{a=H[a+12>>2]}f=H[d>>2];a:{if(!f){d=0;Ba(e,1,2642,0);break a}a=H[a+5584>>2];H[d>>2]=f-1;Da(c,h+12|0,1);g=N(b,1080)+a|0;a=H[h+12>>2];H[g+804>>2]=a>>>5;b=a&31;H[g+24>>2]=b;a=c+1|0;b:{c:{d:{e:{f:{switch(b|0){case 0:f=H[d>>2];break e;case 1:break d;default:break f}}f=H[d>>2]>>>1|0}if(f>>>0>=98){H[h+4>>2]=97;H[h+8>>2]=97;H[h>>2]=f;Ba(e,2,16056,h);b=H[g+24>>2]}if(b){b=f;if(b){break d}a=0;break c}if(f){b=g+28|0;c=0;while(1){Da(a,h+12|0,1);if(c>>>0<=96){e=H[h+12>>2];i=b+(c<<3)|0;H[i+4>>2]=0;H[i>>2]=e>>>3}a=a+1|0;c=c+1|0;if((f|0)!=(c|0)){continue}break}}a=H[d>>2];if(a>>>0>>0){d=0;break a}a=a-f|0;break b}e=g+28|0;c=0;while(1){Da(a,h+12|0,2);if(c>>>0<=96){f=e+(c<<3)|0;i=H[h+12>>2];H[f+4>>2]=i&2047;H[f>>2]=i>>>11}a=a+2|0;c=c+1|0;if((c|0)!=(b|0)){continue}break}a=b<<1}b=H[d>>2];if(a>>>0>b>>>0){d=0;break a}a=b-a|0}H[d>>2]=a;d=1;if(H[g+24>>2]!=1){break a}f=g+28|0;c=H[g+32>>2];e=H[g+28>>2];a=1;while(1){b=f+(a<<3)|0;H[b+12>>2]=c;H[b+4>>2]=c;g=e-((a>>>0)/3|0)|0;H[b+8>>2]=(g|0)>0?g:0;g=b;b=e-((a-1>>>0)/3|0)|0;H[g>>2]=(b|0)>0?b:0;a=a+2|0;if((a|0)!=97){continue}break}}na=h+16|0;return d}function fe(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0,k=0;f=na-32|0;na=f;g=1;a:{if(c>>>0<=1){g=0;Ba(d,1,10062,0);break a}if(H[a+76>>2]){break a}Da(b,f+28|0,1);Da(b+1|0,f+24|0,1);e=H[f+24>>2];i=e>>>4&3;if((i|0)==3){H[a+76>>2]=1;Ba(d,2,11558,0);break a}c=c-2|0;j=(e>>>5&2)+2|0;h=i+j|0;e=(c>>>0)/(h>>>0)|0;if((c|0)!=(N(e,h)|0)){H[a+76>>2]=1;Ba(d,2,11139,0);break a}if(c>>>0>>0){break a}b:{c=H[a+68>>2];if(c>>>0<=(e^-1)>>>0){c=c+e|0;if(c>>>0<536870912){break b}}H[a+76>>2]=1;Ba(d,2,9400,0);break a}h=Ha(H[a+72>>2],c<<3);if(!h){H[a+76>>2]=1;Ba(d,2,9443,0);break a}c=b+2|0;H[a+72>>2]=h;c:{if(i){k=e>>>0<=1?1:e;e=0;while(1){Da(c,f+20|0,i);b=H[f+20>>2];if(b>>>0>=N(H[a+132>>2],H[a+128>>2])>>>0){break c}b=c+i|0;Da(b,f+16|0,j);c=H[a+68>>2];g=h+(c<<3)|0;G[g>>1]=H[f+20>>2];H[g+4>>2]=H[f+16>>2];g=1;H[a+68>>2]=c+1;c=b+j|0;e=e+1|0;if((k|0)!=(e|0)){continue}break}break a}i=e>>>0<=1?1:e;b=H[a+68>>2];e=0;while(1){H[f+20>>2]=b;if(N(H[a+132>>2],H[a+128>>2])>>>0<=b>>>0){break c}Da(c,f+16|0,j);k=H[a+68>>2];g=h+(k<<3)|0;G[g>>1]=b;H[g+4>>2]=H[f+16>>2];g=1;b=k+1|0;H[a+68>>2]=b;c=c+j|0;e=e+1|0;if((i|0)!=(e|0)){continue}break}break a}H[a+76>>2]=1;H[f>>2]=b;Ba(d,2,7799,f)}na=f+32|0;return g|0}function Ad(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0,k=0;h=na-16|0;na=h;a:{if(!(I[a+100|0]&2)){Ba(d,1,11356,0);a=0;break a}H[a+104>>2]=0;b:{c:{d:{if(c){while(1){if(c>>>0<=7){Ba(d,1,3403,0);break b}g=h+12|0;Da(b,g,4);e=H[h+12>>2];Da(b+4|0,g,4);f=8;g=H[h+12>>2];e:{f:{g:{switch(e|0){case 1:if(c>>>0<16){e=3443;break c}Da(b+8|0,h+8|0,4);if(H[h+8>>2]){e=8449;break c}Da(b+12|0,h+12|0,4);e=H[h+12>>2];if(e){break f}e=3268;break c;case 0:break g;default:break e}}Ba(d,1,3268,0);break b}f=16}if(e>>>0>>0){Ba(d,1,9148,0);break b}if(c>>>0>>0){Ba(d,1,9076,0);a=0;break a}h:{i:{j=b+f|0;k=e-f|0;j:{k:{l:{m:{if((g|0)<=1668246641){if((g|0)==1651532643){break m}if((g|0)==1667523942){break k}if((g|0)!=1668112752){break i}f=25296;break j}if((g|0)==1885564018){break l}f=25264;if((g|0)==1768449138){break j}if((g|0)!=1668246642){break i}f=25272;break j}f=25280;break j}f=25288;break j}f=25304}if(ra[H[f+4>>2]](a,j,k,d)|0){break h}a=0;break a}H[a+104>>2]=H[a+104>>2]|2147483647}i=(g|0)==1768449138?1:i;b=b+e|0;c=c-e|0;if(c){continue}break}if(i){break d}}Ba(d,1,8976,0);a=0;break a}F[a+132|0]=1;H[a+100>>2]=H[a+100>>2]|4;a=1;break a}Ba(d,1,e,0)}Ba(d,1,1968,0);a=0}na=h+16|0;return a|0}function Jd(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0;a:{if(!Wa(H[b+8>>2],54,d)){break a}i=H[b+4>>2];f=H[i>>2];e=H[i+8>>2];b:{if(f){g=1;c:{if((f|0)!=1){k=f&1;f=f&-2;while(1){h=0;d:{if(!g){break d}h=0;if(!(ra[H[e>>2]](b,a,d)|0)){break d}h=(ra[H[e+4>>2]](b,a,d)|0)!=0}g=h;e=e+8|0;j=j+2|0;if((f|0)!=(j|0)){continue}break}if(!k){break c}}if(!g){g=0;break c}g=(ra[H[e>>2]](b,a,d)|0)!=0}Pa(i);if(g){break b}break a}Pa(i)}i=H[b+8>>2];f=H[i>>2];e=H[i+8>>2];e:{if(f){g=1;f:{if((f|0)!=1){k=f&1;f=f&-2;j=0;while(1){h=0;g:{if(!g){break g}h=0;if(!(ra[H[e>>2]](b,a,d)|0)){break g}h=(ra[H[e+4>>2]](b,a,d)|0)!=0}g=h;e=e+8|0;j=j+2|0;if((f|0)!=(j|0)){continue}break}if(!k){break f}}if(!g){g=0;break f}g=(ra[H[e>>2]](b,a,d)|0)!=0}Pa(i);if(!g){break a}break e}Pa(i)}if(!I[b+132|0]){Ba(d,1,11696,0);return 0}if(!I[b+133|0]){Ba(d,1,11667,0);return 0}l=Mb(a,H[b>>2],c,d);if(!c|!l){break a}a=H[c>>2];if(!a){break a}e=1;h:{i:{switch(H[b+48>>2]-12|0){case 5:e=2;break h;case 6:e=3;break h;case 12:e=4;break h;case 0:e=5;break h;case 4:break h;default:break i}}e=-1}H[a+20>>2]=e;c=H[b+108>>2];if(!c){break a}H[a+28>>2]=c;H[a+32>>2]=H[b+112>>2];H[b+108>>2]=0}return l|0}function Mb(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0;e=Eb();H[b+96>>2]=e;a:{b:{if(!e){break b}c:{if(Wa(H[b+220>>2],18,d)){if(Wa(H[b+220>>2],19,d)){break c}}break a}i=H[b+220>>2];f=H[i>>2];g=H[i+8>>2];d:{if(f){e=1;e:{if((f|0)!=1){k=f&1;f=f&-2;while(1){h=0;f:{if(!e){break f}h=0;if(!(ra[H[g>>2]](b,a,d)|0)){break f}h=(ra[H[g+4>>2]](b,a,d)|0)!=0}e=h;g=g+8|0;j=j+2|0;if((f|0)!=(j|0)){continue}break}if(!k){break e}}if(!e){e=0;break e}e=(ra[H[g>>2]](b,a,d)|0)!=0}Pa(i);if(e){break d}break a}Pa(i)}g:{if(Wa(H[b+216>>2],20,d)){if(Wa(H[b+216>>2],21,d)){break g}}break a}i=H[b+216>>2];f=H[i>>2];g=H[i+8>>2];h:{if(f){e=1;i:{if((f|0)!=1){k=f&1;f=f&-2;j=0;while(1){h=0;j:{if(!e){break j}h=0;if(!(ra[H[g>>2]](b,a,d)|0)){break j}h=(ra[H[g+4>>2]](b,a,d)|0)!=0}e=h;g=g+8|0;j=j+2|0;if((f|0)!=(j|0)){continue}break}if(!k){break i}}if(!e){e=0;break i}e=(ra[H[g>>2]](b,a,d)|0)!=0}Pa(i);if(e){break h}break a}Pa(i)}a=Eb();H[c>>2]=a;if(!a){break b}Ec(H[b+96>>2],a);l=1}return l|0}Ua(H[b+96>>2]);H[b+96>>2]=0;return 0}function zc(a){var b=0,c=0,d=0,e=0,f=0,g=0,h=0;b=H[6516];c=a+7&-8;a=c+7|0;d=a>>>0<7?1:d;e=a&-8;a=e+b|0;a:{b:{if(!(a>>>0>>0?d+1|0:d)){if(a>>>0<=sa()<<16>>>0){break b}if(ga(a|0)|0){break b}}H[6597]=48;b=-1;break a}H[6516]=a}d=b;if((d|0)!=-1){a=d+c|0;H[a-4>>2]=16;f=a-16|0;H[f>>2]=16;b=H[6856];if(b){e=H[b+8>>2]}else{e=0}c:{d:{if((e|0)==(d|0)){e=H[d-4>>2]&-2;g=d-e|0;h=H[g-4>>2];H[b+8>>2]=a;b=h&-2;a=g-b|0;if(H[(a+H[a>>2]|0)-4>>2]&1){f=H[a+4>>2];g=H[a+8>>2];H[f+8>>2]=g;H[g+4>>2]=f;b=(b+(c+e|0)|0)-16|0;H[a>>2]=b;break c}a=d-16|0;break d}H[d>>2]=16;H[d+8>>2]=a;H[d+4>>2]=b;H[d+12>>2]=16;H[6856]=d;a=d+16|0}b=f-a|0;H[a>>2]=b}H[((b&-4)+a|0)-4>>2]=b|1;c=H[a>>2]-8|0;e:{if(c>>>0<=127){b=(c>>>3|0)-1|0;break e}e=Q(c);b=((c>>>29-e^4)-(e<<2)|0)+110|0;if(c>>>0<=4095){break e}b=((c>>>30-e^2)-(e<<1)|0)+71|0;b=b>>>0>=63?63:b}c=b<<4;H[a+4>>2]=c+26400;c=c+26408|0;H[a+8>>2]=H[c>>2];H[c>>2]=a;H[H[a+8>>2]+4>>2]=a;c=H[6859];a=b&31;if((b&63)>>>0>=32){b=1<>>32-a}H[6858]=e|H[6858];H[6859]=b|c}return(d|0)!=-1}function Ec(a,b){var c=0,d=0,e=0,f=0,g=0;H[b>>2]=H[a>>2];H[b+4>>2]=H[a+4>>2];H[b+8>>2]=H[a+8>>2];H[b+12>>2]=H[a+12>>2];c=H[b+24>>2];if(c){d=H[b+16>>2];if(d){c=0;while(1){f=H[(H[b+24>>2]+N(c,52)|0)+44>>2];if(f){Ca(f);d=H[b+16>>2]}c=c+1|0;if(d>>>0>c>>>0){continue}break}c=H[b+24>>2]}Ca(c);H[b+24>>2]=0}c=H[a+16>>2];H[b+16>>2]=c;c=Fa(N(c,52));H[b+24>>2]=c;if(c){if(H[b+16>>2]){f=0;while(1){g=N(f,52);d=g+c|0;c=H[a+24>>2]+g|0;H[d+48>>2]=H[c+48>>2];e=H[c+44>>2];H[d+40>>2]=H[c+40>>2];H[d+44>>2]=e;e=H[c+36>>2];H[d+32>>2]=H[c+32>>2];H[d+36>>2]=e;e=H[c+28>>2];H[d+24>>2]=H[c+24>>2];H[d+28>>2]=e;e=H[c+20>>2];H[d+16>>2]=H[c+16>>2];H[d+20>>2]=e;e=H[c+12>>2];H[d+8>>2]=H[c+8>>2];H[d+12>>2]=e;e=H[c+4>>2];H[d>>2]=H[c>>2];H[d+4>>2]=e;c=H[b+24>>2];H[(g+c|0)+44>>2]=0;f=f+1|0;if(f>>>0>2]){continue}break}}H[b+20>>2]=H[a+20>>2];c=H[a+32>>2];H[b+32>>2]=c;a:{if(c){c=Fa(c);H[b+28>>2]=c;if(!c){H[b+28>>2]=0;H[b+32>>2]=0;return}b=H[a+32>>2];if(!b){break a}B(c,H[a+28>>2],b);return}H[b+28>>2]=0}return}H[b+16>>2]=0;H[b+24>>2]=0}function mb(a,b,c,d,e,f){var g=0,h=O(0),i=0,j=O(0);g=(c<<6)+b|0;b=c?g+-64|0:a;i=d>>>0>>0?d:e;if(i>>>0>c>>>0){h=L[b>>2];while(1){a=b;b=g;g=b-32|0;j=h;h=L[b>>2];L[g>>2]=O(O(j+h)*f)+L[g>>2];g=b-28|0;L[g>>2]=O(O(L[a+4>>2]+L[b+4>>2])*f)+L[g>>2];g=b-24|0;L[g>>2]=O(O(L[a+8>>2]+L[b+8>>2])*f)+L[g>>2];g=b-20|0;L[g>>2]=O(O(L[a+12>>2]+L[b+12>>2])*f)+L[g>>2];g=b-16|0;L[g>>2]=O(O(L[a+16>>2]+L[b+16>>2])*f)+L[g>>2];g=b-12|0;L[g>>2]=O(O(L[a+20>>2]+L[b+20>>2])*f)+L[g>>2];g=b-8|0;L[g>>2]=O(O(L[a+24>>2]+L[b+24>>2])*f)+L[g>>2];g=b-4|0;L[g>>2]=O(O(L[a+28>>2]+L[b+28>>2])*f)+L[g>>2];g=b- -64|0;c=c+1|0;if((i|0)!=(c|0)){continue}break}}if(d>>>0>e>>>0){a=g-32|0;f=O(f+f);L[a>>2]=O(L[b>>2]*f)+L[a>>2];a=g-28|0;L[a>>2]=O(L[b+4>>2]*f)+L[a>>2];a=g-24|0;L[a>>2]=O(L[b+8>>2]*f)+L[a>>2];a=g-20|0;L[a>>2]=O(L[b+12>>2]*f)+L[a>>2];a=g-16|0;L[a>>2]=O(L[b+16>>2]*f)+L[a>>2];a=g-12|0;L[a>>2]=O(L[b+20>>2]*f)+L[a>>2];a=g-8|0;L[a>>2]=O(L[b+24>>2]*f)+L[a>>2];a=g-4|0;L[a>>2]=O(L[b+28>>2]*f)+L[a>>2]}}function $c(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0;if(K[a+44>>2]>=8){e=H[a+36>>2];n=e<<5;o=N(e,28);p=N(e,24);q=N(e,20);r=e<<4;i=N(e,12);j=e<<3;g=H[a+40>>2];k=8;while(1){zb(a,g,H[a+36>>2],8);Ta(a);h=H[a+32>>2];if(h){f=H[a>>2];b=0;a:{if((h|0)!=1){s=h&1;t=h&-2;l=0;while(1){c=(b<<2)+g|0;d=f+(b<<5)|0;L[c>>2]=L[d>>2];m=e<<2;L[c+m>>2]=L[d+4>>2];L[c+j>>2]=L[d+8>>2];L[c+i>>2]=L[d+12>>2];d=b|1;c=(d<<2)+g|0;d=f+(d<<5)|0;L[c>>2]=L[d>>2];L[c+m>>2]=L[d+4>>2];L[c+j>>2]=L[d+8>>2];L[c+i>>2]=L[d+12>>2];b=b+2|0;l=l+2|0;if((t|0)!=(l|0)){continue}break}if(!s){break a}}c=(b<<2)+g|0;b=f+(b<<5)|0;L[c>>2]=L[b>>2];L[c+(e<<2)>>2]=L[b+4>>2];L[c+j>>2]=L[b+8>>2];L[c+i>>2]=L[b+12>>2]}d=H[a>>2];b=0;while(1){f=(b<<2)+g|0;c=d+(b<<5)|0;L[f+r>>2]=L[c+16>>2];L[f+q>>2]=L[c+20>>2];L[f+p>>2]=L[c+24>>2];L[f+o>>2]=L[c+28>>2];b=b+1|0;if((h|0)!=(b|0)){continue}break}}g=g+n|0;k=k+8|0;if(k>>>0<=K[a+44>>2]){continue}break}}Ca(H[a>>2]);Ca(a)}function wd(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0;f=na-16|0;na=f;a:{if(H[a+120>>2]|c>>>0<3){break a}Da(b,f+12|0,2);k=J[f+12>>1];if(k-1025>>>0<=4294966271){H[f>>2]=k;Ba(d,1,3526,f);break a}Da(b+2|0,f+12|0,1);i=J[f+12>>1];if(!i){Ba(d,1,3174,0);break a}if(i+3>>>0>c>>>0){break a}h=Fa(N(i,k)<<2);if(!h){break a}j=Fa(i);if(!j){Ca(h);break a}l=Fa(i);if(!l){Ca(h);Ca(j);break a}g=Fa(20);if(!g){Ca(h);Ca(j);Ca(l);break a}d=b+3|0;H[g+8>>2]=j;H[g+4>>2]=l;G[g+16>>1]=k;H[g>>2]=h;m=H[f+12>>2];H[g+12>>2]=0;F[g+18|0]=m;H[a+120>>2]=g;while(1){Da(d,f+12|0,1);F[e+j|0]=(I[f+12|0]&127)+1;F[e+l|0]=(H[f+12>>2]&128)>>>7;d=d+1|0;e=e+1|0;if((i|0)!=(e|0)){continue}break}g=0;while(1){e=0;a=0;while(1){e=I[e+j|0]+7>>>3|0;e=e>>>0>=4?4:e;if((e+(d-b|0)|0)>(c|0)){e=0;break a}Da(d,f+12|0,e);H[h>>2]=H[f+12>>2];h=h+4|0;d=d+e|0;a=a+1|0;e=a&65535;if(i>>>0>e>>>0){continue}break}e=1;g=g+1|0;if((g&65535)>>>0>>0){continue}break}}na=f+16|0;return e|0}function od(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0;f=-1;e=-1;if(!(I[a+68|0]&8)){f=H[a+32>>2];H[a+36>>2]=f;a:{b:{c:{e=H[a+48>>2];if(e){while(1){e=ra[H[a+20>>2]](f,e,H[a>>2])|0;if((e|0)==-1){break c}f=e+H[a+36>>2]|0;H[a+36>>2]=f;e=H[a+48>>2]-e|0;H[a+48>>2]=e;if(e){continue}break}f=H[a+32>>2]}H[a+36>>2]=f;if(!!b&(c|0)>=0|(c|0)>0){break b}f=0;e=0;break a}H[a+68>>2]=H[a+68>>2]|8;Ba(d,4,15604,0);H[a+48>>2]=0;H[a+68>>2]=H[a+68>>2]|8;qa=-1;return-1}f=0;e=0;while(1){g=ra[H[a+24>>2]](b,c,H[a>>2])|0;h=qa;i=h;if((g&h)==-1){Ba(d,4,15589,0);H[a+68>>2]=H[a+68>>2]|8;b=e+H[a+60>>2]|0;c=f+H[a+56>>2]|0;b=c>>>0>>0?b+1|0:b;H[a+56>>2]=c;H[a+60>>2]=b;a=!(e|f);b=a?-1:f;qa=a?-1:e;return b|0}e=e+i|0;f=f+g|0;e=f>>>0>>0?e+1|0:e;h=b;b=b-g|0;c=c-(i+(g>>>0>h>>>0)|0)|0;if(!!b&(c|0)>=0|(c|0)>0){continue}break}}b=e+H[a+60>>2]|0;c=f+H[a+56>>2]|0;b=c>>>0>>0?b+1|0:b;H[a+56>>2]=c;H[a+60>>2]=b}qa=e;return f|0}function xc(a){var b=0,c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0;b=a;a:{if(b&3){while(1){c=I[b|0];if(!c|(c|0)==61){break a}b=b+1|0;if(b&3){continue}break}}b:{c:{d=H[b>>2];if(((d|16843008-d)&-2139062144)!=-2139062144){break c}while(1){c=d^1027423549;if(((16843008-c|c)&-2139062144)!=-2139062144){break c}d=H[b+4>>2];c=b+4|0;b=c;if(((16843008-d|d)&-2139062144)==-2139062144){continue}break}break b}c=b}while(1){b=c;d=I[b|0];if(!d){break a}c=b+1|0;if((d|0)!=61){continue}break}}if((a|0)==(b|0)){return 0}g=b-a|0;d:{if(I[g+a|0]){break d}f=H[6860];if(!f){break d}b=H[f>>2];if(!b){break d}while(1){e:{d=a;c=b;h=g;e=0;f:{if(!g){break f}e=I[d|0];if(e){g:{while(1){i=I[c|0];if((i|0)!=(e|0)|!i){break g}h=h-1|0;if(!h){break g}c=c+1|0;e=I[d+1|0];d=d+1|0;if(e){continue}break}e=0}}else{e=0}e=e-I[c|0]|0}if(!e){b=b+g|0;if(I[b|0]==61){break e}}b=H[f+4>>2];f=f+4|0;if(b){continue}break d}break}j=b+1|0}return j}function be(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0;g=na-16|0;na=g;a:{if(c>>>0<=1){Ba(d,1,3983,0);a=0;break a}if(F[a+212|0]&1){Ba(d,1,12668,0);a=0;break a}a=H[a+180>>2]+N(H[a+228>>2],5644)|0;F[a+5640|0]=I[a+5640|0]|2;Da(b,g+12|0,1);e=H[a+5164>>2];b:{if(!e){f=H[g+12>>2]+1|0;e=Ea(f,8);H[a+5164>>2]=e;if(!e){Ba(d,1,4009,0);a=0;break a}H[a+5160>>2]=f;break b}f=H[g+12>>2];if(f>>>0>2]){break b}h=e;e=f+1|0;f=Ha(h,e<<3);if(!f){Ba(d,1,4009,0);a=0;break a}H[a+5164>>2]=f;h=H[a+5160>>2];i=e-h<<3;if(i){y(f+(h<<3)|0,0,i)}H[a+5160>>2]=e;e=H[a+5164>>2]}h=e;e=H[g+12>>2];if(H[h+(e<<3)>>2]){H[g>>2]=e;Ba(d,1,7063,g);a=0;break a}c=c-1|0;e=Fa(c);a=H[a+5164>>2];f=a+(H[g+12>>2]<<3)|0;H[f>>2]=e;if(!e){Ba(d,1,4009,0);a=0;break a}H[f+4>>2]=c;if(c){B(H[a+(H[g+12>>2]<<3)>>2],b+1|0,c)}a=1}na=g+16|0;return a|0}function Cb(a,b,c){var d=0,e=0,f=0,g=0;e=a+4|0;d=(e+b|0)-1&0-b;b=H[a>>2];if(d+c>>>0<=(b+a|0)-4>>>0){f=H[a+4>>2];g=H[a+8>>2];H[f+8>>2]=g;H[g+4>>2]=f;if((d|0)!=(e|0)){d=d-e|0;f=a-(H[a-4>>2]&-2)|0;e=d+H[f>>2]|0;H[f>>2]=e;H[(f+(e&-4)|0)-4>>2]=e;a=a+d|0;b=b-d|0;H[a>>2]=b}a:{if(c+24>>>0<=b>>>0){e=a+c|0;b=(b-c|0)-8|0;H[e+8>>2]=b;g=e+8|0;H[(g+(b&-4)|0)-4>>2]=b|1;d=H[e+8>>2]-8|0;b:{if(d>>>0<=127){b=(d>>>3|0)-1|0;break b}f=Q(d);b=((d>>>29-f^4)-(f<<2)|0)+110|0;if(d>>>0<=4095){break b}b=((d>>>30-f^2)-(f<<1)|0)+71|0;b=b>>>0>=63?63:b}d=b<<4;H[e+12>>2]=d+26400;d=d+26408|0;H[e+16>>2]=H[d>>2];H[d>>2]=g;H[H[e+16>>2]+4>>2]=g;d=H[6858];f=H[6859];e=b&31;if((b&63)>>>0>=32){b=1<>>32-e}H[6858]=g|d;H[6859]=b|f;b=c+8|0;H[a>>2]=b;c=(b&-4)+a|0;break a}c=a+b|0}H[c-4>>2]=b;a=a+4|0}else{a=0}return a}function he(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0;g=na-16|0;na=g;i=H[H[a+96>>2]+16>>2];h=i>>>0<257?1:2;e=(h<<1)+5|0;f=(c>>>0)/(e>>>0)|0;a:{if(!((N(e,f)|0)==(c|0)&c>>>0>=e>>>0)){Ba(d,1,4643,0);a=0;break a}if(H[a+8>>2]==16){e=H[a+180>>2]+N(H[a+228>>2],5644)|0}else{e=H[a+12>>2]}a=0;c=I[e+5640|0];a=c&4?H[e+420>>2]+1|0:a;f=f+a|0;if(f>>>0>=32){H[g>>2]=f;Ba(d,1,7781,g);a=0;break a}F[e+5640|0]=c|4;if(a>>>0>>0){c=(e+N(a,148)|0)+424|0;while(1){Da(b,c,1);b=b+1|0;Da(b,c+4|0,h);b=b+h|0;Da(b,c+8|0,2);d=H[c+8>>2];j=H[e+8>>2];H[c+8>>2]=d>>>0>>0?d:j;Da(b+2|0,c+12|0,1);b=b+3|0;Da(b,c+16|0,h);b=b+h|0;Da(b,g+12|0,1);H[c+36>>2]=H[g+12>>2];d=H[c+16>>2];H[c+16>>2]=d>>>0>>0?d:i;c=c+148|0;b=b+1|0;a=a+1|0;if((f|0)!=(a|0)){continue}break}}H[e+420>>2]=f-1;a=1}na=g+16|0;return a|0}function jb(a){var b=0,c=0,d=0,e=0;a:{if(!a){break a}b=H[a+5164>>2];if(b){c=H[a+5160>>2];if(c){b=0;while(1){d=H[H[a+5164>>2]+(b<<3)>>2];if(d){Ca(d);c=H[a+5160>>2]}b=b+1|0;if(c>>>0>b>>>0){continue}break}b=H[a+5164>>2]}H[a+5160>>2]=0;Ca(b);H[a+5164>>2]=0}b=H[a+5172>>2];if(b){Ca(b);H[a+5172>>2]=0}b=H[a+5584>>2];if(b){Ca(b);H[a+5584>>2]=0}b=H[a+5612>>2];if(b){Ca(b);H[a+5612>>2]=0}b=H[a+5608>>2];if(b){Ca(b);H[a+5608>>2]=0}b=H[a+5628>>2];if(b){Ca(b);H[a+5636>>2]=0;H[a+5628>>2]=0;H[a+5632>>2]=0}b=H[a+5616>>2];if(b){e=H[a+5620>>2];if(e){c=0;while(1){d=H[b+12>>2];if(d){Ca(d);H[b+12>>2]=0;e=H[a+5620>>2]}b=b+20|0;c=c+1|0;if(e>>>0>c>>>0){continue}break}b=H[a+5616>>2]}Ca(b);H[a+5616>>2]=0}b=H[a+5604>>2];if(b){Ca(b);H[a+5604>>2]=0}b=H[a+5596>>2];if(!b){break a}Ca(b);H[a+5596>>2]=0;H[a+5600>>2]=0}}function zd(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0;e=na-32|0;na=e;a:{if(H[a+72>>2]){Ba(d,2,7015,0);c=1;break a}if((c|0)!=14){c=0;Ba(d,1,14445,0);break a}Da(b,a+16|0,4);Da(b+4|0,a+12|0,4);Da(b+8|0,a+20|0,2);f=H[a+12>>2];b:{g=H[a+16>>2];c=H[a+20>>2];c:{if(!g){break c}c=H[a+20>>2];if(!f){break c}if(c){break b}c=0}H[e+8>>2]=c;H[e+4>>2]=g;H[e>>2]=f;Ba(d,1,14289,e);c=0;break a}if(c>>>0>=16385){c=0;Ba(d,1,14203,0);break a}c=Ea(c,12);H[a+72>>2]=c;if(!c){c=0;Ba(d,1,14240,0);break a}c=1;Da(b+10|0,a+24|0,1);Da(b+11|0,a+28|0,1);f=H[a+28>>2];if((f|0)!=7){H[e+16>>2]=f;Ba(d,4,16272,e+16|0)}Da(b+12|0,a+32|0,1);Da(b+13|0,a+36|0,1);b=H[a>>2];F[b+212|0]=I[b+212|0]&251|(H[a+24>>2]==255?4:0);b=H[a>>2];H[b+240>>2]=H[a+12>>2];H[b+244>>2]=H[a+16>>2];F[a+133|0]=1}na=e+32|0;return c|0}function qc(a,b,c,d){a:{switch(b-9|0){case 0:b=H[c>>2];H[c>>2]=b+4;H[a>>2]=H[b>>2];return;case 6:b=H[c>>2];H[c>>2]=b+4;b=G[b>>1];H[a>>2]=b;H[a+4>>2]=b>>31;return;case 7:b=H[c>>2];H[c>>2]=b+4;H[a>>2]=J[b>>1];H[a+4>>2]=0;return;case 8:b=H[c>>2];H[c>>2]=b+4;b=F[b|0];H[a>>2]=b;H[a+4>>2]=b>>31;return;case 9:b=H[c>>2];H[c>>2]=b+4;H[a>>2]=I[b|0];H[a+4>>2]=0;return;case 16:b=H[c>>2]+7&-8;H[c>>2]=b+8;M[a>>3]=M[b>>3];return;case 17:ra[d|0](a,c);default:return;case 1:case 4:case 14:b=H[c>>2];H[c>>2]=b+4;b=H[b>>2];H[a>>2]=b;H[a+4>>2]=b>>31;return;case 2:case 5:case 11:case 15:b=H[c>>2];H[c>>2]=b+4;H[a>>2]=H[b>>2];H[a+4>>2]=0;return;case 3:case 10:case 12:case 13:break a}}b=H[c>>2]+7&-8;H[c>>2]=b+8;c=H[b+4>>2];H[a>>2]=H[b>>2];H[a+4>>2]=c}function ce(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0;g=na-16|0;na=g;a:{if(c>>>0<=1){Ba(d,1,4311,0);a=0;break a}F[a+212|0]=I[a+212|0]|1;Da(b,g+12|0,1);e=H[a+140>>2];b:{if(!e){f=H[g+12>>2]+1|0;e=Ea(f,8);H[a+140>>2]=e;if(!e){Ba(d,1,4337,0);a=0;break a}H[a+136>>2]=f;break b}f=H[g+12>>2];if(f>>>0>2]){break b}h=e;e=f+1|0;f=Ha(h,e<<3);if(!f){Ba(d,1,4337,0);a=0;break a}H[a+140>>2]=f;h=H[a+136>>2];i=e-h<<3;if(i){y(f+(h<<3)|0,0,i)}H[a+136>>2]=e;e=H[a+140>>2]}h=e;e=H[g+12>>2];if(H[h+(e<<3)>>2]){H[g>>2]=e;Ba(d,1,7085,g);a=0;break a}c=c-1|0;e=Fa(c);a=H[a+140>>2];f=a+(H[g+12>>2]<<3)|0;H[f>>2]=e;if(!e){Ba(d,1,4337,0);a=0;break a}H[f+4>>2]=c;if(c){B(H[a+(H[g+12>>2]<<3)>>2],b+1|0,c)}a=1}na=g+16|0;return a|0}function cd(a,b){a=a|0;b=+b;var c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0;c=a<<3;i=c+28640|0;d=M[c+28672>>3];if(d!=0){e=M[i>>3];g=S(e,b);b=g-e;if(b<0x10000000000000000&b>=0){f=~~b>>>0;if(P(b)>=1){c=~~(b>0?R(T(b*2.3283064365386963e-10),4294967295):U((b-+(~~b>>>0>>>0))*2.3283064365386963e-10))>>>0}else{c=0}}else{c=0}if(d<0x10000000000000000&d>=0){j=~~d>>>0;if(P(d)>=1){h=~~(d>0?R(T(d*2.3283064365386963e-10),4294967295):U((d-+(~~d>>>0>>>0))*2.3283064365386963e-10))>>>0}else{h=0}}else{h=0}c=ve(f,c,j,h);f=qa;c=c+1|0;f=c?f:f+1|0;e=(+(c>>>0)+ +(f>>>0)*4294967296)*d+e;g=e-g}M[i>>3]=e;fa(a|0,+g)|0;c=(a|0)==2?27:(a|0)==1?26:14;a=c-1|0;a:{if(H[6892]>>>a&1){H[6894]=H[6894]|1<>2];if(a){ra[a|0](c)}}}function jd(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0,h=0,i=0,j=0;d=na-32|0;na=d;e=H[a+28>>2];H[d+16>>2]=e;f=H[a+20>>2];H[d+28>>2]=c;H[d+24>>2]=b;b=f-e|0;H[d+20>>2]=b;f=b+c|0;i=2;b=d+16|0;a:{while(1){b:{c:{d:{if(!yc(Z(H[a+60>>2],b|0,i|0,d+12|0)|0)){g=H[d+12>>2];if((g|0)==(f|0)){break d}if((g|0)>=0){break c}break b}if((f|0)!=-1){break b}}b=H[a+44>>2];H[a+28>>2]=b;H[a+20>>2]=b;H[a+16>>2]=b+H[a+48>>2];a=c;break a}h=H[b+4>>2];j=h>>>0>>0;e=(j?8:0)+b|0;h=g-(j?h:0)|0;H[e>>2]=h+H[e>>2];b=(j?12:4)+b|0;H[b>>2]=H[b>>2]-h;f=f-g|0;i=i-j|0;b=e;continue}break}H[a+28>>2]=0;H[a+16>>2]=0;H[a+20>>2]=0;H[a>>2]=H[a>>2]|32;a=0;if((i|0)==2){break a}a=c-H[b+4>>2]|0}na=d+32|0;return a|0}function Ca(a){a=a|0;var b=0,c=0,d=0,e=0,f=0;if(a){b=a-4|0;f=H[b>>2];c=f;d=b;e=H[a-8>>2];a=e&-2;if((a|0)!=(e|0)){d=b-a|0;c=H[d+4>>2];e=H[d+8>>2];H[c+8>>2]=e;H[e+4>>2]=c;c=a+f|0}a=b+f|0;b=H[a>>2];if((b|0)!=H[(a+b|0)-4>>2]){f=H[a+4>>2];a=H[a+8>>2];H[f+8>>2]=a;H[a+4>>2]=f;c=b+c|0}H[d>>2]=c;H[((c&-4)+d|0)-4>>2]=c|1;b=H[d>>2]-8|0;a:{if(b>>>0<=127){a=(b>>>3|0)-1|0;break a}c=Q(b);a=((b>>>29-c^4)-(c<<2)|0)+110|0;if(b>>>0<=4095){break a}a=((b>>>30-c^2)-(c<<1)|0)+71|0;a=a>>>0>=63?63:a}b=a<<4;H[d+4>>2]=b+26400;b=b+26408|0;H[d+8>>2]=H[b>>2];H[b>>2]=d;H[H[d+8>>2]+4>>2]=d;b=H[6858];c=H[6859];d=a&31;if((a&63)>>>0>=32){a=1<>>32-d}H[6858]=e|b;H[6859]=a|c}}function td(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0;e=na-16|0;na=e;a:{if(H[a+116>>2]){break a}if(c>>>0<=1){Ba(d,1,8882,0);break a}Da(b,e+12|0,2);f=H[e+12>>2];h=f&65535;if(!h){Ba(d,1,8915,0);break a}if(N(h,6)+2>>>0>c>>>0){Ba(d,1,8882,0);break a}d=Fa(N(f,6));if(!d){break a}c=Fa(8);H[a+116>>2]=c;if(!c){Ca(d);break a}H[c>>2]=d;f=c;c=J[e+12>>1];G[f+4>>1]=c;if(!c){g=1;break a}c=0;while(1){g=e+12|0;Da(b+2|0,g,2);f=d+N(c,6)|0;G[f>>1]=H[e+12>>2];Da(b+4|0,g,2);G[f+2>>1]=H[e+12>>2];b=b+6|0;Da(b,g,2);G[f+4>>1]=H[e+12>>2];g=1;c=c+1|0;if(c>>>0>2]+4>>1]){continue}break}}na=e+16|0;return g|0}function Lb(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0;g=na-32|0;na=g;f=H[a+96>>2];a:{if(!f){Ba(d,1,13752,0);e=0;break a}f=Ea(4,H[f+16>>2]);e=0;if(!f){break a}if(b){j=H[a+96>>2];while(1){b:{e=H[(h<<2)+c>>2];c:{if(e>>>0>=K[j+16>>2]){H[g+16>>2]=e;Ba(d,1,2443,g+16|0);break c}i=f+(e<<2)|0;if(!H[i>>2]){break b}H[g>>2]=e;Ba(d,1,3487,g)}Ca(f);e=0;break a}H[i>>2]=1;h=h+1|0;if((h|0)!=(b|0)){continue}break}}Ca(f);Ca(H[a+64>>2]);d:{if(b){d=b<<2;e=Fa(d);H[a+64>>2]=e;if(!e){H[a+60>>2]=0;e=0;break a}if(!d){break d}B(e,c,d);break d}H[a+64>>2]=0}H[a+60>>2]=b;e=1}na=g+32|0;return e|0}function Fc(a){a=a|0;var b=0,c=0;if(a){wb(H[a>>2]);H[a>>2]=0;b=H[a+72>>2];if(b){Ca(b);H[a+72>>2]=0}b=H[a+68>>2];if(b){Ca(b);H[a+68>>2]=0}b=H[a+108>>2];if(b){Ca(b);H[a+108>>2]=0}b=H[a+116>>2];if(b){c=H[b>>2];if(c){Ca(c);b=H[a+116>>2];H[b>>2]=0}Ca(b);H[a+116>>2]=0}b=H[a+120>>2];if(b){c=H[b+12>>2];if(c){Ca(c);b=H[a+120>>2];H[b+12>>2]=0}c=H[b+4>>2];if(c){Ca(c);b=H[a+120>>2];H[b+4>>2]=0}c=H[b+8>>2];if(c){Ca(c);b=H[a+120>>2];H[b+8>>2]=0}c=H[b>>2];if(c){Ca(c);b=H[a+120>>2];H[b>>2]=0}Ca(b);H[a+120>>2]=0}b=H[a+4>>2];if(b){pb(b);H[a+4>>2]=0}b=H[a+8>>2];if(b){pb(b);H[a+8>>2]=0}Ca(a)}}function Jb(){var a=0,b=0,c=0;a:{a=Ea(1,256);if(a){H[a>>2]=1;H[a+208>>2]=1;F[a+212|0]=I[a+212|0]|6;b=Ea(1,5644);H[a+12>>2]=b;if(!b){break a}b=Ea(1,1e3);H[a+16>>2]=b;if(!b){break a}H[a+48>>2]=0;H[a+52>>2]=0;H[a+44>>2]=-1;H[a+20>>2]=1e3;b:{c=Ea(1,48);if(c){H[c+24>>2]=0;H[c+32>>2]=100;b=Ea(100,24);H[c+28>>2]=b;if(b){break b}Ca(c)}H[a+224>>2]=0;break a}H[c+40>>2]=0;H[a+224>>2]=c;b=qb();H[a+220>>2]=b;if(!b){break a}b=qb();H[a+216>>2]=b;if(!b){break a}c:{if(!xc(1419)){break c}}b=jc();H[a+236>>2]=b;if(!b){b=jc();H[a+236>>2]=b;if(!b){break a}}}else{a=0}return a}wb(a);return 0}function tb(a,b,c,d,e,f){var g=0,h=0,i=0,j=0,k=0,l=0;g=na-240|0;na=g;H[g+236>>2]=c;H[g+232>>2]=b;H[g>>2]=a;l=!e;a:{b:{c:{d:{if((b|0)!=1){h=a;i=1;break d}h=a;i=1;if(c){break d}e=a;break c}while(1){j=(d<<2)+f|0;e=h-H[j>>2]|0;if((Ic(e,a)|0)<=0){e=h;break c}k=l^-1;l=1;e:{if(!((k|(d|0)<2)&1)){j=H[j-8>>2];k=h-8|0;if((Ic(k,e)|0)>=0){break e}if((Ic(k-j|0,e)|0)>=0){break e}}H[(i<<2)+g>>2]=e;b=wc(b,c);ub(g+232|0,b);i=i+1|0;d=b+d|0;h=e;c=H[g+236>>2];b=H[g+232>>2];if(c|(b|0)!=1){continue}break b}break}e=h;break b}if(!l){break a}}vc(g,i);Bb(e,d,f)}na=g+240|0}function me(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0;e=na-16|0;na=e;if(H[a+8>>2]==16){g=H[a+180>>2]+N(H[a+228>>2],5644)|0}else{g=H[a+12>>2]}h=H[a+96>>2];f=K[h+16>>2]<257?1:2;a:{if(f>>>0>=c>>>0){c=0;Ba(d,1,4669,0);break a}H[e+12>>2]=(f^-1)+c;Da(b,e+8|0,f);i=H[e+8>>2];if(i>>>0>=K[h+16>>2]){c=0;Ba(d,1,14067,0);break a}c=1;b=b+f|0;Da(b,H[g+5584>>2]+N(i,1080)|0,1);if(!Lc(a,H[e+8>>2],b+1|0,e+12|0,d)){c=0;Ba(d,1,4669,0);break a}if(!H[e+12>>2]){break a}c=0;Ba(d,1,4669,0)}na=e+16|0;return c|0}function Gc(a,b){var c=0,d=0,e=0,f=0,g=0;f=na-32|0;na=f;c=H[a+60>>2];a:{b:{if(c){g=1;while(1){e=H[H[a+64>>2]+(d<<2)>>2];if(!H[(H[H[a+100>>2]+24>>2]+N(e,52)|0)+44>>2]){H[f+16>>2]=e;Ba(b,2,7604,f+16|0);g=0;c=H[a+60>>2]}d=d+1|0;if(c>>>0>d>>>0){continue}break}break b}g=1;c=H[a+100>>2];e=1;if(!H[c+16>>2]){break a}while(1){if(!H[(H[c+24>>2]+N(d,52)|0)+44>>2]){H[f>>2]=d;Ba(b,2,7604,f);g=0;c=H[a+100>>2]}d=d+1|0;if(d>>>0>2]){continue}break}}e=1;if(g){break a}Ba(b,1,2897,0);e=0}na=f+32|0;return e}function vd(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0;f=na-16|0;na=f;e=H[a+120>>2];a:{if(!e){Ba(d,1,8836,0);c=0;break a}if(H[e+12>>2]){Ba(d,1,11598,0);c=0;break a}e=I[e+18|0];g=e<<2;if(g>>>0>c>>>0){Ba(d,1,8803,0);c=0;break a}g=Fa(g);c=0;if(!g){break a}if(e){d=0;while(1){c=f+12|0;Da(b,c,2);h=g+(d<<2)|0;G[h>>1]=H[f+12>>2];Da(b+2|0,c,1);F[h+2|0]=H[f+12>>2];Da(b+3|0,c,1);F[h+3|0]=H[f+12>>2];b=b+4|0;d=d+1|0;if((e|0)!=(d|0)){continue}break}}H[H[a+120>>2]+12>>2]=g;c=1}na=f+16|0;return c|0}function Zd(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0;e=na-16|0;na=e;g=H[H[a+96>>2]+16>>2];a:{if((g+2|0)!=(c|0)){Ba(d,1,4617,0);break a}Da(b,e+12|0,2);if(H[e+12>>2]!=(g|0)){Ba(d,1,4617,0);break a}if(!g){f=1;break a}c=b+2|0;a=H[H[a+96>>2]+24>>2];b=0;while(1){Da(c,e+8|0,1);f=H[e+8>>2];h=f&127;i=h+1|0;H[a+24>>2]=i;H[a+32>>2]=f>>>7&1;if(h>>>0>=31){H[e+4>>2]=i;H[e>>2]=b;Ba(d,1,15402,e);f=0;break a}a=a+52|0;f=1;c=c+1|0;b=b+1|0;if((g|0)!=(b|0)){continue}break}}na=e+16|0;return f|0}function tc(a,b,c,d,e){var f=0,g=0;f=na-208|0;na=f;H[f+204>>2]=c;c=f+160|0;y(c,0,40);H[f+200>>2]=H[f+204>>2];if((sc(0,b,f+200|0,f+80|0,c,d,e)|0)>=0){c=H[a>>2];H[a>>2]=c&-33;a:{b:{c:{if(!H[a+48>>2]){H[a+48>>2]=80;H[a+28>>2]=0;H[a+16>>2]=0;H[a+20>>2]=0;g=H[a+44>>2];H[a+44>>2]=f;break c}if(H[a+16>>2]){break b}}if(Db(a)){break a}}sc(a,b,f+200|0,f+80|0,f+160|0,d,e)}if(g){ra[H[a+36>>2]](a,0,0)|0;H[a+48>>2]=0;H[a+44>>2]=g;H[a+28>>2]=0;H[a+16>>2]=0;H[a+20>>2]=0}H[a>>2]=H[a>>2]|c&32}na=f+208|0}function je(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0;e=na-16|0;na=e;a:{b:{h=e+8|0;c:{if(K[H[a+96>>2]+16>>2]<=256){if(c){f=-1;g=1;break c}Ba(d,1,4695,0);a=0;break a}if(c>>>0<=1){break b}f=-2;g=2}Da(b,h,g);H[e+12>>2]=c+f;c=H[e+8>>2];f=H[H[a+96>>2]+16>>2];if(c>>>0>=f>>>0){H[e+4>>2]=f;H[e>>2]=c;Ba(d,1,7712,e);a=0;break a}if(!Kc(a,c,b+g|0,e+12|0,d)){Ba(d,1,4695,0);a=0;break a}a=1;if(!H[e+12>>2]){break a}Ba(d,1,4695,0);a=0;break a}Ba(d,1,4695,0);a=0}na=e+16|0;return a|0}function Ed(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0;f=H[a+32>>2];H[a+36>>2]=f;a:{e=H[a+48>>2];if(e){while(1){e=ra[H[a+20>>2]](f,e,H[a>>2])|0;if((e|0)==-1){break a}f=e+H[a+36>>2]|0;H[a+36>>2]=f;e=H[a+48>>2]-e|0;H[a+48>>2]=e;if(e){continue}break}f=H[a+32>>2]}H[a+48>>2]=0;H[a+36>>2]=f;if(!(ra[H[a+28>>2]](b,c,H[a>>2])|0)){H[a+68>>2]=H[a+68>>2]|8;return 0}H[a+56>>2]=b;H[a+60>>2]=c;return 1}H[a+68>>2]=H[a+68>>2]|8;Ba(d,4,15604,0);H[a+68>>2]=H[a+68>>2]|8;return 0}function Ba(a,b,c,d){var e=0,f=0;e=na-528|0;na=e;a:{if(!a){break a}b:{c:{switch(b-1|0){case 0:b=a+12|0;break b;case 1:b=a+16|0;a=a+4|0;break b;case 3:break c;default:break a}}b=a+20|0;a=a+8|0}b=H[b>>2];if(!b|!c){break a}f=H[a>>2];y(e,0,512);H[e+524>>2]=d;a=na-160|0;na=a;H[a+148>>2]=e;H[a+152>>2]=511;y(a,0,144);H[a+76>>2]=-1;H[a+36>>2]=105;H[a+80>>2]=-1;H[a+44>>2]=a+159;H[a+84>>2]=a+148;F[e|0]=0;tc(a,c,d,103,104);na=a+160|0;F[e+511|0]=0;ra[b|0](e,f)}na=e+528|0}function fc(a){var b=0,c=0,d=0;a:{if(!a){break a}b=H[a+8>>2];if(!b){break a}c=b&3;a=H[a+12>>2];if(b>>>0>=4){d=b&-4;b=0;while(1){H[a+60>>2]=0;H[a+52>>2]=999;H[a+56>>2]=0;H[a+44>>2]=0;H[a+36>>2]=999;H[a+40>>2]=0;H[a+28>>2]=0;H[a+20>>2]=999;H[a+24>>2]=0;H[a+12>>2]=0;H[a+4>>2]=999;H[a+8>>2]=0;a=a- -64|0;b=b+4|0;if((d|0)!=(b|0)){continue}break}if(!c){break a}}b=0;while(1){H[a+12>>2]=0;H[a+4>>2]=999;H[a+8>>2]=0;a=a+16|0;b=b+1|0;if((c|0)!=(b|0)){continue}break}}}function Bd(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0;if(H[a+100>>2]!=1){Ba(d,1,11401,0);return 0}a:{if(c>>>0<=7){break a}Da(b,a+56|0,4);Da(b+4|0,a+60|0,4);if(c&3){break a}c=c-8|0;e=c>>>2|0;H[a+64>>2]=e;b:{if(!c){break b}c=Ea(e,4);H[a+68>>2]=c;if(!c){Ba(d,1,2235,0);return 0}if(!H[a+64>>2]){break b}d=b+8|0;c=0;while(1){Da(d,H[a+68>>2]+(c<<2)|0,4);d=d+4|0;c=c+1|0;if(c>>>0>2]){continue}break}}H[a+100>>2]=H[a+100>>2]|2;return 1}Ba(d,1,5955,0);return 0}function ke(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0;e=na-16|0;na=e;H[e+12>>2]=c;a:{if(!(!Kc(a,0,b,e+12|0,d)|H[e+12>>2])){if(H[a+8>>2]==16){b=H[a+180>>2]+N(H[a+228>>2],5644)|0}else{b=H[a+12>>2]}f=1;if(K[H[a+96>>2]+16>>2]<2){break a}c=H[b+5584>>2];g=c+28|0;b=1;d=c;while(1){H[d+1104>>2]=H[c+24>>2];H[d+1884>>2]=H[c+804>>2];B(d+1108|0,g,776);d=d+1080|0;b=b+1|0;if(b>>>0>2]+16>>2]){continue}break}break a}Ba(d,1,4591,0)}na=e+16|0;return f|0}function dc(a,b,c,d){var e=0,f=0,g=0;f=na-128|0;na=f;e=f;b=H[b+12>>2]+(c<<4)|0;c=H[b>>2];if(c){while(1){H[e>>2]=b;e=e+4|0;b=c;c=H[c>>2];if(c){continue}break}}while(1){c=H[b+8>>2];if((g|0)>(c|0)){H[b+8>>2]=g;c=g}a:{if((c|0)>=(d|0)){break a}while(1){if(H[b+4>>2]<=(c|0)){break a}b:{if(Ra(a,1)){H[b+4>>2]=c;break b}c=c+1|0}if((c|0)<(d|0)){continue}break}}H[b+8>>2]=c;if((e|0)!=(f|0)){e=e-4|0;b=H[e>>2];g=c;continue}break}na=f+128|0;return H[b+4>>2]<(d|0)}function Ld(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0;a:{if(!Wa(H[a+8>>2],54,c)){break a}h=H[a+8>>2];d=H[h>>2];f=H[h+8>>2];b:{if(d){e=1;c:{if((d|0)!=1){j=d&1;d=d&-2;while(1){g=0;d:{if(!e){break d}g=0;if(!(ra[H[f>>2]](a,b,c)|0)){break d}g=(ra[H[f+4>>2]](a,b,c)|0)!=0}e=g;f=f+8|0;i=i+2|0;if((d|0)!=(i|0)){continue}break}if(!j){break c}}if(!e){e=0;break c}e=(ra[H[f>>2]](a,b,c)|0)!=0}Pa(h);if(!e){break a}break b}Pa(h)}k=1}return k|0}function le(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0;e=na-16|0;na=e;g=H[H[a+96>>2]+16>>2];f=g>>>0<257?1:2;a:{if((f+2|0)!=(c|0)){a=0;Ba(d,1,4285,0);break a}if(H[a+8>>2]==16){c=H[a+180>>2]+N(H[a+228>>2],5644)|0}else{c=H[a+12>>2]}Da(b,e+12|0,f);a=1;b=b+f|0;Da(b,e+8|0,1);f=H[e+12>>2];if(f>>>0>=g>>>0){H[e+4>>2]=g;H[e>>2]=f;Ba(d,1,14923,e);a=0;break a}Da(b+1|0,(H[c+5584>>2]+N(f,1080)|0)+808|0,1)}na=e+16|0;return a|0}function qe(){var a=0,b=0,c=0;while(1){b=a<<4;c=b+26400|0;H[b+26404>>2]=c;H[b+26408>>2]=c;a=a+1|0;if((a|0)!=64){continue}break}zc(48);a=na-16|0;na=a;a:{if(la(a+12|0,a+8|0)|0){break a}b=vb((H[a+12>>2]<<2)+4|0);H[6860]=b;if(!b){break a}b=vb(H[a+8>>2]);if(b){c=H[6860];H[c+(H[a+12>>2]<<2)>>2]=0;if(!(ka(c|0,b|0)|0)){break a}}H[6860]=0}na=a+16|0;H[6875]=8192;H[6873]=94240;H[6867]=42;H[6874]=65536}function ye(a,b,c){var d=0,e=0,f=0,g=0;g=c&63;f=g;e=f&31;if(f>>>0>=32){f=-1>>>e|0}else{d=-1>>>e|0;f=d|(1<>>0>=32){d=f<>>32-e|d<>>0>=32){d=-1<>>32-d}a=c&a;b=b&d;d=e&31;if(e>>>0>=32){c=0;a=b>>>d|0}else{c=b>>>d|0;a=((1<>>d}a=a|g;qa=c|f;return a} +function eb(a,b,c){var d=0;if(!H[a+12>>2]){ra[b|0](c,H[a+36>>2]);return}d=Fa(8);a:{if(!d){break a}H[d+4>>2]=c;H[d>>2]=b;b=Fa(8);if(!b){Ca(d);return}H[b>>2]=d;c=N(H[a+4>>2],100);H[a+40>>2]=c;while(1){if((c|0)>2]){continue}break}H[b+4>>2]=H[a+20>>2];H[a+20>>2]=b;H[a+24>>2]=H[a+24>>2]+1;b=H[a+28>>2];if(!b){break a}H[H[b>>2]+8>>2]=0;H[a+28>>2]=H[b+4>>2];H[a+32>>2]=H[a+32>>2]-1;Ca(b)}}function Mc(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0;H[a+184>>2]=b;d=H[a+96>>2];a:{if(!d){break a}f=H[d+24>>2];if(!f){break a}e=H[a+12>>2];if(!e|!H[e+5584>>2]){break a}e=H[d+16>>2];if(!e){return 1}d=0;while(1){if(K[(H[H[a+12>>2]+5584>>2]+N(d,1080)|0)+4>>2]<=b>>>0){Ba(c,1,9177,0);return 0}H[(N(d,52)+f|0)+40>>2]=b;g=1;d=d+1|0;if((e|0)!=(d|0)){continue}break}}return g|0}function Ac(a,b,c){var d=0,e=0,f=0;a:{d=H[c+16>>2];if(!d){if(Db(c)){break a}d=H[c+16>>2]}e=H[c+20>>2];if(d-e>>>0>>0){ra[H[c+36>>2]](c,a,b)|0;return}b:{c:{if(!b|H[c+80>>2]<0){break c}d=b;while(1){f=a+d|0;if(I[f-1|0]!=10){d=d-1|0;if(d){continue}break c}break}if(ra[H[c+36>>2]](c,a,d)>>>0>>0){break a}b=b-d|0;e=H[c+20>>2];break b}f=a}ab(e,f,b);H[c+20>>2]=H[c+20>>2]+b}}function Bc(a){var b=0,c=0;b=H[a+76>>2];if(!((b|0)>=0&(!b|H[6867]!=(b&1073741823)))){a:{if(H[a+80>>2]==10){break a}b=H[a+20>>2];if((b|0)==H[a+16>>2]){break a}H[a+20>>2]=b+1;F[b|0]=10;return}Cc(a);return}b=a+76|0;c=H[b>>2];H[b>>2]=c?c:1073741823;b:{c:{if(H[a+80>>2]==10){break c}c=H[a+20>>2];if((c|0)==H[a+16>>2]){break c}H[a+20>>2]=c+1;F[c|0]=10;break b}Cc(a)}H[b>>2]=0}function Ka(a,b,c,d,e,f,g,h){var i=0,j=0;i=+O(e-a|0);j=i*1.402;if(P(j)<2147483647){e=~~j}else{e=-2147483648}e=e+c|0;H[f>>2]=(e|0)>=0?(b|0)>(e|0)?e:b:0;j=+O(d-a|0);i=j*.344+i*.714;if(P(i)<2147483647){a=~~i}else{a=-2147483648}a=c-a|0;H[g>>2]=(a|0)>=0?(a|0)<(b|0)?a:b:0;i=j*1.772;if(P(i)<2147483647){a=~~i}else{a=-2147483648}a=a+c|0;H[h>>2]=(a|0)>=0?(a|0)<(b|0)?a:b:0}function yb(a,b){var c=0,d=0,e=0,f=0,g=0,h=0;if(a){c=H[a+4>>2];if(c){Ca(c);H[a+4>>2]=0}if(b){c=a;while(1){d=H[c+200>>2];if(d){e=0;f=H[c+196>>2];if(f){while(1){g=H[d+12>>2];if(g){Ca(g);H[d+12>>2]=0;f=H[c+196>>2]}d=d+16|0;e=e+1|0;if(e>>>0>>0){continue}break}d=H[c+200>>2]}Ca(d);H[c+200>>2]=0}c=c+240|0;h=h+1|0;if((h|0)!=(b|0)){continue}break}}Ca(a)}}function dd(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0,h=0;e=H[a+84>>2];f=H[e>>2];d=H[e+4>>2];h=H[a+28>>2];g=H[a+20>>2]-h|0;g=d>>>0>>0?d:g;if(g){ab(f,h,g);f=g+H[e>>2]|0;H[e>>2]=f;d=H[e+4>>2]-g|0;H[e+4>>2]=d}d=c>>>0>d>>>0?d:c;if(d){ab(f,b,d);f=d+H[e>>2]|0;H[e>>2]=f;H[e+4>>2]=H[e+4>>2]-d}F[f|0]=0;b=H[a+44>>2];H[a+28>>2]=b;H[a+20>>2]=b;return c|0}function rd(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0,h=0;e=H[c+8>>2];d=e>>>0<=1?1:e;f=H[c+4>>2];g=f-H[c>>2]|0;while(1){h=d;d=d<<1;if(h-g>>>0>>0){continue}break}if((e|0)!=(h|0)){d=Fa(h);if(!d){return-1}e=H[c>>2];if(e){if(g){B(d,e,g)}Ca(H[c>>2])}H[c+8>>2]=h;H[c>>2]=d;f=d+g|0;H[c+4>>2]=f}if(b){B(f,a,b)}H[c+4>>2]=H[c+4>>2]+b;return b|0}function Yb(a){H[a+100>>2]=20832;H[a+96>>2]=20832;H[a+92>>2]=20832;H[a+88>>2]=20832;H[a+84>>2]=20832;H[a+80>>2]=20832;H[a+76>>2]=20832;H[a+72>>2]=20832;H[a+68>>2]=20832;H[a+64>>2]=20832;H[a+60>>2]=20832;H[a+56>>2]=20832;H[a+52>>2]=20832;H[a+48>>2]=20832;H[a+44>>2]=20832;H[a+40>>2]=20832;H[a+36>>2]=20832;H[a+32>>2]=20832;H[a+28>>2]=20832}function Ra(a,b){var c=0,d=0,e=0,f=0;if((b|0)<=0){return 0}c=H[a+12>>2];d=H[a+16>>2];while(1){e=b;a:{if(d){break a}c=c<<8&65280;H[a+12>>2]=c;d=(c|0)==65280?7:8;H[a+16>>2]=d;b=H[a+8>>2];if(b>>>0>=K[a+4>>2]){break a}H[a+8>>2]=b+1;c=I[b|0]|c;H[a+12>>2]=c}d=d-1|0;H[a+16>>2]=d;b=e-1|0;f=(c>>>d&1)<>>0>1){continue}break}return f}function xd(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0;f=na-16|0;na=f;e=H[a+24>>2];if((e|0)!=255){H[f>>2]=e;Ba(d,2,2678,f)}a:{b:{if(H[a+20>>2]==(c|0)){if(c){break b}e=1;break a}e=0;Ba(d,1,14510,0);break a}c=0;while(1){e=1;Da(b,(H[a+72>>2]+N(c,12)|0)+8|0,1);b=b+1|0;c=c+1|0;if(c>>>0>2]){continue}break}}na=f+16|0;return e|0}function Da(a,b,c){var d=0,e=0;H[b>>2]=0;a:{if(!c){break a}d=c&3;b=b+c|0;if(c>>>0>=4){e=c&-4;c=0;while(1){F[b-1|0]=I[a|0];F[b-2|0]=I[a+1|0];F[b-3|0]=I[a+2|0];b=b-4|0;F[b|0]=I[a+3|0];a=a+4|0;c=c+4|0;if((e|0)!=(c|0)){continue}break}if(!d){break a}}c=0;while(1){b=b-1|0;F[b|0]=I[a|0];a=a+1|0;c=c+1|0;if((d|0)!=(c|0)){continue}break}}}function de(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0;e=na-16|0;na=e;a:{if(!c){Ba(d,1,4106,0);a=0;break a}Da(b,e+12|0,1);f=c-1|0;a=1;if(!f){break a}a=0;c=0;while(1){b=b+1|0;Da(b,e+8|0,1);g=H[e+8>>2];c=g<<24>>31&(g&127|c)<<7;a=a+1|0;if((f|0)!=(a|0)){continue}break}a=1;if(!c){break a}Ba(d,1,4106,0);a=0}na=e+16|0;return a|0}function bc(a,b,c,d){var e=0,f=0,g=O(0),h=0,i=O(0),j=0,k=O(0);if(d){while(1){e=f<<2;h=e+b|0;i=L[h>>2];j=a+e|0;g=L[j>>2];e=c+e|0;k=L[e>>2];L[j>>2]=O(k*O(1.4019999504089355))+g;L[h>>2]=O(g+O(i*O(-.3441300094127655)))+O(k*O(-.714139997959137));L[e>>2]=g+O(i*O(1.7719999551773071));f=f+1|0;if((f|0)!=(d|0)){continue}break}}}function Bb(a,b,c){var d=0,e=0,f=0,g=0,h=0,i=0;f=na-240|0;na=f;H[f>>2]=a;g=1;a:{if((b|0)<2){break a}d=a;while(1){d=d-8|0;h=b-2|0;e=d-H[(h<<2)+c>>2]|0;if((Ic(a,e)|0)>=0){if((Ic(a,d)|0)>=0){break a}}i=e;e=(Ic(e,d)|0)>=0;d=e?i:d;H[(g<<2)+f>>2]=d;g=g+1|0;b=e?b-1|0:h;if((b|0)>1){continue}break}}vc(f,g);na=f+240|0}function vc(a,b){var c=0,d=0,e=0,f=0,g=0,h=0;c=8;f=na-256|0;na=f;if((b|0)>=2){h=(b<<2)+a|0;H[h>>2]=f;while(1){e=c>>>0>=256?256:c;ab(H[h>>2],H[a>>2],e);d=0;while(1){g=(d<<2)+a|0;d=d+1|0;ab(H[g>>2],H[(d<<2)+a>>2],e);H[g>>2]=H[g>>2]+e;if((b|0)!=(d|0)){continue}break}c=c-e|0;if(c){continue}break}}na=f+256|0}function Wc(a){a=a|0;var b=0,c=0,d=0,e=0;b=H[a+24>>2];if(b){c=H[a+28>>2];e=(c>>>0)/52|0;if(c>>>0>=52){while(1){c=H[b>>2];if(c){Ca(c-1|0);H[b>>2]=0}c=H[b+4>>2];if(c){Ca(c);H[b+4>>2]=0}c=H[b+8>>2];if(c){Ca(c);H[b+8>>2]=0}b=b+52|0;d=d+1|0;if((e|0)!=(d|0)){continue}break}b=H[a+24>>2]}Ca(b);H[a+24>>2]=0}}function Xc(a){a=a|0;var b=0,c=0,d=0,e=0;b=H[a+24>>2];if(b){c=H[a+28>>2];e=(c>>>0)/68|0;if(c>>>0>=68){while(1){c=H[b>>2];if(c){Ca(c);H[b>>2]=0}c=H[b+4>>2];if(c){Ca(c);H[b+4>>2]=0}Ca(H[b+60>>2]);H[b+60>>2]=0;b=b+68|0;d=d+1|0;if((e|0)!=(d|0)){continue}break}b=H[a+24>>2]}Ca(b);H[a+24>>2]=0}}function ad(a,b){a=a|0;b=b|0;var c=0,d=0;c=H[a+32>>2];b=H[a+28>>2];d=b+8|0;if(c>>>0>=d>>>0){while(1){nb(a,H[a+24>>2]+(b<<2)|0,H[a+20>>2],8);c=H[a+32>>2];b=d;d=b+8|0;if(c>>>0>=d>>>0){continue}break}}if(b>>>0>>0){nb(a,H[a+24>>2]+(b<<2)|0,H[a+20>>2],c-b|0)}Ca(H[a>>2]);Ca(a)}function Cd(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0;e=na-16|0;na=e;a:{if(H[a+100>>2]){Ba(d,1,11302,0);a=0;break a}if((c|0)!=4){Ba(d,1,5991,0);a=0;break a}Da(b,e+12|0,4);if(H[e+12>>2]!=218793738){Ba(d,1,5007,0);a=0;break a}H[a+100>>2]=H[a+100>>2]|1;a=1}na=e+16|0;return a|0}function Wa(a,b,c){var d=0,e=0;a:{d=H[a>>2];e=H[a+4>>2];b:{if((d|0)!=(e|0)){e=H[a+8>>2];break b}d=e+10|0;H[a+4>>2]=d;e=Ha(H[a+8>>2],d<<2);if(!e){break a}H[a+8>>2]=e;d=H[a>>2]}H[(d<<2)+e>>2]=b;H[a>>2]=d+1;return 1}Ca(H[a+8>>2]);H[a>>2]=0;H[a+4>>2]=0;Ba(c,1,6123,0);return 0}function $a(a,b,c){var d=0,e=0,f=0,g=0;if((b|0)==1|b>>>0>1){while(1){d=a;e=b;c=c-1|0;a=ve(a,b,10,0);b=qa;f=c,g=re(a,b,246)+d|48,F[f|0]=g;if(e>>>0>9){continue}break}}if(a|b){while(1){c=c-1|0;b=(a>>>0)/10|0;F[c|0]=N(b,246)+a|48;d=a>>>0>9;a=b;if(d){continue}break}}return c}function Cc(a){var b=0,c=0,d=0;c=na-16|0;na=c;F[c+15|0]=10;b=H[a+16>>2];a:{if(!b){if(Db(a)){break a}b=H[a+16>>2]}d=b;b=H[a+20>>2];if(!((d|0)==(b|0)|H[a+80>>2]==10)){H[a+20>>2]=b+1;F[b|0]=10;break a}if((ra[H[a+36>>2]](a,c+15|0,1)|0)!=1){break a}}na=c+16|0}function rc(a){var b=0,c=0,d=0,e=0,f=0;d=H[a>>2];b=F[d|0]-48|0;if(b>>>0>9){return 0}while(1){e=-1;if(c>>>0<=214748364){c=N(c,10);e=(c^2147483647)>>>0>>0?-1:c+b|0}b=d+1|0;H[a>>2]=b;f=F[d+1|0];c=e;d=b;b=f-48|0;if(b>>>0<10){continue}break}return c} +function pc(a,b){var c=0,d=0,e=0;x(+a);d=s(1)|0;e=s(0)|0;c=d>>>20&2047;if((c|0)!=2047){if(!c){if(a==0){c=0}else{a=pc(a*0x10000000000000000,b);c=H[b>>2]+-64|0}H[b>>2]=c;return a}H[b>>2]=c-1022;u(0,e|0);u(1,d&-2146435073|1071644672);a=+w()}return a}function Qd(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=O(0),f=0,g=0;d=na-16|0;na=d;if(c){while(1){Nc(a,d+12|0);e=L[d+12>>2];if(O(P(e))>2]=f;b=b+4|0;a=a+4|0;g=g+1|0;if((g|0)!=(c|0)){continue}break}}na=d+16|0}function Ua(a){var b=0,c=0,d=0;if(a){b=H[a+24>>2];if(b){c=H[a+16>>2];if(c){b=0;while(1){d=H[(H[a+24>>2]+N(b,52)|0)+44>>2];if(d){Ca(d);c=H[a+16>>2]}b=b+1|0;if(c>>>0>b>>>0){continue}break}b=H[a+24>>2]}Ca(b)}b=H[a+28>>2];if(b){Ca(b)}Ca(a)}}function Pd(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0;d=na-16|0;na=d;if(c){while(1){Kb(a,d+8|0);e=M[d+8>>3];if(P(e)<2147483647){f=~~e}else{f=-2147483648}H[b>>2]=f;b=b+4|0;a=a+8|0;g=g+1|0;if((g|0)!=(c|0)){continue}break}}na=d+16|0}function qd(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0;d=H[c+4>>2];e=H[c>>2]+H[c+8>>2]|0;if((d|0)==(e|0)){qa=-1;return-1}H[c+4>>2]=a+d;f=a;c=e-d|0;d=c;e=a>>>0>>0;a=c>>31;c=e&(a|0)>=(b|0)|(a|0)>(b|0);d=c?f:d;qa=c?b:a;return d|0}function ue(a,b,c,d){var e=0,f=0,g=0,h=0;f=b^d;g=f>>31;e=b>>31;a=a^e;h=a-e|0;e=(b^e)-((a>>>0>>0)+e|0)|0;a=d>>31;b=c^a;f=f>>31;a=ve(h,e,b-a|0,(a^d)-((a>>>0>b>>>0)+a|0)|0)^f;b=a-f|0;qa=(g^qa)-((a>>>0>>0)+g|0)|0;return b}function Va(a){var b=0,c=0,d=0,e=0;if(a){b=H[a+20>>2];c=H[a+16>>2];if(N(b,c)){while(1){e=H[H[a+24>>2]+(d<<2)>>2];if(e){Ca(e);c=H[a+16>>2];b=H[a+20>>2]}d=d+1|0;if(d>>>0>>0){continue}break}}Ca(H[a+24>>2]);Ca(a)}}function cc(a,b,c,d){var e=0,f=0,g=0,h=0,i=0,j=0,k=0;if(d){while(1){e=f<<2;g=e+a|0;h=c+e|0;i=H[h>>2];j=b+e|0;k=H[j>>2];e=H[g>>2]-(i+k>>2)|0;H[g>>2]=e+i;H[j>>2]=e;H[h>>2]=e+k;f=f+1|0;if((f|0)!=(d|0)){continue}break}}}function bb(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0;H[a+48>>2]=0;H[a+36>>2]=H[a+32>>2];e=ra[H[a+28>>2]](b,c,H[a>>2])|0;d=H[a+68>>2];if(!e){H[a+68>>2]=d|4;return 0}H[a+56>>2]=b;H[a+60>>2]=c;H[a+68>>2]=d&-5;return 1}function Na(a,b,c,d,e){var f=0;f=na-256|0;na=f;if(!(e&73728|(c|0)<=(d|0))){d=c-d|0;c=d>>>0<256;Dc(f,b,c?d:256);if(!c){while(1){La(a,f,256);d=d-256|0;if(d>>>0>255){continue}break}}La(a,f,d)}na=f+256|0}function re(a,b,c){var d=0,e=0,f=0,g=0,h=0;e=c>>>16|0;d=a>>>16|0;h=N(e,d);f=c&65535;a=a&65535;g=N(f,a);d=(g>>>16|0)+N(d,f)|0;a=(d&65535)+N(a,e)|0;qa=h+N(b,c)+(d>>>16)+(a>>>16)|0;return g&65535|a<<16}function Db(a){var b=0;b=H[a+72>>2];H[a+72>>2]=b-1|b;b=H[a>>2];if(b&8){H[a>>2]=b|32;return-1}H[a+4>>2]=0;H[a+8>>2]=0;b=H[a+44>>2];H[a+28>>2]=b;H[a+20>>2]=b;H[a+16>>2]=b+H[a+48>>2];return 0}function hc(a){var b=0,c=0;a:{if(I[a+12|0]==255){H[a+12>>2]=65280;H[a+16>>2]=7;b=H[a+8>>2];c=0;if(b>>>0>=K[a+4>>2]){break a}H[a+8>>2]=b+1;H[a+12>>2]=I[b|0]|65280}H[a+16>>2]=0;c=1}return c}function sd(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0;e=H[c+4>>2];d=H[c>>2]+H[c+8>>2]|0;if((e|0)==(d|0)){return-1}d=d-e|0;b=b>>>0>d>>>0?d:b;if(b){B(a,e,b)}H[c+4>>2]=b+H[c+4>>2];return b|0}function Ud(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0;d=na-16|0;na=d;if(c){while(1){Nc(a,d+12|0);L[b>>2]=L[d+12>>2];b=b+4|0;a=a+4|0;e=e+1|0;if((e|0)!=(c|0)){continue}break}}na=d+16|0}function Td(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0;d=na-16|0;na=d;if(c){while(1){Kb(a,d+8|0);L[b>>2]=M[d+8>>3];b=b+4|0;a=a+8|0;e=e+1|0;if((e|0)!=(c|0)){continue}break}}na=d+16|0}function bd(a,b){a=a|0;b=b|0;b=H[a+28>>2];if(b>>>0>2]){while(1){$b(a,H[a+24>>2]+(N(H[a+20>>2],b)<<2)|0);b=b+1|0;if(b>>>0>2]){continue}break}}Ca(H[a>>2]);Ca(a)}function Ic(a,b){a=a|0;b=b|0;var c=0,d=0;c=H[a>>2];d=H[b>>2];a=H[a+4>>2];b=H[b+4>>2];return(c>>>0>d>>>0&(a|0)>=(b|0)|(a|0)>(b|0))-(c>>>0>>0&(a|0)<=(b|0)|(a|0)<(b|0))|0}function kd(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0;e=na-16|0;na=e;a=yc(ea(H[a+60>>2],b|0,c|0,d&255,e+8|0)|0);na=e+16|0;qa=a?-1:H[e+12>>2];return(a?-1:H[e+8>>2])|0}function mc(a,b,c,d){var e=0,f=0;e=na-16|0;na=e;if(c){while(1){Da(a,e+12|0,d);L[b>>2]=K[e+12>>2];b=b+4|0;a=a+d|0;f=f+1|0;if((f|0)!=(c|0)){continue}break}}na=e+16|0}function lc(a,b,c,d){var e=0,f=0;e=na-16|0;na=e;if(c){while(1){Da(a,e+12|0,d);H[b>>2]=H[e+12>>2];b=b+4|0;a=a+d|0;f=f+1|0;if((f|0)!=(c|0)){continue}break}}na=e+16|0}function Kb(a,b){F[b+7|0]=I[a|0];F[b+6|0]=I[a+1|0];F[b+5|0]=I[a+2|0];F[b+4|0]=I[a+3|0];F[b+3|0]=I[a+4|0];F[b+2|0]=I[a+5|0];F[b+1|0]=I[a+6|0];F[b|0]=I[a+7|0]}function Qa(a){var b=0,c=0,d=0,e=0;b=H[a+12>>2];e=b;c=H[a+8>>2];if(!(b|c)){qa=0;return 0}d=H[a+56>>2];b=c-d|0;qa=e-(H[a+60>>2]+(c>>>0>>0)|0)|0;return b}function kc(a,b){var c=0;c=na-16|0;na=c;if(a){if(b&3){a=28}else{a=gb(b,a);H[c+12>>2]=a;a=a?0:48}a=a?0:H[c+12>>2]}else{a=0}na=c+16|0;return a}function Yc(a){a=a|0;var b=0;if(a){b=H[a+116>>2];if(b){Ca(b);H[a+116>>2]=0}b=H[a+120>>2];if(b){Ca(b);H[a+120>>2]=0}Ca(H[a+148>>2]);Ca(a)}} +function sb(a,b){var c=0,d=0;a:{if(b>>>0<=31){d=H[a>>2];c=a+4|0;break a}b=b-32|0;c=a}c=H[c>>2];H[a>>2]=d<>2]=c<>>32-b}function ub(a,b){var c=0,d=0;c=H[a+4>>2];a:{if(b>>>0<=31){d=H[a>>2];break a}b=b-32|0;d=c;c=0}H[a+4>>2]=c>>>b;H[a>>2]=c<<32-b|d>>>b}function ae(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;if(H[H[a+96>>2]+16>>2]<<2!=(c|0)){Ba(d,1,4464,0);a=0}else{a=1}return a|0}function jc(){var a=0,b=0;a=Ea(1,44);a:{if(a){H[a+16>>2]=0;b=Ea(1,8);H[a+36>>2]=b;if(b){break a}Ca(a)}a=0}return a}function Pb(a,b){a=a|0;b=b|0;if(!(!a|!b)){H[a+188>>2]=H[b+4>>2];H[a+184>>2]=H[b>>2];H[a+248>>2]=H[b+8248>>2]&2}}function qb(){var a=0,b=0;a=Ea(1,12);if(a){H[a+4>>2]=10;b=Ea(10,4);H[a+8>>2]=b;if(b){return a}Ca(a)}return 0}function Id(a,b,c,d,e,f,g,h,i,j,k){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;i=i|0;j=j|0;k=k|0;D()}function fb(a){var b=0;if(a){b=H[a+4>>2];if(b){ra[b|0](H[a>>2])}Ca(H[a+32>>2]);H[a+32>>2]=0;Ca(a)}}function Ob(a,b){a=a|0;b=b|0;a:{if(!a){break a}H[a+208>>2]=b;if(!b){break a}F[a+92|0]=I[a+92|0]|8}}function pd(a,b,c){a=a|0;b=b|0;c=c|0;b=H[c+8>>2];H[c+4>>2]=H[c>>2]+(a>>>0>b>>>0?b:a);return 1}function ee(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;if(c){a=1}else{Ba(d,1,4375,0);a=0}return a|0}function oc(a,b){if(!((b&-128)==57216|b>>>0<=127)){H[6597]=25;return-1}F[a|0]=b;return 1}function kb(a){H[a+16>>2]=0;H[a+20>>2]=0;H[a+8>>2]=0;H[a+12>>2]=0;H[a>>2]=0;H[a+4>>2]=0}function Uc(a,b,c){a=a|0;b=b|0;c=c|0;return!H[a+8>>2]&(H[a+216>>2]!=0&H[a+220>>2]!=0)}function Sa(a){if(H[a+12>>2]){H[a+40>>2]=0;while(1){if(H[a+24>>2]>0){continue}break}}}function Nc(a,b){F[b+3|0]=I[a|0];F[b+2|0]=I[a+1|0];F[b+1|0]=I[a+2|0];F[b|0]=I[a+3|0]}function hb(a){if(a){ra[H[(H[a+76>>2]?20:16)+a>>2]](H[a+48>>2]);H[a+48>>2]=0;Ca(a)}}function Nd(a,b){a=a|0;b=b|0;Pb(H[a>>2],b);F[a+124|0]=0;H[a+128>>2]=H[b+8248>>2]&1}function Ea(a,b){if(!a|!b){a=0}else{b=N(a,b);a=gb(8,b);if(a){Dc(a,0,b)}}return a}function Ga(a,b,c){var d=0;d=na-16|0;na=d;H[d+12>>2]=c;tc(a,b,c,0,0);na=d+16|0}function xe(a){var b=0;while(1){if(a){a=a-1&a;b=b+1|0;continue}break}return b}function _a(a){var b=0;if(a){b=H[a+12>>2];if(b){Ca(b);H[a+12>>2]=0}Ca(a)}}function Rc(a,b,c,d,e,f,g){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;g=g|0;D()}function Gd(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;return Lb(H[a>>2],b,c,d)|0}function Hd(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;D()}function wc(a,b){a=uc(a-1|0);if(!a){a=uc(b);a=a?a|32:0}return a}function Qb(a){return H[a+12>>2]==H[a+4>>2]|H[a+8>>2]==H[a>>2]}function Dd(a,b,c){a=a|0;b=b|0;c=c|0;return Mc(H[a>>2],b,c)|0}function uc(a){var b=0,c=0,d=0;return b=te(a),c=0,d=a,d?b:c}function pb(a){var b=0;if(a){b=H[a+8>>2];if(b){Ca(b)}Ca(a)}}function gd(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;qa=0;return 0}function Za(a,b,c,d,e,f,g,h){return ac(a,b,c,d,e,f,g,h,0)}function Oc(a,b,c,d,e){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;D()}function nc(a,b,c,d){return ra[H[a+44>>2]](a,b,c,d)|0}function Xa(a,b,c){H[((b<<2)+a|0)+28>>2]=(c<<5)+20832}function Fb(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;return 1}function Fd(a,b,c){a=a|0;b=b|0;c=c|0;Ib(H[a>>2],b,c)}function te(a){if(a){return 31-Q(a-1^a)|0}return 32}function rb(a,b,c){return ra[H[a+40>>2]](a,b,0,c)|0}function _d(a,b,c){a=a|0;b=b|0;c=c|0;qa=-1;return-1}function Oa(a,b,c,d,e,f,g,h){ac(a,b,c,d,e,f,g,h,1)}function yc(a){if(!a){return 0}H[6597]=a;return-1}function Wd(a,b,c){a=a|0;b=b|0;c=c|0;mc(a,b,c,2)}function Vd(a,b,c){a=a|0;b=b|0;c=c|0;mc(a,b,c,4)}function Sd(a,b,c){a=a|0;b=b|0;c=c|0;lc(a,b,c,2)}function Rd(a,b,c){a=a|0;b=b|0;c=c|0;lc(a,b,c,4)}function we(a,b,c){se(a,0,b,c);qa=pa;return oa}function La(a,b,c){if(!(I[a|0]&32)){Ac(b,c,a)}}function ie(a,b,c){a=a|0;b=b|0;c=c|0;return 0}function Nb(a,b,c){a=a|0;b=b|0;c=c|0;return 1}function Jc(a,b,c){a=a|0;b=b|0;c=c|0;return-1}function ve(a,b,c,d){a=se(a,b,c,d);return a}function Fa(a){if(!a){return 0}return vb(a)}function Md(a,b){a=a|0;b=b|0;Ob(H[a>>2],b)}function Dc(a,b,c){if(c){y(a,b<<24>>24,c)}}function ic(a){return H[a+8>>2]-H[a>>2]|0}function hd(a){a=a|0;ia();ha(a+128|0);D()}function vb(a){a=a|0;return gb(8,a)|0}function md(a,b){a=a|0;b=b|0;_(a|0)}function ld(a,b){a=a|0;b=b|0;Y(a|0)}function Ab(a){return H[a+28>>2]!=2}function ab(a,b,c){if(c){B(a,b,c)}}function Vc(a,b){a=a|0;b=b|0;D()}function ob(a){return kc(a,32)}function Ia(a){return kc(a,16)}function id(a){a=a|0;ja();D()}function Eb(){return Ea(1,36)}function Sb(a,b){a=a|0;b=b|0}function db(a){if(a){Ca(a)}}function Qc(a){a=a|0;D()}function Pa(a){H[a>>2]=0} +// EMSCRIPTEN_END_FUNCS +a=I;m(n);var ra=[null,Sb,ie,_d,Jc,Jc,bb,Ed,ud,od,bd,ad,$c,_c,Zc,Yc,Xc,Wc,Nb,Uc,Tc,Sc,Pc,Ic,pe,oe,ne,me,le,ke,je,he,ge,fe,ee,de,ce,be,ae,Fb,$d,Zd,Fb,Fb,Yd,Xd,Wd,Vd,Ud,Td,Sd,Rd,Qd,Pd,Kd,Cd,Bd,Ad,zd,yd,xd,wd,vd,td,sd,rd,qd,pd,Qc,Qc,Ib,Nb,Gb,Ob,Pb,wb,Mb,Vc,Lb,Mc,Oc,Rc,ib,cb,Qc,Qc,Fd,Ld,Od,Vc,Gd,Dd,Oc,Rc,Md,Nd,Fc,Hd,Id,Jd,Sb,md,ld,fd,ed,dd,hd,id,Qc,jd,kd,Qc,gd];function sa(){return E.byteLength>>16}function xa(ya){ya=ya|0;var ta=sa()|0;var ua=ta+ya|0;if(ta{Module["instantiateWasm"](info,(inst,mod)=>{resolve(receiveInstance(inst,mod))})})}wasmBinaryFile??=findWasmBinary();var result=instantiateSync(wasmBinaryFile,info);return receiveInstance(result[0])}class ExitStatus{name="ExitStatus";constructor(status){this.message=`Program terminated with exit(${status})`;this.status=status}}var HEAP16;var HEAP32;var HEAP8;var HEAPF32;var HEAPF64;var HEAPU16;var HEAPU32;var HEAPU8;var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};var onPostRuns=[];var addOnPostRun=cb=>onPostRuns.push(cb);var onPreRuns=[];var addOnPreRun=cb=>onPreRuns.push(cb);var noExitRuntime=true;var __abort_js=()=>abort("");var runtimeKeepaliveCounter=0;var __emscripten_runtime_keepalive_clear=()=>{noExitRuntime=false;runtimeKeepaliveCounter=0};var timers={};var handleException=e=>{if(e instanceof ExitStatus||e=="unwind"){return EXITSTATUS}quit_(1,e)};var keepRuntimeAlive=()=>noExitRuntime||runtimeKeepaliveCounter>0;var _proc_exit=code=>{EXITSTATUS=code;if(!keepRuntimeAlive()){Module["onExit"]?.(code);ABORT=true}quit_(code,new ExitStatus(code))};var exitJS=(status,implicit)=>{EXITSTATUS=status;_proc_exit(status)};var _exit=exitJS;var maybeExit=()=>{if(!keepRuntimeAlive()){try{_exit(EXITSTATUS)}catch(e){handleException(e)}}};var callUserCallback=func=>{if(ABORT){return}try{return func()}catch(e){handleException(e)}finally{maybeExit()}};var _emscripten_get_now=()=>performance.now();var __setitimer_js=(which,timeout_ms)=>{if(timers[which]){clearTimeout(timers[which].id);delete timers[which]}if(!timeout_ms)return 0;var id=setTimeout(()=>{delete timers[which];callUserCallback(()=>__emscripten_timeout(which,_emscripten_get_now()))},timeout_ms);timers[which]={id,timeout_ms};return 0};function _copy_pixels_1(compG_ptr,nb_pixels){compG_ptr>>=2;const imageData=Module.imageData=new Uint8ClampedArray(nb_pixels);const compG=HEAP32.subarray(compG_ptr,compG_ptr+nb_pixels);imageData.set(compG)}function _copy_pixels_3(compR_ptr,compG_ptr,compB_ptr,nb_pixels){compR_ptr>>=2;compG_ptr>>=2;compB_ptr>>=2;const imageData=Module.imageData=new Uint8ClampedArray(nb_pixels*3);const compR=HEAP32.subarray(compR_ptr,compR_ptr+nb_pixels);const compG=HEAP32.subarray(compG_ptr,compG_ptr+nb_pixels);const compB=HEAP32.subarray(compB_ptr,compB_ptr+nb_pixels);for(let i=0;i>=2;compG_ptr>>=2;compB_ptr>>=2;compA_ptr>>=2;const imageData=Module.imageData=new Uint8ClampedArray(nb_pixels*4);const compR=HEAP32.subarray(compR_ptr,compR_ptr+nb_pixels);const compG=HEAP32.subarray(compG_ptr,compG_ptr+nb_pixels);const compB=HEAP32.subarray(compB_ptr,compB_ptr+nb_pixels);const compA=HEAP32.subarray(compA_ptr,compA_ptr+nb_pixels);for(let i=0;i2147483648;var alignMemory=(size,alignment)=>Math.ceil(size/alignment)*alignment;var growMemory=size=>{var oldHeapSize=wasmMemory.buffer.byteLength;var pages=(size-oldHeapSize+65535)/65536|0;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignMemory(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};var ENV={};var getExecutableName=()=>thisProgram||"./this.program";var getEnvStrings=()=>{if(!getEnvStrings.strings){var lang=(globalThis.navigator?.language??"C").replace("-","_")+".UTF-8";var env={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:lang,_:getExecutableName()};for(var x in ENV){if(ENV[x]===undefined)delete env[x];else env[x]=ENV[x]}var strings=[];for(var x in env){strings.push(`${x}=${env[x]}`)}getEnvStrings.strings=strings}return getEnvStrings.strings};var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63;i++}}heap[outIdx]=0;return outIdx-startIdx};var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite);var _environ_get=(__environ,environ_buf)=>{var bufSize=0;var envp=0;for(var string of getEnvStrings()){var ptr=environ_buf+bufSize;HEAPU32[__environ+envp>>2]=ptr;bufSize+=stringToUTF8(string,ptr,Infinity)+1;envp+=4}return 0};var lengthBytesUTF8=str=>{var len=0;for(var i=0;i=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};var _environ_sizes_get=(penviron_count,penviron_buf_size)=>{var strings=getEnvStrings();HEAPU32[penviron_count>>2]=strings.length;var bufSize=0;for(var string of strings){bufSize+=lengthBytesUTF8(string)+1}HEAPU32[penviron_buf_size>>2]=bufSize;return 0};var convertI32PairToI53Checked=(lo,hi)=>hi+2097152>>>0<4194305-!!lo?(lo>>>0)+hi*4294967296:NaN;function _fd_seek(fd,offset_low,offset_high,whence,newOffset){var offset=convertI32PairToI53Checked(offset_low,offset_high);return 70}var printCharBuffers=[null,[],[]];var UTF8Decoder=new TextDecoder;var findStringEnd=(heapOrArray,idx,maxBytesToRead,ignoreNul)=>{var maxIdx=idx+maxBytesToRead;if(ignoreNul)return maxIdx;while(heapOrArray[idx]&&!(idx>=maxIdx))++idx;return idx};var UTF8ArrayToString=(heapOrArray,idx=0,maxBytesToRead,ignoreNul)=>{var endPtr=findStringEnd(heapOrArray,idx,maxBytesToRead,ignoreNul);return UTF8Decoder.decode(heapOrArray.buffer?heapOrArray.subarray(idx,endPtr):new Uint8Array(heapOrArray.slice(idx,endPtr)))};var printChar=(stream,curr)=>{var buffer=printCharBuffers[stream];if(curr===0||curr===10){(stream===1?out:err)(UTF8ArrayToString(buffer));buffer.length=0}else{buffer.push(curr)}};var UTF8ToString=(ptr,maxBytesToRead,ignoreNul)=>{if(!ptr)return"";var end=findStringEnd(HEAPU8,ptr,maxBytesToRead,ignoreNul);return UTF8Decoder.decode(HEAPU8.subarray(ptr,end))};var _fd_write=(fd,iov,iovcnt,pnum)=>{var num=0;for(var i=0;i>2];var len=HEAPU32[iov+4>>2];iov+=8;for(var j=0;j>2]=num;return 0};function _gray_to_rgba(compG_ptr,nb_pixels){compG_ptr>>=2;const imageData=Module.imageData=new Uint8ClampedArray(nb_pixels*4);const compG=HEAP32.subarray(compG_ptr,compG_ptr+nb_pixels);for(let i=0;i>=2;compA_ptr>>=2;const imageData=Module.imageData=new Uint8ClampedArray(nb_pixels*4);const compG=HEAP32.subarray(compG_ptr,compG_ptr+nb_pixels);const compA=HEAP32.subarray(compA_ptr,compA_ptr+nb_pixels);for(let i=0;i>=2;compG_ptr>>=2;compB_ptr>>=2;const imageData=Module.imageData=new Uint8ClampedArray(nb_pixels*4);const compR=HEAP32.subarray(compR_ptr,compR_ptr+nb_pixels);const compG=HEAP32.subarray(compG_ptr,compG_ptr+nb_pixels);const compB=HEAP32.subarray(compB_ptr,compB_ptr+nb_pixels);for(let i=0;i{HEAP8.set(array,buffer)};{if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(Module["print"])out=Module["print"];if(Module["printErr"])err=Module["printErr"];if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].shift()()}}}Module["writeArrayToMemory"]=writeArrayToMemory;var _malloc,_free,_jp2_decode,__emscripten_timeout,dynCall_iji,dynCall_jji,dynCall_iiji,dynCall_jiji,memory,__indirect_function_table,wasmMemory;function assignWasmExports(wasmExports){_malloc=Module["_malloc"]=wasmExports["t"];_free=Module["_free"]=wasmExports["u"];_jp2_decode=Module["_jp2_decode"]=wasmExports["v"];__emscripten_timeout=wasmExports["w"];dynCall_iji=wasmExports["dynCall_iji"];dynCall_jji=wasmExports["dynCall_jji"];dynCall_iiji=wasmExports["dynCall_iiji"];dynCall_jiji=wasmExports["dynCall_jiji"];memory=wasmMemory=wasmExports["r"];__indirect_function_table=wasmExports["__indirect_function_table"]}var wasmImports={n:__abort_js,m:__emscripten_runtime_keepalive_clear,j:__setitimer_js,f:_copy_pixels_1,e:_copy_pixels_3,d:_copy_pixels_4,k:_emscripten_resize_heap,o:_environ_get,p:_environ_sizes_get,i:_fd_seek,b:_fd_write,q:_gray_to_rgba,h:_graya_to_rgba,c:_jsPrintWarning,l:_proc_exit,g:_rgb_to_rgba,a:_storeErrorMessage};function run(){preRun();function doRun(){Module["calledRun"]=true;if(ABORT)return;initRuntime();readyPromiseResolve?.(Module);Module["onRuntimeInitialized"]?.();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(()=>{setTimeout(()=>Module["setStatus"](""),1);doRun()},1)}else{doRun()}}var wasmExports;wasmExports=createWasm();run();if(runtimeInitialized){moduleRtn=Module}else{moduleRtn=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject})} +;return moduleRtn}export default OpenJPEG; diff --git a/web/public/vendor/pdfjs/wasm/qcms_bg.wasm b/web/public/vendor/pdfjs/wasm/qcms_bg.wasm new file mode 100644 index 00000000..fccd641f Binary files /dev/null and b/web/public/vendor/pdfjs/wasm/qcms_bg.wasm differ diff --git a/web/public/vendor/pdfjs/wasm/quickjs-eval.js b/web/public/vendor/pdfjs/wasm/quickjs-eval.js new file mode 100644 index 00000000..ab9ae8b5 --- /dev/null +++ b/web/public/vendor/pdfjs/wasm/quickjs-eval.js @@ -0,0 +1,16 @@ +/* THIS FILE IS GENERATED - DO NOT EDIT */ +async function QuickJS(moduleArg={}){var moduleRtn;var e=moduleArg,aa=import.meta.url,h="",m;try{h=(new URL(".",aa)).href}catch{}m=async a=>{a=await fetch(a,{credentials:"same-origin"});if(a.ok)return a.arrayBuffer();throw Error(a.status+" : "+a.url);};var q=console.error.bind(console),r,t=!1,u,v,w,x=!1;function y(){var a=z.buffer;A=new Int8Array(a);new Int16Array(a);B=new Uint8Array(a);new Uint16Array(a);C=new Int32Array(a);D=new Uint32Array(a);new Float32Array(a);new Float64Array(a);new BigInt64Array(a);new BigUint64Array(a)} +function E(a){e.onAbort?.(a);a=`Aborted(${a})`;q(a);t=!0;a=new WebAssembly.RuntimeError(a+". Build with -sASSERTIONS for more info.");w?.(a);throw a;}var F;async function ba(a){if(!r)try{var b=await m(a);return new Uint8Array(b)}catch{}if(a==F&&r)a=new Uint8Array(r);else throw"both async and sync fetching of the wasm failed";return a}async function ca(a,b){try{var c=await ba(a);return await WebAssembly.instantiate(c,b)}catch(d){q(`failed to asynchronously prepare wasm: ${d}`),E(d)}} +async function da(a){var b=F;if(!r)try{var c=fetch(b,{credentials:"same-origin"});return await WebAssembly.instantiateStreaming(c,a)}catch(d){q(`wasm streaming compile failed: ${d}`),q("falling back to ArrayBuffer instantiation")}return ca(b,a)}class G{name="ExitStatus";constructor(a){this.message=`Program terminated with exit(${a})`;this.status=a}} +var C,A,D,B,H=a=>{for(;0{var a=e.preRun.shift();J.push(a)},K=!0,L=0,fa=[0,31,60,91,121,152,182,213,244,274,305,335],ha=[0,31,59,90,120,151,181,212,243,273,304,334],M={},N=a=>{if(!(a instanceof G||"unwind"==a))throw a;},O=a=>{u=a;K||0{if(!t)try{a()}catch(b){N(b)}finally{if(!(K||0{var d=B;if(0=f){if(b>= +c)break;d[b++]=f}else if(2047>=f){if(b+1>=c)break;d[b++]=192|f>>6;d[b++]=128|f&63}else if(65535>=f){if(b+2>=c)break;d[b++]=224|f>>12;d[b++]=128|f>>6&63;d[b++]=128|f&63}else{if(b+3>=c)break;d[b++]=240|f>>18;d[b++]=128|f>>12&63;d[b++]=128|f>>6&63;d[b++]=128|f&63;g++}}d[b]=0}},ja=new TextDecoder,Q=a=>{if(a){for(var b=a,c=B,d=b+void 0;c[b]&&!(b>=d);)++b;a=ja.decode(B.subarray(a,b))}else a="";return a},R=a=>{for(var b=0,c=0;c=d?b++:2047>=d?b+=2:55296<=d&&57343>= +d?(b+=4,++c):b+=3}return b},T=a=>{var b=R(a)+1,c=S(b);c&&P(a,c,b);return c};function U(){}var la=(a,b,c,d)=>{var g={string:k=>{var n=0;if(null!==k&&void 0!==k&&0!==k){n=R(k)+1;var X=V(n);P(k,X,n);n=X}return n},array:k=>{var n=V(k.length);A.set(k,n);return n}};a=e["_"+a];var f=[],p=0;if(d)for(var l=0;l{a=Q(a);b=null!==b?JSON.parse(Q(b)):[];try{const d=e.externalCall(a,b);return d?T(d):null}catch(d){return e.HEAPU8[c]=1,T(d.message)}};e.noExitRuntime&&(K=e.noExitRuntime);e.printErr&&(q=e.printErr);e.wasmBinary&&(r=e.wasmBinary);if(e.preInit)for("function"==typeof e.preInit&&(e.preInit=[e.preInit]);0{var g=!c||c.every(f=>"number"===f||"boolean"===f);return"string"!==b&&g&&!d?e["_"+a]:(...f)=>la(a,b,c,f,d)}; +e.stringToNewUTF8=T; +var S,ma,ka,V,W,z,na={e:()=>E(""),a:()=>{K=!1;L=0},b:function(a,b){a=-9007199254740992>a||9007199254740992>2]=a.getSeconds();C[b+4>>2]=a.getMinutes();C[b+8>>2]=a.getHours();C[b+12>>2]=a.getDate();C[b+16>>2]=a.getMonth();C[b+20>>2]=a.getFullYear()-1900;C[b+24>>2]=a.getDay();var c=a.getFullYear();C[b+28>>2]=(0!==c%4||0===c%100&&0!==c%400?ha:fa)[a.getMonth()]+a.getDate()-1|0;C[b+36>>2]=-(60*a.getTimezoneOffset());c=(new Date(a.getFullYear(),6,1)).getTimezoneOffset(); +var d=(new Date(a.getFullYear(),0,1)).getTimezoneOffset();C[b+32>>2]=(c!=d&&a.getTimezoneOffset()==Math.min(d,c))|0},i:(a,b)=>{M[a]&&(clearTimeout(M[a].id),delete M[a]);if(!b)return 0;var c=setTimeout(()=>{delete M[a];ia(()=>ma(a,performance.now()))},b);M[a]={id:c,A:b};return 0},c:(a,b,c,d)=>{var g=(new Date).getFullYear(),f=(new Date(g,0,1)).getTimezoneOffset();g=(new Date(g,6,1)).getTimezoneOffset();D[a>>2]=60*Math.max(f,g);C[b>>2]=Number(f!=g);b=p=>{var l=Math.abs(p);return`UTC${0<=p?"-":"+"}${String(Math.floor(l/ +60)).padStart(2,"0")}${String(l%60).padStart(2,"0")}`};a=b(f);b=b(g);gDate.now(),j:a=>{var b=B.length;a>>>=0;if(2147483648=c;c*=2){var d=b*(1+.2/c);d=Math.min(d,a+100663296);a:{d=(Math.min(2147483648,65536*Math.ceil(Math.max(a,d)/65536))-z.buffer.byteLength+65535)/65536|0;try{z.grow(d);y();var g= +1;break a}catch(f){}g=void 0}if(g)return!0}return!1},m:function(a){a=Q(a);window.console.log(a)},h:function(a){a=Q(a);return Date.parse(a)},l:function(a,b,c,d){a=Q(a);b=Q(b);c=Q(c);c=`Quickjs -- ${a}: ${b}\n${c}`;0!==d?window.alert(c):window.console.error(c)},k:O},Z; +Z=await (async function(){function a(c){c=Z=c.exports;e._evalInSandbox=c.p;e._nukeSandbox=c.q;e._init=c.r;e._commFun=c.s;e._dumpMemoryUse=c.t;S=c.u;e._free=c.v;ma=c.w;ka=c.x;V=c.y;W=c.z;z=c.n;y();return Z}var b={a:na};if(e.instantiateWasm)return new Promise(c=>{e.instantiateWasm(b,(d,g)=>{c(a(d,g))})});F??=e.locateFile?e.locateFile?e.locateFile("quickjs-eval.wasm",h):h+"quickjs-eval.wasm":(new URL("quickjs-eval.wasm",import.meta.url)).href;return a((await da(b)).instance)}()); +(function(){function a(){e.calledRun=!0;if(!t){x=!0;Z.o();v?.(e);e.onRuntimeInitialized?.();if(e.postRun)for("function"==typeof e.postRun&&(e.postRun=[e.postRun]);e.postRun.length;){var b=e.postRun.shift();I.push(b)}H(I)}}if(e.preRun)for("function"==typeof e.preRun&&(e.preRun=[e.preRun]);e.preRun.length;)ea();H(J);e.setStatus?(e.setStatus("Running..."),setTimeout(()=>{setTimeout(()=>e.setStatus(""),1);a()},1)):a()})();x?moduleRtn=e:moduleRtn=new Promise((a,b)=>{v=a;w=b}); +;return moduleRtn}export default QuickJS; diff --git a/web/public/vendor/pdfjs/wasm/quickjs-eval.wasm b/web/public/vendor/pdfjs/wasm/quickjs-eval.wasm new file mode 100644 index 00000000..b5fe551a Binary files /dev/null and b/web/public/vendor/pdfjs/wasm/quickjs-eval.wasm differ diff --git a/web/src/App.tsx b/web/src/App.tsx index ff9f0695..68995c29 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -31,6 +31,7 @@ import Profiles from "@/pages/Profiles"; import Catalog from "@/pages/Catalog"; import LibraryPage from "@/pages/LibraryPage"; import ItemDetail from "@/pages/ItemDetail/index"; +import EbookReader from "@/pages/EbookReader"; import PersonDetail from "@/pages/PersonDetail"; import Collections from "@/pages/Collections"; import CollectionEditor from "@/pages/CollectionEditor"; @@ -359,6 +360,14 @@ function AppRoutes() { } /> + + + + } + /> {/* Admin area — own layout, no profile required */} { }); }); +describe("apiBlob", () => { + beforeEach(() => { + Object.defineProperty(globalThis, "sessionStorage", { + value: { + getItem: () => null, + setItem: () => {}, + removeItem: () => {}, + clear: () => {}, + }, + configurable: true, + }); + }); + + it("rejects responses whose Content-Length exceeds the in-memory cap", async () => { + const fetchMock = vi.fn(async () => { + const res = new Response("x", { status: 200 }); + // 3 GiB; Response normally derives Content-Length from the body, so + // override the header lookup instead of materializing a huge body. + vi.spyOn(res.headers, "get").mockImplementation((name) => + name.toLowerCase() === "content-length" ? String(3 * 1024 * 1024 * 1024) : null, + ); + return res; + }); + vi.stubGlobal("fetch", fetchMock); + + await expect(apiBlob("/ebooks/abc/files/1/read")).rejects.toMatchObject({ + name: "ApiClientError", + code: "response_too_large", + message: expect.stringContaining("too large to open in the browser"), + }); + }); + + it("returns the blob when Content-Length is within the cap or missing", async () => { + const fetchMock = vi.fn(async () => { + const res = new Response("epub-bytes", { status: 200 }); + vi.spyOn(res.headers, "get").mockReturnValue(null); + return res; + }); + vi.stubGlobal("fetch", fetchMock); + + const blob = await apiBlob("/ebooks/abc/files/1/read"); + await expect(blob.text()).resolves.toBe("epub-bytes"); + }); +}); + describe("api", () => { it("forwards AbortSignal from options to fetch", async () => { Object.defineProperty(globalThis, "sessionStorage", { diff --git a/web/src/api/client.ts b/web/src/api/client.ts index c227efb0..a19ff289 100644 --- a/web/src/api/client.ts +++ b/web/src/api/client.ts @@ -401,6 +401,20 @@ function buildApiHeaders(options: RequestInit = {}): Record { return headers; } +/** + * Fire-and-forget API request that survives page unload (pagehide / tab close). + * Sends the same auth, profile, and device headers as `api`, plus `keepalive` + * so the browser finishes the request after the document is gone. The response + * is intentionally ignored: no token refresh or error handling is possible + * while the page is unloading. + */ +export function apiKeepalive(path: string, options: RequestInit = {}): void { + const headers = buildApiHeaders(options); + void fetch(`/api/v1${path}`, { ...options, headers, keepalive: true }).catch(() => { + // Best-effort write during unload; nothing left to recover into. + }); +} + /** Downloads a binary API response and triggers a browser file save. */ export async function apiDownload( path: string, @@ -437,6 +451,51 @@ export async function apiDownload( URL.revokeObjectURL(url); } +/** + * apiBlob buffers the entire response body in memory, so cap what it will + * accept; beyond this a download is the right tool, not an in-tab blob. + */ +export const API_BLOB_MAX_BYTES = 512 * 1024 * 1024; + +export async function apiBlob(path: string, options: RequestInit = {}): Promise { + let headers = buildApiHeaders(options); + let res = await fetch(`/api/v1${path}`, { ...options, headers }); + + if (res.status === 401 && getRefreshToken()) { + if (!refreshPromise) { + refreshPromise = attemptRefresh().finally(() => { + refreshPromise = null; + }); + } + const refreshed = await refreshPromise; + if (refreshed) { + headers = buildApiHeaders(options); + headers["Authorization"] = `Bearer ${accessToken}`; + res = await fetch(`/api/v1${path}`, { ...options, headers }); + } + } + + if (!res.ok) { + throw apiClientErrorFrom(res.status, await parseApiError(res)); + } + + // Reject oversized bodies up front instead of crashing the tab while + // buffering them. When the header is absent, proceed; streaming byte counts + // are not worth the complexity here. + const contentLength = Number(res.headers.get("Content-Length")); + if (Number.isFinite(contentLength) && contentLength > API_BLOB_MAX_BYTES) { + const sizeMiB = Math.round(contentLength / (1024 * 1024)); + const limitMiB = Math.round(API_BLOB_MAX_BYTES / (1024 * 1024)); + throw new ApiClientError( + res.status, + "response_too_large", + `This file is too large to open in the browser (${sizeMiB} MiB, limit ${limitMiB} MiB). Download it instead.`, + ); + } + + return res.blob(); +} + // People API export async function searchPeople(query: string, limit = 20): Promise { const params = new URLSearchParams({ q: query, limit: String(limit) }); diff --git a/web/src/api/types.ts b/web/src/api/types.ts index efc4a80e..ae62dc73 100644 --- a/web/src/api/types.ts +++ b/web/src/api/types.ts @@ -569,6 +569,16 @@ export interface AudiobookDetailExtension { }; } +export interface EbookDetailExtension { + authors: AudiobookPerson[]; + publisher?: string; + series?: AudiobookSeriesGroup; + related: { + also_by_author: AudiobookRelatedItem[]; + similar: AudiobookRelatedItem[]; + }; +} + // Seasons / Watched State export interface LeafItemUserData { played: boolean; @@ -652,7 +662,7 @@ export interface BrowseItemSortMetrics { export interface BrowseItem { content_id: string; - type: "movie" | "series" | "season" | "episode" | "audiobook"; + type: "movie" | "series" | "season" | "episode" | "audiobook" | "ebook"; title: string; series_title?: string; season_number?: number | null; @@ -938,7 +948,7 @@ export type SetMarkersRequest = Partial { expect(markup).toContain('src="/movie-poster.jpg"'); }); + it("links ebook continue rows to the reader and shows percent read", () => { + const queryClient = new QueryClient(); + const markup = renderToStaticMarkup( + + + + + , + ); + + expect(markup).toContain('href="/reader/ebook/ebook%20001?libraryId=12"'); + expect(markup).toContain('href="/item/ebook%20001?libraryId=12"'); + expect(markup).toContain("42% read"); + expect(markup).not.toContain('href="/watch/ebook'); + expect(markup).not.toContain("0 min left"); + }); + it("renders separate watch and item links alongside episodic metadata", () => { const queryClient = new QueryClient(); const markup = renderToStaticMarkup( diff --git a/web/src/components/ContinueWatchingCard.tsx b/web/src/components/ContinueWatchingCard.tsx index 977b3eb2..97a90599 100644 --- a/web/src/components/ContinueWatchingCard.tsx +++ b/web/src/components/ContinueWatchingCard.tsx @@ -1,5 +1,5 @@ import ViewTransitionLink from "@/components/ViewTransitionLink"; -import { Play } from "lucide-react"; +import { BookOpen, Play } from "lucide-react"; import { useCallback } from "react"; import { useLocation } from "react-router"; import type { ItemDetail, SectionItem } from "@/api/types"; @@ -132,11 +132,13 @@ export default function ContinueWatchingCard(props: ContinueWatchingCardProps) { ? premiereBadge ? null : "Next Episode" - : card.durationSeconds > 0 - ? card.type === "audiobook" - ? formatListeningTimeLeft(card.positionSeconds, card.durationSeconds) - : `${Math.round((card.durationSeconds - card.positionSeconds) / 60)} min left` - : "\u00A0"; + : card.type === "ebook" + ? `${Math.round(Math.min(Math.max(progressPercent, 0), 100))}% read` + : card.durationSeconds > 0 + ? card.type === "audiobook" + ? formatListeningTimeLeft(card.positionSeconds, card.durationSeconds) + : `${Math.round((card.durationSeconds - card.positionSeconds) / 60)} min left` + : "\u00A0"; const handleWatchClick = useCallback( (event: React.MouseEvent) => { if ( @@ -251,10 +253,14 @@ export default function ContinueWatchingCard(props: ContinueWatchingCardProps) { - + {card.type === "ebook" ? ( + + ) : ( + + )} ({ + buildDirectDownloadUrl: (fileId: number) => `/api/downloads/files/${fileId}`, +})); + +function version(overrides: Partial): FileVersion { + return { + file_id: overrides.file_id ?? 1, + file_name: overrides.file_name, + file_path: overrides.file_path, + resolution: overrides.resolution ?? "", + codec_video: overrides.codec_video ?? "", + codec_audio: overrides.codec_audio ?? "", + hdr: overrides.hdr ?? false, + container: overrides.container ?? "", + file_size: overrides.file_size ?? 0, + duration: overrides.duration ?? 0, + bitrate: overrides.bitrate ?? 0, + }; +} + +describe("DownloadVersionPicker", () => { + it("uses file-size copy instead of quality copy for multi-file downloads", () => { + render( + undefined} + title="A Psalm for the Wild-Built" + versions={[ + version({ file_id: 1, container: "epub", file_size: 512 * 1024 }), + version({ file_id: 2, container: "pdf", file_size: 2 * 1024 * 1024 }), + ]} + />, + ); + + expect(screen.getByText("Larger files require more storage space.")).toBeTruthy(); + expect(screen.queryByText("Higher quality files require more storage space.")).toBeNull(); + }); + + it("describes download choices as files instead of versions", () => { + render( + undefined} + title="A Psalm for the Wild-Built" + versions={[version({ file_id: 1, container: "epub", file_size: 512 * 1024 })]} + />, + ); + + expect( + screen.getByText("Choose a file to download. Make sure you have enough disk space."), + ).toBeTruthy(); + expect( + screen.queryByText("Choose a version to download. Make sure you have enough disk space."), + ).toBeNull(); + }); + + it("allows ebook download rows to show page counts", () => { + render( + undefined} + title="A Psalm for the Wild-Built" + versions={[ + version({ + file_id: 1, + container: "cbz", + file_size: 25 * 1024 ** 2, + duration: 48, + }), + ]} + summaryBuilder={(file) => `${file.container.toUpperCase()} · 48 pages`} + />, + ); + + expect(screen.getByText("CBZ · 48 pages")).toBeTruthy(); + }); +}); diff --git a/web/src/components/DownloadVersionPicker.tsx b/web/src/components/DownloadVersionPicker.tsx index 4b2502d7..36cbe2aa 100644 --- a/web/src/components/DownloadVersionPicker.tsx +++ b/web/src/components/DownloadVersionPicker.tsx @@ -18,6 +18,7 @@ interface DownloadVersionPickerProps { onOpenChange: (open: boolean) => void; versions: FileVersion[]; title?: string; + summaryBuilder?: (version: FileVersion) => string; } export default function DownloadVersionPicker({ @@ -25,6 +26,7 @@ export default function DownloadVersionPicker({ onOpenChange, versions, title, + summaryBuilder, }: DownloadVersionPickerProps) { const sorted = sortByResolution(versions); const [downloading, setDownloading] = useState(null); @@ -60,14 +62,14 @@ export default function DownloadVersionPicker({ Download{title ? `: ${title}` : ""} - Choose a version to download. Make sure you have enough disk space. + Choose a file to download. Make sure you have enough disk space.

{sorted.map((version) => { - const quality = buildQualitySummary(version); - const size = formatFileSize(version.file_size); + const quality = summaryBuilder?.(version) || buildQualitySummary(version); + const size = summaryBuilder ? "" : formatFileSize(version.file_size); return (
- {isAudiobookLibrary ? null : ( + {isBookLibrary ? null : (
@@ -604,7 +626,7 @@ export default function CollectionGuidedRulesEditor({ />
- {isAudiobookLibrary ? null : ( + {isBookLibrary ? null : ( <>
@@ -671,7 +693,7 @@ export default function CollectionGuidedRulesEditor({ )} - {isAudiobookLibrary ? ( + {isBookLibrary ? ( <>
@@ -696,28 +718,30 @@ export default function CollectionGuidedRulesEditor({ /> )}
-
- - {catalogState ? ( - update({ narrator })} - placeholder="Search narrators..." - disabled={readOnly} - /> - ) : ( - update({ narrator })} - placeholder="Select narrator..." - disabled={readOnly} - isLoading={filtersLoading} - /> - )} -
+ {isAudiobookLibrary ? ( +
+ + {catalogState ? ( + update({ narrator })} + placeholder="Search narrators..." + disabled={readOnly} + /> + ) : ( + update({ narrator })} + placeholder="Select narrator..." + disabled={readOnly} + isLoading={filtersLoading} + /> + )} +
+ ) : null}
@@ -782,7 +806,7 @@ export default function CollectionGuidedRulesEditor({ {allowPersonalizedFilters ? (
- + @@ -810,7 +830,7 @@ export default function CollectionGuidedRulesEditor({ ) : null}
- {isAudiobookLibrary ? null : ( + {isBookLibrary ? null : (
diff --git a/web/src/components/collections/CollectionRulesEditor.test.tsx b/web/src/components/collections/CollectionRulesEditor.test.tsx new file mode 100644 index 00000000..1c43ff49 --- /dev/null +++ b/web/src/components/collections/CollectionRulesEditor.test.tsx @@ -0,0 +1,29 @@ +import { renderToStaticMarkup } from "react-dom/server"; +import { describe, expect, it, vi } from "vitest"; + +import { createEmptyQueryDefinition } from "@/api/types"; + +import CollectionRulesEditor from "./CollectionRulesEditor"; + +vi.mock("@/components/FilterRuleEditor", () => ({ + default: ({ mediaScope }: { mediaScope?: string }) => ( +
filter rule editor mediaScope:{mediaScope}
+ ), +})); + +describe("CollectionRulesEditor", () => { + it("forwards ebook media scope to advanced rule labels", () => { + const markup = renderToStaticMarkup( + {}} + allowPersonalizedFilters + />, + ); + + expect(markup).toContain("mediaScope:ebook"); + }); +}); diff --git a/web/src/components/collections/CollectionRulesEditor.tsx b/web/src/components/collections/CollectionRulesEditor.tsx index 97ec09f9..e4961021 100644 --- a/web/src/components/collections/CollectionRulesEditor.tsx +++ b/web/src/components/collections/CollectionRulesEditor.tsx @@ -70,7 +70,7 @@ export default function CollectionRulesEditor({ media_scope: next === "all" ? undefined - : (next as "movie" | "series" | "episode" | "audiobook"), + : (next as "movie" | "series" | "episode" | "audiobook" | "ebook"), sort: nextSort, }); }} @@ -85,6 +85,7 @@ export default function CollectionRulesEditor({ Series Episodes Audiobooks + Ebooks
@@ -110,6 +111,7 @@ export default function CollectionRulesEditor({ allowPersonalizedFilters={allowPersonalizedFilters} allowPersonalizedSorts={allowPersonalizedSorts} sortRelevanceScope={sortRelevanceScope} + mediaScope={value.media_scope ?? "all"} onChange={(next) => onChange({ ...value, diff --git a/web/src/components/sections/EditableSectionRows.tsx b/web/src/components/sections/EditableSectionRows.tsx index 26c9777a..fcd17a19 100644 --- a/web/src/components/sections/EditableSectionRows.tsx +++ b/web/src/components/sections/EditableSectionRows.tsx @@ -79,6 +79,7 @@ export function SectionSummaryBadges({ {queryDefinition.media_scope === "audiobook" ? ( Audiobooks ) : null} + {queryDefinition.media_scope === "ebook" ? Ebooks : null} {libraries ? queryDefinition.library_ids.map((libraryId) => { const library = libraries.find((entry) => entry.id === libraryId); diff --git a/web/src/components/sections/SectionEditorDrawer.tsx b/web/src/components/sections/SectionEditorDrawer.tsx index 272c6ada..50a39eab 100644 --- a/web/src/components/sections/SectionEditorDrawer.tsx +++ b/web/src/components/sections/SectionEditorDrawer.tsx @@ -479,7 +479,7 @@ export default function SectionEditorDrawer(props: SectionEditorDrawerProps) { media_scope: value === "all" ? undefined - : (value as "movie" | "series" | "episode" | "audiobook"), + : (value as "movie" | "series" | "episode" | "audiobook" | "ebook"), }) } > @@ -492,6 +492,7 @@ export default function SectionEditorDrawer(props: SectionEditorDrawerProps) { Series Episodes Audiobooks + Ebooks
diff --git a/web/src/hooks/queries/catalogRead.test.ts b/web/src/hooks/queries/catalogRead.test.ts index 8aa47bee..139eebfa 100644 --- a/web/src/hooks/queries/catalogRead.test.ts +++ b/web/src/hooks/queries/catalogRead.test.ts @@ -11,6 +11,7 @@ vi.mock("@/api/client", () => ({ import { fetchCatalogItemDetail, fetchCatalogItemEpisodes, + fetchCatalogItemVersions, fetchCatalogSeasonDetail, fetchCatalogSeasonEpisodes, fetchCatalogSeriesSeasons, @@ -28,6 +29,28 @@ describe("catalog read helpers", () => { expect(mocks.api).toHaveBeenCalledWith("/catalog/items/movie-123", undefined); }); + it("encodes item IDs in catalog item endpoints", async () => { + await fetchCatalogItemDetail("ebook 1/isbn:978", 12); + await fetchCatalogItemVersions("ebook 1/isbn:978"); + await fetchCatalogItemEpisodes("season 1/id:abc"); + + expect(mocks.api).toHaveBeenNthCalledWith( + 1, + "/catalog/items/ebook%201%2Fisbn%3A978?library_id=12", + undefined, + ); + expect(mocks.api).toHaveBeenNthCalledWith( + 2, + "/catalog/items/ebook%201%2Fisbn%3A978/versions", + undefined, + ); + expect(mocks.api).toHaveBeenNthCalledWith( + 3, + "/catalog/items/season%201%2Fid%3Aabc/episodes", + undefined, + ); + }); + it("fetches item episodes from the canonical catalog item episodes endpoint", async () => { await fetchCatalogItemEpisodes("season-4"); diff --git a/web/src/hooks/queries/catalogRead.ts b/web/src/hooks/queries/catalogRead.ts index 832071bc..4500d481 100644 --- a/web/src/hooks/queries/catalogRead.ts +++ b/web/src/hooks/queries/catalogRead.ts @@ -10,20 +10,24 @@ import type { } from "@/api/types"; import { catalogKeys } from "./keys"; +function catalogPathID(id: string): string { + return encodeURIComponent(id); +} + export async function fetchCatalogItemDetail( id: string, libraryId?: number, options?: RequestInit, ): Promise { const query = libraryId ? `?library_id=${libraryId}` : ""; - return api(`/catalog/items/${id}${query}`, options); + return api(`/catalog/items/${catalogPathID(id)}${query}`, options); } export async function fetchCatalogItemVersions( id: string, options?: RequestInit, ): Promise { - return api(`/catalog/items/${id}/versions`, options); + return api(`/catalog/items/${catalogPathID(id)}/versions`, options); } export async function fetchCatalogItemEpisodes( @@ -32,7 +36,7 @@ export async function fetchCatalogItemEpisodes( options?: RequestInit, ): Promise { const query = libraryId ? `?library_id=${libraryId}` : ""; - return api(`/catalog/items/${id}/episodes${query}`, options); + return api(`/catalog/items/${catalogPathID(id)}/episodes${query}`, options); } export async function fetchCatalogSeriesSeasons( @@ -41,7 +45,10 @@ export async function fetchCatalogSeriesSeasons( options?: RequestInit, ): Promise { const query = libraryId ? `?library_id=${libraryId}` : ""; - return api(`/catalog/series/${seriesId}/seasons${query}`, options); + return api( + `/catalog/series/${catalogPathID(seriesId)}/seasons${query}`, + options, + ); } export async function fetchCatalogSeasonDetail( @@ -52,7 +59,7 @@ export async function fetchCatalogSeasonDetail( ): Promise { const query = libraryId ? `?library_id=${libraryId}` : ""; return api( - `/catalog/series/${seriesId}/seasons/${seasonNum}${query}`, + `/catalog/series/${catalogPathID(seriesId)}/seasons/${seasonNum}${query}`, options, ); } @@ -65,7 +72,7 @@ export async function fetchCatalogSeasonEpisodes( ): Promise { const query = libraryId ? `?library_id=${libraryId}` : ""; return api( - `/catalog/series/${seriesId}/seasons/${seasonNum}/episodes${query}`, + `/catalog/series/${catalogPathID(seriesId)}/seasons/${seasonNum}/episodes${query}`, options, ); } diff --git a/web/src/hooks/queries/ebookReaderProgress.ts b/web/src/hooks/queries/ebookReaderProgress.ts new file mode 100644 index 00000000..95a892a1 --- /dev/null +++ b/web/src/hooks/queries/ebookReaderProgress.ts @@ -0,0 +1,11 @@ +import { useQuery } from "@tanstack/react-query"; + +import { ebookReaderProgressQueryKey, fetchEbookReaderProgress } from "@/reader/FoliateBookReader"; + +export function useEbookReaderProgress(contentID: string | undefined) { + return useQuery({ + queryKey: ebookReaderProgressQueryKey(contentID), + queryFn: () => fetchEbookReaderProgress(contentID!), + enabled: !!contentID, + }); +} diff --git a/web/src/hooks/queries/homeDismissals.test.ts b/web/src/hooks/queries/homeDismissals.test.ts index a16a1d05..bb61e663 100644 --- a/web/src/hooks/queries/homeDismissals.test.ts +++ b/web/src/hooks/queries/homeDismissals.test.ts @@ -91,6 +91,27 @@ describe("home dismissal query hooks", () => { }); }); + it("encodes item IDs in the dismissal path", async () => { + useDismissHomeItem(); + const mutation = latestMutationOptions(); + + await mutation.mutationFn({ + itemId: "ebook 1/isbn:978", + surface: "continue_watching", + progressUpdatedAt: "2026-03-22T18:10:00Z", + }); + + expect(mocks.api).toHaveBeenCalledWith( + "/home/dismissals/continue_watching/ebook%201%2Fisbn%3A978", + { + method: "PUT", + body: JSON.stringify({ + progress_updated_at: "2026-03-22T18:10:00Z", + }), + }, + ); + }); + it("calls the next up dismissal endpoint with series_id", async () => { useDismissHomeItem(); const mutation = latestMutationOptions(); @@ -162,4 +183,27 @@ describe("home dismissal query hooks", () => { }), ); }); + + it("uses continue reading toast copy for ebook dismissals", async () => { + useDismissHomeItem(); + const mutation = latestMutationOptions(); + + const variables: DismissHomeItemVariables = { + itemId: "ebook-1", + surface: "continue_watching", + mediaType: "ebook", + progressUpdatedAt: "2026-03-22T18:10:00Z", + }; + + await mutation.onSuccess?.(undefined, variables); + + expect(mocks.toastSuccess).toHaveBeenCalledWith( + "Removed from Continue Reading", + expect.objectContaining({ + action: expect.objectContaining({ + label: "Undo", + }), + }), + ); + }); }); diff --git a/web/src/hooks/queries/homeDismissals.ts b/web/src/hooks/queries/homeDismissals.ts index cdfa1bc7..a1422a6f 100644 --- a/web/src/hooks/queries/homeDismissals.ts +++ b/web/src/hooks/queries/homeDismissals.ts @@ -18,7 +18,7 @@ export interface DismissHomeItemVariables { } function dismissalPath({ itemId, surface }: DismissHomeItemVariables) { - return `/home/dismissals/${surface}/${itemId}`; + return `/home/dismissals/${surface}/${encodeURIComponent(itemId)}`; } function dismissalBody({ progressUpdatedAt, seriesId, surface }: DismissHomeItemVariables) { @@ -29,9 +29,9 @@ function dismissalBody({ progressUpdatedAt, seriesId, surface }: DismissHomeItem function dismissalSuccessLabel({ mediaType, surface }: DismissHomeItemVariables) { if (surface === "next_up") return "Removed from Next Up"; - return mediaType === "audiobook" - ? "Removed from Continue Listening" - : "Removed from Continue Watching"; + if (mediaType === "audiobook") return "Removed from Continue Listening"; + if (mediaType === "ebook") return "Removed from Continue Reading"; + return "Removed from Continue Watching"; } export function useDismissHomeItem() { diff --git a/web/src/hooks/queries/items.test.ts b/web/src/hooks/queries/items.test.ts new file mode 100644 index 00000000..eb3dead8 --- /dev/null +++ b/web/src/hooks/queries/items.test.ts @@ -0,0 +1,130 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; + +const mocks = vi.hoisted(() => ({ + api: vi.fn(), + invalidateMediaSurfaceQueries: vi.fn(), + toastSuccess: vi.fn(), + useMutation: vi.fn(), + useQueryClient: vi.fn(), +})); + +vi.mock("@tanstack/react-query", async () => { + const actual = + await vi.importActual("@tanstack/react-query"); + + return { + ...actual, + useMutation: (...args: unknown[]) => mocks.useMutation(...args), + useQueryClient: () => mocks.useQueryClient(), + }; +}); + +vi.mock("@/api/client", () => ({ + api: mocks.api, +})); + +vi.mock("@/components/realtimeEventsContext", () => ({ + useRealtimeEvents: () => ({ awaitAdminJob: vi.fn() }), +})); + +vi.mock("@/pages/ItemDetail/watchedState", async () => { + const actual = await vi.importActual( + "@/pages/ItemDetail/watchedState", + ); + + return { + ...actual, + getCachedWatchedInvalidationKeys: vi.fn(() => []), + }; +}); + +vi.mock("./mediaSurfaceRefresh", () => ({ + invalidateMediaSurfaceQueries: (...args: unknown[]) => + mocks.invalidateMediaSurfaceQueries(...args), +})); + +vi.mock("@/pages/homeSurfaceRefresh", () => ({ + bumpHomeRefreshSignal: vi.fn(), +})); + +vi.mock("sonner", () => ({ + toast: { + error: vi.fn(), + success: (...args: unknown[]) => mocks.toastSuccess(...args), + }, +})); + +import { fetchWatchDetail, redetectEpisodeIntro, useWatchedStateMutation } from "./items"; + +type WatchedMutationOptions = { + mutationFn: (nextPlayed: boolean) => Promise; + onSuccess?: (data: unknown, nextPlayed: boolean) => void; + onSettled?: () => Promise; +}; + +describe("item query helpers", () => { + beforeEach(() => { + mocks.api.mockReset(); + mocks.api.mockResolvedValue({}); + mocks.invalidateMediaSurfaceQueries.mockReset(); + mocks.toastSuccess.mockReset(); + mocks.useMutation.mockReset(); + mocks.useMutation.mockImplementation((options: unknown) => ({ + ...(options as object), + mutate: vi.fn(), + })); + mocks.useQueryClient.mockReset(); + mocks.useQueryClient.mockReturnValue({}); + }); + + it("encodes item IDs in watch detail endpoints", async () => { + await fetchWatchDetail("ebook 1/isbn:978", 42, 12); + + expect(mocks.api).toHaveBeenCalledWith( + "/watch/ebook%201%2Fisbn%3A978?fileId=42&library_id=12", + undefined, + ); + }); + + it("encodes item IDs in admin item endpoints", async () => { + await redetectEpisodeIntro("episode 1/id:abc"); + + expect(mocks.api).toHaveBeenCalledWith("/admin/items/episode%201%2Fid%3Aabc/redetect-intro", { + method: "POST", + }); + }); + + it("toggles ebook read state through the watched endpoint", async () => { + useWatchedStateMutation({ content_id: "ebook 1/isbn:978", type: "ebook" }); + const options = mocks.useMutation.mock.calls[ + mocks.useMutation.mock.calls.length - 1 + ]?.[0] as WatchedMutationOptions; + + await options.mutationFn(true); + expect(mocks.api).toHaveBeenCalledWith("/watched/ebook%201%2Fisbn%3A978", { method: "POST" }); + + await options.mutationFn(false); + expect(mocks.api).toHaveBeenCalledWith("/watched/ebook%201%2Fisbn%3A978", { + method: "DELETE", + }); + }); + + it("uses read toast copy and refreshes surfaces for ebook watched toggles", async () => { + useWatchedStateMutation({ content_id: "ebook-1", type: "ebook" }); + const options = mocks.useMutation.mock.calls[ + mocks.useMutation.mock.calls.length - 1 + ]?.[0] as WatchedMutationOptions; + + options.onSuccess?.(undefined, true); + expect(mocks.toastSuccess).toHaveBeenCalledWith("Marked as read"); + + options.onSuccess?.(undefined, false); + expect(mocks.toastSuccess).toHaveBeenCalledWith("Marked as unread"); + + await options.onSettled?.(); + expect(mocks.invalidateMediaSurfaceQueries).toHaveBeenCalledWith(expect.anything(), { + itemId: "ebook-1", + watchedKeys: [], + }); + }); +}); diff --git a/web/src/hooks/queries/items.ts b/web/src/hooks/queries/items.ts index de879a3f..af851781 100644 --- a/web/src/hooks/queries/items.ts +++ b/web/src/hooks/queries/items.ts @@ -13,10 +13,17 @@ import type { } from "@/api/types"; import { adminKeys, catalogKeys, episodeKeys, itemKeys, sectionKeys } from "./keys"; import { toast } from "sonner"; -import { getCachedWatchedInvalidationKeys } from "@/pages/ItemDetail/watchedState"; +import { + getCachedWatchedInvalidationKeys, + getWatchedToastMessage, +} from "@/pages/ItemDetail/watchedState"; import { invalidateMediaSurfaceQueries } from "./mediaSurfaceRefresh"; import { bumpHomeRefreshSignal } from "@/pages/homeSurfaceRefresh"; +function itemPathID(id: string): string { + return encodeURIComponent(id); +} + export async function fetchWatchDetail( id: string, fileId?: number, @@ -27,7 +34,7 @@ export async function fetchWatchDetail( if (fileId != null) searchParams.set("fileId", String(fileId)); if (libraryId != null) searchParams.set("library_id", String(libraryId)); const query = searchParams.toString(); - return api(`/watch/${id}${query ? `?${query}` : ""}`, options); + return api(`/watch/${itemPathID(id)}${query ? `?${query}` : ""}`, options); } export function useWatchDetail(id: string | undefined, fileId?: number, libraryId?: number) { @@ -64,10 +71,13 @@ export function useRefreshItemMetadata() { const { awaitAdminJob } = useRealtimeEvents(); return useMutation({ mutationFn: async ({ item, mode }: RefreshItemMetadataVariables) => { - const job = await api(`/admin/items/${item.content_id}/refresh-metadata`, { - method: "POST", - body: JSON.stringify({ mode }), - }); + const job = await api( + `/admin/items/${itemPathID(item.content_id)}/refresh-metadata`, + { + method: "POST", + body: JSON.stringify({ mode }), + }, + ); const completed = await awaitAdminJob(job.id); return { job: completed }; }, @@ -169,7 +179,7 @@ export interface RedetectEpisodeIntroResponse { export async function redetectEpisodeIntro( episodeId: string, ): Promise { - return api(`/admin/items/${episodeId}/redetect-intro`, { + return api(`/admin/items/${itemPathID(episodeId)}/redetect-intro`, { method: "POST", }); } @@ -226,7 +236,7 @@ export function useUpdateItemMetadata(contentId: string) { const queryClient = useQueryClient(); return useMutation({ mutationFn: (data: UpdateItemMetadataRequest) => - api(`/admin/items/${contentId}/metadata`, { + api(`/admin/items/${itemPathID(contentId)}/metadata`, { method: "PATCH", body: JSON.stringify(data), }), @@ -252,14 +262,14 @@ export function useWatchedStateMutation(item: WatchedMutationItem) { return useMutation({ mutationFn: (nextPlayed: boolean) => - api(`/watched/${item.content_id}`, { + api(`/watched/${itemPathID(item.content_id)}`, { method: nextPlayed ? "POST" : "DELETE", }), onError: (err) => { toast.error(err instanceof Error ? err.message : "Failed to update watched state"); }, onSuccess: (_data, nextPlayed) => { - toast.success(nextPlayed ? "Marked as watched" : "Marked as unwatched"); + toast.success(getWatchedToastMessage(item, nextPlayed)); }, onSettled: async () => { await invalidateMediaSurfaceQueries(queryClient, { @@ -276,7 +286,7 @@ export function useSearchItemMatchCandidates(contentId: string) { return useMutation({ mutationFn: (params: ItemMatchSearchRequest) => - api(`/admin/items/${contentId}/match/search`, { + api(`/admin/items/${itemPathID(contentId)}/match/search`, { method: "POST", body: JSON.stringify(params), }), @@ -300,7 +310,7 @@ export function useApplyItemMatch() { item: ApplyMatchItem; providerIds: Record; }) => { - return api(`/admin/items/${item.content_id}/match/apply`, { + return api(`/admin/items/${itemPathID(item.content_id)}/match/apply`, { method: "POST", body: JSON.stringify({ provider_ids: providerIds }), }); @@ -365,7 +375,7 @@ export function useApplyItemMatch() { export function useItemImages(contentId: string | undefined, enabled = true) { return useQuery({ queryKey: adminKeys.itemImages(contentId!), - queryFn: () => api(`/admin/items/${contentId}/images`), + queryFn: () => api(`/admin/items/${itemPathID(contentId!)}/images`), enabled: !!contentId && enabled, staleTime: 5 * 60_000, }); @@ -384,7 +394,7 @@ export function useApplyItemImage() { item: ApplyImageItem; request: ApplyItemImageRequest; }) => - api(`/admin/items/${item.content_id}/images/apply`, { + api(`/admin/items/${itemPathID(item.content_id)}/images/apply`, { method: "POST", body: JSON.stringify(request), }), diff --git a/web/src/hooks/queries/keys.ts b/web/src/hooks/queries/keys.ts index 5669ea1f..7aa8a5bd 100644 --- a/web/src/hooks/queries/keys.ts +++ b/web/src/hooks/queries/keys.ts @@ -187,6 +187,10 @@ export const episodeKeys = { byItem: (itemId: string) => ["episodes", "item", itemId] as const, }; +export const ebookKeys = { + readerProgress: (contentId: string | undefined) => ["ebook-reader-progress", contentId] as const, +}; + export const libraryKeys = { all: ["libraries"] as const, user: (profileId?: string | null) => ["libraries", "user", profileId ?? "none"] as const, diff --git a/web/src/hooks/useScreenWakeLock.ts b/web/src/hooks/useScreenWakeLock.ts new file mode 100644 index 00000000..3a9e9639 --- /dev/null +++ b/web/src/hooks/useScreenWakeLock.ts @@ -0,0 +1,51 @@ +import { useEffect, useRef } from "react"; + +export function useScreenWakeLock(active: boolean) { + const sentinelRef = useRef(null); + + useEffect(() => { + if (!active) { + sentinelRef.current?.release().catch(() => {}); + sentinelRef.current = null; + return; + } + if (!("wakeLock" in navigator)) return; + + let cancelled = false; + const acquire = async () => { + try { + const sentinel = await ( + navigator as Navigator & { + wakeLock: { request: (type: "screen") => Promise }; + } + ).wakeLock.request("screen"); + if (cancelled) { + await sentinel.release().catch(() => {}); + return; + } + sentinelRef.current = sentinel; + } catch { + // Unsupported or denied; reading still works without it. + } + }; + void acquire(); + + const onVisible = () => { + if (document.visibilityState === "visible" && !sentinelRef.current) { + void acquire(); + } + }; + document.addEventListener("visibilitychange", onVisible); + + return () => { + cancelled = true; + document.removeEventListener("visibilitychange", onVisible); + sentinelRef.current?.release().catch(() => {}); + sentinelRef.current = null; + }; + }, [active]); +} + +interface WakeLockSentinel { + release(): Promise; +} diff --git a/web/src/hooks/useTTS.test.ts b/web/src/hooks/useTTS.test.ts new file mode 100644 index 00000000..55d0e3a0 --- /dev/null +++ b/web/src/hooks/useTTS.test.ts @@ -0,0 +1,243 @@ +// @vitest-environment jsdom + +import { act, cleanup, renderHook } from "@testing-library/react"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; + +import { useTTS } from "./useTTS"; + +class FakeUtterance { + text: string; + rate = 1; + pitch = 1; + volume = 1; + lang = ""; + voice: SpeechSynthesisVoice | null = null; + onend: (() => void) | null = null; + onerror: (() => void) | null = null; + + constructor(text: string) { + this.text = text; + } +} + +type FakeSynth = { + speak: ReturnType; + cancel: ReturnType; + pause: ReturnType; + resume: ReturnType; + getVoices: ReturnType; + addEventListener: ReturnType; + removeEventListener: ReturnType; + spoken: FakeUtterance[]; +}; + +// Browsers dispatch a synthetic event on the in-flight utterance when cancel() +// is called: Chromium fires end, Firefox fires error. +function installFakeSpeechSynthesis(cancelEvent: "end" | "error"): FakeSynth { + const spoken: FakeUtterance[] = []; + let inFlight: FakeUtterance | null = null; + const synth: FakeSynth = { + spoken, + speak: vi.fn((utterance: FakeUtterance) => { + inFlight = utterance; + spoken.push(utterance); + }), + cancel: vi.fn(() => { + const utterance = inFlight; + inFlight = null; + if (!utterance) return; + if (cancelEvent === "end") utterance.onend?.(); + else utterance.onerror?.(); + }), + pause: vi.fn(), + resume: vi.fn(), + getVoices: vi.fn(() => []), + addEventListener: vi.fn(), + removeEventListener: vi.fn(), + }; + vi.stubGlobal("speechSynthesis", synth); + vi.stubGlobal("SpeechSynthesisUtterance", FakeUtterance); + return synth; +} + +type FakeMediaSession = { + metadata: unknown; + playbackState: string; + setActionHandler: ReturnType; +}; + +function installFakeMediaSession(): FakeMediaSession { + const mediaSession: FakeMediaSession = { + metadata: null, + playbackState: "none", + setActionHandler: vi.fn(), + }; + Object.defineProperty(navigator, "mediaSession", { + value: mediaSession, + configurable: true, + }); + vi.stubGlobal( + "MediaMetadata", + class { + title: string; + constructor(init: { title?: string } = {}) { + this.title = init.title ?? ""; + } + }, + ); + return mediaSession; +} + +// Long enough to split into multiple queue chunks. +const MULTI_CHUNK_TEXT = "First sentence here. Second sentence here. Third sentence here."; + +describe("useTTS", () => { + beforeEach(() => { + installFakeMediaSession(); + }); + + afterEach(() => { + // Unmount while the fake speechSynthesis is still installed; the hook's + // effect cleanup dereferences window.speechSynthesis. + cleanup(); + vi.unstubAllGlobals(); + }); + + it("speaks chunks sequentially and returns to idle when the queue drains", () => { + const synth = installFakeSpeechSynthesis("end"); + const { result } = renderHook(() => useTTS()); + + act(() => { + result.current.speak("One. Two."); + }); + expect(result.current.state).toBe("speaking"); + expect(synth.spoken).toHaveLength(1); + expect(synth.spoken[0]!.text).toBe("One."); + + act(() => { + synth.spoken[0]!.onend?.(); + }); + expect(synth.spoken).toHaveLength(2); + expect(synth.spoken[1]!.text).toBe("Two."); + + act(() => { + synth.spoken[1]!.onend?.(); + }); + expect(result.current.state).toBe("idle"); + expect(synth.spoken).toHaveLength(2); + }); + + it("stop stays stopped when cancel dispatches end on the in-flight utterance (Chromium)", () => { + const synth = installFakeSpeechSynthesis("end"); + const { result } = renderHook(() => useTTS()); + + act(() => { + result.current.speak(MULTI_CHUNK_TEXT); + }); + expect(synth.spoken).toHaveLength(1); + // Capture the handler as the browser's event loop would have: an end event + // already dispatched for the in-flight utterance may race the cancel. + const racingHandler = synth.spoken[0]!.onend; + + act(() => { + result.current.stop(); + racingHandler?.(); + }); + + expect(synth.cancel).toHaveBeenCalled(); + expect(synth.spoken).toHaveLength(1); + expect(result.current.state).toBe("idle"); + }); + + it("stop stays stopped when cancel dispatches error on the in-flight utterance (Firefox)", () => { + const synth = installFakeSpeechSynthesis("error"); + const { result } = renderHook(() => useTTS()); + + act(() => { + result.current.speak(MULTI_CHUNK_TEXT); + }); + const racingHandler = synth.spoken[0]!.onerror; + + act(() => { + result.current.stop(); + racingHandler?.(); + }); + + expect(synth.spoken).toHaveLength(1); + expect(result.current.state).toBe("idle"); + }); + + it("does not interleave the previous queue when a new speak starts", () => { + const synth = installFakeSpeechSynthesis("end"); + const { result } = renderHook(() => useTTS()); + + act(() => { + result.current.speak(MULTI_CHUNK_TEXT); + }); + const staleHandler = synth.spoken[0]!.onend; + + act(() => { + result.current.speak("New text. More new text."); + // A late end event from the superseded utterance must not advance the + // old queue alongside the new one. + staleHandler?.(); + }); + + expect(synth.spoken.map((utterance) => utterance.text)).toEqual([ + "First sentence here.", + "New text.", + ]); + + act(() => { + synth.spoken[1]!.onend?.(); + }); + expect(synth.spoken.map((utterance) => utterance.text)).toEqual([ + "First sentence here.", + "New text.", + "More new text.", + ]); + }); + + it("installs media session handlers on speak and clears them on stop", () => { + installFakeSpeechSynthesis("end"); + const mediaSession = navigator.mediaSession as unknown as FakeMediaSession; + const { result } = renderHook(() => useTTS()); + + act(() => { + result.current.speak("Hello there."); + }); + expect(mediaSession.metadata).not.toBeNull(); + expect(mediaSession.playbackState).toBe("playing"); + + act(() => { + result.current.stop(); + }); + expect(mediaSession.metadata).toBeNull(); + expect(mediaSession.playbackState).toBe("none"); + expect(mediaSession.setActionHandler).toHaveBeenCalledWith("play", null); + expect(mediaSession.setActionHandler).toHaveBeenCalledWith("pause", null); + expect(mediaSession.setActionHandler).toHaveBeenCalledWith("stop", null); + }); + + it("cancels speech and uninstalls the media session when unmounting mid-speech", () => { + const synth = installFakeSpeechSynthesis("end"); + const mediaSession = navigator.mediaSession as unknown as FakeMediaSession; + const { result, unmount } = renderHook(() => useTTS()); + + act(() => { + result.current.speak(MULTI_CHUNK_TEXT); + }); + expect(mediaSession.playbackState).toBe("playing"); + + unmount(); + + expect(synth.cancel).toHaveBeenCalled(); + // No chunk may start after unmount, even though cancel fired an end event. + expect(synth.spoken).toHaveLength(1); + expect(mediaSession.metadata).toBeNull(); + expect(mediaSession.playbackState).toBe("none"); + expect(mediaSession.setActionHandler).toHaveBeenCalledWith("play", null); + expect(mediaSession.setActionHandler).toHaveBeenCalledWith("pause", null); + expect(mediaSession.setActionHandler).toHaveBeenCalledWith("stop", null); + }); +}); diff --git a/web/src/hooks/useTTS.ts b/web/src/hooks/useTTS.ts new file mode 100644 index 00000000..1e1d40b2 --- /dev/null +++ b/web/src/hooks/useTTS.ts @@ -0,0 +1,149 @@ +import { useCallback, useEffect, useRef, useState } from "react"; + +export type TTSState = "idle" | "speaking" | "paused"; + +export interface TTSOptions { + rate?: number; + pitch?: number; + volume?: number; + voiceURI?: string; + lang?: string; +} + +export function useTTS() { + const [state, setState] = useState("idle"); + const [voices, setVoices] = useState([]); + const currentUtter = useRef(null); + // speechSynthesis.cancel() dispatches end (Chromium) or error (Firefox) on the + // in-flight utterance, which would re-trigger the queued speakNext and resume + // playback. Every speak()/stop() bumps this generation; continuations from an + // older generation return without doing anything. + const generationRef = useRef(0); + + useEffect(() => { + if (typeof window === "undefined" || !("speechSynthesis" in window)) return; + const update = () => setVoices(window.speechSynthesis.getVoices()); + update(); + window.speechSynthesis.addEventListener("voiceschanged", update); + return () => window.speechSynthesis.removeEventListener("voiceschanged", update); + }, []); + + // Invalidates any in-flight queue and cancels the current utterance without + // letting its synthetic end/error event restart playback. + const cancelSpeech = useCallback(() => { + generationRef.current += 1; + const utterance = currentUtter.current; + if (utterance) { + utterance.onend = null; + utterance.onerror = null; + currentUtter.current = null; + } + window.speechSynthesis.cancel(); + }, []); + + const stop = useCallback(() => { + if (!("speechSynthesis" in window)) return; + cancelSpeech(); + uninstallMediaSession(); + setState("idle"); + }, [cancelSpeech]); + + const pause = useCallback(() => { + if (!("speechSynthesis" in window)) return; + window.speechSynthesis.pause(); + setState("paused"); + }, []); + + const resume = useCallback(() => { + if (!("speechSynthesis" in window)) return; + window.speechSynthesis.resume(); + setState("speaking"); + }, []); + + const speak = useCallback( + (text: string, opts: TTSOptions = {}) => { + if (!("speechSynthesis" in window)) return; + cancelSpeech(); + const generation = generationRef.current; + const cleaned = text.replace(/\s+/g, " ").trim(); + if (!cleaned) return; + + const sentences = cleaned.match(/[^.!?]+[.!?]+|\S[^.!?]*$/g) ?? [cleaned]; + const queue: string[] = []; + for (const sentence of sentences) { + let chunk = sentence.trim(); + while (chunk.length > 200) { + const cut = chunk.lastIndexOf(" ", 200); + queue.push(chunk.slice(0, cut > 0 ? cut : 200)); + chunk = chunk.slice(cut > 0 ? cut + 1 : 200); + } + if (chunk) queue.push(chunk); + } + + const speakNext = () => { + if (generation !== generationRef.current) return; + const next = queue.shift(); + if (!next) { + setState("idle"); + currentUtter.current = null; + uninstallMediaSession(); + return; + } + const utterance = new SpeechSynthesisUtterance(next); + utterance.rate = opts.rate ?? 1; + utterance.pitch = opts.pitch ?? 1; + utterance.volume = opts.volume ?? 1; + if (opts.lang) utterance.lang = opts.lang; + if (opts.voiceURI) { + const voice = window.speechSynthesis + .getVoices() + .find((candidate) => candidate.voiceURI === opts.voiceURI); + if (voice) utterance.voice = voice; + } + utterance.onend = speakNext; + utterance.onerror = speakNext; + currentUtter.current = utterance; + window.speechSynthesis.speak(utterance); + }; + + setState("speaking"); + installMediaSession({ title: "Read aloud" }, { pause, resume, stop }); + speakNext(); + }, + [cancelSpeech, pause, resume, stop], + ); + + useEffect(() => { + return () => { + if ("speechSynthesis" in window) cancelSpeech(); + // Unmounting mid-speech must not leave stale media session metadata or + // action handlers pointing at the dead component. + uninstallMediaSession(); + }; + }, [cancelSpeech]); + + return { state, voices, speak, pause, resume, stop }; +} + +function installMediaSession( + meta: { title?: string }, + handlers: { pause: () => void; resume: () => void; stop: () => void }, +) { + if (typeof navigator === "undefined" || !("mediaSession" in navigator)) return; + navigator.mediaSession.metadata = new window.MediaMetadata({ + title: meta.title ?? "Read aloud", + }); + navigator.mediaSession.playbackState = "playing"; + navigator.mediaSession.setActionHandler("play", handlers.resume); + navigator.mediaSession.setActionHandler("pause", handlers.pause); + navigator.mediaSession.setActionHandler("stop", handlers.stop); +} + +function uninstallMediaSession() { + if (typeof navigator === "undefined" || !("mediaSession" in navigator)) return; + navigator.mediaSession.metadata = null; + navigator.mediaSession.playbackState = "none"; + navigator.mediaSession.setActionHandler("play", null); + navigator.mediaSession.setActionHandler("pause", null); + navigator.mediaSession.setActionHandler("stop", null); +} diff --git a/web/src/lib/mediaNavigation.ts b/web/src/lib/mediaNavigation.ts index cce72115..cf9653d7 100644 --- a/web/src/lib/mediaNavigation.ts +++ b/web/src/lib/mediaNavigation.ts @@ -1,4 +1,11 @@ -type PlayableMediaType = "movie" | "series" | "season" | "episode" | "audiobook" | "podcast"; +type PlayableMediaType = + | "movie" + | "series" + | "season" + | "episode" + | "audiobook" + | "ebook" + | "podcast"; interface MediaHrefInput { contentId: string; @@ -23,15 +30,22 @@ export function buildItemHref({ contentId, libraryId, }: Pick) { - return appendQuery(`/item/${contentId}`, { libraryId }); + return appendQuery(`/item/${encodeURIComponent(contentId)}`, { libraryId }); } export function buildMediaPlayHref({ contentId, type, libraryId, restart }: MediaHrefInput) { if (type === "movie" || type === "episode") { - return appendQuery(`/watch/${contentId}`, { libraryId, restart }); + return appendQuery(`/watch/${encodeURIComponent(contentId)}`, { libraryId, restart }); } if (type === "audiobook") { - return appendQuery(`/item/${contentId}`, { libraryId, play: true, restart }); + return appendQuery(`/item/${encodeURIComponent(contentId)}`, { + libraryId, + play: true, + restart, + }); + } + if (type === "ebook") { + return appendQuery(`/reader/ebook/${encodeURIComponent(contentId)}`, { libraryId }); } return buildItemHref({ contentId, libraryId }); } diff --git a/web/src/lib/querySortOptions.test.ts b/web/src/lib/querySortOptions.test.ts new file mode 100644 index 00000000..3869d0a8 --- /dev/null +++ b/web/src/lib/querySortOptions.test.ts @@ -0,0 +1,37 @@ +import { describe, expect, it } from "vitest"; + +import { getQuerySortOptions } from "./querySortOptions"; + +describe("getQuerySortOptions", () => { + it("allows ebook book-native sorts without enabling narrator", () => { + const fields = getQuerySortOptions({ relevanceScope: "ebook" }).map((option) => option.value); + + expect(fields).toContain("author"); + expect(fields).toContain("series"); + expect(fields).not.toContain("narrator"); + }); + + it("uses reading labels for ebook personalized sorts", () => { + const labelsByField = new Map( + getQuerySortOptions({ includePersonalized: true, relevanceScope: "ebook" }).map((option) => [ + option.value, + option.label, + ]), + ); + + expect(labelsByField.get("date_viewed")).toBe("Date Read"); + expect(labelsByField.get("plays")).toBe("Reads"); + }); + + it("keeps video labels for movie personalized sorts", () => { + const labelsByField = new Map( + getQuerySortOptions({ includePersonalized: true, relevanceScope: "movie" }).map((option) => [ + option.value, + option.label, + ]), + ); + + expect(labelsByField.get("date_viewed")).toBe("Date Viewed"); + expect(labelsByField.get("plays")).toBe("Plays"); + }); +}); diff --git a/web/src/lib/querySortOptions.ts b/web/src/lib/querySortOptions.ts index 62b19d27..608b0b1f 100644 --- a/web/src/lib/querySortOptions.ts +++ b/web/src/lib/querySortOptions.ts @@ -1,5 +1,11 @@ export type QuerySortOrder = "asc" | "desc"; -export type QuerySortRelevanceScope = "movie" | "series" | "episode" | "audiobook" | "all"; +export type QuerySortRelevanceScope = + | "movie" + | "series" + | "episode" + | "audiobook" + | "ebook" + | "all"; export type QuerySortField = | "title" | "added_at" @@ -53,11 +59,11 @@ interface QuerySortLike { // ALL_VIDEO_SCOPES is the universe of video-side scopes — used as the // baseline for the "all" relevance scope (mixed libraries / episode // browse mode) so that sorts limited to series+episode are still -// filtered out as not universally applicable. Audiobook is explicit -// opt-in: a sort field must list "audiobook" in its applicableMediaScopes -// to be eligible for an audiobook-only library. +// filtered out as not universally applicable. Book scopes are explicit +// opt-in: a sort field must list "audiobook" or "ebook" in its +// applicableMediaScopes to be eligible for book-only libraries. const ALL_VIDEO_SCOPES: ApplicableMediaScope[] = ["movie", "series", "episode"]; -const ALL_MEDIA_SCOPES: ApplicableMediaScope[] = [...ALL_VIDEO_SCOPES, "audiobook"]; +const ALL_MEDIA_SCOPES: ApplicableMediaScope[] = [...ALL_VIDEO_SCOPES, "audiobook", "ebook"]; export const QUERY_SORT_OPTIONS: QuerySortOption[] = [ { @@ -173,16 +179,15 @@ export const QUERY_SORT_OPTIONS: QuerySortOption[] = [ personalized: true, applicableMediaScopes: ALL_MEDIA_SCOPES, }, - // Audiobook-native sorts. Author / Narrator use alphabetically-first - // person name per item; Series uses audiobook_series.series_name with - // series_index breaking ties so books within a series come back in - // narrative order. + // Book-native sorts. Author is shared by audiobooks and ebooks; narrator + // remains audiobook-only. Series is shared by the audiobook_series and + // ebook_series joins on the backend. { value: "author", label: "Author", defaultOrder: "asc", personalized: false, - applicableMediaScopes: ["audiobook"], + applicableMediaScopes: ["audiobook", "ebook"], }, { value: "narrator", @@ -196,12 +201,17 @@ export const QUERY_SORT_OPTIONS: QuerySortOption[] = [ label: "Series", defaultOrder: "asc", personalized: false, - applicableMediaScopes: ["audiobook"], + applicableMediaScopes: ["audiobook", "ebook"], }, ]; const QUERY_SORT_OPTION_MAP = new Map(QUERY_SORT_OPTIONS.map((option) => [option.value, option])); +const EBOOK_SORT_LABELS: Partial> = { + date_viewed: "Date Read", + plays: "Reads", +}; + function normalizeQuerySortOptionsConfig( input: QuerySortOptionsInput = false, ): QuerySortOptionsConfig { @@ -220,7 +230,7 @@ function optionMatchesRelevanceScope( } // "all" — applies to mixed libraries and the episode browse mode. // Match the historical baseline (universal across all video scopes) - // so series-only sorts still get normalized away. Audiobook scope is + // so series-only sorts still get normalized away. Book scopes are // intentionally not part of this baseline; see ALL_VIDEO_SCOPES. if (relevanceScope === "all") { return ALL_VIDEO_SCOPES.every((scope) => option.applicableMediaScopes.includes(scope)); @@ -235,7 +245,10 @@ export function getQuerySortOptions(input: QuerySortOptionsInput = false): Query (option) => (includePersonalized || !option.personalized) && optionMatchesRelevanceScope(option, relevanceScope), - ); + ).map((option) => { + const ebookLabel = relevanceScope === "ebook" ? EBOOK_SORT_LABELS[option.value] : undefined; + return ebookLabel ? { ...option, label: ebookLabel } : option; + }); } export function normalizeQuerySortForScope( diff --git a/web/src/pages/EbookReader.test.tsx b/web/src/pages/EbookReader.test.tsx new file mode 100644 index 00000000..36999b63 --- /dev/null +++ b/web/src/pages/EbookReader.test.tsx @@ -0,0 +1,1198 @@ +// @vitest-environment jsdom + +import { act, useEffect, useImperativeHandle, forwardRef } from "react"; +import { createRoot, type Root } from "react-dom/client"; +import { MemoryRouter, Route, Routes } from "react-router"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; + +import type { FileVersion, ItemDetail } from "@/api/types"; +import EbookReader from "./EbookReader"; + +const mocks = vi.hoisted(() => ({ + useCatalogItemDetail: vi.fn(), + readerPrev: vi.fn(), + readerNext: vi.fn(), + readerGoTo: vi.fn(), + readerGoToFraction: vi.fn(), + readerSearch: vi.fn(), + captureReaderSettings: vi.fn(), + fetchEbookReaderConfig: vi.fn(), + saveEbookReaderConfig: vi.fn(), + saveEbookReaderConfigKeepalive: vi.fn(), + fetchEbookReaderAnnotations: vi.fn(), + createEbookReaderAnnotation: vi.fn(), + deleteEbookReaderAnnotation: vi.fn(), +})); + +vi.mock("@/hooks/queries/catalogRead", () => ({ + useCatalogItemDetail: mocks.useCatalogItemDetail, +})); + +vi.mock("@/components/PageBack", () => ({ + default: () =>
, +})); + +vi.mock("@/reader/ebookReaderApi", () => ({ + createEbookReaderAnnotation: mocks.createEbookReaderAnnotation, + deleteEbookReaderAnnotation: mocks.deleteEbookReaderAnnotation, + fetchEbookReaderAnnotations: mocks.fetchEbookReaderAnnotations, + fetchEbookReaderConfig: mocks.fetchEbookReaderConfig, + saveEbookReaderConfig: mocks.saveEbookReaderConfig, + saveEbookReaderConfigKeepalive: mocks.saveEbookReaderConfigKeepalive, +})); + +vi.mock("@/reader/FoliateBookReader", async () => { + const actual = await vi.importActual( + "@/reader/FoliateBookReader", + ); + + return { + ...actual, + default: forwardRef< + { + prev: () => void; + next: () => void; + goTo: (href: string) => void; + goToFraction: (fraction: number) => Promise; + search: ( + query: string, + ) => Promise>; + clearSearch: () => void; + clearSelection: () => void; + createSelectionAnnotation: () => { cfi: string; selectedText: string } | null; + getReadableText: () => string; + }, + { + file: FileVersion; + settings?: unknown; + annotations?: unknown[]; + onProgressChange?: (progress: number | null) => void; + onFileLoaded?: (state: { objectUrl: string; filename: string } | null) => void; + onSelectionChange?: (selection: { cfi: string; selectedText: string } | null) => void; + onReady?: (state: { + toc: Array<{ + id: number; + label: string; + href: string; + index: number; + subitems?: Array<{ id: number; label: string; href: string; index: number }>; + }>; + }) => void; + } + >(function MockFoliateBookReader( + { file, settings, onProgressChange, onFileLoaded, onSelectionChange, onReady }, + ref, + ) { + mocks.captureReaderSettings(settings); + useImperativeHandle(ref, () => ({ + prev: mocks.readerPrev, + next: mocks.readerNext, + goTo: mocks.readerGoTo, + goToFraction: mocks.readerGoToFraction, + search: mocks.readerSearch, + clearSearch: vi.fn(), + clearSelection: () => onSelectionChange?.(null), + createSelectionAnnotation: () => ({ + cfi: "epubcfi(/6/4,/1:0,/1:12)", + selectedText: "sample text", + }), + getReadableText: () => "Readable text for speech", + })); + useEffect(() => { + onFileLoaded?.({ objectUrl: "blob:ebook", filename: "Reader.epub" }); + onProgressChange?.(0.421); + onSelectionChange?.({ + cfi: "epubcfi(/6/4,/1:0,/1:12)", + selectedText: "sample text", + }); + onReady?.({ + toc: [ + { + id: 1, + label: "Opening", + href: "chapter-1.xhtml", + index: 0, + subitems: [{ id: 2, label: "Aboard", href: "chapter-1.xhtml#aboard", index: 0 }], + }, + ], + }); + return () => onFileLoaded?.(null); + }, [onFileLoaded, onProgressChange, onReady, onSelectionChange]); + return
reader surface {file.file_name}
; + }), + }; +}); + +function makeVersion(overrides: Partial = {}): FileVersion { + return { + file_id: overrides.file_id ?? 7, + file_name: overrides.file_name ?? "Reader.epub", + file_path: overrides.file_path ?? "/books/reader.epub", + resolution: "", + codec_video: "", + codec_audio: "", + hdr: false, + container: overrides.container ?? "epub", + file_size: 100, + duration: 0, + bitrate: 0, + }; +} + +function makeEbookItem( + overrides: Partial = {}, +): ItemDetail & { type: "ebook" } { + return { + content_id: "ebook-1", + type: "ebook", + title: "Reader Book", + original_title: "", + year: 2026, + overview: "", + tagline: "", + runtime: 0, + content_rating: "", + genres: [], + rating_imdb: null, + rating_tmdb: null, + rating_rt_critic: null, + rating_rt_audience: null, + imdb_id: "", + tmdb_id: "", + tvdb_id: "", + cast: [], + crew: [], + studios: [], + networks: [], + countries: [], + release_date: null, + first_air_date: null, + last_air_date: null, + poster_url: "", + poster_thumbhash: "", + backdrop_url: "", + backdrop_thumbhash: "", + logo_url: "", + season_count: null, + series_id: "", + series_title: "", + season_number: null, + episode_number: null, + episode_count: null, + air_date: null, + is_specials: false, + versions: [makeVersion()], + subtitles: [], + intro: null, + credits: null, + ...overrides, + }; +} + +function installStorage() { + const values = new Map(); + const storage = { + getItem: vi.fn((key: string) => values.get(key) ?? null), + setItem: vi.fn((key: string, value: string) => values.set(key, value)), + removeItem: vi.fn((key: string) => values.delete(key)), + clear: vi.fn(() => values.clear()), + key: vi.fn((index: number) => Array.from(values.keys())[index] ?? null), + get length() { + return values.size; + }, + } as Storage; + Object.defineProperty(window, "localStorage", { + value: storage, + configurable: true, + }); + Object.defineProperty(globalThis, "localStorage", { + value: storage, + configurable: true, + }); + return storage; +} + +function setInputValue(input: HTMLInputElement, value: string) { + const setter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value")?.set; + setter?.call(input, value); + input.dispatchEvent(new Event("input", { bubbles: true })); +} + +describe("EbookReader", () => { + let container: HTMLDivElement; + let root: Root; + + beforeEach(() => { + ( + globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT?: boolean } + ).IS_REACT_ACT_ENVIRONMENT = true; + + container = document.createElement("div"); + document.body.appendChild(container); + root = createRoot(container); + installStorage(); + mocks.useCatalogItemDetail.mockReset(); + mocks.readerPrev.mockReset(); + mocks.readerNext.mockReset(); + mocks.readerGoTo.mockReset(); + mocks.readerGoToFraction.mockReset(); + mocks.readerSearch.mockReset(); + mocks.captureReaderSettings.mockReset(); + mocks.fetchEbookReaderConfig.mockReset(); + mocks.saveEbookReaderConfig.mockReset(); + mocks.saveEbookReaderConfigKeepalive.mockReset(); + mocks.fetchEbookReaderAnnotations.mockReset(); + mocks.createEbookReaderAnnotation.mockReset(); + mocks.deleteEbookReaderAnnotation.mockReset(); + mocks.readerSearch.mockResolvedValue([ + { cfi: "epubcfi(/6/8)", label: "Chapter 2", excerpt: "Shanghai harbor" }, + ]); + mocks.fetchEbookReaderConfig.mockResolvedValue({}); + mocks.saveEbookReaderConfig.mockResolvedValue({}); + mocks.fetchEbookReaderAnnotations.mockResolvedValue([]); + mocks.createEbookReaderAnnotation.mockResolvedValue({ + id: "ann-2", + content_id: "ebook-1", + kind: "highlight", + cfi_range: "epubcfi(/6/4,/1:0,/1:12)", + selected_text: "sample text", + note: "", + style: "highlight", + color: "#facc15", + }); + mocks.deleteEbookReaderAnnotation.mockResolvedValue(undefined); + localStorage.clear(); + mocks.useCatalogItemDetail.mockReturnValue({ + data: makeEbookItem(), + isLoading: false, + error: null, + }); + }); + + afterEach(async () => { + vi.useRealTimers(); + await act(async () => { + root.unmount(); + }); + container.remove(); + }); + + it("shows reader progress and wires page navigation controls", async () => { + await act(async () => { + root.render( + + + } /> + + , + ); + }); + + expect(container.textContent).toContain("42%"); + + const previous = container.querySelector( + 'button[aria-label="Previous page"]', + ); + const next = container.querySelector('button[aria-label="Next page"]'); + expect(previous).not.toBeNull(); + expect(next).not.toBeNull(); + + await act(async () => { + previous?.click(); + next?.click(); + }); + + expect(mocks.readerPrev).toHaveBeenCalledTimes(1); + expect(mocks.readerNext).toHaveBeenCalledTimes(1); + }); + + it("preserves library context on the back-to-ebook link", async () => { + await act(async () => { + root.render( + + + } /> + + , + ); + }); + + expect(container.innerHTML).toContain('href="/item/ebook-1?libraryId=12"'); + }); + + it("switches between multiple ebook files from the reader header", async () => { + mocks.useCatalogItemDetail.mockReturnValue({ + data: makeEbookItem({ + versions: [ + makeVersion({ file_id: 8, file_name: "Reader.epub", container: "epub" }), + makeVersion({ file_id: 9, file_name: "Reader.pdf", container: "pdf" }), + ], + }), + isLoading: false, + error: null, + }); + + await act(async () => { + root.render( + + + } /> + + , + ); + }); + + expect(container.textContent).toContain("reader surface Reader.epub"); + const select = container.querySelector('select[aria-label="Reader file"]'); + expect(select).not.toBeNull(); + + await act(async () => { + if (!select) return; + select.value = "9"; + select.dispatchEvent(new Event("change", { bubbles: true })); + }); + + expect(container.textContent).toContain("reader surface Reader.pdf"); + }); + + it("only lists reader-supported files in the reader file selector", async () => { + mocks.useCatalogItemDetail.mockReturnValue({ + data: makeEbookItem({ + versions: [ + makeVersion({ file_id: 8, file_name: "Reader.epub", container: "epub" }), + makeVersion({ file_id: 9, file_name: "Reader.docx", container: "docx" }), + makeVersion({ file_id: 10, file_name: "Reader.pdf", container: "pdf" }), + ], + }), + isLoading: false, + error: null, + }); + + await act(async () => { + root.render( + + + } /> + + , + ); + }); + + const options = Array.from(container.querySelectorAll("option")).map( + (option) => option.textContent, + ); + + expect(options).toEqual(["EPUB · Reader.epub", "PDF · Reader.pdf"]); + }); + + it("falls back to a supported reader file when the requested file is unsupported", async () => { + mocks.useCatalogItemDetail.mockReturnValue({ + data: makeEbookItem({ + versions: [ + makeVersion({ file_id: 8, file_name: "Reader.epub", container: "epub" }), + makeVersion({ file_id: 9, file_name: "Reader.docx", container: "docx" }), + ], + }), + isLoading: false, + error: null, + }); + + await act(async () => { + root.render( + + + } /> + + , + ); + }); + + expect(container.textContent).toContain("reader surface Reader.epub"); + expect(container.textContent).not.toContain("Unsupported ebook format."); + }); + + it("shows the table of contents and navigates to a selected section", async () => { + await act(async () => { + root.render( + + + } /> + + , + ); + }); + + expect(container.textContent).toContain("Opening"); + expect(container.textContent).toContain("Aboard"); + + const aboard = Array.from(container.querySelectorAll("button")).find( + (button) => button.textContent === "Aboard", + ); + + await act(async () => { + aboard?.click(); + }); + + expect(mocks.readerGoTo).toHaveBeenCalledWith("chapter-1.xhtml#aboard"); + }); + + it("searches inside the reader and navigates to a selected result", async () => { + await act(async () => { + root.render( + + + } /> + + , + ); + }); + + const searchTab = container.querySelector( + 'button[aria-label="Search book"]', + ); + await act(async () => { + searchTab?.click(); + }); + + const input = container.querySelector('input[aria-label="Search text"]'); + await act(async () => { + if (!input) return; + setInputValue(input, "Shanghai"); + }); + + const submit = container.querySelector('button[aria-label="Run search"]'); + await act(async () => { + submit?.click(); + }); + + expect(mocks.readerSearch).toHaveBeenCalledWith("Shanghai"); + expect(container.textContent).toContain("Shanghai harbor"); + + const result = Array.from(container.querySelectorAll("button")).find( + (button) => button.textContent?.includes("Shanghai harbor"), + ); + await act(async () => { + result?.click(); + }); + + expect(mocks.readerGoTo).toHaveBeenCalledWith("epubcfi(/6/8)"); + }); + + it("loads server reader settings and passes them to the reader", async () => { + mocks.fetchEbookReaderConfig.mockResolvedValue({ + settings: { theme: "sepia", fontSize: 130 }, + }); + + await act(async () => { + root.render( + + + } /> + + , + ); + }); + + await act(async () => { + await Promise.resolve(); + }); + + expect(mocks.fetchEbookReaderConfig).toHaveBeenCalledWith("ebook-1"); + expect(mocks.captureReaderSettings).toHaveBeenLastCalledWith( + expect.objectContaining({ theme: "sepia", fontSize: 130 }), + ); + }); + + it("persists reader settings to the server and local fallback", async () => { + vi.useFakeTimers(); + + await act(async () => { + root.render( + + + } /> + + , + ); + }); + + const settingsTab = container.querySelector( + 'button[aria-label="Reader settings"]', + ); + await act(async () => { + settingsTab?.click(); + }); + + const theme = container.querySelector('select[aria-label="Theme"]'); + await act(async () => { + if (!theme) return; + theme.value = "dark"; + theme.dispatchEvent(new Event("change", { bubbles: true })); + }); + + await act(async () => { + vi.advanceTimersByTime(450); + }); + + expect(mocks.captureReaderSettings).toHaveBeenLastCalledWith( + expect.objectContaining({ theme: "dark" }), + ); + expect(localStorage.getItem("silo.ebook.reader.settings")).toContain('"theme":"dark"'); + expect(mocks.saveEbookReaderConfig).toHaveBeenCalledWith( + "ebook-1", + expect.objectContaining({ + settings: expect.objectContaining({ theme: "dark" }), + }), + ); + + vi.useRealTimers(); + }); + + it("flushes a pending debounced settings save when unmounting inside the debounce window", async () => { + vi.useFakeTimers(); + + await act(async () => { + root.render( + + + } /> + + , + ); + }); + + const settingsTab = container.querySelector( + 'button[aria-label="Reader settings"]', + ); + await act(async () => { + settingsTab?.click(); + }); + + const theme = container.querySelector('select[aria-label="Theme"]'); + await act(async () => { + if (!theme) return; + theme.value = "dark"; + theme.dispatchEvent(new Event("change", { bubbles: true })); + }); + expect(mocks.saveEbookReaderConfig).not.toHaveBeenCalled(); + + // Unmount before the 400ms debounce elapses (quick SPA navigation). + await act(async () => { + root.unmount(); + }); + + expect(mocks.saveEbookReaderConfig).toHaveBeenCalledWith( + "ebook-1", + expect.objectContaining({ + settings: expect.objectContaining({ theme: "dark" }), + }), + ); + + vi.useRealTimers(); + }); + + it("flushes a pending debounced settings save with keepalive on pagehide, exactly once", async () => { + vi.useFakeTimers(); + + await act(async () => { + root.render( + + + } /> + + , + ); + }); + + const settingsTab = container.querySelector( + 'button[aria-label="Reader settings"]', + ); + await act(async () => { + settingsTab?.click(); + }); + + const theme = container.querySelector('select[aria-label="Theme"]'); + await act(async () => { + if (!theme) return; + theme.value = "sepia"; + theme.dispatchEvent(new Event("change", { bubbles: true })); + }); + + await act(async () => { + window.dispatchEvent(new Event("pagehide")); + }); + + expect(mocks.saveEbookReaderConfigKeepalive).toHaveBeenCalledTimes(1); + expect(mocks.saveEbookReaderConfigKeepalive).toHaveBeenCalledWith( + "ebook-1", + expect.objectContaining({ + settings: expect.objectContaining({ theme: "sepia" }), + }), + ); + + // The pending save was consumed: neither the debounce timer firing nor the + // unmount flush may send it again. + await act(async () => { + vi.advanceTimersByTime(450); + }); + await act(async () => { + root.unmount(); + }); + expect(mocks.saveEbookReaderConfig).not.toHaveBeenCalled(); + expect(mocks.saveEbookReaderConfigKeepalive).toHaveBeenCalledTimes(1); + + vi.useRealTimers(); + }); + + it("resets reader settings to stock defaults", async () => { + vi.useFakeTimers(); + mocks.fetchEbookReaderConfig.mockResolvedValue({ + settings: { theme: "dark", fontSize: 140, flow: "scrolled" }, + }); + + await act(async () => { + root.render( + + + } /> + + , + ); + }); + + await act(async () => { + await Promise.resolve(); + }); + + const settingsTab = container.querySelector( + 'button[aria-label="Reader settings"]', + ); + await act(async () => { + settingsTab?.click(); + }); + + const reset = container.querySelector( + 'button[aria-label="Reset reader settings"]', + ); + await act(async () => { + reset?.click(); + vi.advanceTimersByTime(450); + }); + + expect(mocks.captureReaderSettings).toHaveBeenLastCalledWith( + expect.objectContaining({ theme: "light", fontSize: 112, flow: "paginated" }), + ); + expect(mocks.saveEbookReaderConfig).toHaveBeenCalledWith( + "ebook-1", + expect.objectContaining({ + settings: expect.objectContaining({ theme: "light", fontSize: 112, flow: "paginated" }), + }), + ); + expect(localStorage.getItem("silo.ebook.reader.settings")).toContain('"theme":"light"'); + + vi.useRealTimers(); + }); + + it("offers reliable font choices and reading profile presets", async () => { + await act(async () => { + root.render( + + + } /> + + , + ); + }); + + const settingsTab = container.querySelector( + 'button[aria-label="Reader settings"]', + ); + await act(async () => { + settingsTab?.click(); + }); + + const font = container.querySelector('select[aria-label="Font family"]'); + const fontOptions = Array.from(font?.options ?? []).map((option) => option.textContent ?? ""); + expect(fontOptions).toContain("Book default"); + expect(fontOptions).toContain("System serif"); + expect(fontOptions).toContain("System sans"); + expect(fontOptions).not.toContain("Inter"); + expect(fontOptions).not.toContain("Merriweather"); + + const comfortable = Array.from(container.querySelectorAll("button")).find((button) => + button.textContent?.includes("Comfortable"), + ); + await act(async () => { + comfortable?.click(); + }); + + expect(mocks.captureReaderSettings).toHaveBeenLastCalledWith( + expect.objectContaining({ + fontFamily: 'ui-serif, Georgia, Cambria, "Times New Roman", Times, serif', + fontSize: 112, + lineHeight: 1.75, + }), + ); + expect(comfortable?.getAttribute("aria-pressed")).toBe("true"); + }); + + it("toggles the persisted reading ruler overlay", async () => { + await act(async () => { + root.render( + + + } /> + + , + ); + }); + + const ruler = container.querySelector( + 'button[aria-label="Toggle reading ruler"]', + ); + await act(async () => { + ruler?.click(); + }); + + expect(mocks.captureReaderSettings).toHaveBeenLastCalledWith( + expect.objectContaining({ readingRuler: true }), + ); + const handle = container.querySelector('[role="slider"][aria-label="Reading ruler position"]'); + expect(handle).not.toBeNull(); + expect(handle?.getAttribute("aria-valuenow")).toBe("50"); + }); + + it("constrains the reader grid so the side panel stays inside the viewport", async () => { + await act(async () => { + root.render( + + + } /> + + , + ); + }); + + const main = container.querySelector("main"); + const readerPane = main?.querySelector("section"); + const sidePanel = main?.querySelector("aside"); + + expect(main?.className).toContain("overflow-hidden"); + expect(readerPane?.className).toContain("min-w-0"); + expect(sidePanel?.className).toContain("min-w-0"); + }); + + it("scrubs reader progress and supports keyboard page navigation", async () => { + await act(async () => { + root.render( + + + } /> + + , + ); + }); + + const scrubber = container.querySelector( + 'input[aria-label="Reading progress"]', + ); + await act(async () => { + if (!scrubber) return; + setInputValue(scrubber, "65"); + }); + + expect(mocks.readerGoToFraction).toHaveBeenCalledWith(0.65); + + await act(async () => { + window.dispatchEvent(new KeyboardEvent("keydown", { key: "ArrowLeft" })); + window.dispatchEvent(new KeyboardEvent("keydown", { key: "ArrowRight" })); + }); + + expect(mocks.readerPrev).toHaveBeenCalledTimes(1); + expect(mocks.readerNext).toHaveBeenCalledTimes(1); + }); + + it("loads annotations, creates highlights, and deletes annotations", async () => { + mocks.fetchEbookReaderAnnotations.mockResolvedValue([ + { + id: "ann-1", + content_id: "ebook-1", + kind: "bookmark", + location: "epubcfi(/6/8)", + selected_text: "", + note: "Saved spot", + style: "highlight", + color: "#facc15", + }, + ]); + + await act(async () => { + root.render( + + + } /> + + , + ); + }); + + await act(async () => { + await Promise.resolve(); + }); + + const notesTab = container.querySelector( + 'button[aria-label="Annotations and bookmarks"]', + ); + await act(async () => { + notesTab?.click(); + }); + + expect(container.textContent).toContain("Saved spot"); + + const deleteButton = container.querySelector( + 'button[aria-label="Delete annotation"]', + ); + await act(async () => { + deleteButton?.click(); + }); + + expect(mocks.deleteEbookReaderAnnotation).toHaveBeenCalledWith("ebook-1", "ann-1"); + + const highlight = container.querySelector( + 'button[aria-label="Highlight selection"]', + ); + await act(async () => { + highlight?.click(); + }); + + expect(mocks.createEbookReaderAnnotation).toHaveBeenCalledWith( + "ebook-1", + expect.objectContaining({ + kind: "highlight", + cfi_range: "epubcfi(/6/4,/1:0,/1:12)", + selected_text: "sample text", + }), + ); + }); + + it("navigates fraction bookmarks through goToFraction and CFI annotations through goTo", async () => { + mocks.fetchEbookReaderAnnotations.mockResolvedValue([ + { + id: "ann-frac", + content_id: "ebook-1", + kind: "bookmark", + location: "fraction:0.250000", + selected_text: "", + note: "Fraction bookmark", + style: "", + color: "", + }, + { + id: "ann-cfi", + content_id: "ebook-1", + kind: "highlight", + cfi_range: "epubcfi(/6/12)", + selected_text: "CFI highlight", + note: "", + style: "highlight", + color: "#facc15", + }, + ]); + + await act(async () => { + root.render( + + + } /> + + , + ); + }); + + const notesTab = container.querySelector( + 'button[aria-label="Annotations and bookmarks"]', + ); + await act(async () => { + notesTab?.click(); + }); + + const buttons = Array.from(container.querySelectorAll("button")); + const fractionBookmark = buttons.find((button) => + button.textContent?.includes("Fraction bookmark"), + ); + await act(async () => { + fractionBookmark?.click(); + }); + + expect(mocks.readerGoToFraction).toHaveBeenCalledWith(0.25); + expect(mocks.readerGoTo).not.toHaveBeenCalled(); + + const cfiHighlight = buttons.find((button) => button.textContent?.includes("CFI highlight")); + await act(async () => { + cfiHighlight?.click(); + }); + + expect(mocks.readerGoTo).toHaveBeenCalledWith("epubcfi(/6/12)"); + }); + + it("keeps settings changed before the server config arrives and persists them", async () => { + let resolveConfig!: (config: Record) => void; + mocks.fetchEbookReaderConfig.mockReturnValue( + new Promise>((resolve) => { + resolveConfig = resolve; + }), + ); + + await act(async () => { + root.render( + + + } /> + + , + ); + }); + + const settingsTab = container.querySelector( + 'button[aria-label="Reader settings"]', + ); + await act(async () => { + settingsTab?.click(); + }); + + const theme = container.querySelector('select[aria-label="Theme"]'); + await act(async () => { + if (!theme) return; + theme.value = "dark"; + theme.dispatchEvent(new Event("change", { bubbles: true })); + }); + + await act(async () => { + resolveConfig({ settings: { theme: "sepia", fontSize: 130 } }); + }); + + // The late server config must not clobber the user's in-flight change... + expect(mocks.captureReaderSettings).toHaveBeenLastCalledWith( + expect.objectContaining({ theme: "dark" }), + ); + // ...and the user's settings win on the server too. + expect(mocks.saveEbookReaderConfig).toHaveBeenCalledWith( + "ebook-1", + expect.objectContaining({ + settings: expect.objectContaining({ theme: "dark" }), + }), + ); + }); + + it("renders search results with duplicate CFIs without key collisions", async () => { + mocks.readerSearch.mockResolvedValue([ + { cfi: "epubcfi(/6/8)", excerpt: "first match" }, + { cfi: "epubcfi(/6/8)", excerpt: "second match" }, + ]); + const consoleError = vi.spyOn(console, "error").mockImplementation(() => {}); + + try { + await act(async () => { + root.render( + + + } /> + + , + ); + }); + + const searchTab = container.querySelector( + 'button[aria-label="Search book"]', + ); + await act(async () => { + searchTab?.click(); + }); + + const input = container.querySelector('input[aria-label="Search text"]'); + await act(async () => { + if (!input) return; + setInputValue(input, "match"); + }); + + const submit = container.querySelector('button[aria-label="Run search"]'); + await act(async () => { + submit?.click(); + }); + + expect(container.textContent).toContain("first match"); + expect(container.textContent).toContain("second match"); + const keyWarnings = consoleError.mock.calls.filter((call) => + String(call[0]).includes("same key"), + ); + expect(keyWarnings).toEqual([]); + } finally { + consoleError.mockRestore(); + } + }); + + it("shows read aloud and reading aid controls", async () => { + await act(async () => { + root.render( + + + } /> + + , + ); + }); + + const settingsTab = container.querySelector( + 'button[aria-label="Reader settings"]', + ); + await act(async () => { + settingsTab?.click(); + }); + + expect(container.querySelector('button[aria-label="Speak text"]')).not.toBeNull(); + expect(container.querySelector('input[aria-label="Keep screen awake"]')).not.toBeNull(); + expect(container.querySelector('input[aria-label="E-ink mode"]')).toBeNull(); + }); + + it("shows useful advanced reader controls without diagnostics UI or no-op controls", async () => { + vi.useFakeTimers(); + + await act(async () => { + root.render( + + + } /> + + , + ); + }); + + const settingsTab = container.querySelector( + 'button[aria-label="Reader settings"]', + ); + await act(async () => { + settingsTab?.click(); + }); + + expect(container.querySelector('[aria-label="Diagnostics"]')).toBeNull(); + expect(container.textContent).not.toContain("Diagnostics"); + + const brightness = container.querySelector('input[aria-label="Brightness"]'); + const hyphenation = container.querySelector( + 'input[aria-label="Hyphenation"]', + ); + const rtl = container.querySelector('input[aria-label="Right to left"]'); + const writingMode = container.querySelector( + 'select[aria-label="Writing mode"]', + ); + + expect(brightness).not.toBeNull(); + expect(container.querySelector('input[aria-label="Zoom"]')).toBeNull(); + expect(hyphenation).not.toBeNull(); + expect(rtl).not.toBeNull(); + expect(writingMode).not.toBeNull(); + + await act(async () => { + if (!brightness || !hyphenation || !rtl || !writingMode) return; + setInputValue(brightness, "112"); + hyphenation.click(); + rtl.click(); + writingMode.value = "vertical-rl"; + writingMode.dispatchEvent(new Event("change", { bubbles: true })); + vi.advanceTimersByTime(450); + }); + + expect(mocks.captureReaderSettings).toHaveBeenLastCalledWith( + expect.objectContaining({ + fontBrightness: 112, + hyphenation: false, + rtl: true, + writingMode: "vertical-rl", + }), + ); + + vi.useRealTimers(); + }); + + it("keeps side panel tab labels visible in the narrow panel", async () => { + await act(async () => { + root.render( + + + } /> + + , + ); + }); + + const settingsTab = container.querySelector( + 'button[aria-label="Reader settings"]', + ); + const label = settingsTab?.querySelector("[data-reader-panel-tab-label]"); + + expect(settingsTab?.className).toContain("flex-col"); + expect(label?.textContent).toBe("Settings"); + expect(label?.className).toContain("whitespace-normal"); + }); + + it("keeps range labels readable in the settings panel", async () => { + await act(async () => { + root.render( + + + } /> + + , + ); + }); + + const settingsTab = container.querySelector( + 'button[aria-label="Reader settings"]', + ); + await act(async () => { + settingsTab?.click(); + }); + + const brightness = container.querySelector('input[aria-label="Brightness"]'); + const label = brightness?.closest("label"); + const header = label?.querySelector("[data-reader-range-header]"); + const name = label?.querySelector("[data-reader-range-name]"); + const value = label?.querySelector("[data-reader-range-value]"); + + expect(header?.className).toContain("grid"); + expect(name?.className).toContain("break-words"); + expect(value?.className).toContain("justify-self-end"); + }); + + it("hides paginated-only controls in scrolled flow", async () => { + await act(async () => { + root.render( + + + } /> + + , + ); + }); + + const settingsTab = container.querySelector( + 'button[aria-label="Reader settings"]', + ); + await act(async () => { + settingsTab?.click(); + }); + + expect(container.querySelector('input[aria-label="Width"]')).not.toBeNull(); + expect(container.querySelector('select[aria-label="Spread"]')).not.toBeNull(); + + const flow = container.querySelector('select[aria-label="Flow"]'); + await act(async () => { + if (!flow) return; + flow.value = "scrolled"; + flow.dispatchEvent(new Event("change", { bubbles: true })); + }); + + expect(container.querySelector('input[aria-label="Width"]')).toBeNull(); + expect(container.querySelector('select[aria-label="Spread"]')).toBeNull(); + }); +}); diff --git a/web/src/pages/EbookReader.tsx b/web/src/pages/EbookReader.tsx new file mode 100644 index 00000000..c390eeae --- /dev/null +++ b/web/src/pages/EbookReader.tsx @@ -0,0 +1,1211 @@ +import { + useCallback, + useEffect, + useMemo, + useRef, + useState, + type KeyboardEvent as ReactKeyboardEvent, + type PointerEvent, +} from "react"; +import { + ArrowLeft, + Bookmark, + BookOpen, + Check, + ChevronLeft, + ChevronRight, + Download, + GripHorizontal, + Highlighter, + Library, + ListTree, + Loader2, + PanelRightClose, + PanelRightOpen, + Pause, + Play, + RotateCcw, + Ruler, + Search, + Settings, + StickyNote, + Square, + Trash2, + Type, + Volume2, +} from "lucide-react"; +import { Link, useNavigate, useParams, useSearchParams } from "react-router"; + +import type { FileVersion } from "@/api/types"; +import PageBack from "@/components/PageBack"; +import { Button } from "@/components/ui/button"; +import { useScreenWakeLock } from "@/hooks/useScreenWakeLock"; +import { useTTS } from "@/hooks/useTTS"; +import { useCatalogItemDetail } from "@/hooks/queries/catalogRead"; +import { cn } from "@/lib/utils"; +import type { TOCItem } from "@/reader/readest/libs/document"; +import FoliateBookReader, { + DEFAULT_READER_SETTINGS, + READER_FONT_STACKS, + formatReaderProgress, + isReaderSupportedFile, + normalizeReaderSettings, + parseReaderLocation, + readerFileFormat, + type FoliateBookReaderHandle, + type ReaderLoadState, + type ReaderSearchResult, + type ReaderSelection, + type ReaderSettings, +} from "@/reader/FoliateBookReader"; +import { + createEbookReaderAnnotation, + deleteEbookReaderAnnotation, + fetchEbookReaderAnnotations, + fetchEbookReaderConfig, + saveEbookReaderConfig, + saveEbookReaderConfigKeepalive, + type EbookReaderAnnotation, +} from "@/reader/ebookReaderApi"; + +export const EBOOK_READER_SETTINGS_STORAGE_KEY = "silo.ebook.reader.settings"; + +type ReaderPanel = "toc" | "search" | "notes" | "settings"; + +type TocEntry = TOCItem & { + depth: number; +}; + +const READER_FONT_OPTIONS = [ + { label: "Book default", value: READER_FONT_STACKS.inherit }, + { label: "System serif", value: READER_FONT_STACKS.serif }, + { label: "System sans", value: READER_FONT_STACKS.sans }, + { label: "Monospace", value: READER_FONT_STACKS.mono }, +] as const; + +const READER_PROFILES = [ + { + id: "comfortable", + label: "Comfortable", + description: "Serif, roomier lines", + settings: { + fontFamily: READER_FONT_STACKS.serif, + fontSize: 112, + lineHeight: 1.75, + margin: 28, + }, + }, + { + id: "accessible", + label: "Accessible", + description: "Larger sans text", + settings: { + fontFamily: READER_FONT_STACKS.sans, + fontSize: 126, + lineHeight: 1.9, + margin: 32, + }, + }, + { + id: "compact", + label: "Compact", + description: "More words per page", + settings: { + fontFamily: READER_FONT_STACKS.inherit, + fontSize: 96, + lineHeight: 1.5, + margin: 16, + }, + }, +] as const; + +function profileIsActive(profile: (typeof READER_PROFILES)[number], settings: ReaderSettings) { + return Object.entries(profile.settings).every( + ([key, value]) => settings[key as keyof ReaderSettings] === value, + ); +} + +// Approximates one rendered text line: the renderer sizes type at fontSize% of the +// 16px browser base, but publisher CSS may override sizes per element, so the band +// is a reading guide rather than an exact line match. +function rulerBandHeight(settings: ReaderSettings): number { + const approxLinePx = 16 * (settings.fontSize / 100) * settings.lineHeight; + return Math.min(96, Math.max(28, Math.round(approxLinePx) + 6)); +} + +function clampRulerTop(value: number): number { + return Math.min(100, Math.max(0, value)); +} + +function chooseReaderFile( + files: FileVersion[], + requestedID: number | null, +): FileVersion | undefined { + const requested = requestedID ? files.find((file) => file.file_id === requestedID) : undefined; + if (requested && isReaderSupportedFile(requested)) return requested; + return ( + files.find((file) => readerFileFormat(file) === "epub") ?? + files.find((file) => isReaderSupportedFile(file)) ?? + files[0] + ); +} + +function readerFileLabel(file: FileVersion): string { + const format = readerFileFormat(file).toUpperCase(); + const name = file.file_name || file.file_path?.split(/[\\/]/).pop() || `File ${file.file_id}`; + return format ? `${format} · ${name}` : name; +} + +function flattenToc(items: TOCItem[] = [], depth = 0): TocEntry[] { + return items.flatMap((item) => [ + { ...item, depth }, + ...flattenToc(item.subitems ?? [], depth + 1), + ]); +} + +function readerStorage(): Storage | null { + try { + return window.localStorage ?? null; + } catch { + return null; + } +} + +export function loadStoredReaderSettings(): ReaderSettings { + try { + const raw = readerStorage()?.getItem(EBOOK_READER_SETTINGS_STORAGE_KEY); + if (!raw) return DEFAULT_READER_SETTINGS; + return normalizeReaderSettings(JSON.parse(raw) as Partial); + } catch { + return DEFAULT_READER_SETTINGS; + } +} + +function saveReaderSettings(settings: ReaderSettings) { + readerStorage()?.setItem(EBOOK_READER_SETTINGS_STORAGE_KEY, JSON.stringify(settings)); +} + +function isEditableTarget(target: EventTarget | null): boolean { + if (!(target instanceof HTMLElement)) return false; + if (target.isContentEditable) return true; + return ["INPUT", "TEXTAREA", "SELECT"].includes(target.tagName); +} + +export default function EbookReader() { + const { contentId = "" } = useParams<{ contentId: string }>(); + const navigate = useNavigate(); + const [searchParams] = useSearchParams(); + const requestedFileID = Number(searchParams.get("file_id") || ""); + const libraryIdParam = searchParams.get("libraryId"); + const { data: item, isLoading, error } = useCatalogItemDetail(contentId || undefined); + const selectedFile = useMemo( + () => + chooseReaderFile( + item?.versions ?? [], + Number.isFinite(requestedFileID) ? requestedFileID : null, + ), + [item?.versions, requestedFileID], + ); + const readerFiles = useMemo( + () => (item?.versions ?? []).filter((file) => isReaderSupportedFile(file)), + [item?.versions], + ); + const format = readerFileFormat(selectedFile); + const readerRef = useRef(null); + const [loadedFile, setLoadedFile] = useState(null); + const [readerProgress, setReaderProgress] = useState(null); + const [toc, setToc] = useState([]); + const [panelOpen, setPanelOpen] = useState(true); + const [panel, setPanel] = useState("toc"); + const [readerSettings, setReaderSettings] = useState(() => + loadStoredReaderSettings(), + ); + const [annotations, setAnnotations] = useState([]); + const [selection, setSelection] = useState(null); + const [wakeLockEnabled, setWakeLockEnabled] = useState(false); + const [ttsRate, setTtsRate] = useState(1); + const [ttsVoiceURI, setTtsVoiceURI] = useState(""); + const rulerDragRef = useRef<{ offsetY: number; surface: DOMRect; top: number } | null>(null); + const [rulerDragTop, setRulerDragTop] = useState(null); + const readingSurfaceRef = useRef(null); + const tts = useTTS(); + useScreenWakeLock(wakeLockEnabled); + const configLoadedRef = useRef(false); + // Tracks settings the user changed in this session so a slow server config + // fetch cannot clobber them after the fact. + const settingsDirtyRef = useRef(false); + // Mirrors readerSettings synchronously so updates merge correctly even when + // several setting changes land in the same render batch. + const readerSettingsRef = useRef(readerSettings); + const saveConfigTimerRef = useRef(null); + const [searchText, setSearchText] = useState(""); + const [searchResults, setSearchResults] = useState([]); + const [searching, setSearching] = useState(false); + const progressLabel = formatReaderProgress(readerProgress); + const tocEntries = useMemo(() => flattenToc(toc), [toc]); + const handleFileLoaded = useCallback((state: ReaderLoadState | null) => { + setLoadedFile(state); + }, []); + const handleProgressChange = useCallback((progress: number | null) => { + setReaderProgress(progress); + }, []); + const handleReaderReady = useCallback(({ toc: readyToc }: { toc: TOCItem[] }) => { + setToc(readyToc); + }, []); + const reloadAnnotations = useCallback(async () => { + if (!contentId) return; + setAnnotations(await fetchEbookReaderAnnotations(contentId)); + }, [contentId]); + const handleFileChange = useCallback( + (fileID: string) => { + if (!contentId) return; + const nextParams = new URLSearchParams(); + nextParams.set("file_id", fileID); + if (libraryIdParam) { + nextParams.set("libraryId", libraryIdParam); + } + navigate(`/reader/ebook/${encodeURIComponent(contentId)}?${nextParams.toString()}`, { + replace: true, + }); + }, + [contentId, libraryIdParam, navigate], + ); + const updateReaderSettings = useCallback( + (next: Partial) => { + const merged = normalizeReaderSettings({ ...readerSettingsRef.current, ...next }); + readerSettingsRef.current = merged; + settingsDirtyRef.current = true; + setReaderSettings(merged); + saveReaderSettings(merged); + if (contentId && configLoadedRef.current) { + if (saveConfigTimerRef.current !== null) { + window.clearTimeout(saveConfigTimerRef.current); + } + saveConfigTimerRef.current = window.setTimeout(() => { + saveConfigTimerRef.current = null; + void saveEbookReaderConfig(contentId, { settings: merged }); + }, 400); + } + }, + [contentId], + ); + const resetReaderSettings = useCallback(() => { + const defaults = normalizeReaderSettings(DEFAULT_READER_SETTINGS); + if (saveConfigTimerRef.current !== null) { + window.clearTimeout(saveConfigTimerRef.current); + saveConfigTimerRef.current = null; + } + readerSettingsRef.current = defaults; + settingsDirtyRef.current = true; + saveReaderSettings(defaults); + setReaderSettings(defaults); + if (contentId) { + void saveEbookReaderConfig(contentId, { settings: defaults }); + } + }, [contentId]); + const handleSearchSubmit = useCallback(async () => { + const query = searchText.trim(); + if (!query) { + setSearchResults([]); + readerRef.current?.clearSearch(); + return; + } + setSearching(true); + try { + setSearchResults(await (readerRef.current?.search(query) ?? Promise.resolve([]))); + } finally { + setSearching(false); + } + }, [searchText]); + const handleProgressScrub = useCallback((value: string) => { + const next = Math.min(1, Math.max(0, Number(value) / 100)); + if (!Number.isFinite(next)) return; + setReaderProgress(next); + void readerRef.current?.goToFraction(next); + }, []); + const handleCreateHighlight = useCallback(async () => { + if (!contentId || !selection) return; + const created = await createEbookReaderAnnotation(contentId, { + kind: "highlight", + cfi_range: selection.cfi, + selected_text: selection.selectedText, + style: "highlight", + color: "#facc15", + }); + setAnnotations((current) => [created, ...current]); + readerRef.current?.clearSelection(); + setSelection(null); + }, [contentId, selection]); + const handleCreateBookmark = useCallback(async () => { + if (!contentId) return; + const location = selection?.cfi || `fraction:${(readerProgress ?? 0).toFixed(6)}`; + const created = await createEbookReaderAnnotation(contentId, { + kind: "bookmark", + location, + note: item?.title || "Bookmark", + }); + setAnnotations((current) => [created, ...current]); + setPanel("notes"); + }, [contentId, item?.title, readerProgress, selection]); + const handleAnnotationNavigate = useCallback((annotation: EbookReaderAnnotation) => { + // Toolbar bookmarks store synthetic "fraction:" locations that foliate's + // goTo cannot resolve; route those through goToFraction instead. + const target = parseReaderLocation(annotation.cfi_range || annotation.location); + if (!target) return; + if (target.type === "fraction") { + void readerRef.current?.goToFraction(target.fraction); + } else { + readerRef.current?.goTo(target.location); + } + }, []); + const handleDeleteAnnotation = useCallback( + async (annotationID: string) => { + if (!contentId) return; + await deleteEbookReaderAnnotation(contentId, annotationID); + setAnnotations((current) => current.filter((annotation) => annotation.id !== annotationID)); + }, + [contentId], + ); + const handleSpeak = useCallback(() => { + const text = readerRef.current?.getReadableText() ?? ""; + tts.speak(text, { + rate: ttsRate, + voiceURI: ttsVoiceURI || undefined, + }); + }, [tts, ttsRate, ttsVoiceURI]); + // Dragging only moves a local draft so the book renderer and persistence layers + // are untouched until the pointer is released. + const effectiveRulerTop = rulerDragTop ?? readerSettings.readingRulerTop; + const handleRulerPointerDown = useCallback( + (event: PointerEvent) => { + const surface = readingSurfaceRef.current?.getBoundingClientRect(); + if (!surface || surface.height === 0) return; + event.preventDefault(); + const top = readerSettings.readingRulerTop; + const bandCenterY = surface.top + (surface.height * top) / 100; + rulerDragRef.current = { offsetY: event.clientY - bandCenterY, surface, top }; + event.currentTarget.setPointerCapture(event.pointerId); + }, + [readerSettings.readingRulerTop], + ); + const handleRulerPointerMove = useCallback((event: PointerEvent) => { + const drag = rulerDragRef.current; + if (!drag) return; + const next = clampRulerTop( + ((event.clientY - drag.surface.top - drag.offsetY) / drag.surface.height) * 100, + ); + drag.top = next; + setRulerDragTop(next); + }, []); + const handleRulerPointerUp = useCallback( + (event: PointerEvent) => { + const drag = rulerDragRef.current; + if (!drag) return; + rulerDragRef.current = null; + event.currentTarget.releasePointerCapture(event.pointerId); + setRulerDragTop(null); + updateReaderSettings({ readingRulerTop: drag.top }); + }, + [updateReaderSettings], + ); + const handleRulerPointerCancel = useCallback(() => { + rulerDragRef.current = null; + setRulerDragTop(null); + }, []); + const handleRulerKeyDown = useCallback( + (event: ReactKeyboardEvent) => { + if (event.key !== "ArrowUp" && event.key !== "ArrowDown") return; + event.preventDefault(); + const step = (event.shiftKey ? 5 : 1) * (event.key === "ArrowUp" ? -1 : 1); + updateReaderSettings({ + readingRulerTop: clampRulerTop(readerSettings.readingRulerTop + step), + }); + }, + [readerSettings.readingRulerTop, updateReaderSettings], + ); + + useEffect(() => { + const handleKeyDown = (event: KeyboardEvent) => { + if (event.defaultPrevented || isEditableTarget(event.target)) return; + if (event.key === "ArrowLeft") { + readerRef.current?.prev(); + } else if (event.key === "ArrowRight") { + readerRef.current?.next(); + } + }; + window.addEventListener("keydown", handleKeyDown); + return () => window.removeEventListener("keydown", handleKeyDown); + }, []); + + useEffect(() => { + if (!contentId) return; + let cancelled = false; + configLoadedRef.current = false; + settingsDirtyRef.current = false; + void fetchEbookReaderConfig(contentId) + .then((config) => { + if (cancelled) return; + configLoadedRef.current = true; + if (settingsDirtyRef.current) { + // The user already changed settings while the fetch was in flight; + // persist their choices instead of clobbering them with stale config. + void saveEbookReaderConfig(contentId, { settings: readerSettingsRef.current }); + return; + } + const settings = + config.settings && typeof config.settings === "object" && !Array.isArray(config.settings) + ? normalizeReaderSettings(config.settings as Partial) + : loadStoredReaderSettings(); + readerSettingsRef.current = settings; + saveReaderSettings(settings); + setReaderSettings(settings); + }) + .catch(() => { + if (!cancelled) { + configLoadedRef.current = true; + } + }); + // A scheduled timer means updateReaderSettings has unsaved settings; + // consume it exactly once so unmount and pagehide cannot double-send. + const flushPendingConfigSave = (options?: { keepalive?: boolean }) => { + if (saveConfigTimerRef.current === null) return; + window.clearTimeout(saveConfigTimerRef.current); + saveConfigTimerRef.current = null; + if (options?.keepalive) { + saveEbookReaderConfigKeepalive(contentId, { settings: readerSettingsRef.current }); + } else { + void saveEbookReaderConfig(contentId, { settings: readerSettingsRef.current }); + } + }; + // At tab close a normal request can be torn down with the page; keepalive + // lets the debounced save survive unload. + const flushConfigOnPageHide = () => flushPendingConfigSave({ keepalive: true }); + window.addEventListener("pagehide", flushConfigOnPageHide); + return () => { + cancelled = true; + window.removeEventListener("pagehide", flushConfigOnPageHide); + // Flush instead of dropping the debounced save: on quick SPA navigation a + // dropped save would let the stale server config revert the user's last + // change on next open. The page is still alive here, so a normal + // authenticated request is fine. + flushPendingConfigSave(); + }; + }, [contentId]); + + useEffect(() => { + void reloadAnnotations(); + }, [reloadAnnotations]); + if (isLoading) { + return ( +
+ +
+ ); + } + + if (error || !item || item.type !== "ebook") { + return ( +
+ +
Ebook not found.
+
+ ); + } + + const backHref = `/item/${encodeURIComponent(item.content_id)}${ + libraryIdParam ? `?libraryId=${encodeURIComponent(libraryIdParam)}` : "" + }`; + + if (!selectedFile) { + return ( +
+ +
No ebook files found.
+
+ ); + } + + return ( +
+
+
+ +
+
{item.title}
+
{format.toUpperCase()}
+
+ {progressLabel && ( +
+ {progressLabel} +
+ )} + {isReaderSupportedFile(selectedFile) && readerFiles.length > 1 && ( + + )} +
+ {selection && ( + + )} + + + + + +
+ {loadedFile && ( + + )} +
+
+ + handleProgressScrub(event.target.value)} + className="accent-primary h-2 min-w-0 flex-1" + /> +
+ {progressLabel ?? "0%"} +
+
+
+ +
+ {isReaderSupportedFile(selectedFile) ? ( +
+ + {readerSettings.readingRuler && ( +
+ +
+ )} +
+ ) : ( +
+
+ +

{item.title}

+

Unsupported ebook format.

+
+
+ )} + {panelOpen && isReaderSupportedFile(selectedFile) && ( + + )} +
+
+ ); +} + +type ReaderRangeProps = { + label: string; + value: number; + min: number; + max: number; + step: number; + suffix?: string; + onChange: (value: number) => void; +}; + +function ReaderRange({ label, value, min, max, step, suffix = "", onChange }: ReaderRangeProps) { + return ( + + ); +} diff --git a/web/src/pages/ItemDetail/EbookContent.test.tsx b/web/src/pages/ItemDetail/EbookContent.test.tsx new file mode 100644 index 00000000..88619f09 --- /dev/null +++ b/web/src/pages/ItemDetail/EbookContent.test.tsx @@ -0,0 +1,485 @@ +import type { ReactNode } from "react"; +import { renderToStaticMarkup } from "react-dom/server"; +import { describe, expect, it, vi, beforeEach } from "vitest"; +import { MemoryRouter } from "react-router"; +import type { FileVersion, ItemDetail } from "@/api/types"; +import EbookContent from "./EbookContent"; + +const mocks = vi.hoisted(() => ({ + useAuth: vi.fn(), + useEbookReaderProgress: vi.fn(), + useWatchedStateMutation: vi.fn(), +})); + +vi.mock("@/hooks/useAuth", () => ({ + useAuth: mocks.useAuth, +})); + +vi.mock("@/hooks/queries/items", () => ({ + useWatchedStateMutation: mocks.useWatchedStateMutation, +})); + +vi.mock("@/hooks/useAmbientColor", () => ({ + useAmbientColor: vi.fn(), +})); + +vi.mock("@/hooks/queries/ebookReaderProgress", () => ({ + useEbookReaderProgress: mocks.useEbookReaderProgress, +})); + +vi.mock("@/components/PageBack", () => ({ + default: () =>
, +})); + +vi.mock("@/components/MediaLocations", () => ({ + default: ({ + title, + versions, + emptyMessage, + summaryBuilder, + }: { + title: string; + versions: FileVersion[]; + emptyMessage: string; + summaryBuilder?: (version: FileVersion) => string; + }) => ( +
+ {title} + {versions.length} + {emptyMessage} + {versions.map((version) => ( + {summaryBuilder?.(version)} + ))} +
+ ), +})); + +vi.mock("@/components/DownloadVersionPicker", () => ({ + default: ({ versions, title }: { versions: FileVersion[]; title: string }) => ( +
+ download picker + {title} + {versions.length} +
+ ), +})); + +vi.mock("@/pages/audiobooks/components/RelatedRail", () => ({ + RelatedRail: ({ + heading, + items, + coverAspect, + }: { + heading: string; + coverAspect?: "square" | "poster"; + items: Array<{ content_id: string; title: string; subtitle?: string; highlight?: boolean }>; + }) => ( +
+

{heading}

+ {coverAspect && aspect:{coverAspect}} + {items.map((item) => ( +
+ {item.title} + {item.subtitle && {item.subtitle}} + {item.highlight && Current} +
+ ))} +
+ ), +})); + +vi.mock("./DetailHero", () => ({ + default: ({ + title, + context, + crewLine, + actions, + genres, + genreHref, + }: { + title: string; + context?: ReactNode; + crewLine?: ReactNode; + actions?: ReactNode; + genres?: string[]; + genreHref?: (genre: string) => string; + }) => ( +
+ {title} + {context} + {crewLine} + {genres?.map((genre) => ( + + {genre} + + ))} + {actions} +
+ ), +})); + +vi.mock("./components/MetadataBadges", () => ({ + default: () =>
, +})); + +vi.mock("./components/ScoreRow", () => ({ + default: () =>
, +})); + +function makeVersion(overrides: Partial = {}): FileVersion { + return { + file_id: overrides.file_id ?? 1, + file_path: overrides.file_path ?? "/books/book.epub", + resolution: overrides.resolution ?? "", + codec_video: overrides.codec_video ?? "", + codec_audio: overrides.codec_audio ?? "", + hdr: overrides.hdr ?? false, + container: overrides.container ?? "epub", + file_size: overrides.file_size ?? 1234, + duration: overrides.duration ?? 0, + bitrate: overrides.bitrate ?? 0, + file_name: overrides.file_name ?? "Book.epub", + audio_tracks: overrides.audio_tracks, + video_tracks: overrides.video_tracks, + subtitle_tracks: overrides.subtitle_tracks, + }; +} + +function makeEbookItem( + overrides: Partial = {}, +): ItemDetail & { type: "ebook" } { + return { + content_id: "ebook-1", + type: "ebook", + title: "A Psalm for the Wild-Built", + original_title: "", + year: 2021, + overview: "An ebook overview", + tagline: "", + runtime: 0, + content_rating: "", + genres: [], + rating_imdb: null, + rating_tmdb: null, + rating_rt_critic: null, + rating_rt_audience: null, + imdb_id: "", + tmdb_id: "", + tvdb_id: "", + cast: [], + crew: [ + { name: "Becky Chambers", job: "Author", person_id: "author-1" }, + { name: "A Narrator Should Not Appear", job: "Narrator", person_id: "narrator-1" }, + ], + studios: ["Tor"], + networks: [], + countries: [], + release_date: null, + first_air_date: null, + last_air_date: null, + poster_url: "", + poster_thumbhash: "", + backdrop_url: "", + backdrop_thumbhash: "", + logo_url: "", + season_count: null, + series_id: "", + series_title: "", + season_number: null, + episode_number: null, + episode_count: null, + air_date: null, + is_specials: false, + versions: [makeVersion()], + subtitles: [], + intro: null, + credits: null, + ...overrides, + }; +} + +describe("EbookContent", () => { + beforeEach(() => { + mocks.useAuth.mockReset(); + mocks.useAuth.mockReturnValue({ user: { download_allowed: true } }); + mocks.useEbookReaderProgress.mockReset(); + mocks.useEbookReaderProgress.mockReturnValue({ data: null }); + mocks.useWatchedStateMutation.mockReset(); + mocks.useWatchedStateMutation.mockReturnValue({ mutate: vi.fn(), isPending: false }); + }); + + it("renders ebook authors without audiobook narrator credits", () => { + const markup = renderToStaticMarkup( + + + , + ); + + expect(markup).toContain("Ebook"); + expect(markup).toContain("By"); + expect(markup).toContain("Becky Chambers"); + expect(markup).not.toContain("A Narrator Should Not Appear"); + }); + + it("only shows download action when downloads are allowed and files exist", () => { + let markup = renderToStaticMarkup( + + + , + ); + expect(markup).toContain("Download"); + + mocks.useAuth.mockReturnValue({ user: { download_allowed: false } }); + markup = renderToStaticMarkup( + + + , + ); + expect(markup).not.toContain("Download"); + + mocks.useAuth.mockReturnValue({ user: { download_allowed: true } }); + markup = renderToStaticMarkup( + + + , + ); + expect(markup).not.toContain("Download"); + }); + + it("shows read action for ebook files even when downloads are disabled", () => { + mocks.useAuth.mockReturnValue({ user: { download_allowed: false } }); + + let markup = renderToStaticMarkup( + + + , + ); + + expect(markup).toContain("Read"); + expect(markup).toContain("/reader/ebook/ebook-1?file_id=1"); + + markup = renderToStaticMarkup( + + + , + ); + expect(markup).not.toContain("/reader/ebook/"); + }); + + it("shows the mark read toggle backed by the watched mutation", () => { + const item = makeEbookItem(); + const markup = renderToStaticMarkup( + + + , + ); + + expect(markup).toContain("Mark Read"); + expect(mocks.useWatchedStateMutation).toHaveBeenCalledWith(item); + }); + + it("shows the mark unread toggle for ebooks already marked read", () => { + const markup = renderToStaticMarkup( + + + , + ); + + expect(markup).toContain("Mark Unread"); + }); + + it("preserves library context on reader links", () => { + mocks.useAuth.mockReturnValue({ user: { download_allowed: false } }); + + const markup = renderToStaticMarkup( + + + , + ); + + expect(markup).toContain("/reader/ebook/ebook-1?file_id=1&libraryId=12"); + }); + + it("links ebook genres back to the scoped library", () => { + mocks.useAuth.mockReturnValue({ user: { download_allowed: false } }); + + const markup = renderToStaticMarkup( + + + , + ); + + expect(markup).toContain('href="/library/12?tab=library&genre=Science+Fiction"'); + }); + + it("links ebook genres to the ebook catalog outside a library", () => { + mocks.useAuth.mockReturnValue({ user: { download_allowed: false } }); + + const markup = renderToStaticMarkup( + + + , + ); + + expect(markup).toContain( + 'href="/catalog?source=query&type=ebook&genre=Science+Fiction"', + ); + }); + + it("does not show read action when ebook files are not reader-supported", () => { + mocks.useAuth.mockReturnValue({ user: { download_allowed: true } }); + + const markup = renderToStaticMarkup( + + + , + ); + + expect(markup).not.toContain("/reader/ebook/"); + expect(markup).toContain("Download"); + }); + + it("shows continue action and saved progress when ebook progress exists", () => { + mocks.useAuth.mockReturnValue({ user: { download_allowed: false } }); + mocks.useEbookReaderProgress.mockReturnValue({ + data: { + file_id: 1, + location: "epubcfi(/6/4)", + progress: 0.42, + }, + }); + + const markup = renderToStaticMarkup( + + + , + ); + + expect(markup).toContain("Continue"); + expect(markup).toContain("42%"); + expect(markup).not.toContain(">Read<"); + }); + + it("continues from the saved reader file when progress points at another format", () => { + mocks.useAuth.mockReturnValue({ user: { download_allowed: false } }); + mocks.useEbookReaderProgress.mockReturnValue({ + data: { + file_id: 1, + location: "pdf-location", + progress: 0.2, + }, + }); + + const markup = renderToStaticMarkup( + + + , + ); + + expect(markup).toContain("Continue"); + expect(markup).toContain("/reader/ebook/ebook-1?file_id=1"); + }); + + it("prefers EPUB for the read action when multiple ebook files exist", () => { + mocks.useAuth.mockReturnValue({ user: { download_allowed: false } }); + + const markup = renderToStaticMarkup( + + + , + ); + + expect(markup).toContain("/reader/ebook/ebook-1?file_id=2"); + }); + + it("renders ebook series and related rails from ebook detail extension", () => { + const markup = renderToStaticMarkup( + + + , + ); + + expect(markup).toContain("In Monk and Robot"); + expect(markup).toContain("A Prayer for the Crown-Shy"); + expect(markup).toContain("Book 2"); + expect(markup).toContain("Also by Becky Chambers"); + expect(markup).toContain("The Long Way"); + expect(markup).toContain("You might also like"); + expect(markup).toContain("All Systems Red"); + expect(markup).toContain("aspect:poster"); + }); + + it("shows ebook file format, size, and page count without video quality labels", () => { + const markup = renderToStaticMarkup( + + + , + ); + + expect(markup).toContain("CBZ · 25.0 MB · 48 pages"); + expect(markup).not.toContain("1080p"); + }); +}); diff --git a/web/src/pages/ItemDetail/EbookContent.tsx b/web/src/pages/ItemDetail/EbookContent.tsx new file mode 100644 index 00000000..138b0f89 --- /dev/null +++ b/web/src/pages/ItemDetail/EbookContent.tsx @@ -0,0 +1,246 @@ +import { useState } from "react"; +import { BookOpen, Check, Download } from "lucide-react"; +import { Link } from "react-router"; +import type { FileVersion, ItemDetail } from "@/api/types"; +import DownloadVersionPicker from "@/components/DownloadVersionPicker"; +import MediaLocations from "@/components/MediaLocations"; +import PageBack from "@/components/PageBack"; +import { Button } from "@/components/ui/button"; +import { useAuth } from "@/hooks/useAuth"; +import { useAmbientColor } from "@/hooks/useAmbientColor"; +import { useEbookReaderProgress } from "@/hooks/queries/ebookReaderProgress"; +import { useWatchedStateMutation } from "@/hooks/queries/items"; +import { RelatedRail } from "@/pages/audiobooks/components/RelatedRail"; +import { + formatReaderProgress, + isReaderSupportedFile, + readerFileFormat, +} from "@/reader/FoliateBookReader"; +import DetailHero from "./DetailHero"; +import MetadataBadges from "./components/MetadataBadges"; +import ScoreRow from "./components/ScoreRow"; +import { getWatchedActionLabel } from "./watchedState"; +import { formatFileSize, formatPageCount, metadataLine } from "./components/versionFormatUtils"; + +function authorNames(item: ItemDetail): string[] { + const extensionAuthors = (item.ebook?.authors ?? []) + .map((author) => author.name) + .filter((name) => name.trim() !== ""); + if (extensionAuthors.length > 0) { + return extensionAuthors; + } + return (item.crew ?? []) + .filter((credit) => credit.job === "Author") + .map((credit) => credit.name) + .filter((name) => name.trim() !== ""); +} + +function ebookVersionSummary(version: ItemDetail["versions"][number]): string { + return metadataLine([ + version.container ? version.container.toUpperCase() : undefined, + formatFileSize(version.file_size), + formatPageCount(version.duration), + ]); +} + +function preferredReadVersion(versions: FileVersion[]): FileVersion | undefined { + return ( + versions.find((version) => readerFileFormat(version) === "epub") ?? + versions.find((version) => isReaderSupportedFile(version)) + ); +} + +function progressReadVersion( + versions: FileVersion[], + fileID: number | undefined, +): FileVersion | undefined { + if (typeof fileID !== "number") return undefined; + return versions.find((version) => version.file_id === fileID && isReaderSupportedFile(version)); +} + +function genreHref(genre: string, libraryId?: number): string { + const params = new URLSearchParams(); + if (libraryId) { + params.set("tab", "library"); + params.set("genre", genre); + return `/library/${libraryId}?${params.toString()}`; + } + params.set("source", "query"); + params.set("type", "ebook"); + params.set("genre", genre); + return `/catalog?${params.toString()}`; +} + +export default function EbookContent({ + item, + libraryId, +}: { + item: ItemDetail & { type: "ebook" }; + libraryId?: number; +}) { + useAmbientColor(item.poster_thumbhash); + const { user } = useAuth(); + const { data: readerProgress } = useEbookReaderProgress(item.content_id); + const watchedMutation = useWatchedStateMutation(item); + const isRead = item.user_data?.played === true; + const [downloadOpen, setDownloadOpen] = useState(false); + const authors = authorNames(item); + const publisher = item.ebook?.publisher || (item.studios ?? [])[0]; + const year = item.year ? String(item.year) : ""; + const progressLabel = formatReaderProgress(readerProgress?.progress); + const hasSavedProgress = Boolean(readerProgress && progressLabel); + const readVersion = + (hasSavedProgress ? progressReadVersion(item.versions, readerProgress?.file_id) : undefined) ?? + preferredReadVersion(item.versions); + const canRead = Boolean(readVersion); + const canDownload = Boolean(user?.download_allowed && item.versions.length > 0); + const readerParams = new URLSearchParams(); + if (readVersion) { + readerParams.set("file_id", String(readVersion.file_id)); + } + if (libraryId) { + readerParams.set("libraryId", String(libraryId)); + } + const readerQuery = readerParams.toString(); + const readerHref = `/reader/ebook/${encodeURIComponent(item.content_id)}${ + readerQuery ? `?${readerQuery}` : "" + }`; + + return ( +
+ } + context="Ebook" + studioLabel={publisher} + backdropUrl={item.backdrop_url} + backdropThumbhash={item.backdrop_thumbhash} + posterUrl={item.poster_url} + posterThumbhash={item.poster_thumbhash} + metadata={ + + } + scoreRow={ + + } + overview={item.overview} + crewLine={ + authors.length > 0 ? ( +
+ By + {authors.join(", ")} +
+ ) : undefined + } + genres={item.genres} + genreHref={(genre) => genreHref(genre, libraryId)} + actions={ +
+ {canRead && ( + + )} + {canDownload && ( + + )} + +
+ } + /> + +
+ {item.ebook?.series && item.ebook.series.entries.length > 0 && ( + ({ + content_id: entry.content_id, + title: entry.title, + poster_url: entry.poster_url, + subtitle: + typeof entry.series_index === "number" ? `Book ${entry.series_index}` : undefined, + highlight: entry.content_id === item.content_id, + }))} + /> + )} + + {(item.ebook?.related.also_by_author ?? []).length > 0 && ( + ({ + content_id: entry.content_id, + title: entry.title, + poster_url: entry.poster_url, + subtitle: entry.year ? String(entry.year) : undefined, + }))} + /> + )} + + {(item.ebook?.related.similar ?? []).length > 0 && ( + ({ + content_id: entry.content_id, + title: entry.title, + poster_url: entry.poster_url, + subtitle: entry.year ? String(entry.year) : undefined, + }))} + /> + )} + + +
+ + +
+ ); +} diff --git a/web/src/pages/ItemDetail/components/VersionFlyout.test.ts b/web/src/pages/ItemDetail/components/VersionFlyout.test.ts index 77b3cb95..c706b69a 100644 --- a/web/src/pages/ItemDetail/components/VersionFlyout.test.ts +++ b/web/src/pages/ItemDetail/components/VersionFlyout.test.ts @@ -73,6 +73,19 @@ describe("buildQualitySummary", () => { }); expect(buildQualitySummary(version)).toBe("2160p · HEVC · HDR · Atmos"); }); + + it("falls back to container for ebook-style files without video quality", () => { + const version = makeVersion({ + resolution: "", + codec_video: "", + codec_audio: "", + hdr: false, + container: "epub", + file_name: "A Psalm for the Wild-Built.epub", + }); + + expect(buildQualitySummary(version)).toBe("EPUB"); + }); }); describe("buildDetailLine", () => { diff --git a/web/src/pages/ItemDetail/components/VersionFlyout.tsx b/web/src/pages/ItemDetail/components/VersionFlyout.tsx index 28d28536..09b195ca 100644 --- a/web/src/pages/ItemDetail/components/VersionFlyout.tsx +++ b/web/src/pages/ItemDetail/components/VersionFlyout.tsx @@ -19,6 +19,9 @@ export function buildQualitySummary(version: FileVersion): string { if (version.codec_video) parts.push(version.codec_video.toUpperCase()); if (version.hdr) parts.push("HDR"); if (version.codec_audio) parts.push(mapAudioLabel(version.codec_audio)); + if (parts.length === 0 && version.container) { + parts.push(version.container.toUpperCase()); + } return parts.join(" · "); } diff --git a/web/src/pages/ItemDetail/components/versionFormatUtils.test.ts b/web/src/pages/ItemDetail/components/versionFormatUtils.test.ts index 2ad734b8..b5949fa9 100644 --- a/web/src/pages/ItemDetail/components/versionFormatUtils.test.ts +++ b/web/src/pages/ItemDetail/components/versionFormatUtils.test.ts @@ -1,6 +1,7 @@ import { describe, expect, it } from "vitest"; import { formatFileSize, + formatPageCount, formatChannels, formatBitrate, formatLanguageName, @@ -27,6 +28,18 @@ describe("formatFileSize", () => { }); }); +describe("formatPageCount", () => { + it("formats singular and plural ebook page counts", () => { + expect(formatPageCount(1)).toBe("1 page"); + expect(formatPageCount(321)).toBe("321 pages"); + }); + + it("returns empty string for missing page counts", () => { + expect(formatPageCount(0)).toBe(""); + expect(formatPageCount(undefined)).toBe(""); + }); +}); + describe("formatChannels", () => { it("maps 8 channels to 7.1", () => { expect(formatChannels(8)).toBe("7.1"); diff --git a/web/src/pages/ItemDetail/components/versionFormatUtils.tsx b/web/src/pages/ItemDetail/components/versionFormatUtils.tsx index f82cb1fe..dfc51802 100644 --- a/web/src/pages/ItemDetail/components/versionFormatUtils.tsx +++ b/web/src/pages/ItemDetail/components/versionFormatUtils.tsx @@ -22,6 +22,11 @@ export function formatFileSize(bytes: number): string { return `${bytes} B`; } +export function formatPageCount(pages?: number): string { + if (!pages || pages <= 0) return ""; + return `${pages.toLocaleString()} ${pages === 1 ? "page" : "pages"}`; +} + export function formatAddedAt(value?: string): string { if (!value) return ""; const date = new Date(value); diff --git a/web/src/pages/ItemDetail/index.test.tsx b/web/src/pages/ItemDetail/index.test.tsx index fcab7dad..638998bd 100644 --- a/web/src/pages/ItemDetail/index.test.tsx +++ b/web/src/pages/ItemDetail/index.test.tsx @@ -37,6 +37,14 @@ vi.mock("@/pages/ItemDetail/EpisodeContent", () => ({ default: () =>
Episode
, })); +vi.mock("@/pages/ItemDetail/AudiobookContent", () => ({ + default: () =>
Audiobook
, +})); + +vi.mock("@/pages/ItemDetail/EbookContent", () => ({ + default: ({ item }: { item: { title: string } }) =>
Ebook: {item.title}
, +})); + import ItemDetail from "./index"; describe("ItemDetail", () => { @@ -56,4 +64,16 @@ describe("ItemDetail", () => { expect(markup).toContain("Catalog Detail"); expect(mocks.useCatalogItemDetail).toHaveBeenCalledWith("movie-123", undefined); }); + + it("routes ebook items to ebook detail content", () => { + mocks.useCatalogItemDetail.mockReturnValue({ + data: { content_id: "ebook-123", title: "A Psalm for the Wild-Built", type: "ebook" }, + isLoading: false, + error: null, + }); + + const markup = renderToStaticMarkup(); + + expect(markup).toContain("Ebook: A Psalm for the Wild-Built"); + }); }); diff --git a/web/src/pages/ItemDetail/index.tsx b/web/src/pages/ItemDetail/index.tsx index bb3131c5..48a1c773 100644 --- a/web/src/pages/ItemDetail/index.tsx +++ b/web/src/pages/ItemDetail/index.tsx @@ -10,6 +10,7 @@ import SeriesContent from "@/pages/ItemDetail/SeriesContent"; import SeasonContent from "@/pages/ItemDetail/SeasonContent"; import EpisodeContent from "@/pages/ItemDetail/EpisodeContent"; import AudiobookContent from "@/pages/ItemDetail/AudiobookContent"; +import EbookContent from "@/pages/ItemDetail/EbookContent"; import { CastSkeleton, CrewSkeleton, @@ -103,6 +104,8 @@ export default function ItemDetail() { return ( ); + case "ebook": + return ; case "podcast": return ; default: diff --git a/web/src/pages/ItemDetail/watchedState.test.ts b/web/src/pages/ItemDetail/watchedState.test.ts index a6674e5f..c216a542 100644 --- a/web/src/pages/ItemDetail/watchedState.test.ts +++ b/web/src/pages/ItemDetail/watchedState.test.ts @@ -1,11 +1,12 @@ import { QueryClient } from "@tanstack/react-query"; import { describe, expect, it } from "vitest"; -import { catalogKeys, episodeKeys, itemKeys, progressKeys } from "@/hooks/queries/keys"; +import { catalogKeys, ebookKeys, episodeKeys, itemKeys, progressKeys } from "@/hooks/queries/keys"; import type { ItemDetail } from "@/api/types"; import { getCachedWatchedInvalidationKeys, getWatchedActionLabel, getWatchedInvalidationKeys, + getWatchedToastMessage, } from "./watchedState"; function makeItem(overrides: Partial = {}): ItemDetail { @@ -89,6 +90,45 @@ describe("getWatchedActionLabel", () => { ), ).toBe("Mark Series Watched"); }); + + it("returns listening labels for audiobooks", () => { + expect( + getWatchedActionLabel(makeItem({ type: "audiobook", user_data: { played: false } })), + ).toBe("Mark Listened"); + expect( + getWatchedActionLabel(makeItem({ type: "audiobook", user_data: { played: true } })), + ).toBe("Mark Unlistened"); + }); + + it("returns reading labels for ebooks", () => { + expect(getWatchedActionLabel(makeItem({ type: "ebook", user_data: { played: false } }))).toBe( + "Mark Read", + ); + expect(getWatchedActionLabel(makeItem({ type: "ebook", user_data: { played: true } }))).toBe( + "Mark Unread", + ); + }); +}); + +describe("getWatchedToastMessage", () => { + it("uses watched copy for video items", () => { + expect(getWatchedToastMessage(makeItem({ type: "movie" }), true)).toBe("Marked as watched"); + expect(getWatchedToastMessage(makeItem({ type: "movie" }), false)).toBe("Marked as unwatched"); + }); + + it("uses listened copy for audiobooks", () => { + expect(getWatchedToastMessage(makeItem({ type: "audiobook" }), true)).toBe( + "Marked as listened", + ); + expect(getWatchedToastMessage(makeItem({ type: "audiobook" }), false)).toBe( + "Marked as unlistened", + ); + }); + + it("uses read copy for ebooks", () => { + expect(getWatchedToastMessage(makeItem({ type: "ebook" }), true)).toBe("Marked as read"); + expect(getWatchedToastMessage(makeItem({ type: "ebook" }), false)).toBe("Marked as unread"); + }); }); describe("getWatchedInvalidationKeys", () => { @@ -146,6 +186,22 @@ describe("getWatchedInvalidationKeys", () => { ]); }); + it("includes the ebook reader progress query for an ebook target", () => { + expect( + getWatchedInvalidationKeys( + makeItem({ + content_id: "ebook-1", + type: "ebook", + }), + ), + ).toEqual([ + catalogKeys.itemDetail("ebook-1"), + itemKeys.detail("ebook-1"), + progressKeys.all, + ebookKeys.readerProgress("ebook-1"), + ]); + }); + it("returns the expected related queries for a series target", () => { expect( getWatchedInvalidationKeys( diff --git a/web/src/pages/ItemDetail/watchedState.ts b/web/src/pages/ItemDetail/watchedState.ts index c47dac06..1ac9c132 100644 --- a/web/src/pages/ItemDetail/watchedState.ts +++ b/web/src/pages/ItemDetail/watchedState.ts @@ -1,6 +1,6 @@ import type { QueryClient } from "@tanstack/react-query"; import type { EpisodesResponse, ItemDetail, SeasonsResponse } from "@/api/types"; -import { catalogKeys, episodeKeys, itemKeys, progressKeys } from "@/hooks/queries/keys"; +import { catalogKeys, ebookKeys, episodeKeys, itemKeys, progressKeys } from "@/hooks/queries/keys"; type WatchedStateItem = Pick< ItemDetail, @@ -20,7 +20,7 @@ function appendUniqueKey(keys: Array, nextKey: readonly unkn keys.push(nextKey); } -export function getWatchedActionLabel(item: WatchedStateItem): string { +export function getWatchedActionLabel(item: Pick): string { const played = item.user_data?.played === true; switch (item.type) { @@ -28,6 +28,10 @@ export function getWatchedActionLabel(item: WatchedStateItem): string { return played ? "Mark Series Unwatched" : "Mark Series Watched"; case "season": return played ? "Mark Season Unwatched" : "Mark Season Watched"; + case "audiobook": + return played ? "Mark Unlistened" : "Mark Listened"; + case "ebook": + return played ? "Mark Unread" : "Mark Read"; case "episode": return played ? "Mark Unwatched" : "Mark Watched"; default: @@ -35,6 +39,17 @@ export function getWatchedActionLabel(item: WatchedStateItem): string { } } +export function getWatchedToastMessage(item: Pick, played: boolean): string { + switch (item.type) { + case "audiobook": + return played ? "Marked as listened" : "Marked as unlistened"; + case "ebook": + return played ? "Marked as read" : "Marked as unread"; + default: + return played ? "Marked as watched" : "Marked as unwatched"; + } +} + export function getWatchedInvalidationKeys(item: WatchedStateItem) { const keys: Array = [ catalogKeys.itemDetail(item.content_id), @@ -74,6 +89,10 @@ export function getWatchedInvalidationKeys(item: WatchedStateItem) { keys.push(episodeKeys.byItem(item.content_id)); } + if (item.type === "ebook") { + keys.push(ebookKeys.readerProgress(item.content_id)); + } + return keys; } diff --git a/web/src/pages/LibraryBrowse.test.tsx b/web/src/pages/LibraryBrowse.test.tsx index 670c68ff..d1ea151d 100644 --- a/web/src/pages/LibraryBrowse.test.tsx +++ b/web/src/pages/LibraryBrowse.test.tsx @@ -206,4 +206,51 @@ describe("LibraryBrowse", () => { expect(state.library_id).toBe(10); expect(state.query_definition.media_scope).toBe("audiobook"); }); + + it("uses ebook media scope for ebook libraries", () => { + renderToStaticMarkup( + {}} + onQueryDefinitionChange={() => {}} + />, + ); + + const [state] = mocks.useCatalogWindow.mock.calls[ + mocks.useCatalogWindow.mock.calls.length - 1 + ] as [CatalogSearchState, Record]; + expect(state.library_id).toBe(11); + expect(state.query_definition.media_scope).toBe("ebook"); + }); + + it("normalizes video-only sorts away through the shared ebook relevance scope", () => { + renderToStaticMarkup( + {}} + onQueryDefinitionChange={() => {}} + />, + ); + + const [state] = mocks.useCatalogWindow.mock.calls[ + mocks.useCatalogWindow.mock.calls.length - 1 + ] as [CatalogSearchState, Record]; + expect(state.query_definition.sort).toEqual({ field: "title", order: "asc" }); + }); }); diff --git a/web/src/pages/LibraryBrowse.tsx b/web/src/pages/LibraryBrowse.tsx index 7f720de8..90499ded 100644 --- a/web/src/pages/LibraryBrowse.tsx +++ b/web/src/pages/LibraryBrowse.tsx @@ -8,7 +8,7 @@ import ScrollToTopButton from "@/components/ScrollToTopButton"; import { useCatalogWindow } from "@/hooks/queries/catalog"; import type { AudiobookGroupBy } from "@/hooks/queries/audiobookGroups"; import { cn } from "@/lib/utils"; -import { normalizeQuerySortForScope, type QuerySortRelevanceScope } from "@/lib/querySortOptions"; +import { normalizeQuerySortForScope } from "@/lib/querySortOptions"; import type { CatalogSearchState } from "@/pages/catalogSearchParams"; import { Select, @@ -20,7 +20,9 @@ import { import { AUDIOBOOK_BROWSE_AXES, audiobookBrowseAxisFromBrowseType, + getLibrarySortRelevanceScope, isAudiobookLibraryType, + isEbookLibraryType, type AudiobookBrowseAxis, type LibraryBrowseType, } from "./libraryPageSearchParams"; @@ -97,29 +99,6 @@ function AudiobookAxisTabs({ ); } -function getLibrarySortRelevanceScope( - libraryType: string, - mediaScope?: QueryDefinition["media_scope"], -): QuerySortRelevanceScope { - if (libraryType === "movie" || libraryType === "series") { - return libraryType; - } - // The DB stores audiobook library type as the plural "audiobooks"; - // the sort scope is the singular "audiobook" (matches QueryDefinition.media_scope). - if (libraryType === "audiobook" || libraryType === "audiobooks") { - return "audiobook"; - } - if ( - mediaScope === "movie" || - mediaScope === "series" || - mediaScope === "episode" || - mediaScope === "audiobook" - ) { - return mediaScope; - } - return "all"; -} - export default function LibraryBrowse({ libraryId, libraryType, @@ -141,9 +120,11 @@ export default function LibraryBrowse({ ? queryDefinition.media_scope : isAudiobookLibraryType(libraryType) ? "audiobook" - : libraryType === "movie" - ? libraryType - : undefined, + : isEbookLibraryType(libraryType) + ? "ebook" + : libraryType === "movie" + ? libraryType + : undefined, sort: normalizeQuerySortForScope(queryDefinition.sort, { includePersonalized: true, relevanceScope: sortRelevanceScope, diff --git a/web/src/pages/audiobooks/components/RelatedRail.test.tsx b/web/src/pages/audiobooks/components/RelatedRail.test.tsx new file mode 100644 index 00000000..b29324e1 --- /dev/null +++ b/web/src/pages/audiobooks/components/RelatedRail.test.tsx @@ -0,0 +1,45 @@ +import { renderToStaticMarkup } from "react-dom/server"; +import { MemoryRouter } from "react-router"; +import { describe, expect, it } from "vitest"; + +import { RelatedRail } from "./RelatedRail"; + +const items = [{ content_id: "book-1", title: "Book One", poster_url: "/cover.jpg" }]; + +describe("RelatedRail", () => { + it("keeps square cover geometry by default", () => { + const markup = renderToStaticMarkup( + + + , + ); + + expect(markup).toContain("aspect-square"); + expect(markup).not.toContain("aspect-[2/3]"); + }); + + it("can render portrait poster geometry for ebook rails", () => { + const markup = renderToStaticMarkup( + + + , + ); + + expect(markup).toContain("aspect-[2/3]"); + expect(markup).not.toContain("aspect-square"); + }); + + it("encodes related item links", () => { + const markup = renderToStaticMarkup( + + + , + ); + + expect(markup).toContain('href="/item/ebook%201%2Fisbn%3A978"'); + }); +}); diff --git a/web/src/pages/audiobooks/components/RelatedRail.tsx b/web/src/pages/audiobooks/components/RelatedRail.tsx index 101fb3ef..d9682518 100644 --- a/web/src/pages/audiobooks/components/RelatedRail.tsx +++ b/web/src/pages/audiobooks/components/RelatedRail.tsx @@ -12,26 +12,35 @@ interface RelatedRailProps { heading: string; subtitle?: string; items: RelatedRailItem[]; + coverAspect?: "square" | "poster"; } -export function RelatedRail({ heading, subtitle, items }: RelatedRailProps) { +export function RelatedRail({ + heading, + subtitle, + items, + coverAspect = "square", +}: RelatedRailProps) { if (items.length === 0) return null; + const coverAspectClass = coverAspect === "poster" ? "aspect-[2/3]" : "aspect-square"; return (

{heading}

{subtitle &&

{subtitle}

}
-
+
{items.map((item) => ( -
+
{item.poster_url ? ( { expect(state.query_definition.sort).toEqual({ field: "title", order: "asc" }); }); + it("keeps ebook media scope from catalog URLs", () => { + const state = parseCatalogSearchParams(params("source=query&type=ebook&sort=author")); + + expect(state.query_definition.media_scope).toBe("ebook"); + expect(state.query_definition.sort).toEqual({ field: "author", order: "asc" }); + }); + it("parses the video group scope and treats type=all as unscoped", () => { const video = parseCatalogSearchParams(params("source=query&q=heat&type=video")); expect(video.query_definition.media_scope).toBe("video"); diff --git a/web/src/pages/catalogSearchParams.ts b/web/src/pages/catalogSearchParams.ts index bc45f273..2420e9ee 100644 --- a/web/src/pages/catalogSearchParams.ts +++ b/web/src/pages/catalogSearchParams.ts @@ -139,6 +139,7 @@ export function parseCatalogSearchParams(searchParams: URLSearchParams): Catalog type === "series" || type === "episode" || type === "audiobook" || + type === "ebook" || type === "video" ? type : undefined, diff --git a/web/src/pages/libraryPageSearchParams.test.ts b/web/src/pages/libraryPageSearchParams.test.ts index 58e66b03..f137e521 100644 --- a/web/src/pages/libraryPageSearchParams.test.ts +++ b/web/src/pages/libraryPageSearchParams.test.ts @@ -4,6 +4,7 @@ import { describe, expect, it } from "vitest"; import { applySavedLibraryPageSearchParams, + getLibrarySortRelevanceScope, hasLibraryPageSearchParams, parseLibraryPageState, serializeLibraryPageSearchParams, @@ -142,6 +143,14 @@ describe("parseLibraryPageState", () => { expect(state.queryDefinition.sort).toEqual({ field: "author", order: "asc" }); }); + it("preserves ebook scope for mixed library filters", () => { + const state = parseLibraryPageState(params("tab=library&type=ebook&sort=author"), "mixed"); + + expect(state.activeTab).toBe("library"); + expect(state.queryDefinition.media_scope).toBe("ebook"); + expect(state.queryDefinition.sort).toEqual({ field: "author", order: "asc" }); + }); + it("accepts grouped query params and canonical sorts", () => { const state = parseLibraryPageState( params( @@ -203,6 +212,13 @@ describe("parseLibraryPageState", () => { expect(state.queryDefinition.sort).toEqual({ field: "runtime", order: "desc" }); }); + it("uses ebook scope for ebook libraries", () => { + const state = parseLibraryPageState(params("tab=library&sort=author&order=asc"), "ebooks"); + + expect(state.queryDefinition.media_scope).toBe("ebook"); + expect(state.queryDefinition.sort).toEqual({ field: "author", order: "asc" }); + }); + it("normalizes legacy sort aliases to canonical values", () => { expect( parseLibraryPageState(params("tab=library&sort=sort_title"), "mixed").queryDefinition.sort @@ -430,3 +446,21 @@ describe("library page saved state helpers", () => { }); }); }); + +describe("getLibrarySortRelevanceScope", () => { + it("maps library types to sort relevance scopes", () => { + expect(getLibrarySortRelevanceScope("movie")).toBe("movie"); + expect(getLibrarySortRelevanceScope("series")).toBe("series"); + expect(getLibrarySortRelevanceScope("audiobook")).toBe("audiobook"); + expect(getLibrarySortRelevanceScope("audiobooks")).toBe("audiobook"); + expect(getLibrarySortRelevanceScope("ebook")).toBe("ebook"); + expect(getLibrarySortRelevanceScope("ebooks")).toBe("ebook"); + }); + + it("falls back to the media scope and then to all for mixed libraries", () => { + expect(getLibrarySortRelevanceScope("mixed", "episode")).toBe("episode"); + expect(getLibrarySortRelevanceScope("mixed", "ebook")).toBe("ebook"); + expect(getLibrarySortRelevanceScope("mixed")).toBe("all"); + expect(getLibrarySortRelevanceScope("unknown")).toBe("all"); + }); +}); diff --git a/web/src/pages/libraryPageSearchParams.ts b/web/src/pages/libraryPageSearchParams.ts index ebb00764..065d3a03 100644 --- a/web/src/pages/libraryPageSearchParams.ts +++ b/web/src/pages/libraryPageSearchParams.ts @@ -77,23 +77,27 @@ function parseAudiobookLibraryBrowseType(value: string | undefined): LibraryBrow return value === "series" || value === "authors" || value === "narrators" ? value : "books"; } -function getLibrarySortRelevanceScope( +export function getLibrarySortRelevanceScope( libraryType: string, mediaScope?: QueryDefinition["media_scope"], ): QuerySortRelevanceScope { if (libraryType === "movie" || libraryType === "series") { return libraryType; } - // The DB stores audiobook library type as the plural "audiobooks"; - // the sort scope is the singular "audiobook" (matches QueryDefinition.media_scope). + // The DB stores book library types as plurals; sort scopes are singular + // values matching QueryDefinition.media_scope. if (libraryType === "audiobook" || libraryType === "audiobooks") { return "audiobook"; } + if (libraryType === "ebook" || libraryType === "ebooks") { + return "ebook"; + } if ( mediaScope === "movie" || mediaScope === "series" || mediaScope === "episode" || - mediaScope === "audiobook" + mediaScope === "audiobook" || + mediaScope === "ebook" ) { return mediaScope; } @@ -104,6 +108,10 @@ export function isAudiobookLibraryType(libraryType: string): boolean { return libraryType === "audiobook" || libraryType === "audiobooks"; } +export function isEbookLibraryType(libraryType: string): boolean { + return libraryType === "ebook" || libraryType === "ebooks"; +} + function readString(value: string | null): string | undefined { const normalized = value?.trim(); return normalized ? normalized : undefined; @@ -295,11 +303,14 @@ export function parseLibraryPageState( (mediaScopeParam === "movie" || mediaScopeParam === "series" || mediaScopeParam === "episode" || - mediaScopeParam === "audiobook") + mediaScopeParam === "audiobook" || + mediaScopeParam === "ebook") ? mediaScopeParam : isAudiobookLibraryType(libraryType) ? "audiobook" - : undefined; + : isEbookLibraryType(libraryType) + ? "ebook" + : undefined; const sortRelevanceScope = libraryType === "series" && browseType === "episode" ? "all" diff --git a/web/src/reader/FoliateBookReader.component.test.tsx b/web/src/reader/FoliateBookReader.component.test.tsx new file mode 100644 index 00000000..45d26684 --- /dev/null +++ b/web/src/reader/FoliateBookReader.component.test.tsx @@ -0,0 +1,426 @@ +// @vitest-environment jsdom + +import { act } from "react"; +import { createRoot, type Root } from "react-dom/client"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; + +import type { FileVersion } from "@/api/types"; +import type { BookDoc } from "@/reader/readest/libs/document"; + +const mocks = vi.hoisted(() => ({ + api: vi.fn(), + apiBlob: vi.fn(), + apiKeepalive: vi.fn(), + loaderOpen: vi.fn(), +})); + +vi.mock("@/api/client", () => ({ + api: mocks.api, + apiBlob: mocks.apiBlob, + apiKeepalive: mocks.apiKeepalive, +})); + +vi.mock("@/reader/readest/libs/document", async () => { + const actual = await vi.importActual( + "@/reader/readest/libs/document", + ); + return { + ...actual, + DocumentLoader: class { + private file: File; + + constructor(file: File) { + this.file = file; + } + + open() { + return mocks.loaderOpen(this.file); + } + }, + }; +}); + +vi.mock("foliate-js/view.js", () => ({})); + +import FoliateBookReader, { + ebookReaderProgressQueryKey, + type ReaderReadyState, +} from "@/reader/FoliateBookReader"; + +type DestroyableBook = BookDoc & { destroy: () => void }; + +const createdViews: FakeFoliateView[] = []; +// Each created view shifts one behavior off this queue for its open() call; +// when the queue is empty open() resolves immediately. +const viewOpenBehaviors: Array<() => Promise> = []; + +class FakeFoliateView extends HTMLElement { + book: unknown = null; + close = vi.fn(); + init = vi.fn(async () => {}); + goToFraction = vi.fn(async () => {}); + open = vi.fn((book: unknown) => { + this.book = book; + const behavior = viewOpenBehaviors.shift(); + return behavior ? behavior() : Promise.resolve(); + }); + + constructor() { + super(); + createdViews.push(this); + } +} + +if (!customElements.get("foliate-view")) { + customElements.define("foliate-view", FakeFoliateView); +} + +function deferred() { + let resolve!: (value: T) => void; + let reject!: (reason?: unknown) => void; + const promise = new Promise((res, rej) => { + resolve = res; + reject = rej; + }); + return { promise, resolve, reject }; +} + +function makeBook(label: string): DestroyableBook { + return { + toc: [{ id: 1, label, href: `${label}.xhtml` }], + destroy: vi.fn(), + } as unknown as DestroyableBook; +} + +function makeFile(fileID: number, name: string): FileVersion { + return { + file_id: fileID, + file_name: name, + file_path: `/books/${name}`, + resolution: "", + codec_video: "", + codec_audio: "", + hdr: false, + container: "epub", + file_size: 1, + duration: 0, + bitrate: 0, + }; +} + +function viewAt(index: number): FakeFoliateView { + const view = createdViews[index]; + if (!view) throw new Error(`expected a foliate-view at index ${index}`); + return view; +} + +function relocateEvent(current: number, cfi?: string) { + return new CustomEvent("relocate", { + detail: { cfi, location: { current, total: 10 } }, + }); +} + +describe("FoliateBookReader open flow", () => { + let container: HTMLDivElement; + let root: Root; + let queryClient: QueryClient; + + const fileA = makeFile(7, "a.epub"); + const fileB = makeFile(8, "b.epub"); + + function ui(file: FileVersion, props: { onReady?: (state: ReaderReadyState) => void } = {}) { + return ( + + + + ); + } + + beforeEach(() => { + ( + globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT?: boolean } + ).IS_REACT_ACT_ENVIRONMENT = true; + container = document.createElement("div"); + document.body.appendChild(container); + root = createRoot(container); + queryClient = new QueryClient(); + createdViews.length = 0; + viewOpenBehaviors.length = 0; + mocks.api.mockReset(); + mocks.apiBlob.mockReset(); + mocks.apiKeepalive.mockReset(); + mocks.loaderOpen.mockReset(); + mocks.api.mockImplementation(async (_path: string, options?: RequestInit) => { + if (options?.method === "PUT") { + return JSON.parse(String(options.body)) as Record; + } + return {}; + }); + mocks.apiBlob.mockResolvedValue(new Blob(["epub"], { type: "application/epub+zip" })); + let urlCounter = 0; + URL.createObjectURL = vi.fn(() => `blob:mock-${++urlCounter}`); + URL.revokeObjectURL = vi.fn(); + }); + + afterEach(async () => { + vi.useRealTimers(); + await act(async () => { + root.unmount(); + }); + container.remove(); + }); + + it("tears down a superseded run when the loader resolves after a file switch", async () => { + const bookA = makeBook("A"); + const bookB = makeBook("B"); + const loaderA = deferred<{ book: BookDoc }>(); + mocks.loaderOpen.mockImplementation((file: File) => + file.name === "a.epub" ? loaderA.promise : Promise.resolve({ book: bookB }), + ); + const onReady = vi.fn(); + + await act(async () => { + root.render(ui(fileA, { onReady })); + }); + // Run A is parked on DocumentLoader.open(); switch files before it resolves. + await act(async () => { + root.render(ui(fileB, { onReady })); + }); + await act(async () => { + loaderA.resolve({ book: bookA }); + }); + + expect(bookA.destroy).toHaveBeenCalledTimes(1); + // The stale run must not create a view or report a stale TOC. + expect(createdViews).toHaveLength(1); + expect(container.querySelectorAll("foliate-view")).toHaveLength(1); + expect(onReady).toHaveBeenCalledTimes(1); + expect(onReady).toHaveBeenCalledWith({ toc: bookB.toc }); + expect(URL.revokeObjectURL).toHaveBeenCalledWith("blob:mock-1"); + }); + + it("closes a stale view whose open() resolves after cancellation and keeps the live one", async () => { + const bookA = makeBook("A"); + const bookB = makeBook("B"); + mocks.loaderOpen.mockImplementation((file: File) => + Promise.resolve({ book: file.name === "a.epub" ? bookA : bookB }), + ); + const viewOpenA = deferred(); + viewOpenBehaviors.push(() => viewOpenA.promise); + const onReady = vi.fn(); + + await act(async () => { + root.render(ui(fileA, { onReady })); + }); + expect(createdViews).toHaveLength(1); + const viewA = viewAt(0); + + await act(async () => { + root.render(ui(fileB, { onReady })); + }); + expect(createdViews).toHaveLength(2); + const viewB = viewAt(1); + + await act(async () => { + viewOpenA.resolve(); + }); + + expect(viewA.close).toHaveBeenCalled(); + expect(viewA.isConnected).toBe(false); + expect(bookA.destroy).toHaveBeenCalledTimes(1); + expect(bookB.destroy).not.toHaveBeenCalled(); + expect(onReady).toHaveBeenCalledTimes(1); + expect(onReady).toHaveBeenCalledWith({ toc: bookB.toc }); + expect(Array.from(container.querySelectorAll("foliate-view"))).toEqual([viewB]); + + // Relocates from the superseded view must not save progress for its file. + await act(async () => { + viewA.dispatchEvent(relocateEvent(3, "epubcfi(/6/2)")); + window.dispatchEvent(new Event("pagehide")); + }); + expect(mocks.apiKeepalive).not.toHaveBeenCalled(); + }); + + it("flushes pending progress with a keepalive request when the page hides", async () => { + const book = makeBook("A"); + mocks.loaderOpen.mockResolvedValue({ book }); + + await act(async () => { + root.render(ui(fileA)); + }); + const view = viewAt(0); + + await act(async () => { + view.dispatchEvent(relocateEvent(2, "epubcfi(/6/4)")); + }); + await act(async () => { + window.dispatchEvent(new Event("pagehide")); + }); + + expect(mocks.apiKeepalive).toHaveBeenCalledTimes(1); + expect(mocks.apiKeepalive).toHaveBeenCalledWith("/ebooks/book-1/progress", { + method: "PUT", + body: JSON.stringify({ file_id: 7, location: "epubcfi(/6/4)", progress: 0.3 }), + }); + + // The pending save was consumed; hiding again must not re-send it. + await act(async () => { + window.dispatchEvent(new Event("pagehide")); + }); + expect(mocks.apiKeepalive).toHaveBeenCalledTimes(1); + }); + + it("flushes pending progress through the authenticated api when the tab merely hides", async () => { + const book = makeBook("A"); + mocks.loaderOpen.mockResolvedValue({ book }); + + await act(async () => { + root.render(ui(fileA)); + }); + const view = viewAt(0); + + await act(async () => { + view.dispatchEvent(relocateEvent(2, "epubcfi(/6/4)")); + }); + + const putCalls = () => + mocks.api.mock.calls.filter(([, options]) => (options as RequestInit)?.method === "PUT"); + expect(putCalls()).toHaveLength(0); + + // The page is alive while hidden; the normal api path can refresh an + // expired token, unlike keepalive. + Object.defineProperty(document, "visibilityState", { + configurable: true, + get: () => "hidden", + }); + try { + await act(async () => { + document.dispatchEvent(new Event("visibilitychange")); + }); + + expect(mocks.apiKeepalive).not.toHaveBeenCalled(); + expect(putCalls()).toHaveLength(1); + expect(putCalls()[0]).toEqual([ + "/ebooks/book-1/progress", + { + method: "PUT", + body: JSON.stringify({ file_id: 7, location: "epubcfi(/6/4)", progress: 0.3 }), + }, + ]); + + // The pending save was consumed once; a pagehide with no new progress + // must not double-send it through keepalive. + await act(async () => { + window.dispatchEvent(new Event("pagehide")); + }); + expect(mocks.apiKeepalive).not.toHaveBeenCalled(); + expect(putCalls()).toHaveLength(1); + } finally { + Object.defineProperty(document, "visibilityState", { + configurable: true, + get: () => "visible", + }); + } + }); + + it("opens external http(s) links itself with noopener and blocks unsafe schemes", async () => { + const book = makeBook("A"); + mocks.loaderOpen.mockResolvedValue({ book }); + const open = vi.fn(); + vi.stubGlobal("open", open); + + try { + await act(async () => { + root.render(ui(fileA)); + }); + const view = viewAt(0); + + // Mirrors the vendored foliate contract: it emits a cancelable + // 'external-link' event and only runs its own globalThis.open(href, + // '_blank') when dispatchEvent returns true (no preventDefault). + const externalLink = (href: string) => + new CustomEvent("external-link", { detail: { href }, cancelable: true }); + + const httpsEvent = externalLink("https://example.com/page"); + let defaultAllowed = true; + await act(async () => { + defaultAllowed = view.dispatchEvent(httpsEvent); + }); + // The vendor default open must be cancelled; we open safely ourselves. + expect(defaultAllowed).toBe(false); + expect(open).toHaveBeenCalledTimes(1); + expect(open).toHaveBeenCalledWith( + "https://example.com/page", + "_blank", + "noopener,noreferrer", + ); + + for (const href of ["javascript:alert(1)", "data:text/html,hi", "not a url"]) { + let allowed = true; + await act(async () => { + allowed = view.dispatchEvent(externalLink(href)); + }); + expect(allowed).toBe(false); + } + expect(open).toHaveBeenCalledTimes(1); + } finally { + vi.unstubAllGlobals(); + } + }); + + it("surfaces a user-facing error when the ebook file fails to load", async () => { + mocks.apiBlob.mockRejectedValue( + new Error("This file is too large to open in the browser (3072 MiB, limit 512 MiB)."), + ); + + await act(async () => { + root.render(ui(fileA)); + }); + + expect(container.textContent).toContain("This file is too large to open in the browser"); + expect(container.textContent).not.toContain("Loading reader..."); + }); + + it("ignores progress save responses that resolve out of order", async () => { + const book = makeBook("A"); + mocks.loaderOpen.mockResolvedValue({ book }); + const firstPut = deferred>(); + const secondPut = deferred>(); + const puts = [firstPut, secondPut]; + mocks.api.mockImplementation((_path: string, options?: RequestInit) => { + if (options?.method === "PUT") { + return puts.shift()!.promise; + } + return Promise.resolve({}); + }); + + await act(async () => { + root.render(ui(fileA)); + }); + const view = viewAt(0); + vi.useFakeTimers(); + + await act(async () => { + view.dispatchEvent(relocateEvent(1, "epubcfi(/6/2)")); + vi.advanceTimersByTime(800); + }); + await act(async () => { + view.dispatchEvent(relocateEvent(4, "epubcfi(/6/8)")); + vi.advanceTimersByTime(800); + }); + + // The newer save resolves first; the older response must not overwrite it. + await act(async () => { + secondPut.resolve({ file_id: 7, location: "epubcfi(/6/8)", progress: 0.5 }); + }); + await act(async () => { + firstPut.resolve({ file_id: 7, location: "epubcfi(/6/2)", progress: 0.2 }); + }); + + expect(queryClient.getQueryData(ebookReaderProgressQueryKey("book-1"))).toMatchObject({ + location: "epubcfi(/6/8)", + progress: 0.5, + }); + }); +}); diff --git a/web/src/reader/FoliateBookReader.test.ts b/web/src/reader/FoliateBookReader.test.ts new file mode 100644 index 00000000..d07b67f0 --- /dev/null +++ b/web/src/reader/FoliateBookReader.test.ts @@ -0,0 +1,300 @@ +import { describe, expect, it } from "vitest"; +import { QueryClient } from "@tanstack/react-query"; + +import type { FileVersion } from "@/api/types"; +import { DocumentLoader } from "@/reader/readest/libs/document"; +import { + cacheEbookReaderProgress, + DEFAULT_READER_SETTINGS, + ebookProgressPath, + ebookReaderProgressQueryKey, + ebookReadPath, + formatReaderProgress, + isReaderSupportedFile, + normalizeReaderSettings, + parseReaderLocation, + READER_FONT_STACKS, + readerRendererAttributes, + readerStyles, + restoreProgressTarget, + progressFromRelocate, + readerFileFormat, + readerMimeType, +} from "./FoliateBookReader"; + +function version(overrides: Partial): FileVersion { + return { + file_id: overrides.file_id ?? 7, + file_name: overrides.file_name, + file_path: overrides.file_path, + resolution: "", + codec_video: "", + codec_audio: "", + hdr: false, + container: overrides.container ?? "", + file_size: 1, + duration: 0, + bitrate: 0, + }; +} + +describe("FoliateBookReader helpers", () => { + it("builds the protected ebook read endpoint", () => { + expect(ebookReadPath("ebook 1", 42)).toBe("/ebooks/ebook%201/files/42/read"); + }); + + it("builds the protected ebook progress endpoint", () => { + expect(ebookProgressPath("ebook 1")).toBe("/ebooks/ebook%201/progress"); + }); + + it("caches saved reader progress under the shared detail query key", () => { + const client = new QueryClient(); + const saved = { + file_id: 42, + location: "epubcfi(/6/4)", + progress: 0.42, + content_id: "ebook 1", + }; + + cacheEbookReaderProgress(client, "ebook 1", saved); + + expect(client.getQueryData(ebookReaderProgressQueryKey("ebook 1"))).toEqual(saved); + }); + + it("detects reader file formats from container or filename", () => { + expect(readerFileFormat(version({ container: ".AZW3", file_name: "ignored.epub" }))).toBe( + "azw3", + ); + expect(readerFileFormat(version({ file_name: "book.fb2.zip" }))).toBe("fbz"); + expect(readerFileFormat(version({ container: "zip", file_name: "book.fb2.zip" }))).toBe("fbz"); + expect(readerFileFormat(version({ container: "zip", file_name: "comic.cbz" }))).toBe("cbz"); + expect(readerFileFormat(version({ container: "rar", file_name: "comic.cbr" }))).toBe("cbr"); + expect(readerFileFormat(version({ file_path: "/library/book.cbz" }))).toBe("cbz"); + }); + + it("does not support plain text files in the reader", () => { + expect(isReaderSupportedFile(version({ file_name: "notes.txt" }))).toBe(false); + }); + + it("does not advertise markdown as a supported ebook format", () => { + expect(isReaderSupportedFile(version({ file_name: "notes.md" }))).toBe(false); + expect(readerMimeType("md")).toBe("application/octet-stream"); + }); + + it("rejects plain text when the document loader is called directly", async () => { + const file = new File(["notes"], "notes.txt", { type: "text/plain" }); + + await expect(new DocumentLoader(file).open()).rejects.toThrow("Unsupported book format"); + }); + + it("maps readest formats to MIME types", () => { + expect(readerMimeType("epub")).toBe("application/epub+zip"); + expect(readerMimeType("azw3")).toBe("application/vnd.amazon.mobi8-ebook"); + expect(readerMimeType("fbz")).toBe("application/x-zip-compressed-fb2"); + }); + + it("converts relocate events into saveable progress", () => { + expect( + progressFromRelocate( + { + cfi: "epubcfi(/6/4)", + location: { current: 2, total: 10 }, + }, + 42, + ), + ).toEqual({ + file_id: 42, + location: "epubcfi(/6/4)", + progress: 0.3, + }); + }); + + it("converts non-CFI relocate events into fraction-backed progress", () => { + expect( + progressFromRelocate( + { + location: { current: 4, total: 10 }, + }, + 42, + ), + ).toEqual({ + file_id: 42, + location: "fraction:0.500000", + progress: 0.5, + }); + }); + + it("chooses a fraction restore target for non-CFI reader progress", () => { + expect( + restoreProgressTarget({ + file_id: 42, + location: "fraction:0.500000", + progress: 0.5, + }), + ).toEqual({ type: "fraction", fraction: 0.5 }); + expect( + restoreProgressTarget({ + file_id: 42, + location: "epubcfi(/6/4)", + progress: 0.3, + }), + ).toEqual({ type: "location", location: "epubcfi(/6/4)" }); + }); + + it("parses stored reader locations into navigation targets", () => { + expect(parseReaderLocation("fraction:0.500000")).toEqual({ type: "fraction", fraction: 0.5 }); + expect(parseReaderLocation("fraction:1.5")).toEqual({ type: "fraction", fraction: 1 }); + expect(parseReaderLocation(" epubcfi(/6/4) ")).toEqual({ + type: "location", + location: "epubcfi(/6/4)", + }); + expect(parseReaderLocation("fraction:not-a-number")).toBeNull(); + expect(parseReaderLocation("")).toBeNull(); + expect(parseReaderLocation(undefined)).toBeNull(); + }); + + it("formats reader progress for compact controls", () => { + expect(formatReaderProgress(null)).toBeNull(); + expect(formatReaderProgress(Number.NaN)).toBeNull(); + expect(formatReaderProgress(0)).toBe("0%"); + expect(formatReaderProgress(0.421)).toBe("42%"); + expect(formatReaderProgress(1)).toBe("100%"); + expect(formatReaderProgress(1.5)).toBe("100%"); + }); + + it("ignores relocate events without a usable location", () => { + expect(progressFromRelocate({ cfi: "epubcfi(/6/4)", location: { total: 0 } }, 42)).toBeNull(); + }); + + it("normalizes reader settings into bounded values", () => { + expect( + normalizeReaderSettings({ + fontSize: 400, + lineHeight: 0.2, + margin: -10, + maxWidth: 200, + theme: "sepia", + flow: "scrolled", + spread: "none", + }), + ).toMatchObject({ + fontSize: 180, + lineHeight: 1.1, + margin: 0, + maxWidth: 96, + theme: "sepia", + flow: "scrolled", + spread: "none", + }); + }); + + it("defaults to the ebook publisher font instead of an unloaded named font", () => { + expect(DEFAULT_READER_SETTINGS.fontFamily).toBe("inherit"); + expect(normalizeReaderSettings({ fontFamily: "" }).fontFamily).toBe("inherit"); + }); + + it("migrates font choices persisted before the generic stacks existed", () => { + expect( + normalizeReaderSettings({ fontFamily: "Inter, ui-sans-serif, system-ui, sans-serif" }) + .fontFamily, + ).toBe(READER_FONT_STACKS.sans); + expect(normalizeReaderSettings({ fontFamily: "Georgia, serif" }).fontFamily).toBe( + READER_FONT_STACKS.serif, + ); + expect(normalizeReaderSettings({ fontFamily: "Merriweather, Georgia, serif" }).fontFamily).toBe( + READER_FONT_STACKS.serif, + ); + expect( + normalizeReaderSettings({ fontFamily: "ui-serif, Georgia, Cambria, serif" }).fontFamily, + ).toBe(READER_FONT_STACKS.serif); + }); + + it("normalizes persisted reading ruler settings", () => { + expect( + normalizeReaderSettings({ + readingRuler: true, + readingRulerTop: 250, + }), + ).toMatchObject({ + readingRuler: true, + readingRulerTop: 100, + }); + expect(normalizeReaderSettings({ readingRulerTop: -10 })).toMatchObject({ + readingRuler: false, + readingRulerTop: 0, + }); + }); + + it("builds reader styles from settings", () => { + const styles = readerStyles( + normalizeReaderSettings({ + theme: "dark", + fontFamily: READER_FONT_STACKS.serif, + fontSize: 128, + fontWeight: 500, + lineHeight: 1.8, + maxWidth: 68, + hyphenation: false, + rtl: true, + writingMode: "vertical-rl", + }), + ); + + expect(styles).toContain("color-scheme: dark"); + expect(styles).toContain("background: #111827 !important"); + expect(styles).toContain(`font-family: ${READER_FONT_STACKS.serif} !important`); + expect(styles).toContain("font-size: 128% !important"); + expect(styles).toContain("font-weight: 500 !important"); + expect(styles).toContain("hyphens: none !important"); + expect(styles).toContain("line-height: 1.8 !important"); + expect(styles).toContain("max-width: 68ch !important"); + expect(styles).toContain("direction: rtl !important"); + expect(styles).toContain("writing-mode: vertical-rl !important"); + }); + + it("uses full available width in scrolled flow", () => { + const scrolledStyles = readerStyles( + normalizeReaderSettings({ + flow: "scrolled", + maxWidth: 68, + }), + ); + + expect(scrolledStyles).toContain("max-width: none !important"); + expect(scrolledStyles).not.toContain("max-width: 68ch !important"); + + expect( + readerRendererAttributes( + normalizeReaderSettings({ + flow: "paginated", + maxWidth: 68, + margin: 20, + spread: "auto", + }), + ), + ).toEqual({ + flow: null, + gap: "7%", + margin: "20px", + maxColumnCount: "2", + maxInlineSize: "680px", + }); + + expect( + readerRendererAttributes( + normalizeReaderSettings({ + flow: "scrolled", + maxWidth: 68, + margin: 20, + spread: "auto", + }), + ), + ).toEqual({ + flow: "scrolled", + gap: "7%", + margin: "20px", + maxColumnCount: "1", + maxInlineSize: "9999px", + }); + }); +}); diff --git a/web/src/reader/FoliateBookReader.tsx b/web/src/reader/FoliateBookReader.tsx new file mode 100644 index 00000000..9047f8f6 --- /dev/null +++ b/web/src/reader/FoliateBookReader.tsx @@ -0,0 +1,941 @@ +import { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from "react"; +import { type QueryClient, useQueryClient } from "@tanstack/react-query"; + +import { api, apiBlob, apiKeepalive } from "@/api/client"; +import type { FileVersion } from "@/api/types"; +import { ebookKeys } from "@/hooks/queries/keys"; +import type { EbookReaderAnnotation } from "@/reader/ebookReaderApi"; +import { DocumentLoader, type BookDoc, type TOCItem } from "@/reader/readest/libs/document"; + +type FoliateViewElement = HTMLElement & { + open: (book: BookDoc) => Promise; + close?: () => void; + init: (options: { lastLocation: string }) => Promise; + goToFraction: (fraction: number) => Promise; + goTo?: (href: string) => void; + getCFI?: (index: number, range: Range) => string; + addAnnotation?: ( + annotation: { value: string; color?: string; style?: string; note?: string }, + remove?: boolean, + ) => void; + deselect?: () => void; + next?: () => void; + prev?: () => void; + search?: ( + options: ReaderSearchOptions & { query: string }, + ) => AsyncGenerator; + clearSearch?: () => void; + renderer?: HTMLElement & { + primaryIndex?: number; + getContents?: () => Array<{ doc: Document; index?: number }>; + setStyles?: (css: string) => void; + render?: () => Promise; + }; +}; + +export type ReaderLoadState = { + objectUrl: string; + filename: string; +}; + +export type EbookReaderProgressPayload = { + file_id: number; + location: string; + progress: number; +}; + +export type EbookReaderProgress = EbookReaderProgressPayload & { + content_id?: string; + updated_at?: string; +}; + +export type RestoreProgressTarget = + | { type: "location"; location: string } + | { type: "fraction"; fraction: number }; + +export type FoliateBookReaderHandle = { + next: () => void; + prev: () => void; + goTo: (href: string) => void; + goToFraction: (fraction: number) => Promise; + search: (query: string) => Promise; + clearSearch: () => void; + clearSelection: () => void; + createSelectionAnnotation: () => ReaderSelection | null; + getReadableText: () => string; +}; + +export type ReaderTheme = "light" | "sepia" | "dark"; +export type ReaderFlow = "paginated" | "scrolled"; +export type ReaderSpread = "auto" | "none"; +export type ReaderWritingMode = "auto" | "horizontal-tb" | "vertical-rl"; + +export type ReaderSettings = { + theme: ReaderTheme; + fontFamily: string; + fontSize: number; + fontWeight: number; + hyphenation: boolean; + lineHeight: number; + margin: number; + maxWidth: number; + spread: ReaderSpread; + flow: ReaderFlow; + fontBrightness: number; + rtl: boolean; + writingMode: ReaderWritingMode; + readingRuler: boolean; + readingRulerTop: number; +}; + +export type ReaderReadyState = { + toc: TOCItem[]; +}; + +export type ReaderSearchOptions = { + matchCase?: boolean; + matchDiacritics?: boolean; + matchWholeWords?: boolean; + scope?: "book" | "section"; +}; + +export type ReaderSearchResult = { + cfi: string; + label?: string; + excerpt?: string; +}; + +export type ReaderSelection = { + cfi: string; + rect: { + height: number; + left: number; + top: number; + width: number; + }; + selectedText: string; +}; + +type FoliateSearchResult = { + cfi?: string; + excerpt?: string; + label?: string; + section?: { label?: string; href?: string }; + subitems?: Array<{ + cfi?: string; + excerpt?: string; + label?: string; + }>; +}; + +type RelocateDetail = { + cfi?: string; + location?: { + current?: number; + total?: number; + }; +}; + +// foliate book documents expose destroy() to release cached resource blob URLs, +// but the readest BookDoc interface does not declare it. +type DisposableBookDoc = BookDoc & { destroy?: () => void }; + +const READEST_FORMATS = new Set(["epub", "pdf", "mobi", "azw", "azw3", "cbz", "cbr", "fb2", "fbz"]); + +export const READER_FONT_STACKS = { + inherit: "inherit", + serif: 'ui-serif, Georgia, Cambria, "Times New Roman", Times, serif', + sans: 'ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif', + mono: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace', +} as const; + +// Font values persisted before the reader switched to generic stacks; without this +// mapping the font select renders blank for settings saved by older builds. +const LEGACY_READER_FONT_ALIASES: Record = { + "Inter, ui-sans-serif, system-ui, sans-serif": READER_FONT_STACKS.sans, + "Georgia, serif": READER_FONT_STACKS.serif, + "Merriweather, Georgia, serif": READER_FONT_STACKS.serif, + "ui-serif, Georgia, Cambria, serif": READER_FONT_STACKS.serif, +}; + +export const DEFAULT_READER_SETTINGS: ReaderSettings = { + theme: "light", + fontFamily: READER_FONT_STACKS.inherit, + fontSize: 112, + fontWeight: 400, + hyphenation: true, + lineHeight: 1.65, + margin: 24, + maxWidth: 74, + spread: "auto", + flow: "paginated", + fontBrightness: 100, + rtl: false, + writingMode: "auto", + readingRuler: false, + readingRulerTop: 50, +}; + +export function ebookReadPath(contentID: string, fileID: number): string { + return `/ebooks/${encodeURIComponent(contentID)}/files/${fileID}/read`; +} + +export function ebookProgressPath(contentID: string): string { + return `/ebooks/${encodeURIComponent(contentID)}/progress`; +} + +export function ebookReaderProgressQueryKey(contentID: string | undefined) { + return ebookKeys.readerProgress(contentID); +} + +export function cacheEbookReaderProgress( + queryClient: QueryClient, + contentID: string, + progress: EbookReaderProgress, +) { + queryClient.setQueryData(ebookReaderProgressQueryKey(contentID), progress); +} + +export function readerFileFormat(file: FileVersion | undefined): string { + if (!file) return ""; + const fileName = file.file_name || file.file_path || ""; + if (fileName.toLowerCase().endsWith(".fb2.zip")) return "fbz"; + const extension = /\.([a-z0-9]+)$/i.exec(fileName)?.[1]?.toLowerCase() ?? ""; + const container = file.container?.trim().toLowerCase(); + const normalizedContainer = container ? container.replace(/^\./, "") : ""; + if (normalizedContainer && normalizedContainer !== "zip" && normalizedContainer !== "rar") { + return normalizedContainer; + } + return extension || normalizedContainer; +} + +export function isReaderSupportedFile(file: FileVersion | undefined): boolean { + return READEST_FORMATS.has(readerFileFormat(file)); +} + +export function readerMimeType(format: string): string { + switch (format.toLowerCase()) { + case "epub": + return "application/epub+zip"; + case "pdf": + return "application/pdf"; + case "mobi": + return "application/x-mobipocket-ebook"; + case "azw": + return "application/vnd.amazon.ebook"; + case "azw3": + return "application/vnd.amazon.mobi8-ebook"; + case "cbz": + return "application/vnd.comicbook+zip"; + case "cbr": + return "application/vnd.comicbook-rar"; + case "fb2": + return "application/x-fictionbook+xml"; + case "fbz": + return "application/x-zip-compressed-fb2"; + default: + return "application/octet-stream"; + } +} + +export function progressFromRelocate( + detail: RelocateDetail, + fileID: number, +): EbookReaderProgressPayload | null { + const current = detail.location?.current ?? 0; + const total = detail.location?.total ?? 0; + if (total <= 0 || current < 0) return null; + const progress = Math.min(1, Math.max(0, (current + 1) / total)); + const cfi = typeof detail.cfi === "string" ? detail.cfi.trim() : ""; + return { + file_id: fileID, + location: cfi || `fraction:${progress.toFixed(6)}`, + progress, + }; +} + +function clampFraction(value: number): number { + return Math.min(1, Math.max(0, value)); +} + +/** + * Parses a stored reader location into a navigation target. Locations are either + * raw CFI/href strings or synthetic `fraction:` values (used for bookmarks and + * progress in formats without CFIs), which foliate cannot resolve directly. + */ +export function parseReaderLocation( + location: string | null | undefined, +): RestoreProgressTarget | null { + if (typeof location !== "string") return null; + const trimmed = location.trim(); + if (!trimmed) return null; + if (trimmed.startsWith("fraction:")) { + const value = Number(trimmed.slice("fraction:".length)); + if (!Number.isFinite(value)) return null; + return { type: "fraction", fraction: clampFraction(value) }; + } + return { type: "location", location: trimmed }; +} + +export function restoreProgressTarget( + progress: Pick | null | undefined, +): RestoreProgressTarget | null { + if (!progress || typeof progress.location !== "string") return null; + const location = progress.location.trim(); + const target = parseReaderLocation(location); + if (target) return target; + // A malformed fraction location can still be restored from the stored progress value. + if (location.startsWith("fraction:") && Number.isFinite(progress.progress)) { + return { type: "fraction", fraction: clampFraction(progress.progress) }; + } + return null; +} + +export function formatReaderProgress(progress: number | null | undefined): string | null { + if (typeof progress !== "number" || !Number.isFinite(progress)) return null; + const bounded = Math.min(1, Math.max(0, progress)); + return `${Math.round(bounded * 100)}%`; +} + +function clampNumber(value: unknown, fallback: number, min: number, max: number): number { + const number = typeof value === "number" ? value : Number(value); + if (!Number.isFinite(number)) return fallback; + return Math.min(max, Math.max(min, number)); +} + +function isReaderTheme(value: unknown): value is ReaderTheme { + return value === "light" || value === "sepia" || value === "dark"; +} + +function isReaderFlow(value: unknown): value is ReaderFlow { + return value === "paginated" || value === "scrolled"; +} + +function isReaderSpread(value: unknown): value is ReaderSpread { + return value === "auto" || value === "none"; +} + +function isReaderWritingMode(value: unknown): value is ReaderWritingMode { + return value === "auto" || value === "horizontal-tb" || value === "vertical-rl"; +} + +function normalizeReaderFontFamily(value: unknown): string { + if (typeof value !== "string" || !value.trim()) return DEFAULT_READER_SETTINGS.fontFamily; + const trimmed = value.trim(); + return LEGACY_READER_FONT_ALIASES[trimmed] ?? trimmed; +} + +export function normalizeReaderSettings(settings?: Partial): ReaderSettings { + return { + theme: isReaderTheme(settings?.theme) ? settings.theme : DEFAULT_READER_SETTINGS.theme, + fontFamily: normalizeReaderFontFamily(settings?.fontFamily), + fontSize: clampNumber(settings?.fontSize, DEFAULT_READER_SETTINGS.fontSize, 80, 180), + fontWeight: clampNumber(settings?.fontWeight, DEFAULT_READER_SETTINGS.fontWeight, 300, 800), + hyphenation: + typeof settings?.hyphenation === "boolean" + ? settings.hyphenation + : DEFAULT_READER_SETTINGS.hyphenation, + lineHeight: clampNumber(settings?.lineHeight, DEFAULT_READER_SETTINGS.lineHeight, 1.1, 2.4), + margin: clampNumber(settings?.margin, DEFAULT_READER_SETTINGS.margin, 0, 64), + maxWidth: clampNumber(settings?.maxWidth, DEFAULT_READER_SETTINGS.maxWidth, 42, 96), + spread: isReaderSpread(settings?.spread) ? settings.spread : DEFAULT_READER_SETTINGS.spread, + flow: isReaderFlow(settings?.flow) ? settings.flow : DEFAULT_READER_SETTINGS.flow, + fontBrightness: clampNumber( + settings?.fontBrightness, + DEFAULT_READER_SETTINGS.fontBrightness, + 70, + 125, + ), + rtl: typeof settings?.rtl === "boolean" ? settings.rtl : DEFAULT_READER_SETTINGS.rtl, + writingMode: isReaderWritingMode(settings?.writingMode) + ? settings.writingMode + : DEFAULT_READER_SETTINGS.writingMode, + readingRuler: + typeof settings?.readingRuler === "boolean" + ? settings.readingRuler + : DEFAULT_READER_SETTINGS.readingRuler, + readingRulerTop: clampNumber( + settings?.readingRulerTop, + DEFAULT_READER_SETTINGS.readingRulerTop, + 0, + 100, + ), + }; +} + +export async function fetchEbookReaderProgress( + contentID: string, +): Promise { + const progress = await api>(ebookProgressPath(contentID)); + if (!progress || typeof progress.location !== "string" || progress.location.trim() === "") { + return null; + } + if (typeof progress.file_id !== "number" || typeof progress.progress !== "number") { + return null; + } + return { + file_id: progress.file_id, + location: progress.location, + progress: progress.progress, + content_id: progress.content_id, + updated_at: progress.updated_at, + }; +} + +export async function saveEbookReaderProgress( + contentID: string, + progress: EbookReaderProgressPayload, +): Promise { + return api(ebookProgressPath(contentID), { + method: "PUT", + body: JSON.stringify(progress), + }); +} + +function readerColors(theme: ReaderTheme) { + switch (theme) { + case "dark": + return { + background: "#111827", + foreground: "#f8fafc", + link: "#93c5fd", + scheme: "dark", + }; + case "sepia": + return { + background: "#f4ecd8", + foreground: "#2f261b", + link: "#8b5a2b", + scheme: "light", + }; + default: + return { + background: "#ffffff", + foreground: "#171717", + link: "#2563eb", + scheme: "light", + }; + } +} + +export function readerStyles(settings: ReaderSettings = DEFAULT_READER_SETTINGS) { + const colors = readerColors(settings.theme); + const contentMaxWidth = settings.flow === "scrolled" ? "none" : `${settings.maxWidth}ch`; + return ` + :root { + --theme-bg-color: ${colors.background}; + --theme-fg-color: ${colors.foreground}; + --override-color: true; + color-scheme: ${colors.scheme}; + } + html, body { + background: ${colors.background} !important; + color: ${colors.foreground} !important; + font-family: ${settings.fontFamily} !important; + font-size: ${settings.fontSize}% !important; + font-weight: ${settings.fontWeight} !important; + hyphens: ${settings.hyphenation ? "auto" : "none"} !important; + line-height: ${settings.lineHeight} !important; + max-width: ${contentMaxWidth} !important; + direction: ${settings.rtl ? "rtl" : "inherit"} !important; + writing-mode: ${settings.writingMode === "auto" ? "inherit" : settings.writingMode} !important; + filter: brightness(${settings.fontBrightness}%) !important; + } + body :where(p, span, div, li, blockquote, h1, h2, h3, h4, h5, h6, + em, i, strong, b, code, pre, td, th, caption, figcaption, + dt, dd, small, sub, sup, cite, q, mark) { + color: ${colors.foreground} !important; + } + p, li, blockquote { margin-block: 0.75em !important; } + a { color: ${colors.link} !important; } + `; +} + +export function readerRendererAttributes(settings: ReaderSettings) { + const scrolled = settings.flow === "scrolled"; + const maxInlinePx = Math.round(settings.maxWidth * 10); + return { + flow: scrolled ? "scrolled" : null, + gap: "7%", + margin: `${settings.margin}px`, + maxInlineSize: scrolled ? "9999px" : `${maxInlinePx}px`, + maxColumnCount: scrolled ? "1" : settings.spread === "none" ? "1" : "2", + }; +} + +function searchResultLabel(result: FoliateSearchResult, item?: { label?: string }): string { + return item?.label || result.label || result.section?.label || ""; +} + +function flattenSearchResult(result: FoliateSearchResult): ReaderSearchResult[] { + const direct = result.cfi + ? [ + { + cfi: result.cfi, + label: searchResultLabel(result), + excerpt: result.excerpt, + }, + ] + : []; + const subitems = result.subitems ?? []; + return direct.concat( + subitems + .filter((item) => typeof item.cfi === "string" && item.cfi.trim() !== "") + .map((item) => ({ + cfi: item.cfi || "", + label: searchResultLabel(result, item), + excerpt: item.excerpt, + })), + ); +} + +type FoliateBookReaderProps = { + contentID: string; + file: FileVersion; + title: string; + annotations?: EbookReaderAnnotation[]; + settings?: Partial; + onFileLoaded?: (state: ReaderLoadState | null) => void; + onProgressChange?: (progress: number | null) => void; + onReady?: (state: ReaderReadyState) => void; + onSelectionChange?: (selection: ReaderSelection | null) => void; +}; + +const FoliateBookReader = forwardRef( + function FoliateBookReader( + { + contentID, + file, + title, + annotations = [], + settings, + onFileLoaded, + onProgressChange, + onReady, + onSelectionChange, + }, + ref, + ) { + const queryClient = useQueryClient(); + const containerRef = useRef(null); + const viewRef = useRef(null); + const initializedRef = useRef(false); + const saveTimerRef = useRef(null); + const pendingProgressRef = useRef(null); + const progressSaveSeqRef = useRef(0); + const settingsRef = useRef(normalizeReaderSettings(settings)); + const appliedRendererKeyRef = useRef(""); + const annotationsRef = useRef(annotations); + const drawnCfisRef = useRef>(new Set()); + const selectionCleanupRef = useRef<(() => void)[]>([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(""); + + const applyReaderSettings = useCallback((nextSettings: Partial | undefined) => { + const normalized = normalizeReaderSettings(nextSettings); + settingsRef.current = normalized; + const renderer = viewRef.current?.renderer; + if (!renderer) return; + const styles = readerStyles(normalized); + const attributes = readerRendererAttributes(normalized); + // Settings such as the reading ruler never reach the renderer; re-styling and + // re-rendering the book for those (or for any no-op update) causes visible jank. + const rendererKey = JSON.stringify([styles, attributes]); + if (rendererKey === appliedRendererKeyRef.current) return; + appliedRendererKeyRef.current = rendererKey; + renderer.setStyles?.(styles); + renderer.setAttribute("gap", attributes.gap); + renderer.setAttribute("margin", attributes.margin); + renderer.setAttribute("max-inline-size", attributes.maxInlineSize); + renderer.setAttribute("max-column-count", attributes.maxColumnCount); + if (attributes.flow) { + renderer.setAttribute("flow", attributes.flow); + } else { + renderer.removeAttribute("flow"); + } + void renderer.render?.(); + }, []); + + const drawAnnotations = useCallback(() => { + const view = viewRef.current; + if (!view?.addAnnotation) return; + const activeCfis = new Set( + annotationsRef.current + .filter((annotation) => annotation.kind !== "bookmark" && annotation.cfi_range) + .map((annotation) => annotation.cfi_range || ""), + ); + for (const cfi of drawnCfisRef.current) { + if (!activeCfis.has(cfi)) { + view.addAnnotation({ value: cfi }, true); + drawnCfisRef.current.delete(cfi); + } + } + for (const annotation of annotationsRef.current) { + if (annotation.kind === "bookmark" || !annotation.cfi_range) continue; + if (drawnCfisRef.current.has(annotation.cfi_range)) continue; + view.addAnnotation({ + value: annotation.cfi_range, + color: annotation.color || "#facc15", + style: annotation.style || "highlight", + note: annotation.note || undefined, + }); + drawnCfisRef.current.add(annotation.cfi_range); + } + }, []); + + const createSelectionAnnotation = useCallback((): ReaderSelection | null => { + const view = viewRef.current; + const contents = view?.renderer?.getContents?.() ?? []; + if (!view?.getCFI) return null; + for (const content of contents) { + const selection = content.doc.getSelection(); + if (!selection || selection.isCollapsed || selection.rangeCount === 0) continue; + const selectedText = selection.toString().trim(); + if (!selectedText) continue; + const range = selection.getRangeAt(0); + const cfi = view.getCFI(content.index ?? 0, range); + const rangeRect = range.getBoundingClientRect(); + const frameRect = content.doc.defaultView?.frameElement?.getBoundingClientRect(); + return { + cfi, + selectedText, + rect: { + height: rangeRect.height, + left: rangeRect.left + (frameRect?.left ?? 0), + top: rangeRect.top + (frameRect?.top ?? 0), + width: rangeRect.width, + }, + }; + } + return null; + }, []); + + const emitSelectionChange = useCallback(() => { + onSelectionChange?.(createSelectionAnnotation()); + }, [createSelectionAnnotation, onSelectionChange]); + + const attachSelectionListeners = useCallback(() => { + for (const cleanup of selectionCleanupRef.current) cleanup(); + selectionCleanupRef.current = []; + const contents = viewRef.current?.renderer?.getContents?.() ?? []; + for (const content of contents) { + const doc = content.doc; + const handler = () => window.setTimeout(emitSelectionChange, 0); + doc.addEventListener("selectionchange", handler); + doc.addEventListener("pointerup", handler); + doc.addEventListener("keyup", handler); + selectionCleanupRef.current.push(() => { + doc.removeEventListener("selectionchange", handler); + doc.removeEventListener("pointerup", handler); + doc.removeEventListener("keyup", handler); + }); + } + }, [emitSelectionChange]); + + const getReadableText = useCallback(() => { + const contents = viewRef.current?.renderer?.getContents?.() ?? []; + for (const content of contents) { + const selectedText = content.doc.getSelection()?.toString().trim(); + if (selectedText) return selectedText; + } + const primaryIndex = viewRef.current?.renderer?.primaryIndex; + const primary = contents.find((content) => content.index === primaryIndex) ?? contents[0]; + return (primary?.doc.body?.innerText ?? "").replace(/\s+/g, " ").trim().slice(0, 5000); + }, []); + + useImperativeHandle( + ref, + () => ({ + next: () => viewRef.current?.next?.(), + prev: () => viewRef.current?.prev?.(), + goTo: (href: string) => viewRef.current?.goTo?.(href), + goToFraction: async (fraction: number) => { + await viewRef.current?.goToFraction(Math.min(1, Math.max(0, fraction))); + }, + search: async (query: string) => { + const trimmed = query.trim(); + const view = viewRef.current; + if (!trimmed || !view?.search) return []; + const results: ReaderSearchResult[] = []; + for await (const result of view.search({ query: trimmed, scope: "book" })) { + results.push(...flattenSearchResult(result)); + } + return results; + }, + clearSearch: () => viewRef.current?.clearSearch?.(), + clearSelection: () => { + viewRef.current?.deselect?.(); + onSelectionChange?.(null); + }, + createSelectionAnnotation, + getReadableText, + }), + [createSelectionAnnotation, getReadableText, onSelectionChange], + ); + + useEffect(() => { + applyReaderSettings(settings); + }, [applyReaderSettings, settings]); + + useEffect(() => { + annotationsRef.current = annotations; + drawAnnotations(); + }, [annotations, drawAnnotations]); + + useEffect(() => { + let cancelled = false; + let objectUrl: string | null = null; + let openedBook: DisposableBookDoc | null = null; + let openedView: FoliateViewElement | null = null; + setLoading(true); + setError(""); + onFileLoaded?.(null); + onProgressChange?.(null); + + // Releases everything this effect run created. It runs from the effect + // cleanup and again from late continuations of a superseded open(); it is + // idempotent and never touches resources owned by a newer run. + const disposeOpenArtifacts = () => { + if (openedView) { + openedView.close?.(); + openedView.remove(); + if (viewRef.current === openedView) { + viewRef.current = null; + } + openedView = null; + } + if (openedBook) { + openedBook.destroy?.(); + openedBook = null; + } + if (objectUrl) { + URL.revokeObjectURL(objectUrl); + objectUrl = null; + } + }; + + // Progress is deliberately per-content (one record per book): opening another + // format overwrites this position, keeping "your place in the book" shared + // across formats. + const flushProgress = (options?: { keepalive?: boolean }) => { + const pending = pendingProgressRef.current; + if (!pending) return; + pendingProgressRef.current = null; + const seq = ++progressSaveSeqRef.current; + if (options?.keepalive) { + // The page may be unloading; fire a keepalive PUT so the write survives + // teardown. The response cannot be observed, so skip cache updates. + apiKeepalive(ebookProgressPath(contentID), { + method: "PUT", + body: JSON.stringify(pending), + }); + return; + } + saveEbookReaderProgress(contentID, pending).then( + (saved) => { + // Saves can resolve out of order; only the newest may cache its response. + if (seq === progressSaveSeqRef.current) { + cacheEbookReaderProgress(queryClient, contentID, saved); + } + }, + () => { + // Progress saves are best-effort; the next relocate retries. + }, + ); + }; + + const scheduleProgressSave = (progress: EbookReaderProgressPayload) => { + pendingProgressRef.current = progress; + if (saveTimerRef.current !== null) { + window.clearTimeout(saveTimerRef.current); + } + saveTimerRef.current = window.setTimeout(() => { + saveTimerRef.current = null; + flushProgress(); + }, 800); + }; + + const flushProgressOnVisibilityChange = () => { + if (document.visibilityState === "hidden") { + // The page is still alive when it merely hides, so use the normal + // authenticated path, which can refresh an expired access token. + // Keepalive (no refresh possible) is reserved for pagehide. + flushProgress(); + } + }; + const flushProgressOnPageHide = () => flushProgress({ keepalive: true }); + + async function open() { + try { + const format = readerFileFormat(file); + const [blob, savedProgress] = await Promise.all([ + apiBlob(ebookReadPath(contentID, file.file_id)), + fetchEbookReaderProgress(contentID), + ]); + if (cancelled) return; + + objectUrl = URL.createObjectURL(blob); + const filename = file.file_name || `${title}.${format || "ebook"}`; + onFileLoaded?.({ objectUrl, filename }); + const documentFile = new File([blob], filename, { + type: blob.type || readerMimeType(format), + }); + const { book } = await new DocumentLoader(documentFile).open(); + openedBook = book as DisposableBookDoc; + if (cancelled) { + disposeOpenArtifacts(); + return; + } + + await import("foliate-js/view.js"); + if (cancelled) { + disposeOpenArtifacts(); + return; + } + const view = document.createElement("foliate-view") as FoliateViewElement; + openedView = view; + viewRef.current = view; + containerRef.current?.replaceChildren(view); + view.addEventListener("draw-annotation", async (event: Event) => { + const { Overlayer } = await import("foliate-js/overlayer.js"); + const detail = ( + event as CustomEvent<{ + annotation: { color?: string; style?: string }; + draw: (fn: unknown, options?: Record) => void; + }> + ).detail; + const style = detail.annotation.style || "highlight"; + const color = detail.annotation.color || "#facc15"; + const draw = + style === "underline" + ? Overlayer.underline + : style === "squiggly" + ? Overlayer.squiggly + : Overlayer.highlight; + detail.draw(draw, { color }); + }); + view.addEventListener("external-link", (event: Event) => { + // The vendored foliate view would open the link itself via + // globalThis.open(href, "_blank") with no noopener and no scheme + // filter (reverse tabnabbing, javascript: URLs). Cancel its default + // and open only safe schemes without an opener reference. + event.preventDefault(); + const href = (event as CustomEvent<{ href?: unknown }>).detail?.href; + if (typeof href !== "string") return; + let scheme = ""; + try { + scheme = new URL(href).protocol; + } catch { + return; + } + if (scheme === "http:" || scheme === "https:") { + window.open(href, "_blank", "noopener,noreferrer"); + } + }); + view.addEventListener("create-overlay", () => { + attachSelectionListeners(); + drawAnnotations(); + }); + view.addEventListener("relocate", (event: Event) => { + // Only the run that owns the live view may save progress for its file; + // a superseded view firing late relocates must not overwrite it. + if (!initializedRef.current || viewRef.current !== view) return; + const progress = progressFromRelocate( + (event as CustomEvent).detail, + file.file_id, + ); + if (progress) { + onProgressChange?.(progress.progress); + scheduleProgressSave(progress); + } + }); + await view.open(book); + if (cancelled) { + disposeOpenArtifacts(); + return; + } + onReady?.({ toc: book.toc ?? [] }); + applyReaderSettings(settingsRef.current); + attachSelectionListeners(); + drawAnnotations(); + const savedFileProgress = savedProgress?.file_id === file.file_id ? savedProgress : null; + const restoreTarget = restoreProgressTarget(savedFileProgress); + if (savedFileProgress && restoreTarget?.type === "location") { + onProgressChange?.(savedFileProgress.progress); + await view.init({ lastLocation: restoreTarget.location }); + } else if (savedFileProgress && restoreTarget?.type === "fraction") { + onProgressChange?.(savedFileProgress.progress); + await view.goToFraction(restoreTarget.fraction); + } else { + await view.goToFraction(0); + } + if (cancelled) { + disposeOpenArtifacts(); + return; + } + initializedRef.current = true; + setLoading(false); + } catch (err) { + if (cancelled) { + disposeOpenArtifacts(); + return; + } + setError(err instanceof Error ? err.message : "Unable to open ebook"); + setLoading(false); + } + } + + void open(); + const drawnCfis = drawnCfisRef.current; + document.addEventListener("visibilitychange", flushProgressOnVisibilityChange); + window.addEventListener("pagehide", flushProgressOnPageHide); + return () => { + cancelled = true; + initializedRef.current = false; + document.removeEventListener("visibilitychange", flushProgressOnVisibilityChange); + window.removeEventListener("pagehide", flushProgressOnPageHide); + if (saveTimerRef.current !== null) { + window.clearTimeout(saveTimerRef.current); + saveTimerRef.current = null; + } + flushProgress(); + disposeOpenArtifacts(); + appliedRendererKeyRef.current = ""; + for (const cleanup of selectionCleanupRef.current) cleanup(); + selectionCleanupRef.current = []; + drawnCfis.clear(); + onFileLoaded?.(null); + onProgressChange?.(null); + }; + }, [ + applyReaderSettings, + attachSelectionListeners, + contentID, + drawAnnotations, + file, + onFileLoaded, + onProgressChange, + onReady, + queryClient, + title, + ]); + + return ( +
+
+ {loading && !error && ( +
+ Loading reader... +
+ )} + {error && ( +
+ {error} +
+ )} +
+ ); + }, +); + +export default FoliateBookReader; diff --git a/web/src/reader/ebookReaderApi.ts b/web/src/reader/ebookReaderApi.ts new file mode 100644 index 00000000..a3e3932c --- /dev/null +++ b/web/src/reader/ebookReaderApi.ts @@ -0,0 +1,114 @@ +import { api, apiKeepalive } from "@/api/client"; + +export type EbookReaderConfigEnvelope = { + content_id?: string; + config?: Record; + updated_at?: string; +}; + +export type EbookReaderAnnotation = { + id: string; + content_id: string; + kind: "highlight" | "note" | "bookmark"; + cfi_range?: string; + location?: string; + selected_text: string; + note: string; + style: string; + color: string; + metadata?: Record; + created_at?: string; + updated_at?: string; +}; + +export type EbookReaderAnnotationInput = Partial< + Pick< + EbookReaderAnnotation, + "kind" | "cfi_range" | "location" | "selected_text" | "note" | "style" | "color" | "metadata" + > +>; + +export function ebookReaderConfigPath(contentID: string): string { + return `/ebooks/${encodeURIComponent(contentID)}/reader-config`; +} + +export function ebookReaderAnnotationsPath(contentID: string): string { + return `/ebooks/${encodeURIComponent(contentID)}/annotations`; +} + +export function ebookReaderAnnotationPath(contentID: string, annotationID: string): string { + return `${ebookReaderAnnotationsPath(contentID)}/${encodeURIComponent(annotationID)}`; +} + +export async function fetchEbookReaderConfig(contentID: string): Promise> { + const envelope = await api(ebookReaderConfigPath(contentID)); + return envelope.config && typeof envelope.config === "object" && !Array.isArray(envelope.config) + ? envelope.config + : {}; +} + +export async function saveEbookReaderConfig( + contentID: string, + config: Record, +): Promise> { + const envelope = await api(ebookReaderConfigPath(contentID), { + method: "PUT", + body: JSON.stringify({ config }), + }); + return envelope.config && typeof envelope.config === "object" && !Array.isArray(envelope.config) + ? envelope.config + : {}; +} + +/** + * Fire-and-forget config save for page unload (pagehide), when a normal + * authenticated request can no longer complete or refresh tokens. + */ +export function saveEbookReaderConfigKeepalive( + contentID: string, + config: Record, +): void { + apiKeepalive(ebookReaderConfigPath(contentID), { + method: "PUT", + body: JSON.stringify({ config }), + }); +} + +export async function fetchEbookReaderAnnotations( + contentID: string, +): Promise { + const envelope = await api<{ items?: EbookReaderAnnotation[] }>( + ebookReaderAnnotationsPath(contentID), + ); + return Array.isArray(envelope.items) ? envelope.items : []; +} + +export async function createEbookReaderAnnotation( + contentID: string, + annotation: EbookReaderAnnotationInput, +): Promise { + return api(ebookReaderAnnotationsPath(contentID), { + method: "POST", + body: JSON.stringify(annotation), + }); +} + +export async function updateEbookReaderAnnotation( + contentID: string, + annotationID: string, + annotation: EbookReaderAnnotationInput, +): Promise { + return api(ebookReaderAnnotationPath(contentID, annotationID), { + method: "PATCH", + body: JSON.stringify(annotation), + }); +} + +export async function deleteEbookReaderAnnotation( + contentID: string, + annotationID: string, +): Promise { + await api(ebookReaderAnnotationPath(contentID, annotationID), { + method: "DELETE", + }); +} diff --git a/web/src/reader/foliate-overlayer.d.ts b/web/src/reader/foliate-overlayer.d.ts new file mode 100644 index 00000000..3175f784 --- /dev/null +++ b/web/src/reader/foliate-overlayer.d.ts @@ -0,0 +1,7 @@ +declare module "foliate-js/overlayer.js" { + export const Overlayer: { + highlight: unknown; + squiggly: unknown; + underline: unknown; + }; +} diff --git a/web/src/reader/readest/libs/document.ts b/web/src/reader/readest/libs/document.ts new file mode 100644 index 00000000..9bd605f4 --- /dev/null +++ b/web/src/reader/readest/libs/document.ts @@ -0,0 +1,455 @@ +import * as epubcfi from "foliate-js/epubcfi.js"; + +export const CFI = epubcfi; + +export type BookFormat = + | "EPUB" + | "PDF" + | "MOBI" + | "AZW" + | "AZW3" + | "CBZ" + | "CBR" + | "FB2" + | "FBZ" + | "MD"; + +export type LanguageMap = Record; + +export type Identifier = { + scheme: string; + value: string; +}; + +export type Contributor = { + name: LanguageMap; +}; + +export type Collection = { + name: string; + position?: string; +}; + +export type Location = { + current: number; + next: number; + total: number; +}; + +export interface TOCItem { + id: number; + label: string; + href: string; + index: number; + cfi?: string; + location?: Location; + subitems?: TOCItem[]; +} + +export interface SectionFragment { + id: string; + href: string; + cfi: string; + size: number; + linear: string; + location?: Location; + fragments?: Array; +} + +export interface SectionItem { + id: string; + cfi: string; + size: number; + linear: string; + href?: string; + location?: Location; + pageSpread?: "left" | "right" | "center" | ""; + fragments?: Array; + loadText?: () => Promise; + createDocument: () => Promise; +} + +export type BookMetadata = { + title: string | LanguageMap; + author: string | Contributor; + language: string | string[]; + editor?: string; + publisher?: string; + published?: string; + description?: string; + subject?: string | string[] | Contributor; + identifier?: string; + isbn?: string; + altIdentifier?: string | string[] | Identifier; + belongsTo?: { + collection?: Array | Collection; + series?: Array | Collection; + }; + subtitle?: string; + series?: string; + seriesIndex?: number; + seriesTotal?: number; + coverImageFile?: string; + coverImageUrl?: string; + coverImageBlobUrl?: string; +}; + +export interface BookDoc { + metadata: BookMetadata; + rendition: { + layout?: "pre-paginated" | "reflowable"; + spread?: "auto" | "none"; + viewport?: { width: number; height: number }; + }; + dir: string; + toc?: Array; + sections: Array; + transformTarget?: EventTarget; + splitTOCHref(href: string): Array; + getCover(): Promise; +} + +export const EXTS: Record = { + EPUB: "epub", + PDF: "pdf", + MOBI: "mobi", + AZW: "azw", + AZW3: "azw3", + CBZ: "cbz", + CBR: "cbr", + FB2: "fb2", + FBZ: "fbz", + MD: "md", +}; + +export const MIMETYPES: Record = { + EPUB: ["application/epub+zip"], + PDF: ["application/pdf"], + MOBI: ["application/x-mobipocket-ebook"], + AZW: ["application/vnd.amazon.ebook"], + AZW3: ["application/vnd.amazon.mobi8-ebook", "application/x-mobi8-ebook"], + CBZ: ["application/vnd.comicbook+zip", "application/zip", "application/x-cbz"], + CBR: [ + "application/vnd.comicbook-rar", + "application/x-cbr", + "application/vnd.rar", + "application/x-rar-compressed", + ], + FB2: ["application/x-fictionbook+xml", "text/xml", "application/xml"], + FBZ: ["application/x-zip-compressed-fb2", "application/zip"], + MD: ["text/markdown", "text/x-markdown"], +}; + +export class DocumentLoader { + private file: File; + + constructor(file: File) { + this.file = file; + } + + private async isZip(): Promise { + const arr = new Uint8Array(await this.file.slice(0, 4).arrayBuffer()); + return arr[0] === 0x50 && arr[1] === 0x4b && arr[2] === 0x03; + } + + private async isPDF(): Promise { + const arr = new Uint8Array(await this.file.slice(0, 5).arrayBuffer()); + return ( + arr[0] === 0x25 && arr[1] === 0x50 && arr[2] === 0x44 && arr[3] === 0x46 && arr[4] === 0x2d + ); + } + + private async isRar(): Promise { + const arr = new Uint8Array(await this.file.slice(0, 8).arrayBuffer()); + return ( + arr[0] === 0x52 && + arr[1] === 0x61 && + arr[2] === 0x72 && + arr[3] === 0x21 && + arr[4] === 0x1a && + arr[5] === 0x07 && + (arr[6] === 0x00 || (arr[6] === 0x01 && arr[7] === 0x00)) + ); + } + + private async makeZipLoader() { + const { configure, ZipReader, BlobReader, TextWriter, BlobWriter } = + await import("@zip.js/zip.js"); + configure({ useWebWorkers: false, useCompressionStream: false }); + + type Entry = import("@zip.js/zip.js").Entry; + const reader = new ZipReader(new BlobReader(this.file)); + const entries = await reader.getEntries(); + const map = new Map(entries.map((entry) => [entry.filename, entry])); + const lowercaseMap = new Map(); + for (const entry of entries) { + const lowercaseName = entry.filename.toLowerCase(); + const existing = lowercaseMap.get(lowercaseName); + lowercaseMap.set( + lowercaseName, + existing && existing.filename !== entry.filename ? null : entry, + ); + } + const getEntry = (name: string) => + map.get(name) ?? lowercaseMap.get(name.toLowerCase()) ?? null; + const load = + (f: (entry: Entry, type?: string) => Promise | null) => + (name: string, ...args: [string?]) => { + const entry = getEntry(name); + return entry ? f(entry, ...args) : null; + }; + + const loadText = load((entry: Entry) => + !entry.directory ? entry.getData(new TextWriter()) : null, + ); + const loadBlob = load((entry: Entry, type?: string) => + !entry.directory ? entry.getData(new BlobWriter(type ?? "")) : null, + ); + const getSize = (name: string) => getEntry(name)?.uncompressedSize ?? 0; + const getComment = async (): Promise => null; + + return { + entries, + loadText, + loadBlob, + getSize, + getComment, + sha1: undefined, + }; + } + + private async makeRarLoader() { + const [{ createExtractorFromData }, wasmUrl] = await Promise.all([ + import("node-unrar-js/esm/index.esm"), + import("node-unrar-js/esm/js/unrar.wasm?url").then((module) => module.default), + ]); + const [data, wasmBinary] = await Promise.all([ + this.file.arrayBuffer(), + fetch(wasmUrl).then((response) => response.arrayBuffer()), + ]); + const extractor = await createExtractorFromData({ data, wasmBinary }); + const headers = [...extractor.getFileList().fileHeaders]; + const extracted = new Map(); + for (const file of extractor.extract().files) { + if (!file.fileHeader.flags.directory && file.extraction) { + extracted.set(file.fileHeader.name, file.extraction); + } + } + + const entries = headers.map((header) => ({ + directory: header.flags.directory, + filename: header.name, + uncompressedSize: header.unpSize, + })); + const lowercaseMap = new Map(); + for (const entry of entries) { + const lowercaseName = entry.filename.toLowerCase(); + const existing = lowercaseMap.get(lowercaseName); + lowercaseMap.set( + lowercaseName, + existing && existing !== entry.filename ? null : entry.filename, + ); + } + const resolveName = (name: string) => + extracted.has(name) ? name : lowercaseMap.get(name.toLowerCase()) || name; + const loadBlob = (name: string, type?: string) => { + const data = extracted.get(resolveName(name)); + return data ? new Blob([new Uint8Array(data)], { type: type ?? "" }) : null; + }; + const loadText = async (name: string) => { + const blob = loadBlob(name); + return blob ? blob.text() : null; + }; + const getSize = (name: string) => { + const resolved = resolveName(name); + return entries.find((entry) => entry.filename === resolved)?.uncompressedSize ?? 0; + }; + + return { + entries, + loadText, + loadBlob, + getSize, + getComment: async (): Promise => null, + sha1: undefined, + }; + } + + private isCBZ(): boolean { + return ( + this.file.type === "application/vnd.comicbook+zip" || + this.file.name.toLowerCase().endsWith(`.${EXTS.CBZ}`) + ); + } + + private isCBR(): boolean { + const name = this.file.name.toLowerCase(); + return ( + this.file.type === "application/vnd.comicbook-rar" || + this.file.type === "application/x-cbr" || + this.file.type === "application/vnd.rar" || + this.file.type === "application/x-rar-compressed" || + name.endsWith(`.${EXTS.CBR}`) || + name.endsWith(".rar") + ); + } + + private isFB2(): boolean { + return ( + this.file.type === "application/x-fictionbook+xml" || + this.file.name.toLowerCase().endsWith(`.${EXTS.FB2}`) + ); + } + + private isFBZ(): boolean { + const name = this.file.name.toLowerCase(); + return ( + this.file.type === "application/x-zip-compressed-fb2" || + name.endsWith(".fb.zip") || + name.endsWith(".fb2.zip") || + name.endsWith(`.${EXTS.FBZ}`) + ); + } + + private isPlainText(): boolean { + const name = this.file.name.toLowerCase(); + return ( + this.file.type === "text/markdown" || + this.file.type === "text/x-markdown" || + name.endsWith(`.${EXTS.MD}`) + ); + } + + private async makeTextBook(): Promise { + const text = await this.file.text(); + const title = this.file.name.replace(/\.[^.]+$/, "") || "Document"; + const createDocument = async () => { + const doc = document.implementation.createHTMLDocument(title); + const pre = doc.createElement("pre"); + pre.textContent = text; + pre.style.whiteSpace = "pre-wrap"; + pre.style.fontFamily = "inherit"; + pre.style.lineHeight = "inherit"; + doc.body.appendChild(pre); + return doc; + }; + return { + metadata: { + title, + author: "", + language: "", + }, + rendition: { + layout: "reflowable", + spread: "none", + }, + dir: "", + toc: [{ id: 0, label: title, href: "text", index: 0 }], + sections: [ + { + id: "text", + href: "text", + cfi: "", + size: text.length, + linear: "yes", + createDocument, + loadText: async () => text, + }, + ], + splitTOCHref: () => ["text"], + getCover: async () => null, + }; + } + + public async open(): Promise<{ book: BookDoc; format: BookFormat }> { + let book: BookDoc | null = null; + let format: BookFormat = "EPUB"; + if (!this.file.size) { + throw new Error("File is empty"); + } + try { + if (await this.isZip()) { + const loader = await this.makeZipLoader(); + const { entries } = loader; + + if (this.isCBZ()) { + const { makeComicBook } = await import("foliate-js/comic-book.js"); + book = (await makeComicBook(loader, this.file)) as BookDoc; + format = "CBZ"; + } else if (this.isFBZ()) { + const entry = entries.find((item) => + item.filename.toLowerCase().endsWith(`.${EXTS.FB2}`), + ); + const blob = await loader.loadBlob((entry ?? entries[0]!).filename); + const { makeFB2 } = await import("foliate-js/fb2.js"); + book = (await makeFB2(blob)) as BookDoc; + format = "FBZ"; + } else { + const { EPUB } = await import("foliate-js/epub.js"); + book = (await new EPUB(loader).init()) as BookDoc; + format = "EPUB"; + } + } else if ((await this.isRar()) || this.isCBR()) { + const loader = await this.makeRarLoader(); + const { makeComicBook } = await import("foliate-js/comic-book.js"); + book = (await makeComicBook(loader, this.file)) as BookDoc; + format = "CBR"; + } else if (await this.isPDF()) { + const { makePDF } = await import("foliate-js/pdf.js"); + book = (await makePDF(this.file)) as BookDoc; + format = "PDF"; + } else if (await (await import("foliate-js/mobi.js")).isMOBI(this.file)) { + const fflate = await import("foliate-js/vendor/fflate.js"); + const { MOBI } = await import("foliate-js/mobi.js"); + book = (await new MOBI({ unzlib: fflate.unzlibSync }).open(this.file)) as BookDoc; + const ext = this.file.name.split(".").pop()?.toLowerCase(); + switch (ext) { + case "azw": + format = "AZW"; + break; + case "azw3": + format = "AZW3"; + break; + default: + format = "MOBI"; + } + } else if (this.isFB2()) { + const { makeFB2 } = await import("foliate-js/fb2.js"); + book = (await makeFB2(this.file)) as BookDoc; + format = "FB2"; + } else if (this.isPlainText()) { + format = "MD"; + book = await this.makeTextBook(); + } + } catch (e: unknown) { + console.error("Failed to open document:", e); + if (e instanceof Error && e.message?.includes("not a valid zip")) { + throw new Error("Unsupported or corrupted book file"); + } + throw e; + } + if (!book) { + throw new Error("Unsupported book format"); + } + return { book, format }; + } +} + +export const getDirection = (doc: Document) => { + const { defaultView } = doc; + const computedStyle = defaultView!.getComputedStyle(doc.body); + const { direction } = computedStyle; + let { writingMode } = computedStyle; + if (!writingMode || writingMode === "horizontal-tb") { + const firstChild = doc.body.querySelector(":scope > :not([cfi-inert])"); + if (firstChild) { + const childStyle = defaultView!.getComputedStyle(firstChild); + if (childStyle.writingMode === "vertical-rl" || childStyle.writingMode === "vertical-lr") { + writingMode = childStyle.writingMode; + } + } + } + const vertical = writingMode === "vertical-rl" || writingMode === "vertical-lr"; + const rtl = doc.body.dir === "rtl" || direction === "rtl" || doc.documentElement.dir === "rtl"; + return { vertical, rtl }; +}; diff --git a/web/src/vite-env.d.ts b/web/src/vite-env.d.ts index 11f02fe2..cbf50ee8 100644 --- a/web/src/vite-env.d.ts +++ b/web/src/vite-env.d.ts @@ -1 +1,10 @@ /// + +declare module "foliate-js/view.js"; +declare module "foliate-js/epubcfi.js"; +declare module "foliate-js/comic-book.js"; +declare module "foliate-js/fb2.js"; +declare module "foliate-js/epub.js"; +declare module "foliate-js/pdf.js"; +declare module "foliate-js/mobi.js"; +declare module "foliate-js/vendor/fflate.js"; diff --git a/web/vendor/foliate-js/LICENSE b/web/vendor/foliate-js/LICENSE new file mode 100644 index 00000000..5930571b --- /dev/null +++ b/web/vendor/foliate-js/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 John Factotum + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/web/vendor/foliate-js/README.md b/web/vendor/foliate-js/README.md new file mode 100644 index 00000000..a3fe2d51 --- /dev/null +++ b/web/vendor/foliate-js/README.md @@ -0,0 +1,374 @@ +# foliate-js + +Library for rendering e-books in the browser. + +Features: +- Supports EPUB, MOBI, KF8 (AZW3), FB2, CBZ, PDF (experimental; requires PDF.js) +- Add support for other formats yourself by implementing the book interface +- Pure JavaScript +- Small and modular +- No hard dependencies +- Does not require loading whole file into memory +- Does not care about older browsers + +## Demo + +The repo includes a demo viewer that can be used to open local files. To use it, serve the files with a server, and navigate to `reader.html`. Or visit the [online demo](https://johnfactotum.github.io/foliate-js/reader.html) hosted on GitHub. Note that it is very incomplete at the moment, and lacks many basic features such as keyboard shortcuts. + +Also note that deobfuscating fonts with the IDPF algorithm requires a SHA-1 function. By default it uses Web Crypto, which is only available in secure contexts. Without HTTPS, you will need to modify `reader.js` and pass your own SHA-1 implementation. + +## Current Status + +It works reasonably well, and has been used in several stable releases of [Foliate](https://github.com/johnfactotum/foliate). This library itself is, however, *not* stable. Expect it to break and the API to change at any time. Use it at your own risk. + +If you do decide to use it, since there's no release yet, it is recommended that you include the library as a git submodule in your project so that you can easily update it. + +## Documentation + +### Overview + +This project uses native ES modules. There's no build step, and you can import them directly. + +There are mainly three kinds of modules: + +- Modules that parse and load books, implementing the "book" interface + - `comic-book.js`, for comic book archives (CBZ) + - `epub.js` and `epubcfi.js`, for EPUB + - `fb2.js`, for FictionBook 2 + - `mobi.js`, for both Mobipocket files and KF8 (commonly known as AZW3) files +- Modules that handle pagination, implementing the "renderer" interface + - `fixed-layout.js`, for fixed layout books + - `paginator.js`, for reflowable books +- Auxiliary modules used to add additional functionalities + - `overlayer.js`, for rendering annotations + - `progress.js`, for getting reading progress + - `search.js`, for searching + +The modules are designed to be modular. In general, they don't directly depend on each other. Instead they depend on certain interfaces, detailed below. The exception is `view.js`. It is the higher level renderer that strings most of the things together, and you can think of it as the main entry point of the library. See "Basic Usage" below. + +The repo also includes a still higher level reader, though strictly speaking, `reader.html` (along with `reader.js` and its associated files in `ui/`) is not considered part of the library itself. It's akin to [Epub.js Reader](https://github.com/futurepress/epubjs-reader). You are expected to modify it or replace it with your own code. + +### Basic Usage + +To get started, clone the repo and import `view.js`: + +```js +import './foliate-js/view.js' + +const view = document.createElement('foliate-view') +document.body.append(view) + +view.addEventListener('relocate', e => { + console.log('location changed') + console.log(e.detail) +}) + +// can open a File/Blob object or a URL +// or any object that implements the "book" interface +await view.open('example.epub') +await view.goTo(/* path, section index, or CFI */) +``` + +See the [online demo](https://johnfactotum.github.io/foliate-js/reader.html) for a more advanced example. + +### Security + +EPUB books can contain [scripted content](https://www.w3.org/TR/epub/#sec-scripted-content) (i.e. JavaScript in the e-book), which is potentially dangerous, and is not supported by this library because + +- It is currently impossible to do so securely due to the content being served from the same origin (using `blob:` URLs). +- Due to [WebKit Bug 218086](https://bugs.webkit.org/show_bug.cgi?id=218086), the `allow-scripts` attribute is required on iframes, which renders iframe sandbox useless. + +It is therefore imperative that you use [Content Security Policy (CSP)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) to block all scripts except `'self'`. An EPUB file for testing can be found at https://github.com/johnfactotum/epub-test. + +> [!CAUTION] +> Do NOT use this library (or any other e-book library, for that matter) without CSP unless you completely trust the content you're rendering or can block scripts by other means. + +### The Main Interface for Books + +Processors for each book format return an object that implements the following interface: +- `.sections`: an array of sections in the book. Each item has the following properties: + - `.load()`: returns a string containing the URL that will be rendered. May be async. + - `.unload()`: returns nothing. If present, can be used to free the section. + - `.createDocument()`: returns a `Document` object of the section. Used for searching. May be async. + - `.size`: a number, the byte size of the section. Used for showing reading progress. + - `.linear`: a string. If it is `"no"`, the section is not part of the linear reading sequence (see the [`linear`](https://www.w3.org/publishing/epub32/epub-packages.html#attrdef-itemref-linear) attribute in EPUB). + - `.cfi`: base CFI string of the section. The part that goes before the `!` in CFIs. + - `.id`: an identifier for the section, used for getting TOC item (see below). Can be anything, as long as they can be used as keys in a `Map`. +- `.dir`: a string representing the page progression direction of the book (`"rtl"` or `"ltr"`). +- `.toc`: an array representing the table of contents of the book. Each item has + - `.label`: a string label for the item + - `.href`: a string representing the destination of the item. Does not have to be a valid URL. + - `.subitems`: a array that contains TOC items +- `.pageList`: same as the TOC, but for the [page list](https://www.w3.org/publishing/epub32/epub-packages.html#sec-nav-pagelist). +- `.metadata`: an object representing the metadata of the book. Currently, it follows more or less the metadata schema of [Readium's webpub manifest](https://github.com/readium/webpub-manifest). Note that titles and names can be a string or an object like `{ ja: "草枕", en: 'Kusamakura' }`, and authors, etc. can be a string, an object, or an array of strings or objects. +- `.rendition`: an object that contains properties that correspond to the [rendition properties](https://www.w3.org/publishing/epub32/epub-packages.html#sec-package-metadata-rendering) in EPUB. If `.layout` is `"pre-paginated"`, the book is rendered with the fixed layout renderer. +- `.resolveHref(href)`: given an href string, returns an object representing the destination referenced by the href, which has the following properties: + - `.index`: the index of the referenced section in the `.section` array + - `.anchor(doc)`: given a `Document` object, returns the document fragment referred to by the href (can either be an `Element` or a `Range`), or `null` +- `.resolveCFI(cfi)`: same as above, but with a CFI string instead of href +- `.isExternal(href)`: returns a boolean. If `true`, the link should be opened externally. + +The following methods are consumed by `progress.js`, for getting the correct TOC and page list item when navigating: +- `.splitTOCHref(href)`: given an href string (from the TOC), returns an array, the first element of which is the `id` of the section (see above), and the second element is the fragment identifier (can be any type; see below). May be async. +- `.getTOCFragment(doc, id)`: given a `Document` object and a fragment identifier (the one provided by `.splitTOCHref()`; see above), returns a `Node` representing the target linked by the TOC item + +In addition, the `.transformTarget`, if present, can be used to transform the contents of the book as it loads. It is an `EventTarget` with a custom event `"data"`, whose `.detail` is `{ data, type, name }`, where `.data` is either a string or `Blob`, or a `Promise` thereof, `.type` the content type string, and `.name` the identifier of the resource. Event handlers should mutate `.data` to transform the data. + +Almost all of the properties and methods are optional. At minimum it needs `.sections` and the `.load()` method for the sections, as otherwise there won't be anything to render. + +### Archived Files + +Reading Zip-based formats requires adapting an external library. Both `epub.js` and `comic-book.js` expect a `loader` object that implements the following interface: + +- `.entries`: (only used by `comic-book.js`) an array, each element of which has a `filename` property, which is a string containing the filename (the full path). +- `.loadText(filename)`: given the path, returns the contents of the file as string. May be async. +- `.loadBlob(filename)`: given the path, returns the file as a `Blob` object. May be async. +- `.getSize(filename)`: returns the file size in bytes. Used to set the `.size` property for `.sections` (see above). + +In `view.js`, this is implemented using [zip.js](https://github.com/gildas-lormeau/zip.js), which is highly recommended because it seems to be the only library that supports random access for `File` objects (as well as HTTP range requests). + +One advantage of having such an interface is that one can easily use it for reading unarchived files as well. For example, `view.js` has a loader that allows you to open unpacked EPUBs as directories. + +### Mobipocket and Kindle Files + +It can read both MOBI and KF8 (.azw3, and combo .mobi files) from a `File` (or `Blob`) object. For MOBI files, it decompresses all text at once and splits the raw markup into sections at every ``, instead of outputting one long page for the whole book, which drastically improves rendering performance. For KF8 files, it tries to decompress as little text as possible when loading a section, but it can still be quite slow due to the slowness of the current HUFF/CDIC decompressor implementation. In all cases, images and other resources are not loaded until they are needed. + +Note that KF8 files can contain fonts that are zlib-compressed. They need to be decompressed with an external library. `view.js` uses [fflate](https://github.com/101arrowz/fflate) to decompress them. + +### PDF and Other Fixed-Layout Formats + +There is a proof-of-concept, highly experimental adapter for [PDF.js](https://mozilla.github.io/pdf.js/), with which you can show PDFs using the same fixed-layout renderer for EPUBs. + +CBZs are similarly handled like fixed-layout EPUBs. + +### The Renderers + +To simplify things, it has two separate renderers, one for reflowable books, and one for fixed layout books (as such there's no support for mixed layout books). These renderers are custom elements (web components). + +A renderer's interface is currently mainly: +- `.open(book)`: open a book object. +- `.goTo({ index, anchor })`: navigate to a destination. The argument has the same type as the one returned by `.resolveHref()` in the book object. +- `.prev()`: go to previous page. +- `.next()`: go to next page. + +It has the following custom events: +- `load`, when a section is loaded. Its `event.detail` has two properties, `doc`, the `Document` object, and `index`, the index of the section. +- `relocate`, when the location changes. Its `event.detail` has the properties `range`, `index`, and `fraction`, where `range` is a `Range` object containing the current visible area, and `fraction` is a number between 0 and 1, representing the reading progress within the section. +- `create-overlayer`, which allows adding an overlay to the page. The `event.detail` has the properties `doc`, `index`, and a function `attach(overlay)`, which should be called with an overlayer object (see the description for `overlayer.js` below). + +Both renderers have the [`part`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/part) named `filter`, which you can apply CSS filters to, to e.g. invert colors or adjust brightness: + +```css +foliate-view::part(filter) { + filter: invert(1) hue-rotate(180deg); +} +``` + +The filter only applies to the book itself, leaving overlaid elements such as highlights unaffected. + +### The Paginator + +The paginator uses the same pagination strategy as [Epub.js](https://github.com/futurepress/epub.js): it uses CSS multi-column. As such it shares much of the same limitations (it's slow, some CSS styles do not work as expected, and other bugs). There are a few differences: +- It is a totally standalone module. You can use it to paginate any content. +- It is much simpler, but currently there's no support for continuous scrolling. +- It has no concept of CFIs and operates on `Range` objects directly. +- It uses bisecting to find the current visible range, which is more accurate than what Epub.js does. +- It has an internal `#anchor` property, which can be a `Range`, `Element`, or a fraction that represents the current location. The view is *anchored* to it no matter how you resize the window. +- It supports more than two columns. +- It supports switching between scrolled and paginated mode without reloading (I can't figure out how to do this in Epub.js). + +The layout can be configured by setting the following attributes: +- `animated`: a [boolean attribute](https://developer.mozilla.org/en-US/docs/Glossary/Boolean/HTML). If present, adds a sliding transition effect. +- `flow`: either `paginated` or `scrolled`. +- `margin`: a CSS ``. The unit must be `px`. The height of the header and footer. +- `gap`: a CSS ``. The size of the space between columns, relative to page size. +- `max-inline-size`: a CSS ``. The unit must be `px`. The maximum inline size of the text (column width in paginated mode). +- `max-block-size`: same as above, but for the size in the block direction. +- `max-column-count`: integer. The maximum number of columns. Has no effect in scrolled mode, or when the orientation of the renderer element is `portrait` (or, for vertical writing, `landscape`). + +(Note: there's no JS property API. You must use `.setAttribute()`.) + +It has built-in header and footer regions accessible via the `.heads` and `.feet` properties of the paginator instance. These can be used to display running heads and reading progress. They are only available in paginated mode, and there will be one element for each column. They are styleable with `::part(head)` and `::part(foot)`. E.g., to add a border under the running heads, + +```css +foliate-view::part(head) { + padding-bottom: 4px; + border-bottom: 1px solid graytext; +} +``` + +### EPUB CFI + +Parsed CFIs are represented as a plain array or object. The basic type is called a "part", which is an object with the following structure: `{ index, id, offset, temporal, spatial, text, side }`, corresponding to a step + offset in the CFI. + +A collapsed, non-range CFI is represented as an array whose elements are arrays of parts, each corresponding to a full path. That is, `/6/4!/4` is turned into + +```json +[ + [ + { "index": 6 }, + { "index": 4 } + ], + [ + { "index": 4 } + ] +] +``` + +A range CFI is an object `{ parent, start, end }`, each property being the same type as a collapsed CFI. For example, `/6/4!/2,/2,/4` is represented as + +```json +{ + "parent": [ + [ + { "index": 6 }, + { "index": 4 } + ], + [ + { "index": 2 } + ] + ], + "start": [ + [ + { "index": 2 } + ] + ], + "end": [ + [ + { "index": 4 } + ] + ] +} +``` + +The parser uses a state machine rather than regex, and should handle assertions that contain escaped characters correctly (see tests for examples of this). + +It has the ability ignore nodes, which is needed if you want to inject your own nodes into the document without affecting CFIs. To do this, you need to pass the optional filter function that works similarly to the filter function of [`TreeWalker`s](https://developer.mozilla.org/en-US/docs/Web/API/Document/createTreeWalker): + +```js +const filter = node => node.nodeType !== 1 ? NodeFilter.FILTER_ACCEPT + : node.matches('.reject') ? NodeFilter.FILTER_REJECT + : node.matches('.skip') ? NodeFilter.FILTER_SKIP + : NodeFilter.FILTER_ACCEPT + +CFI.toRange(doc, 'epubcfi(...)', filter) +CFI.fromRange(range, filter) +``` + +It can parse and stringify spatial and temporal offsets, as well as text location assertions and side bias, but there's no support for employing them when rendering yet. + +### Highlighting Text + +There is a generic module for overlaying arbitrary SVG elements, `overlayer.js`. It can be used to implement highlighting text for annotations. It's the same technique used by [marks-pane](https://github.com/fchasen/marks), used by Epub.js, but it's designed to be easily extensible. You can return any SVG element in the `draw` function, making it possible to add custom styles such as squiggly lines or even free hand drawings. + +The overlay has no event listeners by default. It only provides a `.hitTest(event)` method, that can be used to do hit tests. Currently it does this with the client rects of `Range`s, not the element returned by `draw()`. + +An overlayer object implements the following interface for the consumption of renderers: +- `.element`: the DOM element of the overlayer. This element will be inserted, resized, and positioned automatically by the renderer on top of the page. +- `.redraw()`: called by the renderer when the overlay needs to be redrawn. + +### The Text Walker + +Not a particularly descriptive name, but essentially, `text-walker.js` is a small DOM utility that allows you to + +1. Gather all text nodes in a `Range`, `Document` or `DocumentFragment` into an array of strings. +2. Perform splitting or matching on the strings. +3. Get back the results of these string operations as `Range`s. + +E.g. you can join all the text nodes together, use `Intl.Segmenter` to segment the string into words, and get the results in DOM Ranges, so you can mark up those words in the original document. + +In foliate-js, this is used for searching and TTS. + +### Searching + +It provides a search module, which can in fact be used as a standalone module for searching across any array of strings. There's no limit on the number of strings a match is allowed to span. It's based on `Intl.Collator` and `Intl.Segmenter`, to support ignoring diacritics and matching whole words only. It's extrenely slow, and you'd probably want to load results incrementally. + +### Text-to-Speech (TTS) + +The TTS module doesn't directly handle speech output. Rather, its methods return SSML documents (as strings), which you can then feed to your speech synthesizer. + +The SSML attributes `ssml:ph` and `ssml:alphabet` are supported. There's no support for PLS and CSS Speech. + +### Offline Dictionaries + +The `dict.js` module can be used to load dictd and StarDict dictionaries. Usage: + +```js +import { StarDict } from './dict.js' +import { inflate } from 'your inflate implementation' + +const { ifo, dz, idx, syn } = { /* `File` (or `Blob`) objects */ } +const dict = new StarDict() +await dict.loadIfo(ifo) +await dict.loadDict(dz, inflate) +await dict.loadIdx(idx) +await dict.loadSyn(syn) + +// look up words +const query = '...' +await dictionary.lookup(query) +await dictionary.synonyms(query) +``` + +Note that you must supply your own `inflate` function. Here is an example using [fflate](https://github.com/101arrowz/fflate): +```js +const inflate = data => new Promise(resolve => { + const inflate = new fflate.Inflate() + inflate.ondata = data => resolve(data) + inflate.push(data) +}) +``` + +### OPDS + +The `opds.js` module can be used to implement OPDS clients. It can convert OPDS 1.x documents to OPDS 2.0: + +- `getFeed(doc)`: converts an OPDS 1.x feed to OPDS 2.0. The argument must be a DOM Document object. You need to use a `DOMParser` to obtain a Document first if you have a string. +- `getPublication(entry)`: converts a OPDS 1.x entry in acquisition feeds to an OPDS 2.0 publication. The argument must be a DOM Element object. + +It exports the following symbols for properties unsupported by OPDS 2.0: +- `SYMBOL.SUMMARY`: used on navigation links to represent the summary/content (see https://github.com/opds-community/drafts/issues/51) +- `SYMBOL.CONTENT`: used on publications to represent the content/description and its type. This is mainly for preserving the type info for XHTML. The value of this property is an object whose properties are: + - `.type`: either "text", "html", or "xhtml" + - `.value`: the value of the content + +There are also two functions that can be used to implement search forms: + +- `getOpenSearch(doc)`: for OpenSearch. The argument is a DOM Document object of an OpenSearch search document. +- `getSearch(link)` for templated search in OPDS 2.0. The argument must be an OPDS 2.0 Link object. Note that this function will import `uri-template.js`. + +These two functions return an object that implements the following interface: +- `.metadata`: an object with the string properties `title` and `description` +- `.params`: an array, representing the search parameters, whose elements are objects whose properties are + - `ns`: a string; the namespace of the parameter + - `name`: a string; the name of the parameter + - `required`: a boolean, whether the parameter is required + - `value`: a string; the default value of the parameter +- `.search(map)`: a function, whose argument is a `Map` whose values are `Map`s (i.e. a two-dimensional map). The first key is the namespace of the search parameter. For non-namespaced parameters, the first key must be `null`. The second key is the parameter's name. Returns a string representing the URL of the search results. + +### Generating Images for Quotes + +With `quote-image.js`, one can generate shareable images for quotes: + +```js +document.querySelector('foliate-quoteimage').getBlob({ + title: 'The Time Machine', + author: 'H. G. Wells', + text: 'Can an instantaneous cube exist?', +}) +``` + +### Supported Browsers + +It aims to support the latest version of WebKitGTK, Firefox, and Chromium. Older browsers like Firefox ESR are not supported. + +Although it's mainly indeded for rendering e-books in the browser, some features can be used in non-browser environments as well. In particular, `epubcfi.js` can be used as is in any environment if you only need to parse or sort CFIs. Most other features depend on having the global objects `Blob`, `TextDecoder`, `TextEncoder`, `DOMParser`, `XMLSerializer`, and `URL`, and should work if you polyfill them. + +## License + +MIT. + +Vendored libraries: +- [zip.js](https://github.com/gildas-lormeau/zip.js) is licensed under the BSD-3-Clause license. +- [fflate](https://github.com/101arrowz/fflate) is MIT licensed. +- [PDF.js](https://mozilla.github.io/pdf.js/) is licensed under Apache. diff --git a/web/vendor/foliate-js/comic-book.js b/web/vendor/foliate-js/comic-book.js new file mode 100644 index 00000000..924ee2bd --- /dev/null +++ b/web/vendor/foliate-js/comic-book.js @@ -0,0 +1,114 @@ +// Read series metadata from a ComicInfo.xml entry, if present. +// Spec: https://anansi-project.github.io/docs/comicinfo/intro +const readComicInfoXML = async ({ entries, loadBlob }) => { + const entry = entries.find(e => e.filename.toLowerCase() === 'comicinfo.xml') + if (!entry) return null + let text + try { + text = await (await loadBlob(entry.filename)).text() + } catch { + return null + } + let doc + try { + doc = new DOMParser().parseFromString(text, 'application/xml') + } catch { + return null + } + if (!doc || doc.getElementsByTagName('parsererror').length) return null + const get = name => doc.getElementsByTagName(name).item(0)?.textContent?.trim() || undefined + return { + title: get('Title'), + publisher: get('Publisher'), + language: get('LanguageISO'), + author: get('Writer'), + series: get('Series'), + seriesPosition: get('Number'), + seriesTotal: get('Count'), + } +} + +const readComicBookInfo = async ({ getComment }) => { + let info + try { + info = JSON.parse(await getComment() || '')['ComicBookInfo/1.0'] + } catch { + return null + } + if (!info) return null + const year = info.publicationYear + const month = info.publicationMonth + const mm = month && month >= 1 && month <= 12 ? String(month).padStart(2, '0') : null + return { + title: info.title, + publisher: info.publisher, + language: info.language || info.lang, + author: info.credits ? info.credits.map(c => `${c.person} (${c.role})`).join(', ') : '', + published: year && month ? `${year}-${mm}` : undefined, + series: info.series, + seriesPosition: info.issue == null ? undefined : String(info.issue), + } +} + +export const makeComicBook = async ({ entries, loadBlob, getSize, getComment }, file) => { + const cache = new Map() + const urls = new Map() + const load = async name => { + if (cache.has(name)) return cache.get(name) + const src = URL.createObjectURL(await loadBlob(name)) + const page = URL.createObjectURL( + new Blob([``], { type: 'text/html' })) + urls.set(name, [src, page]) + cache.set(name, page) + return page + } + const unload = name => { + urls.get(name)?.forEach?.(url => URL.revokeObjectURL(url)) + urls.delete(name) + cache.delete(name) + } + + const exts = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp', '.svg', '.jxl', '.avif'] + const files = entries + .map(entry => entry.filename) + .filter(name => exts.some(ext => name.endsWith(ext))) + .sort() + if (!files.length) throw new Error('No supported image files in archive') + + const book = {} + // Prefer ComicInfo.xml (Anansi standard) over ComicBookInfo (JSON in zip comment). + // Fields missing from the preferred source fall through to the secondary one. + const xml = await readComicInfoXML({ entries, loadBlob }) + const cbi = await readComicBookInfo({ getComment }) + const merged = { ...(cbi || {}), ...(xml || {}) } + book.metadata = { + title: merged.title || file.name, + publisher: merged.publisher, + language: merged.language, + author: merged.author, + published: merged.published, + } + if (merged.series) { + const series = { name: merged.series } + if (merged.seriesPosition) series.position = merged.seriesPosition + if (merged.seriesTotal) series.total = merged.seriesTotal + book.metadata.belongsTo = { series } + } + book.getCover = () => loadBlob(files[0]) + book.sections = files.map(name => ({ + id: name, + load: () => load(name), + unload: () => unload(name), + size: getSize(name), + })) + book.toc = files.map(name => ({ label: name, href: name })) + book.rendition = { layout: 'pre-paginated' } + book.resolveHref = href => ({ index: book.sections.findIndex(s => s.id === href) }) + book.splitTOCHref = href => [href, null] + book.getTOCFragment = doc => doc.documentElement + book.destroy = () => { + for (const arr of urls.values()) + for (const url of arr) URL.revokeObjectURL(url) + } + return book +} diff --git a/web/vendor/foliate-js/dict.js b/web/vendor/foliate-js/dict.js new file mode 100644 index 00000000..9afa7ea1 --- /dev/null +++ b/web/vendor/foliate-js/dict.js @@ -0,0 +1,241 @@ +const decoder = new TextDecoder() +const decode = decoder.decode.bind(decoder) + +const concatTypedArray = (a, b) => { + const result = new a.constructor(a.length + b.length) + result.set(a) + result.set(b, a.length) + return result +} + +const strcmp = (a, b) => { + a = a.toLowerCase(), b = b.toLowerCase() + return a < b ? -1 : a > b ? 1 : 0 +} + +class DictZip { + #chlen + #chunks + #compressed + inflate + async load(file) { + const header = new DataView(await file.slice(0, 12).arrayBuffer()) + if (header.getUint8(0) !== 31 || header.getUint8(1) !== 139 + || header.getUint8(2) !== 8) throw new Error('Not a DictZip file') + const flg = header.getUint8(3) + if (!flg & 0b100) throw new Error('Missing FEXTRA flag') + + const xlen = header.getUint16(10, true) + const extra = new DataView(await file.slice(12, 12 + xlen).arrayBuffer()) + if (extra.getUint8(0) !== 82 || extra.getUint8(1) !== 65) + throw new Error('Subfield ID should be RA') + if (extra.getUint16(4, true) !== 1) throw new Error('Unsupported version') + + this.#chlen = extra.getUint16(6, true) + const chcnt = extra.getUint16(8, true) + this.#chunks = [] + for (let i = 0, chunkOffset = 0; i < chcnt; i++) { + const chunkSize = extra.getUint16(10 + 2 * i, true) + this.#chunks.push([chunkOffset, chunkSize]) + chunkOffset = chunkOffset + chunkSize + } + + // skip to compressed data + let offset = 12 + xlen + const max = Math.min(offset + 512, file.size) + const strArr = new Uint8Array(await file.slice(0, max).arrayBuffer()) + if (flg & 0b1000) { // fname + const i = strArr.indexOf(0, offset) + if (i < 0) throw new Error('Header too long') + offset = i + 1 + } + if (flg & 0b10000) { // fcomment + const i = strArr.indexOf(0, offset) + if (i < 0) throw new Error('Header too long') + offset = i + 1 + } + if (flg & 0b10) offset += 2 // fhcrc + this.#compressed = file.slice(offset) + } + async read(offset, size) { + const chunks = this.#chunks + const startIndex = Math.trunc(offset / this.#chlen) + const endIndex = Math.trunc((offset + size) / this.#chlen) + const buf = await this.#compressed.slice(chunks[startIndex][0], + chunks[endIndex][0] + chunks[endIndex][1]).arrayBuffer() + let arr = new Uint8Array() + for (let pos = 0, i = startIndex; i <= endIndex; i++) { + const data = new Uint8Array(buf, pos, chunks[i][1]) + arr = concatTypedArray(arr, await this.inflate(data)) + pos += chunks[i][1] + } + const startOffset = offset - startIndex * this.#chlen + return arr.subarray(startOffset, startOffset + size) + } +} + +class Index { + strcmp = strcmp + // binary search + bisect(query, start = 0, end = this.words.length - 1) { + if (end - start === 1) { + if (!this.strcmp(query, this.getWord(start))) return start + if (!this.strcmp(query, this.getWord(end))) return end + return null + } + const mid = Math.floor(start + (end - start) / 2) + const cmp = this.strcmp(query, this.getWord(mid)) + if (cmp < 0) return this.bisect(query, start, mid) + if (cmp > 0) return this.bisect(query, mid, end) + return mid + } + // check for multiple definitions + checkAdjacent(query, i) { + if (i == null) return [] + let j = i + const equals = i => { + const word = this.getWord(i) + return word ? this.strcmp(query, word) === 0 : false + } + while (equals(j - 1)) j-- + let k = i + while (equals(k + 1)) k++ + return j === k ? [i] : Array.from({ length: k + 1 - j }, (_, i) => j + i) + } + lookup(query) { + return this.checkAdjacent(query, this.bisect(query)) + } +} + +const decodeBase64Number = str => { + const { length } = str + let n = 0 + for (let i = 0; i < length; i++) { + const c = str.charCodeAt(i) + n += (c === 43 ? 62 // "+" + : c === 47 ? 63 // "/" + : c < 58 ? c + 4 // 0-9 -> 52-61 + : c < 91 ? c - 65 // A-Z -> 0-25 + : c - 71 // a-z -> 26-51 + ) * 64 ** (length - 1 - i) + } + return n +} + +class DictdIndex extends Index { + getWord(i) { + return this.words[i] + } + async load(file) { + const words = [] + const offsets = [] + const sizes = [] + for (const line of decode(await file.arrayBuffer()).split('\n')) { + const a = line.split('\t') + words.push(a[0]) + offsets.push(decodeBase64Number(a[1])) + sizes.push(decodeBase64Number(a[2])) + } + this.words = words + this.offsets = offsets + this.sizes = sizes + } +} + +export class DictdDict { + #dict = new DictZip() + #idx = new DictdIndex() + loadDict(file, inflate) { + this.#dict.inflate = inflate + return this.#dict.load(file) + } + async #readWord(i) { + const word = this.#idx.getWord(i) + const offset = this.#idx.offsets[i] + const size = this.#idx.sizes[i] + return { word, data: ['m', this.#dict.read(offset, size)] } + } + #readWords(arr) { + return Promise.all(arr.map(this.#readWord.bind(this))) + } + lookup(query) { + return this.#readWords(this.#idx.lookup(query)) + } +} + +class StarDictIndex extends Index { + isSyn + #arr + getWord(i) { + const word = this.words[i] + if (!word) return + return decode(this.#arr.subarray(word[0], word[1])) + } + async load(file) { + const { isSyn } = this + const buf = await file.arrayBuffer() + const arr = new Uint8Array(buf) + this.#arr = arr + const view = new DataView(buf) + const words = [] + const offsets = [] + const sizes = [] + for (let i = 0; i < arr.length;) { + const newI = arr.subarray(0, i + 256).indexOf(0, i) + if (newI < 0) throw new Error('Word too big') + words.push([i, newI]) + offsets.push(view.getUint32(newI + 1)) + if (isSyn) i = newI + 5 + else { + sizes.push(view.getUint32(newI + 5)) + i = newI + 9 + } + } + this.words = words + this.offsets = offsets + this.sizes = sizes + } +} + +export class StarDict { + #dict = new DictZip() + #idx = new StarDictIndex() + #syn = Object.assign(new StarDictIndex(), { isSyn: true }) + async loadIfo(file) { + const str = decode(await file.arrayBuffer()) + this.ifo = Object.fromEntries(str.split('\n').map(line => { + const sep = line.indexOf('=') + if (sep < 0) return + return [line.slice(0, sep), line.slice(sep + 1)] + }).filter(x => x)) + } + loadDict(file, inflate) { + this.#dict.inflate = inflate + return this.#dict.load(file) + } + loadIdx(file) { + return this.#idx.load(file) + } + loadSyn(file) { + if (file) return this.#syn.load(file) + } + async #readWord(i) { + const word = this.#idx.getWord(i) + const offset = this.#idx.offsets[i] + const size = this.#idx.sizes[i] + const data = await this.#dict.read(offset, size) + const seq = this.ifo.sametypesequence + if (!seq) throw new Error('TODO') + if (seq.length === 1) return { word, data: [[seq[0], data]] } + throw new Error('TODO') + } + #readWords(arr) { + return Promise.all(arr.map(this.#readWord.bind(this))) + } + lookup(query) { + return this.#readWords(this.#idx.lookup(query)) + } + synonyms(query) { + return this.#readWords(this.#syn.lookup(query).map(i => this.#syn.offsets[i])) + } +} diff --git a/web/vendor/foliate-js/epub.js b/web/vendor/foliate-js/epub.js new file mode 100644 index 00000000..e12f12ed --- /dev/null +++ b/web/vendor/foliate-js/epub.js @@ -0,0 +1,1223 @@ +import * as CFI from './epubcfi.js' + +const NS = { + CONTAINER: 'urn:oasis:names:tc:opendocument:xmlns:container', + XHTML: 'http://www.w3.org/1999/xhtml', + OPF: 'http://www.idpf.org/2007/opf', + EPUB: 'http://www.idpf.org/2007/ops', + DC: 'http://purl.org/dc/elements/1.1/', + DCTERMS: 'http://purl.org/dc/terms/', + ENC: 'http://www.w3.org/2001/04/xmlenc#', + NCX: 'http://www.daisy.org/z3986/2005/ncx/', + XLINK: 'http://www.w3.org/1999/xlink', + SMIL: 'http://www.w3.org/ns/SMIL', +} + +const MIME = { + XML: 'application/xml', + NCX: 'application/x-dtbncx+xml', + XHTML: 'application/xhtml+xml', + HTML: 'text/html', + CSS: 'text/css', + SVG: 'image/svg+xml', + JS: /\/(x-)?(javascript|ecmascript)/, +} + +// https://www.w3.org/TR/epub-33/#sec-reserved-prefixes +const PREFIX = { + a11y: 'http://www.idpf.org/epub/vocab/package/a11y/#', + dcterms: 'http://purl.org/dc/terms/', + marc: 'http://id.loc.gov/vocabulary/', + media: 'http://www.idpf.org/epub/vocab/overlays/#', + onix: 'http://www.editeur.org/ONIX/book/codelists/current.html#', + rendition: 'http://www.idpf.org/vocab/rendition/#', + schema: 'http://schema.org/', + xsd: 'http://www.w3.org/2001/XMLSchema#', + msv: 'http://www.idpf.org/epub/vocab/structure/magazine/#', + prism: 'http://www.prismstandard.org/specifications/3.0/PRISM_CV_Spec_3.0.htm#', +} + +const RELATORS = { + art: 'artist', + aut: 'author', + clr: 'colorist', + edt: 'editor', + ill: 'illustrator', + nrt: 'narrator', + trl: 'translator', + pbl: 'publisher', +} + +const ONIX5 = { + '02': 'isbn', + '06': 'doi', + '15': 'isbn', + '26': 'doi', + '34': 'issn', +} + +// convert to camel case +const camel = x => x.toLowerCase().replace(/[-:](.)/g, (_, g) => g.toUpperCase()) + +// strip and collapse ASCII whitespace +// https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace +const normalizeWhitespace = str => str ? str + .replace(/[\t\n\f\r ]+/g, ' ') + .replace(/^[\t\n\f\r ]+/, '') + .replace(/[\t\n\f\r ]+$/, '') : '' + +const filterAttribute = (attr, value, isList) => isList + ? el => el.getAttribute(attr)?.split(/\s/)?.includes(value) + : typeof value === 'function' + ? el => value(el.getAttribute(attr)) + : el => el.getAttribute(attr) === value + +const getAttributes = (...xs) => el => + el ? Object.fromEntries(xs.map(x => [camel(x), el.getAttribute(x)])) : null + +const getElementText = el => normalizeWhitespace(el?.textContent) + +const childGetter = (doc, ns) => { + // ignore the namespace if it doesn't appear in document at all + const useNS = doc.lookupNamespaceURI(null) === ns || doc.lookupPrefix(ns) + const f = useNS + ? (el, name) => el => el.namespaceURI === ns && el.localName === name + : (el, name) => el => el.localName === name + return { + $: (el, name) => [...el.children].find(f(el, name)), + $$: (el, name) => [...el.children].filter(f(el, name)), + $$$: useNS + ? (el, name) => [...el.getElementsByTagNameNS(ns, name)] + : (el, name) => [...el.getElementsByTagName(name)], + } +} + +const resolveURL = (url, relativeTo) => { + try { + // replace %2c in the url with a comma, this might be introduced by calibre + url = url.replace(/%2c/gi, ',').replace(/%3a/gi, ':') + if (relativeTo.includes(':') && !relativeTo.startsWith('OEBPS')) return new URL(url, relativeTo) + // the base needs to be a valid URL, so set a base URL and then remove it + const root = 'https://invalid.invalid/' + const obj = new URL(url, root + relativeTo) + obj.search = '' + return decodeURI(obj.href.replace(root, '')) + } catch(e) { + console.warn(e) + return url + } +} + +const isExternal = uri => /^(?!blob)\w+:/i.test(uri) + +// like `path.relative()` in Node.js +const pathRelative = (from, to) => { + if (!from) return to + const as = from.replace(/\/$/, '').split('/') + const bs = to.replace(/\/$/, '').split('/') + const i = (as.length > bs.length ? as : bs).findIndex((_, i) => as[i] !== bs[i]) + return i < 0 ? '' : Array(as.length - i).fill('..').concat(bs.slice(i)).join('/') +} + +const pathDirname = str => str.slice(0, str.lastIndexOf('/') + 1) + +// replace asynchronously and sequentially +// same technique as https://stackoverflow.com/a/48032528 +const replaceSeries = async (str, regex, f) => { + const matches = [] + str.replace(regex, (...args) => (matches.push(args), null)) + const results = [] + for (const args of matches) results.push(await f(...args)) + return str.replace(regex, () => results.shift()) +} + +const regexEscape = str => str.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&') + +const tidy = obj => { + for (const [key, val] of Object.entries(obj)) + if (val == null) delete obj[key] + else if (Array.isArray(val)) { + obj[key] = val.filter(x => x).map(x => + typeof x === 'object' && !Array.isArray(x) ? tidy(x) : x) + if (!obj[key].length) delete obj[key] + else if (obj[key].length === 1) obj[key] = obj[key][0] + } + else if (typeof val === 'object') { + obj[key] = tidy(val) + if (!Object.keys(val).length) delete obj[key] + } + const keys = Object.keys(obj) + if (keys.length === 1 && keys[0] === 'name') return obj[keys[0]] + return obj +} + +// https://www.w3.org/TR/epub/#sec-prefix-attr +const getPrefixes = doc => { + const map = new Map(Object.entries(PREFIX)) + const value = doc.documentElement.getAttributeNS(NS.EPUB, 'prefix') + || doc.documentElement.getAttribute('prefix') + if (value) for (const [, prefix, url] of value + .matchAll(/(.+): +(.+)[ \t\r\n]*/g)) map.set(prefix, url) + return map +} + +// https://www.w3.org/TR/epub-rs/#sec-property-values +// but ignoring the case where the prefix is omitted +const getPropertyURL = (value, prefixes) => { + if (!value) return null + const [a, b] = value.split(':') + const prefix = b ? a : null + const reference = b ? b : a + const baseURL = prefixes.get(prefix) + return baseURL ? baseURL + reference : null +} + +const getMetadata = opf => { + const { $ } = childGetter(opf, NS.OPF) + const $metadata = $(opf.documentElement, 'metadata') + + // first pass: convert to JS objects + const els = Object.groupBy($metadata.children, el => + el.namespaceURI === NS.DC ? 'dc' + : el.namespaceURI === NS.OPF && el.localName === 'meta' ? + (el.hasAttribute('name') ? 'legacyMeta' : 'meta') : '') + const baseLang = $metadata.getAttribute('xml:lang') + ?? opf.documentElement.getAttribute('xml:lang') ?? 'und' + const prefixes = getPrefixes(opf) + const parse = el => { + const property = el.getAttribute('property') + const scheme = el.getAttribute('scheme') + return { + property: getPropertyURL(property, prefixes) ?? property, + scheme: getPropertyURL(scheme, prefixes) ?? scheme, + lang: el.getAttribute('xml:lang'), + value: getElementText(el), + props: getProperties(el), + // `opf:` attributes from EPUB 2 & EPUB 3.1 (removed in EPUB 3.2) + attrs: Object.fromEntries(Array.from(el.attributes) + .filter(attr => attr.namespaceURI === NS.OPF) + .map(attr => [attr.localName, attr.value])), + } + } + const refines = Map.groupBy(els.meta ?? [], el => el.getAttribute('refines')) + const getProperties = el => { + const els = refines.get(el ? '#' + el.getAttribute('id') : null) + if (!els) return null + return Object.groupBy(els.map(parse), x => x.property) + } + const dc = Object.fromEntries(Object.entries(Object.groupBy(els.dc || [], el => el.localName)) + .map(([name, els]) => [name, els.map(parse)])) + const properties = getProperties() ?? {} + const legacyMeta = Object.fromEntries(els.legacyMeta?.map(el => + [el.getAttribute('name'), el.getAttribute('content')]) ?? []) + + // second pass: map to webpub + const one = x => x?.[0]?.value + const prop = (x, p) => one(x?.props?.[p]) + const makeLanguageMap = x => { + if (!x) return null + const alts = x.props?.['alternate-script'] ?? [] + const altRep = x.attrs['alt-rep'] + if (!alts.length && (!x.lang || x.lang === baseLang) && !altRep) return x.value + const map = { [x.lang ?? baseLang]: x.value } + if (altRep) map[x.attrs['alt-rep-lang']] = altRep + for (const y of alts) map[y.lang] ??= y.value + return map + } + const makeContributor = x => x ? ({ + name: makeLanguageMap(x), + sortAs: makeLanguageMap(x.props?.['file-as']?.[0]) ?? x.attrs['file-as'], + role: x.props?.role?.filter(x => x.scheme === PREFIX.marc + 'relators') + ?.map(x => x.value) ?? [x.attrs.role], + code: prop(x, 'term') ?? x.attrs.term, + scheme: prop(x, 'authority') ?? x.attrs.authority, + }) : null + const makeCollection = x => ({ + name: makeLanguageMap(x), + // NOTE: webpub requires number but EPUB allows values like "2.2.1" + position: one(x.props?.['group-position']), + }) + const makeSeries = x => ({ + name: x.value, + position: one(x.props?.['group-position']), + }) + const makeAltIdentifier = x => { + const { value } = x + if (/^urn:/i.test(value)) return value + if (/^doi:/i.test(value)) return `urn:${value}` + const type = x.props?.['identifier-type'] + if (!type) { + const scheme = x.attrs.scheme + if (!scheme) return value + // https://idpf.github.io/epub-registries/identifiers/ + // but no "jdcn", which isn't a registered URN namespace + if (/^(doi|isbn|uuid)$/i.test(scheme)) return `urn:${scheme}:${value}` + // NOTE: webpub requires scheme to be a URI; EPUB allows anything + return { scheme, value } + } + if (type.scheme === PREFIX.onix + 'codelist5') { + const nid = ONIX5[type.value] + if (nid) return `urn:${nid}:${value}` + } + return value + } + const belongsTo = Object.groupBy(properties['belongs-to-collection'] ?? [], + x => prop(x, 'collection-type') === 'series' ? 'series' : 'collection') + const mainTitle = dc.title?.find(x => prop(x, 'title-type') === 'main') ?? dc.title?.[0] + const metadata = { + identifier: getIdentifier(opf), + title: makeLanguageMap(mainTitle), + sortAs: makeLanguageMap(mainTitle?.props?.['file-as']?.[0]) + ?? mainTitle?.attrs?.['file-as'] + ?? legacyMeta?.['calibre:title_sort'], + subtitle: dc.title?.find(x => prop(x, 'title-type') === 'subtitle')?.value, + language: dc.language?.map(x => x.value), + description: one(dc.description), + publisher: makeContributor(dc.publisher?.[0]), + published: dc.date?.find(x => x.attrs.event === 'publication')?.value + ?? one(dc.date), + modified: one(properties[PREFIX.dcterms + 'modified']) + ?? dc.date?.find(x => x.attrs.event === 'modification')?.value, + subject: dc.subject?.map(makeContributor), + belongsTo: { + collection: belongsTo.collection?.map(makeCollection), + series: belongsTo.series?.map(makeSeries) + ?? (legacyMeta?.['calibre:series'] ? { + name: legacyMeta?.['calibre:series'], + position: parseFloat(legacyMeta?.['calibre:series_index']), + } : null), + }, + altIdentifier: dc.identifier?.map(makeAltIdentifier), + source: dc.source?.map(makeAltIdentifier), // NOTE: not in webpub schema + rights: one(dc.rights), // NOTE: not in webpub schema + } + const remapContributor = defaultKey => x => { + const keys = new Set(x.role?.map(role => RELATORS[role] ?? defaultKey)) + return [keys.size ? keys : [defaultKey], x] + } + for (const [keys, val] of [].concat( + dc.creator?.map(makeContributor)?.map(remapContributor('author')) ?? [], + dc.contributor?.map(makeContributor)?.map(remapContributor('contributor')) ?? [])) + for (const key of keys) { + // if already parsed publisher don't remap it from author/contributor again + if (key === 'publisher' && metadata.publisher) continue + if (metadata[key]) metadata[key].push(val) + else metadata[key] = [val] + } + tidy(metadata) + if (metadata.altIdentifier === metadata.identifier) + delete metadata.altIdentifier + + const rendition = {} + const media = {} + for (const [key, val] of Object.entries(properties)) { + if (key.startsWith(PREFIX.rendition)) + rendition[camel(key.replace(PREFIX.rendition, ''))] = one(val) + else if (key.startsWith(PREFIX.media)) + media[camel(key.replace(PREFIX.media, ''))] = one(val) + } + if (media.duration) media.duration = parseClock(media.duration) + return { metadata, rendition, media } +} + +const parseNav = (doc, resolve = f => f) => { + const { $, $$, $$$ } = childGetter(doc, NS.XHTML) + const resolveHref = href => href ? decodeURI(resolve(href)) : null + const parseLI = getType => $li => { + const $a = $($li, 'a') ?? $($li, 'span') + const $ol = $($li, 'ol') + const href = resolveHref($a?.getAttribute('href')) + const label = getElementText($a) || $a?.getAttribute('title') + // TODO: get and concat alt/title texts in content + const result = { label, href, subitems: parseOL($ol) } + if (getType) result.type = $a?.getAttributeNS(NS.EPUB, 'type')?.split(/\s/) + return result + } + const parseOL = ($ol, getType) => $ol ? $$($ol, 'li').map(parseLI(getType)) : null + const parseNav = ($nav, getType) => parseOL($($nav, 'ol'), getType) + + const $$nav = $$$(doc, 'nav') + let toc = null, pageList = null, landmarks = null, others = [] + for (const $nav of $$nav) { + const type = $nav.getAttributeNS(NS.EPUB, 'type')?.split(/\s/) ?? [] + if (type.includes('toc')) toc ??= parseNav($nav) + else if (type.includes('page-list')) pageList ??= parseNav($nav) + else if (type.includes('landmarks')) landmarks ??= parseNav($nav, true) + else others.push({ + label: getElementText($nav.firstElementChild), type, + list: parseNav($nav), + }) + } + return { toc, pageList, landmarks, others } +} + +const parseNCX = (doc, resolve = f => f) => { + const { $, $$ } = childGetter(doc, NS.NCX) + const resolveHref = href => href ? decodeURI(resolve(href)) : null + const parseItem = el => { + const $label = $(el, 'navLabel') + const $content = $(el, 'content') + const label = getElementText($label) + const href = resolveHref($content.getAttribute('src')) + if (el.localName === 'navPoint') { + const els = $$(el, 'navPoint') + return { label, href, subitems: els.length ? els.map(parseItem) : null } + } + return { label, href } + } + const parseList = (el, itemName) => $$(el, itemName).map(parseItem) + const getSingle = (container, itemName) => { + const $container = $(doc.documentElement, container) + return $container ? parseList($container, itemName) : null + } + return { + toc: getSingle('navMap', 'navPoint'), + pageList: getSingle('pageList', 'pageTarget'), + others: $$(doc.documentElement, 'navList').map(el => ({ + label: getElementText($(el, 'navLabel')), + list: parseList(el, 'navTarget'), + })), + } +} + +const parseClock = str => { + if (!str) return + const parts = str.split(':').map(x => parseFloat(x)) + if (parts.length === 3) { + const [h, m, s] = parts + return h * 60 * 60 + m * 60 + s + } + if (parts.length === 2) { + const [m, s] = parts + return m * 60 + s + } + const [x, unit] = str.split(/(?=[^\d.])/) + const n = parseFloat(x) + const f = unit === 'h' ? 60 * 60 + : unit === 'min' ? 60 + : unit === 'ms' ? .001 + : 1 + return n * f +} + +const IMAGE_EXTENSIONS = ['jpg', 'jpeg', 'png', 'gif', 'webp'] +const FONT_EXTENSIONS = ['woff', 'woff2', 'ttf', 'otf'] + +const getImageMediaType = (path) => { + const extension = path.toLowerCase().split('.').pop() + const mediaTypeMap = { + 'jpg': 'image/jpeg', + 'jpeg': 'image/jpeg', + 'png': 'image/png', + 'gif': 'image/gif', + 'webp': 'image/webp', + } + return mediaTypeMap[extension] || 'image/jpeg' +} + +const getFontMediaType = (path) => { + const extension = path.toLowerCase().split('.').pop() + const mediaTypeMap = { + 'woff': 'font/woff', + 'woff2': 'font/woff2', + 'ttf': 'font/ttf', + 'otf': 'font/otf', + } + return mediaTypeMap[extension] || 'font/ttf' +} + +class MediaOverlay extends EventTarget { + #entries + #lastMediaOverlayItem + #sectionIndex + #audioIndex + #itemIndex + #audio + #volume = 1 + #rate = 1 + #state + constructor(book, loadXML) { + super() + this.book = book + this.loadXML = loadXML + } + async #loadSMIL(item) { + if (this.#lastMediaOverlayItem === item) return + const doc = await this.loadXML(item.href) + const resolve = href => href ? resolveURL(href, item.href) : null + const { $, $$$ } = childGetter(doc, NS.SMIL) + this.#audioIndex = -1 + this.#itemIndex = -1 + this.#entries = $$$(doc, 'par').reduce((arr, $par) => { + const text = resolve($($par, 'text')?.getAttribute('src')) + const $audio = $($par, 'audio') + if (!text || !$audio) return arr + const src = resolve($audio.getAttribute('src')) + const begin = parseClock($audio.getAttribute('clipBegin')) + const end = parseClock($audio.getAttribute('clipEnd')) + const last = arr.at(-1) + if (last?.src === src) last.items.push({ text, begin, end }) + else arr.push({ src, items: [{ text, begin, end }] }) + return arr + }, []) + this.#lastMediaOverlayItem = item + } + get #activeAudio() { + return this.#entries[this.#audioIndex] + } + get #activeItem() { + return this.#activeAudio?.items?.[this.#itemIndex] + } + #error(e) { + console.error(e) + this.dispatchEvent(new CustomEvent('error', { detail: e })) + } + #highlight() { + this.dispatchEvent(new CustomEvent('highlight', { detail: this.#activeItem })) + } + #unhighlight() { + this.dispatchEvent(new CustomEvent('unhighlight', { detail: this.#activeItem })) + } + async #play(audioIndex, itemIndex) { + this.#stop() + this.#audioIndex = audioIndex + this.#itemIndex = itemIndex + const src = this.#activeAudio?.src + if (!src || !this.#activeItem) return this.start(this.#sectionIndex + 1) + + const url = URL.createObjectURL(await this.book.loadBlob(src)) + const audio = new Audio(url) + this.#audio = audio + audio.volume = this.#volume + audio.playbackRate = this.#rate + audio.addEventListener('timeupdate', () => { + if (audio.paused) return + const t = audio.currentTime + const { items } = this.#activeAudio + if (t > this.#activeItem?.end) { + this.#unhighlight() + if (this.#itemIndex === items.length - 1) { + this.#play(this.#audioIndex + 1, 0).catch(e => this.#error(e)) + return + } + } + const oldIndex = this.#itemIndex + while (items[this.#itemIndex + 1]?.begin <= t) this.#itemIndex++ + if (this.#itemIndex !== oldIndex) this.#highlight() + }) + audio.addEventListener('error', () => + this.#error(new Error(`Failed to load ${src}`))) + audio.addEventListener('playing', () => this.#highlight()) + audio.addEventListener('ended', () => { + this.#unhighlight() + URL.revokeObjectURL(url) + this.#audio = null + this.#play(audioIndex + 1, 0).catch(e => this.#error(e)) + }) + if (this.#state === 'paused') { + this.#highlight() + audio.currentTime = this.#activeItem.begin ?? 0 + } + else audio.addEventListener('canplaythrough', () => { + // for some reason need to seek in `canplaythrough` + // or it won't play when skipping in WebKit + audio.currentTime = this.#activeItem.begin ?? 0 + this.#state = 'playing' + audio.play().catch(e => this.#error(e)) + }, { once: true }) + } + async start(sectionIndex, filter = () => true) { + this.#audio?.pause() + const section = this.book.sections[sectionIndex] + const href = section?.id + if (!href) return + + const { mediaOverlay } = section + if (!mediaOverlay) return this.start(sectionIndex + 1) + this.#sectionIndex = sectionIndex + await this.#loadSMIL(mediaOverlay) + + for (let i = 0; i < this.#entries.length; i++) { + const { items } = this.#entries[i] + for (let j = 0; j < items.length; j++) { + if (items[j].text.split('#')[0] === href && filter(items[j], j, items)) + return this.#play(i, j).catch(e => this.#error(e)) + } + } + } + pause() { + this.#state = 'paused' + this.#audio?.pause() + } + resume() { + this.#state = 'playing' + this.#audio?.play().catch(e => this.#error(e)) + } + #stop() { + if (this.#audio) { + this.#audio.pause() + URL.revokeObjectURL(this.#audio.src) + this.#audio = null + this.#unhighlight() + } + } + stop() { + this.#state = 'stopped' + this.#stop() + } + prev() { + if (this.#itemIndex > 0) this.#play(this.#audioIndex, this.#itemIndex - 1) + else if (this.#audioIndex > 0) this.#play(this.#audioIndex - 1, + this.#entries[this.#audioIndex - 1].items.length - 1) + else if (this.#sectionIndex > 0) + this.start(this.#sectionIndex - 1, (_, i, items) => i === items.length - 1) + } + next() { + this.#play(this.#audioIndex, this.#itemIndex + 1) + } + setVolume(volume) { + this.#volume = volume + if (this.#audio) this.#audio.volume = volume + } + setRate(rate) { + this.#rate = rate + if (this.#audio) this.#audio.playbackRate = rate + } +} + +const isUUID = /([0-9a-f]{8})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{12})/i + +const getUUID = opf => { + const extractUUID = el => { + const text = getElementText(el) + const id = text.split(':').slice(-1)[0] + const match = isUUID.exec(id) + return match ? match[0] : null + } + const identifiers = Array.from(opf.getElementsByTagNameNS(NS.DC, 'identifier')) + // 1. Prefer the unique-identifier (used by Adobe font obfuscation) + const uniqueIdAttr = opf.documentElement.getAttribute('unique-identifier') + if (uniqueIdAttr) { + const el = identifiers.find(el => el.getAttribute('id') === uniqueIdAttr) + if (el) { + const uuid = extractUUID(el) + if (uuid) return uuid + } + } + // 2. Prefer urn:uuid: identifiers (standard UUID URN per RFC 4122) + for (const el of identifiers) { + const text = getElementText(el) + if (/^urn:uuid:/i.test(text)) { + const uuid = extractUUID(el) + if (uuid) return uuid + } + } + // 3. Fall back to any identifier containing a UUID + for (const el of identifiers) { + const uuid = extractUUID(el) + if (uuid) return uuid + } + return '' +} + +const getIdentifier = opf => getElementText( + opf.getElementById(opf.documentElement.getAttribute('unique-identifier')) + ?? opf.getElementsByTagNameNS(NS.DC, 'identifier')[0]) + +// https://www.w3.org/publishing/epub32/epub-ocf.html#sec-resource-obfuscation +const deobfuscate = async (key, length, blob) => { + const array = new Uint8Array(await blob.slice(0, length).arrayBuffer()) + length = Math.min(length, array.length) + for (var i = 0; i < length; i++) array[i] = array[i] ^ key[i % key.length] + return new Blob([array, blob.slice(length)], { type: blob.type }) +} + +const WebCryptoSHA1 = async str => { + const data = new TextEncoder().encode(str) + const buffer = await globalThis.crypto.subtle.digest('SHA-1', data) + return new Uint8Array(buffer) +} + +const deobfuscators = (sha1 = WebCryptoSHA1) => ({ + 'http://www.idpf.org/2008/embedding': { + key: opf => sha1(getIdentifier(opf) + // eslint-disable-next-line no-control-regex + .replaceAll(/[\u0020\u0009\u000d\u000a]/g, '')), + decode: (key, blob) => deobfuscate(key, 1040, blob), + }, + 'http://ns.adobe.com/pdf/enc#RC': { + key: opf => { + const uuid = getUUID(opf).replaceAll('-', '') + return Uint8Array.from({ length: 16 }, (_, i) => + parseInt(uuid.slice(i * 2, i * 2 + 2), 16)) + }, + decode: (key, blob) => deobfuscate(key, 1024, blob), + }, +}) + +class Encryption { + #uris = new Map() + #decoders = new Map() + #algorithms + constructor(algorithms) { + this.#algorithms = algorithms + } + async init(encryption, opf) { + if (!encryption) return + const data = Array.from( + encryption.getElementsByTagNameNS(NS.ENC, 'EncryptedData'), el => ({ + algorithm: el.getElementsByTagNameNS(NS.ENC, 'EncryptionMethod')[0] + ?.getAttribute('Algorithm'), + uri: el.getElementsByTagNameNS(NS.ENC, 'CipherReference')[0] + ?.getAttribute('URI'), + })) + for (const { algorithm, uri } of data) { + if (!this.#decoders.has(algorithm)) { + const algo = this.#algorithms[algorithm] + if (!algo) { + console.warn('Unknown encryption algorithm') + continue + } + const key = await algo.key(opf) + this.#decoders.set(algorithm, blob => algo.decode(key, blob)) + } + this.#uris.set(uri, algorithm) + } + } + getDecoder(uri) { + return this.#decoders.get(this.#uris.get(uri)) ?? (x => x) + } +} + +class Resources { + constructor({ opf, resolveHref }) { + this.opf = opf + const { $, $$, $$$ } = childGetter(opf, NS.OPF) + + const $manifest = $(opf.documentElement, 'manifest') + const $spine = $(opf.documentElement, 'spine') + const $$itemref = $$($spine, 'itemref') + + this.manifest = $$($manifest, 'item') + .map(getAttributes('href', 'id', 'media-type', 'properties', 'media-overlay')) + .map(item => { + item.href = resolveHref(item.href) + item.properties = item.properties?.split(/\s/) + return item + }) + this.manifestById = new Map(this.manifest.map(item => [item.id, item])) + this.spine = $$itemref + .map(getAttributes('idref', 'id', 'linear', 'properties')) + .map(item => (item.properties = item.properties?.split(/\s/), item)) + this.pageProgressionDirection = $spine + .getAttribute('page-progression-direction') + + this.navPath = this.getItemByProperty('nav')?.href + this.ncxPath = (this.getItemByID($spine.getAttribute('toc')) + ?? this.manifest.find(item => item.mediaType === MIME.NCX))?.href + + const $guide = $(opf.documentElement, 'guide') + if ($guide) this.guide = $$($guide, 'reference') + .map(getAttributes('type', 'title', 'href')) + .map(({ type, title, href }) => ({ + label: title, + type: type.split(/\s/), + href: resolveHref(href), + })) + + this.cover = this.getItemByProperty('cover-image') + // EPUB 2 compat + ?? this.getItemByID($$$(opf, 'meta') + .find(filterAttribute('name', 'cover')) + ?.getAttribute('content')) + ?? this.manifest.find(item => item.id === 'cover' + && item.mediaType.startsWith('image')) + ?? this.manifest.find(item => item.href.includes('cover') + && item.mediaType.startsWith('image')) + ?? this.getItemByHref(this.guide + ?.find(ref => ref.type.includes('cover'))?.href) + // last resort: first image in manifest + ?? this.manifest.find(item => item.mediaType.startsWith('image')) + + this.cfis = CFI.fromElements($$itemref) + } + getItemByID(id) { + return this.manifestById.get(id) + } + getItemByHref(href) { + return this.manifest.find(item => item.href === href) + } + getItemByProperty(prop) { + return this.manifest.find(item => item.properties?.includes(prop)) + } + resolveCFI(cfi) { + const parts = CFI.parse(cfi) + const top = (parts.parent ?? parts).shift() + let $itemref = CFI.toElement(this.opf, top) + // make sure it's an idref; if not, try again without the ID assertion + // mainly because Epub.js used to generate wrong ID assertions + // https://github.com/futurepress/epub.js/issues/1236 + if ($itemref && $itemref.nodeName !== 'idref') { + top.at(-1).id = null + $itemref = CFI.toElement(this.opf, top) + } + const idref = $itemref?.getAttribute('idref') + const index = this.spine.findIndex(item => item.idref === idref) + const anchor = doc => CFI.toRange(doc, parts) + return { index, anchor } + } +} + +class Loader { + #cache = new Map() + #cacheXHTMLContent = new Map() + #children = new Map() + #refCount = new Map() + eventTarget = new EventTarget() + constructor({ loadText, loadBlob, resources, entries }) { + this.loadText = loadText + this.loadBlob = loadBlob + this.manifest = resources.manifest + this.assets = resources.manifest + this.entries = entries + // needed only when replacing in (X)HTML w/o parsing (see below) + //.filter(({ mediaType }) => ![MIME.XHTML, MIME.HTML].includes(mediaType)) + } + async createURL(href, data, type, parent) { + if (!data) return '' + const detail = { data, type } + Object.defineProperty(detail, 'name', { value: href }) // readonly + const event = new CustomEvent('data', { detail }) + this.eventTarget.dispatchEvent(event) + const newData = await event.detail.data + const newType = await event.detail.type + const url = URL.createObjectURL(new Blob([newData], { type: newType })) + this.#cache.set(href, url) + this.#refCount.set(href, 1) + if (newType === MIME.XHTML || newType === MIME.HTML) { + this.#cacheXHTMLContent.set(url, {href, type: newType, data: newData}) + } + if (parent) { + const childList = this.#children.get(parent) + if (childList) childList.push(href) + else this.#children.set(parent, [href]) + } + return url + } + ref(href, parent) { + const childList = this.#children.get(parent) + if (!childList?.includes(href)) { + this.#refCount.set(href, this.#refCount.get(href) + 1) + //console.log(`referencing ${href}, now ${this.#refCount.get(href)}`) + if (childList) childList.push(href) + else this.#children.set(parent, [href]) + } + return this.#cache.get(href) + } + unref(href) { + if (!this.#refCount.has(href)) return + const count = this.#refCount.get(href) - 1 + //console.log(`unreferencing ${href}, now ${count}`) + if (count < 1) { + //console.log(`unloading ${href}`) + const url = this.#cache.get(href) + URL.revokeObjectURL(url) + this.#cache.delete(href) + this.#cacheXHTMLContent.delete(url) + this.#refCount.delete(href) + // unref children + const childList = this.#children.get(href) + if (childList) while (childList.length) this.unref(childList.pop()) + this.#children.delete(href) + } else this.#refCount.set(href, count) + } + // load manifest item, recursively loading all resources as needed + async loadItem(item, parents = []) { + if (!item) return null + const { href, mediaType } = item + + const isScript = MIME.JS.test(item.mediaType) + const detail = { type: mediaType, href, isScript, allow: true} + const event = new CustomEvent('load', { detail }) + this.eventTarget.dispatchEvent(event) + const { allow, url } = await event.detail + if (!allow) return null + if (url !== undefined) return url + + const parent = parents.at(-1) + if (this.#cache.has(href)) return this.ref(href, parent) + + const shouldReplace = + (isScript || [MIME.XHTML, MIME.HTML, MIME.CSS, MIME.SVG].includes(mediaType)) + // prevent circular references + && parents.every(p => p !== href) + if (shouldReplace) return this.loadReplaced(item, parents) + // NOTE: this can be replaced with `Promise.try()` + const tryLoadBlob = Promise.resolve().then(() => this.loadBlob(href)) + return this.createURL(href, tryLoadBlob, mediaType, parent) + } + async loadItemXHTMLContent(item, parents = []) { + const url = await this.loadItem(item, parents) + if (url) return this.#cacheXHTMLContent.get(url)?.data + } + tryImageEntryItem(path) { + if (!IMAGE_EXTENSIONS.some(ext => path.toLowerCase().endsWith(`.${ext}`))) { + return null + } + if (!this.entries.get(path)) { + return null + } + return { + href: path, + mediaType: getImageMediaType(path), + } + } + tryFontEntryItem(path) { + if (!FONT_EXTENSIONS.some(ext => path.toLowerCase().endsWith(`.${ext}`))) { + return null + } + if (this.entries.get(path)) { + return { + href: path, + mediaType: getFontMediaType(path), + } + } + return { + href: `fonts/${path.split('/').pop()}`, + mediaType: getFontMediaType(path), + } + } + async loadHref(href, base, parents = []) { + if (isExternal(href)) return href + const path = resolveURL(href, base) + let item = this.manifest.find(item => item.href === path) + if (!item) { + item = this.tryImageEntryItem(path) ?? this.tryFontEntryItem(path) + if (!item) { + return href + } + } + return this.loadItem(item, parents.concat(base)) + } + async loadReplaced(item, parents = []) { + const { href, mediaType } = item + const parent = parents.at(-1) + let str = '' + try { + str = await this.loadText(href) + } catch (e) { + return this.createURL(href, Promise.reject(e), mediaType, parent) + } + if (!str) return null + + // note that one can also just use `replaceString` for everything: + // ``` + // const replaced = await this.replaceString(str, href, parents) + // return this.createURL(href, replaced, mediaType, parent) + // ``` + // which is basically what Epub.js does, which is simpler, but will + // break things like iframes (because you don't want to replace links) + // or text that just happen to be paths + + // parse and replace in HTML + if ([MIME.XHTML, MIME.HTML, MIME.SVG].includes(mediaType)) { + let doc = new DOMParser().parseFromString(str, mediaType) + // change to HTML if it's not valid XHTML + if (mediaType === MIME.XHTML && (doc.querySelector('parsererror') + || !doc.documentElement?.namespaceURI)) { + console.warn(doc.querySelector('parsererror')?.innerText ?? 'Invalid XHTML') + item.mediaType = MIME.HTML + doc = new DOMParser().parseFromString(str, item.mediaType) + } + // replace hrefs in XML processing instructions + // this is mainly for SVGs that use xml-stylesheet + if ([MIME.XHTML, MIME.SVG].includes(item.mediaType)) { + let child = doc.firstChild + while (child instanceof ProcessingInstruction) { + if (child.data) { + const replacedData = await replaceSeries(child.data, + /(?:^|\s*)(href\s*=\s*['"])([^'"]*)(['"])/i, + (_, p1, p2, p3) => this.loadHref(p2, href, parents) + .then(p2 => `${p1}${p2}${p3}`)) + child.replaceWith(doc.createProcessingInstruction( + child.target, replacedData)) + } + child = child.nextSibling + } + } + // replace hrefs (excluding anchors) + const replace = async (el, attr) => el.setAttribute(attr, + await this.loadHref(el.getAttribute(attr), href, parents)) + for (const el of doc.querySelectorAll('link[href]')) await replace(el, 'href') + for (const el of doc.querySelectorAll('[src]')) await replace(el, 'src') + for (const el of doc.querySelectorAll('[poster]')) await replace(el, 'poster') + for (const el of doc.querySelectorAll('object[data]')) await replace(el, 'data') + for (const el of doc.querySelectorAll('[*|href]:not([href])')) + el.setAttributeNS(NS.XLINK, 'href', await this.loadHref( + el.getAttributeNS(NS.XLINK, 'href'), href, parents)) + for (const el of doc.querySelectorAll('[srcset]')) + el.setAttribute('srcset', await replaceSeries(el.getAttribute('srcset'), + /(\s*)(.+?)\s*((?:\s[\d.]+[wx])+\s*(?:,|$)|,\s+|$)/g, + (_, p1, p2, p3) => this.loadHref(p2, href, parents) + .then(p2 => `${p1}${p2}${p3}`))) + // replace inline styles + for (const el of doc.querySelectorAll('style')) + if (el.textContent) el.textContent = + await this.replaceCSS(el.textContent, href, parents) + for (const el of doc.querySelectorAll('[style]')) + el.setAttribute('style', + await this.replaceCSS(el.getAttribute('style'), href, parents)) + // TODO: replace inline scripts? probably not worth the trouble + const result = new XMLSerializer().serializeToString(doc) + return this.createURL(href, result, item.mediaType, parent) + } + + const result = mediaType === MIME.CSS + ? await this.replaceCSS(str, href, parents) + : await this.replaceString(str, href, parents) + return this.createURL(href, result, mediaType, parent) + } + async replaceCSS(str, href, parents = []) { + const replacedUrls = await replaceSeries(str, + /url\(\s*["']?([^'"\n]*?)\s*["']?\s*\)/gi, + (_, url) => this.loadHref(url, href, parents) + .then(url => `url("${url}")`)) + // apart from `url()`, strings can be used for `@import` (but why?!) + return replaceSeries(replacedUrls, + /@import\s*["']([^"'\n]*?)["']/gi, + (_, url) => this.loadHref(url, href, parents) + .then(url => `@import "${url}"`)) + } + // find & replace all possible relative paths for all assets without parsing + replaceString(str, href, parents = []) { + const assetMap = new Map() + const urls = this.assets.map(asset => { + // do not replace references to the file itself + if (asset.href === href) return + // href was decoded and resolved when parsing the manifest + const relative = pathRelative(pathDirname(href), asset.href) + const relativeEnc = encodeURI(relative) + const rootRelative = '/' + asset.href + const rootRelativeEnc = encodeURI(rootRelative) + const set = new Set([relative, relativeEnc, rootRelative, rootRelativeEnc]) + for (const url of set) assetMap.set(url, asset) + return Array.from(set) + }).flat().filter(x => x) + if (!urls.length) return str + const regex = new RegExp(urls.map(regexEscape).join('|'), 'g') + return replaceSeries(str, regex, async match => + this.loadItem(assetMap.get(match.replace(/^\//, '')), + parents.concat(href))) + } + unloadItem(item) { + this.unref(item?.href) + } + destroy() { + for (const url of this.#cache.values()) URL.revokeObjectURL(url) + } +} + +const getHTMLFragment = (doc, id) => doc.getElementById(id) + ?? doc.querySelector(`[name="${CSS.escape(id)}"]`) + +const getPageSpread = properties => { + for (const p of properties) { + if (p === 'page-spread-left' || p === 'rendition:page-spread-left') + return 'left' + if (p === 'page-spread-right' || p === 'rendition:page-spread-right') + return 'right' + if (p === 'rendition:page-spread-center') return 'center' + } +} + +const getDisplayOptions = doc => { + if (!doc) return null + return { + fixedLayout: getElementText(doc.querySelector('option[name="fixed-layout"]')), + openToSpread: getElementText(doc.querySelector('option[name="open-to-spread"]')), + } +} + +export class EPUB { + parser = new DOMParser() + #loader + #encryption + constructor({ entries, loadText, loadBlob, getSize, sha1 }) { + this.entries = entries.reduce((map, entry) => { + map.set(entry.filename, entry) + return map + }, new Map()) + this.loadText = loadText + this.loadBlob = loadBlob + this.getSize = getSize + this.#encryption = new Encryption(deobfuscators(sha1)) + } + #sanitizeXMLEntities(str) { + // Common HTML entities that aren't valid in XML + const entityMap = { + 'nbsp': ' ', + 'mdash': '—', + 'ndash': '–', + 'ldquo': '“', + 'rdquo': '”', + 'lsquo': '‘', + 'rsquo': '’', + 'hellip': '…', + 'copy': '©', + 'reg': '®', + 'trade': '™', + 'bull': '•', + 'middot': '·', + } + return str.replace(/&([a-z]+);/gi, (match, entity) => { + return entityMap[entity.toLowerCase()] || match + }) + } + async #loadXML(uri) { + const str = await this.loadText(uri) + if (!str) return null + const sanitized = this.#sanitizeXMLEntities(str) + const doc = this.parser.parseFromString(sanitized, MIME.XML) + if (doc.querySelector('parsererror')) + throw new Error(`XML parsing error: ${uri} +${doc.querySelector('parsererror').innerText}`) + return doc + } + async init() { + const $container = await this.#loadXML('META-INF/container.xml') + if (!$container) throw new Error('Failed to load container file') + + const opfs = Array.from( + $container.getElementsByTagNameNS(NS.CONTAINER, 'rootfile'), + getAttributes('full-path', 'media-type')) + .filter(file => file.mediaType === 'application/oebps-package+xml') + + if (!opfs.length) throw new Error('No package document defined in container') + const opfPath = opfs[0].fullPath + const opf = await this.#loadXML(opfPath) + if (!opf) throw new Error('Failed to load package document') + + const $encryption = await this.#loadXML('META-INF/encryption.xml') + await this.#encryption.init($encryption, opf) + + this.resources = new Resources({ + opf, + resolveHref: url => resolveURL(url, opfPath), + }) + this.#loader = new Loader({ + loadText: this.loadText, + loadBlob: uri => Promise.resolve(this.loadBlob(uri)) + .then(this.#encryption.getDecoder(uri)), + resources: this.resources, + entries: this.entries, + }) + this.transformTarget = this.#loader.eventTarget + this.sections = this.resources.spine.map((spineItem, index) => { + const { idref, linear, properties = [] } = spineItem + const item = this.resources.getItemByID(idref) + if (!item) { + console.warn(`Could not find item with ID "${idref}" in manifest`) + return null + } + return { + id: item.href, + load: () => this.#loader.loadItem(item), + unload: () => this.#loader.unloadItem(item), + loadText: () => this.#loader.loadText(item.href), + loadContent: () => this.#loader.loadItemXHTMLContent(item), + createDocument: () => this.loadDocument(item), + size: this.getSize(item.href), + cfi: this.resources.cfis[index], + linear, + spineProperties: properties, + pageSpread: getPageSpread(properties), + resolveHref: href => resolveURL(href, item.href), + mediaOverlay: item.mediaOverlay + ? this.resources.getItemByID(item.mediaOverlay) : null, + } + }).filter(s => s) + + const { navPath, ncxPath } = this.resources + if (navPath) try { + const resolve = url => resolveURL(url, navPath) + const nav = parseNav(await this.#loadXML(navPath), resolve) + this.toc = nav.toc + this.pageList = nav.pageList + this.landmarks = nav.landmarks + } catch(e) { + console.warn(e) + } + if (!this.toc && ncxPath) try { + const resolve = url => resolveURL(url, ncxPath) + const ncx = parseNCX(await this.#loadXML(ncxPath), resolve) + this.toc = ncx.toc + this.pageList = ncx.pageList + } catch(e) { + console.warn(e) + } + + this.landmarks ??= this.resources.guide + + const { metadata, rendition, media } = getMetadata(opf) + this.metadata = metadata + this.rendition = rendition + this.media = media + this.dir = this.resources.pageProgressionDirection + const displayOptions = getDisplayOptions( + await this.#loadXML('META-INF/com.apple.ibooks.display-options.xml') + ?? await this.#loadXML('META-INF/com.kobobooks.display-options.xml')) + if (displayOptions) { + if (displayOptions.fixedLayout === 'true') + this.rendition.layout ??= 'pre-paginated' + if (displayOptions.openToSpread === 'false') this.sections + .find(section => section.linear !== 'no').pageSpread ??= + this.dir === 'rtl' ? 'left' : 'right' + } + return this + } + async loadDocument(item) { + const str = await this.loadText(item.href) + return this.parser.parseFromString(str, item.mediaType) + } + getMediaOverlay() { + return new MediaOverlay(this, this.#loadXML.bind(this)) + } + resolveCFI(cfi) { + return this.resources.resolveCFI(cfi) + } + resolveHref(href) { + const [path, hash] = href.split('#') + const item = this.resources.getItemByHref(decodeURI(path)) + if (!item) return null + const index = this.resources.spine.findIndex(({ idref }) => idref === item.id) + const anchor = hash ? doc => getHTMLFragment(doc, hash) : () => 0 + return { index, anchor } + } + splitTOCHref(href) { + return href?.split('#') ?? [] + } + getTOCFragment(doc, id) { + return doc.getElementById(id) + ?? doc.querySelector(`[name="${CSS.escape(id)}"]`) + } + isExternal(uri) { + return isExternal(uri) + } + async getCover() { + const cover = this.resources?.cover + return cover?.href + ? new Blob([await this.loadBlob(cover.href)], { type: cover.mediaType }) + : null + } + async getCalibreBookmarks() { + const txt = await this.loadText('META-INF/calibre_bookmarks.txt') + const magic = 'encoding=json+base64:' + if (txt?.startsWith(magic)) { + const json = atob(txt.slice(magic.length)) + return JSON.parse(json) + } + } + destroy() { + this.#loader?.destroy() + } +} diff --git a/web/vendor/foliate-js/epubcfi.js b/web/vendor/foliate-js/epubcfi.js new file mode 100644 index 00000000..d61dc7b8 --- /dev/null +++ b/web/vendor/foliate-js/epubcfi.js @@ -0,0 +1,354 @@ +const findIndices = (arr, f) => arr + .map((x, i, a) => f(x, i, a) ? i : null).filter(x => x != null) +const splitAt = (arr, is) => [-1, ...is, arr.length].reduce(({ xs, a }, b) => + ({ xs: xs?.concat([arr.slice(a + 1, b)]) ?? [], a: b }), {}).xs +const concatArrays = (a, b) => + a.slice(0, -1).concat([a[a.length - 1].concat(b[0])]).concat(b.slice(1)) + +const isNumber = /\d/ +export const isCFI = /^epubcfi\((.*)\)$/ +const escapeCFI = str => str.replace(/[\^[\](),;=]/g, '^$&') + +const wrap = x => isCFI.test(x) ? x : `epubcfi(${x})` +const unwrap = x => x.match(isCFI)?.[1] ?? x +const lift = f => (...xs) => + `epubcfi(${f(...xs.map(x => x.match(isCFI)?.[1] ?? x))})` +export const joinIndir = lift((...xs) => xs.join('!')) + +const tokenizer = str => { + const tokens = [] + let state, escape, value = '' + const push = x => (tokens.push(x), state = null, value = '') + const cat = x => (value += x, escape = false) + for (const char of Array.from(str.trim()).concat('')) { + if (char === '^' && !escape) { + escape = true + continue + } + if (state === '!') push(['!']) + else if (state === ',') push([',']) + else if (state === '/' || state === ':') { + if (isNumber.test(char)) { + cat(char) + continue + } else push([state, parseInt(value)]) + } else if (state === '~') { + if (isNumber.test(char) || char === '.') { + cat(char) + continue + } else push(['~', parseFloat(value)]) + } else if (state === '@') { + if (char === ':') { + push(['@', parseFloat(value)]) + state = '@' + continue + } + if (isNumber.test(char) || char === '.') { + cat(char) + continue + } else push(['@', parseFloat(value)]) + } else if (state === '[') { + if (char === ';' && !escape) { + push(['[', value]) + state = ';' + } else if (char === ',' && !escape) { + push(['[', value]) + state = '[' + } else if (char === ']' && !escape) push(['[', value]) + else cat(char) + continue + } else if (state?.startsWith(';')) { + if (char === '=' && !escape) { + state = `;${value}` + value = '' + } else if (char === ';' && !escape) { + push([state, value]) + state = ';' + } else if (char === ']' && !escape) push([state, value]) + else cat(char) + continue + } + if (char === '/' || char === ':' || char === '~' || char === '@' + || char === '[' || char === '!' || char === ',') state = char + } + return tokens +} + +const findTokens = (tokens, x) => findIndices(tokens, ([t]) => t === x) + +const parser = tokens => { + const parts = [] + let state + for (const [type, val] of tokens) { + if (type === '/') parts.push({ index: val }) + else { + const last = parts[parts.length - 1] + if (type === ':') last.offset = val + else if (type === '~') last.temporal = val + else if (type === '@') last.spatial = (last.spatial ?? []).concat(val) + else if (type === ';s') last.side = val + else if (type === '[') { + if (state === '/' && val) last.id = val + else { + last.text = (last.text ?? []).concat(val) + continue + } + } + } + state = type + } + return parts +} + +// split at step indirections, then parse each part +const parserIndir = tokens => + splitAt(tokens, findTokens(tokens, '!')).map(parser) + +export const parse = cfi => { + const tokens = tokenizer(unwrap(cfi)) + const commas = findTokens(tokens, ',') + if (!commas.length) return parserIndir(tokens) + const [parent, start, end] = splitAt(tokens, commas).map(parserIndir) + return { parent, start, end } +} + +const partToString = ({ index, id, offset, temporal, spatial, text, side }) => { + const param = side ? `;s=${side}` : '' + return `/${index}` + + (id ? `[${escapeCFI(id)}${param}]` : '') + // "CFI expressions [..] SHOULD include an explicit character offset" + + (offset != null && index % 2 ? `:${offset}` : '') + + (temporal ? `~${temporal}` : '') + + (spatial ? `@${spatial.join(':')}` : '') + + (text || (!id && side) ? '[' + + (text?.map(escapeCFI)?.join(',') ?? '') + + param + ']' : '') +} + +const toInnerString = parsed => parsed.parent + ? [parsed.parent, parsed.start, parsed.end].map(toInnerString).join(',') + : parsed.map(parts => parts.map(partToString).join('')).join('!') + +const toString = parsed => wrap(toInnerString(parsed)) + +export const collapse = (x, toEnd) => typeof x === 'string' + ? toString(collapse(parse(x), toEnd)) + : x.parent ? concatArrays(x.parent, x[toEnd ? 'end' : 'start']) : x + +// create range CFI from two CFIs +const buildRange = (from, to) => { + if (typeof from === 'string') from = parse(from) + if (typeof to === 'string') to = parse(to) + from = collapse(from) + to = collapse(to, true) + // ranges across multiple documents are not allowed; handle local paths only + const localFrom = from[from.length - 1], localTo = to[to.length - 1] + const localParent = [], localStart = [], localEnd = [] + let pushToParent = true + const len = Math.max(localFrom.length, localTo.length) + for (let i = 0; i < len; i++) { + const a = localFrom[i], b = localTo[i] + pushToParent &&= a?.index === b?.index && !a?.offset && !b?.offset + if (pushToParent) localParent.push(a) + else { + if (a) localStart.push(a) + if (b) localEnd.push(b) + } + } + // copy non-local paths from `from` + const parent = from.slice(0, -1).concat([localParent]) + return toString({ parent, start: [localStart], end: [localEnd] }) +} + +export const compare = (a, b) => { + if (typeof a === 'string') a = parse(a) + if (typeof b === 'string') b = parse(b) + if (a.start || b.start) return compare(collapse(a), collapse(b)) + || compare(collapse(a, true), collapse(b, true)) + + for (let i = 0; i < Math.max(a.length, b.length); i++) { + const p = a[i] ?? [], q = b[i] ?? [] + const maxIndex = Math.max(p.length, q.length) - 1 + for (let i = 0; i <= maxIndex; i++) { + const x = p[i], y = q[i] + if (!x) return -1 + if (!y) return 1 + if (x.index > y.index) return 1 + if (x.index < y.index) return -1 + if (i === maxIndex) { + // TODO: compare temporal & spatial offsets + if (x.offset > y.offset) return 1 + if (x.offset < y.offset) return -1 + } + } + } + return 0 +} + +const isTextNode = ({ nodeType }) => nodeType === 3 || nodeType === 4 +const isElementNode = ({ nodeType }) => nodeType === 1 +const isInertNode = (node) => node.hasAttribute?.('cfi-inert') + +const getChildNodes = (node, filter) => { + const nodes = Array.from(node.childNodes) + // "content other than element and character data is ignored" + .filter(node => isTextNode(node) || isElementNode(node)) + // always skip nodes marked with cfi-inert attribute + .filter(node => !isInertNode(node)) + return filter ? nodes.map(node => { + const accept = filter(node) + if (accept === NodeFilter.FILTER_REJECT) return null + else if (accept === NodeFilter.FILTER_SKIP) return getChildNodes(node, filter) + else return node + }).flat().filter(x => x) : nodes +} + +// child nodes are organized such that the result is always +// [element, text, element, text, ..., element], +// regardless of the actual structure in the document; +// so multiple text nodes need to be combined, and nonexistent ones counted; +// see "Step Reference to Child Element or Character Data (/)" in EPUB CFI spec +const indexChildNodes = (node, filter) => { + const nodes = getChildNodes(node, filter) + .reduce((arr, node) => { + let last = arr[arr.length - 1] + if (!last) arr.push(node) + // "there is one chunk between each pair of child elements" + else if (isTextNode(node)) { + if (Array.isArray(last)) last.push(node) + else if (isTextNode(last)) arr[arr.length - 1] = [last, node] + else arr.push(node) + } else { + if (isElementNode(last)) arr.push(null, node) + else arr.push(node) + } + return arr + }, []) + // "the first chunk is located before the first child element" + if (isElementNode(nodes[0])) nodes.unshift('first') + // "the last chunk is located after the last child element" + if (isElementNode(nodes[nodes.length - 1])) nodes.push('last') + // "'virtual' elements" + nodes.unshift('before') // "0 is a valid index" + nodes.push('after') // "n+2 is a valid index" + return nodes +} + +const partsToNode = (node, parts, filter) => { + const { id } = parts[parts.length - 1] + if (id) { + const el = node.ownerDocument.getElementById(id) + if (el) return { node: el, offset: 0 } + } + for (const { index } of parts) { + const newNode = node ? indexChildNodes(node, filter)[index] : null + // handle non-existent nodes + if (newNode === 'first') return { node: node.firstChild ?? node } + if (newNode === 'last') return { node: node.lastChild ?? node } + if (newNode === 'before') return { node, before: true } + if (newNode === 'after') return { node, after: true } + node = newNode + } + const { offset } = parts[parts.length - 1] + if (!Array.isArray(node)) return { node, offset } + // get underlying text node and offset from the chunk + let sum = 0 + for (const n of node) { + const { length } = n.nodeValue + if (sum + length >= offset) return { node: n, offset: offset - sum } + sum += length + } +} + +const nodeToParts = (node, offset, filter) => { + const { parentNode, id } = node + const indexed = indexChildNodes(parentNode, filter) + const index = indexed.findIndex(x => + Array.isArray(x) ? x.some(x => x === node) : x === node) + // adjust offset as if merging the text nodes in the chunk + const chunk = indexed[index] + if (Array.isArray(chunk)) { + let sum = 0 + for (const x of chunk) { + if (x === node) { + sum += offset + break + } else sum += x.nodeValue.length + } + offset = sum + } + const part = { id, index, offset } + return (parentNode !== node.ownerDocument.documentElement + ? nodeToParts(parentNode, null, filter).concat(part) : [part]) + // remove ignored nodes + .filter(x => x.index !== -1) +} + +export const fromRange = (range, filter) => { + const { startContainer, startOffset, endContainer, endOffset } = range + const start = nodeToParts(startContainer, startOffset, filter) + if (range.collapsed) return toString([start]) + const end = nodeToParts(endContainer, endOffset, filter) + return buildRange([start], [end]) +} + +export const toRange = (doc, parts, filter) => { + try { + const startParts = collapse(parts) + const endParts = collapse(parts, true) + + const root = doc.documentElement + const start = partsToNode(root, startParts[0], filter) + const end = partsToNode(root, endParts[0], filter) + + if (!start?.node || !end?.node) return null + + const range = doc.createRange() + + if (start.before) range.setStartBefore(start.node) + else if (start.after) range.setStartAfter(start.node) + else range.setStart(start.node, start.offset) + + if (end.before) range.setEndBefore(end.node) + else if (end.after) range.setEndAfter(end.node) + else range.setEnd(end.node, end.offset) + return range + } catch { + return null + } +} + +// faster way of getting CFIs for sorted elements in a single parent +export const fromElements = elements => { + const results = [] + const { parentNode } = elements[0] + const parts = nodeToParts(parentNode) + for (const [index, node] of indexChildNodes(parentNode).entries()) { + const el = elements[results.length] + if (node === el) + results.push(toString([parts.concat({ id: el.id, index })])) + } + return results +} + +export const toElement = (doc, parts) => + partsToNode(doc.documentElement, collapse(parts)).node + +// turn indices into standard CFIs when you don't have an actual package document +export const fake = { + fromIndex: index => wrap(`/6/${(index + 1) * 2}`), + toIndex: parts => parts?.at(-1).index / 2 - 1, +} + +// get CFI from Calibre bookmarks +// see https://github.com/johnfactotum/foliate/issues/849 +export const fromCalibrePos = pos => { + const [parts] = parse(pos) + const item = parts.shift() + parts.shift() + return toString([[{ index: 6 }, item], parts]) +} +export const fromCalibreHighlight = ({ spine_index, start_cfi, end_cfi }) => { + const pre = fake.fromIndex(spine_index) + '!' + return buildRange(pre + start_cfi.slice(2), pre + end_cfi.slice(2)) +} diff --git a/web/vendor/foliate-js/fb2.js b/web/vendor/foliate-js/fb2.js new file mode 100644 index 00000000..a1623c40 --- /dev/null +++ b/web/vendor/foliate-js/fb2.js @@ -0,0 +1,348 @@ +const normalizeWhitespace = str => str ? str + .replace(/[\t\n\f\r ]+/g, ' ') + .replace(/^[\t\n\f\r ]+/, '') + .replace(/[\t\n\f\r ]+$/, '') : '' +const getElementText = el => normalizeWhitespace(el?.textContent) + +const NS = { + XLINK: 'http://www.w3.org/1999/xlink', + EPUB: 'http://www.idpf.org/2007/ops', +} + +const MIME = { + XML: 'application/xml', + XHTML: 'application/xhtml+xml', +} + +const STYLE = { + 'strong': ['strong', 'self'], + 'emphasis': ['em', 'self'], + 'style': ['span', 'self'], + 'a': 'anchor', + 'strikethrough': ['s', 'self'], + 'sub': ['sub', 'self'], + 'sup': ['sup', 'self'], + 'code': ['code', 'self'], + 'image': 'image', +} + +const TABLE = { + 'tr': ['tr', { + 'th': ['th', STYLE, ['colspan', 'rowspan', 'align', 'valign']], + 'td': ['td', STYLE, ['colspan', 'rowspan', 'align', 'valign']], + }, ['align']], +} + +const POEM = { + 'epigraph': ['blockquote'], + 'subtitle': ['h2', STYLE], + 'text-author': ['p', STYLE], + 'date': ['p', STYLE], + 'stanza': ['div', 'self'], + 'v': ['div', STYLE], +} + +const SECTION = { + 'title': ['header', { + 'p': ['h1', STYLE], + 'empty-line': ['br'], + }], + 'epigraph': ['blockquote', 'self'], + 'image': 'image', + 'annotation': ['aside'], + 'section': ['section', 'self'], + 'p': ['p', STYLE], + 'poem': ['blockquote', POEM], + 'subtitle': ['h2', STYLE], + 'cite': ['blockquote', 'self'], + 'empty-line': ['br'], + 'table': ['table', TABLE], + 'text-author': ['p', STYLE], +} +POEM['epigraph'].push(SECTION) + +const BODY = { + 'image': 'image', + 'title': ['section', { + 'p': ['h1', STYLE], + 'empty-line': ['br'], + }], + 'epigraph': ['section', SECTION], + 'section': ['section', SECTION], +} + +class FB2Converter { + constructor(fb2) { + this.fb2 = fb2 + this.doc = document.implementation.createDocument(NS.XHTML, 'html') + // use this instead of `getElementById` to allow images like + // `` + this.bins = new Map(Array.from(this.fb2.getElementsByTagName('binary'), + el => [el.id, el])) + } + getImageSrc(el) { + const href = el.getAttributeNS(NS.XLINK, 'href') + if (!href) return 'data:,' + const [, id] = href.split('#') + if (!id) return href + const bin = this.bins.get(id) + return bin + ? `data:${bin.getAttribute('content-type')};base64,${bin.textContent}` + : href + } + image(node) { + const el = this.doc.createElement('img') + el.alt = node.getAttribute('alt') + el.title = node.getAttribute('title') + el.setAttribute('src', this.getImageSrc(node)) + return el + } + anchor(node) { + const el = this.convert(node, { 'a': ['a', STYLE] }) + el.setAttribute('href', node.getAttributeNS(NS.XLINK, 'href')) + if (node.getAttribute('type') === 'note') + el.setAttributeNS(NS.EPUB, 'epub:type', 'noteref') + return el + } + convert(node, def) { + // not an element; return text content + if (node.nodeType === 3) return this.doc.createTextNode(node.textContent) + if (node.nodeType === 4) return this.doc.createCDATASection(node.textContent) + if (node.nodeType === 8) return this.doc.createComment(node.textContent) + + const d = def?.[node.nodeName] + if (!d) return null + if (typeof d === 'string') return this[d](node) + + const [name, opts, attrs] = d + const el = this.doc.createElement(name) + + // copy the ID, and set class name from original element name + if (node.id) el.id = node.id + el.classList.add(node.nodeName) + + // copy attributes + if (Array.isArray(attrs)) for (const attr of attrs) { + const value = node.getAttribute(attr) + if (value) el.setAttribute(attr, value) + } + + // process child elements recursively + const childDef = opts === 'self' ? def : opts + let child = node.firstChild + while (child) { + const childEl = this.convert(child, childDef) + if (childEl) el.append(childEl) + child = child.nextSibling + } + return el + } +} + +const parseXML = async blob => { + const buffer = await blob.arrayBuffer() + const str = new TextDecoder('utf-8').decode(buffer) + const parser = new DOMParser() + const doc = parser.parseFromString(str, MIME.XML) + const encoding = doc.xmlEncoding + // `Document.xmlEncoding` is deprecated, and already removed in Firefox + // so parse the XML declaration manually + || str.match(/^<\?xml\s+version\s*=\s*["']1.\d+"\s+encoding\s*=\s*["']([A-Za-z0-9._-]*)["']/)?.[1] + if (encoding && encoding.toLowerCase() !== 'utf-8') { + const str = new TextDecoder(encoding).decode(buffer) + return parser.parseFromString(str, MIME.XML) + } + return doc +} + +const style = URL.createObjectURL(new Blob([` +@namespace epub "http://www.idpf.org/2007/ops"; +body > img, section > img { + display: block; + margin: auto; +} +.title h1 { + text-align: center; +} +body > section > .title, body.notesBodyType > .title { + margin: 3em 0; +} +body.notesBodyType > section .title h1 { + text-align: start; +} +body.notesBodyType > section .title { + margin: 1em 0; +} +p { + text-indent: 1em; + margin: 0; +} +:not(p) + p, p:first-child { + text-indent: 0; +} +.stanza { + text-indent: 0; + margin: 1em 0; +} +.text-author, .date { + text-align: end; +} +.text-author:before { + content: "—"; +} +table { + border-collapse: collapse; +} +td, th { + padding: .25em; +} +a[epub|type~="noteref"] { + font-size: .75em; + vertical-align: super; +} +body:not(.notesBodyType) > .title, body:not(.notesBodyType) > .epigraph { + margin: 3em 0; +} +`], { type: 'text/css' })) + +const template = html => ` + + + ${html} +` + +// name of custom ID attribute for TOC items +const dataID = 'data-foliate-id' + +export const makeFB2 = async blob => { + const book = {} + const doc = await parseXML(blob) + const converter = new FB2Converter(doc) + + const $ = x => doc.querySelector(x) + const $$ = x => [...doc.querySelectorAll(x)] + const getPerson = el => { + const nick = getElementText(el.querySelector('nickname')) + if (nick) return nick + const first = getElementText(el.querySelector('first-name')) + const middle = getElementText(el.querySelector('middle-name')) + const last = getElementText(el.querySelector('last-name')) + const name = [first, middle, last].filter(x => x).join(' ') + const sortAs = last + ? [last, [first, middle].filter(x => x).join(' ')].join(', ') + : null + return { name, sortAs } + } + const getDate = el => el?.getAttribute('value') ?? getElementText(el) + const annotation = $('title-info annotation') + book.metadata = { + title: getElementText($('title-info book-title')), + identifier: getElementText($('document-info id')), + language: getElementText($('title-info lang')), + author: $$('title-info author').map(getPerson), + translator: $$('title-info translator').map(getPerson), + contributor: $$('document-info author').map(getPerson) + // techincially the program probably shouldn't get the `bkp` role + // but it has been so used by calibre, so ¯\_(ツ)_/¯ + .concat($$('document-info program-used').map(getElementText)) + .map(x => Object.assign(typeof x === 'string' ? { name: x } : x, + { role: 'bkp' })), + publisher: getElementText($('publish-info publisher')), + published: getDate($('title-info date')), + modified: getDate($('document-info date')), + description: annotation ? converter.convert(annotation, + { annotation: ['div', SECTION] }).innerHTML : null, + subject: $$('title-info genre').map(getElementText), + } + if ($('coverpage image')) { + const src = converter.getImageSrc($('coverpage image')) + book.getCover = () => fetch(src).then(res => res.blob()) + } else book.getCover = () => null + + // get convert each body + const bodyData = Array.from(doc.querySelectorAll('body'), body => { + const converted = converter.convert(body, { body: ['body', BODY] }) + return [Array.from(converted.children, el => { + // get list of IDs in the section + const ids = [el, ...el.querySelectorAll('[id]')].map(el => el.id) + return { el, ids } + }), converted] + }) + + const urls = [] + const sectionData = bodyData[0][0] + // make a separate section for each section in the first body + .map(({ el, ids }, id) => { + // set up titles for TOC + const titles = Array.from( + el.querySelectorAll(':scope > section > .title'), + (el, index) => { + el.setAttribute(dataID, index) + const section = el.closest('section') + const size = new TextEncoder().encode(section.innerHTML).length + - Array.from(section.querySelectorAll('[src]')) + .reduce((sum, el) => sum + (el.getAttribute('src')?.length ?? 0), 0) + return { title: getElementText(el), index, size, href: `${id}#${index}` } + }) + return { ids, titles, el } + }) + // for additional bodies, only make one section for each body + .concat(bodyData.slice(1).map(([sections, body]) => { + const ids = sections.map(s => s.ids).flat() + body.classList.add('notesBodyType') + return { ids, el: body, linear: 'no' } + })) + .map(({ ids, titles, el, linear }) => { + const str = template(el.outerHTML) + const blob = new Blob([str], { type: MIME.XHTML }) + const url = URL.createObjectURL(blob) + urls.push(url) + const title = normalizeWhitespace( + el.querySelector('.title, .subtitle, p')?.textContent + ?? (el.classList.contains('title') ? el.textContent : '')) + return { + ids, title, titles, load: () => url, + createDocument: () => new DOMParser().parseFromString(str, MIME.XHTML), + // doo't count image data as it'd skew the size too much + size: blob.size - Array.from(el.querySelectorAll('[src]'), + el => el.getAttribute('src')?.length ?? 0) + .reduce((a, b) => a + b, 0), + linear, + } + }) + + const idMap = new Map() + book.sections = sectionData.map((section, index) => { + const { ids, load, createDocument, size, linear, titles } = section + for (const id of ids) if (id) idMap.set(id, index) + return { id: index, load, createDocument, size, linear, subitems: titles } + }) + + book.toc = sectionData.map(({ title, titles }, index) => { + const id = index.toString() + return { + label: title, + href: id, + subitems: titles?.length ? titles.map(({ title, index }) => ({ + label: title, + href: `${id}#${index}`, + })) : null, + } + }).filter(item => item) + + book.resolveHref = href => { + const [a, b] = href.split('#') + return a + // the link is from the TOC + ? { index: Number(a), anchor: doc => doc.querySelector(`[${dataID}="${b}"]`) } + // link from within the page + : { index: idMap.get(b), anchor: doc => doc.getElementById(b) } + } + book.splitTOCHref = href => href?.split('#')?.map(x => Number(x)) ?? [] + book.getTOCFragment = (doc, id) => doc.querySelector(`[${dataID}="${id}"]`) + + book.destroy = () => { + for (const url of urls) URL.revokeObjectURL(url) + } + return book +} diff --git a/web/vendor/foliate-js/fixed-layout.js b/web/vendor/foliate-js/fixed-layout.js new file mode 100644 index 00000000..57c85f2b --- /dev/null +++ b/web/vendor/foliate-js/fixed-layout.js @@ -0,0 +1,1295 @@ +import 'construct-style-sheets-polyfill' + +const parseViewport = str => str + ?.split(/[,;\s]/) // NOTE: technically, only the comma is valid + ?.filter(x => x) + ?.map(x => x.split('=').map(x => x.trim())) + +const getViewport = (doc, viewport) => { + // use `viewBox` for SVG + if (doc.documentElement.localName === 'svg') { + const [, , width, height] = doc.documentElement + .getAttribute('viewBox')?.split(/\s/) ?? [] + return { width, height } + } + + // get `viewport` `meta` element + const meta = parseViewport(doc.querySelector('meta[name="viewport"]') + ?.getAttribute('content')) + if (meta) return Object.fromEntries(meta) + + // fallback to book's viewport + if (typeof viewport === 'string') return parseViewport(viewport) + if (viewport?.width && viewport.height) return viewport + + // if no viewport (possibly with image directly in spine), get image size + const img = doc.querySelector('img') + if (img) return { width: img.naturalWidth, height: img.naturalHeight } + + // just show *something*, i guess... + console.warn(new Error('Missing viewport properties')) + return { width: 1000, height: 2000 } +} + +const clamp = (value, min, max) => Math.min(max, Math.max(min, value)) + +export const captureScrollModeAnchor = (pages, scrollTop, fallbackIndex = -1) => { + const fallbackPage = pages.find(page => page.index === fallbackIndex) + const currentPage = pages.find(page => + page.height > 0 + && scrollTop >= page.top + && scrollTop < page.top + page.height) + ?? fallbackPage + ?? pages.find(page => page.height > 0) + + if (!currentPage) return null + return { + index: currentPage.index, + fraction: currentPage.height > 0 + ? clamp((scrollTop - currentPage.top) / currentPage.height, 0, 1) + : 0, + scrollTop, + } +} + +export const restoreScrollModeAnchor = (pages, anchor, maxScrollTop) => { + if (!anchor) return 0 + const page = pages.find(candidate => candidate.index === anchor.index) + if (!page || page.height <= 0) return clamp(anchor.scrollTop, 0, maxScrollTop) + return clamp(page.top + page.height * anchor.fraction, 0, maxScrollTop) +} + +export class FixedLayout extends HTMLElement { + static observedAttributes = ['zoom', 'scale-factor', 'spread', 'flow'] + #root = this.attachShadow({ mode: 'open' }) + #observer = new ResizeObserver(() => this.#render()) + #spreads + #index = -1 + defaultViewport + spread + #portrait = false + #left + #right + #center + #side + #zoom + #scaleFactor = 1.0 + #totalScaleFactor = 1.0 + #scrollLocked = false + #isOverflowX = false + #isOverflowY = false + #preloadCache = new Map() + #prerenderedSpreads = new Map() + #spreadAccessTime = new Map() + #maxConcurrentPreloads = 1 + #numPrerenderedSpreads = 1 + #maxCachedSpreads = 2 + #overlayers = new Map() + #pageColors = {} + #preloadQueue = [] + #activePreloads = 0 + // Scroll mode fields + #scrollMode = false + #scrollPages = [] + #scrollObserver = null + #scrollContainer = null + #scrollLoadGen = new Map() + #scrollMaxLoaded = 8 + #scrollIdleTimer = null + #scrollCurrentIndex = -1 + #getScrollModePageMetrics() { + return this.#scrollPages.map(page => ({ + index: page.index, + top: page.el.offsetTop, + height: page.el.offsetHeight, + })) + } + #captureScrollModeAnchor() { + if (!this.#scrollPages.length) return null + const fallbackIndex = this.#scrollCurrentIndex >= 0 + ? this.#scrollCurrentIndex : this.#getScrollIndex() + return captureScrollModeAnchor( + this.#getScrollModePageMetrics(), + this.scrollTop, + fallbackIndex, + ) + } + #restoreScrollModeAnchor(anchor) { + if (!anchor || !this.#scrollPages.length) return + const maxScrollTop = Math.max(0, this.scrollHeight - this.clientHeight) + this.scrollTop = restoreScrollModeAnchor( + this.#getScrollModePageMetrics(), + anchor, + maxScrollTop, + ) + this.#scrollCurrentIndex = anchor.index + } + constructor() { + super() + + const sheet = new CSSStyleSheet() + this.#root.adoptedStyleSheets = [sheet] + sheet.replaceSync(`:host { + width: 100%; + height: 100%; + display: flex; + justify-content: flex-start; + align-items: center; + overflow: auto; + } + @supports (justify-content: safe center) { + :host { + justify-content: safe center; + } + } + :host([flow="scrolled"]) { + display: block; + overflow-y: auto; + overflow-x: hidden; + } + :host([flow="scrolled"]) .scroll-container { + display: flex; + flex-direction: column; + align-items: center; + min-height: 100%; + background-color: var(--scroll-bg-color); + background-opacity: var(--scroll-bg-opacity); + } + :host([flow="scrolled"]) .scroll-page { + position: relative; + flex-shrink: 0; + overflow: hidden; + margin: 4px 0; + } + :host([flow="scrolled"]) .scroll-page iframe { + pointer-events: none; + }`) + + this.#observer.observe(this) + } + attributeChangedCallback(name, _, value) { + switch (name) { + case 'zoom': + this.#zoom = value !== 'fit-width' && value !== 'fit-page' + ? parseFloat(value) : value + this.#render() + break + case 'scale-factor': + this.#scaleFactor = parseFloat(value) / 100 + this.#render() + break + case 'spread': + this.#respread(value) + break + case 'flow': + if (value === 'scrolled' && !this.#scrollMode) { + // Capture index from paginated mode BEFORE setting scroll flag + const savedIndex = this.index + this.#scrollMode = true + if (this.book) this.#initScrollMode(savedIndex) + } else if (value !== 'scrolled' && this.#scrollMode) { + this.#destroyScrollMode() + this.#scrollMode = false + this.#render() + } + break + } + } + async #createFrame({ index, src: srcOption, detached = false }) { + const srcOptionIsString = typeof srcOption === 'string' + const src = srcOptionIsString ? srcOption : srcOption?.src + const data = srcOptionIsString ? null : srcOption?.data + const onZoom = srcOptionIsString ? null : srcOption?.onZoom + const element = document.createElement('div') + element.setAttribute('dir', 'ltr') + element.style.position = 'relative' + const iframe = document.createElement('iframe') + element.append(iframe) + Object.assign(iframe.style, { + border: '0', + display: 'none', + overflow: 'hidden', + }) + // `allow-scripts` is needed for events because of WebKit bug + // https://bugs.webkit.org/show_bug.cgi?id=218086 + iframe.setAttribute('sandbox', 'allow-same-origin allow-scripts') + iframe.setAttribute('scrolling', 'no') + iframe.setAttribute('part', 'filter') + this.#root.append(element) + + if (detached) { + Object.assign(element.style, { + position: 'absolute', + visibility: 'hidden', + pointerEvents: 'none', + }) + } + + if (!src) return { blank: true, element, iframe } + return new Promise(resolve => { + iframe.addEventListener('load', () => { + const doc = iframe.contentDocument + iframe.dataset.sectionIndex = index + this.dispatchEvent(new CustomEvent('load', { detail: { doc, index } })) + const { width, height } = getViewport(doc, this.defaultViewport) + resolve({ + element, iframe, + width: parseFloat(width), + height: parseFloat(height), + onZoom, + detached, + }) + }, { once: true }) + if (data) { + iframe.srcdoc = data + } else { + iframe.src = src + } + }) + } + #render(side = this.#side) { + if (this.#scrollMode) { + this.#renderScrollMode() + return [] + } + if (!side) return [] + const left = this.#left ?? {} + const right = this.#center ?? this.#right ?? {} + const target = side === 'left' ? left : right + const { width, height } = this.getBoundingClientRect() + // for unfolded devices with slightly taller height than width also use landscape layout + const portrait = this.spread !== 'both' && this.spread !== 'portrait' + && height > width * 1.2 + this.#portrait = portrait + const blankWidth = left.width ?? right.width ?? 0 + const blankHeight = left.height ?? right.height ?? 0 + + let scale = typeof this.#zoom === 'number' && !isNaN(this.#zoom) + ? this.#zoom + : (this.#zoom === 'fit-width' + ? (portrait || this.#center + ? width / (target.width ?? blankWidth) + : width / ((left.width ?? blankWidth) + (right.width ?? blankWidth))) + : (portrait || this.#center + ? Math.min( + width / (target.width ?? blankWidth), + height / (target.height ?? blankHeight)) + : Math.min( + width / ((left.width ?? blankWidth) + (right.width ?? blankWidth)), + height / Math.max( + left.height ?? blankHeight, + right.height ?? blankHeight))) + ) || 1 + + scale *= this.#scaleFactor + this.#totalScaleFactor = scale + + const renderPromises = [] + const transform = ({frame, styles}) => { + let { element, iframe, width, height, blank, onZoom } = frame + if (!iframe) return + if (onZoom) { + const p = onZoom({ doc: frame.iframe.contentDocument, scale, pageColors: this.#pageColors }) + if (p?.then) { + // onZoom (e.g. pdf.js) may rebuild the text layer DOM, + // invalidating Range objects stored in the overlayer. After + // the rebuild, re-emit create-overlayer so listeners can + // re-anchor annotations against the fresh DOM. + const refreshed = p.then(() => this.#refreshOverlayerForFrame(frame)) + renderPromises.push(refreshed) + } + } + const iframeScale = onZoom ? scale : 1 + const zoomedOut = this.#scaleFactor < 1.0 + Object.assign(iframe.style, { + width: `${width * iframeScale}px`, + height: `${height * iframeScale}px`, + transform: onZoom ? 'none' : `scale(${scale})`, + transformOrigin: 'top left', + display: blank ? 'none' : 'block', + }) + Object.assign(element.style, { + width: `${(width ?? blankWidth) * scale}px`, + height: `${(height ?? blankHeight) * scale}px`, + flexShrink: '0', + display: zoomedOut ? 'flex' : 'block', + marginBlock: zoomedOut ? undefined : 'auto', + alignItems: zoomedOut ? 'center' : undefined, + justifyContent: zoomedOut ? 'center' : undefined, + ...styles, + }) + if (portrait && frame !== target) { + element.style.display = 'none' + } + + // position and redraw overlayer to match the scaled iframe + const sectionIndex = iframe.dataset.sectionIndex != null + ? parseInt(iframe.dataset.sectionIndex) : undefined + if (sectionIndex != null) { + const overlayer = this.#overlayers.get(sectionIndex) + if (overlayer) { + Object.assign(overlayer.element.style, { + position: 'absolute', + top: '0', + left: '0', + width: `${(width ?? blankWidth) * scale}px`, + height: `${(height ?? blankHeight) * scale}px`, + }) + overlayer.redraw() + } + } + + const container= element.parentNode?.host + if (!container) return + const containerWidth = container.clientWidth + const containerHeight = container.clientHeight + container.scrollLeft = (element.clientWidth - containerWidth) / 2 + + return { + width: element.clientWidth, + height: element.clientHeight, + containerWidth, + containerHeight, + } + } + if (this.#center) { + const dimensions = transform({frame: this.#center, styles: { marginInline: 'auto' }}) + if (!dimensions) return renderPromises + const {width, height, containerWidth, containerHeight} = dimensions + this.#isOverflowX = width > containerWidth + this.#isOverflowY = height > containerHeight + } else { + const leftDimensions = transform({frame: left, styles: { marginInlineStart: 'auto' }}) + const rightDimensions = transform({frame: right, styles: { marginInlineEnd: 'auto' }}) + if (!leftDimensions || !rightDimensions) return renderPromises + const {width: leftWidth, height: leftHeight, containerWidth, containerHeight} = leftDimensions + const {width: rightWidth, height: rightHeight} = rightDimensions + this.#isOverflowX = leftWidth + rightWidth > containerWidth + this.#isOverflowY = Math.max(leftHeight, rightHeight) > containerHeight + } + return renderPromises + } + async #showSpread({ left, right, center, side, spreadIndex }) { + this.#left = null + this.#right = null + this.#center = null + + const cacheKey = spreadIndex !== undefined ? `spread-${spreadIndex}` : null + const prerendered = cacheKey ? this.#prerenderedSpreads.get(cacheKey) : null + + if (prerendered) { + this.#spreadAccessTime.set(cacheKey, Date.now()) + if (prerendered.center) { + this.#center = prerendered.center + } else { + this.#left = prerendered.left + this.#right = prerendered.right + } + } else { + if (center) { + this.#center = await this.#createFrame(center) + if (cacheKey) { + this.#prerenderedSpreads.set(cacheKey, { center: this.#center }) + this.#spreadAccessTime.set(cacheKey, Date.now()) + } + } else { + this.#left = await this.#createFrame(left) + this.#right = await this.#createFrame(right) + if (cacheKey) { + this.#prerenderedSpreads.set(cacheKey, { left: this.#left, right: this.#right }) + this.#spreadAccessTime.set(cacheKey, Date.now()) + } + } + } + + this.#side = center ? 'center' : this.#left?.blank ? 'right' + : this.#right?.blank ? 'left' : side + const visibleFrames = center + ? [this.#center?.element] + : [this.#left?.element, this.#right?.element] + + Array.from(this.#root.children).forEach(child => { + const isVisible = visibleFrames.includes(child) + Object.assign(child.style, { + position: isVisible ? 'relative' : 'absolute', + visibility: isVisible ? 'visible' : 'hidden', + pointerEvents: isVisible ? 'auto' : 'none', + }) + }) + + // Render layout and await any async onZoom callbacks (e.g. PDF text + // layer rendering) so the document is fully populated before overlayers + // try to resolve CFIs against it. + const renderPromises = this.#render() + if (renderPromises.length) await Promise.all(renderPromises) + + const showingFrames = center + ? [this.#center] + : [this.#left, this.#right] + for (const frame of showingFrames) { + if (!frame?.iframe) continue + const index = frame.iframe.dataset.sectionIndex != null + ? parseInt(frame.iframe.dataset.sectionIndex) : undefined + if (index != null && !this.#overlayers.has(index)) { + const doc = frame.iframe.contentDocument + if (doc) { + this.dispatchEvent(new CustomEvent('create-overlayer', { + detail: { + doc, index, + attach: overlayer => { + this.#overlayers.set(index, overlayer) + frame.element.append(overlayer.element) + }, + }, + })) + } + } + } + } + #initScrollMode(targetIndex = 0) { + const currentIndex = targetIndex + + // Hide all paginated content + for (const child of Array.from(this.#root.children)) { + child.style.display = 'none' + } + + this.#scrollContainer = document.createElement('div') + this.#scrollContainer.className = 'scroll-container' + this.#root.append(this.#scrollContainer) + + const sections = this.book.sections + const viewport = this.defaultViewport + const vw = viewport?.width ?? 1000 + const vh = viewport?.height ?? 1400 + this.#scrollPages = sections.map((section, i) => { + const el = document.createElement('div') + el.className = 'scroll-page' + el.dataset.index = i + this.#scrollContainer.append(el) + return { el, index: i, section, state: 'idle', frame: null, vpWidth: vw, vpHeight: vh } + }) + + this.#renderScrollMode() + + // Scroll to target position BEFORE setting up the observer + // so only pages near the target are observed as intersecting + if (currentIndex >= 0 && currentIndex < this.#scrollPages.length) { + this.#scrollPages[currentIndex].el.scrollIntoView() + this.#scrollCurrentIndex = currentIndex + } + + this.addEventListener('scroll', this.#handleScrollEvent) + + // Set up IntersectionObserver after scroll position is established. + // rootMargin '50%' loads ~1 page buffer above/below the viewport. + this.#scrollObserver = new IntersectionObserver(entries => { + for (const entry of entries) { + if (!entry.isIntersecting) continue + const index = parseInt(entry.target.dataset.index) + const pageData = this.#scrollPages[index] + if (pageData && pageData.state === 'idle') { + this.#loadScrollPage(pageData) + } + } + this.#evictScrollPages() + }, { root: this, rootMargin: '50% 0px' }) + + for (const page of this.#scrollPages) { + this.#scrollObserver.observe(page.el) + } + } + #handleScrollEvent = () => { + // Disable iframe interaction during scroll for native smooth scrolling + this.#setScrollIframeInteraction(false) + if (this.#scrollIdleTimer) clearTimeout(this.#scrollIdleTimer) + this.#scrollIdleTimer = setTimeout(() => { + this.#setScrollIframeInteraction(true) + // Report location only after scroll settles to avoid + // expensive React re-renders on every frame + this.#reportScrollLocation() + }, 150) + } + #setScrollIframeInteraction(enabled) { + const value = enabled ? 'auto' : '' + for (const page of this.#scrollPages) { + if (page.frame?.iframe) { + page.frame.iframe.style.pointerEvents = value + } + } + } + #destroyScrollMode() { + // Use the cached scroll index because by the time attributeChangedCallback + // fires, the CSS has already switched from block/scroll to flex layout, + // making #getScrollIndex() return incorrect positions + const currentIndex = this.#scrollCurrentIndex >= 0 + ? this.#scrollCurrentIndex : this.#getScrollIndex() + this.removeEventListener('scroll', this.#handleScrollEvent) + if (this.#scrollObserver) { + this.#scrollObserver.disconnect() + this.#scrollObserver = null + } + if (this.#scrollIdleTimer) { + clearTimeout(this.#scrollIdleTimer) + this.#scrollIdleTimer = null + } + // Clean up all scroll page frames and overlayers + for (const page of this.#scrollPages) { + this.#teardownScrollPage(page) + } + this.#scrollPages = [] + this.#scrollLoadGen.clear() + this.#scrollCurrentIndex = -1 + if (this.#scrollContainer) { + this.#scrollContainer.remove() + this.#scrollContainer = null + } + + // Reset scroll position left over from scroll mode + this.scrollTop = 0 + this.scrollLeft = 0 + + // Restore paginated content + for (const child of Array.from(this.#root.children)) { + child.style.display = '' + } + + // Navigate to the page we were on + if (currentIndex >= 0) { + const section = this.book.sections[currentIndex] + if (section) { + const spread = this.getSpreadOf(section) + if (spread) { + this.#index = -1 + this.goToSpread(spread.index, spread.side, 'page') + } + } + } + } + // Create an iframe directly inside the page placeholder (no reparenting) + async #createScrollFrame(pageData, srcOption) { + const srcOptionIsString = typeof srcOption === 'string' + const src = srcOptionIsString ? srcOption : srcOption?.src + const data = srcOptionIsString ? null : srcOption?.data + const onZoom = srcOptionIsString ? null : srcOption?.onZoom + + const element = document.createElement('div') + element.setAttribute('dir', 'ltr') + element.style.position = 'relative' + const iframe = document.createElement('iframe') + element.append(iframe) + Object.assign(iframe.style, { + border: '0', + display: 'none', + overflow: 'hidden', + }) + iframe.setAttribute('sandbox', 'allow-same-origin allow-scripts') + iframe.setAttribute('scrolling', 'no') + iframe.setAttribute('part', 'filter') + // Place directly in the placeholder — no root append + reparent + pageData.el.append(element) + + if (!src) return { blank: true, element, iframe } + return new Promise(resolve => { + iframe.addEventListener('load', () => { + const doc = iframe.contentDocument + iframe.dataset.sectionIndex = pageData.index + this.dispatchEvent(new CustomEvent('load', { detail: { doc, index: pageData.index } })) + const { width, height } = getViewport(doc, this.defaultViewport) + resolve({ + element, iframe, + width: parseFloat(width), + height: parseFloat(height), + onZoom, + }) + }, { once: true }) + if (data) { + iframe.srcdoc = data + } else { + iframe.src = src + } + }) + } + async #loadScrollPage(pageData) { + if (pageData.state !== 'idle') return + pageData.state = 'loading' + + // Generation counter to detect stale loads + const gen = (this.#scrollLoadGen.get(pageData.index) || 0) + 1 + this.#scrollLoadGen.set(pageData.index, gen) + + try { + const src = await pageData.section.load?.() + // Bail if cancelled or mode changed + if (this.#scrollLoadGen.get(pageData.index) !== gen || !this.#scrollMode) { + pageData.state = 'idle' + return + } + if (!src) { pageData.state = 'idle'; return } + + const frame = await this.#createScrollFrame(pageData, src) + // Bail if cancelled during frame creation + if (this.#scrollLoadGen.get(pageData.index) !== gen || !this.#scrollMode) { + frame.element?.remove() + pageData.state = 'idle' + return + } + + pageData.frame = frame + pageData.state = 'loaded' + const scrollAnchor = this.#captureScrollModeAnchor() + // Update dimensions from actual page viewport + if (frame.width && frame.height) { + pageData.vpWidth = frame.width + pageData.vpHeight = frame.height + } + this.#renderScrollPage(pageData) + this.#restoreScrollModeAnchor(scrollAnchor) + + // Create overlayer + const doc = frame.iframe.contentDocument + if (doc) { + this.dispatchEvent(new CustomEvent('create-overlayer', { + detail: { + doc, index: pageData.index, + attach: overlayer => { + this.#overlayers.set(pageData.index, overlayer) + frame.element.append(overlayer.element) + }, + }, + })) + // Forward wheel events to host when iframe has pointer-events + // (fallback for the brief window after scroll settles) + doc.addEventListener('wheel', e => { + // Disable pointer-events immediately so subsequent + // wheel ticks use native scroll + this.#setScrollIframeInteraction(false) + this.scrollBy({ top: e.deltaY, left: e.deltaX, behavior: 'instant' }) + }, { passive: true }) + } + } catch (e) { + console.warn('Failed to load scroll page', pageData.index, e) + pageData.state = 'idle' + } + } + // Remove a loaded scroll page's frame and overlayer + #teardownScrollPage(pageData) { + // Bump generation to cancel any in-progress load + const gen = (this.#scrollLoadGen.get(pageData.index) || 0) + 1 + this.#scrollLoadGen.set(pageData.index, gen) + + if (pageData.frame) { + const idx = pageData.index + this.#overlayers.delete(idx) + pageData.frame.element?.remove() + } + pageData.frame = null + pageData.state = 'idle' + } + // Evict the farthest loaded pages when over limit + #evictScrollPages() { + const loaded = this.#scrollPages.filter(p => p.state === 'loaded') + if (loaded.length <= this.#scrollMaxLoaded) return + const currentIndex = this.#getScrollIndex() + loaded.sort((a, b) => + Math.abs(a.index - currentIndex) - Math.abs(b.index - currentIndex)) + for (const page of loaded.slice(this.#scrollMaxLoaded)) { + this.#teardownScrollPage(page) + } + } + #renderScrollMode() { + const { width: hostWidth } = this.getBoundingClientRect() + if (!hostWidth) return + const scrollAnchor = this.#captureScrollModeAnchor() + for (const page of this.#scrollPages) { + const scale = (hostWidth / page.vpWidth) * this.#scaleFactor + page.el.style.width = `${page.vpWidth * scale}px` + page.el.style.height = `${page.vpHeight * scale}px` + if (page.state === 'loaded' && page.frame) { + this.#renderScrollPage(page) + } + } + this.#restoreScrollModeAnchor(scrollAnchor) + } + #renderScrollPage(pageData) { + const { width: hostWidth } = this.getBoundingClientRect() + if (!hostWidth || !pageData.frame) return + const { vpWidth: vw, vpHeight: vh, frame } = pageData + const scale = (hostWidth / vw) * this.#scaleFactor + + if (frame.onZoom) { + frame.onZoom({ doc: frame.iframe.contentDocument, scale, pageColors: this.#pageColors }) + Object.assign(frame.iframe.style, { + width: `${vw * scale}px`, + height: `${vh * scale}px`, + transform: 'none', + display: 'block', + }) + } else { + Object.assign(frame.iframe.style, { + width: `${vw}px`, + height: `${vh}px`, + transform: `scale(${scale})`, + transformOrigin: 'top left', + display: 'block', + }) + } + Object.assign(frame.element.style, { + width: `${vw * scale}px`, + height: `${vh * scale}px`, + }) + // Update placeholder to match actual page dimensions + pageData.el.style.width = `${vw * scale}px` + pageData.el.style.height = `${vh * scale}px` + + const overlayer = this.#overlayers.get(pageData.index) + if (overlayer) { + Object.assign(overlayer.element.style, { + position: 'absolute', + top: '0', + left: '0', + width: `${vw * scale}px`, + height: `${vh * scale}px`, + }) + overlayer.redraw() + } + } + #getScrollIndex() { + if (!this.#scrollPages.length) return -1 + const hostRect = this.getBoundingClientRect() + const midY = hostRect.top + hostRect.height / 2 + for (const page of this.#scrollPages) { + const rect = page.el.getBoundingClientRect() + if (rect.top <= midY && rect.bottom >= midY) return page.index + } + let closest = 0, minDist = Infinity + for (const page of this.#scrollPages) { + const rect = page.el.getBoundingClientRect() + const dist = Math.abs(rect.top + rect.height / 2 - midY) + if (dist < minDist) { minDist = dist; closest = page.index } + } + return closest + } + #reportScrollLocation() { + const index = this.#getScrollIndex() + if (index < 0) return + this.#scrollCurrentIndex = index + this.dispatchEvent(new CustomEvent('relocate', { detail: + { reason: 'scroll', range: null, index, fraction: 0, size: 1 } })) + } + #goLeft() { + if (this.#center || this.#left?.blank) return + if (this.#portrait && this.#left?.element?.style?.display === 'none') { + this.#side = 'left' + this.#render() + this.#reportLocation('page') + return true + } + } + #goRight() { + if (this.#center || this.#right?.blank) return + if (this.#portrait && this.#right?.element?.style?.display === 'none') { + this.#side = 'right' + this.#render() + this.#reportLocation('page') + return true + } + } + open(book) { + this.book = book + this.defaultViewport = book.rendition?.viewport + this.rtl = book.dir === 'rtl' + + this.#spread() + if (this.#scrollMode) this.#initScrollMode() + } + #spread(mode) { + const book = this.book + const { rendition } = book + const rtl = this.rtl + const ltr = !rtl + this.spread = mode || rendition?.spread + + if (this.spread === 'none') + this.#spreads = book.sections.map(section => ({ center: section })) + else this.#spreads = book.sections.reduce((arr, section, i) => { + const last = arr[arr.length - 1] + const { pageSpread } = section + const newSpread = () => { + const spread = {} + arr.push(spread) + return spread + } + if (pageSpread === 'center') { + const spread = last.left || last.right ? newSpread() : last + spread.center = section + } + else if (pageSpread === 'left') { + const spread = last.center || last.left || ltr && i ? newSpread() : last + spread.left = section + } + else if (pageSpread === 'right') { + const spread = last.center || last.right || rtl && i ? newSpread() : last + spread.right = section + } + else if (ltr) { + if (last.center || last.right) newSpread().left = section + else if (last.left || !i) last.right = section + else last.left = section + } + else { + if (last.center || last.left) newSpread().right = section + else if (last.right || !i) last.left = section + else last.right = section + } + return arr + }, [{}]) + } + #respread(spreadMode) { + if (this.#index === -1) return + const section = this.book.sections[this.index] + this.#spread(spreadMode) + const { index } = this.getSpreadOf(section) + this.#index = -1 + this.#preloadCache.clear() + for (const frames of this.#prerenderedSpreads.values()) { + if (frames.center) { + frames.center.element?.remove() + } else { + frames.left?.element?.remove() + frames.right?.element?.remove() + } + } + this.#prerenderedSpreads.clear() + this.#spreadAccessTime.clear() + this.#overlayers.clear() + this.goToSpread(index, this.rtl ? 'right' : 'left', 'page') + } + get index() { + if (this.#scrollMode) return this.#scrollCurrentIndex >= 0 + ? this.#scrollCurrentIndex : this.#getScrollIndex() + if (this.#index < 0 || !this.#spreads) return -1 + const spread = this.#spreads[this.#index] + if (!spread) return -1 + const section = spread.center ?? (this.#side === 'left' + ? spread.left ?? spread.right : spread.right ?? spread.left) + return this.book.sections.indexOf(section) + } + get pageColors() { + return this.#pageColors + } + set pageColors(value) { + this.#pageColors = value + this.#render() + } + get scrolled() { + return this.#scrollMode + } + get scrollLocked() { + return this.#scrollLocked + } + set scrollLocked(value) { + this.#scrollLocked = value + } + get isOverflowX() { + return this.#isOverflowX + } + get isOverflowY() { + return this.#isOverflowY + } + get atStart() { + if (this.#scrollMode) return this.scrollTop <= 0 + return this.#index <= 0 + } + get atEnd() { + if (this.#scrollMode) return this.scrollTop + this.clientHeight >= this.scrollHeight - 2 + return this.#index >= this.#spreads.length - 1 + } + #reportLocation(reason) { + this.dispatchEvent(new CustomEvent('relocate', { detail: + { reason, range: null, index: this.index, fraction: 0, size: 1 } })) + } + getSpreadOf(section) { + const spreads = this.#spreads + for (let index = 0; index < spreads.length; index++) { + const { left, right, center } = spreads[index] + if (left === section) return { index, side: 'left' } + if (right === section) return { index, side: 'right' } + if (center === section) return { index, side: 'center' } + } + } + async goToSpread(index, side, reason) { + if (index < 0 || index > this.#spreads.length - 1) return + if (index === this.#index) { + this.#render(side) + return + } + this.#index = index + const spread = this.#spreads[index] + const cacheKey = `spread-${index}` + const cached = this.#preloadCache.get(cacheKey) + if (cached && cached !== 'loading') { + if (cached.center) { + const sectionIndex = this.book.sections.indexOf(spread.center) + await this.#showSpread({ center: { index: sectionIndex, src: cached.center }, spreadIndex: index, side }) + } else { + const indexL = this.book.sections.indexOf(spread.left) + const indexR = this.book.sections.indexOf(spread.right) + const left = { index: indexL, src: cached.left } + const right = { index: indexR, src: cached.right } + await this.#showSpread({ left, right, side, spreadIndex: index }) + } + } else { + if (spread.center) { + const sectionIndex = this.book.sections.indexOf(spread.center) + const src = await spread.center?.load?.() + await this.#showSpread({ center: { index: sectionIndex, src }, spreadIndex: index, side }) + } else { + const indexL = this.book.sections.indexOf(spread.left) + const indexR = this.book.sections.indexOf(spread.right) + const srcL = await spread.left?.load?.() + const srcR = await spread.right?.load?.() + const left = { index: indexL, src: srcL } + const right = { index: indexR, src: srcR } + await this.#showSpread({ left, right, side, spreadIndex: index }) + } + } + + this.#reportLocation(reason) + this.#preloadNextSpreads() + } + #preloadNextSpreads() { + this.#cleanupPreloadCache() + + if (this.#numPrerenderedSpreads <= 0) return + + const toPreload = [] + const forwardPreloadCount = Math.max(1, this.#numPrerenderedSpreads - 1) + const backwardPreloadCount = Math.max(0, this.#numPrerenderedSpreads - forwardPreloadCount) + for (let distance = 1; distance <= forwardPreloadCount; distance++) { + const forwardIndex = this.#index + distance + if (forwardIndex >= 0 && forwardIndex < this.#spreads.length) { + toPreload.push({ index: forwardIndex, direction: 'forward', distance }) + } + } + for (let distance = 1; distance <= backwardPreloadCount; distance++) { + const backwardIndex = this.#index - distance + if (backwardIndex >= 0 && backwardIndex < this.#spreads.length) { + toPreload.push({ index: backwardIndex, direction: 'backward', distance }) + } + } + for (const { index: targetIndex, direction } of toPreload) { + const cacheKey = `spread-${targetIndex}` + if (this.#prerenderedSpreads.has(cacheKey)) continue + const spread = this.#spreads[targetIndex] + if (!spread) continue + this.#preloadQueue.push({ targetIndex, direction, spread, cacheKey }) + } + + this.#processPreloadQueue() + } + + async #processPreloadQueue() { + while (this.#preloadQueue.length > 0 && this.#activePreloads < this.#maxConcurrentPreloads) { + const task = this.#preloadQueue.shift() + if (!task) break + + const { spread, cacheKey } = task + this.#preloadCache.set(cacheKey, 'loading') + this.#activePreloads++ + Promise.resolve().then(async () => { + try { + if (spread.center) { + const src = await spread.center?.load?.() + this.#preloadCache.set(cacheKey, { center: src }) + + const sectionIndex = this.book.sections.indexOf(spread.center) + const frame = await this.#createFrame({ index: sectionIndex, src, detached: true }) + + this.#prerenderedSpreads.set(cacheKey, { center: frame }) + this.#spreadAccessTime.set(cacheKey, Date.now()) + if (frame.onZoom) { + const doc = frame.iframe.contentDocument + frame.onZoom({ doc, scale: this.#totalScaleFactor, pageColors: this.#pageColors }) + } + } else { + const srcL = await spread.left?.load?.() + const srcR = await spread.right?.load?.() + this.#preloadCache.set(cacheKey, { left: srcL, right: srcR }) + + const indexL = this.book.sections.indexOf(spread.left) + const indexR = this.book.sections.indexOf(spread.right) + const leftFrame = await this.#createFrame({ index: indexL, src: srcL, detached: true }) + const rightFrame = await this.#createFrame({ index: indexR, src: srcR, detached: true }) + + this.#prerenderedSpreads.set(cacheKey, { left: leftFrame, right: rightFrame }) + this.#spreadAccessTime.set(cacheKey, Date.now()) + + if (leftFrame.onZoom) { + const docL = leftFrame.iframe.contentDocument + leftFrame.onZoom({ doc: docL, scale: this.#totalScaleFactor, pageColors: this.#pageColors }) + } + if (rightFrame.onZoom) { + const docR = rightFrame.iframe.contentDocument + rightFrame.onZoom({ doc: docR, scale: this.#totalScaleFactor, pageColors: this.#pageColors }) + } + } + } catch { + this.#preloadCache.delete(cacheKey) + this.#prerenderedSpreads.delete(cacheKey) + } finally { + this.#activePreloads-- + this.#processPreloadQueue() + } + }) + } + } + #cleanupPreloadCache() { + const maxSpreads = this.#maxCachedSpreads + if (this.#prerenderedSpreads.size <= maxSpreads) { + return + } + + const framesByAge = Array.from(this.#prerenderedSpreads.keys()) + .map(key => ({ + key, + accessTime: this.#spreadAccessTime.get(key) || 0, + })) + .sort((a, b) => a.accessTime - b.accessTime) + + const numToRemove = this.#prerenderedSpreads.size - maxSpreads + const framesToDelete = framesByAge.slice(0, numToRemove).map(item => item.key) + + if (framesToDelete.length > 0) { + framesToDelete.forEach(key => { + const frames = this.#prerenderedSpreads.get(key) + if (frames) { + if (frames.center) { + this.#removeOverlayerForFrame(frames.center) + frames.center.element?.remove() + } else { + this.#removeOverlayerForFrame(frames.left) + this.#removeOverlayerForFrame(frames.right) + frames.left?.element?.remove() + frames.right?.element?.remove() + } + } + + this.#prerenderedSpreads.delete(key) + this.#spreadAccessTime.delete(key) + this.#preloadCache.delete(key) + }) + } + } + #removeOverlayerForFrame(frame) { + if (!frame?.iframe) return + const idx = frame.iframe.dataset.sectionIndex != null + ? parseInt(frame.iframe.dataset.sectionIndex) : undefined + if (idx != null) this.#overlayers.delete(idx) + } + // Drop a frame's overlayer and re-emit create-overlayer so listeners can + // re-add annotations. Called after a text layer rebuild (e.g. pdf.js + // onZoom) which invalidates Range objects stored in the overlayer. + #refreshOverlayerForFrame(frame) { + if (!frame?.iframe) return + const index = frame.iframe.dataset.sectionIndex != null + ? parseInt(frame.iframe.dataset.sectionIndex) : undefined + if (index == null) return + const stale = this.#overlayers.get(index) + if (!stale) return + // Only refresh for frames currently visible; hidden frames keep their + // overlayer untouched until they are shown again. + const isVisible = frame.element?.parentNode + && frame.element.style.visibility !== 'hidden' + if (!isVisible) return + stale.element?.remove() + this.#overlayers.delete(index) + const doc = frame.iframe.contentDocument + if (!doc) return + this.dispatchEvent(new CustomEvent('create-overlayer', { + detail: { + doc, index, + attach: overlayer => { + this.#overlayers.set(index, overlayer) + frame.element.append(overlayer.element) + }, + }, + })) + } + async select(target) { + await this.goTo(target) + // TODO + } + async goTo(target) { + const resolved = await target + if (this.#scrollMode) { + const page = this.#scrollPages[resolved.index] + if (page) { + page.el.scrollIntoView() + this.#scrollCurrentIndex = resolved.index + } + return + } + const { book } = this + const section = book.sections[resolved.index] + if (!section) return + const { index, side } = this.getSpreadOf(section) + await this.goToSpread(index, side) + } + async next(distance) { + if (this.#scrollMode) { + this.scrollBy({ top: distance || this.clientHeight, behavior: 'smooth' }) + return + } + const s = this.rtl ? this.#goLeft() : this.#goRight() + if (!s) return this.goToSpread(this.#index + 1, this.rtl ? 'right' : 'left', 'page') + } + async prev(distance) { + if (this.#scrollMode) { + this.scrollBy({ top: -(distance || this.clientHeight), behavior: 'smooth' }) + return + } + const s = this.rtl ? this.#goRight() : this.#goLeft() + if (!s) return this.goToSpread(this.#index - 1, this.rtl ? 'left' : 'right', 'page') + } + nextSection() { + if (!this.#scrollMode) return + const currentIndex = this.#getScrollIndex() + const nextIndex = Math.min(currentIndex + 1, this.#scrollPages.length - 1) + this.#scrollPages[nextIndex]?.el.scrollIntoView({ behavior: 'smooth' }) + this.#scrollCurrentIndex = nextIndex + } + prevSection() { + if (!this.#scrollMode) return + const currentIndex = this.#getScrollIndex() + const prevIndex = Math.max(currentIndex - 1, 0) + this.#scrollPages[prevIndex]?.el.scrollIntoView({ behavior: 'smooth' }) + this.#scrollCurrentIndex = prevIndex + } + async pan(dx, dy) { + if (this.#scrollMode) { + this.scrollBy({ top: dy, left: dx, behavior: 'auto' }) + return + } + if (this.#scrollLocked) return + this.#scrollLocked = true + + const transform = frame => { + let { element, iframe } = frame + if (!iframe || !element) return + + const scrollableContainer = element.parentNode.host + scrollableContainer.scrollLeft += dx + scrollableContainer.scrollTop += dy + } + + transform(this.#center ?? this.#right ?? {}) + this.#scrollLocked = false + } + getContents() { + if (this.#scrollMode) { + return this.#scrollPages + .filter(p => p.state === 'loaded' && p.frame?.iframe) + .map(p => ({ + doc: p.frame.iframe.contentDocument, + index: p.index, + overlayer: this.#overlayers.get(p.index), + })) + } + return Array.from(this.#root.querySelectorAll('iframe')) + .filter(frame => { + const parent = frame.parentElement + return parent && parent.style.visibility !== 'hidden' + }) + .map(frame => { + const index = frame.dataset.sectionIndex != null + ? parseInt(frame.dataset.sectionIndex) : undefined + return { + doc: frame.contentDocument, + index, + overlayer: index != null ? this.#overlayers.get(index) : undefined, + } + }) + } + pinchZoom(ratio) { + const frames = this.#center + ? [this.#center] + : [this.#left, this.#right] + for (const frame of frames) { + if (!frame?.element || frame.element.style.visibility === 'hidden') continue + frame.element.style.transform = `scale(${ratio})` + frame.element.style.transformOrigin = 'center' + } + } + pinchEnd() { + for (const frame of [this.#center, this.#left, this.#right]) { + if (!frame?.element) continue + frame.element.style.removeProperty('transform') + frame.element.style.removeProperty('transform-origin') + } + } + get size() { + return this.clientHeight + } + get viewSize() { + return this.#scrollMode ? this.scrollHeight : this.clientHeight + } + get start() { + return this.#scrollMode ? this.scrollTop : 0 + } + get end() { + return this.#scrollMode ? this.scrollTop + this.clientHeight : this.clientHeight + } + get page() { + if (this.#scrollMode) return this.#scrollCurrentIndex >= 0 + ? this.#scrollCurrentIndex : this.#getScrollIndex() + return this.#index + } + get pages() { + if (this.#scrollMode) return this.#scrollPages.length + return this.#spreads?.length ?? 0 + } + get containerPosition() { + return 0 + } + get sideProp() { + return this.#scrollMode ? 'height' : 'width' + } + destroy() { + this.#observer.unobserve(this) + if (this.#scrollMode) { + this.removeEventListener('scroll', this.#handleScrollEvent) + if (this.#scrollObserver) { + this.#scrollObserver.disconnect() + this.#scrollObserver = null + } + if (this.#scrollIdleTimer) { + clearTimeout(this.#scrollIdleTimer) + this.#scrollIdleTimer = null + } + for (const page of this.#scrollPages) { + this.#teardownScrollPage(page) + } + this.#scrollPages = [] + this.#scrollLoadGen.clear() + if (this.#scrollContainer) { + this.#scrollContainer.remove() + this.#scrollContainer = null + } + } + for (const frames of this.#prerenderedSpreads.values()) { + if (frames.center) { + frames.center.element?.remove() + } else { + frames.left?.element?.remove() + frames.right?.element?.remove() + } + } + this.#prerenderedSpreads.clear() + this.#preloadCache.clear() + this.#spreadAccessTime.clear() + this.#overlayers.clear() + } +} + +customElements.define('foliate-fxl', FixedLayout) diff --git a/web/vendor/foliate-js/footnotes.js b/web/vendor/foliate-js/footnotes.js new file mode 100644 index 00000000..836e3601 --- /dev/null +++ b/web/vendor/foliate-js/footnotes.js @@ -0,0 +1,152 @@ +const getTypes = el => new Set([ + ...(el?.getAttributeNS?.('http://www.idpf.org/2007/ops', 'type')?.split(' ') ?? []), + ...(el?.attributes?.getNamedItem?.('epub:type')?.value?.split(' ') ?? []), +]) +const getRoles = el => new Set(el?.getAttribute?.('role')?.split(' ')) + +const isSuper = el => { + if (el.matches('sup')) return true + const { verticalAlign } = getComputedStyle(el) + return verticalAlign === 'super' + || verticalAlign === 'top' + || verticalAlign === 'text-top' + || /^\d/.test(verticalAlign) +} + +const refTypes = ['biblioref', 'glossref', 'noteref'] +const refRoles = ['doc-biblioref', 'doc-glossref', 'doc-noteref'] +const isFootnoteReference = a => { + const types = getTypes(a) + const roles = getRoles(a) + return { + yes: refRoles.some(r => roles.has(r)) || refTypes.some(t => types.has(t)), + maybe: () => !types.has('backlink') && !roles.has('doc-backlink') + && (isSuper(a) || a.children.length === 1 && isSuper(a.children[0]) + || isSuper(a.parentElement)), + } +} + +const getReferencedType = el => { + const types = getTypes(el) + const roles = getRoles(el) + return roles.has('doc-biblioentry') || types.has('biblioentry') ? 'biblioentry' + : roles.has('definition') || types.has('glossdef') ? 'definition' + : roles.has('doc-endnote') || types.has('endnote') || types.has('rearnote') ? 'endnote' + : roles.has('doc-footnote') || types.has('footnote') ? 'footnote' + : roles.has('note') || types.has('note') ? 'note' : null +} + +const isInline = 'a, span, sup, sub, em, strong, i, b, small, big' +const extractFootnote = (doc, anchor) => { + let el = anchor(doc) + const target = el + while (el.matches(isInline)) { + const parent = el.parentElement + if (!parent) break + el = parent + } + if (el === doc.body) { + const sibling = target.nextElementSibling + if (sibling && !sibling.matches(isInline)) return sibling + throw new Error('Failed to extract footnote') + } + return el +} + +export class FootnoteHandler extends EventTarget { + detectFootnotes = true + #showFragment(book, { index, anchor, check }, href) { + const view = document.createElement('foliate-view') + return new Promise((resolve, reject) => { + view.addEventListener('load', e => { + try { + const { doc } = e.detail + const el = anchor(doc) + const type = getReferencedType(el) + const hidden = el?.matches?.('aside') && type === 'footnote' + if (el) { + let range + if (el.startContainer) { + range = el + } else if (el.matches('li, aside')) { + range = doc.createRange() + range.selectNodeContents(el) + } else if (el.matches('dt')) { + range = doc.createRange() + range.setStartBefore(el) + let sibling = el.nextElementSibling + let lastDD = null + while (sibling && sibling.matches('dd')) { + lastDD = sibling + sibling = sibling.nextElementSibling + } + range.setEndAfter(lastDD || el) + } else if (el.closest('li')) { + range = doc.createRange() + range.selectNodeContents(el.closest('li')) + } else if (el.closest('.note')) { + range = doc.createRange() + range.selectNodeContents(el.closest('.note')) + } else if (el.querySelector('a')) { + range = doc.createRange() + range.setStartBefore(el) + let next = el.nextElementSibling + while (next) { + if (next.querySelector('a')) break + next = next.nextElementSibling + } + if (next) { + range.setEndBefore(next) + } else { + range.setEndAfter(el.parentNode.lastChild) + } + if (check && el.children.length > 3) { + reject(new Error('Failed to locate footnote content')) + return + } + } else if (check) { + reject(new Error('Failed to locate footnote content')) + return + } else { + range = doc.createRange() + const hasContent = el.textContent?.trim() || el.children.length > 0 + if (!hasContent && el.parentElement) { + range.selectNodeContents(el.parentElement) + } else { + range.selectNode(el) + } + } + const frag = range.extractContents() + doc.body.replaceChildren() + doc.body.appendChild(frag) + } + const detail = { view, href, type, hidden, target: el } + this.dispatchEvent(new CustomEvent('render', { detail })) + resolve() + } catch (e) { + reject(e) + } + }) + view.open(book) + .then(() => this.dispatchEvent(new CustomEvent('before-render', { detail: { view } }))) + .then(() => view.goTo(index)) + .catch(reject) + }) + } + handle(book, e) { + const { a, href, follow, check } = e.detail + const { yes, maybe } = isFootnoteReference(a) + if (yes || follow) { + e.preventDefault() + return Promise.resolve(book.resolveHref(href)).then(target => + this.#showFragment(book, target, href)) + } + else if (this.detectFootnotes && (maybe() || check)) { + e.preventDefault() + return Promise.resolve(book.resolveHref(href)).then(({ index, anchor }) => { + const target = { index, anchor: doc => extractFootnote(doc, anchor), check } + return this.#showFragment(book, target, href) + }) + } + } +} diff --git a/web/vendor/foliate-js/mobi.js b/web/vendor/foliate-js/mobi.js new file mode 100644 index 00000000..99ceb36f --- /dev/null +++ b/web/vendor/foliate-js/mobi.js @@ -0,0 +1,1242 @@ +const unescapeHTML = str => { + if (!str) return '' + const textarea = document.createElement('textarea') + textarea.innerHTML = str + return textarea.value +} + +const MIME = { + XML: 'application/xml', + XHTML: 'application/xhtml+xml', + HTML: 'text/html', + CSS: 'text/css', + SVG: 'image/svg+xml', +} + +const PDB_HEADER = { + name: [0, 32, 'string'], + type: [60, 4, 'string'], + creator: [64, 4, 'string'], + numRecords: [76, 2, 'uint'], +} + +const PALMDOC_HEADER = { + compression: [0, 2, 'uint'], + numTextRecords: [8, 2, 'uint'], + recordSize: [10, 2, 'uint'], + encryption: [12, 2, 'uint'], +} + +const MOBI_HEADER = { + magic: [16, 4, 'string'], + length: [20, 4, 'uint'], + type: [24, 4, 'uint'], + encoding: [28, 4, 'uint'], + uid: [32, 4, 'uint'], + version: [36, 4, 'uint'], + titleOffset: [84, 4, 'uint'], + titleLength: [88, 4, 'uint'], + localeRegion: [94, 1, 'uint'], + localeLanguage: [95, 1, 'uint'], + resourceStart: [108, 4, 'uint'], + huffcdic: [112, 4, 'uint'], + numHuffcdic: [116, 4, 'uint'], + exthFlag: [128, 4, 'uint'], + trailingFlags: [240, 4, 'uint'], + indx: [244, 4, 'uint'], +} + +const KF8_HEADER = { + resourceStart: [108, 4, 'uint'], + fdst: [192, 4, 'uint'], + numFdst: [196, 4, 'uint'], + frag: [248, 4, 'uint'], + skel: [252, 4, 'uint'], + guide: [260, 4, 'uint'], +} + +const EXTH_HEADER = { + magic: [0, 4, 'string'], + length: [4, 4, 'uint'], + count: [8, 4, 'uint'], +} + +const INDX_HEADER = { + magic: [0, 4, 'string'], + length: [4, 4, 'uint'], + type: [8, 4, 'uint'], + idxt: [20, 4, 'uint'], + numRecords: [24, 4, 'uint'], + encoding: [28, 4, 'uint'], + language: [32, 4, 'uint'], + total: [36, 4, 'uint'], + ordt: [40, 4, 'uint'], + ligt: [44, 4, 'uint'], + numLigt: [48, 4, 'uint'], + numCncx: [52, 4, 'uint'], +} + +const TAGX_HEADER = { + magic: [0, 4, 'string'], + length: [4, 4, 'uint'], + numControlBytes: [8, 4, 'uint'], +} + +const HUFF_HEADER = { + magic: [0, 4, 'string'], + offset1: [8, 4, 'uint'], + offset2: [12, 4, 'uint'], +} + +const CDIC_HEADER = { + magic: [0, 4, 'string'], + length: [4, 4, 'uint'], + numEntries: [8, 4, 'uint'], + codeLength: [12, 4, 'uint'], +} + +const FDST_HEADER = { + magic: [0, 4, 'string'], + numEntries: [8, 4, 'uint'], +} + +const FONT_HEADER = { + flags: [8, 4, 'uint'], + dataStart: [12, 4, 'uint'], + keyLength: [16, 4, 'uint'], + keyStart: [20, 4, 'uint'], +} + +const MOBI_ENCODING = { + 1252: 'windows-1252', + 65001: 'utf-8', +} + +const EXTH_RECORD_TYPE = { + 100: ['creator', 'string', true], + 101: ['publisher'], + 103: ['description'], + 104: ['isbn'], + 105: ['subject', 'string', true], + 106: ['date'], + 108: ['contributor', 'string', true], + 109: ['rights'], + 110: ['subjectCode', 'string', true], + 112: ['source', 'string', true], + 113: ['asin'], + 121: ['boundary', 'uint'], + 122: ['fixedLayout'], + 125: ['numResources', 'uint'], + 126: ['originalResolution'], + 127: ['zeroGutter'], + 128: ['zeroMargin'], + 129: ['coverURI'], + 132: ['regionMagnification'], + 201: ['coverOffset', 'uint'], + 202: ['thumbnailOffset', 'uint'], + 503: ['title'], + 524: ['language', 'string', true], + 527: ['pageProgressionDirection'], +} + +const MOBI_LANG = { + 1: ['ar', 'ar-SA', 'ar-IQ', 'ar-EG', 'ar-LY', 'ar-DZ', 'ar-MA', 'ar-TN', 'ar-OM', + 'ar-YE', 'ar-SY', 'ar-JO', 'ar-LB', 'ar-KW', 'ar-AE', 'ar-BH', 'ar-QA'], + 2: ['bg'], 3: ['ca'], 4: ['zh', 'zh-TW', 'zh-CN', 'zh-HK', 'zh-SG'], 5: ['cs'], + 6: ['da'], 7: ['de', 'de-DE', 'de-CH', 'de-AT', 'de-LU', 'de-LI'], 8: ['el'], + 9: ['en', 'en-US', 'en-GB', 'en-AU', 'en-CA', 'en-NZ', 'en-IE', 'en-ZA', + 'en-JM', null, 'en-BZ', 'en-TT', 'en-ZW', 'en-PH'], + 10: ['es', 'es-ES', 'es-MX', null, 'es-GT', 'es-CR', 'es-PA', 'es-DO', + 'es-VE', 'es-CO', 'es-PE', 'es-AR', 'es-EC', 'es-CL', 'es-UY', 'es-PY', + 'es-BO', 'es-SV', 'es-HN', 'es-NI', 'es-PR'], + 11: ['fi'], 12: ['fr', 'fr-FR', 'fr-BE', 'fr-CA', 'fr-CH', 'fr-LU', 'fr-MC'], + 13: ['he'], 14: ['hu'], 15: ['is'], 16: ['it', 'it-IT', 'it-CH'], + 17: ['ja'], 18: ['ko'], 19: ['nl', 'nl-NL', 'nl-BE'], 20: ['no', 'nb', 'nn'], + 21: ['pl'], 22: ['pt', 'pt-BR', 'pt-PT'], 23: ['rm'], 24: ['ro'], 25: ['ru'], + 26: ['hr', null, 'sr'], 27: ['sk'], 28: ['sq'], 29: ['sv', 'sv-SE', 'sv-FI'], + 30: ['th'], 31: ['tr'], 32: ['ur'], 33: ['id'], 34: ['uk'], 35: ['be'], + 36: ['sl'], 37: ['et'], 38: ['lv'], 39: ['lt'], 41: ['fa'], 42: ['vi'], + 43: ['hy'], 44: ['az'], 45: ['eu'], 46: ['hsb'], 47: ['mk'], 48: ['st'], + 49: ['ts'], 50: ['tn'], 52: ['xh'], 53: ['zu'], 54: ['af'], 55: ['ka'], + 56: ['fo'], 57: ['hi'], 58: ['mt'], 59: ['se'], 62: ['ms'], 63: ['kk'], + 65: ['sw'], 67: ['uz', null, 'uz-UZ'], 68: ['tt'], 69: ['bn'], 70: ['pa'], + 71: ['gu'], 72: ['or'], 73: ['ta'], 74: ['te'], 75: ['kn'], 76: ['ml'], + 77: ['as'], 78: ['mr'], 79: ['sa'], 82: ['cy', 'cy-GB'], 83: ['gl', 'gl-ES'], + 87: ['kok'], 97: ['ne'], 98: ['fy'], +} + +const concatTypedArray = (a, b) => { + const result = new a.constructor(a.length + b.length) + result.set(a) + result.set(b, a.length) + return result +} +const concatTypedArray3 = (a, b, c) => { + const result = new a.constructor(a.length + b.length + c.length) + result.set(a) + result.set(b, a.length) + result.set(c, a.length + b.length) + return result +} + +const decoder = new TextDecoder() +const getString = buffer => decoder.decode(buffer) +const getUint = buffer => { + if (!buffer) return + const l = buffer.byteLength + const func = l === 4 ? 'getUint32' : l === 2 ? 'getUint16' : 'getUint8' + return new DataView(buffer)[func](0) +} +const getStruct = (def, buffer) => Object.fromEntries(Array.from(Object.entries(def)) + .map(([key, [start, len, type]]) => [key, + (type === 'string' ? getString : getUint)(buffer.slice(start, start + len))])) + +const getDecoder = x => new TextDecoder(MOBI_ENCODING[x]) + +const getVarLen = (byteArray, i = 0) => { + let value = 0, length = 0 + for (const byte of byteArray.subarray(i, i + 4)) { + value = (value << 7) | (byte & 0b111_1111) >>> 0 + length++ + if (byte & 0b1000_0000) break + } + return { value, length } +} + +// variable-length quantity, but read from the end of data +const getVarLenFromEnd = byteArray => { + let value = 0 + for (const byte of byteArray.subarray(-4)) { + // `byte & 0b1000_0000` indicates the start of value + if (byte & 0b1000_0000) value = 0 + value = (value << 7) | (byte & 0b111_1111) + } + return value +} + +const countBitsSet = x => { + let count = 0 + for (; x > 0; x = x >> 1) if ((x & 1) === 1) count++ + return count +} + +const countUnsetEnd = x => { + let count = 0 + while ((x & 1) === 0) x = x >> 1, count++ + return count +} + +const decompressPalmDOC = array => { + let output = [] + for (let i = 0; i < array.length; i++) { + const byte = array[i] + if (byte === 0) output.push(0) // uncompressed literal, just copy it + else if (byte <= 8) // copy next 1-8 bytes + for (const x of array.subarray(i + 1, (i += byte) + 1)) + output.push(x) + else if (byte <= 0b0111_1111) output.push(byte) // uncompressed literal + else if (byte <= 0b1011_1111) { + // 1st and 2nd bits are 10, meaning this is a length-distance pair + // read next byte and combine it with current byte + const bytes = (byte << 8) | array[i++ + 1] + // the 3rd to 13th bits encode distance + const distance = (bytes & 0b0011_1111_1111_1111) >>> 3 + // the last 3 bits, plus 3, is the length to copy + const length = (bytes & 0b111) + 3 + for (let j = 0; j < length; j++) + output.push(output[output.length - distance]) + } + // compressed from space plus char + else output.push(32, byte ^ 0b1000_0000) + } + return Uint8Array.from(output) +} + +const read32Bits = (byteArray, from) => { + const startByte = from >> 3 + const end = from + 32 + const endByte = end >> 3 + let bits = 0n + for (let i = startByte; i <= endByte; i++) + bits = bits << 8n | BigInt(byteArray[i] ?? 0) + return (bits >> (8n - BigInt(end & 7))) & 0xffffffffn +} + +const huffcdic = async (mobi, loadRecord) => { + const huffRecord = await loadRecord(mobi.huffcdic) + const { magic, offset1, offset2 } = getStruct(HUFF_HEADER, huffRecord) + if (magic !== 'HUFF') throw new Error('Invalid HUFF record') + + // table1 is indexed by byte value + const table1 = Array.from({ length: 256 }, (_, i) => offset1 + i * 4) + .map(offset => getUint(huffRecord.slice(offset, offset + 4))) + .map(x => [x & 0b1000_0000, x & 0b1_1111, x >>> 8]) + + // table2 is indexed by code length + const table2 = [null].concat(Array.from({ length: 32 }, (_, i) => offset2 + i * 8) + .map(offset => [ + getUint(huffRecord.slice(offset, offset + 4)), + getUint(huffRecord.slice(offset + 4, offset + 8))])) + + const dictionary = [] + for (let i = 1; i < mobi.numHuffcdic; i++) { + const record = await loadRecord(mobi.huffcdic + i) + const cdic = getStruct(CDIC_HEADER, record) + if (cdic.magic !== 'CDIC') throw new Error('Invalid CDIC record') + // `numEntries` is the total number of dictionary data across CDIC records + // so `n` here is the number of entries in *this* record + const n = Math.min(1 << cdic.codeLength, cdic.numEntries - dictionary.length) + const buffer = record.slice(cdic.length) + for (let i = 0; i < n; i++) { + const offset = getUint(buffer.slice(i * 2, i * 2 + 2)) + const x = getUint(buffer.slice(offset, offset + 2)) + const length = x & 0x7fff + const decompressed = x & 0x8000 + const value = new Uint8Array( + buffer.slice(offset + 2, offset + 2 + length)) + dictionary.push([value, decompressed]) + } + } + + const decompress = byteArray => { + let output = new Uint8Array() + const bitLength = byteArray.byteLength * 8 + for (let i = 0; i < bitLength;) { + const bits = Number(read32Bits(byteArray, i)) + let [found, codeLength, value] = table1[bits >>> 24] + if (!found) { + while (bits >>> (32 - codeLength) < table2[codeLength][0]) + codeLength += 1 + value = table2[codeLength][1] + } + if ((i += codeLength) > bitLength) break + + const code = value - (bits >>> (32 - codeLength)) + let [result, decompressed] = dictionary[code] + if (!decompressed) { + // the result is itself compressed + result = decompress(result) + // cache the result for next time + dictionary[code] = [result, true] + } + output = concatTypedArray(output, result) + } + return output + } + return decompress +} + +const getIndexData = async (indxIndex, loadRecord) => { + const indxRecord = await loadRecord(indxIndex) + const indx = getStruct(INDX_HEADER, indxRecord) + if (indx.magic !== 'INDX') throw new Error('Invalid INDX record') + const decoder = getDecoder(indx.encoding) + + const tagxBuffer = indxRecord.slice(indx.length) + const tagx = getStruct(TAGX_HEADER, tagxBuffer) + if (tagx.magic !== 'TAGX') throw new Error('Invalid TAGX section') + const numTags = (tagx.length - 12) / 4 + const tagTable = Array.from({ length: numTags }, (_, i) => + new Uint8Array(tagxBuffer.slice(12 + i * 4, 12 + i * 4 + 4))) + + const cncx = {} + let cncxRecordOffset = 0 + for (let i = 0; i < indx.numCncx; i++) { + const record = await loadRecord(indxIndex + indx.numRecords + i + 1) + const array = new Uint8Array(record) + for (let pos = 0; pos < array.byteLength;) { + const index = pos + const { value, length } = getVarLen(array, pos) + pos += length + const result = record.slice(pos, pos + value) + pos += value + cncx[cncxRecordOffset + index] = decoder.decode(result) + } + cncxRecordOffset += 0x10000 + } + + const table = [] + for (let i = 0; i < indx.numRecords; i++) { + const record = await loadRecord(indxIndex + 1 + i) + const array = new Uint8Array(record) + const indx = getStruct(INDX_HEADER, record) + if (indx.magic !== 'INDX') throw new Error('Invalid INDX record') + for (let j = 0; j < indx.numRecords; j++) { + const offsetOffset = indx.idxt + 4 + 2 * j + const offset = getUint(record.slice(offsetOffset, offsetOffset + 2)) + + const length = getUint(record.slice(offset, offset + 1)) + const name = getString(record.slice(offset + 1, offset + 1 + length)) + + const tags = [] + const startPos = offset + 1 + length + let controlByteIndex = 0 + let pos = startPos + tagx.numControlBytes + for (const [tag, numValues, mask, end] of tagTable) { + if (end & 1) { + controlByteIndex++ + continue + } + const offset = startPos + controlByteIndex + const value = getUint(record.slice(offset, offset + 1)) & mask + if (value === mask) { + if (countBitsSet(mask) > 1) { + const { value, length } = getVarLen(array, pos) + tags.push([tag, null, value, numValues]) + pos += length + } else tags.push([tag, 1, null, numValues]) + } else tags.push([tag, value >> countUnsetEnd(mask), null, numValues]) + } + + const tagMap = {} + for (const [tag, valueCount, valueBytes, numValues] of tags) { + const values = [] + if (valueCount != null) { + for (let i = 0; i < valueCount * numValues; i++) { + const { value, length } = getVarLen(array, pos) + values.push(value) + pos += length + } + } else { + let count = 0 + while (count < valueBytes) { + const { value, length } = getVarLen(array, pos) + values.push(value) + pos += length + count += length + } + } + tagMap[tag] = values + } + table.push({ name, tagMap }) + } + } + return { table, cncx } +} + +const getNCX = async (indxIndex, loadRecord) => { + const { table, cncx } = await getIndexData(indxIndex, loadRecord) + const items = table.map(({ tagMap }, index) => ({ + index, + offset: tagMap[1]?.[0], + size: tagMap[2]?.[0], + label: cncx[tagMap[3]] ?? '', + headingLevel: tagMap[4]?.[0], + pos: tagMap[6], + parent: tagMap[21]?.[0], + firstChild: tagMap[22]?.[0], + lastChild: tagMap[23]?.[0], + })) + const getChildren = item => { + if (item.firstChild == null) return item + item.children = items.filter(x => x.parent === item.index).map(getChildren) + return item + } + return items.filter(item => item.headingLevel === 0).map(getChildren) +} + +const getEXTH = (buf, encoding) => { + const { magic, count } = getStruct(EXTH_HEADER, buf) + if (magic !== 'EXTH') throw new Error('Invalid EXTH header') + const decoder = getDecoder(encoding) + const results = {} + let offset = 12 + for (let i = 0; i < count; i++) { + const type = getUint(buf.slice(offset, offset + 4)) + const length = getUint(buf.slice(offset + 4, offset + 8)) + if (type in EXTH_RECORD_TYPE) { + const [name, typ, many] = EXTH_RECORD_TYPE[type] + const data = buf.slice(offset + 8, offset + length) + const value = typ === 'uint' ? getUint(data) : decoder.decode(data) + if (many) { + results[name] ??= [] + results[name].push(value) + } else results[name] = value + } + offset += length + } + return results +} + +const getFont = async (buf, unzlib) => { + const { flags, dataStart, keyLength, keyStart } = getStruct(FONT_HEADER, buf) + const array = new Uint8Array(buf.slice(dataStart)) + // deobfuscate font + if (flags & 0b10) { + const bytes = keyLength === 16 ? 1024 : 1040 + const key = new Uint8Array(buf.slice(keyStart, keyStart + keyLength)) + const length = Math.min(bytes, array.length) + for (var i = 0; i < length; i++) array[i] = array[i] ^ key[i % key.length] + } + // decompress font + if (flags & 1) try { + return await unzlib(array) + } catch (e) { + console.warn(e) + console.warn('Failed to decompress font') + } + return array +} + +export const isMOBI = async file => { + const magic = getString(await file.slice(60, 68).arrayBuffer()) + return magic === 'BOOKMOBI'// || magic === 'TEXtREAd' +} + +class PDB { + #file + #offsets + pdb + async open(file) { + this.#file = file + const pdb = getStruct(PDB_HEADER, await file.slice(0, 78).arrayBuffer()) + this.pdb = pdb + const buffer = await file.slice(78, 78 + pdb.numRecords * 8).arrayBuffer() + // get start and end offsets for each record + this.#offsets = Array.from({ length: pdb.numRecords }, + (_, i) => getUint(buffer.slice(i * 8, i * 8 + 4))) + .map((x, i, a) => [x, a[i + 1]]) + } + loadRecord(index) { + const offsets = this.#offsets[index] + if (!offsets) throw new RangeError('Record index out of bounds') + return this.#file.slice(...offsets).arrayBuffer() + } + async loadMagic(index) { + const start = this.#offsets[index][0] + return getString(await this.#file.slice(start, start + 4).arrayBuffer()) + } +} + +export class MOBI extends PDB { + #start = 0 + #resourceStart + #decoder + #encoder + #decompress + #removeTrailingEntries + constructor({ unzlib }) { + super() + this.unzlib = unzlib + } + async open(file) { + await super.open(file) + // TODO: if (this.pdb.type === 'TEXt') + this.headers = this.#getHeaders(await super.loadRecord(0)) + this.#resourceStart = this.headers.mobi.resourceStart + let isKF8 = this.headers.mobi.version >= 8 + if (!isKF8) { + const boundary = this.headers.exth?.boundary + if (boundary < 0xffffffff) try { + // it's a "combo" MOBI/KF8 file; try to open the KF8 part + this.headers = this.#getHeaders(await super.loadRecord(boundary)) + this.#start = boundary + isKF8 = true + } catch (e) { + console.warn(e) + console.warn('Failed to open KF8; falling back to MOBI') + } + } + await this.#setup() + return isKF8 ? new KF8(this).init() : new MOBI6(this).init() + } + #getHeaders(buf) { + const palmdoc = getStruct(PALMDOC_HEADER, buf) + const mobi = getStruct(MOBI_HEADER, buf) + if (mobi.magic !== 'MOBI') throw new Error('Missing MOBI header') + + const { titleOffset, titleLength, localeLanguage, localeRegion } = mobi + mobi.title = buf.slice(titleOffset, titleOffset + titleLength) + const lang = MOBI_LANG[localeLanguage] + mobi.language = lang?.[localeRegion >> 2] ?? lang?.[0] + + const exth = mobi.exthFlag & 0b100_0000 + ? getEXTH(buf.slice(mobi.length + 16), mobi.encoding) : null + const kf8 = mobi.version >= 8 ? getStruct(KF8_HEADER, buf) : null + return { palmdoc, mobi, exth, kf8 } + } + async #setup() { + const { palmdoc, mobi } = this.headers + this.#decoder = getDecoder(mobi.encoding) + // `TextEncoder` only supports UTF-8 + // we are only encoding ASCII anyway, so I think it's fine + this.#encoder = new TextEncoder() + + // set up decompressor + const { compression } = palmdoc + this.#decompress = compression === 1 ? f => f + : compression === 2 ? decompressPalmDOC + : compression === 17480 ? await huffcdic(mobi, this.loadRecord.bind(this)) + : null + if (!this.#decompress) throw new Error('Unknown compression type') + + // set up function for removing trailing bytes + const { trailingFlags } = mobi + const multibyte = trailingFlags & 1 + const numTrailingEntries = countBitsSet(trailingFlags >>> 1) + this.#removeTrailingEntries = array => { + for (let i = 0; i < numTrailingEntries; i++) { + const length = getVarLenFromEnd(array) + array = array.subarray(0, -length) + } + if (multibyte) { + const length = (array[array.length - 1] & 0b11) + 1 + array = array.subarray(0, -length) + } + return array + } + } + decode(...args) { + return this.#decoder.decode(...args) + } + encode(...args) { + return this.#encoder.encode(...args) + } + loadRecord(index) { + return super.loadRecord(this.#start + index) + } + loadMagic(index) { + return super.loadMagic(this.#start + index) + } + loadText(index) { + return this.loadRecord(index + 1) + .then(buf => new Uint8Array(buf)) + .then(this.#removeTrailingEntries) + .then(this.#decompress) + } + async loadResource(index) { + const buf = await super.loadRecord(this.#resourceStart + index) + const magic = getString(buf.slice(0, 4)) + if (magic === 'FONT') return getFont(buf, this.unzlib) + if (magic === 'VIDE' || magic === 'AUDI') return buf.slice(12) + return buf + } + getNCX() { + const index = this.headers.mobi.indx + if (index < 0xffffffff) return getNCX(index, this.loadRecord.bind(this)) + } + getMetadata() { + const { mobi, exth } = this.headers + return { + identifier: mobi.uid.toString(), + title: unescapeHTML(exth?.title || this.decode(mobi.title)), + author: exth?.creator?.map(unescapeHTML), + publisher: unescapeHTML(exth?.publisher), + language: exth?.language ?? mobi.language, + published: exth?.date, + description: unescapeHTML(exth?.description), + subject: exth?.subject?.map(unescapeHTML), + rights: unescapeHTML(exth?.rights), + contributor: exth?.contributor, + } + } + async getCover() { + const { exth } = this.headers + const offset = exth?.coverOffset < 0xffffffff ? exth?.coverOffset + : exth?.thumbnailOffset < 0xffffffff ? exth?.thumbnailOffset : null + if (offset != null) { + const buf = await this.loadResource(offset) + return new Blob([buf]) + } + } +} + +const mbpPagebreakRegex = /<\s*(?:mbp:)?pagebreak[^>]*>/gi +const fileposRegex = /<[^<>]+filepos=['"]{0,1}(\d+)[^<>]*>/gi +const selfClosingRegex = /<(a|div|span|p)\s*\/>/gi + +const getIndent = el => { + let x = 0 + while (el) { + const parent = el.parentElement + if (parent) { + const tag = parent.tagName.toLowerCase() + if (tag === 'p') x += 1.5 + else if (tag === 'blockquote') x += 2 + } + el = parent + } + return x +} + +function rawBytesToString(uint8Array) { + const chunkSize = 0x8000 + let result = '' + for (let i = 0; i < uint8Array.length; i += chunkSize) { + result += String.fromCharCode.apply(null, uint8Array.subarray(i, i + chunkSize)) + } + return result +} + +class MOBI6 { + parser = new DOMParser() + serializer = new XMLSerializer() + #resourceCache = new Map() + #textCache = new Map() + #cache = new Map() + #sections + #fileposList = [] + #type = MIME.HTML + constructor(mobi) { + this.mobi = mobi + } + async init() { + const recordBuffers = [] + for (let i = 0; i < this.mobi.headers.palmdoc.numTextRecords; i++) { + const buf = await this.mobi.loadText(i) + recordBuffers.push(buf) + } + const totalLength = recordBuffers.reduce((sum, buf) => sum + buf.byteLength, 0) + // load all text records in an array + const array = new Uint8Array(totalLength) + recordBuffers.reduce((offset, buf) => { + array.set(new Uint8Array(buf), offset) + return offset + buf.byteLength + }, 0) + // convert to string so we can use regex + // note that `filepos` are byte offsets + // so it needs to preserve each byte as a separate character + // (see https://stackoverflow.com/q/50198017) + const str = rawBytesToString(array) + + // split content into sections at each `` + this.#sections = [0] + .concat(Array.from(str.matchAll(mbpPagebreakRegex), m => m.index)) + .map((start, i, a) => { + const end = a[i + 1] ?? array.length + return { book: this, raw: array.subarray(start, end) } + }) + // get start and end filepos for each section + .map((section, i, arr) => { + section.start = arr[i - 1]?.end ?? 0 + section.end = section.start + section.raw.byteLength + return section + }) + + this.sections = this.#sections.map((section, index) => ({ + id: index, + load: () => this.loadSection(section), + createDocument: () => this.createDocument(section), + size: section.end - section.start, + })) + + try { + this.landmarks = await this.getGuide() + const tocHref = this.landmarks + .find(({ type }) => type?.includes('toc'))?.href + if (tocHref) { + const { index } = this.resolveHref(tocHref) + const doc = await this.sections[index].createDocument() + let lastItem + let lastLevel = 0 + let lastIndent = 0 + const lastLevelOfIndent = new Map() + const lastParentOfLevel = new Map() + this.toc = Array.from(doc.querySelectorAll('a[filepos]')) + .reduce((arr, a) => { + const indent = getIndent(a) + const item = { + label: a.innerText?.trim() ?? '', + href: `filepos:${a.getAttribute('filepos')}`, + } + const level = indent > lastIndent ? lastLevel + 1 + : indent === lastIndent ? lastLevel + : lastLevelOfIndent.get(indent) ?? Math.max(0, lastLevel - 1) + if (level > lastLevel) { + if (lastItem) { + lastItem.subitems ??= [] + lastItem.subitems.push(item) + lastParentOfLevel.set(level, lastItem) + } + else arr.push(item) + } + else { + const parent = lastParentOfLevel.get(level) + if (parent) parent.subitems.push(item) + else arr.push(item) + } + lastItem = item + lastLevel = level + lastIndent = indent + lastLevelOfIndent.set(indent, level) + return arr + }, []) + } + } catch(e) { + console.warn(e) + } + + // get list of all `filepos` references in the book, + // which will be used to insert anchor elements + // because only then can they be referenced in the DOM + this.#fileposList = [...new Set( + Array.from(str.matchAll(fileposRegex), m => m[1]))] + .map(filepos => ({ filepos, number: Number(filepos) })) + .sort((a, b) => a.number - b.number) + + this.metadata = this.mobi.getMetadata() + this.getCover = this.mobi.getCover.bind(this.mobi) + return this + } + async getGuide() { + const doc = await this.createDocument(this.#sections[0]) + return Array.from(doc.getElementsByTagName('reference'), ref => ({ + label: ref.getAttribute('title'), + type: ref.getAttribute('type')?.split(/\s/), + href: `filepos:${ref.getAttribute('filepos')}`, + })) + } + async loadResource(index) { + if (this.#resourceCache.has(index)) return this.#resourceCache.get(index) + const raw = await this.mobi.loadResource(index) + const url = URL.createObjectURL(new Blob([raw])) + this.#resourceCache.set(index, url) + return url + } + async loadRecindex(recindex) { + return this.loadResource(Number(recindex) - 1) + } + async replaceResources(doc) { + for (const img of doc.querySelectorAll('img[recindex]')) { + const recindex = img.getAttribute('recindex') + try { + img.src = await this.loadRecindex(recindex) + } catch { + console.warn(`Failed to load image ${recindex}`) + } + } + for (const media of doc.querySelectorAll('[mediarecindex]')) { + const mediarecindex = media.getAttribute('mediarecindex') + const recindex = media.getAttribute('recindex') + try { + media.src = await this.loadRecindex(mediarecindex) + if (recindex) media.poster = await this.loadRecindex(recindex) + } catch { + console.warn(`Failed to load media ${mediarecindex}`) + } + } + for (const a of doc.querySelectorAll('[filepos]')) { + const filepos = a.getAttribute('filepos') + a.href = `filepos:${filepos}` + } + } + async loadText(section) { + if (this.#textCache.has(section)) return this.#textCache.get(section) + const { raw } = section + + // insert anchor elements for each `filepos` + const fileposList = this.#fileposList + .filter(({ number }) => number >= section.start && number < section.end) + .map(obj => ({ ...obj, offset: obj.number - section.start })) + let arr = raw + if (fileposList.length) { + arr = raw.subarray(0, fileposList[0].offset) + fileposList.forEach(({ filepos, offset }, i) => { + const next = fileposList[i + 1] + const a = this.mobi.encode(``) + arr = concatTypedArray3(arr, a, raw.subarray(offset, next?.offset)) + }) + } + const str = this.mobi.decode(arr).replaceAll(mbpPagebreakRegex, '') + this.#textCache.set(section, str) + return str + } + #sanitize(str) { + // HTML5 ignores the `/` in `` for non-void elements, + // leaving the tag unclosed. Rewrite the ones we've seen in MOBI. + return str.replace(selfClosingRegex, '<$1>') + } + async createDocument(section) { + const str = await this.loadText(section) + return this.parser.parseFromString(this.#sanitize(str), this.#type) + } + async loadSection(section) { + if (this.#cache.has(section)) return this.#cache.get(section) + const doc = await this.createDocument(section) + + // inject default stylesheet + const style = doc.createElement('style') + doc.head.append(style) + // blockquotes in MOBI seem to have only a small left margin by default + // many books seem to rely on this, as it's the only way to set margin + // (since there's no CSS) + style.append(doc.createTextNode(`blockquote { + margin-block-start: 0; + margin-block-end: 0; + margin-inline-start: 1em; + margin-inline-end: 0; + }`)) + + await this.replaceResources(doc) + const result = this.serializer.serializeToString(doc) + const url = URL.createObjectURL(new Blob([result], { type: this.#type })) + this.#cache.set(section, url) + return url + } + resolveHref(href) { + const filepos = href.match(/filepos:(.*)/)[1] + const number = Number(filepos) + const index = this.#sections.findIndex(section => section.end > number) + const anchor = doc => doc.getElementById(`filepos${filepos}`) + return { index, anchor } + } + splitTOCHref(href) { + const filepos = href.match(/filepos:(.*)/)[1] + const number = Number(filepos) + const index = this.#sections.findIndex(section => section.end > number) + return [index, `filepos${filepos}`] + } + getTOCFragment(doc, id) { + return doc.getElementById(id) + } + isExternal(uri) { + return /^(?!blob|filepos)\w+:/i.test(uri) + } + destroy() { + for (const url of this.#resourceCache.values()) URL.revokeObjectURL(url) + for (const url of this.#cache.values()) URL.revokeObjectURL(url) + } +} + +// handlers for `kindle:` uris +const kindleResourceRegex = /kindle:(flow|embed):(\w+)(?:\?mime=(\w+\/[-+.\w]+))?/ +const kindlePosRegex = /kindle:pos:fid:(\w+):off:(\w+)/ +const parseResourceURI = str => { + const [resourceType, id, type] = str.match(kindleResourceRegex).slice(1) + return { resourceType, id: parseInt(id, 32), type } +} +const parsePosURI = str => { + const [fid, off] = str.match(kindlePosRegex).slice(1) + return { fid: parseInt(fid, 32), off: parseInt(off, 32) } +} +const makePosURI = (fid = 0, off = 0) => + `kindle:pos:fid:${fid.toString(32).toUpperCase().padStart(4, '0') + }:off:${off.toString(32).toUpperCase().padStart(10, '0')}` + +// `kindle:pos:` links are originally links that contain fragments identifiers +// so there should exist an element with `id` or `name` +// otherwise try to find one with an `aid` attribute +const getFragmentSelector = str => { + const match = str.match(/\s(id|name|aid)\s*=\s*['"]([^'"]*)['"]/i) + if (!match) return + const [, attr, value] = match + return `[${attr}="${CSS.escape(value)}"]` +} + +// replace asynchronously and sequentially +const replaceSeries = async (str, regex, f) => { + const matches = [] + str.replace(regex, (...args) => (matches.push(args), null)) + const results = [] + for (const args of matches) results.push(await f(...args)) + return str.replace(regex, () => results.shift()) +} + +const getPageSpread = properties => { + for (const p of properties) { + if (p === 'page-spread-left' || p === 'rendition:page-spread-left') + return 'left' + if (p === 'page-spread-right' || p === 'rendition:page-spread-right') + return 'right' + if (p === 'rendition:page-spread-center') return 'center' + } +} + +class KF8 { + parser = new DOMParser() + serializer = new XMLSerializer() + transformTarget = new EventTarget() + #cache = new Map() + #fragmentOffsets = new Map() + #fragmentSelectors = new Map() + #tables = {} + #sections + #fullRawLength + #rawHead = new Uint8Array() + #rawTail = new Uint8Array() + #lastLoadedHead = -1 + #lastLoadedTail = -1 + #type = MIME.XHTML + #inlineMap = new Map() + constructor(mobi) { + this.mobi = mobi + } + async init() { + const loadRecord = this.mobi.loadRecord.bind(this.mobi) + const { kf8 } = this.mobi.headers + + try { + const fdstBuffer = await loadRecord(kf8.fdst) + const fdst = getStruct(FDST_HEADER, fdstBuffer) + if (fdst.magic !== 'FDST') throw new Error('Missing FDST record') + const fdstTable = Array.from({ length: fdst.numEntries }, + (_, i) => 12 + i * 8) + .map(offset => [ + getUint(fdstBuffer.slice(offset, offset + 4)), + getUint(fdstBuffer.slice(offset + 4, offset + 8))]) + this.#tables.fdstTable = fdstTable + this.#fullRawLength = fdstTable[fdstTable.length - 1][1] + } catch {} + + const skelTable = (await getIndexData(kf8.skel, loadRecord)).table + .map(({ name, tagMap }, index) => ({ + index, name, + numFrag: tagMap[1][0], + offset: tagMap[6][0], + length: tagMap[6][1], + })) + const fragData = await getIndexData(kf8.frag, loadRecord) + const fragTable = fragData.table.map(({ name, tagMap }) => ({ + insertOffset: parseInt(name), + selector: fragData.cncx[tagMap[2][0]], + index: tagMap[4][0], + offset: tagMap[6][0], + length: tagMap[6][1], + })) + this.#tables.skelTable = skelTable + this.#tables.fragTable = fragTable + + this.#sections = skelTable.reduce((arr, skel) => { + const last = arr[arr.length - 1] + const fragStart = last?.fragEnd ?? 0, fragEnd = fragStart + skel.numFrag + const frags = fragTable.slice(fragStart, fragEnd) + const length = skel.length + frags.map(f => f.length).reduce((a, b) => a + b, 0) + const totalLength = (last?.totalLength ?? 0) + length + return arr.concat({ skel, frags, fragEnd, length, totalLength }) + }, []) + + const resources = await this.getResourcesByMagic(['RESC', 'PAGE']) + const pageSpreads = new Map() + if (resources.RESC) { + const buf = await this.mobi.loadRecord(resources.RESC) + const str = this.mobi.decode(buf.slice(16)).replace(/\0/g, '') + // the RESC record lacks the root `` element + // but seem to be otherwise valid XML + const index = str.search(/\?>/) + const xmlStr = `${str.slice(index)}` + const opf = this.parser.parseFromString(xmlStr, MIME.XML) + for (const $itemref of opf.querySelectorAll('spine > itemref')) { + const i = parseInt($itemref.getAttribute('skelid')) + pageSpreads.set(i, getPageSpread( + $itemref.getAttribute('properties')?.split(' ') ?? [])) + } + } + + this.sections = this.#sections.map((section, index) => + section.frags.length ? ({ + id: index, + load: () => this.loadSection(section), + createDocument: () => this.createDocument(section), + size: section.length, + pageSpread: pageSpreads.get(index), + }) : ({ linear: 'no' })) + + try { + const ncx = await this.mobi.getNCX() + const map = ({ label, pos, children }) => { + const [fid, off] = pos + const href = makePosURI(fid, off) + const arr = this.#fragmentOffsets.get(fid) + if (arr) arr.push(off) + else this.#fragmentOffsets.set(fid, [off]) + return { label: unescapeHTML(label), href, subitems: children?.map(map) } + } + this.toc = ncx?.map(map) + this.landmarks = await this.getGuide() + } catch(e) { + console.warn(e) + } + + const { exth } = this.mobi.headers + this.dir = exth.pageProgressionDirection + this.rendition = { + layout: exth.fixedLayout === 'true' ? 'pre-paginated' : 'reflowable', + viewport: Object.fromEntries(exth.originalResolution + ?.split('x')?.slice(0, 2) + ?.map((x, i) => [i ? 'height' : 'width', x]) ?? []), + } + + this.metadata = this.mobi.getMetadata() + this.getCover = this.mobi.getCover.bind(this.mobi) + return this + } + // is this really the only way of getting to RESC, PAGE, etc.? + async getResourcesByMagic(keys) { + const results = {} + const start = this.mobi.headers.kf8.resourceStart + const end = this.mobi.pdb.numRecords + for (let i = start; i < end; i++) { + try { + const magic = await this.mobi.loadMagic(i) + const match = keys.find(key => key === magic) + if (match) results[match] = i + } catch {} + } + return results + } + async getGuide() { + const index = this.mobi.headers.kf8.guide + if (index < 0xffffffff) { + const loadRecord = this.mobi.loadRecord.bind(this.mobi) + const { table, cncx } = await getIndexData(index, loadRecord) + return table.map(({ name, tagMap }) => ({ + label: cncx[tagMap[1][0]] ?? '', + type: name?.split(/\s/), + href: makePosURI(tagMap[6]?.[0] ?? tagMap[3]?.[0]), + })) + } + } + async loadResourceBlob(str) { + const { resourceType, id, type } = parseResourceURI(str) + const raw = resourceType === 'flow' ? await this.loadFlow(id) + : await this.mobi.loadResource(id - 1) + const result = [MIME.XHTML, MIME.HTML, MIME.CSS, MIME.SVG].includes(type) + ? await this.replaceResources(this.mobi.decode(raw)) : raw + const detail = { data: result, type } + const event = new CustomEvent('data', { detail }) + this.transformTarget.dispatchEvent(event) + const newData = await event.detail.data + const newType = await event.detail.type + const doc = newType === MIME.SVG ? this.parser.parseFromString(newData, newType) : null + return [new Blob([newData], { newType }), + // SVG wrappers need to be inlined + // as browsers don't allow external resources when loading SVG as an image + doc?.getElementsByTagNameNS('http://www.w3.org/2000/svg', 'image')?.length + ? doc.documentElement : null] + } + async loadResource(str) { + if (this.#cache.has(str)) return this.#cache.get(str) + const [blob, inline] = await this.loadResourceBlob(str) + const url = inline ? str : URL.createObjectURL(blob) + if (inline) this.#inlineMap.set(url, inline) + this.#cache.set(str, url) + return url + } + replaceResources(str) { + const regex = new RegExp(kindleResourceRegex, 'g') + return replaceSeries(str, regex, this.loadResource.bind(this)) + } + // NOTE: there doesn't seem to be a way to access text randomly? + // how to know the decompressed size of the records without decompressing? + // 4096 is just the maximum size + async loadRaw(start, end) { + // here we load either from the front or back until we have reached the + // required offsets; at worst you'd have to load half the book at once + const distanceHead = end - this.#rawHead.length + const distanceEnd = this.#fullRawLength == null ? Infinity + : (this.#fullRawLength - this.#rawTail.length) - start + // load from the start + if (distanceHead < 0 || distanceHead < distanceEnd) { + while (this.#rawHead.length < end) { + const index = ++this.#lastLoadedHead + const data = await this.mobi.loadText(index) + this.#rawHead = concatTypedArray(this.#rawHead, data) + } + return this.#rawHead.slice(start, end) + } + // load from the end + while (this.#fullRawLength - this.#rawTail.length > start) { + const index = this.mobi.headers.palmdoc.numTextRecords - 1 + - (++this.#lastLoadedTail) + const data = await this.mobi.loadText(index) + this.#rawTail = concatTypedArray(data, this.#rawTail) + } + const rawTailStart = this.#fullRawLength - this.#rawTail.length + return this.#rawTail.slice(start - rawTailStart, end - rawTailStart) + } + loadFlow(index) { + if (index < 0xffffffff) + return this.loadRaw(...this.#tables.fdstTable[index]) + } + async loadText(section) { + const { skel, frags, length } = section + const raw = await this.loadRaw(skel.offset, skel.offset + length) + let skeleton = raw.slice(0, skel.length) + for (const frag of frags) { + const insertOffset = frag.insertOffset - skel.offset + const offset = skel.length + frag.offset + const fragRaw = raw.slice(offset, offset + frag.length) + skeleton = concatTypedArray3( + skeleton.slice(0, insertOffset), fragRaw, + skeleton.slice(insertOffset)) + + const offsets = this.#fragmentOffsets.get(frag.index) + if (offsets) for (const offset of offsets) { + const str = this.mobi.decode(fragRaw.slice(offset)) + const selector = getFragmentSelector(str) + this.#setFragmentSelector(frag.index, offset, selector) + } + } + return this.mobi.decode(skeleton) + } + async createDocument(section) { + const str = await this.loadText(section) + return this.parser.parseFromString(str, this.#type) + } + async loadSection(section) { + if (this.#cache.has(section)) return this.#cache.get(section) + const str = await this.loadText(section) + const replaced = await this.replaceResources(str) + + // by default, type is XHTML; change to HTML if it's not valid XHTML + let doc = this.parser.parseFromString(replaced, this.#type) + if (doc.querySelector('parsererror') || !doc.documentElement?.namespaceURI) { + this.#type = MIME.HTML + doc = this.parser.parseFromString(replaced, this.#type) + } + for (const [url, node] of this.#inlineMap) { + for (const el of doc.querySelectorAll(`img[src="${url}"]`)) + el.replaceWith(node) + } + const url = URL.createObjectURL( + new Blob([this.serializer.serializeToString(doc)], { type: this.#type })) + this.#cache.set(section, url) + return url + } + getIndexByFID(fid) { + return this.#sections.findIndex(section => + section.frags.some(frag => frag.index === fid)) + } + #setFragmentSelector(id, offset, selector) { + const map = this.#fragmentSelectors.get(id) + if (map) map.set(offset, selector) + else { + const map = new Map() + this.#fragmentSelectors.set(id, map) + map.set(offset, selector) + } + } + async resolveHref(href) { + const { fid, off } = parsePosURI(href) + const index = this.getIndexByFID(fid) + if (index < 0) return + + const saved = this.#fragmentSelectors.get(fid)?.get(off) + if (saved) return { index, anchor: doc => doc.querySelector(saved) } + + const { skel, frags } = this.#sections[index] + const frag = frags.find(frag => frag.index === fid) + const offset = skel.offset + skel.length + frag.offset + const fragRaw = await this.loadRaw(offset, offset + frag.length) + const str = this.mobi.decode(fragRaw.slice(off)) + const selector = getFragmentSelector(str) + this.#setFragmentSelector(fid, off, selector) + const anchor = doc => doc.querySelector(selector) + return { index, anchor } + } + splitTOCHref(href) { + const pos = parsePosURI(href) + const index = this.getIndexByFID(pos.fid) + return [index, pos] + } + getTOCFragment(doc, { fid, off }) { + const selector = this.#fragmentSelectors.get(fid)?.get(off) + return doc.querySelector(selector) + } + isExternal(uri) { + return /^(?!blob|kindle)\w+:/i.test(uri) + } + destroy() { + for (const url of this.#cache.values()) URL.revokeObjectURL(url) + } +} diff --git a/web/vendor/foliate-js/opds.js b/web/vendor/foliate-js/opds.js new file mode 100644 index 00000000..5112e8f7 --- /dev/null +++ b/web/vendor/foliate-js/opds.js @@ -0,0 +1,400 @@ +const NS = { + ATOM: 'http://www.w3.org/2005/Atom', + OPDS: 'http://opds-spec.org/2010/catalog', + THR: 'http://purl.org/syndication/thread/1.0', + DC: 'http://purl.org/dc/elements/1.1/', + DCTERMS: 'http://purl.org/dc/terms/', + FH: 'http://purl.org/syndication/history/1.0', + PSE: 'http://vaemendis.net/opds-pse/ns', + OS: 'http://a9.com/-/spec/opensearch/1.1/', +} + +const MIME = { + ATOM: 'application/atom+xml', + OPDS2: 'application/opds+json', +} + +export const REL = { + ACQ: 'http://opds-spec.org/acquisition', + FACET: 'http://opds-spec.org/facet', + GROUP: 'http://opds-spec.org/group', + COVER: [ + 'http://opds-spec.org/image', + 'http://opds-spec.org/cover', // ManyBooks legacy, not in spec + 'x-stanza-cover-image', // Lexcycle Stanza legacy + ], + THUMBNAIL: [ + 'http://opds-spec.org/image/thumbnail', + 'http://opds-spec.org/thumbnail', // ManyBooks legacy, not in spec + 'x-stanza-cover-image-thumbnail', // Lexcycle Stanza legacy + ], + STREAM: 'http://vaemendis.net/opds-pse/stream', +} + +export const SYMBOL = { + SUMMARY: Symbol('summary'), + CONTENT: Symbol('content'), +} + +const FACET_GROUP = Symbol('facetGroup') + +const groupByArray = (arr, f) => { + const map = new Map() + if (arr) { + for (const el of arr) { + const keys = f(el) + const keyArray = keys == null ? [] : (Array.isArray(keys) ? keys : [keys]) + for (const key of keyArray) { + const group = map.get(key) + if (group) group.push(el) + else map.set(key, [el]) + } + } + } + return map +} + +// https://www.rfc-editor.org/rfc/rfc7231#section-3.1.1 +const parseMediaType = str => { + if (!str) return + const [mediaType, ...ps] = str.split(/ *; */) + if (!mediaType) return + return { + mediaType: mediaType.toLowerCase(), + parameters: ps.reduce((acc, p) => { + const [name, val] = p.split('=') + if (name) { + acc[name.toLowerCase()] = val?.replace(/(^"|"$)/g, '') + } + return acc + }, {}), + } +} + +export const isOPDSCatalog = str => { + const parsed = parseMediaType(str) + if (!parsed) return false + const { mediaType, parameters } = parsed + if (mediaType === MIME.OPDS2) return true + return mediaType === MIME.ATOM && parameters.profile?.toLowerCase() === 'opds-catalog' +} + +// ignore the namespace if it doesn't appear in document at all +const useNS = (doc, ns) => + doc.lookupNamespaceURI(null) === ns || doc.lookupPrefix(ns) ? ns : undefined + +const filterNS = ns => ns + ? name => el => el.namespaceURI === ns && el.localName === name + : name => el => el.localName === name + +const getContent = el => { + if (!el) return + const type = el.getAttribute('type') ?? 'text' + const value = type === 'xhtml' ? el.innerHTML + : type === 'html' ? el.textContent + .replaceAll('<', '<') + .replaceAll('>', '>') + .replaceAll('&', '&') + : el.textContent + return { value, type } +} + +const getTextContent = el => { + const content = getContent(el) + if (content?.type === 'text') return content.value +} + +const getSummary = (a, b) => getTextContent(a) ?? getTextContent(b) + +// Fetch only direct children to avoid polluting with nested deep indirect acquisitions +const getDirectChildren = (el, ns, localName, tagName) => { + return Array.from(el.childNodes).filter(node => + node.nodeType === 1 && + ( + (node.namespaceURI === ns && node.localName === localName) || + (node.tagName === tagName) + ) + ) +} + +const getPrice = link => { + const prices = getDirectChildren(link, NS.OPDS, 'price', 'opds:price') + if (!prices.length) return + const parsed = prices.reduce((acc, price) => { + const value = parseFloat(price.textContent) + if (!Number.isNaN(value)) { + acc.push({ + currency: price.getAttribute('currencycode') ?? undefined, + value, + }) + } + return acc + }, []) + + if (!parsed.length) return + // OPDS 1.x allows multiple prices, OPDS 2.0 schema defines price as a single object + return parsed.length === 1 ? parsed[0] : parsed +} + +const getIndirectAcquisition = el => { + const ias = getDirectChildren(el, NS.OPDS, 'indirectAcquisition', 'opds:indirectAcquisition') + if (!ias.length) return [] + return ias.reduce((acc, ia) => { + const type = ia.getAttribute('type') + if (type) { + acc.push({ + type, + child: getIndirectAcquisition(ia), + }) + } + return acc + }, []) +} + +const getLink = link => { + const relAttr = link.getAttribute('rel') + const rel = relAttr ? relAttr.split(/ +/) : undefined + + const isAcquisition = rel?.some(r => r.startsWith(REL.ACQ) || r === 'preview') + const isStream = rel?.includes(REL.STREAM) + + // Map OPDS 1.x active facets to OPDS 2.0 "self" link + const activeFacet = link.getAttributeNS(NS.OPDS, 'activeFacet') || link.getAttribute('opds:activeFacet') + const mappedRel = activeFacet === 'true' ? [rel ?? []].flat().concat('self') : rel + + // Maps OPDS 1.x thr:count seamlessly to OPDS 2.0 properties.numberOfItems + const thrCount = link.getAttributeNS(NS.THR, 'count') || link.getAttribute('thr:count') + // Support for systems that incorrectly use standard `count` for facet hints + const fallbackCount = link.getAttribute('count') + + // --- OPDS-PSE Extensions --- + const pseCount = link.getAttributeNS(NS.PSE, 'count') || link.getAttribute('pse:count') + const pseLastRead = link.getAttributeNS(NS.PSE, 'lastRead') || link.getAttribute('pse:lastRead') + const pseLastReadDate = link.getAttributeNS(NS.PSE, 'lastReadDate') || link.getAttribute('pse:lastReadDate') + + return { + rel: mappedRel, + href: link.getAttribute('href') ?? undefined, + type: link.getAttribute('type') ?? undefined, + title: link.getAttribute('title') ?? undefined, + // --- Facet Grouping --- + [FACET_GROUP]: (link.getAttributeNS(NS.OPDS, 'facetGroup') || link.getAttribute('opds:facetGroup')) ?? undefined, + properties: { + price: (isAcquisition || isStream) ? getPrice(link) : undefined, + indirectAcquisition: (isAcquisition || isStream) ? getIndirectAcquisition(link) : undefined, + // --- Pagination / Facet Counters --- + numberOfItems: thrCount != null ? Number(thrCount) : (!isStream && fallbackCount != null) ? Number(fallbackCount) : undefined, + 'pse:count': isStream && (pseCount ?? fallbackCount) != null ? Number(pseCount ?? fallbackCount) : undefined, + 'pse:lastRead': isStream && pseLastRead != null ? Number(pseLastRead) : undefined, + 'pse:lastReadDate': isStream ? pseLastReadDate ?? undefined : undefined, + }, + } +} + +const getPerson = person => { + const NS = person.namespaceURI + const uri = person.getElementsByTagNameNS(NS, 'uri')[0]?.textContent + return { + name: person.getElementsByTagNameNS(NS, 'name')[0]?.textContent ?? undefined, + links: uri ? [{ href: uri }] : [], + } +} + +export const getPublication = entry => { + const filter = filterNS(useNS(entry.ownerDocument, NS.ATOM)) + const children = Array.from(entry.children) + const filterDCEL = filterNS(NS.DC) + const filterDCTERMS = filterNS(NS.DCTERMS) + const filterDC = x => y => filterDCEL(x)(y) || filterDCTERMS(x)(y) + const links = children.filter(filter('link')).map(getLink) + const linksByRel = groupByArray(links, link => link.rel) + return { + metadata: { + id: children.find(filter('id'))?.textContent ?? undefined, + updated: children.find(filter('updated'))?.textContent ?? undefined, + title: children.find(filter('title'))?.textContent ?? undefined, + author: children.filter(filter('author')).map(getPerson), + contributor: children.filter(filter('contributor')).map(getPerson), + publisher: children.find(filterDC('publisher'))?.textContent ?? undefined, + published: (children.find(filter('published')) + ?? children.find(filterDCTERMS('issued')) + ?? children.find(filterDC('date')))?.textContent ?? undefined, + language: children.find(filterDC('language'))?.textContent ?? undefined, + identifier: children.find(filterDC('identifier'))?.textContent ?? undefined, + subject: children.filter(filter('category')).map(category => ({ + name: category.getAttribute('label') ?? undefined, + code: category.getAttribute('term') ?? undefined, + scheme: category.getAttribute('scheme') ?? undefined, + })), + rights: children.find(filter('rights'))?.textContent ?? undefined, + [SYMBOL.CONTENT]: getContent(children.find(filter('content')) ?? children.find(filter('summary'))) ?? undefined, + }, + links, + images: REL.COVER.concat(REL.THUMBNAIL) + .map(R => linksByRel.get(R)?.[0]).filter(Boolean), + } +} + +export const getFeed = doc => { + const ns = useNS(doc, NS.ATOM) + const filter = filterNS(ns) + const children = Array.from(doc.documentElement.children) + const entries = children.filter(filter('entry')) + const links = children.filter(filter('link')).map(getLink) + const linksByRel = groupByArray(links, link => link.rel) + + const filterFH = filterNS(NS.FH) + const filterOS = filterNS(NS.OS) + + const groupedItems = new Map([[undefined, []]]) + const groupLinkMap = new Map() + for (const entry of entries) { + const children = Array.from(entry.children) + const links = children.filter(filter('link')).map(getLink) + const linksByRel = groupByArray(links, link => link.rel) + const isPub = Array.from(linksByRel.keys()) + .some(rel => rel?.startsWith(REL.ACQ) || rel === 'preview' || rel === REL.STREAM) + + const groupLinks = linksByRel.get(REL.GROUP) ?? linksByRel.get('collection') + const groupLink = groupLinks?.length + ? groupLinks.find(link => groupedItems.has(link.href)) ?? groupLinks[0] : undefined + if (groupLink?.href && !groupLinkMap.has(groupLink.href)) { + groupLinkMap.set(groupLink.href, groupLink) + } + + const item = isPub + ? getPublication(entry) + : Object.assign(links.find(link => isOPDSCatalog(link.type)) ?? links[0] ?? {}, { + title: children.find(filter('title'))?.textContent ?? undefined, + [SYMBOL.SUMMARY]: getSummary(children.find(filter('summary')), + children.find(filter('content'))) ?? undefined, + }) + + // Flag the item with its resolved OPDS 2.0 type + item.__type = isPub ? 'publication' : 'navigation' + + const arr = groupedItems.get(groupLink?.href) + if (arr) arr.push(item) + else groupedItems.set(groupLink.href, [item]) + } + + const [items, ...groups] = Array.from(groupedItems, ([key, groupItems]) => { + // Separate publications and navigation items within the group + const publications = [] + const navigation = [] + for (const item of groupItems) { + if (item.__type === 'publication') { + delete item.__type + publications.push(item) + } else { + delete item.__type + navigation.push(item) + } + } + + const groupContent = {} + if (publications.length) groupContent.publications = publications + if (navigation.length) groupContent.navigation = navigation + + if (key === undefined) return groupContent + const link = groupLinkMap.get(key) + return { + metadata: { + title: link?.title, + numberOfItems: link?.properties?.numberOfItems, + }, + links: [{ rel: 'self', href: link?.href, type: link?.type }], + ...groupContent, + } + }) + + // --- OPDS 2.0 Pagination (derived from OpenSearch / RFC 5005) --- + const totalResults = children.find(filterOS('totalResults'))?.textContent + const itemsPerPage = children.find(filterOS('itemsPerPage'))?.textContent + const startIndex = children.find(filterOS('startIndex'))?.textContent + + let currentPage + if (startIndex != null && itemsPerPage != null) { + const start = Number(startIndex) + const items = Number(itemsPerPage) + // Resolves typical 1-based offset to a page number + currentPage = Math.floor((start > 0 ? start - 1 : 0) / items) + 1 + } + + return { + metadata: { + id: children.find(filter('id'))?.textContent ?? undefined, + updated: children.find(filter('updated'))?.textContent ?? undefined, + title: children.find(filter('title'))?.textContent ?? undefined, + subtitle: children.find(filter('subtitle'))?.textContent ?? undefined, + numberOfItems: totalResults != null ? Number(totalResults) : undefined, + itemsPerPage: itemsPerPage != null ? Number(itemsPerPage) : undefined, + currentPage, + }, + links, + isComplete: !!children.find(filterFH('complete')) || undefined, + isArchive: !!children.find(filterFH('archive')) || undefined, + ...items, // contains top-level 'publications' and/or 'navigation' arrays + groups: groups.length ? groups : undefined, + facets: Array.from( + groupByArray(linksByRel.get(REL.FACET) ?? [], link => link[FACET_GROUP]), + ([facet, links]) => ({ metadata: { title: facet ?? undefined }, links }) + ), + } +} + +export const getSearch = async link => { + const { replace, getVariables } = await import('./uri-template.js') + const href = link.href || '' + return { + metadata: { + title: link.title ?? undefined, + }, + search: map => replace(href, map.get(undefined)), + params: Array.from(getVariables(href), name => ({ name })), + } +} + +export const getOpenSearch = doc => { + const defaultNS = doc.documentElement.namespaceURI + const filter = filterNS(defaultNS) + const children = Array.from(doc.documentElement.children) + + const $$urls = children.filter(filter('Url')) + const $url = $$urls.find(url => isOPDSCatalog(url.getAttribute('type'))) ?? $$urls[0] + if (!$url) throw new Error('document must contain at least one Url element') + + const regex = /{(?:([^}]+?):)?(.+?)(\?)?}/g + const defaultMap = new Map([ + ['count', '100'], + ['startIndex', $url.getAttribute('indexOffset') ?? '0'], + ['startPage', $url.getAttribute('pageOffset') ?? '0'], + ['language', '*'], + ['inputEncoding', 'UTF-8'], + ['outputEncoding', 'UTF-8'], + ]) + + const template = $url.getAttribute('template') || '' + return { + metadata: { + title: (children.find(filter('LongName')) ?? children.find(filter('ShortName')))?.textContent ?? undefined, + description: children.find(filter('Description'))?.textContent ?? undefined, + }, + search: map => template.replace(regex, (_, prefix, param) => { + const namespace = prefix ? $url.lookupNamespaceURI(prefix) : undefined + const ns = namespace === defaultNS ? undefined : namespace + const val = map.get(ns)?.get(param) + return encodeURIComponent(val ?? (!ns ? defaultMap.get(param) ?? '' : '')) + }), + params: Array.from(template.matchAll(regex), ([, prefix, param, optional]) => { + const namespace = prefix ? $url.lookupNamespaceURI(prefix) : undefined + const ns = namespace === defaultNS ? undefined : namespace + return { + ns, + name: param, // (.+?) ensures `param` is non-empty + required: !optional, + value: ns && ns !== defaultNS ? undefined : defaultMap.get(param) ?? undefined, + } + }), + } +} diff --git a/web/vendor/foliate-js/overlayer.js b/web/vendor/foliate-js/overlayer.js new file mode 100644 index 00000000..172d37cd --- /dev/null +++ b/web/vendor/foliate-js/overlayer.js @@ -0,0 +1,422 @@ +const createSVGElement = tag => + document.createElementNS('http://www.w3.org/2000/svg', tag) + +let overlayerCounter = 0 + +export class Overlayer { + #svg = createSVGElement('svg') + #map = new Map() + #doc = null + #clipPath = null + #clipPathPath = null + #clipPathId + + constructor(doc) { + this.#doc = doc + this.#clipPathId = `foliate-loupe-clip-${overlayerCounter++}` + Object.assign(this.#svg.style, { + position: 'absolute', top: '0', left: '0', + width: '100%', height: '100%', + pointerEvents: 'none', + }) + + // Create a clipPath to cut a hole for the loupe. + // We use clip-rule="evenodd" with a large outer rect and inner circle + // to create the hole effect efficiently without mask compositing. + const defs = createSVGElement('defs') + this.#clipPath = createSVGElement('clipPath') + this.#clipPath.setAttribute('id', this.#clipPathId) + this.#clipPath.setAttribute('clipPathUnits', 'userSpaceOnUse') + + this.#clipPathPath = createSVGElement('path') + this.#clipPathPath.setAttribute('clip-rule', 'evenodd') + this.#clipPathPath.setAttribute('fill-rule', 'evenodd') // for older renderers + + this.#clipPath.append(this.#clipPathPath) + defs.append(this.#clipPath) + this.#svg.append(defs) + } + + setHole(cx, cy, w, h, r) { + // Define a path with a large outer rect and a capsule-shaped hole. + // The capsule is a rounded rectangle (stadium shape) centred at (cx, cy). + const outer = 'M -2000000 -2000000 H 4000000 V 4000000 H -2000000 Z' + const hw = w / 2, hh = h / 2 + const cr = Math.min(r, hw, hh) // clamp corner radius + const inner = `M ${cx - hw + cr} ${cy - hh}` + + ` H ${cx + hw - cr}` + + ` A ${cr} ${cr} 0 0 1 ${cx + hw} ${cy - hh + cr}` + + ` V ${cy + hh - cr}` + + ` A ${cr} ${cr} 0 0 1 ${cx + hw - cr} ${cy + hh}` + + ` H ${cx - hw + cr}` + + ` A ${cr} ${cr} 0 0 1 ${cx - hw} ${cy + hh - cr}` + + ` V ${cy - hh + cr}` + + ` A ${cr} ${cr} 0 0 1 ${cx - hw + cr} ${cy - hh} Z` + this.#clipPathPath.setAttribute('d', `${outer} ${inner}`) + + this.#svg.setAttribute('clip-path', `url(#${this.#clipPathId})`) + this.#svg.style.webkitClipPath = `url(#${this.#clipPathId})` + } + + clearHole() { + this.#svg.removeAttribute('clip-path') + this.#svg.style.webkitClipPath = '' + this.#clipPathPath.removeAttribute('d') + } + + get element() { + return this.#svg + } + get #zoom() { + // Safari does not zoom the client rects, while Chrome, Edge and Firefox does + if (/^((?!chrome|android).)*AppleWebKit/i.test(navigator.userAgent) && !window.chrome) { + return window.getComputedStyle(this.#doc.body).zoom || 1.0 + } + return 1.0 + } + #splitRangeByParagraph(range) { + const ancestor = range.commonAncestorContainer + const paragraphs = Array.from(ancestor.querySelectorAll?.('p, h1, h2, h3, h4') || []) + + const splitRanges = [] + paragraphs.forEach((p) => { + const pRange = document.createRange() + if (range.intersectsNode(p)) { + pRange.selectNodeContents(p) + if (pRange.compareBoundaryPoints(Range.START_TO_START, range) < 0) { + pRange.setStart(range.startContainer, range.startOffset) + } + if (pRange.compareBoundaryPoints(Range.END_TO_END, range) > 0) { + pRange.setEnd(range.endContainer, range.endOffset) + } + splitRanges.push(pRange) + } + }) + return splitRanges.length === 0 ? [range] : splitRanges + } + add(key, range, draw, options) { + if (this.#map.has(key)) this.remove(key) + if (typeof range === 'function') range = range(this.#svg.getRootNode()) + const zoom = this.#zoom + let rects = [] + this.#splitRangeByParagraph(range).forEach((pRange) => { + const pRects = Array.from(pRange.getClientRects()).map(rect => ({ + left: rect.left * zoom, + top: rect.top * zoom, + right: rect.right * zoom, + bottom: rect.bottom * zoom, + width: rect.width * zoom, + height: rect.height * zoom, + })) + rects = rects.concat(pRects) + }) + const element = draw(rects, options) + this.#svg.append(element) + this.#map.set(key, { range, draw, options, element, rects }) + } + remove(key) { + if (!this.#map.has(key)) return + this.#svg.removeChild(this.#map.get(key).element) + this.#map.delete(key) + } + redraw() { + for (const obj of this.#map.values()) { + const { range, draw, options, element } = obj + this.#svg.removeChild(element) + const zoom = this.#zoom + let rects = [] + this.#splitRangeByParagraph(range).forEach((pRange) => { + const pRects = Array.from(pRange.getClientRects()).map(rect => ({ + left: rect.left * zoom, + top: rect.top * zoom, + right: rect.right * zoom, + bottom: rect.bottom * zoom, + width: rect.width * zoom, + height: rect.height * zoom, + })) + rects = rects.concat(pRects) + }) + const el = draw(rects, options) + this.#svg.append(el) + obj.element = el + obj.rects = rects + } + } + hitTest({ x, y }) { + const arr = Array.from(this.#map.entries()) + // loop in reverse to hit more recently added items first + for (let i = arr.length - 1; i >= 0; i--) { + const tolerance = 5 + const [key, obj] = arr[i] + for (const { left, top, right, bottom } of obj.rects) { + if ( + top <= y + tolerance && + left <= x + tolerance && + bottom > y - tolerance && + right > x - tolerance + ) { + return [key, obj.range, { left, top, right, bottom }] + } + } + } + return [] + } + static underline(rects, options = {}) { + const { color = 'red', width: strokeWidth = 2, padding = 0, writingMode } = options + const g = createSVGElement('g') + g.setAttribute('fill', color) + if (writingMode === 'vertical-rl' || writingMode === 'vertical-lr') + for (const { right, top, height } of rects) { + const el = createSVGElement('rect') + el.setAttribute('x', right - strokeWidth / 2 + padding) + el.setAttribute('y', top) + el.setAttribute('height', height) + el.setAttribute('width', strokeWidth) + g.append(el) + } + else for (const { left, bottom, width } of rects) { + const el = createSVGElement('rect') + el.setAttribute('x', left) + el.setAttribute('y', bottom - strokeWidth / 2 + padding) + el.setAttribute('height', strokeWidth) + el.setAttribute('width', width) + g.append(el) + } + return g + } + static strikethrough(rects, options = {}) { + const { color = 'red', width: strokeWidth = 2, writingMode } = options + const g = createSVGElement('g') + g.setAttribute('fill', color) + if (writingMode === 'vertical-rl' || writingMode === 'vertical-lr') + for (const { right, left, top, height } of rects) { + const el = createSVGElement('rect') + el.setAttribute('x', (right + left) / 2) + el.setAttribute('y', top) + el.setAttribute('height', height) + el.setAttribute('width', strokeWidth) + g.append(el) + } + else for (const { left, top, bottom, width } of rects) { + const el = createSVGElement('rect') + el.setAttribute('x', left) + el.setAttribute('y', (top + bottom) / 2) + el.setAttribute('height', strokeWidth) + el.setAttribute('width', width) + g.append(el) + } + return g + } + static squiggly(rects, options = {}) { + const { color = 'red', width: strokeWidth = 2, padding = 0, writingMode } = options + const g = createSVGElement('g') + g.setAttribute('fill', 'none') + g.setAttribute('stroke', color) + g.setAttribute('stroke-width', strokeWidth) + const block = strokeWidth * 1.5 + if (writingMode === 'vertical-rl' || writingMode === 'vertical-lr') + for (const { right, top, height } of rects) { + const el = createSVGElement('path') + const n = Math.round(height / block / 1.5) + const inline = height / n + const ls = Array.from({ length: n }, + (_, i) => `l${i % 2 ? -block : block} ${inline}`).join('') + el.setAttribute('d', `M${right - strokeWidth / 2 + padding} ${top}${ls}`) + g.append(el) + } + else for (const { left, bottom, width } of rects) { + const el = createSVGElement('path') + const n = Math.round(width / block / 1.5) + const inline = width / n + const ls = Array.from({ length: n }, + (_, i) => `l${inline} ${i % 2 ? block : -block}`).join('') + el.setAttribute('d', `M${left} ${bottom + strokeWidth / 2 + padding}${ls}`) + g.append(el) + } + return g + } + static highlight(rects, options = {}) { + const { + color = 'red', + padding = 0, + radius = 4, + radiusPadding = 2, + vertical = false, + } = options + + const g = createSVGElement('g') + g.setAttribute('fill', color) + g.style.opacity = 'var(--overlayer-highlight-opacity, .3)' + g.style.mixBlendMode = 'var(--overlayer-highlight-blend-mode, normal)' + + for (const [index, { left, top, height, width }] of rects.entries()) { + const isFirst = index === 0 + const isLast = index === rects.length - 1 + + let x, y, w, h + + let radiusTopLeft, radiusTopRight, radiusBottomRight, radiusBottomLeft + + if (vertical) { + x = left - padding + y = top - padding - (isFirst ? radiusPadding : 0) + w = width + padding * 2 + h = height + padding * 2 + (isFirst ? radiusPadding : 0) + (isLast ? radiusPadding : 0) + radiusTopLeft = isFirst ? radius : 0 + radiusTopRight = isFirst ? radius : 0 + radiusBottomRight = isLast ? radius : 0 + radiusBottomLeft = isLast ? radius : 0 + } else { + x = left - padding - (isFirst ? radiusPadding : 0) + y = top - padding + w = width + padding * 2 + (isFirst ? radiusPadding : 0) + (isLast ? radiusPadding : 0) + h = height + padding * 2 + radiusTopLeft = isFirst ? radius : 0 + radiusTopRight = isLast ? radius : 0 + radiusBottomRight = isLast ? radius : 0 + radiusBottomLeft = isFirst ? radius : 0 + } + + const rtl = Math.min(radiusTopLeft, w / 2, h / 2) + const rtr = Math.min(radiusTopRight, w / 2, h / 2) + const rbr = Math.min(radiusBottomRight, w / 2, h / 2) + const rbl = Math.min(radiusBottomLeft, w / 2, h / 2) + + if (rtl === 0 && rtr === 0 && rbr === 0 && rbl === 0) { + const el = createSVGElement('rect') + el.setAttribute('x', x) + el.setAttribute('y', y) + el.setAttribute('height', h) + el.setAttribute('width', w) + g.append(el) + } else { + const el = createSVGElement('path') + const d = ` + M ${x + rtl} ${y} + L ${x + w - rtr} ${y} + ${rtr > 0 ? `Q ${x + w} ${y} ${x + w} ${y + rtr}` : `L ${x + w} ${y}`} + L ${x + w} ${y + h - rbr} + ${rbr > 0 ? `Q ${x + w} ${y + h} ${x + w - rbr} ${y + h}` : `L ${x + w} ${y + h}`} + L ${x + rbl} ${y + h} + ${rbl > 0 ? `Q ${x} ${y + h} ${x} ${y + h - rbl}` : `L ${x} ${y + h}`} + L ${x} ${y + rtl} + ${rtl > 0 ? `Q ${x} ${y} ${x + rtl} ${y}` : `L ${x} ${y}`} + Z + `.trim().replace(/\s+/g, ' ') + el.setAttribute('d', d) + g.append(el) + } + } + return g + } + static outline(rects, options = {}) { + const { color = 'red', width: strokeWidth = 3, padding = 0, radius = 3 } = options + const g = createSVGElement('g') + g.setAttribute('fill', 'none') + g.setAttribute('stroke', color) + g.setAttribute('stroke-width', strokeWidth) + for (const { left, top, height, width } of rects) { + const el = createSVGElement('rect') + el.setAttribute('x', left - padding) + el.setAttribute('y', top - padding) + el.setAttribute('height', height + padding * 2) + el.setAttribute('width', width + padding * 2) + el.setAttribute('rx', radius) + g.append(el) + } + return g + } + static bubble(rects, options = {}) { + const { color = '#fbbf24', writingMode, opacity = 0.85, size = 20, padding = 10 } = options + const isVertical = writingMode === 'vertical-rl' || writingMode === 'vertical-lr' + const g = createSVGElement('g') + g.style.opacity = opacity + if (rects.length === 0) return g + rects.splice(1) + const firstRect = rects[0] + const x = isVertical ? firstRect.right - size + padding : firstRect.right - size + padding + const y = isVertical ? firstRect.bottom - size + padding : firstRect.top - size + padding + firstRect.top = y - padding + firstRect.right = x + size + padding + firstRect.bottom = y + size + padding + firstRect.left = x - padding + const bubble = createSVGElement('path') + const s = size + const r = s * 0.15 + // Speech bubble shape with a small tail + // Main rounded rectangle body + const d = ` + M ${x + r} ${y} + h ${s - 2 * r} + a ${r} ${r} 0 0 1 ${r} ${r} + v ${s * 0.65 - 2 * r} + a ${r} ${r} 0 0 1 ${-r} ${r} + h ${-s * 0.3} + l ${-s * 0.15} ${s * 0.2} + l ${s * 0.05} ${-s * 0.2} + h ${-s * 0.6 + 2 * r} + a ${r} ${r} 0 0 1 ${-r} ${-r} + v ${-s * 0.65 + 2 * r} + a ${r} ${r} 0 0 1 ${r} ${-r} + z + `.replace(/\s+/g, ' ').trim() + + bubble.setAttribute('d', d) + bubble.setAttribute('fill', color) + bubble.setAttribute('stroke', 'rgba(0, 0, 0, 0.2)') + bubble.setAttribute('stroke-width', '1') + // Add horizontal lines inside to represent text + const lineGroup = createSVGElement('g') + lineGroup.setAttribute('stroke', 'rgba(0, 0, 0, 0.3)') + lineGroup.setAttribute('stroke-width', '1.5') + lineGroup.setAttribute('stroke-linecap', 'round') + const lineY1 = y + s * 0.18 + const lineY2 = y + s * 0.33 + const lineY3 = y + s * 0.48 + const lineX1 = x + s * 0.2 + const lineX2 = x + s * 0.8 + const line1 = createSVGElement('line') + line1.setAttribute('x1', lineX1) + line1.setAttribute('y1', lineY1) + line1.setAttribute('x2', lineX2) + line1.setAttribute('y2', lineY1) + const line2 = createSVGElement('line') + line2.setAttribute('x1', lineX1) + line2.setAttribute('y1', lineY2) + line2.setAttribute('x2', lineX2) + line2.setAttribute('y2', lineY2) + const line3 = createSVGElement('line') + line3.setAttribute('x1', lineX1) + line3.setAttribute('y1', lineY3) + line3.setAttribute('x2', x + s * 0.6) + line3.setAttribute('y2', lineY3) + lineGroup.append(line1, line2, line3) + + if (isVertical) { + const centerX = x + s / 2 + const centerY = y + s / 2 + bubble.setAttribute('transform', `rotate(90 ${centerX} ${centerY})`) + lineGroup.setAttribute('transform', `rotate(90 ${centerX} ${centerY})`) + } + + g.append(bubble) + g.append(lineGroup) + return g + } + // make an exact copy of an image in the overlay + // one can then apply filters to the entire element, without affecting them; + // it's a bit silly and probably better to just invert images twice + // (though the color will be off in that case if you do heu-rotate) + static copyImage([rect], options = {}) { + const { src } = options + const image = createSVGElement('image') + const { left, top, height, width } = rect + image.setAttribute('href', src) + image.setAttribute('x', left) + image.setAttribute('y', top) + image.setAttribute('height', height) + image.setAttribute('width', width) + return image + } +} + diff --git a/web/vendor/foliate-js/package.json b/web/vendor/foliate-js/package.json new file mode 100644 index 00000000..dc87561a --- /dev/null +++ b/web/vendor/foliate-js/package.json @@ -0,0 +1,50 @@ +{ + "name": "foliate-js", + "version": "0.0.0", + "description": "Render e-books in the browser", + "repository": { + "type": "git", + "url": "git+https://github.com/johnfactotum/foliate-js.git" + }, + "bugs": { + "url": "https://github.com/johnfactotum/foliate-js/issues" + }, + "homepage": "https://github.com/johnfactotum/foliate-js#readme", + "author": "John Factotum", + "license": "MIT", + "type": "module", + "exports": { + "./*.js": "./*.js" + }, + "devDependencies": { + "@eslint/js": "^9.9.1", + "@rollup/plugin-node-resolve": "^15.2.3", + "@rollup/plugin-terser": "^0.4.4", + "@zip.js/zip.js": "^2.7.52", + "fflate": "^0.8.2", + "fs-extra": "^11.2.0", + "globals": "^15.9.0", + "pdfjs-dist": "^5.4.530", + "rollup": "^4.22.4" + }, + "scripts": { + "build": "npx rollup -c" + }, + "keywords": [ + "ebook", + "reader", + "epub", + "cfi", + "mobi", + "azw", + "azw3", + "fb2", + "cbz", + "dictd", + "stardict", + "opds" + ], + "dependencies": { + "construct-style-sheets-polyfill": "^3.1.0" + } +} diff --git a/web/vendor/foliate-js/paginator.js b/web/vendor/foliate-js/paginator.js new file mode 100644 index 00000000..fd9b154d --- /dev/null +++ b/web/vendor/foliate-js/paginator.js @@ -0,0 +1,2332 @@ +const wait = ms => new Promise(resolve => setTimeout(resolve, ms)) + +const debounce = (f, wait, immediate) => { + let timeout + return (...args) => { + const later = () => { + timeout = null + if (!immediate) f(...args) + } + const callNow = immediate && !timeout + if (timeout) clearTimeout(timeout) + timeout = setTimeout(later, wait) + if (callNow) f(...args) + } +} + +// Transforms ALL children of the container so multi-view layouts +// animate as a unified whole. Extra elements (e.g. background) are +// also transformed so they slide in sync with the content. +const animateScroll = (element, scrollProp, startValue, endValue, duration, extraElements = []) => new Promise(resolve => { + if (document.hidden) { + element[scrollProp] = endValue + return resolve() + } + + const children = [...element.children] + if (!children.length) { + element[scrollProp] = endValue + return resolve() + } + + const allElements = [...children, ...extraElements] + const isHorizontal = scrollProp === 'scrollLeft' + const delta = endValue - startValue + const transformProp = isHorizontal ? 'translateX' : 'translateY' + + // Prepare all elements for animation + for (const el of allElements) { + el.style.willChange = 'transform' + el.style.transform = `${transformProp}(0px)` + el.style.transition = 'none' + } + + // Force reflow to apply initial state + element.getBoundingClientRect() + + // Start animation on all elements + for (const el of allElements) { + el.style.transition = `transform ${duration}ms cubic-bezier(0.25, 0.46, 0.45, 0.94)` + el.style.transform = `${transformProp}(${-delta}px)` + } + + let resolved = false + const cleanup = () => { + if (resolved) return + resolved = true + + for (const el of allElements) { + el.style.willChange = '' + el.style.transform = '' + el.style.transition = '' + } + + // Apply final scroll position + element[scrollProp] = endValue + resolve() + } + + // Listen for transition end on the first child + const first = children[0] + const onTransitionEnd = (e) => { + if (e.target === first && e.propertyName === 'transform') { + first.removeEventListener('transitionend', onTransitionEnd) + cleanup() + } + } + first.addEventListener('transitionend', onTransitionEnd) + + // Fallback timeout in case transitionend doesn't fire + setTimeout(cleanup, duration + 50) +}) + +// collapsed range doesn't return client rects sometimes (or always?) +// try make get a non-collapsed range or element +const uncollapse = range => { + if (!range?.collapsed) return range + const { endOffset, endContainer } = range + if (endContainer.nodeType === 1) { + const node = endContainer.childNodes[endOffset] + if (node?.nodeType === 1) return node + return endContainer + } + if (endOffset + 1 < endContainer.length) range.setEnd(endContainer, endOffset + 1) + else if (endOffset > 1) range.setStart(endContainer, endOffset - 1) + else return endContainer.parentNode + return range +} + +const makeRange = (doc, node, start, end = start) => { + const range = doc.createRange() + range.setStart(node, start) + range.setEnd(node, end) + return range +} + +// use binary search to find an offset value in a text node +const bisectNode = (doc, node, cb, start = 0, end = node.nodeValue.length) => { + if (end - start === 1) { + const result = cb(makeRange(doc, node, start), makeRange(doc, node, end)) + return result < 0 ? start : end + } + const mid = Math.floor(start + (end - start) / 2) + const result = cb(makeRange(doc, node, start, mid), makeRange(doc, node, mid, end)) + return result < 0 ? bisectNode(doc, node, cb, start, mid) + : result > 0 ? bisectNode(doc, node, cb, mid, end) : mid +} + +const { SHOW_ELEMENT, SHOW_TEXT, SHOW_CDATA_SECTION, + FILTER_ACCEPT, FILTER_REJECT, FILTER_SKIP } = NodeFilter + +const filter = SHOW_ELEMENT | SHOW_TEXT | SHOW_CDATA_SECTION + +// needed cause there seems to be a bug in `getBoundingClientRect()` in Firefox +// where it fails to include rects that have zero width and non-zero height +// (CSSOM spec says "rectangles [...] of which the height or width is not zero") +// which makes the visible range include an extra space at column boundaries +const getBoundingClientRect = target => { + let top = Infinity, right = -Infinity, left = Infinity, bottom = -Infinity + for (const rect of target.getClientRects()) { + left = Math.min(left, rect.left) + top = Math.min(top, rect.top) + right = Math.max(right, rect.right) + bottom = Math.max(bottom, rect.bottom) + } + return new DOMRect(left, top, right - left, bottom - top) +} + +const getVisibleRange = (doc, start, end, mapRect) => { + // first get all visible nodes + const acceptNode = node => { + const name = node.localName?.toLowerCase() + // ignore all scripts, styles, and their children + if (name === 'script' || name === 'style') return FILTER_REJECT + if (node.nodeType === 1) { + const { left, right } = mapRect(node.getBoundingClientRect()) + if (left === 0 && right === 0) return FILTER_REJECT + // no need to check child nodes if it's completely out of view + if (right < start || left > end) return FILTER_REJECT + // elements must be completely in view to be considered visible + // because you can't specify offsets for elements + if (left >= start && right <= end) return FILTER_ACCEPT + // TODO: it should probably allow elements that do not contain text + // because they can exceed the whole viewport in both directions + // especially in scrolled mode + } else { + // ignore empty text nodes + if (!node.nodeValue?.trim()) return FILTER_SKIP + // create range to get rect + const range = doc.createRange() + range.selectNodeContents(node) + const { left, right } = mapRect(range.getBoundingClientRect()) + // it's visible if any part of it is in view + if (left === 0 && right === 0) return FILTER_REJECT + if (right >= start && left <= end) return FILTER_ACCEPT + } + return FILTER_SKIP + } + const walker = doc.createTreeWalker(doc.body, filter, { acceptNode }) + const nodes = [] + for (let node = walker.nextNode(); node; node = walker.nextNode()) + nodes.push(node) + + // we're only interested in the first and last visible nodes + const from = nodes[0] ?? doc.body + const to = nodes[nodes.length - 1] ?? from + + // find the offset at which visibility changes + const startOffset = from.nodeType === 1 ? 0 + : bisectNode(doc, from, (a, b) => { + const p = mapRect(getBoundingClientRect(a)) + const q = mapRect(getBoundingClientRect(b)) + if (p.right < start && q.left > start) return 0 + return q.left > start ? -1 : 1 + }) + const endOffset = to.nodeType === 1 ? 0 + : bisectNode(doc, to, (a, b) => { + const p = mapRect(getBoundingClientRect(a)) + const q = mapRect(getBoundingClientRect(b)) + if (p.right < end && q.left > end) return 0 + return q.left > end ? -1 : 1 + }) + + const range = doc.createRange() + range.setStart(from, startOffset) + range.setEnd(to, endOffset) + return range +} + +const selectionIsBackward = sel => { + const range = document.createRange() + range.setStart(sel.anchorNode, sel.anchorOffset) + range.setEnd(sel.focusNode, sel.focusOffset) + return range.collapsed +} + +const setSelectionTo = (target, collapse) => { + let range + if (target.startContainer) range = target.cloneRange() + else if (target.nodeType) { + range = document.createRange() + range.selectNode(target) + } + if (range) { + const sel = range.startContainer.ownerDocument?.defaultView.getSelection() + if (sel) { + sel.removeAllRanges() + if (collapse === -1) range.collapse(true) + else if (collapse === 1) range.collapse() + sel.addRange(range) + } + } +} + +export const getDirection = doc => { + const { defaultView } = doc + let { writingMode, direction } = defaultView.getComputedStyle(doc.body) + // Some EPUBs set writing-mode on the first child of body instead of body itself + if (!writingMode || writingMode === 'horizontal-tb') { + const firstChild = doc.body.querySelector(':scope > :not([cfi-inert])') + if (firstChild) { + const childStyle = defaultView.getComputedStyle(firstChild) + if (childStyle.writingMode === 'vertical-rl' + || childStyle.writingMode === 'vertical-lr') { + writingMode = childStyle.writingMode + } + } + } + const vertical = writingMode === 'vertical-rl' + || writingMode === 'vertical-lr' + const rtl = doc.body.dir === 'rtl' + || direction === 'rtl' + || doc.documentElement.dir === 'rtl' + return { vertical, rtl } +} + +const getBackground = doc => { + const bodyStyle = doc.defaultView.getComputedStyle(doc.body) + return bodyStyle.backgroundColor === 'rgba(0, 0, 0, 0)' + && bodyStyle.backgroundImage === 'none' + ? doc.defaultView.getComputedStyle(doc.documentElement).background + : bodyStyle.background +} + +const makeMarginals = (length, part) => Array.from({ length }, () => { + const div = document.createElement('div') + const child = document.createElement('div') + div.append(child) + child.setAttribute('part', part) + return div +}) + +const setStyles = (el, styles) => { + const { style } = el + for (const [k, v] of Object.entries(styles)) style.setProperty(k, v) +} + +const setStylesImportant = (el, styles) => { + const { style } = el + for (const [k, v] of Object.entries(styles)) style.setProperty(k, v, 'important') +} + +class View { + #observer = new ResizeObserver(() => this.expand()) + #element = document.createElement('div') + #iframe = document.createElement('iframe') + #contentRange = document.createRange() + #overlayer + #vertical = false + #rtl = false + #column = true + #size + #columnCount = 1 + #layout = {} + #contentPages = 0 + #bgImageSize = null + fontReady = Promise.resolve() + constructor({ container, onExpand }) { + this.container = container + this.onExpand = onExpand + this.#iframe.setAttribute('part', 'filter') + this.#element.append(this.#iframe) + Object.assign(this.#element.style, { + boxSizing: 'content-box', + position: 'relative', + overflow: 'hidden', + flex: '0 0 auto', + width: '100%', height: '100%', + display: 'flex', + justifyContent: 'flex-start', + alignItems: 'center', + }) + Object.assign(this.#iframe.style, { + overflow: 'hidden', + border: '0', + display: 'none', + width: '100%', height: '100%', + }) + // `allow-scripts` is needed for events because of WebKit bug + // https://bugs.webkit.org/show_bug.cgi?id=218086 + this.#iframe.setAttribute('sandbox', 'allow-same-origin allow-scripts') + this.#iframe.setAttribute('scrolling', 'no') + } + get element() { + return this.#element + } + get document() { + return this.#iframe.contentDocument + } + get contentPages() { + return this.#contentPages + } + async load(src, data, afterLoad, beforeRender) { + if (typeof src !== 'string') throw new Error(`${src} is not string`) + return new Promise(resolve => { + this.#iframe.addEventListener('load', () => { + const doc = this.document + afterLoad?.(doc) + + this.#iframe.setAttribute('aria-label', doc.title) + // it needs to be visible for Firefox to get computed style + this.#iframe.style.display = 'block' + const { vertical, rtl } = getDirection(doc) + this.docBackground = getBackground(doc) + doc.body.style.background = 'none' + // Preload background image to get natural dimensions; + // in scrolled mode the view expands to fit the image. + const bgUrl = this.docBackground + ?.match(/url\(["']?([^"')]+)["']?\)/)?.[1] + if (bgUrl && !this.container.noBackground) { + const img = new Image() + img.onload = () => { + this.#bgImageSize = { + width: img.naturalWidth, + height: img.naturalHeight, + } + if (!this.#column) this.expand() + } + img.src = bgUrl + } + this.#iframe.style.display = 'none' + + this.#vertical = vertical + this.#rtl = rtl + + this.#contentRange.selectNodeContents(doc.body) + const layout = beforeRender?.({ vertical, rtl }) + this.#iframe.style.display = 'block' + this.render(layout) + this.#observer.observe(doc.body) + + // the resize observer above doesn't work in Firefox + // (see https://bugzilla.mozilla.org/show_bug.cgi?id=1832939) + // until the bug is fixed we can at least account for font load + this.fontReady = doc.fonts.ready.then(() => this.expand()) + + resolve() + }, { once: true }) + if (data) { + this.#iframe.srcdoc = data + } else { + this.#iframe.src = src + } + }) + } + render(layout) { + if (!layout || !this.document?.documentElement) return + this.#column = layout.flow !== 'scrolled' + this.#layout = layout + if (this.#column) this.columnize(layout) + else this.scrolled(layout) + } + scrolled({ width, height, marginTop, marginRight, marginBottom, marginLeft, gap, columnWidth }) { + const vertical = this.#vertical + const doc = this.document + setStylesImportant(doc.documentElement, { + 'box-sizing': 'border-box', + 'column-width': 'auto', + 'height': 'auto', + 'width': 'auto', + }) + const availableWidth = Math.trunc(width - marginLeft - marginRight) + const availableHeight = Math.trunc(height - marginTop - marginBottom) + const sidePaddingLeft = marginLeft / 2 + gap / 2 + const sidePaddingRight = marginRight / 2 + gap / 2 + setStyles(doc.documentElement, { + 'padding': vertical + ? `${marginTop * 1.5}px 0px ${marginBottom * 1.5}px 0px` + : `0px ${sidePaddingRight}px 0px ${sidePaddingLeft}px`, + '--page-margin-top': `${vertical ? marginTop * 1.5 : marginTop}px`, + '--page-margin-right': `${vertical ? marginRight : sidePaddingRight}px`, + '--page-margin-bottom': `${vertical ? marginBottom * 1.5 : marginBottom}px`, + '--page-margin-left': `${vertical ? marginLeft : sidePaddingLeft}px`, + '--full-width': `${Math.trunc(width)}`, + '--full-height': `${Math.trunc(height)}`, + '--available-width': `${availableWidth}`, + '--available-height': `${availableHeight}`, + }) + setStylesImportant(doc.body, { + [vertical ? 'max-height' : 'max-width']: `${columnWidth}px`, + 'margin': 'auto', + // Prevent position:absolute/fixed on body from coupling its + // size to the iframe, which causes diverging expand() loops + 'position': 'static', + }) + this.setImageSize(availableWidth, availableHeight) + this.expand() + } + columnize({ width, height, marginTop, marginRight, marginBottom, marginLeft, gap, columnWidth, columnCount }) { + const vertical = this.#vertical + this.#size = vertical ? height : width + this.#columnCount = columnCount || 1 + + const doc = this.document + const horizontalColumnGap = columnCount > 1 ? (marginLeft + marginRight) / 4 + gap / 2 : (marginLeft + marginRight) / 2 + gap + const sidePaddingLeft = columnCount > 1 ? marginLeft / 4 + gap / 4 : marginLeft / 2 + gap / 2 + const sidePaddingRight = columnCount > 1 ? marginRight / 4 + gap / 4 : marginRight / 2 + gap / 2 + setStylesImportant(doc.documentElement, { + 'box-sizing': 'border-box', + 'column-width': `${Math.trunc(columnWidth)}px`, + 'column-gap': vertical ? `${(marginTop + marginBottom) * 1.5}px` : `${horizontalColumnGap}px`, + 'column-fill': 'auto', + ...(vertical + ? { 'width': `${width}px` } + : { 'height': `${height}px` }), + 'overflow': 'hidden', + // force wrap long words + 'overflow-wrap': 'break-word', + // reset some potentially problematic props + 'position': 'static', 'border': '0', 'margin': '0', + 'max-height': 'none', 'max-width': 'none', + 'min-height': 'none', 'min-width': 'none', + // fix glyph clipping in WebKit + '-webkit-line-box-contain': 'block glyphs replaced', + }) + const availableWidth = vertical + ? Math.trunc(width - marginLeft / 2 - marginRight / 2 - gap) + : Math.trunc(width / this.#columnCount) + const availableHeight = vertical + ? Math.trunc(height / this.#columnCount) + : Math.trunc(height - marginTop - marginBottom) + setStyles(doc.documentElement, { + 'padding': vertical + ? `${marginTop * 1.5}px ${marginRight}px ${marginBottom * 1.5}px ${marginLeft}px` + : `${marginTop}px ${sidePaddingRight}px ${marginBottom}px ${sidePaddingLeft}px`, + '--page-margin-top': `${vertical ? marginTop * 1.5 : marginTop}px`, + '--page-margin-right': `${vertical ? marginRight : sidePaddingRight}px`, + '--page-margin-bottom': `${vertical ? marginBottom * 1.5 : marginBottom}px`, + '--page-margin-left': `${vertical ? marginLeft : sidePaddingLeft}px`, + '--full-width': `${Math.trunc(availableWidth)}`, + '--full-height': `${Math.trunc(availableHeight)}`, + '--available-width': `${availableWidth}`, + '--available-height': `${availableHeight}`, + }) + setStylesImportant(doc.body, { + 'max-height': 'none', + 'max-width': 'none', + 'margin': '0', + // Prevent position:absolute/fixed on body from coupling its + // size to the iframe, which causes diverging expand() loops + 'position': 'static', + }) + this.setImageSize(availableWidth, availableHeight) + this.expand() + } + setImageSize(availableWidth, availableHeight) { + const { width, height, marginTop, marginRight, marginBottom, marginLeft } = this.#layout + const vertical = this.#vertical + const doc = this.document + const pageFullscreen = doc.documentElement.hasAttribute('data-duokan-page-fullscreen') + for (const el of doc.body.querySelectorAll('img, svg, video')) { + // clear previous inline constraints so we read CSS-authored values, + // not stale pixel values from a previous resize (#3634) + el.style.removeProperty('max-width') + el.style.removeProperty('max-height') + // preserve max size if they are already set in CSS + let { maxHeight, maxWidth } = doc.defaultView.getComputedStyle(el) + if (parseInt(maxWidth) > availableWidth) { + maxWidth = `${availableWidth}px` + } + if (parseInt(maxHeight) > availableHeight) { + maxHeight = `${availableHeight}px` + } + setStylesImportant(el, { + 'max-height': vertical + ? (maxHeight !== 'none' && maxHeight !== '0px' ? maxHeight : '100%') + : `${height - (pageFullscreen ? 0 : (marginTop + marginBottom))}px`, + 'max-width': vertical + ? `${width - (pageFullscreen ? 0 : (marginLeft + marginRight))}px` + : (maxWidth !== 'none' && maxWidth !== '0px' ? maxWidth : '100%'), + 'object-fit': pageFullscreen ? 'cover': 'contain', + 'page-break-inside': 'avoid', + 'break-inside': 'avoid', + 'box-sizing': 'border-box', + }) + if (pageFullscreen) { + setStylesImportant(doc.documentElement, { + position: 'relative', + }) + setStylesImportant(el, { + position: 'absolute', + inset: '0', + width: '100%', + height: '100%', + margin: '0', + }) + let ancestor = el.parentElement + while (ancestor && ancestor !== doc.body) { + setStylesImportant(ancestor, { + width: '100%', + height: '100%', + margin: '0', + padding: '0', + }) + ancestor = ancestor.parentElement + } + if (el.localName === 'svg') { + el.setAttribute('preserveAspectRatio', 'xMidYMid slice') + } + } + } + } + get #zoom() { + // Safari does not zoom the client rects, while Chrome, Edge and Firefox does + if (/^((?!chrome|android).)*AppleWebKit/i.test(navigator.userAgent) && !window.chrome) { + return window.getComputedStyle(this.document.body).zoom || 1.0 + } + return 1.0 + } + expand() { + if (!this.document?.documentElement) return + const { documentElement } = this.document + if (this.#column) { + const side = this.#vertical ? 'height' : 'width' + const otherSide = this.#vertical ? 'width' : 'height' + const contentRect = this.#contentRange.getBoundingClientRect() + const rootRect = documentElement.getBoundingClientRect() + // offset caused by column break at the start of the page + // which seem to be supported only by WebKit and only for horizontal writing + const contentStart = this.#vertical ? 0 + : this.#rtl ? rootRect.right - contentRect.right : contentRect.left - rootRect.left + const contentSize = (contentStart + contentRect[side]) * this.#zoom + // Size content by individual columns, not full spreads. + // This allows adjacent sections to share a spread when a + // section doesn't fill all available columns. + const columnSize = this.#size / this.#columnCount + const pageCount = Math.ceil(contentSize / columnSize) + this.#contentPages = pageCount + const expandedSize = pageCount * columnSize + this.#element.style.padding = '0' + this.#iframe.style[side] = `${expandedSize}px` + this.#element.style[side] = `${expandedSize}px` + this.#iframe.style[otherSide] = '100%' + this.#element.style[otherSide] = '100%' + // One column per "page" — overflow columns extend into adjacent pages + documentElement.style[side] = `${columnSize}px` + if (this.#overlayer) { + this.#overlayer.element.style.margin = '0' + this.#overlayer.element.style.left = '0' + this.#overlayer.element.style.top = '0' + this.#overlayer.element.style[side] = `${expandedSize}px` + this.#overlayer.redraw() + } + } else { + const side = this.#vertical ? 'width' : 'height' + const otherSide = this.#vertical ? 'height' : 'width' + const contentSize = documentElement.getBoundingClientRect()[side] + let expandedSize = contentSize + // If the section has a background image, ensure the view is + // at least as large as the image scaled to fit the cross axis + if (this.#bgImageSize) { + const crossSize = this.#element.getBoundingClientRect()[otherSide] + if (crossSize > 0) { + const { width: imgW, height: imgH } = this.#bgImageSize + const scaledSize = this.#vertical + ? imgW * crossSize / imgH + : imgH * crossSize / imgW + expandedSize = Math.max(expandedSize, scaledSize) + } + } + this.#element.style.padding = '0' + this.#iframe.style[side] = `${expandedSize}px` + this.#element.style[side] = `${expandedSize}px` + this.#iframe.style[otherSide] = '100%' + this.#element.style[otherSide] = '100%' + if (this.#overlayer) { + this.#overlayer.element.style.margin = '0' + this.#overlayer.element.style.left = '0' + this.#overlayer.element.style.top = '0' + this.#overlayer.element.style[side] = `${expandedSize}px` + this.#overlayer.redraw() + } + } + this.onExpand() + } + set overlayer(overlayer) { + this.#overlayer = overlayer + this.#element.append(overlayer.element) + } + get overlayer() { + return this.#overlayer + } + #loupeEl = null + #loupeScaler = null + #loupeCursor = null + // Show a magnifier loupe inside the iframe document. + // winX/winY are in main-window (screen) coordinates. + showLoupe(winX, winY, { isVertical, color, gap, margin, radius, magnification }) { + const doc = this.document + if (!doc) return + + const frameRect = this.#iframe.getBoundingClientRect() + // Cursor in iframe-viewport coordinates. + const vpX = winX - frameRect.left + const vpY = winY - frameRect.top + + // Cursor in document coordinates (accounts for scroll). + const scrollX = doc.scrollingElement?.scrollLeft ?? 0 + const scrollY = doc.scrollingElement?.scrollTop ?? 0 + const docX = vpX + scrollX + const docY = vpY + scrollY + + const MAGNIFICATION = magnification + const MARGIN = margin + + // Capsule dimensions: elongated along the reading direction. + // For horizontal text the capsule is wider; for vertical it is taller. + const shortSide = radius * 2 + const longSide = Math.round(radius * 3.6) + const loupeW = isVertical ? shortSide : longSide + const loupeH = isVertical ? longSide : shortSide + const halfW = loupeW / 2 + const halfH = loupeH / 2 + const borderRadius = shortSide / 2 // fully rounded ends + + // Position loupe above the cursor (or to the left for vertical text). + const GAP = gap + let loupeLeft = isVertical ? vpX - loupeW - GAP : vpX - halfW + let loupeTop = isVertical ? vpY - halfH : vpY - loupeH - GAP + loupeLeft = Math.max(MARGIN, Math.min(loupeLeft, frameRect.width - loupeW - MARGIN)) + loupeTop = Math.max(MARGIN, Math.min(loupeTop, frameRect.height - loupeH - MARGIN)) + + // CSS-transform math: map document point (docX, docY) to loupe centre. + // visual_pos = offset + coord × MAGNIFICATION = halfW (or halfH) + // ⟹ offset = half − coord × MAGNIFICATION + const offsetX = halfW - docX * MAGNIFICATION + const offsetY = halfH - docY * MAGNIFICATION + + // Build loupe DOM structure once; cache it across hide/show cycles so + // the expensive body clone is not repeated on every drag start. + if (!this.#loupeEl || !this.#loupeEl.isConnected) { + this.#loupeEl = doc.createElement('div') + + // Clone the live body once — inside the iframe the epub's CSS + // variables, @font-face fonts, and styles apply automatically. + const bodyClone = doc.body.cloneNode(true) + + // Wrap the clone in a div that replicates documentElement's inline + // styles (column-width, column-gap, padding, height, etc.) so text + // flows with the same column layout as the original document. + const htmlWrapper = doc.createElement('div') + htmlWrapper.style.cssText = doc.documentElement.style.cssText + // expand() constrains documentElement's page-axis dimension to one + // page size (width for horizontal, height for vertical). Override + // with the full scroll dimension so all columns are rendered. + if (this.#vertical) + htmlWrapper.style.height = `${doc.documentElement.scrollHeight}px` + else + htmlWrapper.style.width = `${doc.documentElement.scrollWidth}px` + htmlWrapper.appendChild(bodyClone) + + this.#loupeScaler = doc.createElement('div') + this.#loupeScaler.appendChild(htmlWrapper) + + const cursorLen = Math.round(shortSide * 0.44) + this.#loupeCursor = doc.createElement('div') + this.#loupeCursor.style.cssText = isVertical + ? `position:absolute;left:calc(50% - ${cursorLen / 2}px);top:50%;` + + `margin-top:-1px;width:${cursorLen}px;height:2px;background:${color};pointer-events:none;z-index:1;box-sizing:border-box;` + : `position:absolute;left:50%;top:calc(50% - ${cursorLen / 2}px);` + + `margin-left:-1px;width:2px;height:${cursorLen}px;background:${color};pointer-events:none;z-index:1;box-sizing:border-box;` + + this.#loupeEl.appendChild(this.#loupeScaler) + this.#loupeEl.appendChild(this.#loupeCursor) + doc.documentElement.appendChild(this.#loupeEl) + + // Static loupe shell styles (set once). + this.#loupeEl.style.cssText = ` + position: absolute; + width: ${loupeW}px; + height: ${loupeH}px; + border-radius: ${borderRadius}px; + overflow: hidden; + border: 2.5px solid ${color}; + box-shadow: 0 6px 24px rgba(0,0,0,0.28); + background-color: var(--theme-bg-color); + z-index: 9999; + pointer-events: none; + user-select: none; + box-sizing: border-box; + contain: strict; + ` + + // Static scaler styles (set once; only left/top change per move). + this.#loupeScaler.style.cssText = ` + position: absolute; + transform: scale(${MAGNIFICATION}); + transform-origin: 0 0; + pointer-events: none; + ` + } + + // Ensure visible (hideLoupe hides via CSS instead of removing). + this.#loupeEl.style.display = '' + + // Update only the dynamic position values (fast path on every move). + this.#loupeScaler.style.left = `${offsetX}px` + this.#loupeScaler.style.top = `${offsetY}px` + this.#loupeScaler.style.width = `${doc.documentElement.scrollWidth}px` + this.#loupeScaler.style.height = `${doc.documentElement.scrollHeight}px` + this.#loupeEl.style.left = `${loupeLeft + scrollX}px` + this.#loupeEl.style.top = `${loupeTop + scrollY}px` + + // Cut a capsule-shaped hole in the overlayer so highlights don't paint + // over the loupe. + if (this.#overlayer) { + const overlayerRect = this.#overlayer.element.getBoundingClientRect() + const dx = frameRect.left - overlayerRect.left + const dy = frameRect.top - overlayerRect.top + + const pad = 3 + const cx = loupeLeft + halfW + dx + const cy = loupeTop + halfH + dy + + this.#overlayer.setHole(cx, cy, loupeW + pad * 2, loupeH + pad * 2, borderRadius + pad) + } + } + hideLoupe() { + // Hide via CSS instead of removing — keeps the cached body clone so + // the next showLoupe call skips the expensive cloneNode(true). + if (this.#loupeEl) { + this.#loupeEl.style.display = 'none' + } + if (this.#overlayer) + this.#overlayer.clearHole() + } + destroyLoupe() { + if (this.#loupeEl) { + this.#loupeEl.remove() + this.#loupeEl = null + this.#loupeScaler = null + this.#loupeCursor = null + } + if (this.#overlayer) + this.#overlayer.clearHole() + } + destroy() { + if (this.document?.body) this.#observer.unobserve(this.document.body) + this.destroyLoupe() + } +} + +// NOTE: everything here assumes the so-called "negative scroll type" for RTL +export class Paginator extends HTMLElement { + static observedAttributes = [ + 'flow', 'gap', 'margin-top', 'margin-bottom', 'margin-left', 'margin-right', + 'max-inline-size', 'max-block-size', 'max-column-count', + 'no-preload', 'no-background', 'no-continuous-scroll', + ] + #root = this.attachShadow({ mode: 'open' }) + #observer = new ResizeObserver(() => this.render()) + #top + #background + #container + #header + #footer + #views = new Map() // Map + #primaryIndex = -1 + #vertical = false + #rtl = false + #marginTop = 0 + #marginBottom = 0 + #anchor = 0 // anchor view to a fraction (0-1), Range, or Element + #justAnchored = false + #locked = false // while true, prevent any further navigation + #styles + #styleMap = new WeakMap() + #mediaQuery = matchMedia('(prefers-color-scheme: dark)') + #mediaQueryListener + #scrollBounds + #touchState + #touchScrolled + #lastVisibleRange + #scrollLocked = false + #isAnimating = false + #filling = false // true while #fillVisibleArea is running + #fillPromise = null // tracks in-progress #fillVisibleArea for awaiting + #stabilizing = false // true while #display is stabilizing layout + #rendered = false // true after first #display completes + #lastLayout = null // cached layout from the last #beforeRender call + // Cache of section index → vertical (boolean). Populated as views + // are loaded so we can check direction *before* loading a section. + #directionCache = new Map() + constructor() { + super() + this.#root.innerHTML = ` +
+
+ +
+ +
+ ` + + this.#top = this.#root.getElementById('top') + this.#background = this.#root.getElementById('background') + this.#container = this.#root.getElementById('container') + this.#header = this.#root.getElementById('header') + this.#footer = this.#root.getElementById('footer') + + this.#observer.observe(this.#container) + const debouncedScroll = debounce(() => { + if (this.scrolled && !this.#isAnimating) { + // Skip entirely while stabilizing — preserve #justAnchored + // so the first post-stabilization fire still sees it. + if (this.#stabilizing) return + if (this.#justAnchored) this.#justAnchored = false + else this.#afterScroll('scroll') + // Load previous section when user scrolls near the top. + // Done in debounced handler (not instant) to avoid cascade + // from rapid DOM insertions breaking scroll anchoring. + if (!this.noPreload && !this.noContinuousScroll && !this.#filling && this.#renderedStart < this.size) { + const sorted = this.#sortedViews + const firstIndex = sorted[0]?.[0] + if (firstIndex != null) { + const prevIdx = this.#adjacentIndex(-1, firstIndex) + if (prevIdx != null && !this.#views.has(prevIdx) && this.#isSameDirection(prevIdx)) { + this.#filling = true + this.#loadAdjacentSection(prevIdx) + .finally(() => { + this.#filling = false + this.dispatchEvent(new Event('stabilized')) + }) + } + } + } + } else if (!this.scrolled) { + this.#afterScroll('container-scroll') + } + }, 250) + this.#container.addEventListener('scroll', () => { + if (!this.#isAnimating) this.dispatchEvent(new Event('scroll')) + // Preload forward when fewer than minPages ahead + if (!this.noPreload && !this.noContinuousScroll && !this.#filling && !this.#stabilizing) { + const minPages = 5 + const pagesAhead = this.size > 0 + ? Math.floor((this.#renderedViewSize - this.#renderedEnd) / this.size) + : 0 + if (pagesAhead < minPages) { + const sorted = this.#sortedViews + const lastIndex = sorted[sorted.length - 1]?.[0] + if (lastIndex != null) { + const nextIdx = this.#adjacentIndex(1, lastIndex) + if (nextIdx != null && !this.#views.has(nextIdx) && this.#isSameDirection(nextIdx)) { + this.#filling = true + this.#loadAdjacentSection(nextIdx) + .finally(() => { + this.#filling = false + this.dispatchEvent(new Event('stabilized')) + }) + } + } + } + } + debouncedScroll() + }) + + const opts = { passive: false } + this.addEventListener('touchstart', this.#onTouchStart.bind(this), opts) + this.addEventListener('touchmove', this.#onTouchMove.bind(this), opts) + this.addEventListener('touchend', this.#onTouchEnd.bind(this)) + this.addEventListener('load', ({ detail: { doc } }) => { + doc.addEventListener('touchstart', this.#onTouchStart.bind(this), opts) + doc.addEventListener('touchmove', this.#onTouchMove.bind(this), opts) + doc.addEventListener('touchend', this.#onTouchEnd.bind(this)) + }) + + this.addEventListener('relocate', ({ detail }) => { + if (detail.reason === 'selection') setSelectionTo(this.#anchor, 0) + else if (detail.reason === 'navigation') { + if (this.#anchor === 1) setSelectionTo(detail.range, 1) + else if (typeof this.#anchor === 'number') + setSelectionTo(detail.range, -1) + else setSelectionTo(this.#anchor, -1) + } + }) + const checkPointerSelection = debounce((range, sel) => { + if (!sel.rangeCount) return + const selRange = sel.getRangeAt(0) + const backward = selectionIsBackward(sel) + if (backward && selRange.compareBoundaryPoints(Range.START_TO_START, range) < 0) + this.prev() + else if (!backward && selRange.compareBoundaryPoints(Range.END_TO_END, range) > 0) + this.next() + }, 700) + this.addEventListener('load', ({ detail: { doc } }) => { + let isPointerSelecting = false + doc.addEventListener('pointerdown', () => isPointerSelecting = true) + doc.addEventListener('pointerup', () => isPointerSelecting = false) + let isKeyboardSelecting = false + doc.addEventListener('keydown', () => isKeyboardSelecting = true) + doc.addEventListener('keyup', () => isKeyboardSelecting = false) + doc.addEventListener('selectionchange', () => { + if (this.scrolled) return + const range = this.#lastVisibleRange + if (!range) return + const sel = doc.getSelection() + if (!sel.rangeCount) return + // FIXME: this won't work on Android WebView, disable for now + if (!isPointerSelecting && isPointerSelecting && sel.type === 'Range') + checkPointerSelection(range, sel) + else if (isKeyboardSelecting) { + const selRange = sel.getRangeAt(0).cloneRange() + const backward = selectionIsBackward(sel) + if (!backward) selRange.collapse() + this.#scrollToAnchor(selRange) + } + }) + doc.addEventListener('focusin', e => { + if (this.scrolled) return null + if (this.#container && this.#container.contains(e.target)) { + // NOTE: `requestAnimationFrame` is needed in WebKit + requestAnimationFrame(() => this.#scrollToAnchor(e.target)) + } + }) + }) + + this.#mediaQueryListener = () => { + const view = this.#primaryView + if (!view) return + this.#replaceBackground() + } + this.#mediaQuery.addEventListener('change', this.#mediaQueryListener) + } + get #primaryView() { + return this.#views.get(this.#primaryIndex) + } + get #sortedViews() { + return [...this.#views.entries()].sort(([a], [b]) => a - b) + } + get primaryIndex() { + return this.#primaryIndex + } + setAttribute(name, value) { + // The scrolled-mode scroll handler is debounced, so #anchor and + // #primaryIndex can lag behind the user's actual viewport by up to + // ~250ms. Toggling out of scrolled mode within that window made + // render() restore the stale anchor — reverting the position to a + // previously visible section. Flush the pending scroll state here, + // before the attribute change so the layout is still in scrolled + // mode and `this.scrolled` (which reads the attribute) is still true. + if (name === 'flow' + && this.scrolled + && String(value) !== 'scrolled' + && this.#views.size > 0) { + this.#flushScrolledState() + } + super.setAttribute(name, value) + } + #flushScrolledState() { + if (this.#views.size > 1) this.#detectPrimaryView() + const result = this.#getVisibleRange() + if (result?.range && !result.range.collapsed) this.#anchor = result.range + } + attributeChangedCallback(name, _, value) { + switch (name) { + case 'flow': + this.render() + break + case 'gap': + case 'margin-top': + case 'margin-bottom': + case 'margin-left': + case 'margin-right': + case 'max-block-size': + case 'max-column-count': + this.#top.style.setProperty('--_' + name, value) + this.render() + break + case 'max-inline-size': + // needs explicit `render()` as it doesn't necessarily resize + this.#top.style.setProperty('--_' + name, value) + this.render() + break + case 'no-continuous-scroll': + if (this.noContinuousScroll) { + for (const [i] of this.#views) { + if (i !== this.#primaryIndex) this.#destroyView(i) + } + } + break + } + } + open(book) { + this.bookDir = book.dir + this.sections = book.sections + book.transformTarget?.addEventListener('data', ({ detail }) => { + if (detail.type !== 'text/css') return + detail.data = Promise.resolve(detail.data).then(data => data + // unprefix as most of the props are (only) supported unprefixed + .replace(/([{\s;])-epub-/gi, '$1') + // `page-break-*` unsupported in columns; replace with `column-break-*` + .replace(/page-break-(after|before|inside)\s*:/gi, (_, x) => + `-webkit-column-break-${x}:`) + .replace(/break-(after|before|inside)\s*:\s*(avoid-)?page/gi, (_, x, y) => + `break-${x}: ${y ?? ''}column`)) + }) + } + #createView(index) { + // Destroy existing view for this index if any + const existing = this.#views.get(index) + if (existing) { + existing.destroy() + this.#container.removeChild(existing.element) + this.#views.delete(index) + } + const view = new View({ + container: this, + onExpand: () => { + // Only the primary view's resize should adjust scroll; + // non-primary views (preloaded/adjacent) must not scroll + if (this.#filling || this.#stabilizing || this.scrolled) return + if (this.#primaryIndex === index) + this.#scrollToAnchor(this.#anchor) + }, + }) + this.#views.set(index, view) + const sorted = this.#sortedViews + const myPos = sorted.findIndex(([i]) => i === index) + const nextEntry = sorted[myPos + 1] + if (nextEntry) this.#container.insertBefore(view.element, nextEntry[1].element) + else this.#container.append(view.element) + this.#syncA11y() + return view + } + // Hide non-primary views from accessibility tree so screen-reader + // swipe-next does not wander into pre-loaded adjacent sections + // (which would land several pages into the next section instead + // of its first paragraph). Uses `inert` (blocks focus/keyboard + // traversal) and `aria-hidden` (removes from accessibility tree). + #syncA11y() { + for (const [index, view] of this.#views) { + if (index === this.#primaryIndex) { + view.element.removeAttribute('inert') + view.element.removeAttribute('aria-hidden') + } else { + view.element.setAttribute('inert', '') + view.element.setAttribute('aria-hidden', 'true') + } + } + } + #destroyView(index) { + const view = this.#views.get(index) + if (!view) return + view.destroy() + this.#container.removeChild(view.element) + this.#views.delete(index) + this.sections[index]?.unload?.() + } + #destroyAllViews() { + for (const [index] of this.#views) this.#destroyView(index) + } + #clearViewsExcept(keepIndices) { + for (const [index] of this.#views) { + if (!keepIndices.has(index)) this.#destroyView(index) + } + } + // Check if a section has the same writing direction as current primary. + // Returns true if same or unknown (not yet cached). + #isSameDirection(index) { + if (!this.#directionCache.has(index)) return true + return this.#directionCache.get(index) === this.#vertical + } + // Update the #background grid so each column shows the correct section's + // background. Pass atPosition to pre-compute for a destination scroll + // position (e.g. before an animation starts). + #replaceBackground(atPosition) { + const doc = this.#primaryView?.document + if (!doc?.documentElement) return + if (this.noBackground) return + const htmlStyle = doc.defaultView.getComputedStyle(doc.documentElement) + const themeBgColor = htmlStyle.getPropertyValue('--theme-bg-color') + const overrideColor = htmlStyle.getPropertyValue('--override-color') === 'true' + const bgTextureId = htmlStyle.getPropertyValue('--bg-texture-id') + const isDarkMode = htmlStyle.getPropertyValue('color-scheme') === 'dark' + const fallbackBg = themeBgColor || '' + + const resolveBackground = (background) => { + if (!background) return fallbackBg + if (themeBgColor) { + const parsed = background.split(/\s(?=(?:url|rgb|hsl|#[0-9a-fA-F]{3,6}))/) + if ((isDarkMode || overrideColor) && (bgTextureId === 'none' || !bgTextureId)) { + parsed[0] = themeBgColor + } + return parsed.join(' ') + } + return background + } + + // Reset any inline backgrounds left over from a previous mode so the + // host's texture isn't occluded after toggling. + this.#background.style.background = '' + for (const [, view] of this.#sortedViews) { + view.element.style.background = '' + } + + if (this.scrolled) { + // In scrolled mode, set background directly on each view element + // so it scrolls with the content. The static #background provides + // the fallback color for margins and gaps between views. + const hasTexture = !!bgTextureId && bgTextureId !== 'none' + this.#background.innerHTML = '' + this.#background.style.display = '' + this.#background.style.background = hasTexture ? '' : fallbackBg + for (const [, view] of this.#sortedViews) { + const resolved = resolveBackground(view.docBackground) + const isTransparent = !resolved + || /^\s*(transparent|rgba\(0,\s*0,\s*0,\s*0\))/.test(resolved) + view.element.style.background = hasTexture && isTransparent ? '' : resolved + } + return + } + + const cc = this.columnCount + const columnSize = this.size / cc + const sorted = this.#sortedViews + + this.#background.innerHTML = '' + this.#background.style.display = 'grid' + this.#background.style.gridTemplateColumns = `repeat(${cc}, 1fr)` + + const scrollPos = atPosition ?? this.#renderedStart + for (let i = 0; i < cc; i++) { + const columnMid = Math.abs(scrollPos) + (i + 0.5) * columnSize + let bg = fallbackBg + + // Find which view's content area contains this column + let offset = 0 + for (const [, view] of sorted) { + const viewSize = view.element.getBoundingClientRect()[this.sideProp] + if (columnMid < offset + viewSize) { + const contentStart = offset + const contentEnd = contentStart + view.contentPages * columnSize + if (columnMid >= contentStart && columnMid < contentEnd) { + bg = resolveBackground(view.docBackground) + } + break + } + offset += viewSize + } + + const col = document.createElement('div') + col.style.background = bg + col.style.backgroundAttachment = 'initial' + col.style.width = '100%' + col.style.height = '100%' + this.#background.appendChild(col) + } + } + #beforeRender({ vertical, rtl }) { + // If writing-mode is about to change, destroy all non-primary + // views BEFORE updating global state. This prevents stale views + // with the wrong direction from remaining in the container while + // flex-direction / scrollProp / sideProp flip. + if (this.#rendered && vertical !== this.#vertical) { + for (const [i] of this.#views) { + if (i !== this.#primaryIndex) this.#destroyView(i) + } + } + this.#vertical = vertical + this.#rtl = rtl + this.#top.classList.toggle('vertical', vertical) + this.#container.classList.toggle('vertical', vertical) + + const style = getComputedStyle(this.#top) + const maxInlineSize = parseFloat(style.getPropertyValue('--_max-inline-size')) + const maxColumnCount = parseInt(style.getPropertyValue('--_max-column-count-spread')) + const marginTop = parseFloat(style.getPropertyValue('--_margin-top')) + const marginRight = parseFloat(style.getPropertyValue('--_margin-right')) + const marginBottom = parseFloat(style.getPropertyValue('--_margin-bottom')) + const marginLeft = parseFloat(style.getPropertyValue('--_margin-left')) + this.#marginTop = marginTop + this.#marginBottom = marginBottom + + // Compute the column count from the host (Paginator) size rather than + // the #container size. The container width depends on --_column-count + // via the grid template (the outer 1fr tracks have a non-zero min for + // multi-column spreads), so deriving the column count from container + // size at threshold widths creates a feedback loop where the layout + // oscillates between 1 and 2 columns on resize. + const flow = this.getAttribute('flow') + const hostRect = this.getBoundingClientRect() + const hostSize = vertical ? hostRect.height : hostRect.width + const divisor = flow === 'scrolled' + ? 1 + : Math.min( + maxColumnCount + (vertical ? 1 : 0), + Math.ceil(Math.floor(hostSize) / Math.floor(maxInlineSize)), + ) + // Set --_column-count BEFORE measuring the container so the read + // below reflects the grid template that will actually be used. + this.#top.style.setProperty('--_column-count', divisor) + + const { width, height } = this.#container.getBoundingClientRect() + const size = vertical ? height : width + + const g = parseFloat(style.getPropertyValue('--_gap')) / 100 + // The gap will be a percentage of the #container, not the whole view. + // This means the outer padding will be bigger than the column gap. Let + // `a` be the gap percentage. The actual percentage for the column gap + // will be (1 - a) * a. Let us call this `b`. + // + // To make them the same, we start by shrinking the outer padding + // setting to `b`, but keep the column gap setting the same at `a`. Then + // the actual size for the column gap will be (1 - b) * a. Repeating the + // process again and again, we get the sequence + // x₁ = (1 - b) * a + // x₂ = (1 - x₁) * a + // ... + // which converges to x = (1 - x) * a. Solving for x, x = a / (1 + a). + // So to make the spacing even, we must shrink the outer padding with + // f(x) = x / (1 + x). + // But we want to keep the outer padding, and make the inner gap bigger. + // So we apply the inverse, f⁻¹ = -x / (x - 1) to the column gap. + const gap = -g / (g - 1) * size + + if (flow === 'scrolled') { + // FIXME: vertical-rl only, not -lr + this.setAttribute('dir', vertical ? 'rtl' : 'ltr') + this.#top.style.padding = '0' + const columnWidth = maxInlineSize + + this.heads = null + this.feet = null + this.#header.replaceChildren() + this.#footer.replaceChildren() + + this.columnCount = 1 + this.#replaceBackground() + + const layout = { width, height, flow, marginTop, marginRight, marginBottom, marginLeft, gap, columnWidth, columnCount: 1 } + this.#lastLayout = layout + return layout + } + + const columnWidth = vertical + ? (size / divisor - marginTop * 1.5 - marginBottom * 1.5) + : (size / divisor - gap - marginRight / 2 - marginLeft / 2) + this.setAttribute('dir', rtl ? 'rtl' : 'ltr') + + // set background to `doc` background + // this is needed because the iframe does not fill the whole element + this.columnCount = divisor + this.#replaceBackground() + + const marginalDivisor = vertical + ? Math.min(2, Math.ceil(Math.floor(width) / Math.floor(maxInlineSize))) + : divisor + const marginalStyle = { + gridTemplateColumns: `repeat(${marginalDivisor}, 1fr)`, + gap: `${gap}px`, + direction: this.bookDir === 'rtl' ? 'rtl' : 'ltr', + } + Object.assign(this.#header.style, marginalStyle) + Object.assign(this.#footer.style, marginalStyle) + const heads = makeMarginals(marginalDivisor, 'head') + const feet = makeMarginals(marginalDivisor, 'foot') + this.heads = heads.map(el => el.children[0]) + this.feet = feet.map(el => el.children[0]) + this.#header.replaceChildren(...heads) + this.#footer.replaceChildren(...feet) + + const layout = { width, height, marginTop, marginRight, marginBottom, marginLeft, gap, columnWidth, columnCount: divisor } + this.#lastLayout = layout + return layout + } + render() { + if (this.#views.size === 0) return + const primaryView = this.#primaryView + if (!primaryView) return + this.#stabilizing = true + const layout = this.#beforeRender({ + vertical: this.#vertical, + rtl: this.#rtl, + }) + for (const [, view] of this.#views) { + if (view.document) view.render(layout) + } + // Scroll synchronously to prevent visible layout shift during resize. + // RAF deferral is only needed for initial display and mode switches + // (handled by #display), not for resize re-renders. + this.#scrollToAnchor(this.#anchor) + this.#stabilizing = false + this.dispatchEvent(new Event('stabilized')) + } + get scrolled() { + return this.getAttribute('flow') === 'scrolled' + } + get noPreload() { + return this.hasAttribute('no-preload') + } + get noBackground() { + return this.hasAttribute('no-background') + } + get noContinuousScroll() { + return this.scrolled && this.hasAttribute('no-continuous-scroll') + } + get scrollProp() { + const { scrolled } = this + return this.#vertical ? (scrolled ? 'scrollLeft' : 'scrollTop') + : scrolled ? 'scrollTop' : 'scrollLeft' + } + get sideProp() { + const { scrolled } = this + return this.#vertical ? (scrolled ? 'width' : 'height') + : scrolled ? 'height' : 'width' + } + get size() { + return this.#container.getBoundingClientRect()[this.sideProp] + } + get viewSize() { + const primaryView = this.#primaryView + if (!primaryView) return 0 + return primaryView.element.getBoundingClientRect()[this.sideProp] + } + get start() { + return this.#renderedStart - this.#getViewOffset(this.#primaryIndex) + } + get end() { + return this.#renderedEnd - this.#getViewOffset(this.#primaryIndex) + } + get page() { + return Math.floor(((this.start + this.end) / 2) / this.size) + } + get pages() { + const primaryView = this.#primaryView + if (!primaryView) return 0 + const viewSize = primaryView.element.getBoundingClientRect()[this.sideProp] + return Math.round(viewSize / this.size) + } + get containerPosition() { + return this.#container[this.scrollProp] + } + get isOverflowX() { + return false + } + get isOverflowY() { + return false + } + get #renderedViewSize() { + if (this.#views.size === 0) return 0 + let total = 0 + for (const [, view] of this.#views) + total += view.element.getBoundingClientRect()[this.sideProp] + return total + } + get #renderedStart() { + return Math.abs(this.#container[this.scrollProp]) + } + get #renderedEnd() { + return this.#renderedStart + this.size + } + get #renderedPage() { + return Math.floor(((this.#renderedStart + this.#renderedEnd) / 2) / this.size) + } + get #renderedPages() { + return Math.round(this.#renderedViewSize / this.size) + } + set containerPosition(newVal) { + this.#container[this.scrollProp] = newVal + } + set scrollLocked(value) { + this.#scrollLocked = value + } + + scrollBy(dx, dy) { + const delta = this.#vertical ? dy : dx + const [offset, a, b] = this.#scrollBounds + const rtl = this.#rtl + const min = rtl ? offset - b : offset - a + const max = rtl ? offset + a : offset + b + this.containerPosition = Math.max(min, Math.min(max, + this.containerPosition + delta)) + } + + // vx, vy: velocity at the end of the swipe (pixels per ms) + // dx, dy: total distance swiped + // dt: total time of the swipe (ms) + snap(vx, vy, dx, dy, dt) { + const velocity = this.#vertical ? vy : vx + const avgVelocity = this.#vertical ? dy / dt : dx / dt + const horizontal = Math.abs(vx) * 2 > Math.abs(vy) + const orthogonal = this.#vertical ? !horizontal : horizontal + const [offset, a, b] = this.#scrollBounds + const size = this.size + const start = this.#renderedStart + const end = this.#renderedEnd + const pages = this.#renderedPages + const min = Math.abs(offset) - a + const max = Math.abs(offset) + b + const snapping = this.hasAttribute('animated') && !this.hasAttribute('eink') + const v = snapping ? velocity : avgVelocity + const d = v * (this.#rtl ? -size : size) * (orthogonal ? 1 : 0) + const snapOffset = (isNaN(d) ? 0 : snapping ? d * 2 : d * 10) + const page = Math.floor(Math.max(min, Math.min(max, (start + end) / 2 + snapOffset)) / size) + const dir = page < 0 ? -1 : page >= pages ? 1 : null + const doGoTo = () => { + if (!dir) return + const sorted = this.#sortedViews + const edgeIndex = dir < 0 + ? sorted[0]?.[0] ?? this.#primaryIndex + : sorted[sorted.length - 1]?.[0] ?? this.#primaryIndex + return this.#goTo({ + index: this.#adjacentIndex(dir, edgeIndex), + anchor: dir < 0 ? () => 1 : () => 0, + }) + } + // Out of range — skip animation, go straight to adjacent section + if (dir) return doGoTo() + this.#scrollToPage(page, 'snap') + } + #onTouchStart(e) { + const touch = e.changedTouches[0] + this.#touchState = { + x: touch?.screenX, y: touch?.screenY, + t: e.timeStamp, + vx: 0, xy: 0, + dx: 0, dy: 0, + dt: 0, + } + // Hint to browser that scrolling will occur for better GPU layer management + const pv = this.#primaryView + if (pv?.element) { + pv.element.style.willChange = 'transform' + } + } + #onTouchMove(e) { + const state = this.#touchState + if (state.pinched) return + state.pinched = globalThis.visualViewport.scale > 1 + if (this.scrolled || state.pinched) return + if (e.touches.length > 1) { + if (this.#touchScrolled) e.preventDefault() + return + } + const doc = this.#primaryView?.document + const selection = doc?.getSelection() + if (selection && selection.rangeCount > 0 && !selection.isCollapsed) { + return + } + const touch = e.changedTouches[0] + const isStylus = touch.touchType === 'stylus' + if (!isStylus) e.preventDefault() + if (this.#scrollLocked) return + const x = touch.screenX, y = touch.screenY + const dx = state.x - x, dy = state.y - y + const dt = e.timeStamp - state.t + state.x = x + state.y = y + state.t = e.timeStamp + state.vx = dx / dt + state.vy = dy / dt + state.dx += dx + state.dy += dy + state.dt += dt + this.#touchScrolled = true + if (!this.hasAttribute('animated') || this.hasAttribute('eink')) return + if (!this.#vertical && Math.abs(state.dx) >= Math.abs(state.dy) && !this.hasAttribute('eink') && (!isStylus || Math.abs(dx) > 1)) { + this.scrollBy(dx, 0) + } else if (this.#vertical && Math.abs(state.dx) < Math.abs(state.dy) && !this.hasAttribute('eink') && (!isStylus || Math.abs(dy) > 1)) { + this.scrollBy(0, dy) + } + } + #onTouchEnd() { + // Remove will-change hint to free GPU resources + // if (this.#view?.element) { + // this.#view.element.style.willChange = 'auto' + // } + + if (!this.#touchScrolled) return + this.#touchScrolled = false + if (this.scrolled) return + + // XXX: Firefox seems to report scale as 1... sometimes...? + // at this point I'm basically throwing `requestAnimationFrame` at + // anything that doesn't work + requestAnimationFrame(() => { + if (globalThis.visualViewport.scale === 1) { + const { vx, vy, dx, dy, dt } = this.#touchState + this.snap(vx, vy, dx, dy, dt) + } + }) + } + // allows one to process rects as if they were LTR and horizontal + #getRectMapper(view) { + if (this.scrolled) { + const size = view ? view.element.getBoundingClientRect()[this.sideProp] : this.#renderedViewSize + const marginTop = this.#marginTop + const marginBottom = this.#marginBottom + return this.#vertical + ? ({ left, right }) => + ({ left: size - right - marginTop, right: size - left - marginBottom }) + : ({ top, bottom }) => ({ left: top - marginTop, right: bottom - marginBottom }) + } + const pxSize = this.#renderedPages * this.size + return this.#rtl + ? ({ left, right }) => + ({ left: pxSize - right, right: pxSize - left }) + : this.#vertical + ? ({ top, bottom }) => ({ left: top, right: bottom }) + : f => f + } + async #scrollToRect(rect, reason) { + if (this.scrolled) { + // rect is in iframe-local coordinates; add view offset + // to convert to container scroll coordinates + const localOffset = this.#getRectMapper()(rect).left - 3 + const viewOffset = this.#getViewOffset(this.#primaryIndex) + return this.#scrollTo(viewOffset + localOffset, reason) + } + // rect is in iframe-local coordinates. Convert to container + // coordinates by adding the primary view's offset. + const localOffset = this.#getRectMapper()(rect).left + const viewOffset = this.#getViewOffset(this.#primaryIndex) + const containerOffset = viewOffset + localOffset + return this.#scrollToPage(Math.floor(containerOffset / this.size + 0.01), reason) + } + async #scrollTo(offset, reason, smooth) { + const { size } = this + if (this.containerPosition === offset) { + this.#scrollBounds = [offset, this.atStart ? 0 : size, this.atEnd ? 0 : size] + this.#afterScroll(reason) + return + } + // FIXME: vertical-rl only, not -lr + if (this.scrolled && this.#vertical) offset = -offset + if ((reason === 'snap' || smooth) && this.hasAttribute('animated') && !this.hasAttribute('eink')) { + const startPosition = this.containerPosition + // Pre-set background for the destination so it's already + // correct when the slide animation reveals the new page + if (!this.scrolled) this.#replaceBackground(offset) + // Use GPU-accelerated scroll animation for smoother experience on high refresh rate screens + this.#isAnimating = true + return animateScroll( + this.#container, + this.scrollProp, + startPosition, + offset, + 300, + ).then(() => { + this.#isAnimating = false + this.#scrollBounds = [offset, this.atStart ? 0 : size, this.atEnd ? 0 : size] + this.#afterScroll(reason) + }) + } else { + this.containerPosition = offset + this.#scrollBounds = [offset, this.atStart ? 0 : size, this.atEnd ? 0 : size] + this.#afterScroll(reason) + } + } + async #scrollToPage(page, reason, smooth) { + const offset = this.size * (this.#rtl ? -page : page) + return this.#scrollTo(offset, reason, smooth) + } + async scrollToAnchor(anchor, select, smooth) { + return this.#scrollToAnchor(anchor, select ? 'selection' : 'navigation', smooth) + } + async #scrollToAnchor(anchor, reason = 'anchor', smooth = false) { + this.#anchor = anchor + const rects = uncollapse(anchor)?.getClientRects?.() + // if anchor is an element or a range + if (rects) { + // when the start of the range is immediately after a hyphen in the + // previous column, there is an extra zero width rect in that column + const rect = Array.from(rects) + .find(r => r.width > 0 && r.height > 0 && r.x >= 0 && r.y >= 0) || rects[0] + if (!rect) return + await this.#scrollToRect(rect, reason) + // focus the element when navigating with keyboard or screen reader + if (reason === 'navigation') { + let node = anchor.focus ? anchor : undefined + if (!node && anchor.startContainer) { + node = anchor.startContainer + if (node.nodeType === Node.TEXT_NODE) { + node = node.parentElement + } + } + if (node && node.focus) { + node.tabIndex = -1 + node.style.outline = 'none' + node.focus({ preventScroll: true }) + } + } + return + } + // if anchor is a fraction + if (this.scrolled) { + // In scrolled mode with multi-view, offset to the primary view's position + const primaryOffset = this.#getViewOffset(this.#primaryIndex) + const primaryView = this.#primaryView + const primarySize = primaryView + ? primaryView.element.getBoundingClientRect()[this.sideProp] : this.#renderedViewSize + await this.#scrollTo(primaryOffset + anchor * primarySize, reason, smooth) + return + } + // In paginated mode, account for pages before the primary section + const primaryView = this.#primaryView + if (!primaryView) return + const pagesBeforePrimary = this.#getPagesBeforeView(this.#primaryIndex) + const textPages = primaryView.contentPages + if (!textPages) return + // textPages is in column units; convert to spread page for scrolling + const newColumn = Math.round(anchor * (textPages - 1)) + const newSpreadPage = Math.floor(newColumn / this.columnCount) + await this.#scrollToPage(pagesBeforePrimary + newSpreadPage, reason, smooth) + } + // Get the pixel offset of a view within the container + #getViewOffset(index) { + let offset = 0 + for (const [i, view] of this.#sortedViews) { + if (i === index) return offset + offset += view.element.getBoundingClientRect()[this.sideProp] + } + return offset + } + // Get number of full pages (spreads) before a given view. + // Uses floor so the view's first column is always on or after + // the returned page — never rounded past it. The 0.01 tolerance + // absorbs sub-pixel drift on fractional-DPR devices where + // getBoundingClientRect() accumulates ~0.0001px errors. + #getPagesBeforeView(index) { + return Math.floor(this.#getViewOffset(index) / this.size + 0.01) + } + #getVisibleRange() { + const targetView = this.#primaryView + if (!targetView?.document) return + const viewOffset = this.#getViewOffset(this.#primaryIndex) + if (this.scrolled) { + // In scrolled mode, the primary view may be scrolled out of + // the viewport at a section boundary. Try all visible views + // and return the first valid (non-collapsed) range. + for (const [index, v] of this.#sortedViews) { + if (!v.document) continue + const off = this.#getViewOffset(index) + const vSize = v.element.getBoundingClientRect()[this.sideProp] + // Skip views entirely outside the viewport + if (off + vSize <= this.#renderedStart || off >= this.#renderedEnd) continue + const range = getVisibleRange(v.document, + this.#renderedStart - off, this.#renderedEnd - off, + this.#getRectMapper(v)) + if (range && !range.collapsed) return { range, index } + } + return + } + const range = getVisibleRange(targetView.document, + this.#renderedStart - viewOffset, + this.#renderedEnd - viewOffset, + this.#getRectMapper(targetView)) + return range ? { range, index: this.#primaryIndex } : undefined + } + // Determine which view is primary based on scroll position + #detectPrimaryView() { + if (this.#views.size <= 1) return + const visibleStart = this.#renderedStart + let offset = 0 + for (const [index, view] of this.#sortedViews) { + const viewSize = view.element.getBoundingClientRect()[this.sideProp] + if (visibleStart < offset + viewSize - 1) { + if (index !== this.#primaryIndex) { + this.#primaryIndex = index + this.#syncA11y() + this.#trimDistantViews() + this.#replaceBackground() + this.#fillPromise = this.#preloadNext() + } + return + } + offset += viewSize + } + } + // Pre-load adjacent sections from the current primary so the + // next/prev sections are ready when the user paginates. + // Does NOT re-scroll to avoid fighting with the user's current + // scroll position. + async #preloadNext() { + if (this.noPreload || this.noContinuousScroll) return + this.#filling = true + try { + const { size } = this + const minPages = 5 + const maxSections = 8 + // Load forward sections until we have enough pages ahead + let iterations = 0 + while (this.#views.size < maxSections && iterations < maxSections) { + iterations++ + const pagesAhead = size > 0 + ? Math.floor((this.#renderedViewSize - this.#renderedEnd) / size) + : 0 + if (pagesAhead >= minPages) break + const sorted = this.#sortedViews + const lastIndex = sorted[sorted.length - 1]?.[0] + if (lastIndex == null) break + const nextIdx = this.#adjacentIndex(1, lastIndex) + if (nextIdx == null) break + // Stop preloading at writing-mode boundaries + if (!this.#isSameDirection(nextIdx)) break + await this.#loadAdjacentSection(nextIdx) + if (!this.#views.has(nextIdx)) break + } + // Wait a frame so ResizeObserver callbacks fire while + // #filling is still true, preventing onExpand from + // re-scrolling to a stale anchor position. + await new Promise(r => requestAnimationFrame(r)) + } finally { + this.#filling = false + this.dispatchEvent(new Event('stabilized')) + } + } + #afterScroll(reason) { + // In multi-view, detect which section is primary + if (this.#views.size > 1 && reason !== 'anchor' && reason !== 'navigation') { + this.#detectPrimaryView() + } + const { range, index: visibleIndex } = this.#getVisibleRange() || {} + if (!range) return + this.#lastVisibleRange = range + // don't set new anchor if relocation was to scroll to anchor + if (reason !== 'selection' && reason !== 'navigation' && reason !== 'anchor') + this.#anchor = range + else this.#justAnchored = true + + const index = visibleIndex ?? this.#primaryIndex + const primaryView = this.#primaryView + const detail = { reason, range, index } + if (this.scrolled) { + const primaryOffset = this.#getViewOffset(index) + const primarySize = primaryView + ? primaryView.element.getBoundingClientRect()[this.sideProp] : this.#renderedViewSize + detail.fraction = primarySize > 0 + ? Math.max(0, Math.min(1, (this.#renderedStart - primaryOffset) / primarySize)) : 0 + } else if (this.#renderedPages > 0 && primaryView) { + const page = this.#renderedPage + const pagesBeforePrimary = this.#getPagesBeforeView(index) + const textPages = primaryView.contentPages + this.#header.style.visibility = page > 0 ? 'visible' : 'hidden' + // page is in spread units, textPages is in column units + const localPage = page - pagesBeforePrimary + const localColumn = localPage * this.columnCount + detail.fraction = textPages > 0 ? Math.max(0, Math.min(1, localColumn / textPages)) : 0 + detail.size = textPages > 0 ? this.columnCount / textPages : 1 + if (reason === 'container-scroll' && localPage === 0) return + } + // Update per-column backgrounds for the current scroll position + if (!this.scrolled) this.#replaceBackground() + this.dispatchEvent(new CustomEvent('relocate', { detail })) + } + async #display(promise) { + this.#stabilizing = true + this.#container.style.opacity = '0' + const { index, src, data, anchor, onLoad, select } = await promise + this.#primaryIndex = index + this.#syncA11y() + const hasFocus = this.#primaryView?.document?.hasFocus() + if (src) { + const view = this.#createView(index) + const afterLoad = doc => { + if (doc.head) { + const $styleBefore = doc.createElement('style') + doc.head.prepend($styleBefore) + const $style = doc.createElement('style') + doc.head.append($style) + this.sections[index].spineProperties?.forEach( + prop => doc.documentElement.setAttribute('data-' + prop, '')) + this.#styleMap.set(doc, [$styleBefore, $style]) + } + onLoad?.({ doc, index }) + } + const beforeRender = this.#beforeRender.bind(this) + await view.load(src, data, afterLoad, beforeRender) + // Cache direction for future preload boundary checks + if (view.document) { + const dir = getDirection(view.document) + this.#directionCache.set(index, dir.vertical) + } + this.dispatchEvent(new CustomEvent('create-overlayer', { + detail: { + doc: view.document, index, + attach: overlayer => view.overlayer = overlayer, + }, + })) + } + // Pre-load previous section when needed: + // - Short primary alignment (section shorter than one spread) + // - Scrolled mode with anchor in top half — so the user can + // scroll backward into the previous section immediately + const primaryView = this.#primaryView + if (!this.noPreload && !this.noContinuousScroll && primaryView) { + const needsPrev = (primaryView.contentPages > 0 && primaryView.contentPages < this.columnCount) + if (needsPrev || this.scrolled) { + const sorted = this.#sortedViews + const firstIndex = sorted[0]?.[0] + if (firstIndex != null) { + const prevIdx = this.#adjacentIndex(-1, firstIndex) + if (prevIdx != null && this.#isSameDirection(prevIdx)) { + await this.#loadAdjacentSection(prevIdx) + } + } + } + } + const resolvedAnchor = (typeof anchor === 'function' + ? anchor(primaryView.document) : anchor) ?? 0 + await this.scrollToAnchor(resolvedAnchor, select) + if (hasFocus) this.focusView() + // Reveal content now that primary section is positioned + this.#container.style.opacity = '1' + this.#rendered = true + // Emit stabilized so listeners can react, but keep #stabilizing + // true until fill completes to prevent the debounced scroll + // handler from loading backward sections during rapid DOM changes. + this.dispatchEvent(new Event('stabilized')) + // Load remaining adjacent sections progressively (non-blocking). + // In scrolled mode, skip reanchor — browser scroll anchoring + // preserves position when content is added above/below. + this.#fillPromise = this.#fillVisibleArea( + { reanchor: !this.scrolled }) + this.#fillPromise.then(() => { this.#stabilizing = false }) + } + // Load an adjacent section without changing primary index + async #loadAdjacentSection(index) { + if (this.#views.has(index) || !this.#canGoToIndex(index)) return + const section = this.sections[index] + if (!section || section.linear === 'no') return + try { + const src = await section.load() + const data = await section.loadContent?.() + const view = this.#createView(index) + const afterLoad = doc => { + if (doc.head) { + const $styleBefore = doc.createElement('style') + doc.head.prepend($styleBefore) + const $style = doc.createElement('style') + doc.head.append($style) + section.spineProperties?.forEach( + prop => doc.documentElement.setAttribute('data-' + prop, '')) + this.#styleMap.set(doc, [$styleBefore, $style]) + } + this.setStyles(this.#styles) + this.dispatchEvent(new CustomEvent('load', { detail: { doc, index } })) + } + // Adjacent sections reuse the primary view's cached layout + // — they must NOT call #beforeRender, which would modify + // global state (direction, CSS classes, dir attribute, etc.). + const cachedLayout = this.#lastLayout + const beforeRender = () => cachedLayout + await view.load(src, data, afterLoad, beforeRender) + // Cache direction for future preload boundary checks + if (view.document) { + const dir = getDirection(view.document) + this.#directionCache.set(index, dir.vertical) + // Destroy views with a different writing-mode immediately. + // Mixed-direction views corrupt scroll/page calculations. + if (dir.vertical !== this.#vertical) { + this.#destroyView(index) + return + } + } + this.dispatchEvent(new CustomEvent('create-overlayer', { + detail: { + doc: view.document, index, + attach: overlayer => view.overlayer = overlayer, + }, + })) + } catch (e) { + console.warn(e) + console.warn(new Error(`Failed to load adjacent section ${index}`)) + } + } + // Fill adjacent sections until at least `minPages` pages exist + // beyond the current viewport in each direction (forward always, + // backward only when the primary section is short). + // When reanchor is false (background pre-loading), skip re-scrolling + // to avoid fighting with the user's current scroll position. + async #fillVisibleArea({ reanchor = true } = {}) { + if (this.noPreload || this.noContinuousScroll || this.#filling) return + this.#filling = true + try { + const { size } = this + if (!size) return + const minPages = 5 + const maxSections = 8 + + // If the primary section is shorter than one spread and + // there's no section already loaded before it, load the + // previous section to fill the leading columns + const primaryView = this.#primaryView + if (primaryView && primaryView.contentPages > 0 + && primaryView.contentPages < this.columnCount) { + const sorted = this.#sortedViews + const firstIndex = sorted[0]?.[0] + if (firstIndex != null && firstIndex >= this.#primaryIndex) { + const prevIdx = this.#adjacentIndex(-1, firstIndex) + if (prevIdx != null && this.#isSameDirection(prevIdx)) { + await this.#loadAdjacentSection(prevIdx) + } + } + } + + // Load forward sections until we have enough pages ahead + let iterations = 0 + while (this.#views.size < maxSections && iterations < maxSections) { + iterations++ + const pagesAhead = Math.floor( + (this.#renderedViewSize - this.#renderedEnd) / size) + if (pagesAhead >= minPages) break + const sorted = this.#sortedViews + const lastIndex = sorted[sorted.length - 1]?.[0] + if (lastIndex == null) break + const nextIdx = this.#adjacentIndex(1, lastIndex) + if (nextIdx == null) break + // Stop at writing-mode boundaries + if (!this.#isSameDirection(nextIdx)) break + await this.#loadAdjacentSection(nextIdx) + if (!this.#views.has(nextIdx)) break + } + if (reanchor) this.#scrollToAnchor(this.#anchor) + } finally { + this.#filling = false + // Emit stabilized so post-layout processing (e.g. warichu) + // runs for newly loaded adjacent sections. + this.dispatchEvent(new Event('stabilized')) + } + } + // Trim views whose content is entirely more than 10 pages away + // from the current viewport. Only removes views AFTER the primary + // — removing views before would shift scroll position. + #trimDistantViews() { + const { size } = this + if (!size) return + const maxDistance = size * 10 + const viewportEnd = this.#renderedEnd + for (const [index, view] of this.#sortedViews) { + if (index <= this.#primaryIndex) continue + const offset = this.#getViewOffset(index) + if (offset - viewportEnd > maxDistance) { + this.#destroyView(index) + } + } + } + #canGoToIndex(index) { + return index >= 0 && index <= this.sections.length - 1 + } + async #goTo({ index, anchor, select }) { + // Check if the target section has a different writing-mode. + // If direction changes, we must destroy all views and do a full + // rebuild via #display — mixed-direction views cannot coexist. + let directionChanged = false + if (this.#views.has(index)) { + const view = this.#views.get(index) + if (view?.document) { + const { vertical } = getDirection(view.document) + directionChanged = vertical !== this.#vertical + } + } else if (this.#directionCache.has(index)) { + directionChanged = this.#directionCache.get(index) !== this.#vertical + } + // When direction is unknown (not cached), #beforeRender will + // detect and clean up stale views if a change actually occurs. + + if (this.#views.has(index) && !directionChanged) { + // View already loaded — reuse it without + // clearing/reloading. Just change primary and scroll. + this.#stabilizing = true + this.#container.style.opacity = '0' + const hasFocus = this.#primaryView?.document?.hasFocus() + this.#primaryIndex = index + this.#syncA11y() + this.#trimDistantViews() + // Handle short section alignment + const primaryView = this.#primaryView + if (!this.noPreload && !this.noContinuousScroll && primaryView + && primaryView.contentPages > 0 + && primaryView.contentPages < this.columnCount) { + const sorted = this.#sortedViews + const firstIndex = sorted[0]?.[0] + if (firstIndex != null) { + const prevIdx = this.#adjacentIndex(-1, firstIndex) + if (prevIdx != null) { + await this.#loadAdjacentSection(prevIdx) + } + } + } + // In noContinuousScroll mode, destroy all non-primary views + if (this.noContinuousScroll) { + for (const [i] of this.#views) { + if (i !== index) this.#destroyView(i) + } + } + await this.scrollToAnchor((typeof anchor === 'function' + ? anchor(primaryView.document) : anchor) ?? 0, select) + this.#container.style.opacity = '1' + if (hasFocus) this.focusView() + // Load remaining adjacent sections progressively; + // keep #stabilizing true until fill completes + this.#fillPromise = this.#fillVisibleArea() + this.#fillPromise.then(() => { this.#stabilizing = false }) + } else { + // When direction changes, clear ALL views — no reuse possible + // across writing-mode boundaries. When direction is unknown + // (not yet cached), keep nearby views; #beforeRender will + // clean up if the loaded section turns out to differ. + if (directionChanged) { + this.#destroyAllViews() + } else { + const keep = new Set([index]) + if (!this.noContinuousScroll) { + for (const [i] of this.#views) { + if (Math.abs(i - index) <= 2) keep.add(i) + } + } + this.#clearViewsExcept(keep) + } + const oldIndex = this.#primaryIndex + const onLoad = detail => { + if (oldIndex >= 0 && !this.#views.has(oldIndex)) + this.sections[oldIndex]?.unload?.() + this.setStyles(this.#styles) + this.dispatchEvent(new CustomEvent('load', { detail })) + } + await this.#display(Promise.resolve(this.sections[index].load()) + .then(async src => { + const data = await this.sections[index].loadContent?.() + return { index, src, data, anchor, onLoad, select } + }).catch(e => { + console.warn(e) + console.warn(new Error(`Failed to load section ${index}`)) + return {} + })) + } + } + async goTo(target) { + if (this.#locked) return + const resolved = await target + if (this.#canGoToIndex(resolved.index)) return this.#goTo(resolved) + } + #scrollPrev(distance) { + if (this.#views.size === 0) return true + if (this.scrolled) { + if (this.#renderedStart > 0) return this.#scrollTo( + Math.max(0, this.#renderedStart - (distance ?? this.size)), null, true) + return !this.atStart + } + if (this.atStart) return + const page = this.#renderedPage - 1 + // Out of range — skip animation, go straight to previous section + if (page < 0) return true + return this.#scrollToPage(page, 'page', true) + } + #scrollNext(distance) { + if (this.#views.size === 0) return true + if (this.scrolled) { + if (this.#renderedViewSize - this.#renderedEnd > 2) return this.#scrollTo( + Math.min(this.#renderedViewSize, distance ? this.#renderedStart + distance : this.#renderedEnd), null, true) + return !this.atEnd + } + if (this.atEnd) return + const page = this.#renderedPage + 1 + const pages = this.#renderedPages + // Out of range — skip animation, go straight to next section + if (page >= pages) return true + return this.#scrollToPage(page, 'page', true) + } + get atStart() { + const sorted = this.#sortedViews + const firstIndex = sorted[0]?.[0] ?? this.#primaryIndex + if (this.scrolled) return this.#adjacentIndex(-1, firstIndex) == null && this.#renderedStart <= 0 + return this.#adjacentIndex(-1, firstIndex) == null && this.#renderedPage <= 0 + } + get atEnd() { + const sorted = this.#sortedViews + const lastIndex = sorted[sorted.length - 1]?.[0] ?? this.#primaryIndex + if (this.scrolled) return this.#adjacentIndex(1, lastIndex) == null && this.#renderedViewSize - this.#renderedEnd <= 2 + return this.#adjacentIndex(1, lastIndex) == null && this.#renderedPage >= this.#renderedPages - 1 + } + #adjacentIndex(dir, fromIndex) { + if (fromIndex === undefined) fromIndex = this.#primaryIndex + for (let index = fromIndex + dir; this.#canGoToIndex(index); index += dir) + if (this.sections[index]?.linear !== 'no') return index + } + async #turnPage(dir, distance) { + if (this.#locked) return + this.#locked = true + const prev = dir === -1 + const shouldGo = await (prev ? this.#scrollPrev(distance) : this.#scrollNext(distance)) + if (shouldGo) { + // Wait for any in-progress background pre-loading to complete — + // it may already be loading the section we need, so awaiting + // it lets #goTo reuse the view instead of loading from scratch + if (this.#fillPromise) await this.#fillPromise + const sorted = this.#sortedViews + const edgeIndex = prev + ? sorted[0]?.[0] ?? this.#primaryIndex + : sorted[sorted.length - 1]?.[0] ?? this.#primaryIndex + await this.#goTo({ + index: this.#adjacentIndex(dir, edgeIndex), + anchor: prev ? () => 1 : () => 0, + }) + } + if (shouldGo || !this.hasAttribute('animated')) await wait(100) + this.#locked = false + } + async prev(distance) { + return await this.#turnPage(-1, distance) + } + async next(distance) { + return await this.#turnPage(1, distance) + } + async pan(dx, dy) { + if (this.#locked) return + this.#locked = true + this.scrollBy(dx, dy) + this.#locked = false + } + prevSection() { + return this.goTo({ index: this.#adjacentIndex(-1) }) + } + nextSection() { + return this.goTo({ index: this.#adjacentIndex(1) }) + } + firstSection() { + const index = this.sections.findIndex(section => section.linear !== 'no') + return this.goTo({ index }) + } + lastSection() { + const index = this.sections.findLastIndex(section => section.linear !== 'no') + return this.goTo({ index }) + } + getContents() { + const contents = [] + for (const [index, view] of this.#sortedViews) { + if (view.document) contents.push({ + index, + overlayer: view.overlayer, + doc: view.document, + }) + } + return contents + } + setStyles(styles) { + this.#styles = styles + for (const [, view] of this.#views) { + const $$styles = this.#styleMap.get(view.document) + if (!$$styles) continue + const [$beforeStyle, $style] = $$styles + if (Array.isArray(styles)) { + const [beforeStyle, style] = styles + $beforeStyle.textContent = beforeStyle + $style.textContent = style + } else $style.textContent = styles + + // needed because the resize observer doesn't work in Firefox + view.document?.fonts?.ready?.then(() => view.expand()) + } + + // NOTE: needs `requestAnimationFrame` in Chromium + const primaryView = this.#primaryView + if (primaryView) { + requestAnimationFrame(() => this.#replaceBackground()) + } + } + focusView() { + this.#primaryView?.document?.defaultView?.focus() + } + showLoupe(winX, winY, { isVertical, color, gap, margin, radius, magnification }) { + this.#primaryView?.showLoupe(winX, winY, { isVertical, color, gap, margin, radius, magnification }) + } + hideLoupe() { + this.#primaryView?.hideLoupe() + } + destroyLoupe() { + this.#primaryView?.destroyLoupe() + } + destroy() { + this.#observer.unobserve(this) + this.#destroyAllViews() + this.#mediaQuery.removeEventListener('change', this.#mediaQueryListener) + } +} + +customElements.define('foliate-paginator', Paginator) diff --git a/web/vendor/foliate-js/pdf.js b/web/vendor/foliate-js/pdf.js new file mode 100644 index 00000000..c4d0df9c --- /dev/null +++ b/web/vendor/foliate-js/pdf.js @@ -0,0 +1,513 @@ +const pdfjsPath = path => `/vendor/pdfjs/${path}` + +import '@pdfjs/pdf.min.mjs' +const pdfjsLib = globalThis.pdfjsLib +pdfjsLib.GlobalWorkerOptions.workerSrc = pdfjsPath('pdf.worker.min.mjs') + +const fetchText = async url => await (await fetch(url)).text() + +let textLayerBuilderCSS = null +let annotationLayerBuilderCSS = null + +// Track active render tasks per iframe document to cancel superseded renders +const activeRenderTasks = new WeakMap() +// Generation counter per document to detect stale renders after async gaps +const renderGenerations = new WeakMap() + +// Set up panning and selection event handlers once per iframe document +const setupPanningEvents = (doc) => { + if (doc._readestEventsInitialized) return + doc._readestEventsInitialized = true + + const container = doc.querySelector('.textLayer') + if (!container) return + + let isPanning = false + let startX = 0 + let startY = 0 + let scrollLeft = 0 + let scrollTop = 0 + let scrollParent = null + + const findScrollableParent = (element) => { + let current = element + while (current) { + if (current !== document.body && current.nodeType === 1) { + const style = window.getComputedStyle(current) + const overflow = style.overflow + style.overflowY + style.overflowX + if (/(auto|scroll)/.test(overflow)) { + if (current.scrollHeight > current.clientHeight || + current.scrollWidth > current.clientWidth) { + return current + } + } + } + if (current.parentElement) { + current = current.parentElement + } else if (current.parentNode && current.parentNode.host) { + current = current.parentNode.host + } else { + break + } + } + return window + } + + container.onpointerdown = (e) => { + const selection = doc.getSelection() + const hasTextSelection = selection && selection.toString().length > 0 + + const elementUnderCursor = doc.elementFromPoint(e.clientX, e.clientY) + const hasTextUnderneath = elementUnderCursor && + (elementUnderCursor.tagName === 'SPAN' || elementUnderCursor.tagName === 'P') && + elementUnderCursor.textContent.trim().length > 0 + + if (!hasTextUnderneath && !hasTextSelection) { + isPanning = true + startX = e.screenX + startY = e.screenY + + const iframe = doc.defaultView?.frameElement + if (iframe) { + scrollParent = findScrollableParent(iframe) + if (scrollParent === window) { + scrollLeft = window.scrollX || window.pageXOffset + scrollTop = window.scrollY || window.pageYOffset + } else { + scrollLeft = scrollParent.scrollLeft + scrollTop = scrollParent.scrollTop + } + container.style.cursor = 'grabbing' + } + } else { + container.classList.add('selecting') + } + } + + container.onpointermove = (e) => { + if (isPanning && scrollParent) { + e.preventDefault() + + const dx = e.screenX - startX + const dy = e.screenY - startY + + if (scrollParent === window) { + window.scrollTo(scrollLeft - dx, scrollTop - dy) + } else { + scrollParent.scrollLeft = scrollLeft - dx + scrollParent.scrollTop = scrollTop - dy + } + } + } + + container.onpointerup = () => { + if (isPanning) { + isPanning = false + scrollParent = null + container.style.cursor = 'grab' + } else { + container.classList.remove('selecting') + } + } + + container.onpointerleave = () => { + if (isPanning) { + isPanning = false + scrollParent = null + container.style.cursor = 'grab' + } + } + + doc.addEventListener('selectionchange', () => { + const selection = doc.getSelection() + if (selection && selection.toString().length > 0) { + container.style.cursor = 'text' + } else if (!isPanning) { + container.style.cursor = 'grab' + } + }) + + container.style.cursor = 'grab' +} + +const render = async (page, doc, zoom, pageColors) => { + if (!doc) return + + // Increment generation to invalidate any in-progress render for this doc + const generation = (renderGenerations.get(doc) || 0) + 1 + renderGenerations.set(doc, generation) + + // Cancel any in-progress render task for this document + const existingTask = activeRenderTasks.get(doc) + if (existingTask) { + existingTask.cancel() + activeRenderTasks.delete(doc) + } + + const scale = zoom * devicePixelRatio + doc.documentElement.style.transform = `scale(${1 / devicePixelRatio})` + doc.documentElement.style.transformOrigin = 'top left' + doc.documentElement.style.setProperty('--total-scale-factor', scale) + doc.documentElement.style.setProperty('--user-unit', '1') + doc.documentElement.style.setProperty('--scale-round-x', '1px') + doc.documentElement.style.setProperty('--scale-round-y', '1px') + const viewport = page.getViewport({ scale }) + + // the canvas must be in the `PDFDocument`'s `ownerDocument` + // (`globalThis.document` by default); that's where the fonts are loaded + const canvas = document.createElement('canvas') + canvas.height = viewport.height + canvas.width = viewport.width + const canvasContext = canvas.getContext('2d') + const renderTask = page.render({ canvasContext, viewport, pageColors }) + activeRenderTasks.set(doc, renderTask) + + try { + await renderTask.promise + } catch { + // Render was cancelled or failed — release canvas bitmap memory + canvas.width = 0 + canvas.height = 0 + return + } finally { + if (activeRenderTasks.get(doc) === renderTask) { + activeRenderTasks.delete(doc) + } + } + + // Bail out if a newer render has started or iframe was removed + if (renderGenerations.get(doc) !== generation || !doc.defaultView) { + canvas.width = 0 + canvas.height = 0 + return + } + + const canvasElement = doc.querySelector('#canvas') + if (!canvasElement) { + canvas.width = 0 + canvas.height = 0 + return + } + + // Release old canvas bitmap memory before replacing + const oldCanvas = canvasElement.querySelector('canvas') + if (oldCanvas) { + oldCanvas.width = 0 + oldCanvas.height = 0 + } + canvasElement.replaceChildren(doc.adoptNode(canvas)) + + // Clear text layer before re-rendering to prevent DOM accumulation + const container = doc.querySelector('.textLayer') + container.replaceChildren() + const textLayer = new pdfjsLib.TextLayer({ + textContentSource: await page.streamTextContent(), + container, viewport, + }) + await textLayer.render() + + // Bail out if superseded after async text layer render + if (renderGenerations.get(doc) !== generation) return + + // hide "offscreen" canvases appended to document when rendering text layer + // https://github.com/mozilla/pdf.js/blob/642b9a5ae67ef642b9a8808fd9efd447e8c350e2/web/pdf_viewer.css#L51-L58 + for (const hiddenCanvas of document.querySelectorAll('.hiddenCanvasElement')) + Object.assign(hiddenCanvas.style, { + position: 'absolute', + top: '0', + left: '0', + width: '0', + height: '0', + display: 'none', + }) + + // fix text selection + // https://github.com/mozilla/pdf.js/blob/642b9a5ae67ef642b9a8808fd9efd447e8c350e2/web/text_layer_builder.js#L105-L107 + const endOfContent = document.createElement('div') + endOfContent.className = 'endOfContent' + container.append(endOfContent) + + // Set up panning/selection event handlers once per document + setupPanningEvents(doc) + + // Clear annotation layer before re-rendering to prevent DOM accumulation + const div = doc.querySelector('.annotationLayer') + div.replaceChildren() + const linkService = { + goToDestination: () => {}, + getDestinationHash: dest => JSON.stringify(dest), + addLinkAttributes: (link, url) => link.href = url, + } + await new pdfjsLib.AnnotationLayer({ page, viewport, div, linkService }).render({ + annotations: await page.getAnnotations(), + }) +} + +const renderPage = async (page, getImageBlob) => { + const viewport = page.getViewport({ scale: 1 }) + if (getImageBlob) { + const canvas = document.createElement('canvas') + canvas.height = viewport.height + canvas.width = viewport.width + const canvasContext = canvas.getContext('2d') + await page.render({ canvasContext, viewport }).promise + return new Promise(resolve => canvas.toBlob(blob => { + // Release canvas bitmap memory after extracting the blob + canvas.width = 0 + canvas.height = 0 + resolve(blob) + })) + } + // https://github.com/mozilla/pdf.js/blob/642b9a5ae67ef642b9a8808fd9efd447e8c350e2/web/text_layer_builder.css + if (textLayerBuilderCSS == null) { + textLayerBuilderCSS = await fetchText(pdfjsPath('text_layer_builder.css')) + } + // https://github.com/mozilla/pdf.js/blob/642b9a5ae67ef642b9a8808fd9efd447e8c350e2/web/annotation_layer_builder.css + if (annotationLayerBuilderCSS == null) { + annotationLayerBuilderCSS = await fetchText(pdfjsPath('annotation_layer_builder.css')) + } + const data = ` + + + + + +
+
+
+ ` + const src = URL.createObjectURL(new Blob([data], { type: 'text/html' })) + const onZoom = ({ doc, scale, pageColors }) => render(page, doc, scale, pageColors) + return { src, data, onZoom } +} + +const makeTOCItem = async (item, pdf) => { + let pageIndex = undefined + + if (item.dest) { + try { + const dest = typeof item.dest === 'string' + ? await pdf.getDestination(item.dest) + : item.dest + if (dest?.[0]) { + pageIndex = await pdf.getPageIndex(dest[0]) + } + } catch (e) { + console.warn('Failed to get page index for TOC item:', item.title, e) + } + } + + return { + label: item.title, + href: item.dest ? JSON.stringify(item.dest) : '', + index: pageIndex, + subitems: item.items?.length + ? await Promise.all(item.items.map(i => makeTOCItem(i, pdf))) + : null, + } +} + +const MAX_CACHED_PAGES = 8 + +const CALIBRE_NS = 'http://calibre-ebook.com/xmp-namespace' +const CALIBRE_SI_NS = 'http://calibre-ebook.com/xmp-namespace-series-index' +const RDF_NS = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#' + +// Calibre writes series metadata into the XMP packet as +// +// Name +// 1.00 +// +const parseCalibreSeriesFromXMP = raw => { + if (!raw || typeof raw !== 'string') return null + let doc + try { + doc = new DOMParser().parseFromString(raw, 'application/xml') + } catch { + return null + } + if (!doc || doc.getElementsByTagName('parsererror').length) return null + const seriesEls = doc.getElementsByTagNameNS(CALIBRE_NS, 'series') + const seriesEl = seriesEls.item(0) + if (!seriesEl) return null + const valueEl = seriesEl.getElementsByTagNameNS(RDF_NS, 'value').item(0) + const name = valueEl?.textContent?.trim() + if (!name) return null + const indexEl = seriesEl.getElementsByTagNameNS(CALIBRE_SI_NS, 'series_index').item(0) + const position = indexEl?.textContent?.trim() + return position ? { name, position } : { name } +} + +export const makePDF = async file => { + const transport = new pdfjsLib.PDFDataRangeTransport(file.size, []) + transport.requestDataRange = (begin, end) => { + file.slice(begin, end).arrayBuffer().then(chunk => { + transport.onDataRange(begin, chunk) + }) + } + const pdf = await pdfjsLib.getDocument({ + range: transport, + wasmUrl: pdfjsPath(''), + cMapUrl: pdfjsPath('cmaps/'), + standardFontDataUrl: pdfjsPath('standard_fonts/'), + isEvalSupported: false, + }).promise + + // Get viewport dimensions from first page for fixed-layout rendering + const firstPage = await pdf.getPage(1) + const firstViewport = firstPage.getViewport({ scale: 1 }) + const book = { rendition: { + layout: 'pre-paginated', + viewport: { width: firstViewport.width, height: firstViewport.height }, + } } + + const { metadata, info } = await pdf.getMetadata() ?? {} + // TODO: for better results, parse `metadata.getRaw()` + book.metadata = { + title: metadata?.get('dc:title') ?? info?.Title, + author: metadata?.get('dc:creator') ?? info?.Author, + contributor: metadata?.get('dc:contributor'), + description: metadata?.get('dc:description') ?? info?.Subject, + language: metadata?.get('dc:language'), + publisher: metadata?.get('dc:publisher'), + subject: metadata?.get('dc:subject'), + identifier: metadata?.get('dc:identifier'), + source: metadata?.get('dc:source'), + rights: metadata?.get('dc:rights'), + } + + const calibreSeries = parseCalibreSeriesFromXMP(metadata?.getRaw?.()) + if (calibreSeries) book.metadata.belongsTo = { series: calibreSeries } + + const outline = await pdf.getOutline() + book.toc = outline ? await Promise.all(outline.map(item => makeTOCItem(item, pdf))) : null + + const cache = new Map() + const pageCache = new Map() + const getPage = async (i) => { + const cached = pageCache.get(i) + if (cached) { + // Move to end for LRU ordering + pageCache.delete(i) + pageCache.set(i, cached) + return cached + } + const page = await pdf.getPage(i + 1) + pageCache.set(i, page) + + // Evict oldest pages when over limit, freeing internal page data + while (pageCache.size > MAX_CACHED_PAGES) { + const oldestKey = pageCache.keys().next().value + const oldPage = pageCache.get(oldestKey) + pageCache.delete(oldestKey) + oldPage?.cleanup() + } + + return page + } + book.sections = Array.from({ length: pdf.numPages }).map((_, i) => ({ + id: i, + load: async () => { + const cached = cache.get(i) + if (cached) { + // Move to end for LRU ordering + cache.delete(i) + cache.set(i, cached) + return cached + } + const url = await renderPage(await getPage(i)) + cache.set(i, url) + + // Evict oldest render results when over limit + while (cache.size > MAX_CACHED_PAGES) { + const oldestKey = cache.keys().next().value + const oldEntry = cache.get(oldestKey) + cache.delete(oldestKey) + if (oldEntry?.src) URL.revokeObjectURL(oldEntry.src) + } + + return url + }, + createDocument: async () => { + const page = await getPage(i) + const doc = document.implementation.createHTMLDocument('') + + const canvas = doc.createElement('div') + canvas.id = 'canvas' + doc.body.appendChild(canvas) + + const textLayer = doc.createElement('div') + textLayer.className = 'textLayer' + doc.body.appendChild(textLayer) + + const annotationLayer = doc.createElement('div') + annotationLayer.className = 'annotationLayer' + doc.body.appendChild(annotationLayer) + + // TextLayer requires canvas 2d context for font metrics; + // fall back to manual span construction when unavailable + const probe = doc.createElement('canvas') + if (probe.getContext?.('2d')) { + const textLayerInstance = new pdfjsLib.TextLayer({ + textContentSource: await page.streamTextContent(), + container: textLayer, viewport: page.getViewport({ scale: 1 }), + }) + await textLayerInstance.render() + } else { + const content = await page.getTextContent() + for (const item of content.items) { + if (item.str) { + const span = doc.createElement('span') + span.textContent = item.str + textLayer.appendChild(span) + } + } + } + return doc + }, + size: 1000, + })) + book.isExternal = uri => /^\w+:/i.test(uri) + book.resolveHref = async href => { + const parsed = JSON.parse(href) + const dest = typeof parsed === 'string' + ? await pdf.getDestination(parsed) : parsed + const index = await pdf.getPageIndex(dest[0]) + return { index } + } + book.splitTOCHref = async href => { + if (!href) return [null, null] + const parsed = JSON.parse(href) + const dest = typeof parsed === 'string' + ? await pdf.getDestination(parsed) : parsed + try { + const index = await pdf.getPageIndex(dest[0]) + return [index, null] + } catch (e) { + console.warn('Error getting page index for href', href, e) + return [null, null] + } + } + book.getTOCFragment = doc => doc.documentElement + book.getCover = async () => renderPage(await pdf.getPage(1), true) + book.destroy = () => { + // Clean up all cached canvases and revoke blob URLs + for (const [, entry] of cache) { + if (entry?.src) URL.revokeObjectURL(entry.src) + } + cache.clear() + for (const [, page] of pageCache) { + page?.cleanup() + } + pageCache.clear() + pdf.destroy() + } + return book +} diff --git a/web/vendor/foliate-js/progress.js b/web/vendor/foliate-js/progress.js new file mode 100644 index 00000000..ab0edc19 --- /dev/null +++ b/web/vendor/foliate-js/progress.js @@ -0,0 +1,224 @@ +// assign a unique ID for each TOC item +const assignIDs = toc => { + let id = 0 + const assignID = item => { + item.id = id++ + if (item.subitems) for (const subitem of item.subitems) assignID(subitem) + } + for (const item of toc) assignID(item) + return toc +} + +const flatten = items => items + .map(item => item.subitems?.length + ? [item, flatten(item.subitems)].flat() + : item) + .flat() + +export class TOCProgress { + async init({ toc, ids, splitHref, getFragment }) { + assignIDs(toc) + const items = flatten(toc) + const grouped = new Map() + for (const [i, item] of items.entries()) { + const [id, fragment] = await splitHref(item?.href) ?? [] + const value = { fragment, item } + if (grouped.has(id)) grouped.get(id).items.push(value) + else grouped.set(id, { prev: items[i - 1], items: [value] }) + } + const map = new Map() + for (const [i, id] of ids.entries()) { + if (grouped.has(id)) map.set(id, grouped.get(id)) + else map.set(id, map.get(ids[i - 1])) + } + this.ids = ids + this.map = map + this.getFragment = getFragment + } + getProgress(index, range) { + if (!this.ids) return + const id = this.ids[index] + const obj = this.map.get(id) + if (!obj) return null + const { prev, items } = obj + if (!items) return prev + if (!range || items.length === 1 && !items[0].fragment) return items[0].item + + const doc = range.startContainer.getRootNode() + for (const [i, { fragment }] of items.entries()) { + const el = this.getFragment(doc, fragment) + if (!el) continue + if (range.comparePoint(el, 0) > 0) + return (items[i - 1]?.item ?? prev) + } + return items[items.length - 1].item + } +} + +export class PageProgress { + #book + #cache = new Map() + #resolveNavigation + + constructor(book, resolveNavigation) { + this.#book = book + this.#resolveNavigation = resolveNavigation + } + + async #getCache(index) { + let cached = this.#cache.get(index) + if (cached) return cached + + const section = this.#book.sections[index] + if (!section?.createDocument) return null + + const doc = await section.createDocument() + const root = doc.body ?? doc.documentElement + const walker = doc.createTreeWalker(root, NodeFilter.SHOW_TEXT) + const nodes = [] + const offsets = [] + let total = 0 + for (let node = walker.nextNode(); node; node = walker.nextNode()) { + const len = node.nodeValue?.length ?? 0 + nodes.push(node) + offsets.push(total) + total += len + } + + cached = { doc, nodes, offsets, total } + this.#cache.set(index, cached) + return cached + } + + async getProgress(cfi) { + try { + const nav = this.#resolveNavigation(cfi) + if (!nav) return null + + const { index, anchor } = nav + if (index == null || !anchor) return null + + const cached = await this.#getCache(index) + if (!cached) return null + + const { doc, nodes, offsets, total } = cached + const frag = anchor(doc) + if (!frag) return null + + const isRange = frag instanceof Range + const range = isRange ? frag : doc.createRange() + if (!isRange) range.selectNodeContents(frag) + + const offset = this.#findOffset(range, nodes, offsets, total) + return { + fraction: total > 0 ? offset / total : 0, + index, + } + } catch (e) { + console.error(e) + return null + } + } + + #findOffset(range, nodes, offsets, total) { + if (!nodes.length) return 0 + const container = range.startContainer + // fast path: startContainer is a text node in the index + if (container.nodeType === Node.TEXT_NODE) { + const i = this.#bsearchNode(container, nodes) + if (i >= 0) return offsets[i] + range.startOffset + } + // element container: collapse to start and binary search + const collapsed = range.cloneRange() + collapsed.collapse(true) + const i = this.#bsearchCollapsed(collapsed, nodes) + return i >= 0 ? offsets[i] : total + } + + // binary search for an exact text node by document position + #bsearchNode(target, nodes) { + let low = 0, high = nodes.length - 1 + while (low <= high) { + const mid = (low + high) >> 1 + const node = nodes[mid] + if (node === target) return mid + const pos = node.compareDocumentPosition(target) + if (pos & Node.DOCUMENT_POSITION_FOLLOWING) low = mid + 1 + else high = mid - 1 + } + return -1 + } + + // binary search for the first text node at or after a collapsed range point + // collapsed.comparePoint returns: -1 = node is before, 1 = node is at/after + #bsearchCollapsed(collapsed, nodes) { + let low = 0, high = nodes.length - 1, result = -1 + while (low <= high) { + const mid = (low + high) >> 1 + if (collapsed.comparePoint(nodes[mid], 0) > 0) { + result = mid + high = mid - 1 + } else { + low = mid + 1 + } + } + return result + } +} + +export class SectionProgress { + constructor(sections, sizePerLoc, sizePerTimeUnit) { + this.sizes = sections.map(s => s.linear != 'no' && s.size > 0 ? s.size : 0) + this.sizePerLoc = sizePerLoc + this.sizePerTimeUnit = sizePerTimeUnit + this.sizeTotal = this.sizes.reduce((a, b) => a + b, 0) + this.sectionFractions = this.#getSectionFractions() + } + #getSectionFractions() { + const { sizeTotal } = this + const results = [0] + let sum = 0 + for (const size of this.sizes) results.push((sum += size) / sizeTotal) + return results + } + // get progress given index of and fractions within a section + getProgress(index, fractionInSection, pageFraction = 0) { + const { sizes, sizePerLoc, sizePerTimeUnit, sizeTotal } = this + const sizeInSection = sizes[index] ?? 0 + const sizeBefore = sizes.slice(0, index).reduce((a, b) => a + b, 0) + const size = sizeBefore + fractionInSection * sizeInSection + const nextSize = size + pageFraction * sizeInSection + const remainingTotal = sizeTotal - size + const remainingSection = (1 - fractionInSection) * sizeInSection + return { + fraction: nextSize / sizeTotal, + section: { + current: index, + total: sizes.length, + }, + location: { + current: Math.floor(size / sizePerLoc), + next: Math.floor(nextSize / sizePerLoc), + total: Math.ceil(sizeTotal / sizePerLoc), + }, + time: { + section: remainingSection / sizePerTimeUnit, + total: remainingTotal / sizePerTimeUnit, + }, + } + } + // the inverse of `getProgress` + // get index of and fraction in section based on total fraction + getSection(fraction) { + if (fraction <= 0) return [0, 0] + if (fraction >= 1) return [this.sizes.length - 1, 1] + fraction = fraction + Number.EPSILON + const { sizeTotal } = this + let index = this.sectionFractions.findIndex(x => x > fraction) - 1 + if (index < 0) return [0, 0] + while (!this.sizes[index]) index++ + const fractionInSection = (fraction - this.sectionFractions[index]) + / (this.sizes[index] / sizeTotal) + return [index, fractionInSection] + } +} diff --git a/web/vendor/foliate-js/quote-image.js b/web/vendor/foliate-js/quote-image.js new file mode 100644 index 00000000..1336c59e --- /dev/null +++ b/web/vendor/foliate-js/quote-image.js @@ -0,0 +1,86 @@ +const SVG_NS = 'http://www.w3.org/2000/svg' + +// bisect +const fit = (el, a = 1, b = 50) => { + const c = Math.floor(a + (b - a) / 2) + el.style.fontSize = `${c}px` + if (b - a === 1) return + if (el.scrollHeight > el.clientHeight + || el.scrollWidth > el.clientWidth) fit(el, a, c) + else fit(el, c, b) +} + +const width = 540 +const height = 540 +const pixelRatio = 2 + +const html = ` +
+ +
“
+
+
+
+
+ +
+
+ +
+
+
+
 
+
` + +// TODO: lang, vertical writing +customElements.define('foliate-quoteimage', class extends HTMLElement { + #root = this.attachShadow({ mode: 'closed' }) + constructor() { + super() + this.#root.innerHTML = html + } + async getBlob({ title, author, text }) { + this.#root.querySelector('#title').textContent = title + this.#root.querySelector('#author').textContent = author + this.#root.querySelector('#text').innerText = text + + fit(this.#root.querySelector('main')) + + const img = document.createElement('img') + return new Promise(resolve => { + img.onload = () => { + const canvas = document.createElement('canvas') + canvas.width = pixelRatio * width + canvas.height = pixelRatio * height + const ctx = canvas.getContext('2d') + ctx.drawImage(img, 0, 0, canvas.width, canvas.height) + canvas.toBlob(resolve) + } + const doc = document.implementation.createDocument(SVG_NS, 'svg') + doc.documentElement.setAttribute('viewBox', `0 0 ${width} ${height}`) + const obj = doc.createElementNS(SVG_NS, 'foreignObject') + obj.setAttribute('width', width) + obj.setAttribute('height', height) + obj.append(doc.importNode(this.#root.querySelector('main'), true)) + doc.documentElement.append(obj) + img.src = 'data:image/svg+xml;charset=utf-8,' + + new XMLSerializer().serializeToString(doc) + }) + } +}) diff --git a/web/vendor/foliate-js/reader.js b/web/vendor/foliate-js/reader.js new file mode 100644 index 00000000..f62102c4 --- /dev/null +++ b/web/vendor/foliate-js/reader.js @@ -0,0 +1,239 @@ +import './view.js' +import { createTOCView } from './ui/tree.js' +import { createMenu } from './ui/menu.js' +import { Overlayer } from './overlayer.js' + +const getCSS = ({ spacing, justify, hyphenate }) => ` + @namespace epub "http://www.idpf.org/2007/ops"; + html { + color-scheme: light dark; + } + /* https://github.com/whatwg/html/issues/5426 */ + @media (prefers-color-scheme: dark) { + a:link { + color: lightblue; + } + } + p, li, blockquote, dd { + line-height: ${spacing}; + text-align: ${justify ? 'justify' : 'start'}; + -webkit-hyphens: ${hyphenate ? 'auto' : 'manual'}; + hyphens: ${hyphenate ? 'auto' : 'manual'}; + -webkit-hyphenate-limit-before: 3; + -webkit-hyphenate-limit-after: 2; + -webkit-hyphenate-limit-lines: 2; + hanging-punctuation: allow-end last; + widows: 2; + } + /* prevent the above from overriding the align attribute */ + [align="left"] { text-align: left; } + [align="right"] { text-align: right; } + [align="center"] { text-align: center; } + [align="justify"] { text-align: justify; } + + pre { + white-space: pre-wrap !important; + } + aside[epub|type~="endnote"], + aside[epub|type~="footnote"], + aside[epub|type~="note"], + aside[epub|type~="rearnote"] { + display: none; + } +` + +const $ = document.querySelector.bind(document) + +const locales = 'en' +const percentFormat = new Intl.NumberFormat(locales, { style: 'percent' }) +const listFormat = new Intl.ListFormat(locales, { style: 'short', type: 'conjunction' }) + +const formatLanguageMap = x => { + if (!x) return '' + if (typeof x === 'string') return x + const keys = Object.keys(x) + return x[keys[0]] +} + +const formatOneContributor = contributor => typeof contributor === 'string' + ? contributor : formatLanguageMap(contributor?.name) + +const formatContributor = contributor => Array.isArray(contributor) + ? listFormat.format(contributor.map(formatOneContributor)) + : formatOneContributor(contributor) + +class Reader { + #tocView + style = { + spacing: 1.4, + justify: true, + hyphenate: true, + } + annotations = new Map() + annotationsByValue = new Map() + closeSideBar() { + $('#dimming-overlay').classList.remove('show') + $('#side-bar').classList.remove('show') + } + constructor() { + $('#side-bar-button').addEventListener('click', () => { + $('#dimming-overlay').classList.add('show') + $('#side-bar').classList.add('show') + }) + $('#dimming-overlay').addEventListener('click', () => this.closeSideBar()) + + const menu = createMenu([ + { + name: 'layout', + label: 'Layout', + type: 'radio', + items: [ + ['Paginated', 'paginated'], + ['Scrolled', 'scrolled'], + ], + onclick: value => { + this.view?.renderer.setAttribute('flow', value) + }, + }, + ]) + menu.element.classList.add('menu') + + $('#menu-button').append(menu.element) + $('#menu-button > button').addEventListener('click', () => + menu.element.classList.toggle('show')) + menu.groups.layout.select('paginated') + } + async open(file) { + this.view = document.createElement('foliate-view') + document.body.append(this.view) + await this.view.open(file) + this.view.addEventListener('load', this.#onLoad.bind(this)) + this.view.addEventListener('relocate', this.#onRelocate.bind(this)) + + const { book } = this.view + book.transformTarget?.addEventListener('data', ({ detail }) => { + detail.data = Promise.resolve(detail.data).catch(e => { + console.error(new Error(`Failed to load ${detail.name}`, { cause: e })) + return '' + }) + }) + this.view.renderer.setStyles?.(getCSS(this.style)) + this.view.renderer.next() + + $('#header-bar').style.visibility = 'visible' + $('#nav-bar').style.visibility = 'visible' + $('#left-button').addEventListener('click', () => this.view.goLeft()) + $('#right-button').addEventListener('click', () => this.view.goRight()) + + const slider = $('#progress-slider') + slider.dir = book.dir + slider.addEventListener('input', e => + this.view.goToFraction(parseFloat(e.target.value))) + for (const fraction of this.view.getSectionFractions()) { + const option = document.createElement('option') + option.value = fraction + $('#tick-marks').append(option) + } + + document.addEventListener('keydown', this.#handleKeydown.bind(this)) + + const title = formatLanguageMap(book.metadata?.title) || 'Untitled Book' + document.title = title + $('#side-bar-title').innerText = title + $('#side-bar-author').innerText = formatContributor(book.metadata?.author) + Promise.resolve(book.getCover?.())?.then(blob => + blob ? $('#side-bar-cover').src = URL.createObjectURL(blob) : null) + + const toc = book.toc + if (toc) { + this.#tocView = createTOCView(toc, href => { + this.view.goTo(href).catch(e => console.error(e)) + this.closeSideBar() + }) + $('#toc-view').append(this.#tocView.element) + } + + // load and show highlights embedded in the file by Calibre + const bookmarks = await book.getCalibreBookmarks?.() + if (bookmarks) { + const { fromCalibreHighlight } = await import('./epubcfi.js') + for (const obj of bookmarks) { + if (obj.type === 'highlight') { + const value = fromCalibreHighlight(obj) + const color = obj.style.which + const note = obj.notes + const annotation = { value, color, note } + const list = this.annotations.get(obj.spine_index) + if (list) list.push(annotation) + else this.annotations.set(obj.spine_index, [annotation]) + this.annotationsByValue.set(value, annotation) + } + } + this.view.addEventListener('create-overlay', e => { + const { index } = e.detail + const list = this.annotations.get(index) + if (list) for (const annotation of list) + this.view.addAnnotation(annotation) + }) + this.view.addEventListener('draw-annotation', e => { + const { draw, annotation } = e.detail + const { color } = annotation + draw(Overlayer.highlight, { color }) + }) + this.view.addEventListener('show-annotation', e => { + const annotation = this.annotationsByValue.get(e.detail.value) + if (annotation.note) alert(annotation.note) + }) + } + } + #handleKeydown(event) { + const k = event.key + if (k === 'ArrowLeft' || k === 'h') this.view.goLeft() + else if(k === 'ArrowRight' || k === 'l') this.view.goRight() + } + #onLoad({ detail: { doc } }) { + doc.addEventListener('keydown', this.#handleKeydown.bind(this)) + } + #onRelocate({ detail }) { + const { fraction, location, tocItem, pageItem } = detail + const percent = percentFormat.format(fraction) + const loc = pageItem + ? `Page ${pageItem.label}` + : `Loc ${location.current}` + const slider = $('#progress-slider') + slider.style.visibility = 'visible' + slider.value = fraction + slider.title = `${percent} · ${loc}` + if (tocItem?.href) this.#tocView?.setCurrentHref?.(tocItem.href) + } +} + +const open = async file => { + document.body.removeChild($('#drop-target')) + const reader = new Reader() + globalThis.reader = reader + await reader.open(file) +} + +const dragOverHandler = e => e.preventDefault() +const dropHandler = e => { + e.preventDefault() + const item = Array.from(e.dataTransfer.items) + .find(item => item.kind === 'file') + if (item) { + const entry = item.webkitGetAsEntry() + open(entry.isFile ? item.getAsFile() : entry).catch(e => console.error(e)) + } +} +const dropTarget = $('#drop-target') +dropTarget.addEventListener('drop', dropHandler) +dropTarget.addEventListener('dragover', dragOverHandler) + +$('#file-input').addEventListener('change', e => + open(e.target.files[0]).catch(e => console.error(e))) +$('#file-button').addEventListener('click', () => $('#file-input').click()) + +const params = new URLSearchParams(location.search) +const url = params.get('url') +if (url) open(url).catch(e => console.error(e)) +else dropTarget.style.visibility = 'visible' diff --git a/web/vendor/foliate-js/search.js b/web/vendor/foliate-js/search.js new file mode 100644 index 00000000..a6f176ff --- /dev/null +++ b/web/vendor/foliate-js/search.js @@ -0,0 +1,130 @@ +// length for context in excerpts +const CONTEXT_LENGTH = 50 + +const normalizeWhitespace = str => str.replace(/\s+/g, ' ') + +const makeExcerpt = (strs, { startIndex, startOffset, endIndex, endOffset }) => { + const start = strs[startIndex] + const end = strs[endIndex] + const match = start === end + ? start.slice(startOffset, endOffset) + : start.slice(startOffset) + + strs.slice(start + 1, end).join('') + + end.slice(0, endOffset) + const trimmedStart = normalizeWhitespace(start.slice(0, startOffset)).trimStart() + const trimmedEnd = normalizeWhitespace(end.slice(endOffset)).trimEnd() + const ellipsisPre = trimmedStart.length < CONTEXT_LENGTH ? '' : '…' + const ellipsisPost = trimmedEnd.length < CONTEXT_LENGTH ? '' : '…' + const pre = `${ellipsisPre}${trimmedStart.slice(-CONTEXT_LENGTH)}` + const post = `${trimmedEnd.slice(0, CONTEXT_LENGTH)}${ellipsisPost}` + return { pre, match, post } +} + +const simpleSearch = function* (strs, query, options = {}) { + const { locales = 'en', sensitivity } = options + const matchCase = sensitivity === 'variant' + const haystack = strs.join('') + const lowerHaystack = matchCase ? haystack : haystack.toLocaleLowerCase(locales) + const needle = matchCase ? query : query.toLocaleLowerCase(locales) + const needleLength = needle.length + let index = -1 + let strIndex = -1 + let sum = 0 + do { + index = lowerHaystack.indexOf(needle, index + 1) + if (index > -1) { + while (sum <= index) sum += strs[++strIndex].length + const startIndex = strIndex + const startOffset = index - (sum - strs[strIndex].length) + const end = index + needleLength + while (sum <= end) sum += strs[++strIndex].length + const endIndex = strIndex + const endOffset = end - (sum - strs[strIndex].length) + const range = { startIndex, startOffset, endIndex, endOffset } + yield { range, excerpt: makeExcerpt(strs, range) } + } + } while (index > -1) +} + +const segmenterSearch = function* (strs, query, options = {}) { + const { locales = 'en', granularity = 'word', sensitivity = 'base' } = options + let segmenter, collator + try { + segmenter = new Intl.Segmenter(locales, { usage: 'search', granularity }) + collator = new Intl.Collator(locales, { sensitivity }) + } catch (e) { + console.warn(e) + segmenter = new Intl.Segmenter('en', { usage: 'search', granularity }) + collator = new Intl.Collator('en', { sensitivity }) + } + const queryLength = Array.from(segmenter.segment(query)).length + + const substrArr = [] + let strIndex = 0 + let segments = segmenter.segment(strs[strIndex])[Symbol.iterator]() + main: while (strIndex < strs.length) { + while (substrArr.length < queryLength) { + const { done, value } = segments.next() + if (done) { + // the current string is exhausted + // move on to the next string + strIndex++ + if (strIndex < strs.length) { + segments = segmenter.segment(strs[strIndex])[Symbol.iterator]() + continue + } else break main + } + const { index, segment } = value + // ignore formatting characters + if (!/[^\p{Format}]/u.test(segment)) continue + // normalize whitespace + if (/\s/u.test(segment)) { + if (!/\s/u.test(substrArr[substrArr.length - 1]?.segment)) + substrArr.push({ strIndex, index, segment: ' ' }) + continue + } + value.strIndex = strIndex + substrArr.push(value) + } + const substr = substrArr.map(x => x.segment).join('') + if (collator.compare(query, substr) === 0) { + const endIndex = strIndex + const lastSeg = substrArr[substrArr.length - 1] + const endOffset = lastSeg.index + lastSeg.segment.length + const startIndex = substrArr[0].strIndex + const startOffset = substrArr[0].index + const range = { startIndex, startOffset, endIndex, endOffset } + yield { range, excerpt: makeExcerpt(strs, range) } + } + substrArr.shift() + } +} + +export const search = (strs, query, options) => { + const { granularity = 'grapheme', sensitivity = 'base' } = options + if (!Intl?.Segmenter || granularity === 'grapheme' + && (sensitivity === 'variant' || sensitivity === 'accent')) + return simpleSearch(strs, query, options) + return segmenterSearch(strs, query, options) +} + +export const searchMatcher = (textWalker, opts) => { + const { defaultLocale, matchCase, matchDiacritics, matchWholeWords, acceptNode } = opts + return function* (doc, query) { + const iter = textWalker(doc, function* (strs, makeRange) { + for (const result of search(strs, query, { + locales: doc.body.lang || doc.documentElement.lang || defaultLocale || 'en', + granularity: matchWholeWords ? 'word' : 'grapheme', + sensitivity: matchDiacritics && matchCase ? 'variant' + : matchDiacritics && !matchCase ? 'accent' + : !matchDiacritics && matchCase ? 'case' + : 'base', + })) { + const { startIndex, startOffset, endIndex, endOffset } = result.range + result.range = makeRange(startIndex, startOffset, endIndex, endOffset) + yield result + } + }, acceptNode) + for (const result of iter) yield result + } +} diff --git a/web/vendor/foliate-js/text-walker.js b/web/vendor/foliate-js/text-walker.js new file mode 100644 index 00000000..c1249b79 --- /dev/null +++ b/web/vendor/foliate-js/text-walker.js @@ -0,0 +1,43 @@ +const walkRange = (range, walker) => { + const nodes = [] + for (let node = walker.currentNode; node; node = walker.nextNode()) { + const compare = range.comparePoint(node, 0) + if (compare === 0) nodes.push(node) + else if (compare > 0) break + } + return nodes +} + +const walkDocument = (_, walker) => { + const nodes = [] + for (let node = walker.nextNode(); node; node = walker.nextNode()) + nodes.push(node) + return nodes +} + +const filter = NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT + | NodeFilter.SHOW_CDATA_SECTION + +const acceptNode = node => { + if (node.nodeType === 1) { + const name = node.tagName.toLowerCase() + if (name === 'script' || name === 'style') return NodeFilter.FILTER_REJECT + return NodeFilter.FILTER_SKIP + } + return NodeFilter.FILTER_ACCEPT +} + +export const textWalker = function* (x, func, filterFunc) { + const root = x.commonAncestorContainer ?? x.body ?? x + const walker = document.createTreeWalker(root, filter, { acceptNode: filterFunc || acceptNode }) + const walk = x.commonAncestorContainer ? walkRange : walkDocument + const nodes = walk(x, walker) + const strs = nodes.map(node => node.nodeValue ?? '') + const makeRange = (startIndex, startOffset, endIndex, endOffset) => { + const range = document.createRange() + range.setStart(nodes[startIndex], startOffset) + range.setEnd(nodes[endIndex], endOffset) + return range + } + for (const match of func(strs, makeRange)) yield match +} diff --git a/web/vendor/foliate-js/tts.js b/web/vendor/foliate-js/tts.js new file mode 100644 index 00000000..76e10493 --- /dev/null +++ b/web/vendor/foliate-js/tts.js @@ -0,0 +1,450 @@ +const NS = { + XML: 'http://www.w3.org/XML/1998/namespace', + SSML: 'http://www.w3.org/2001/10/synthesis', +} + +const blockTags = new Set([ + 'article', 'aside', 'audio', 'blockquote', 'caption', + 'details', 'dialog', 'div', 'dl', 'dt', 'dd', + 'figure', 'footer', 'form', 'figcaption', + 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'hgroup', 'hr', 'li', + 'main', 'math', 'nav', 'ol', 'p', 'pre', 'section', 'tr', +]) + +const getLang = el => { + const x = el.lang || el?.getAttributeNS?.(NS.XML, 'lang') + return x ? x : el.parentElement ? getLang(el.parentElement) : null +} + +const getAlphabet = el => { + const x = el?.getAttributeNS?.(NS.XML, 'lang') + return x ? x : el.parentElement ? getAlphabet(el.parentElement) : null +} + +const getSegmenter = (lang, granularity = 'word') => { + const segmenter = new Intl.Segmenter(lang || undefined, { granularity }) + const granularityIsWord = granularity === 'word' + return function* (strs, makeRange) { + const str = strs.join('').replace(/\r\n/g, ' ').replace(/\r/g, ' ').replace(/\n/g, ' ') + let name = 0 + let strIndex = -1 + let sum = 0 + const rawSegments = Array.from(segmenter.segment(str)) + const mergedSegments = [] + for (let i = 0, j = 0; i < rawSegments.length; i++) { + const current = rawSegments[i] + const segment = ' ' + current.segment + const endsWithAbbr = /\s([A-Z]{1,2}[a-z]{0,5}|[a-z]{1,3})\.\s*$/.test(segment) + if (!endsWithAbbr || i >= (rawSegments.length-1)) { + const mergedSegment = { + index: rawSegments[j].index, + segment: '', + isWordLike: (i == j) ? current.isWordLike : true, + } + while (j <= i) { + mergedSegment.segment += rawSegments[j++].segment + } + mergedSegments.push(mergedSegment) + } + } + + for (const { index, segment, isWordLike } of mergedSegments) { + if (granularityIsWord && !isWordLike) continue + while (sum <= index) sum += strs[++strIndex].length + const startIndex = strIndex + const startOffset = index - (sum - strs[strIndex].length) + const end = index + segment.length - 1 + if (end < str.length) while (sum <= end) sum += strs[++strIndex].length + const endIndex = strIndex + const endOffset = end - (sum - strs[strIndex].length) + 1 + yield [(name++).toString(), + makeRange(startIndex, startOffset, endIndex, endOffset)] + } + } +} + +const fragmentToSSML = (fragment, nodeFilter, inherited) => { + const ssml = document.implementation.createDocument(NS.SSML, 'speak') + const { lang } = inherited + if (lang) ssml.documentElement.setAttributeNS(NS.XML, 'lang', lang) + + const convert = (node, parent, inheritedAlphabet) => { + if (!node) return + if (node.nodeType === 3) return ssml.createTextNode(node.textContent) + if (node.nodeType === 4) return ssml.createCDATASection(node.textContent) + if (node.nodeType !== 1 && node.nodeType !== 11) return + if (nodeFilter && nodeFilter(node) === NodeFilter.FILTER_REJECT) return + + let el + const nodeName = node.nodeName.toLowerCase() + if (nodeName === 'foliate-mark') { + el = ssml.createElementNS(NS.SSML, 'mark') + el.setAttribute('name', node.dataset.name) + } + else if (nodeName === 'br') + el = ssml.createElementNS(NS.SSML, 'break') + else if (nodeName === 'em' || nodeName === 'strong') + el = ssml.createElementNS(NS.SSML, 'emphasis') + + const lang = node.lang || node.getAttributeNS?.(NS.XML, 'lang') + if (lang) { + if (!el) el = ssml.createElementNS(NS.SSML, 'lang') + el.setAttributeNS(NS.XML, 'lang', lang) + } + + const alphabet = node.getAttributeNS?.(NS.SSML, 'alphabet') || inheritedAlphabet + if (!el) { + const ph = node.getAttributeNS?.(NS.SSML, 'ph') + if (ph) { + el = ssml.createElementNS(NS.SSML, 'phoneme') + if (alphabet) el.setAttribute('alphabet', alphabet) + el.setAttribute('ph', ph) + } + } + + if (!el) el = parent + + let child = node.firstChild + while (child) { + const childEl = convert(child, el, alphabet) + if (childEl && el !== childEl) el.append(childEl) + child = child.nextSibling + } + return el + } + convert(fragment, ssml.documentElement, inherited.alphabet) + return ssml +} + +const getFragmentWithMarks = (range, textWalker, nodeFilter, granularity) => { + const lang = getLang(range.commonAncestorContainer) + const alphabet = getAlphabet(range.commonAncestorContainer) + + const segmenter = getSegmenter(lang, granularity) + const fragment = range.cloneContents() + + // we need ranges on both the original document (for highlighting) + // and the document fragment (for inserting marks) + // so unfortunately need to do it twice, as you can't copy the ranges + const entries = [...textWalker(range, segmenter, nodeFilter)] + const fragmentEntries = [...textWalker(fragment, segmenter, nodeFilter)] + + for (const [name, range] of fragmentEntries) { + const mark = document.createElement('foliate-mark') + mark.dataset.name = name + range.insertNode(mark) + } + const ssml = fragmentToSSML(fragment, nodeFilter, { lang, alphabet }) + return { entries, ssml } +} + +const rangeIsEmpty = range => !range.toString().trim() + +// For PDF text layers, split content into sentence-level blocks so TTS +// reads one sentence at a time instead of the whole page in one block. +// Text nodes are split at sentence boundaries so that every block range +// aligns with node edges — this prevents the text walker from including +// text outside the sentence in word marks. +function* getPDFSentenceBlocks(doc, textLayer) { + const collectNodes = () => { + const w = doc.createTreeWalker(textLayer, NodeFilter.SHOW_TEXT) + const res = [] + for (let n = w.nextNode(); n; n = w.nextNode()) res.push(n) + return res + } + + let nodes = collectNodes() + if (!nodes.length) return + + const fullText = nodes.map(n => n.nodeValue).join('') + if (!fullText.trim()) return + + // Find sentence boundary positions + const lang = getLang(textLayer) || undefined + const segmenter = new Intl.Segmenter(lang, { granularity: 'sentence' }) + const boundaries = new Set() + for (const { index } of segmenter.segment(fullText)) + if (index > 0) boundaries.add(index) + + // Split text nodes at sentence boundaries so ranges align with node edges. + // Process in reverse order to preserve earlier character positions. + let cum = 0 + const nodeStarts = nodes.map(n => { const s = cum; cum += n.nodeValue.length; return s }) + + for (const pos of [...boundaries].sort((a, b) => b - a)) { + for (let i = 0; i < nodes.length; i++) { + const start = nodeStarts[i] + const end = start + nodes[i].nodeValue.length + if (pos > start && pos < end) { + nodes[i].splitText(pos - start) + break + } + } + } + + // Re-collect nodes after splits and group into sentence blocks + nodes = collectNodes() + cum = 0 + let groupStart = 0 + let blockCount = 0 + + for (let i = 0; i < nodes.length; i++) { + cum += nodes[i].nodeValue.length + const isEnd = i === nodes.length - 1 || boundaries.has(cum) + if (isEnd) { + const range = doc.createRange() + range.setStart(nodes[groupStart], 0) + range.setEnd(nodes[i], nodes[i].nodeValue.length) + if (!rangeIsEmpty(range)) { + blockCount++ + yield range + } + groupStart = i + 1 + } + } +} + +function* getBlocks(doc) { + const root = doc.body + ?? doc.querySelector('body') + ?? doc.documentElement + + // For PDF text layers, yield sentence-level blocks + const textLayer = root.querySelector?.('.textLayer') + if (textLayer) { + yield* getPDFSentenceBlocks(doc, textLayer) + return + } + + let last + const walker = doc.createTreeWalker(root, NodeFilter.SHOW_ELEMENT) + for (let node = walker.nextNode(); node; node = walker.nextNode()) { + const name = node.tagName.toLowerCase() + if (blockTags.has(name)) { + if (last) { + last.setEndBefore(node) + if (!rangeIsEmpty(last)) yield last + } + last = doc.createRange() + last.setStart(node, 0) + } + } + if (!last) { + last = doc.createRange() + last.setStart(root.firstChild ?? root, 0) + } + last.setEndAfter(root.lastChild ?? root) + if (!rangeIsEmpty(last)) yield last +} + +class ListIterator { + #arr = [] + #iter + #index = -1 + #f + constructor(iter, f = x => x) { + this.#iter = iter + this.#f = f + } + current() { + if (this.#arr[this.#index]) return this.#f(this.#arr[this.#index]) + } + first() { + const newIndex = 0 + if (this.#arr[newIndex]) { + this.#index = newIndex + return this.#f(this.#arr[newIndex]) + } + } + prev() { + const newIndex = this.#index - 1 + if (this.#arr[newIndex]) { + this.#index = newIndex + return this.#f(this.#arr[newIndex]) + } + } + next() { + const newIndex = this.#index + 1 + if (this.#arr[newIndex]) { + this.#index = newIndex + return this.#f(this.#arr[newIndex]) + } + while (true) { + const { done, value } = this.#iter.next() + if (done) break + this.#arr.push(value) + if (this.#arr[newIndex]) { + this.#index = newIndex + return this.#f(this.#arr[newIndex]) + } + } + } + find(f) { + const index = this.#arr.findIndex(x => f(x)) + if (index > -1) { + this.#index = index + return this.#f(this.#arr[index]) + } + while (true) { + const { done, value } = this.#iter.next() + if (done) break + this.#arr.push(value) + if (f(value)) { + this.#index = this.#arr.length - 1 + return this.#f(value) + } + } + } +} + +export class TTS { + #list + #ranges + #lastMark + #serializer = new XMLSerializer() + constructor(doc, textWalker, nodeFilter, highlight, granularity) { + this.doc = doc + this.highlight = highlight + this.#list = new ListIterator(getBlocks(doc), range => { + const { entries, ssml } = getFragmentWithMarks(range, textWalker, nodeFilter, granularity) + this.#ranges = new Map(entries) + return [ssml, range] + }) + } + #getMarkElement(doc, mark) { + if (!mark) return null + return doc.querySelector(`mark[name="${CSS.escape(mark)}"`) + } + #speak(doc, getNode) { + if (!doc) return + if (!getNode) return this.#serializer.serializeToString(doc) + const ssml = document.implementation.createDocument(NS.SSML, 'speak') + ssml.documentElement.replaceWith(ssml.importNode(doc.documentElement, true)) + let node = getNode(ssml)?.previousSibling + while (node) { + const next = node.previousSibling ?? node.parentNode?.previousSibling + node.parentNode.removeChild(node) + node = next + } + const ssmlStr = this.#serializer.serializeToString(ssml) + return ssmlStr + } + start() { + this.#lastMark = null + const [doc] = this.#list.first() ?? [] + if (!doc) return this.next() + return this.#speak(doc, ssml => this.#getMarkElement(ssml, this.#lastMark)) + } + resume() { + const [doc] = this.#list.current() ?? [] + if (!doc) return this.next() + return this.#speak(doc, ssml => this.#getMarkElement(ssml, this.#lastMark)) + } + prev(paused) { + this.#lastMark = null + const [doc, range] = this.#list.prev() ?? [] + if (paused && range) this.highlight(range.cloneRange()) + return this.#speak(doc) + } + next(paused) { + this.#lastMark = null + const [doc, range] = this.#list.next() ?? [] + if (paused && range) this.highlight(range.cloneRange()) + return this.#speak(doc) + } + prevMark(paused) { + const marks = Array.from(this.#ranges.keys()) + if (marks.length === 0) return + + const currentIndex = this.#lastMark ? marks.indexOf(this.#lastMark) : -1 + if (currentIndex > 0) { + const prevMarkName = marks[currentIndex - 1] + const range = this.#ranges.get(prevMarkName) + if (range) { + this.#lastMark = prevMarkName + if (paused) this.highlight(range.cloneRange()) + + const [doc] = this.#list.current() ?? [] + return this.#speak(doc, ssml => this.#getMarkElement(ssml, prevMarkName)) + } + } else { + const [doc, range] = this.#list.prev() ?? [] + if (doc && range) { + const prevMarks = Array.from(this.#ranges.keys()) + if (prevMarks.length > 0) { + const lastMarkName = prevMarks[prevMarks.length - 1] + const lastMarkRange = this.#ranges.get(lastMarkName) + if (lastMarkRange) { + this.#lastMark = lastMarkName + if (paused) this.highlight(lastMarkRange.cloneRange()) + return this.#speak(doc, ssml => this.#getMarkElement(ssml, lastMarkName)) + } + } else { + this.#lastMark = null + if (paused) this.highlight(range.cloneRange()) + return this.#speak(doc) + } + } + } + } + nextMark(paused) { + const marks = Array.from(this.#ranges.keys()) + if (marks.length === 0) return + + const currentIndex = this.#lastMark ? marks.indexOf(this.#lastMark) : -1 + if (currentIndex >= 0 && currentIndex < marks.length - 1) { + const nextMarkName = marks[currentIndex + 1] + const range = this.#ranges.get(nextMarkName) + if (range) { + this.#lastMark = nextMarkName + if (paused) this.highlight(range.cloneRange()) + const [doc] = this.#list.current() ?? [] + return this.#speak(doc, ssml => this.#getMarkElement(ssml, nextMarkName)) + } + } else { + const [doc, range] = this.#list.next() ?? [] + if (doc && range) { + const nextMarks = Array.from(this.#ranges.keys()) + if (nextMarks.length > 0) { + const firstMarkName = nextMarks[0] + const firstMarkRange = this.#ranges.get(firstMarkName) + if (firstMarkRange) { + this.#lastMark = firstMarkName + if (paused) this.highlight(firstMarkRange.cloneRange()) + return this.#speak(doc, ssml => this.#getMarkElement(ssml, firstMarkName)) + } + } else { + this.#lastMark = null + if (paused) this.highlight(range.cloneRange()) + return this.#speak(doc) + } + } + } + } + from(range) { + this.#lastMark = null + const [doc] = this.#list.find(range_ => + range.compareBoundaryPoints(Range.END_TO_START, range_) <= 0) + let mark + for (const [name, range_] of this.#ranges.entries()) + if (range.compareBoundaryPoints(Range.START_TO_START, range_) <= 0) { + mark = name + break + } + return this.#speak(doc, ssml => this.#getMarkElement(ssml, mark)) + } + getLastRange() { + if (this.#lastMark) { + const range = this.#ranges.get(this.#lastMark) + if (range) return range.cloneRange() + } + } + setMark(mark) { + const range = this.#ranges.get(mark) + if (range) { + this.#lastMark = mark + this.highlight(range.cloneRange()) + return range + } + } +} diff --git a/web/vendor/foliate-js/uri-template.js b/web/vendor/foliate-js/uri-template.js new file mode 100644 index 00000000..d66bbf7f --- /dev/null +++ b/web/vendor/foliate-js/uri-template.js @@ -0,0 +1,52 @@ +// URI Template: https://datatracker.ietf.org/doc/html/rfc6570 + +const regex = /{([+#./;?&])?([^}]+?)}/g +const varspecRegex = /(.+?)(\*|:[1-9]\d{0,3})?$/ + +const table = { + undefined: { first: '', sep: ',' }, + '+': { first: '', sep: ',', allowReserved: true }, + '.': { first: '.', sep: '.' }, + '/': { first: '/', sep: '/' }, + ';': { first: ';', sep: ';', named: true, ifemp: '' }, + '?': { first: '?', sep: '&', named: true, ifemp: '=' }, + '&': { first: '&', sep: '&', named: true, ifemp: '=' }, + '#': { first: '&', sep: '&', allowReserved: true }, +} + +// 2.4.1 Prefix Values, "Note that this numbering is in characters, not octets" +const prefix = (maxLength, str) => { + let result = '' + for (const char of str) { + const newResult = char + if (newResult.length > maxLength) return result + else result = newResult + } + return result +} + +export const replace = (str, map) => str.replace(regex, (_, operator, variableList) => { + const { first, sep, named, ifemp, allowReserved } = table[operator] + // TODO: this isn't spec compliant + const encode = allowReserved ? encodeURI : encodeURIComponent + const values = variableList.split(',').map(varspec => { + const match = varspec.match(varspecRegex) + if (!match) return + const [, name, modifier] = match + let value = map.get(name) + if (modifier?.startsWith(':')) { + const maxLength = parseInt(modifier.slice(1)) + value = prefix(maxLength, value) + } + return [name, value ? encode(value) : null] + }) + if (!values.filter(([, value]) => value).length) return '' + return first + values + .map(([name, value]) => value + ? (named ? name + (value ? '=' + value : ifemp) : value) : '') + .filter(x => x).join(sep) +}) + +export const getVariables = str => new Set(Array.from(str.matchAll(regex), + ([,, variableList]) => variableList.split(',') + .map(varspec => varspec.match(varspecRegex)?.[1])).flat()) diff --git a/web/vendor/foliate-js/vendor/fflate.js b/web/vendor/foliate-js/vendor/fflate.js new file mode 100644 index 00000000..f275976a --- /dev/null +++ b/web/vendor/foliate-js/vendor/fflate.js @@ -0,0 +1 @@ +var r=Uint8Array,a=Uint16Array,e=Int32Array,n=new r([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,0,0,0]),i=new r([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,0,0]),t=new r([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]),f=function(r,n){for(var i=new a(31),t=0;t<31;++t)i[t]=n+=1<>1|(21845&d)<<1;w=(61680&(w=(52428&w)>>2|(13107&w)<<2))>>4|(3855&w)<<4,c[d]=((65280&w)>>8|(255&w)<<8)>>1}var b=function(r,e,n){for(var i=r.length,t=0,f=new a(e);t>l]=u}else for(o=new a(i),t=0;t>15-r[t]);return o},s=new r(288);for(d=0;d<144;++d)s[d]=8;for(d=144;d<256;++d)s[d]=9;for(d=256;d<280;++d)s[d]=7;for(d=280;d<288;++d)s[d]=8;var h=new r(32);for(d=0;d<32;++d)h[d]=5;var y=b(s,9,1),g=b(h,5,1),p=function(r){for(var a=r[0],e=1;ea&&(a=r[e]);return a},k=function(r,a,e){var n=a/8|0;return(r[n]|r[n+1]<<8)>>(7&a)&e},m=function(r,a){var e=a/8|0;return(r[e]|r[e+1]<<8|r[e+2]<<16)>>(7&a)},x=["unexpected EOF","invalid block type","invalid length/literal","invalid distance","stream finished","no stream handler",,"no callback","invalid UTF-8 data","extra field too long","date not in range 1980-2099","filename too long","stream finishing","invalid zip data"],T=function(r,a,e){var n=new Error(a||x[r]);if(n.code=r,Error.captureStackTrace&&Error.captureStackTrace(n,T),!e)throw n;return n},E=function(a,e,f,o){var l=a.length,c=o?o.length:0;if(!l||e.f&&!e.l)return f||new r(0);var d=!f,w=d||2!=e.i,s=e.i;d&&(f=new r(3*l));var h=function(a){var e=f.length;if(a>e){var n=new r(Math.max(2*e,a));n.set(f),f=n}},x=e.f||0,E=e.p||0,z=e.b||0,A=e.l,U=e.d,D=e.m,F=e.n,M=8*l;do{if(!A){x=k(a,E,1);var S=k(a,E+1,3);if(E+=3,!S){var I=a[(N=4+((E+7)/8|0))-4]|a[N-3]<<8,O=N+I;if(O>l){s&&T(0);break}w&&h(z+I),f.set(a.subarray(N,O),z),e.b=z+=I,e.p=E=8*O,e.f=x;continue}if(1==S)A=y,U=g,D=9,F=5;else if(2==S){var j=k(a,E,31)+257,q=k(a,E+10,15)+4,B=j+k(a,E+5,31)+1;E+=14;for(var C=new r(B),G=new r(19),H=0;H>4)<16)C[H++]=N;else{var Q=0,R=0;for(16==N?(R=3+k(a,E,3),E+=2,Q=C[H-1]):17==N?(R=3+k(a,E,7),E+=3):18==N&&(R=11+k(a,E,127),E+=7);R--;)C[H++]=Q}}var V=C.subarray(0,j),W=C.subarray(j);D=p(V),F=p(W),A=b(V,D,1),U=b(W,F,1)}else T(1);if(E>M){s&&T(0);break}}w&&h(z+131072);for(var X=(1<>4;if((E+=15&Q)>M){s&&T(0);break}if(Q||T(2),$<256)f[z++]=$;else{if(256==$){Z=E,A=null;break}var _=$-254;if($>264){var rr=n[H=$-257];_=k(a,E,(1<>4;ar||T(3),E+=15&ar;W=u[er];if(er>3){rr=i[er];W+=m(a,E)&(1<M){s&&T(0);break}w&&h(z+131072);var nr=z+_;if(za.length)&&(n=a.length),new r(a.subarray(e,n))}(f,0,z):f.subarray(0,z)},z=new r(0);function A(r,a){return E(r.subarray((e=r,n=a&&a.dictionary,(8!=(15&e[0])||e[0]>>4>7||(e[0]<<8|e[1])%31)&&T(6,"invalid zlib data"),(e[1]>>5&1)==+!n&&T(6,"invalid zlib data: "+(32&e[1]?"need":"unexpected")+" dictionary"),2+(e[1]>>3&4)),-4),{i:2},a&&a.out,a&&a.dictionary);var e,n}var U="undefined"!=typeof TextDecoder&&new TextDecoder;try{U.decode(z,{stream:!0})}catch(r){}export{A as unzlibSync}; diff --git a/web/vendor/foliate-js/vendor/zip.js b/web/vendor/foliate-js/vendor/zip.js new file mode 100644 index 00000000..a65fd86f --- /dev/null +++ b/web/vendor/foliate-js/vendor/zip.js @@ -0,0 +1 @@ +const e=-2,t=-3,n=-5,i=[0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535],r=[96,7,256,0,8,80,0,8,16,84,8,115,82,7,31,0,8,112,0,8,48,0,9,192,80,7,10,0,8,96,0,8,32,0,9,160,0,8,0,0,8,128,0,8,64,0,9,224,80,7,6,0,8,88,0,8,24,0,9,144,83,7,59,0,8,120,0,8,56,0,9,208,81,7,17,0,8,104,0,8,40,0,9,176,0,8,8,0,8,136,0,8,72,0,9,240,80,7,4,0,8,84,0,8,20,85,8,227,83,7,43,0,8,116,0,8,52,0,9,200,81,7,13,0,8,100,0,8,36,0,9,168,0,8,4,0,8,132,0,8,68,0,9,232,80,7,8,0,8,92,0,8,28,0,9,152,84,7,83,0,8,124,0,8,60,0,9,216,82,7,23,0,8,108,0,8,44,0,9,184,0,8,12,0,8,140,0,8,76,0,9,248,80,7,3,0,8,82,0,8,18,85,8,163,83,7,35,0,8,114,0,8,50,0,9,196,81,7,11,0,8,98,0,8,34,0,9,164,0,8,2,0,8,130,0,8,66,0,9,228,80,7,7,0,8,90,0,8,26,0,9,148,84,7,67,0,8,122,0,8,58,0,9,212,82,7,19,0,8,106,0,8,42,0,9,180,0,8,10,0,8,138,0,8,74,0,9,244,80,7,5,0,8,86,0,8,22,192,8,0,83,7,51,0,8,118,0,8,54,0,9,204,81,7,15,0,8,102,0,8,38,0,9,172,0,8,6,0,8,134,0,8,70,0,9,236,80,7,9,0,8,94,0,8,30,0,9,156,84,7,99,0,8,126,0,8,62,0,9,220,82,7,27,0,8,110,0,8,46,0,9,188,0,8,14,0,8,142,0,8,78,0,9,252,96,7,256,0,8,81,0,8,17,85,8,131,82,7,31,0,8,113,0,8,49,0,9,194,80,7,10,0,8,97,0,8,33,0,9,162,0,8,1,0,8,129,0,8,65,0,9,226,80,7,6,0,8,89,0,8,25,0,9,146,83,7,59,0,8,121,0,8,57,0,9,210,81,7,17,0,8,105,0,8,41,0,9,178,0,8,9,0,8,137,0,8,73,0,9,242,80,7,4,0,8,85,0,8,21,80,8,258,83,7,43,0,8,117,0,8,53,0,9,202,81,7,13,0,8,101,0,8,37,0,9,170,0,8,5,0,8,133,0,8,69,0,9,234,80,7,8,0,8,93,0,8,29,0,9,154,84,7,83,0,8,125,0,8,61,0,9,218,82,7,23,0,8,109,0,8,45,0,9,186,0,8,13,0,8,141,0,8,77,0,9,250,80,7,3,0,8,83,0,8,19,85,8,195,83,7,35,0,8,115,0,8,51,0,9,198,81,7,11,0,8,99,0,8,35,0,9,166,0,8,3,0,8,131,0,8,67,0,9,230,80,7,7,0,8,91,0,8,27,0,9,150,84,7,67,0,8,123,0,8,59,0,9,214,82,7,19,0,8,107,0,8,43,0,9,182,0,8,11,0,8,139,0,8,75,0,9,246,80,7,5,0,8,87,0,8,23,192,8,0,83,7,51,0,8,119,0,8,55,0,9,206,81,7,15,0,8,103,0,8,39,0,9,174,0,8,7,0,8,135,0,8,71,0,9,238,80,7,9,0,8,95,0,8,31,0,9,158,84,7,99,0,8,127,0,8,63,0,9,222,82,7,27,0,8,111,0,8,47,0,9,190,0,8,15,0,8,143,0,8,79,0,9,254,96,7,256,0,8,80,0,8,16,84,8,115,82,7,31,0,8,112,0,8,48,0,9,193,80,7,10,0,8,96,0,8,32,0,9,161,0,8,0,0,8,128,0,8,64,0,9,225,80,7,6,0,8,88,0,8,24,0,9,145,83,7,59,0,8,120,0,8,56,0,9,209,81,7,17,0,8,104,0,8,40,0,9,177,0,8,8,0,8,136,0,8,72,0,9,241,80,7,4,0,8,84,0,8,20,85,8,227,83,7,43,0,8,116,0,8,52,0,9,201,81,7,13,0,8,100,0,8,36,0,9,169,0,8,4,0,8,132,0,8,68,0,9,233,80,7,8,0,8,92,0,8,28,0,9,153,84,7,83,0,8,124,0,8,60,0,9,217,82,7,23,0,8,108,0,8,44,0,9,185,0,8,12,0,8,140,0,8,76,0,9,249,80,7,3,0,8,82,0,8,18,85,8,163,83,7,35,0,8,114,0,8,50,0,9,197,81,7,11,0,8,98,0,8,34,0,9,165,0,8,2,0,8,130,0,8,66,0,9,229,80,7,7,0,8,90,0,8,26,0,9,149,84,7,67,0,8,122,0,8,58,0,9,213,82,7,19,0,8,106,0,8,42,0,9,181,0,8,10,0,8,138,0,8,74,0,9,245,80,7,5,0,8,86,0,8,22,192,8,0,83,7,51,0,8,118,0,8,54,0,9,205,81,7,15,0,8,102,0,8,38,0,9,173,0,8,6,0,8,134,0,8,70,0,9,237,80,7,9,0,8,94,0,8,30,0,9,157,84,7,99,0,8,126,0,8,62,0,9,221,82,7,27,0,8,110,0,8,46,0,9,189,0,8,14,0,8,142,0,8,78,0,9,253,96,7,256,0,8,81,0,8,17,85,8,131,82,7,31,0,8,113,0,8,49,0,9,195,80,7,10,0,8,97,0,8,33,0,9,163,0,8,1,0,8,129,0,8,65,0,9,227,80,7,6,0,8,89,0,8,25,0,9,147,83,7,59,0,8,121,0,8,57,0,9,211,81,7,17,0,8,105,0,8,41,0,9,179,0,8,9,0,8,137,0,8,73,0,9,243,80,7,4,0,8,85,0,8,21,80,8,258,83,7,43,0,8,117,0,8,53,0,9,203,81,7,13,0,8,101,0,8,37,0,9,171,0,8,5,0,8,133,0,8,69,0,9,235,80,7,8,0,8,93,0,8,29,0,9,155,84,7,83,0,8,125,0,8,61,0,9,219,82,7,23,0,8,109,0,8,45,0,9,187,0,8,13,0,8,141,0,8,77,0,9,251,80,7,3,0,8,83,0,8,19,85,8,195,83,7,35,0,8,115,0,8,51,0,9,199,81,7,11,0,8,99,0,8,35,0,9,167,0,8,3,0,8,131,0,8,67,0,9,231,80,7,7,0,8,91,0,8,27,0,9,151,84,7,67,0,8,123,0,8,59,0,9,215,82,7,19,0,8,107,0,8,43,0,9,183,0,8,11,0,8,139,0,8,75,0,9,247,80,7,5,0,8,87,0,8,23,192,8,0,83,7,51,0,8,119,0,8,55,0,9,207,81,7,15,0,8,103,0,8,39,0,9,175,0,8,7,0,8,135,0,8,71,0,9,239,80,7,9,0,8,95,0,8,31,0,9,159,84,7,99,0,8,127,0,8,63,0,9,223,82,7,27,0,8,111,0,8,47,0,9,191,0,8,15,0,8,143,0,8,79,0,9,255],a=[80,5,1,87,5,257,83,5,17,91,5,4097,81,5,5,89,5,1025,85,5,65,93,5,16385,80,5,3,88,5,513,84,5,33,92,5,8193,82,5,9,90,5,2049,86,5,129,192,5,24577,80,5,2,87,5,385,83,5,25,91,5,6145,81,5,7,89,5,1537,85,5,97,93,5,24577,80,5,4,88,5,769,84,5,49,92,5,12289,82,5,13,90,5,3073,86,5,193,192,5,24577],s=[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,0,0],o=[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,112,112],l=[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577],c=[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],u=15;function d(){let e,i,r,a,d,f;function h(e,i,s,o,l,c,h,w,_,b,p){let m,g,y,x,k,v,S,z,A,U,D,E,T,F,O;U=0,k=s;do{r[e[i+U]]++,U++,k--}while(0!==k);if(r[0]==s)return h[0]=-1,w[0]=0,0;for(z=w[0],v=1;v<=u&&0===r[v];v++);for(S=v,zk&&(z=k),w[0]=z,F=1<E+z;){if(x++,E+=z,O=y-E,O=O>z?z:O,(g=1<<(v=S-E))>m+1&&(g-=m+1,T=S,v1440)return t;d[x]=D=b[0],b[0]+=O,0!==x?(f[x]=k,a[0]=v,a[1]=z,v=k>>>E-z,a[2]=D-d[x-1]-v,_.set(a,3*(d[x-1]+v))):h[0]=D}for(a[1]=S-E,U>=s?a[0]=192:p[U]>>E;v>>=1)k^=v;for(k^=v,A=(1<257?(g==t?m.msg="oversubscribed distance tree":g==n?(m.msg="incomplete distance tree",g=t):-4!=g&&(m.msg="empty distance tree with lengths",g=t),g):0)}}d.inflate_trees_fixed=function(e,t,n,i){return e[0]=9,t[0]=5,n[0]=r,i[0]=a,0};function f(){const n=this;let r,a,s,o,l=0,c=0,u=0,d=0,f=0,h=0,w=0,_=0,b=0,p=0;function m(e,n,r,a,s,o,l,c){let u,d,f,h,w,_,b,p,m,g,y,x,k,v,S,z;b=c.next_in_index,p=c.avail_in,w=l.bitb,_=l.bitk,m=l.write,g=m>=d[z+1],_-=d[z+1],16&h){for(h&=15,k=d[z+2]+(w&i[h]),w>>=h,_-=h;_<15;)p--,w|=(255&c.read_byte(b++))<<_,_+=8;for(u=w&x,d=s,f=o,z=3*(f+u),h=d[z];;){if(w>>=d[z+1],_-=d[z+1],16&h){for(h&=15;_>=h,_-=h,g-=k,m>=v)S=m-v,m-S>0&&2>m-S?(l.win[m++]=l.win[S++],l.win[m++]=l.win[S++],k-=2):(l.win.set(l.win.subarray(S,S+2),m),m+=2,S+=2,k-=2);else{S=m-v;do{S+=l.end}while(S<0);if(h=l.end-S,k>h){if(k-=h,m-S>0&&h>m-S)do{l.win[m++]=l.win[S++]}while(0!=--h);else l.win.set(l.win.subarray(S,S+h),m),m+=h,S+=h,h=0;S=0}}if(m-S>0&&k>m-S)do{l.win[m++]=l.win[S++]}while(0!=--k);else l.win.set(l.win.subarray(S,S+k),m),m+=k,S+=k,k=0;break}if(64&h)return c.msg="invalid distance code",k=c.avail_in-p,k=_>>3>3:k,p+=k,b-=k,_-=k<<3,l.bitb=w,l.bitk=_,c.avail_in=p,c.total_in+=b-c.next_in_index,c.next_in_index=b,l.write=m,t;u+=d[z+2],u+=w&i[h],z=3*(f+u),h=d[z]}break}if(64&h)return 32&h?(k=c.avail_in-p,k=_>>3>3:k,p+=k,b-=k,_-=k<<3,l.bitb=w,l.bitk=_,c.avail_in=p,c.total_in+=b-c.next_in_index,c.next_in_index=b,l.write=m,1):(c.msg="invalid literal/length code",k=c.avail_in-p,k=_>>3>3:k,p+=k,b-=k,_-=k<<3,l.bitb=w,l.bitk=_,c.avail_in=p,c.total_in+=b-c.next_in_index,c.next_in_index=b,l.write=m,t);if(u+=d[z+2],u+=w&i[h],z=3*(f+u),0===(h=d[z])){w>>=d[z+1],_-=d[z+1],l.win[m++]=d[z+2],g--;break}}else w>>=d[z+1],_-=d[z+1],l.win[m++]=d[z+2],g--}while(g>=258&&p>=10);return k=c.avail_in-p,k=_>>3>3:k,p+=k,b-=k,_-=k<<3,l.bitb=w,l.bitk=_,c.avail_in=p,c.total_in+=b-c.next_in_index,c.next_in_index=b,l.write=m,0}n.init=function(e,t,n,i,l,c){r=0,w=e,_=t,s=n,b=i,o=l,p=c,a=null},n.proc=function(n,g,y){let x,k,v,S,z,A,U,D=0,E=0,T=0;for(T=g.next_in_index,S=g.avail_in,D=n.bitb,E=n.bitk,z=n.write,A=z=258&&S>=10&&(n.bitb=D,n.bitk=E,g.avail_in=S,g.total_in+=T-g.next_in_index,g.next_in_index=T,n.write=z,y=m(w,_,s,b,o,p,n,g),T=g.next_in_index,S=g.avail_in,D=n.bitb,E=n.bitk,z=n.write,A=z>>=a[k+1],E-=a[k+1],v=a[k],0===v){d=a[k+2],r=6;break}if(16&v){f=15&v,l=a[k+2],r=2;break}if(!(64&v)){u=v,c=k/3+a[k+2];break}if(32&v){r=7;break}return r=9,g.msg="invalid literal/length code",y=t,n.bitb=D,n.bitk=E,g.avail_in=S,g.total_in+=T-g.next_in_index,g.next_in_index=T,n.write=z,n.inflate_flush(g,y);case 2:for(x=f;E>=x,E-=x,u=_,a=o,c=p,r=3;case 3:for(x=u;E>=a[k+1],E-=a[k+1],v=a[k],16&v){f=15&v,h=a[k+2],r=4;break}if(!(64&v)){u=v,c=k/3+a[k+2];break}return r=9,g.msg="invalid distance code",y=t,n.bitb=D,n.bitk=E,g.avail_in=S,g.total_in+=T-g.next_in_index,g.next_in_index=T,n.write=z,n.inflate_flush(g,y);case 4:for(x=f;E>=x,E-=x,r=5;case 5:for(U=z-h;U<0;)U+=n.end;for(;0!==l;){if(0===A&&(z==n.end&&0!==n.read&&(z=0,A=z7&&(E-=8,S++,T--),n.write=z,y=n.inflate_flush(g,y),z=n.write,A=ze.avail_out&&(i=e.avail_out),0!==i&&t==n&&(t=0),e.avail_out-=i,e.total_out+=i,e.next_out.set(s.win.subarray(a,a+i),r),r+=i,a+=i,a==s.end&&(a=0,s.write==s.end&&(s.write=0),i=s.write-a,i>e.avail_out&&(i=e.avail_out),0!==i&&t==n&&(t=0),e.avail_out-=i,e.total_out+=i,e.next_out.set(s.win.subarray(a,a+i),r),r+=i,a+=i),e.next_out_index=r,s.read=a,t},s.proc=function(n,r){let a,f,x,k,v,S,z,A;for(k=n.next_in_index,v=n.avail_in,f=s.bitb,x=s.bitk,S=s.write,z=S>>1){case 0:f>>>=3,x-=3,a=7&x,f>>>=a,x-=a,l=1;break;case 1:U=[],D=[],E=[[]],T=[[]],d.inflate_trees_fixed(U,D,E,T),p.init(U[0],D[0],E[0],0,T[0],0),f>>>=3,x-=3,l=6;break;case 2:f>>>=3,x-=3,l=3;break;case 3:return f>>>=3,x-=3,l=9,n.msg="invalid block type",r=t,s.bitb=f,s.bitk=x,n.avail_in=v,n.total_in+=k-n.next_in_index,n.next_in_index=k,s.write=S,s.inflate_flush(n,r)}break;case 1:for(;x<32;){if(0===v)return s.bitb=f,s.bitk=x,n.avail_in=v,n.total_in+=k-n.next_in_index,n.next_in_index=k,s.write=S,s.inflate_flush(n,r);r=0,v--,f|=(255&n.read_byte(k++))<>>16&65535)!=(65535&f))return l=9,n.msg="invalid stored block lengths",r=t,s.bitb=f,s.bitk=x,n.avail_in=v,n.total_in+=k-n.next_in_index,n.next_in_index=k,s.write=S,s.inflate_flush(n,r);c=65535&f,f=x=0,l=0!==c?2:0!==m?7:0;break;case 2:if(0===v)return s.bitb=f,s.bitk=x,n.avail_in=v,n.total_in+=k-n.next_in_index,n.next_in_index=k,s.write=S,s.inflate_flush(n,r);if(0===z&&(S==s.end&&0!==s.read&&(S=0,z=Sv&&(a=v),a>z&&(a=z),s.win.set(n.read_buf(k,a),S),k+=a,v-=a,S+=a,z-=a,0!=(c-=a))break;l=0!==m?7:0;break;case 3:for(;x<14;){if(0===v)return s.bitb=f,s.bitk=x,n.avail_in=v,n.total_in+=k-n.next_in_index,n.next_in_index=k,s.write=S,s.inflate_flush(n,r);r=0,v--,f|=(255&n.read_byte(k++))<29||(a>>5&31)>29)return l=9,n.msg="too many length or distance symbols",r=t,s.bitb=f,s.bitk=x,n.avail_in=v,n.total_in+=k-n.next_in_index,n.next_in_index=k,s.write=S,s.inflate_flush(n,r);if(a=258+(31&a)+(a>>5&31),!o||o.length>>=14,x-=14,w=0,l=4;case 4:for(;w<4+(u>>>10);){for(;x<3;){if(0===v)return s.bitb=f,s.bitk=x,n.avail_in=v,n.total_in+=k-n.next_in_index,n.next_in_index=k,s.write=S,s.inflate_flush(n,r);r=0,v--,f|=(255&n.read_byte(k++))<>>=3,x-=3}for(;w<19;)o[h[w++]]=0;if(_[0]=7,a=y.inflate_trees_bits(o,_,b,g,n),0!=a)return(r=a)==t&&(o=null,l=9),s.bitb=f,s.bitk=x,n.avail_in=v,n.total_in+=k-n.next_in_index,n.next_in_index=k,s.write=S,s.inflate_flush(n,r);w=0,l=5;case 5:for(;a=u,!(w>=258+(31&a)+(a>>5&31));){let e,c;for(a=_[0];x>>=a,x-=a,o[w++]=c;else{for(A=18==c?7:c-14,e=18==c?11:3;x>>=a,x-=a,e+=f&i[A],f>>>=A,x-=A,A=w,a=u,A+e>258+(31&a)+(a>>5&31)||16==c&&A<1)return o=null,l=9,n.msg="invalid bit length repeat",r=t,s.bitb=f,s.bitk=x,n.avail_in=v,n.total_in+=k-n.next_in_index,n.next_in_index=k,s.write=S,s.inflate_flush(n,r);c=16==c?o[A-1]:0;do{o[A++]=c}while(0!=--e);w=A}}if(b[0]=-1,F=[],O=[],C=[],W=[],F[0]=9,O[0]=6,a=u,a=y.inflate_trees_dynamic(257+(31&a),1+(a>>5&31),o,F,O,C,W,g,n),0!=a)return a==t&&(o=null,l=9),r=a,s.bitb=f,s.bitk=x,n.avail_in=v,n.total_in+=k-n.next_in_index,n.next_in_index=k,s.write=S,s.inflate_flush(n,r);p.init(F[0],O[0],g,C[0],g,W[0]),l=6;case 6:if(s.bitb=f,s.bitk=x,n.avail_in=v,n.total_in+=k-n.next_in_index,n.next_in_index=k,s.write=S,1!=(r=p.proc(s,n,r)))return s.inflate_flush(n,r);if(r=0,p.free(n),k=n.next_in_index,v=n.avail_in,f=s.bitb,x=s.bitk,S=s.write,z=S15?(i.inflateEnd(t),e):(i.wbits=n,t.istate.blocks=new w(t,1<>4)>o.wbits){o.mode=_,i.msg="invalid win size",o.marker=5;break}o.mode=1;case 1:if(0===i.avail_in)return a;if(a=r,i.avail_in--,i.total_in++,s=255&i.read_byte(i.next_in_index++),((o.method<<8)+s)%31!=0){o.mode=_,i.msg="incorrect header check",o.marker=5;break}if(!(32&s)){o.mode=7;break}o.mode=2;case 2:if(0===i.avail_in)return a;a=r,i.avail_in--,i.total_in++,o.need=(255&i.read_byte(i.next_in_index++))<<24&4278190080,o.mode=3;case 3:if(0===i.avail_in)return a;a=r,i.avail_in--,i.total_in++,o.need+=(255&i.read_byte(i.next_in_index++))<<16&16711680,o.mode=4;case 4:if(0===i.avail_in)return a;a=r,i.avail_in--,i.total_in++,o.need+=(255&i.read_byte(i.next_in_index++))<<8&65280,o.mode=5;case 5:return 0===i.avail_in?a:(a=r,i.avail_in--,i.total_in++,o.need+=255&i.read_byte(i.next_in_index++),o.mode=6,2);case 6:return o.mode=_,i.msg="need dictionary",o.marker=0,e;case 7:if(a=o.blocks.proc(i,a),a==t){o.mode=_,o.marker=0;break}if(0==a&&(a=r),1!=a)return a;a=r,o.blocks.reset(i,o.was),o.mode=12;case 12:return i.avail_in=0,1;case _:return t;default:return e}},i.inflateSetDictionary=function(t,n,i){let r=0,a=i;if(!t||!t.istate||6!=t.istate.mode)return e;const s=t.istate;return a>=1<>>1^3988292384:t>>>=1;C[e]=t}class W{constructor(e){this.crc=e||-1}append(e){let t=0|this.crc;for(let n=0,i=0|e.length;n>>8^C[255&(t^e[n])];this.crc=t}get(){return~this.crc}}class j extends TransformStream{constructor(){let e;const t=new W;super({transform(e,n){t.append(e),n.enqueue(e)},flush(){const n=new Uint8Array(4);new DataView(n.buffer).setUint32(0,t.get()),e.value=n}}),e=this}}const M={concat(e,t){if(0===e.length||0===t.length)return e.concat(t);const n=e[e.length-1],i=M.getPartial(n);return 32===i?e.concat(t):M._shiftRight(t,i,0|n,e.slice(0,e.length-1))},bitLength(e){const t=e.length;if(0===t)return 0;const n=e[t-1];return 32*(t-1)+M.getPartial(n)},clamp(e,t){if(32*e.length0&&t&&(e[n-1]=M.partial(t,e[n-1]&2147483648>>t-1,1)),e},partial:(e,t,n)=>32===e?t:(n?0|t:t<<32-e)+1099511627776*e,getPartial:e=>Math.round(e/1099511627776)||32,_shiftRight(e,t,n,i){for(void 0===i&&(i=[]);t>=32;t-=32)i.push(n),n=0;if(0===t)return i.concat(e);for(let r=0;r>>t),n=e[r]<<32-t;const r=e.length?e[e.length-1]:0,a=M.getPartial(r);return i.push(M.partial(t+a&31,t+a>32?n:i.pop(),1)),i}},L={bytes:{fromBits(e){const t=M.bitLength(e)/8,n=new Uint8Array(t);let i;for(let r=0;r>>24,i<<=8;return n},toBits(e){const t=[];let n,i=0;for(n=0;n9007199254740991)throw new Error("Cannot hash more than 2^53 - 1 bits");const a=new Uint32Array(n);let s=0;for(let e=t.blockSize+i-(t.blockSize+i&t.blockSize-1);e<=r;e+=t.blockSize)t._block(a.subarray(16*s,16*(s+1))),s+=1;return n.splice(0,16*s),t}finalize(){const e=this;let t=e._buffer;const n=e._h;t=M.concat(t,[M.partial(1,1)]);for(let e=t.length+2;15&e;e++)t.push(0);for(t.push(Math.floor(e._length/4294967296)),t.push(0|e._length);t.length;)e._block(t.splice(0,16));return e.reset(),n}_f(e,t,n,i){return e<=19?t&n|~t&i:e<=39?t^n^i:e<=59?t&n|t&i|n&i:e<=79?t^n^i:void 0}_S(e,t){return t<>>32-e}_block(e){const t=this,n=t._h,i=Array(80);for(let t=0;t<16;t++)i[t]=e[t];let r=n[0],a=n[1],s=n[2],o=n[3],l=n[4];for(let e=0;e<=79;e++){e>=16&&(i[e]=t._S(1,i[e-3]^i[e-8]^i[e-14]^i[e-16]));const n=t._S(5,r)+t._f(e,a,s,o)+l+i[e]+t._key[Math.floor(e/20)]|0;l=o,o=s,s=t._S(30,a),a=r,r=n}n[0]=n[0]+r|0,n[1]=n[1]+a|0,n[2]=n[2]+s|0,n[3]=n[3]+o|0,n[4]=n[4]+l|0}}},R={aes:class{constructor(e){const t=this;t._tables=[[[],[],[],[],[]],[[],[],[],[],[]]],t._tables[0][0][0]||t._precompute();const n=t._tables[0][4],i=t._tables[1],r=e.length;let a,s,o,l=1;if(4!==r&&6!==r&&8!==r)throw new Error("invalid aes key size");for(t._key=[s=e.slice(0),o=[]],a=r;a<4*r+28;a++){let e=s[a-1];(a%r==0||8===r&&a%r==4)&&(e=n[e>>>24]<<24^n[e>>16&255]<<16^n[e>>8&255]<<8^n[255&e],a%r==0&&(e=e<<8^e>>>24^l<<24,l=l<<1^283*(l>>7))),s[a]=s[a-r]^e}for(let e=0;a;e++,a--){const t=s[3&e?a:a-4];o[e]=a<=4||e<4?t:i[0][n[t>>>24]]^i[1][n[t>>16&255]]^i[2][n[t>>8&255]]^i[3][n[255&t]]}}encrypt(e){return this._crypt(e,0)}decrypt(e){return this._crypt(e,1)}_precompute(){const e=this._tables[0],t=this._tables[1],n=e[4],i=t[4],r=[],a=[];let s,o,l,c;for(let e=0;e<256;e++)a[(r[e]=e<<1^283*(e>>7))^e]=e;for(let u=s=0;!n[u];u^=o||1,s=a[s]||1){let a=s^s<<1^s<<2^s<<3^s<<4;a=a>>8^255&a^99,n[u]=a,i[a]=u,c=r[l=r[o=r[u]]];let d=16843009*c^65537*l^257*o^16843008*u,f=257*r[a]^16843008*a;for(let n=0;n<4;n++)e[n][u]=f=f<<24^f>>>8,t[n][a]=d=d<<24^d>>>8}for(let n=0;n<5;n++)e[n]=e[n].slice(0),t[n]=t[n].slice(0)}_crypt(e,t){if(4!==e.length)throw new Error("invalid aes block size");const n=this._key[t],i=n.length/4-2,r=[0,0,0,0],a=this._tables[t],s=a[0],o=a[1],l=a[2],c=a[3],u=a[4];let d,f,h,w=e[0]^n[0],_=e[t?3:1]^n[1],b=e[2]^n[2],p=e[t?1:3]^n[3],m=4;for(let e=0;e>>24]^o[_>>16&255]^l[b>>8&255]^c[255&p]^n[m],f=s[_>>>24]^o[b>>16&255]^l[p>>8&255]^c[255&w]^n[m+1],h=s[b>>>24]^o[p>>16&255]^l[w>>8&255]^c[255&_]^n[m+2],p=s[p>>>24]^o[w>>16&255]^l[_>>8&255]^c[255&b]^n[m+3],m+=4,w=d,_=f,b=h;for(let e=0;e<4;e++)r[t?3&-e:e]=u[w>>>24]<<24^u[_>>16&255]<<16^u[b>>8&255]<<8^u[255&p]^n[m++],d=w,w=_,_=b,b=p,p=d;return r}}},B={getRandomValues(e){const t=new Uint32Array(e.buffer),n=e=>{let t=987654321;const n=4294967295;return function(){t=36969*(65535&t)+(t>>16)&n;return(((t<<16)+(e=18e3*(65535&e)+(e>>16)&n)&n)/4294967296+.5)*(Math.random()>.5?1:-1)}};for(let i,r=0;r>24))e+=1<<24;else{let t=e>>16&255,n=e>>8&255,i=255&e;255===t?(t=0,255===n?(n=0,255===i?i=0:++i):++n):++t,e=0,e+=t<<16,e+=n<<8,e+=i}return e}incCounter(e){0===(e[0]=this.incWord(e[0]))&&(e[1]=this.incWord(e[1]))}calculate(e,t,n){let i;if(!(i=t.length))return[];const r=M.bitLength(t);for(let r=0;rnew N.hmacSha1(L.bytes.toBits(e)),pbkdf2(e,t,n,i){if(n=n||1e4,i<0||n<0)throw new Error("invalid params to pbkdf2");const r=1+(i>>5)<<2;let a,s,o,l,c;const u=new ArrayBuffer(r),d=new DataView(u);let f=0;const h=M;for(t=L.bytes.toBits(t),c=1;f<(r||1);c++){for(a=s=e.encrypt(h.concat(t,[c])),o=1;or&&(e=(new n).update(e).finalize());for(let t=0;tthis.resolveReady=e)),password:be(e,t),signed:n,strength:i-1,pending:new Uint8Array})},async transform(e,t){const n=this,{password:i,strength:a,resolveReady:s,ready:o}=n;i?(await async function(e,t,n,i){const r=await _e(e,t,n,me(i,0,$[t])),a=me(i,$[t]);if(r[0]!=a[0]||r[1]!=a[1])throw new Error(q)}(n,a,i,me(e,0,$[a]+2)),e=me(e,$[a]+2),r?t.error(new Error(K)):s()):await o;const l=new Uint8Array(e.length-te-(e.length-te)%G);t.enqueue(we(n,e,l,0,te,!0))},async flush(e){const{signed:t,ctr:n,hmac:i,pending:r,ready:a}=this;if(i&&n){await a;const s=me(r,0,r.length-te),o=me(r,r.length-te);let l=new Uint8Array;if(s.length){const e=ye(se,s);i.update(e);const t=n.update(e);l=ge(se,t)}if(t){const e=me(ge(se,i.digest()),0,te);for(let t=0;tthis.resolveReady=e)),password:be(e,t),strength:n-1,pending:new Uint8Array})},async transform(e,t){const n=this,{password:i,strength:r,resolveReady:a,ready:s}=n;let o=new Uint8Array;i?(o=await async function(e,t,n){const i=Z(new Uint8Array($[t])),r=await _e(e,t,n,i);return pe(i,r)}(n,r,i),a()):await s;const l=new Uint8Array(o.length+e.length-e.length%G);l.set(o,0),t.enqueue(we(n,e,l,o.length,0))},async flush(e){const{ctr:t,hmac:n,pending:r,ready:a}=this;if(n&&t){await a;let s=new Uint8Array;if(r.length){const e=t.update(ye(se,r));n.update(e),s=ge(se,e)}i.signature=ge(se,n.digest()).slice(0,te),e.enqueue(pe(s,i.signature))}}}),i=this}}function we(e,t,n,i,r,a){const{ctr:s,hmac:o,pending:l}=e,c=t.length-r;let u;for(l.length&&(t=pe(l,t),n=function(e,t){if(t&&t>e.length){const n=e;(e=new Uint8Array(t)).set(n,0)}return e}(n,c-c%G)),u=0;u<=c-G;u+=G){const e=ye(se,me(t,u,u+G));a&&o.update(e);const r=s.update(e);a||o.update(r),n.set(ge(se,r),u+i)}return e.pending=me(t,u),n}async function _e(e,t,n,i){e.password=null;const r=await async function(e,t,n,i,r){if(!ue)return N.importKey(t);try{return await re.importKey(e,t,n,i,r)}catch(e){return ue=!1,N.importKey(t)}}("raw",n,Q,!1,Y),a=await async function(e,t,n){if(!de)return N.pbkdf2(t,e.salt,X.iterations,n);try{return await re.deriveBits(e,t,n)}catch(i){return de=!1,N.pbkdf2(t,e.salt,X.iterations,n)}}(Object.assign({salt:i},X),r,8*(2*ee[t]+2)),s=new Uint8Array(a),o=ye(se,me(s,0,ee[t])),l=ye(se,me(s,ee[t],2*ee[t])),c=me(s,2*ee[t]);return Object.assign(e,{keys:{key:o,authentication:l,passwordVerification:c},ctr:new le(new oe(o),Array.from(ne)),hmac:new ce(l)}),c}function be(e,t){return t===S?function(e){if(typeof TextEncoder==z){e=unescape(encodeURIComponent(e));const t=new Uint8Array(e.length);for(let n=0;n>>24]),r=~e.crcKey2.get(),e.keys=[n,i,r]}function De(e){const t=2|e.keys[2];return Ee(Math.imul(t,1^t)>>>8)}function Ee(e){return 255&e}function Te(e){return 4294967295&e}const Fe="deflate-raw";class Oe extends TransformStream{constructor(e,{chunkSize:t,CompressionStream:n,CompressionStreamNative:i}){super({});const{compressed:r,encrypted:a,useCompressionStream:s,zipCrypto:o,signed:l,level:c}=e,u=this;let d,f,h=We(super.readable);a&&!o||!l||(d=new j,h=Le(h,d)),r&&(h=Me(h,s,{level:c,chunkSize:t},i,n)),a&&(o?h=Le(h,new ve(e)):(f=new he(e),h=Le(h,f))),je(u,h,(()=>{let e;a&&!o&&(e=f.signature),a&&!o||!l||(e=new DataView(d.value.buffer).getUint32(0)),u.signature=e}))}}class Ce extends TransformStream{constructor(e,{chunkSize:t,DecompressionStream:n,DecompressionStreamNative:i}){super({});const{zipCrypto:r,encrypted:a,signed:s,signature:o,compressed:l,useCompressionStream:c}=e;let u,d,f=We(super.readable);a&&(r?f=Le(f,new ke(e)):(d=new fe(e),f=Le(f,d))),l&&(f=Me(f,c,{chunkSize:t},i,n)),a&&!r||!s||(u=new j,f=Le(f,u)),je(this,f,(()=>{if((!a||r)&&s){const e=new DataView(u.value.buffer);if(o!=e.getUint32(0,!1))throw new Error(H)}}))}}function We(e){return Le(e,new TransformStream({transform(e,t){e&&e.length&&t.enqueue(e)}}))}function je(e,t,n){t=Le(t,new TransformStream({flush:n})),Object.defineProperty(e,"readable",{get:()=>t})}function Me(e,t,n,i,r){try{e=Le(e,new(t&&i?i:r)(Fe,n))}catch(i){if(!t)return e;try{e=Le(e,new r(Fe,n))}catch(t){return e}}return e}function Le(e,t){return e.pipeThrough(t)}const Pe="message",Re="start",Be="pull",Ie="data",Ne="close",Ve="inflate";class qe extends TransformStream{constructor(e,t){super({});const n=this,{codecType:i}=e;let r;i.startsWith("deflate")?r=Oe:i.startsWith(Ve)&&(r=Ce);let a=0,s=0;const o=new r(e,t),l=super.readable,c=new TransformStream({transform(e,t){e&&e.length&&(s+=e.length,t.enqueue(e))},flush(){Object.assign(n,{inputSize:s})}}),u=new TransformStream({transform(e,t){e&&e.length&&(a+=e.length,t.enqueue(e))},flush(){const{signature:e}=o;Object.assign(n,{signature:e,outputSize:a,inputSize:s})}});Object.defineProperty(n,"readable",{get:()=>l.pipeThrough(c).pipeThrough(o).pipeThrough(u)})}}class He extends TransformStream{constructor(e){let t;super({transform:function n(i,r){if(t){const e=new Uint8Array(t.length+i.length);e.set(t),e.set(i,t.length),i=e,t=null}i.length>e?(r.enqueue(i.slice(0,e)),n(i.slice(e),r)):t=i},flush(e){t&&t.length&&e.enqueue(t)}})}}let Ke=typeof Worker!=z;class Ze{constructor(e,{readable:t,writable:n},{options:i,config:r,streamOptions:a,useWebWorkers:s,transferStreams:o,scripts:l},c){const{signal:u}=a;return Object.assign(e,{busy:!0,readable:t.pipeThrough(new He(r.chunkSize)).pipeThrough(new Ge(t,a),{signal:u}),writable:n,options:Object.assign({},i),scripts:l,transferStreams:o,terminate:()=>new Promise((t=>{const{worker:n,busy:i}=e;n?(i?e.resolveTerminated=t:(n.terminate(),t()),e.interface=null):t()})),onTaskFinished(){const{resolveTerminated:t}=e;t&&(e.resolveTerminated=null,e.terminated=!0,e.worker.terminate(),t()),e.busy=!1,c(e)}}),(s&&Ke?Xe:Qe)(e,r)}}class Ge extends TransformStream{constructor(e,{onstart:t,onprogress:n,size:i,onend:r}){let a=0;super({async start(){t&&await Je(t,i)},async transform(e,t){a+=e.length,n&&await Je(n,a,i),t.enqueue(e)},async flush(){e.size=a,r&&await Je(r,a)}})}}async function Je(e,...t){try{await e(...t)}catch(e){}}function Qe(e,t){return{run:()=>async function({options:e,readable:t,writable:n,onTaskFinished:i},r){try{const i=new qe(e,r);await t.pipeThrough(i).pipeTo(n,{preventClose:!0,preventAbort:!0});const{signature:a,inputSize:s,outputSize:o}=i;return{signature:a,inputSize:s,outputSize:o}}finally{i()}}(e,t)}}function Xe(e,t){const{baseURL:n,chunkSize:i}=t;if(!e.interface){let r;try{r=function(e,t,n){const i={type:"module"};let r,a;typeof e==A&&(e=e());try{r=new URL(e,t)}catch(t){r=e}if(Ye)try{a=new Worker(r)}catch(e){Ye=!1,a=new Worker(r,i)}else a=new Worker(r,i);return a.addEventListener(Pe,(e=>async function({data:e},t){const{type:n,value:i,messageId:r,result:a,error:s}=e,{reader:o,writer:l,resolveResult:c,rejectResult:u,onTaskFinished:d}=t;try{if(s){const{message:e,stack:t,code:n,name:i}=s,r=new Error(e);Object.assign(r,{stack:t,code:n,name:i}),f(r)}else{if(n==Be){const{value:e,done:n}=await o.read();et({type:Ie,value:e,done:n,messageId:r},t)}n==Ie&&(await l.ready,await l.write(new Uint8Array(i)),et({type:"ack",messageId:r},t)),n==Ne&&f(null,a)}}catch(s){et({type:Ne,messageId:r},t),f(s)}function f(e,t){e?u(e):c(t),l&&l.releaseLock(),d()}}(e,n))),a}(e.scripts[0],n,e)}catch(n){return Ke=!1,Qe(e,t)}Object.assign(e,{worker:r,interface:{run:()=>async function(e,t){let n,i;const r=new Promise(((e,t)=>{n=e,i=t}));Object.assign(e,{reader:null,writer:null,resolveResult:n,rejectResult:i,result:r});const{readable:a,options:s,scripts:o}=e,{writable:l,closed:c}=function(e){let t;const n=new Promise((e=>t=e)),i=new WritableStream({async write(t){const n=e.getWriter();await n.ready,await n.write(t),n.releaseLock()},close(){t()},abort:t=>e.getWriter().abort(t)});return{writable:i,closed:n}}(e.writable),u=et({type:Re,scripts:o.slice(1),options:s,config:t,readable:a,writable:l},e);u||Object.assign(e,{reader:a.getReader(),writer:l.getWriter()});const d=await r;u||await l.getWriter().close();return await c,d}(e,{chunkSize:i})}})}return e.interface}let Ye=!0,$e=!0;function et(e,{worker:t,writer:n,onTaskFinished:i,transferStreams:r}){try{let{value:n,readable:i,writable:a}=e;const s=[];if(n&&(n.byteLength!e.busy));if(n)return at(n),new Ze(n,e,t,w);if(tt.lengthnt.push({resolve:n,stream:e,workerOptions:t})))}()).run();function w(e){if(nt.length){const[{resolve:t,stream:n,workerOptions:i}]=nt.splice(0,1);t(new Ze(e,n,i,w))}else e.worker?(at(e),function(e,t){const{config:n}=t,{terminateWorkerTimeout:i}=n;Number.isFinite(i)&&i>=0&&(e.terminated?e.terminated=!1:e.terminateTimeout=setTimeout((async()=>{tt=tt.filter((t=>t!=e));try{await e.terminate()}catch(e){}}),i))}(e,t)):tt=tt.filter((t=>t!=e))}}function at(e){const{terminateTimeout:t}=e;t&&(clearTimeout(t),e.terminateTimeout=null)}const st=65536,ot="writable";class lt{constructor(){this.size=0}init(){this.initialized=!0}}class ct extends lt{get readable(){const e=this,{chunkSize:t=st}=e,n=new ReadableStream({start(){this.chunkOffset=0},async pull(i){const{offset:r=0,size:a,diskNumberStart:s}=n,{chunkOffset:o}=this;i.enqueue(await pt(e,r+o,Math.min(t,a-o),s)),o+t>a?i.close():this.chunkOffset+=t}});return n}}class ut extends ct{constructor(e){super(),Object.assign(this,{blob:e,size:e.size})}async readUint8Array(e,t){const n=this,i=e+t,r=e||it&&(a=a.slice(e,i)),new Uint8Array(a)}}class dt extends lt{constructor(e){super();const t=new TransformStream,n=[];e&&n.push(["Content-Type",e]),Object.defineProperty(this,ot,{get:()=>t.writable}),this.blob=new Response(t.readable,{headers:n}).blob()}getData(){return this.blob}}class ft extends dt{constructor(e){super(e),Object.assign(this,{encoding:e,utf8:!e||"utf-8"==e.toLowerCase()})}async getData(){const{encoding:e,utf8:t}=this,n=await super.getData();if(n.text&&t)return n.text();{const t=new FileReader;return new Promise(((i,r)=>{Object.assign(t,{onload:({target:e})=>i(e.result),onerror:()=>r(t.error)}),t.readAsText(n,e)}))}}}class ht extends ct{constructor(e){super(),this.readers=e}async init(){const e=this,{readers:t}=e;e.lastDiskNumber=0,e.lastDiskOffset=0,await Promise.all(t.map((async(n,i)=>{await n.init(),i!=t.length-1&&(e.lastDiskOffset+=n.size),e.size+=n.size}))),super.init()}async readUint8Array(e,t,n=0){const i=this,{readers:r}=this;let a,s=n;-1==s&&(s=r.length-1);let o=e;for(;o>=r[s].size;)o-=r[s].size,s++;const l=r[s],c=l.size;if(o+t<=c)a=await pt(l,o,t);else{const r=c-o;a=new Uint8Array(t),a.set(await pt(l,o,r)),a.set(await i.readUint8Array(e+r,t-r,n),r)}return i.lastDiskNumber=Math.max(s,i.lastDiskNumber),a}}class wt extends lt{constructor(e,t=4294967295){super();const n=this;let i,r,a;Object.assign(n,{diskNumber:0,diskOffset:0,size:0,maxSize:t,availableSize:t});const s=new WritableStream({async write(t){const{availableSize:s}=n;if(a)t.length>=s?(await o(t.slice(0,s)),await l(),n.diskOffset+=i.size,n.diskNumber++,a=null,await this.write(t.slice(s))):await o(t);else{const{value:s,done:o}=await e.next();if(o&&!s)throw new Error("Writer iterator completed too soon");i=s,i.size=0,i.maxSize&&(n.maxSize=i.maxSize),n.availableSize=n.maxSize,await _t(i),r=s.writable,a=r.getWriter(),await this.write(t)}},async close(){await a.ready,await l()}});async function o(e){const t=e.length;t&&(await a.ready,await a.write(e),i.size+=t,n.size+=t,n.availableSize-=t)}async function l(){r.size=i.size,await a.close()}Object.defineProperty(n,ot,{get:()=>s})}}async function _t(e,t){if(!e.init||e.initialized)return Promise.resolve();await e.init(t)}function bt(e){return Array.isArray(e)&&(e=new ht(e)),e instanceof ReadableStream&&(e={readable:e}),e}function pt(e,t,n,i){return e.readUint8Array(t,n,i)}const mt="\0☺☻♥♦♣♠•◘○◙♂♀♪♫☼►◄↕‼¶§▬↨↑↓→←∟↔▲▼ !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~⌂ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜ¢£¥₧ƒáíóúñѪº¿⌐¬½¼¡«»░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀αßΓπΣσµτΦΘΩδ∞φε∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²■ ".split(""),gt=256==mt.length;function yt(e,t){return t&&"cp437"==t.trim().toLowerCase()?function(e){if(gt){let t="";for(let n=0;nthis[t]=e[t]))}}const Lt="File format is not recognized",Pt="Zip64 extra field not found",Rt="Compression method not supported",Bt="Split zip file",It="utf-8",Nt="cp437",Vt=[[zt,g],[At,g],[Ut,g],[Dt,y]],qt={[y]:{getValue:tn,bytes:4},[g]:{getValue:nn,bytes:8}};class Ht{constructor(e,t={}){Object.assign(this,{reader:bt(e),options:t,config:T})}async*getEntriesGenerator(e={}){const t=this;let{reader:n}=t;const{config:i}=t;if(await _t(n),n.size!==S&&n.readUint8Array||(n=new ut(await new Response(n.readable).blob()),await _t(n)),n.size=0;e--)if(s[e]==a[0]&&s[e+1]==a[1]&&s[e+2]==a[2]&&s[e+3]==a[3])return{offset:r+e,buffer:s.slice(e,e+i).buffer}}}(n,101010256,n.size,v,1048560);if(!r){throw 134695760==tn(rn(await pt(n,0,4)))?new Error(Bt):new Error("End of central directory not found")}const a=rn(r);let s=tn(a,12),o=tn(a,16);const l=r.offset,c=en(a,20),u=l+v+c;let d=en(a,4);const f=n.lastDiskNumber||0;let h=en(a,6),w=en(a,8),_=0,b=0;if(o==g||s==g||w==y||h==y){const e=rn(await pt(n,r.offset-20,20));if(117853008==tn(e,0)){o=nn(e,8);let t=await pt(n,o,56,-1),i=rn(t);const a=r.offset-20-56;if(tn(i,0)!=k&&o!=a){const e=o;o=a,_=o-e,t=await pt(n,o,56,-1),i=rn(t)}if(tn(i,0)!=k)throw new Error("End of Zip64 central directory locator not found");d==y&&(d=tn(i,16)),h==y&&(h=tn(i,20)),w==y&&(w=nn(i,32)),s==g&&(s=nn(i,40)),o-=s}}if(o>=n.size&&(_=n.size-o-s-v,o=n.size-s-v),f!=d)throw new Error(Bt);if(o<0)throw new Error(Lt);let p=0,m=await pt(n,o,s,h),z=rn(m);if(s){const e=r.offset-s;if(tn(z,p)!=x&&o!=e){const t=o;o=e,_+=o-t,m=await pt(n,o,s,h),z=rn(m)}}const A=r.offset-o-(n.lastDiskOffset||0);if(s!=A&&A>=0&&(s=A,m=await pt(n,o,s,h),z=rn(m)),o<0||o>=n.size)throw new Error(Lt);const U=Qt(t,e,"filenameEncoding"),D=Qt(t,e,"commentEncoding");for(let r=0;ra.getData(e,j,t),p=g;const{onprogress:M}=e;if(M)try{await M(r+1,w,new Mt(a))}catch(e){}yield j}const E=Qt(t,e,"extractPrependedData"),T=Qt(t,e,"extractAppendedData");return E&&(t.prependedData=b>0?await pt(n,0,b):new Uint8Array),t.comment=c?await pt(n,l+v,c):new Uint8Array,T&&(t.appendedData=u>>8&255:d>>>24&255),signature:d,compressed:0!=l&&!g,encrypted:i.encrypted&&!g,useWebWorkers:Qt(i,n,"useWebWorkers"),useCompressionStream:Qt(i,n,"useCompressionStream"),transferStreams:Qt(i,n,"transferStreams"),checkPasswordOnly:D},config:c,streamOptions:{signal:U,size:v,onstart:T,onprogress:F,onend:O}};let W=0;try{({outputSize:W}=await rt({readable:z,writable:E},C))}catch(e){if(!D||e.message!=K)throw e}finally{const e=Qt(i,n,"preventClose");E.size+=W,e||E.locked||await E.getWriter().close()}return D?S:e.getData?e.getData():E}}function Zt(e,t,n){const i=e.rawBitFlag=en(t,n+2),r=!(1&~i),a=tn(t,n+6);Object.assign(e,{encrypted:r,version:en(t,n),bitFlag:{level:(6&i)>>1,dataDescriptor:!(8&~i),languageEncodingFlag:!(2048&~i)},rawLastModDate:a,lastModDate:Xt(a),filenameLength:en(t,n+22),extraFieldLength:en(t,n+24)})}async function Gt(e,t,n,i,r){const{rawExtraField:a}=t,s=t.extraField=new Map,o=rn(new Uint8Array(a));let l=0;try{for(;lt[e]==n));for(let r=0,a=0;r=5&&(a.push(Et),s.push(Tt));let o=1;a.forEach(((n,r)=>{if(e.data.length>=o+4){const a=tn(i,o);t[n]=e[n]=new Date(1e3*a);const l=s[r];e[l]=a}o+=4}))}(_,t,r),t.extraFieldExtendedTimestamp=_);const b=s.get(6534);b&&(t.extraFieldUSDZ=b)}async function Jt(e,t,n,i,r){const a=rn(e.data),s=new W;s.append(r[n]);const o=rn(new Uint8Array(4));o.setUint32(0,s.get(),!0);const l=tn(a,1);Object.assign(e,{version:$t(a,0),[t]:yt(e.data.subarray(5)),valid:!r.bitFlag.languageEncodingFlag&&l==tn(o,0)}),e.valid&&(i[t]=e[t],i[t+"UTF8"]=!0)}function Qt(e,t,n){return t[n]===S?e.options[n]:t[n]}function Xt(e){const t=(4294901760&e)>>16,n=65535&e;try{return new Date(1980+((65024&t)>>9),((480&t)>>5)-1,31&t,(63488&n)>>11,(2016&n)>>5,2*(31&n),0)}catch(e){}}function Yt(e){return new Date(Number(e/BigInt(1e4)-BigInt(116444736e5)))}function $t(e,t){return e.getUint8(t)}function en(e,t){return e.getUint16(t,!0)}function tn(e,t){return e.getUint32(t,!0)}function nn(e,t){return Number(e.getBigUint64(t,!0))}function rn(e){return new DataView(e.buffer)}F({Inflate:function(e){const t=new m,i=e&&e.chunkSize?Math.floor(2*e.chunkSize):131072,r=new Uint8Array(i);let a=!1;t.inflateInit(),t.next_out=r,this.append=function(e,s){const o=[];let l,c,u=0,d=0,f=0;if(0!==e.length){t.next_in_index=0,t.next_in=e,t.avail_in=e.length;do{if(t.next_out_index=0,t.avail_out=i,0!==t.avail_in||a||(t.next_in_index=0,a=!0),l=t.inflate(0),a&&l===n){if(0!==t.avail_in)throw new Error("inflating: bad input")}else if(0!==l&&1!==l)throw new Error("inflating: "+t.msg);if((a||1===l)&&t.avail_in===e.length)throw new Error("inflating: bad input");t.next_out_index&&(t.next_out_index===i?o.push(new Uint8Array(r)):o.push(r.subarray(0,t.next_out_index))),f+=t.next_out_index,s&&t.next_in_index>0&&t.next_in_index!=u&&(s(t.next_in_index),u=t.next_in_index)}while(t.avail_in>0||0===t.avail_out);return o.length>1?(c=new Uint8Array(f),o.forEach((function(e){c.set(e,d),d+=e.length}))):c=o[0]?new Uint8Array(o[0]):new Uint8Array,c}},this.flush=function(){t.inflateEnd()}}});export{ut as BlobReader,dt as BlobWriter,ft as TextWriter,Ht as ZipReader,F as configure}; diff --git a/web/vendor/foliate-js/view.js b/web/vendor/foliate-js/view.js new file mode 100644 index 00000000..5fd2b7c3 --- /dev/null +++ b/web/vendor/foliate-js/view.js @@ -0,0 +1,680 @@ +import * as CFI from './epubcfi.js' +import { TOCProgress, SectionProgress, PageProgress } from './progress.js' +import { Overlayer } from './overlayer.js' +import { textWalker } from './text-walker.js' + +const SEARCH_PREFIX = 'foliate-search:' + +const NOTE_PREFIX = 'foliate-note:' + +const isZip = async file => { + const arr = new Uint8Array(await file.slice(0, 4).arrayBuffer()) + return arr[0] === 0x50 && arr[1] === 0x4b && arr[2] === 0x03 && arr[3] === 0x04 +} + +const isPDF = async file => { + const arr = new Uint8Array(await file.slice(0, 5).arrayBuffer()) + return arr[0] === 0x25 + && arr[1] === 0x50 && arr[2] === 0x44 && arr[3] === 0x46 + && arr[4] === 0x2d +} + +const isCBZ = ({ name, type }) => + type === 'application/vnd.comicbook+zip' || name.endsWith('.cbz') + +const isFB2 = ({ name, type }) => + type === 'application/x-fictionbook+xml' || name.endsWith('.fb2') + +const isFBZ = ({ name, type }) => + type === 'application/x-zip-compressed-fb2' + || name.endsWith('.fb2.zip') || name.endsWith('.fbz') + +const makeZipLoader = async file => { + const { configure, ZipReader, BlobReader, TextWriter, BlobWriter } = + await import('./vendor/zip.js') + configure({ useWebWorkers: false }) + const reader = new ZipReader(new BlobReader(file)) + const entries = await reader.getEntries() + const map = new Map(entries.map(entry => [entry.filename, entry])) + const load = f => (name, ...args) => + map.has(name) ? f(map.get(name), ...args) : null + const loadText = load(entry => entry.getData(new TextWriter())) + const loadBlob = load((entry, type) => entry.getData(new BlobWriter(type))) + const getSize = name => map.get(name)?.uncompressedSize ?? 0 + return { entries, loadText, loadBlob, getSize } +} + +const getFileEntries = async entry => entry.isFile ? entry + : (await Promise.all(Array.from( + await new Promise((resolve, reject) => entry.createReader() + .readEntries(entries => resolve(entries), error => reject(error))), + getFileEntries))).flat() + +const makeDirectoryLoader = async entry => { + const entries = await getFileEntries(entry) + const files = await Promise.all( + entries.map(entry => new Promise((resolve, reject) => + entry.file(file => resolve([file, entry.fullPath]), + error => reject(error))))) + const map = new Map(files.map(([file, path]) => + [path.replace(entry.fullPath + '/', ''), file])) + const decoder = new TextDecoder() + const decode = x => x ? decoder.decode(x) : null + const getBuffer = name => map.get(name)?.arrayBuffer() ?? null + const loadText = async name => decode(await getBuffer(name)) + const loadBlob = name => map.get(name) + const getSize = name => map.get(name)?.size ?? 0 + return { loadText, loadBlob, getSize } +} + +export class ResponseError extends Error {} +export class NotFoundError extends Error {} +export class UnsupportedTypeError extends Error {} + +const fetchFile = async url => { + const res = await fetch(url) + if (!res.ok) throw new ResponseError( + `${res.status} ${res.statusText}`, { cause: res }) + return new File([await res.blob()], new URL(res.url).pathname) +} + +export const makeBook = async file => { + if (typeof file === 'string') file = await fetchFile(file) + let book + if (file.isDirectory) { + const loader = await makeDirectoryLoader(file) + const { EPUB } = await import('./epub.js') + book = await new EPUB(loader).init() + } + else if (!file.size) throw new NotFoundError('File not found') + else if (await isZip(file)) { + const loader = await makeZipLoader(file) + if (isCBZ(file)) { + const { makeComicBook } = await import('./comic-book.js') + book = makeComicBook(loader, file) + } + else if (isFBZ(file)) { + const { makeFB2 } = await import('./fb2.js') + const { entries } = loader + const entry = entries.find(entry => entry.filename.endsWith('.fb2')) + const blob = await loader.loadBlob((entry ?? entries[0]).filename) + book = await makeFB2(blob) + } + else { + const { EPUB } = await import('./epub.js') + book = await new EPUB(loader).init() + } + } + else if (await isPDF(file)) { + const { makePDF } = await import('./pdf.js') + book = await makePDF(file) + } + else { + const { isMOBI, MOBI } = await import('./mobi.js') + if (await isMOBI(file)) { + const fflate = await import('./vendor/fflate.js') + book = await new MOBI({ unzlib: fflate.unzlibSync }).open(file) + } + else if (isFB2(file)) { + const { makeFB2 } = await import('./fb2.js') + book = await makeFB2(file) + } + } + if (!book) throw new UnsupportedTypeError('File type not supported') + return book +} + +class CursorAutohider { + #timeout + #el + #check + #state + constructor(el, check, state = {}) { + this.#el = el + this.#check = check + this.#state = state + if (this.#state.hidden) this.hide() + this.#el.addEventListener('mousemove', ({ screenX, screenY }) => { + // check if it actually moved + if (screenX === this.#state.x && screenY === this.#state.y) return + this.#state.x = screenX, this.#state.y = screenY + this.show() + if (this.#timeout) clearTimeout(this.#timeout) + if (check()) this.#timeout = setTimeout(this.hide.bind(this), 1000) + }, false) + } + cloneFor(el) { + return new CursorAutohider(el, this.#check, this.#state) + } + hide() { + this.#el.style.cursor = 'none' + this.#state.hidden = true + } + show() { + this.#el.style.removeProperty('cursor') + this.#state.hidden = false + } +} + +class History extends EventTarget { + #arr = [] + #index = -1 + pushState(x) { + const last = this.#arr[this.#index] + if (last === x || last?.fraction && last.fraction === x.fraction) return + this.#arr[++this.#index] = x + this.#arr.length = this.#index + 1 + this.dispatchEvent(new Event('index-change')) + } + replaceState(x) { + const index = this.#index + this.#arr[index] = x + } + back() { + const index = this.#index + if (index <= 0) return + const detail = { state: this.#arr[index - 1] } + this.#index = index - 1 + this.dispatchEvent(new CustomEvent('popstate', { detail })) + this.dispatchEvent(new Event('index-change')) + } + forward() { + const index = this.#index + if (index >= this.#arr.length - 1) return + const detail = { state: this.#arr[index + 1] } + this.#index = index + 1 + this.dispatchEvent(new CustomEvent('popstate', { detail })) + this.dispatchEvent(new Event('index-change')) + } + get canGoBack() { + return this.#index > 0 + } + get canGoForward() { + return this.#index < this.#arr.length - 1 + } + clear() { + this.#arr = [] + this.#index = -1 + } +} + +const languageInfo = lang => { + if (!lang) return {} + try { + const canonical = Intl.getCanonicalLocales(lang)[0] + const locale = new Intl.Locale(canonical) + const isCJK = ['zh', 'ja', 'kr'].includes(locale.language) + const direction = (locale.getTextInfo?.() ?? locale.textInfo)?.direction + return { canonical, locale, isCJK, direction } + } catch (e) { + console.warn(e) + return {} + } +} + +export class View extends HTMLElement { + #root = this.attachShadow({ mode: 'open' }) + #sectionProgress + #tocProgress + #pageProgress + #cfiProgress + #searchResults = new Map() + #cursorAutohider = new CursorAutohider(this, () => + this.hasAttribute('autohide-cursor')) + isFixedLayout = false + lastLocation + history = new History() + constructor() { + super() + this.history.addEventListener('popstate', ({ detail }) => { + const resolved = this.resolveNavigation(detail.state) + this.renderer.goTo(resolved) + }) + } + async open(book) { + if (typeof book === 'string' + || typeof book.arrayBuffer === 'function' + || book.isDirectory) book = await makeBook(book) + this.book = book + this.language = languageInfo(book.metadata?.language) + + if (book.splitTOCHref && book.getTOCFragment) { + const ids = book.sections.map(s => s.id) + this.#sectionProgress = new SectionProgress(book.sections, 1500, 1600) + const splitHref = book.splitTOCHref.bind(book) + const getFragment = book.getTOCFragment.bind(book) + this.#tocProgress = new TOCProgress() + await this.#tocProgress.init({ + toc: book.toc ?? [], ids, splitHref, getFragment }) + this.#pageProgress = new TOCProgress() + await this.#pageProgress.init({ + toc: book.pageList ?? [], ids, splitHref, getFragment }) + } + this.#cfiProgress = new PageProgress(book, this.resolveNavigation.bind(this)) + + this.isFixedLayout = this.book.rendition?.layout === 'pre-paginated' + if (this.isFixedLayout) { + await import('./fixed-layout.js') + this.renderer = document.createElement('foliate-fxl') + } else { + await import('./paginator.js') + this.renderer = document.createElement('foliate-paginator') + } + this.renderer.setAttribute('exportparts', 'head,foot,filter,container') + this.renderer.addEventListener('load', e => this.#onLoad(e.detail)) + this.renderer.addEventListener('relocate', e => this.#onRelocate(e.detail)) + this.renderer.addEventListener('create-overlayer', e => + e.detail.attach(this.#createOverlayer(e.detail))) + this.renderer.open(book) + this.#root.append(this.renderer) + + if (book.sections.some(section => section.mediaOverlay)) { + const activeClass = book.media.activeClass + const playbackActiveClass = book.media.playbackActiveClass + this.mediaOverlay = book.getMediaOverlay() + let lastActive + this.mediaOverlay.addEventListener('highlight', e => { + const resolved = this.resolveNavigation(e.detail.text) + this.renderer.goTo(resolved) + .then(() => { + const { doc } = this.renderer.getContents() + .find(x => x.index = resolved.index) + const el = resolved.anchor(doc) + el.classList.add(activeClass) + if (playbackActiveClass) el.ownerDocument + .documentElement.classList.add(playbackActiveClass) + lastActive = new WeakRef(el) + }) + }) + this.mediaOverlay.addEventListener('unhighlight', () => { + const el = lastActive?.deref() + if (el) { + el.classList.remove(activeClass) + if (playbackActiveClass) el.ownerDocument + .documentElement.classList.remove(playbackActiveClass) + } + }) + } + } + close() { + this.renderer?.destroy() + this.renderer?.remove() + this.#sectionProgress = null + this.#tocProgress = null + this.#pageProgress = null + this.#cfiProgress = null + this.#searchResults = new Map() + this.lastLocation = null + this.history.clear() + this.tts = null + this.mediaOverlay = null + } + goToTextStart() { + return this.goTo(this.book.landmarks + ?.find(m => m.type.includes('bodymatter') || m.type.includes('text')) + ?.href ?? this.book.sections.findIndex(s => s.linear !== 'no')) + } + async init({ lastLocation, showTextStart }) { + const resolved = lastLocation ? this.resolveNavigation(lastLocation) : null + if (resolved) { + await this.renderer.goTo(resolved) + this.history.pushState(lastLocation) + } + else if (showTextStart) await this.goToTextStart() + else { + this.history.pushState(0) + await this.next() + } + } + #emit(name, detail, cancelable) { + return this.dispatchEvent(new CustomEvent(name, { detail, cancelable })) + } + #onRelocate({ reason, range, index, fraction, size }) { + const progress = this.#sectionProgress?.getProgress(index, fraction, size) ?? {} + const tocItem = this.#tocProgress?.getProgress(index, range) + const pageItem = this.#pageProgress?.getProgress(index, range) + const cfi = this.getCFI(index, range) + this.lastLocation = { ...progress, tocItem, pageItem, cfi, range } + if (reason === 'snap' || reason === 'page' || reason === 'scroll') + this.history.replaceState(cfi) + this.#emit('relocate', this.lastLocation) + } + #onLoad({ doc, index }) { + // set language and dir if not already set + doc.documentElement.lang ||= this.language.canonical ?? '' + if (!this.language.isCJK) + doc.documentElement.dir ||= this.language.direction ?? '' + + this.#handleLinks(doc, index) + this.#cursorAutohider.cloneFor(doc.documentElement) + + this.#emit('load', { doc, index }) + } + #handleLinks(doc, index) { + const { book } = this + const section = book.sections[index] + doc.addEventListener('click', e => { + const a = e.target.closest('a[href]') + if (!a) return + e.preventDefault() + const href_ = a.getAttribute('href') + const href = section?.resolveHref?.(href_) ?? href_ + if (book?.isExternal?.(href)) + Promise.resolve(this.#emit('external-link', { a, href }, true)) + .then(x => x ? globalThis.open(href, '_blank') : null) + .catch(e => console.error(e)) + else { + let internalHref = href + if (!book.resolveHref(href)) { + const hashIndex = href_.indexOf('#') + if (hashIndex >= 0) { + const hash = href_.slice(hashIndex) + internalHref = section?.resolveHref?.(hash) ?? href + } + } + Promise.resolve(this.#emit('link', { a, href: internalHref }, true)) + .then(x => x ? this.goTo(internalHref) : null) + .catch(e => console.error(e)) + } + }) + } + async addAnnotation(annotation, remove) { + const { value } = annotation + if (value.startsWith(SEARCH_PREFIX)) { + const cfi = value.replace(SEARCH_PREFIX, '') + const { index, anchor } = await this.resolveNavigation(cfi) + const obj = this.#getOverlayer(index) + if (obj) { + const { overlayer, doc } = obj + if (remove) { + overlayer.remove(value) + return + } + const range = doc ? anchor(doc) : anchor + if (range) overlayer.add(value, range, Overlayer.outline) + } + return + } else if (value.startsWith(NOTE_PREFIX)) { + const cfi = value.replace(NOTE_PREFIX, '') + const { index, anchor } = await this.resolveNavigation(cfi) + const obj = this.#getOverlayer(index) + if (obj) { + const { overlayer, doc } = obj + if (remove) { + overlayer.remove(value) + return + } + const range = doc ? anchor(doc) : anchor + if (range) { + const draw = (func, opts) => overlayer.add(value, range, func, opts) + this.#emit('draw-annotation', { draw, annotation, doc, range }) + } + } + return + } + const { index, anchor } = await this.resolveNavigation(value) + const obj = this.#getOverlayer(index) + if (obj) { + const { overlayer, doc } = obj + overlayer.remove(value) + if (!remove) { + const range = doc ? anchor(doc) : anchor + if (range) { + const draw = (func, opts) => overlayer.add(value, range, func, opts) + this.#emit('draw-annotation', { draw, annotation, doc, range }) + } + } + } + const label = this.#tocProgress?.getProgress(index)?.label ?? '' + return { index, label } + } + deleteAnnotation(annotation) { + return this.addAnnotation(annotation, true) + } + #getOverlayer(index) { + return this.renderer.getContents() + .find(x => x.index === index && x.overlayer) + } + #createOverlayer({ doc, index }) { + const overlayer = new Overlayer(doc) + doc.addEventListener('click', e => { + const [value, range, rect] = overlayer.hitTest(e) + if (value && !value.startsWith(SEARCH_PREFIX)) { + this.#emit('show-annotation', { value, index, range, rect }) + } + }, false) + + let lastHitTestTime = 0 + const THROTTLE_MS = 200 + const isAndroid = /Android/i.test(navigator.userAgent) + + doc.addEventListener('mousemove', (e) => { + if (isAndroid) return + const now = performance.now() + if (now - lastHitTestTime < THROTTLE_MS) return + lastHitTestTime = now + const [value] = overlayer.hitTest(e) + if (value && !value.startsWith(SEARCH_PREFIX)) { + doc.body.style.cursor = 'pointer' + } else { + doc.body.style.cursor = '' + } + }) + + const list = this.#searchResults.get(index) + if (list) for (const item of list) this.addAnnotation(item) + + this.#emit('create-overlay', { index }) + return overlayer + } + async showAnnotation(annotation) { + const { value } = annotation + const resolved = await this.goTo(value) + if (resolved) { + const { index, anchor } = resolved + const { doc } = this.#getOverlayer(index) + const range = anchor(doc) + this.#emit('show-annotation', { value, index, range }) + } + } + getCFI(index, range) { + const baseCFI = this.book.sections[index].cfi ?? CFI.fake.fromIndex(index) + if (!range) return baseCFI + return CFI.joinIndir(baseCFI, CFI.fromRange(range)) + } + resolveCFI(cfi) { + if (this.book.resolveCFI) + return this.book.resolveCFI(cfi) + else { + const parts = CFI.parse(cfi) + const index = CFI.fake.toIndex((parts.parent ?? parts).shift()) + const anchor = doc => CFI.toRange(doc, parts) + return { index, anchor } + } + } + resolveNavigation(target) { + try { + if (typeof target === 'number') return { index: target } + if (typeof target.fraction === 'number') { + const [index, anchor] = this.#sectionProgress.getSection(target.fraction) + return { index, anchor } + } + if (CFI.isCFI.test(target)) return this.resolveCFI(target) + return this.book.resolveHref(target) + } catch (e) { + console.error(e) + console.error(`Could not resolve target ${target}`) + } + } + async goTo(target) { + const resolved = this.resolveNavigation(target) + try { + await this.renderer.goTo(resolved) + this.history.pushState(target) + return resolved + } catch(e) { + console.error(e) + console.error(`Could not go to ${target}`) + } + } + async goToFraction(frac) { + const [index, anchor] = this.#sectionProgress.getSection(frac) + await this.renderer.goTo({ index, anchor }) + this.history.pushState({ fraction: frac }) + } + async select(target) { + try { + const obj = await this.resolveNavigation(target) + await this.renderer.goTo({ ...obj, select: true }) + this.history.pushState(target) + } catch(e) { + console.error(e) + console.error(`Could not go to ${target}`) + } + } + deselect() { + for (const { doc } of this.renderer.getContents()) + doc.defaultView.getSelection().removeAllRanges() + } + getSectionFractions() { + return (this.#sectionProgress?.sectionFractions ?? []) + .map(x => x + Number.EPSILON) + } + getProgressOf(index, range) { + const tocItem = this.#tocProgress?.getProgress(index, range) + const pageItem = this.#pageProgress?.getProgress(index, range) + return { tocItem, pageItem } + } + async getCFIProgress(cfi) { + const progress = await this.#cfiProgress?.getProgress(cfi) + if (!progress || progress.index === -1) return null + return this.#sectionProgress?.getProgress(progress.index, progress.fraction) + } + async getTOCItemOf(target) { + try { + const { index, anchor } = await this.resolveNavigation(target) + const doc = await this.book.sections[index].createDocument() + const frag = anchor(doc) + const isRange = frag instanceof Range + const range = isRange ? frag : doc.createRange() + if (!isRange) range.selectNodeContents(frag) + return this.#tocProgress?.getProgress(index, range) + } catch(e) { + console.error(e) + console.error(`Could not get ${target}`) + } + } + async prev(distance) { + await this.renderer.prev(distance) + } + async next(distance) { + await this.renderer.next(distance) + } + async pan(dx, dy) { + await this.renderer.pan(dx, dy) + } + isOverflowX() { + return this.renderer.isOverflowX + } + isOverflowY() { + return this.renderer.isOverflowY + } + goLeft() { + return this.book.dir === 'rtl' ? this.next() : this.prev() + } + goRight() { + return this.book.dir === 'rtl' ? this.prev() : this.next() + } + async * #searchSection(matcher, query, index) { + const doc = await this.book.sections[index].createDocument() + for (const { range, excerpt } of matcher(doc, query)) + yield { cfi: this.getCFI(index, range), excerpt } + } + async * #searchBook(matcher, query) { + const { sections } = this.book + for (const [index, { createDocument }] of sections.entries()) { + if (!createDocument) continue + const doc = await createDocument() + const subitems = Array.from(matcher(doc, query), ({ range, excerpt }) => + ({ cfi: this.getCFI(index, range), excerpt })) + const progress = (index + 1) / sections.length + yield { progress } + if (subitems.length) yield { index, subitems } + } + } + async * search(opts) { + this.clearSearch() + const { searchMatcher } = await import('./search.js') + const { sections } = this.book + const { query, index, results } = opts + const matcher = searchMatcher(textWalker, + { defaultLocale: this.language, ...opts }) + + const iter = results?.length + ? (async function* () { + for (const result of results) { + if (result.subitems) { + const progress = (result.index + 1) / sections.length + yield { progress } + yield { index: result.index, subitems: result.subitems } + } else { + yield { cfi: result.cfi, excerpt: result.excerpt } + } + } + })() + : index != null + ? this.#searchSection(matcher, query, index) + : this.#searchBook(matcher, query) + + const list = [] + this.#searchResults.set(index, list) + + for await (const result of iter) { + if (result.subitems){ + const list = result.subitems + .map(({ cfi }) => ({ value: SEARCH_PREFIX + cfi })) + this.#searchResults.set(result.index, list) + for (const item of list) this.addAnnotation(item) + yield { + index: result.index, + label: this.#tocProgress?.getProgress(result.index)?.label ?? '', + subitems: result.subitems, + } + } + else { + if (result.cfi) { + const item = { value: SEARCH_PREFIX + result.cfi } + list.push(item) + this.addAnnotation(item) + } + yield result + } + } + yield 'done' + } + clearSearch() { + for (const list of this.#searchResults.values()) + for (const item of list) this.deleteAnnotation(item) + this.#searchResults.clear() + } + async initTTS(granularity = 'word', nodeFilter, highlighter) { + const contents = this.renderer.getContents() + const primaryIndex = this.renderer.primaryIndex + const primary = contents.find(x => x.index === primaryIndex) ?? contents[0] + const doc = primary?.doc + if (!doc) return + if (this.tts && this.tts.doc === doc) return + const { TTS } = await import('./tts.js') + this.tts = new TTS(doc, textWalker, nodeFilter, highlighter || (range => + this.renderer.scrollToAnchor(range, true)), granularity) + } + startMediaOverlay() { + const contents = this.renderer.getContents() + const primaryIndex = this.renderer.primaryIndex + const primary = contents.find(x => x.index === primaryIndex) ?? contents[0] + const { index } = primary ?? {} + return this.mediaOverlay.start(index) + } +} + +customElements.define('foliate-view', View) diff --git a/web/vite.config.ts b/web/vite.config.ts index 0c74de36..d61c1bd8 100644 --- a/web/vite.config.ts +++ b/web/vite.config.ts @@ -17,6 +17,7 @@ export default defineConfig(({ mode }) => { resolve: { alias: { "@": path.resolve(__dirname, "./src"), + "@pdfjs": path.resolve(__dirname, "./public/vendor/pdfjs"), }, }, server: {