diff --git a/.gitattributes b/.gitattributes
index a602ff7..5fcda59 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1 +1,20 @@
-build/ export-ignore
+# Export ignore for release ZIP
+# Development and build files
+/build export-ignore
+/tests export-ignore
+/.github export-ignore
+/.tools export-ignore
+
+# Configuration files
+/.gitattributes export-ignore
+/.gitignore export-ignore
+/.php-cs-fixer.dist.php export-ignore
+/.php-cs-fixer.cache export-ignore
+/composer.json export-ignore
+/composer.lock export-ignore
+/phpstan.neon export-ignore
+/psalm.xml export-ignore
+
+# Documentation (optional - entfernen falls README im Release sein soll)
+# /README.md export-ignore
+# /CHANGELOG.md export-ignore
diff --git a/.github/workflows/code-style.yml b/.github/workflows/code-style.yml
new file mode 100644
index 0000000..4a07a4d
--- /dev/null
+++ b/.github/workflows/code-style.yml
@@ -0,0 +1,53 @@
+name: PHP-CS-Fixer
+
+# Erstelle eine `composer.json` im Repo mit folgendem Inhalt
+# {
+# "require-dev": {
+# "friendsofphp/php-cs-fixer": "^3.0"
+# },
+# "scripts": {
+# "cs-fix": "php-cs-fixer fix"
+# }
+# }
+
+on:
+ push:
+ branches: [ master, main ]
+ pull_request:
+ branches: [ master, main ]
+
+permissions:
+ contents: read
+
+jobs:
+ code-style:
+ if: github.event.pull_request.draft == false
+
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write # for Git to git apply
+
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: '8.2'
+ extensions: gd, intl, pdo_mysql
+ coverage: none # disable xdebug, pcov
+
+ # install dependencies from composer.json
+ - name: Install test dependencies
+ env:
+ COMPOSER: composer.json
+ run: composer install --prefer-dist --no-progress
+
+ # run php-cs-fixer
+ - name: Run PHP CS Fixer
+ run: composer cs-fix
+
+ # commit and push fixed files
+ - uses: stefanzweifel/git-auto-commit-action@v4
+ with:
+ commit_message: Apply php-cs-fixer changes
diff --git a/.php-cs-fixer.cache b/.php-cs-fixer.cache
new file mode 100644
index 0000000..713ad00
--- /dev/null
+++ b/.php-cs-fixer.cache
@@ -0,0 +1 @@
+{"php":"8.4.11","version":"3.87.1:v3.87.1#2f5170365e2a422d0c5421f9c8818b2c078105f6","indent":" ","lineEnding":"\n","rules":{"array_indentation":true,"array_syntax":true,"cast_spaces":true,"concat_space":{"spacing":"one"},"function_declaration":true,"method_argument_space":{"on_multiline":"ignore"},"new_with_parentheses":{"anonymous_class":false},"single_line_empty_body":true,"single_space_around_construct":true,"trailing_comma_in_multiline":{"after_heredoc":true,"elements":["arguments","arrays","match","parameters"]},"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_anonymous_functions":true,"allow_single_line_empty_anonymous_classes":true},"class_definition":{"single_line":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"no_blank_lines_after_class_opening":true,"no_extra_blank_lines":{"tokens":["attribute","case","continue","curly_brace_block","default","extra","parenthesis_brace_block","square_brace_block","switch","throw","use"]},"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait","case","constant_public","constant_protected","constant_private","property","construct","phpunit","method"]},"ordered_imports":{"imports_order":["class","function","const"],"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":true,"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":true,"single_line_after_imports":true,"spaces_inside_parentheses":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"no_unreachable_default_argument_value":true,"align_multiline_comment":true,"backtick_to_shell_exec":true,"class_attributes_separation":{"elements":{"method":"one"}},"class_reference_name_casing":true,"clean_namespace":true,"declare_parentheses":true,"echo_tag_syntax":{"format":"short"},"empty_loop_body":{"style":"braces"},"empty_loop_condition":true,"fully_qualified_strict_types":{"import_symbols":true},"general_phpdoc_tag_rename":{"replacements":{"inheritDocs":"inheritDoc"}},"global_namespace_import":{"import_constants":true,"import_functions":true,"import_classes":true},"include":true,"increment_style":true,"integer_literal_case":true,"lambda_not_used_import":true,"linebreak_after_opening_tag":true,"magic_constant_casing":true,"magic_method_casing":true,"native_function_casing":true,"native_type_declaration_casing":true,"no_alias_language_construct_call":true,"no_binary_string":true,"no_empty_comment":true,"no_empty_phpdoc":true,"no_empty_statement":true,"no_leading_namespace_whitespace":true,"no_mixed_echo_print":true,"no_multiline_whitespace_around_double_arrow":true,"no_null_property_initialization":true,"no_short_bool_cast":true,"no_singleline_whitespace_before_semicolons":true,"no_spaces_around_offset":true,"no_superfluous_phpdoc_tags":{"allow_mixed":true,"remove_inheritdoc":true},"no_trailing_comma_in_singleline":true,"no_unneeded_braces":{"namespaces":true},"no_unneeded_control_parentheses":{"statements":["break","clone","continue","echo_print","others","return","switch_case","yield","yield_from"]},"no_unneeded_import_alias":true,"no_unset_cast":true,"no_unused_imports":true,"no_useless_concat_operator":true,"no_useless_nullsafe_operator":true,"no_whitespace_before_comma_in_array":{"after_heredoc":true},"normalize_index_brace":true,"nullable_type_declaration_for_default_null_value":true,"object_operator_without_whitespace":true,"operator_linebreak":{"only_booleans":true},"php_unit_fqcn_annotation":true,"php_unit_method_casing":true,"phpdoc_annotation_without_dot":true,"phpdoc_indent":true,"phpdoc_inline_tag_normalizer":true,"phpdoc_no_access":true,"phpdoc_no_alias_tag":true,"phpdoc_no_useless_inheritdoc":true,"phpdoc_order":true,"phpdoc_return_self_reference":true,"phpdoc_scalar":true,"phpdoc_single_line_var_spacing":true,"phpdoc_summary":true,"phpdoc_tag_type":{"tags":{"inheritDoc":"inline"}},"phpdoc_trim":true,"phpdoc_trim_consecutive_blank_line_separation":true,"phpdoc_types":true,"phpdoc_types_order":{"null_adjustment":"always_last","sort_algorithm":"none"},"phpdoc_var_annotation_correct_order":true,"phpdoc_var_without_name":true,"simple_to_complex_string_variable":true,"single_line_comment_spacing":true,"single_line_comment_style":{"comment_types":["hash"]},"single_quote":true,"space_after_semicolon":{"remove_in_empty_for_expressions":true},"standardize_increment":true,"standardize_not_equals":true,"switch_continue_to_break":true,"trim_array_spaces":true,"type_declaration_spaces":true,"whitespace_after_comma_in_array":true,"yoda_style":true,"nullable_type_declaration":true,"ordered_types":{"null_adjustment":"always_last","sort_algorithm":"none"},"types_spaces":true,"array_push":true,"combine_nested_dirname":true,"dir_constant":true,"ereg_to_preg":true,"error_suppression":true,"fopen_flag_order":true,"fopen_flags":{"b_mode":false},"function_to_constant":true,"get_class_to_class_keyword":true,"implode_call":true,"is_null":true,"logical_operators":true,"long_to_shorthand_operator":true,"modernize_strpos":true,"modernize_types_casting":true,"native_constant_invocation":{"scope":"namespaced","strict":false},"native_function_invocation":{"include":["@compiler_optimized"],"scope":"namespaced","strict":true},"no_alias_functions":{"sets":["@all"]},"no_homoglyph_names":true,"no_php4_constructor":true,"no_unneeded_final_method":true,"no_useless_sprintf":true,"non_printable_character":true,"ordered_traits":true,"php_unit_construct":true,"php_unit_mock_short_will_return":true,"php_unit_set_up_tear_down_visibility":true,"php_unit_test_annotation":true,"self_accessor":true,"set_type_to_cast":true,"string_length_to_empty":true,"string_line_ending":true,"ternary_to_elvis_operator":true,"pow_to_exponentiation":true,"octal_notation":true,"assign_null_coalescing_to_coalesce_equal":true,"heredoc_indentation":true,"list_syntax":true,"ternary_to_null_coalescing":true,"random_api_migration":{"replacements":{"mt_rand":"random_int","rand":"random_int"}},"php_unit_data_provider_static":{"force":true},"php_unit_assert_new_names":true,"php_unit_expectation":{"target":"8.4"},"php_unit_namespaced":{"target":"6.0"},"php_unit_dedicate_assert":{"target":"5.6"},"php_unit_mock":{"target":"5.5"},"php_unit_no_expectation_annotation":{"target":"4.3"},"php_unit_dedicate_assert_internal_type":{"target":"7.5"},"comment_to_phpdoc":true,"heredoc_to_nowdoc":true,"multiline_comment_opening_closing":true,"multiline_promoted_properties":{"keep_blank_lines":true},"no_superfluous_elseif":true,"no_useless_else":true,"no_useless_return":true,"php_unit_internal_class":true,"php_unit_test_case_static_method_calls":{"call_type":"self"},"phpdoc_array_type":true,"static_lambda":true,"string_implicit_backslashes":{"single_quoted":"ignore"},"PhpCsFixerCustomFixers\/phpdoc_single_line_var":true,"Redaxo\/no_semicolon_before_closing_tag":true,"Redaxo\/statement_indentation":true},"hashes":{"lang\/translations.php":"f629043bfd30ac33b48651256579fcf7","lib\/Translator.php":"d7f446ca0c8dfdfaaf6c3ed908b8a41e","lib\/OembedParser.php":"1bb7945b9f2dee0d3f9b3faab5582b68","lib\/AssetHelper.php":"454be1ce6a10da74ec04e31ab7ac7635","lib\/VidstackPlayer.php":"a1198133d9bb64d873f2e9b5434536da","lib\/Utilities.php":"d91ddf047e85ba2b5aa230ef1ae45201","lib\/PlatformDetector.php":"01e93a5eb448abf67cf725441e366fb4","lib\/BackendIntegration.php":"7522a270b2fa92c0cbd104e02a2c92cb","boot.php":"e8090f9b7dc05adf2fc3eddfcdf504ae"}}
\ No newline at end of file
diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php
new file mode 100644
index 0000000..034718e
--- /dev/null
+++ b/.php-cs-fixer.dist.php
@@ -0,0 +1,12 @@
+in(__DIR__)
+ ->exclude('vendor')
+;
+
+return (new Redaxo\PhpCsFixerConfig\Config())
+ ->setFinder($finder)
+ ;
diff --git a/.tools/bootstrap.php b/.tools/bootstrap.php
new file mode 100644
index 0000000..56c33b7
--- /dev/null
+++ b/.tools/bootstrap.php
@@ -0,0 +1,13 @@
+';
-echo ' ';
+```bash
+# Via Composer (recommended)
+composer require friendsofredaxo/vidstack
-// JavaScript einbinden
-echo '';
-echo '';
+# Or via REDAXO Installer
+# Search for "Vidstack Player" in the addon installer
```
-Was passiert hier? Wir benutzen `rex_url::addonAssets()`, um die richtigen URLs für unsere Assets zu generieren. Das ist wie ein Zauberstab, der immer auf die korrekten Dateien in deinem REDAXO-Setup zeigt, egal wo sie sich versteckt haben.
-
-Die `vidstack.css` und `vidstack.js` sind die Hauptdarsteller - sie bringen den Video-Player zum Laufen. Die `*_helper`-Dateien sind wie die fleißigen Backstage-Helfer. Sie kümmern sich um Extras wie die DSGVO-Abfrage und andere nützliche Funktionen.
-
-Übrigens: Wenn du nur die `generate()`-Methode verwendest und auf den ganzen Schnickschnack wie Consent-Abfragen verzichten möchtest, kannst du die Helper-Dateien weglassen. Aber für das volle Programm mit `generateFull()` braucht man alle vier Dateien.
+## Quick Start
-So, jetzt aber! Dein REDAXO ist jetzt bereit, Videos mit Style zu servieren. 🎬🍿
-
-### Source Sizes für Desktop/Mobile Videos
-
-Mit dem Vidstack-Addon können Sie verschiedene Video-Auflösungen für Desktop und Mobile bereitstellen:
+### Basic Video
```php
setResponsiveSources('video-1080p.mp4', 'video-480p.mp4');
-echo $video->generateFull();
-
-// Mit benutzerdefinierten Auflösungen
-$video = new Video('video-desktop.mp4', 'Custom Responsive Video');
-$video->setResponsiveSources(
- 'video-high.mp4',
- 'video-low.mp4',
- [2560, 1440], // Desktop: 2K
- [960, 540] // Mobile: Mobile HD
-);
-echo $video->generateFull();
-
-// Mit Auflösungspresets
-$video = new Video('video.mp4', 'Preset Video');
-$video->setResponsiveSourcesWithPresets('video-2k.mp4', 'video-mobile.mp4', '2k', 'mobile_hd');
-echo $video->generateFull();
-
-// Automatische Erstellung aus Dateinamen-Pattern
-$video = new Video('produktvideo.mp4', 'Produktvideo');
-if ($video->createAutoSources('produktvideo')) {
- // Sucht automatisch nach: produktvideo-1080p.mp4, produktvideo-720p.mp4, produktvideo-480p.mp4
- echo $video->generateFull();
-}
-
-// Mehrere Qualitätsstufen mit manueller Kontrolle
-$video = new Video('video.mp4', 'Multi-Quality Video');
-$video->setSources([
- ['src' => 'video-4k.mp4', 'width' => 3840, 'height' => 2160, 'type' => 'video/mp4'],
- ['src' => 'video-1080p.mp4', 'width' => 1920, 'height' => 1080, 'type' => 'video/mp4'],
- ['src' => 'video-720p.mp4', 'width' => 1280, 'height' => 720, 'type' => 'video/mp4'],
- ['src' => 'video-480p.mp4', 'width' => 854, 'height' => 480, 'type' => 'video/mp4']
-]);
-echo $video->generateFull();
-```
+use FriendsOfRedaxo\VidstackPlayer\VidstackPlayer;
-**Verfügbare Auflösungspresets:**
-- `4k` (3840×2160), `2k` (2560×1440), `1080p` (1920×1080)
-- `720p` (1280×720), `480p` (854×480), `360p` (640×360)
-- `mobile_hd` (960×540), `mobile_sd` (640×360), `tablet` (1024×576)
+$player = (new VidstackPlayer('video.mp4'))
+ ->title('My Video')
+ ->poster('poster.jpg')
+ ->attributes(['controls' => true]);
-**Wie es funktioniert:** Der Browser wählt automatisch die beste verfügbare Quelle basierend auf Gerätegröße und Netzwerkbedingungen. Die Quellen werden nach Qualität sortiert ausgegeben (höchste zuerst). Das Sorting wird gecacht für bessere Performance.
+echo $player->render();
+```
-### Grundlegende Verwendung
+### Basic Audio
```php
-generateFull();
-
-// Vimeo-Video
-$vimeoVideo = new Video('https://vimeo.com/148751763', 'Vimeo-Beispiel');
-echo $vimeoVideo->generateFull();
+$player = (new VidstackPlayer('audio.mp3'))
+ ->title('Podcast Episode 1')
+ ->attributes(['controls' => true]);
-// Lokales Video
-$localVideo = new Video('video.mp4', 'Eigenes Video');
-echo $localVideo->generate();
-
-// Externes Video
-$externalVideo = new Video('https://somedomain.tld/video.mp4', 'Eigenes Video');
-echo $externalVideo->generate();
+echo $player->render();
```
-### Grundlegende Beispiele für den Alltag
-
-#### Video mit Poster-Bild und Titel
+### YouTube Video
```php
-title('Never Gonna Give You Up');
-// Video aus dem Medienpool mit Poster-Bild
-$video = new Video('mein_video.mp4', 'Mein tolles Video mit Vorschaubild');
-$video->setPoster('vorschaubild.jpg', 'Beschreibung des Vorschaubilds');
-echo $video->generate();
+echo $player->render();
```
-#### Video mit Untertiteln (VTT-Format)
+## Fluent API Reference
-```php
-addSubtitle('untertitel_de.vtt', 'captions', 'Deutsch', 'de', true); // Standard-Untertitel
-$video->addSubtitle('untertitel_en.vtt', 'captions', 'Englisch', 'en');
-echo $video->generate();
+```php
+$player = (new VidstackPlayer('source.mp4'))
+ ->title('Title') // Set player title
+ ->lang('de') // Set language (default: 'de')
+ ->poster('poster.jpg', 'Alt text') // Set poster image
+ ->aspectRatio('16/9') // Set aspect ratio
+ ->thumbnails('thumbs.vtt') // Set thumbnail track
+ ->attributes(['controls' => true]) // Set multiple attributes
+ ->attr('muted', true) // Set single attribute
+ ->render(); // Generate HTML
```
-#### Barrierefreies Video mit Beschreibungen
+### Subtitles & Captions
```php
-setA11yContent(
- 'Das Video zeigt Schritt für Schritt, wie REDAXO installiert wird. Beginnend mit dem Download bis zur ersten Anmeldung im Backend.',
- 'https://beispiel.de/redaxo-installation-text.html' // Alternative Text-Version
+$player->track(
+ src: 'subtitles.vtt',
+ label: 'Deutsch',
+ srclang: 'de',
+ kind: 'subtitles', // subtitles|captions|descriptions|chapters|metadata
+ default: true
);
-
-// Kapitelmarken hinzufügen
-$video->addSubtitle('chapters.vtt', 'chapters', 'Kapitel', 'de');
-
-echo $video->generateFull();
```
-#### YouTube mit DSGVO-konformer Zwei-Klick-Lösung
+### Multiple Sources (Adaptive Quality)
```php
- Für das Frontend
-
-// generateFull() erzeugt automatisch den DSGVO-konformen Platzhalter für YouTube und Vimeo
-echo $video->generateFull();
+$player->multipleSources([
+ ['src' => 'video-1080p.mp4', 'width' => 1920, 'height' => 1080, 'type' => 'video/mp4'],
+ ['src' => 'video-720p.mp4', 'width' => 1280, 'height' => 720, 'type' => 'video/mp4'],
+ ['src' => 'video-480p.mp4', 'width' => 854, 'height' => 480, 'type' => 'video/mp4']
+]);
```
-#### Video mit Vorschaubildern für die Zeitleiste (VTT-Format)
-
-```php
-setThumbnails('thumbnails.vtt');
+## Frontend Integration
-// Beispiel für eine thumbnails.vtt Datei:
-// WEBVTT
-//
-// 00:00:00.000 --> 00:00:05.000
-// thumbnails/img1.jpg
-//
-// 00:00:05.000 --> 00:00:10.000
-// thumbnails/img2.jpg
-
-echo $video->generate();
-```
-
-#### Audio-Player
+### Include Assets
```php
generate();
+use FriendsOfRedaxo\VidstackPlayer\AssetHelper;
+?>
+
+
+
+
+
+
+
+
+
+
+
```
-## � FFmpeg-Integration (Backend-Funktionalität)
-
-Wenn das [FFmpeg-AddOn](https://github.com/FriendsOfREDAXO/ffmpeg) installiert und aktiv ist, zeigt Vidstack automatisch detaillierte Video-Informationen im Medienpool an.
+### Asset Helper Options
-### Was wird angezeigt?
-
-Im Medienpool wird unter jedem Video automatisch eine kompakte Informationsbox eingeblendet mit:
+```php
+// CSS with custom attributes
+echo AssetHelper::getCss(['media' => 'screen'], cachebuster: true);
-- **Auflösung**: Breite × Höhe in Pixeln (z.B. 1920 × 1080 px) und Seitenverhältnis (z.B. 16:9)
-- **Video-Codec**: Komprimierungsformat (z.B. H264, VP9, AV1)
-- **Dauer**: Formatierte Videolänge (z.B. 05:42 oder 01:23:45)
-- **Dateigröße**: Größe der Videodatei (z.B. 45.2 MB)
-- **Bitrate**: Datenrate des Videos (z.B. 2.4 Mbps) - nur bei aussagekräftigen Werten
+// JS with defer (recommended for performance)
+echo AssetHelper::getJs(defer: true);
-### Voraussetzungen
+// JS with async (use with caution)
+echo AssetHelper::getJs(async: true);
-```bash
-# FFmpeg muss auf dem Server installiert sein
-ffmpeg -version
+// Custom attributes
+echo AssetHelper::getJs(
+ defer: true,
+ attributes: ['type' => 'module', 'crossorigin' => 'anonymous'],
+ cachebuster: true
+);
-# FFmpeg-AddOn in REDAXO installieren und aktivieren
+// Disable cache-busting
+echo AssetHelper::getCss(cachebuster: false);
```
-### Funktionsweise
-
-Die Integration erfolgt vollautomatisch:
-
-1. **Automatische Erkennung**: Vidstack prüft beim Laden einer Video-Datei im Medienpool, ob das FFmpeg-AddOn verfügbar ist
-2. **Video-Analyse**: Falls verfügbar, werden die Video-Metadaten über die FFmpeg VideoInfo-Klasse ausgelesen
-3. **Anzeige**: Die Informationen werden kompakt unter dem Video-Player dargestellt
-4. **Action-Buttons**: Direkte Verlinkung zu FFmpeg-Tools für weitere Bearbeitung
-
-### Action-Buttons
-
-Unter den Video-Informationen werden praktische Buttons angezeigt:
-
-- **🔧 Trimmen**: Öffnet den FFmpeg-Trimmer zum Schneiden des Videos
-- **📦 Optimieren**: Startet die Komprimierung für Web-optimierte Versionen
-- **ℹ️ Details**: Zeigt ausführliche technische Video-Informationen
-
-Die Buttons führen direkt zu den entsprechenden FFmpeg-Tools und übertragen automatisch den Dateinamen.
-
-### Ohne FFmpeg-AddOn
+## Advanced Examples
-Ohne das FFmpeg-AddOn funktioniert Vidstack weiterhin normal, zeigt aber keine technischen Video-Informationen an.
+### Complete Video Setup
-## �🛠 Die Class
-
-### Konstruktor
```php
-__construct($source, $title = '', $lang = 'de'): void
-```
-- `$source`: URL oder Pfad zum Video (Pflicht)
-- `$title`: Titel des Videos (Optional)
-- `$lang`: Sprachcode (Optional, Standard: 'de')
-
-### Methoden
-- `setAttributes(array $attributes): void`: Zusätzliche Player-Attribute
-- `setA11yContent($description, $alternativeUrl = ''): void`: Barrierefreiheits-Infos
-- `setThumbnails($thumbnailsUrl): void`: Thumbnail-Vorschaubilder (VTT-Format)
-- `setPoster($posterSrc, $posterAlt): void`: Poster-Bild für das Video setzen
-- `addSubtitle($src, $kind, $label, $lang, $default = false): void`: Untertitel hinzufügen
-- `generateFull(): string`: Vollständiger HTML-Code mit allen Schikanen
-- `generate(): string`: Einfacher Video-Player ohne Schnickschnack
-- `isMedia($url): bool`: Prüft, ob es sich um eine Mediendatei handelt
-- `isAudio($url): bool`: Prüft, ob es sich um eine Audiodatei handelt
-- `videoOembedHelper(): void`: Registriert einen Output-Filter für oEmbed-Tags
-- `parseOembedTags(string $content): string`: Parst oEmbed-Tags im Inhalt
-- `show_sidebar(\rex_extension_point $ep): ?string`: Generiert Medienvorschau für die Sidebar im Medienpool
-- `getSourceUrl(): string`: Gibt die URL der Videoquelle zurück
-- `getAlternativeUrl(): string`: Gibt eine alternative URL für das Video zurück
-- `getVideoInfo($source): array`: Gibt Informationen über das Video zurück (Plattform und ID) [Statische Methode]
-- `generateAttributesString(): string`: Generiert einen String mit allen gesetzten Attributen
-- `generateConsentPlaceholder(string $consentText, string $platform, string $videoId): string`: Generiert einen Platzhalter für die Consent-Abfrage
-
-## 📋 Optionen und Pflichtangaben
-
-### Pflichtangaben
-- `$source` beim Erstellen des Video-Objekts
-
-### Optionale Angaben
-- `$title` beim Erstellen des Video-Objekts
-- `$lang` beim Erstellen des Video-Objekts
-- Alle Attribute in `setAttributes()`
-- Beschreibung und alternativer URL in `setA11yContent()`
-- Thumbnail-URL in `setThumbnails()`
-- Poster-Bild in `setPoster()`
-- Untertitel-Informationen in `addSubtitle()`
-
-## 🌍 Sprachenwirrwarr
-
-Der Video-Player spricht mehr Sprachen als ein UNO-Dolmetscher! Aktuell im Repertoire:
-- Deutsch (de)
-- Englisch (en)
-- Spanisch (es)
-- Slowenisch (si)
-- Französisch (fr)
-
-Sprachänderung leicht gemacht:
+title('My Movie')
+ ->lang('de')
+ ->poster('poster.jpg', 'Movie poster')
+ ->aspectRatio('16/9')
+ ->thumbnails('thumbs.vtt')
+ ->track('subtitles_de.vtt', 'Deutsch', 'de', 'subtitles', true)
+ ->track('subtitles_en.vtt', 'English', 'en', 'subtitles')
+ ->multipleSources([
+ ['src' => 'movie-1080p.mp4', 'width' => 1920, 'height' => 1080, 'type' => 'video/mp4'],
+ ['src' => 'movie-720p.mp4', 'width' => 1280, 'height' => 720, 'type' => 'video/mp4']
+ ])
+ ->attributes([
+ 'controls' => true,
+ 'playsinline' => true,
+ 'preload' => 'metadata'
+ ]);
-```php
-$videoES = new Video('https://www.youtube.com/watch?v=example', 'Mi Video', 'es');
+echo $player->render();
```
-## 🎭 Beispiele für die Dramaturgen
-
-### Ein YouTube-Video mit vollem Programm
+### Podcast with Chapters
```php
-$video = new Video('https://www.youtube.com/watch?v=dQw4w9WgXcQ', 'Never Gonna Give You Up', 'en');
-$video->setAttributes(['autoplay' => true, 'muted' => true]);
-$video->setA11yContent('This is a music video by Rick Astley');
-$video->setThumbnails('/pfad/zu/thumbnails.vtt');
-$video->setPoster('/pfad/zu/poster.jpg', 'Rick Astley dancing');
-$video->addSubtitle('/untertitel/deutsch.vtt', 'captions', 'Deutsch', 'de', true);
-$video->addSubtitle('/untertitel/english.vtt', 'captions', 'English', 'en');
-echo $video->generateFull();
+$podcast = (new VidstackPlayer('episode-01.mp3'))
+ ->title('Episode 1: Getting Started')
+ ->lang('en')
+ ->poster('cover.jpg', 'Podcast cover')
+ ->track('chapters.vtt', 'Chapters', 'en', 'chapters', true)
+ ->attributes(['controls' => true]);
+
+echo $podcast->render();
```
-### Ein schlichtes lokales Video
+## Utility Classes
+
+### Platform Detection
```php
-$video = new Video('/pfad/zu/katzen_spielen_schach.mp4', 'Schachgenies');
-echo $video->generate();
-```
+ 'youtube', 'id' => 'abc']
-```php
-$video = new Video('https://vimeo.com/148751763', 'Vimeo-Meisterwerk', 'fr');
-$video->setThumbnails('/vimeo_thumbs.vtt');
-$video->setPoster('/vimeo_poster.jpg', 'Video thumbnail');
-$video->addSubtitle('/sous-titres.vtt', 'captions', 'Français', 'fr', true);
-echo $video->generateFull();
-```
+// Check if audio
+$isAudio = PlatformDetector::isAudio('podcast.mp3'); // true
-### 🌟 Full Featured Beispiel - Ein bisschen Hollywood ⭐️
+// Check if valid media
+$isMedia = PlatformDetector::isMedia('video.mp4'); // true
+```
-**Aufwendig und zu teuer**
-Hier kommt der Königsklasse-Einsatz - alle Funktionen auf einmal:
+### Utilities
```php
setAttributes([
- 'autoplay' => false,
- 'muted' => false,
- 'loop' => true,
- 'playsinline' => true,
- 'crossorigin' => 'anonymous',
- 'preload' => 'metadata',
- 'controlsList' => 'nodownload',
- 'class' => 'my-custom-video-class',
- 'data-custom' => 'some-value'
+use FriendsOfRedaxo\VidstackPlayer\Utilities;
+
+// Build HTML attributes
+$attrs = Utilities::buildHtmlAttributes([
+ 'controls' => true,
+ 'muted' => true,
+ 'data-id' => '123'
]);
+// Returns: ' controls muted data-id="123"'
-// Hinzufügen von ausführlichen Barrierefreiheits-Inhalten
-$video->setA11yContent(
- 'This legendary music video features Rick Astley performing "Never Gonna Give You Up". The video begins with Rick, dressed in a black leather jacket, dancing in various locations. The catchy synth-pop tune and Rick\'s distinctive baritone voice have made this song an internet phenomenon.',
- 'https://example.com/detailed-audio-description'
-);
+// Detect MIME type
+$mime = Utilities::detectMimeType('video.webm'); // 'video/webm'
+```
-// Setzen von Thumbnail-Vorschaubildern für den Player-Fortschritt
-$video->setThumbnails('/pfad/zu/detailed-thumbnails.vtt');
+## Backend Features
-// Setzen des Poster-Bildes
-$video->setPoster('/pfad/zu/rickroll_poster.jpg', 'Rick Astley in his iconic pose');
+### Mediapool Integration
-// Hinzufügen von Untertiteln in mehreren Sprachen
-$video->addSubtitle('/untertitel/english.vtt', 'captions', 'English', 'en', true);
-$video->addSubtitle('/untertitel/deutsch.vtt', 'captions', 'Deutsch', 'de');
-$video->addSubtitle('/untertitel/francais.vtt', 'captions', 'Français', 'fr');
-$video->addSubtitle('/untertitel/espanol.vtt', 'captions', 'Español', 'es');
-$video->addSubtitle('/untertitel/slovenscina.vtt', 'captions', 'Slovenščina', 'si');
+The addon automatically integrates into the REDAXO mediapool:
-// Hinzufügen von Audiodeskription
-$video->addSubtitle('/audio/description.vtt', 'descriptions', 'Audio Description', 'en');
+- **Preview Player** - Video/audio preview in media detail sidebar
+- **Video Info** - Resolution, codec, duration, filesize, bitrate (requires FFmpeg addon)
+- **Quick Tools** - Trim, optimize, and analyze videos (requires FFmpeg addon)
-// Hinzufügen von Kapitelmarkierungen
-$video->addSubtitle('/chapters/rickroll.vtt', 'chapters', 'Chapters', 'en');
+### FFmpeg Integration
-// Generieren des vollständigen Video-Player-Codes
-$fullPlayerCode = $video->generateFull();
+Install the [FFmpeg AddOn](https://github.com/FriendsOfREDAXO/ffmpeg) for enhanced features:
-// Ausgabe des generierten Codes
-echo $fullPlayerCode;
+```bash
+composer require friendsofredaxo/ffmpeg
```
-Dieses Beispiel zeigt die Hauptfunktionalität des Players mit allen verfügbaren Optionen. In den meisten Fällen wird das bereits alles sein, was Sie brauchen.
+Features with FFmpeg:
+- Video information display
+- Quick trim tool
+- Optimization tool
+- Detailed video analysis
-## 🛠️ Erweiterte Methoden für spezielle Anwendungsfälle
+### CKE5 Integration
-Die folgenden erweiterten Methoden sind für spezielle Anwendungsfälle gedacht, wenn Sie mehr Kontrolle über den Player benötigen oder eigene Implementierungen erstellen möchten.
+The addon provides an oEmbed parser to convert CKEditor 5 oEmbed tags to Vidstack players.
-### Beispiel 1: Eigenen DSGVO-konformen Player mit zwei-Klick-Lösung erstellen
+**Manual activation required** (e.g., in your project addon's `boot.php`):
```php
getSourceUrl());
+// In your project addon's boot.php
+use FriendsOfRedaxo\VidstackPlayer\OembedParser;
-// Nur wenn es ein YouTube oder Vimeo Video ist, DSGVO-Abfrage anzeigen
-if ($videoInfo['platform'] !== 'default') {
- // Angepassten Consent-Text erstellen
- $consentText = "Um dieses {$videoInfo['platform']}-Video anzusehen, klicken Sie bitte auf 'Video laden'. " .
- "Dadurch werden Daten an {$videoInfo['platform']} übermittelt. " .
- "Weitere Informationen finden Sie in unserer Datenschutzerklärung.";
-
- // Container mit eigener Klasse für Styling erstellen
- echo '';
-
- // Vorschaubild mit Platzhalter anzeigen (nutzt die Video-Klassen-Methode)
- echo $video->generateConsentPlaceholder($consentText, $videoInfo['platform'], $videoInfo['id']);
-
- // Informationstext anzeigen
- echo '
';
- echo '
Video-Quelle: ' . htmlspecialchars($video->getSourceUrl()) . '
';
- echo '
';
-
- echo '
';
-} else {
- // Bei lokalen Videos direkt anzeigen
- echo $video->generate();
-}
+// Register oEmbed parser for CKE5
+OembedParser::register();
```
-### Beispiel 2: Erweiterter Player mit Analytics-Integration
+**How it works:**
-```php
-getSourceUrl());
- $platform = $videoInfo['platform'];
- $videoId = $videoInfo['id'];
-
- // Standard HTML für den Player generieren
- $playerHtml = $video->generate();
-
- // Attribute für das Analytics-Tracking hinzufügen
- $trackingAttributes = ' data-tracking="true" data-platform="' . htmlspecialchars($platform) .
- '" data-video-id="' . htmlspecialchars($videoId) . '"';
-
- // HTML-Code mit Tracking-Attributen ergänzen
- $trackedHtml = str_replace('
-document.addEventListener('DOMContentLoaded', function() {
- const player = document.querySelector('media-player[data-tracking="true"]');
- if (player) {
- player.addEventListener('play', function() {
- // Hier Tracking-Code einfügen
- console.log('Video gestartet:', player.getAttribute('data-platform'), player.getAttribute('data-video-id'));
- });
-
- player.addEventListener('ended', function() {
- // Video wurde vollständig angesehen
- console.log('Video beendet:', player.getAttribute('data-platform'), player.getAttribute('data-video-id'));
- });
- }
-});
-
-EOT;
-
- return $trackedHtml;
-}
+```html
+
+
-// Verwendung
-echo createTrackedVideo('https://www.youtube.com/watch?v=dQw4w9WgXcQ', 'Tracking-Demo');
+
+...
```
-### Beispiel 3: Eigenes Player-Layout mit vorgenerierten Elementen erstellen
+**Why manual activation?**
+- Gives you full control over when and how videos are embedded
+- Prevents conflicts with existing oEmbed handlers or custom implementations
+- Opt-in approach - activate only if you need it
-```php
-getVideoInfo();
- $isYouTube = $videoInfo['platform'] === 'youtube';
-
- // Custom Container erstellen
- $output = '';
-
- // Titel und Info anzeigen, wenn gewünscht
- if ($showInfo) {
- $output .= '';
- }
-
- // Player-Container
- $output .= '
';
-
- // Für YouTube wird der Consent-Platzhalter verwendet
- if ($isYouTube) {
- $consentText = "YouTube-Videos werden erst nach Zustimmung geladen, um Ihre Privatsphäre zu schützen.";
- $output .= $video->generateConsentPlaceholder($consentText, 'youtube', $videoInfo['id']);
- } else {
- // Für lokale Videos normalen Player anzeigen
- $output .= $video->generate();
- }
-
- $output .= '
';
-
- // Custom Controls oder zusätzliche Informationen
- if ($showInfo) {
- $output .= '';
- }
-
- $output .= '
';
-
- return $output;
-}
+The addon is structured into focused, single-purpose classes:
-// Verwendung
-echo createCustomLayoutVideo('https://www.youtube.com/watch?v=dQw4w9WgXcQ', 'Custom Layout Demo');
```
-
-### Beispiel 4: Adaptive Einbindung basierend auf Gerätetyp
-
-```php
-setAttributes([
- 'playsinline' => true,
- 'preload' => 'none', // Bandbreite sparen
- 'controlsList' => 'nodownload',
- 'disablePictureInPicture' => true,
- 'class' => 'mobile-optimized'
- ]);
-
- // Einfache Version für mobile Geräte
- return $video->generate();
- } else {
- // Auf Desktop volle Funktionalität
- $video->setAttributes([
- 'class' => 'desktop-enhanced',
- 'preload' => 'metadata'
- ]);
-
- // Poster und Untertitel für Desktop hinzufügen
- $video->setPoster('/pfad/zu/hq-poster.jpg', 'Video-Vorschau');
- $video->addSubtitle('/untertitel/deutsch.vtt', 'captions', 'Deutsch', 'de', true);
-
- return $video->generateFull();
- }
-}
-
-// Einfache Geräteerkennung (in der Praxis würden Sie hier eine richtige Erkennung verwenden)
-$isMobile = strpos($_SERVER['HTTP_USER_AGENT'], 'Mobile') !== false;
-
-// Verwendung
-echo createResponsiveVideo('https://example.com/video.mp4', 'Responsives Video', $isMobile);
+lib/
+├── VidstackPlayer.php # Main player class (fluent API)
+├── PlatformDetector.php # Platform & media type detection
+├── AssetHelper.php # CSS/JS loading with cache-busting
+├── Utilities.php # HTML attributes, MIME types
+├── Translator.php # i18n translation helper
+├── OembedParser.php # CKE5 oEmbed integration
+└── BackendIntegration.php # Mediapool sidebar & FFmpeg
```
-### Beispiel 5: Integration mit REX_MEDIA-Variablen
-
-```php
-';
- echo 'Audio-Player ';
- echo $video->generate();
- echo '';
- } else {
- // Video mit Standardeinstellungen anzeigen
- $video->setAttributes([
- 'controls' => true,
- 'playsinline' => true
- ]);
-
- // Wenn ein Poster-Bild ausgewählt wurde
- if (REX_MEDIA[2]) {
- $video->setPoster(rex_url::media(REX_MEDIA[2]), 'Vorschaubild');
- }
-
- echo $video->generateFull();
- }
-}
-```
+**Key Principles:**
+- ✅ Single Responsibility - Each class has one clear purpose
+- ✅ No "God Classes" - Separated concerns
+- ✅ Universal naming - Not just "Video", but "VidstackPlayer" (supports audio too!)
-Durch diese praktischen Beispiele wird deutlich, wie die erweiterten Methoden der Video-Klasse sinnvoll in verschiedenen Szenarien eingesetzt werden können, anstatt sie nur isoliert zu demonstrieren.
+## Consent Management
-## 🧙♂️ Tipp: Die magische Default-Funktion
+**Important:** This addon does NOT include consent management for YouTube/Vimeo.
-Wer faul clever ist, baut sich eine Hilfsfunktion für Standardeinstellungen:
+For GDPR-compliant embeds, install the [Consent Manager AddOn](https://github.com/FriendsOfREDAXO/consent_manager):
-```php
-function createDefaultVideo($source, $title = '', $a11yContent = null) {
- $current_lang = rex_clang::getCurrent();
- $lang_code = $current_lang->getCode();
- $video = new Video($source, $title, $lang_code);
- $video->setAttributes([
- 'autoplay' => false,
- 'muted' => true,
- 'playsinline' => true
- ]);
- if ($a11yContent !== null) {
- $video->setA11yContent($a11yContent);
- }
- $video->setPoster('/pfad/zu/default_poster.jpg', 'Default video poster');
- return $video;
-}
-
-// Verwendung
-$easyVideo = createDefaultVideo('https://youtube.com/watch?v=abcdefg', 'Einfach Genial', 'Ein Video über etwas Interessantes');
-echo $easyVideo->generateFull();
+```bash
+composer require friendsofredaxo/consent_manager
```
-## 🎸 Unterstützung für Audio-Dateien
+The Consent Manager will automatically handle consent for YouTube and Vimeo embeds.
-Das Addon unterstützt auch die Einbindung von Audio-Dateien. Genauso wie für Videos:
+## Browser Support
-```php
-$audio = new Video('audio.mp3', 'Mein Lieblingssong');
-echo $audio->generate();
-```
+- Chrome 90+
+- Firefox 88+
+- Safari 14+
+- Edge 90+
-## ✔︎ Im Backend schon integriert
+For older browsers, Vidstack will gracefully degrade to native video/audio elements.
-Hier muss man nichts machen - außer Videos schauen.
+## Migration from Vidstack 1.x
-
+See [MIGRATION.md](MIGRATION.md) for detailed migration instructions.
+**Breaking Changes:**
+- Package name: `vidstack` → `vidstack`
+- Namespace: `FriendsOfRedaxo\VidStack\Video` → `FriendsOfRedaxo\VidstackPlayer\VidstackPlayer`
+- Class name: `Video` → `VidstackPlayer` (reflects audio support)
+- API: Setter methods → Fluent method chaining
+- Assets: Use `AssetHelper::getCss()` and `getJs()` instead of manual inclusion
-## 🍪 Consent und Kekse
+## Development
-Leider muss es ja sein.
+Dieses Addon nutzt die [Standard GitHub Workflows für REDAXO Addons](https://github.com/FriendsOfREDAXO/github-workflows).
-Hiermit kann man in einem Consent-Manager oder auch so mal zwischendurch die Erlaubnis für Vimeo oder Youtube setzen. Wer keine Cookies erlaubt bekommt halt Local-Storage 😉.
+### Setup
-```js
-
-```
+```bash
+# Das Addon muss im REDAXO-Kontext entwickelt werden
+# Klone REDAXO und installiere das Addon dort
-oder für beide
-```js
-
+# Install Node dependencies
+cd redaxo/src/addons/vidstack/build
+npm install
```
+### Lokale Quality Checks
+**PHP** (im Docker Container oder REDAXO-Root):
+```bash
+# Im REDAXO Docker Container (empfohlen)
+docker exec coreweb bash -c "cd /var/www/html/public && \
+ vendor/bin/php-cs-fixer fix redaxo/src/addons/vidstack --config=redaxo/src/addons/vidstack/.php-cs-fixer.dist.php"
-
-## 📄 CKE5 Oembed - lässig aufgelöst
-(*das Plyr-AddOn lässt grüßen*)
-
-CKE5 kann ja bekanntlich Videos einbinden, aber liefert nichts für die Ausgabe im Frontend mit. 👋 Hier ist die Lösung:
-
-Einfach im String suchen und umwanden:
-
-```php
-echo Video::parseOembedTags($content);
+docker exec coreweb bash -c "cd /var/www/html/public && \
+ php redaxo/src/addons/vidstack/.tools/rexstan.php && \
+ redaxo/bin/console rexstan:analyze"
```
-und schon sind die Videos da 😀
-…oder in der boot.php vom Project-AddOn (gerne auch im eigenen AddOn) den Outputfilter nutzen.
-
-
-### Outputfilter im Frontend
-
-```php
-if (rex::isFrontend()) {
-Video::videoOembedHelper();
-}
+**JavaScript:**
+```bash
+cd build
+npm run build # Build assets
+npx eslint config/*.js # Lint JavaScript
+npx eslint config/*.js --fix # Auto-fix JavaScript
```
-### Outputfilter im Backend:
-Es soll ja nicht nur vorne schön sein. ❤️
-Hier muss man dafür sorgen, dass es ggf. in den Blocks nicht ausgeführt wird.
+### GitHub Actions
-```php
-if (rex::isBackend() && rex_be_controller::getCurrentPagePart(1) == 'content' && !in_array(rex_request::get('function', 'string'), ['add', 'edit'])) {
-Video::videoOembedHelper();
-}
-```
+Automatisch bei jedem Push/PR:
+1. **Code Style** - PHP-CS-Fixer formatiert Code automatisch
+2. **Rexstan** - PHPStan für REDAXO (Level 9)
+3. **Build Assets** - ESLint + npm build
+4. **Publish to REDAXO** - Bei GitHub Release
-## 🎉 HEUREKA!
-
-Jetzt bist du ein Video-Einbettungs-Ninja! Geh raus und mache das Internet zu einem besseren Ort - ein Video nach dem anderen. Und denk dran: Mit großer Macht kommt große Verantwortung (und coole Videos)!
-
-Viel Spaß beim Coden! 🚀👩💻👨💻
-
-## 👓 Für die DEVs, Nerds und Geeks
-
-Ihr wollt uns sicher mal bei der Weiterentwicklung helfen. Das geht so:
-
-### Den Vendor aktualisieren und ein frisches Build erstellen
-
-Im Ordner build ist alles drin was man braucht.
-- Also forken, lokal runterladen.
-- npm install ausführen
-- npm npm run build ausführen
-- Im Assets-Ordner die Dateien des Dist-Ordners austauschen (Ihr habt richtig gesehen, es gibt auch die reine JS-Variante 😉)
-
-PR erstellen 😀
-
-### Alles andere
-
-…fliegt hier so im Repo rum, einfach mal reinschauen. 👀
-
-## Wie es arbeitet
-
-### Video-Klasse Prozess mit Prüfungen
-
-```mermaid
-flowchart TD
- A[Start] --> B[Erstelle Video-Objekt mit Dateipfad]
- B --> C{Ist es eine gültige Datei?}
- C -->|Nein| D[Fehler: Ungültige Datei]
- C -->|Ja| E{Ist es ein unterstütztes Format?}
- E -->|Nein| F[Fehler: Nicht unterstütztes Format]
- E -->|Ja| G[Setze grundlegende Attribute]
- G --> H{Ist es ein Video?}
- H -->|Ja| I[Setze Video-spezifische Attribute]
- H -->|Nein| J[Setze Audio-spezifische Attribute]
- I --> K{Poster-Bild angegeben?}
- K -->|Ja| L{Ist Poster-Datei gültig?}
- L -->|Nein| M[Warnung: Ungültiges Poster]
- L -->|Ja| N[Setze Poster-Bild]
- K -->|Nein| O[Verwende Standard-Poster]
- J --> P[Prüfe auf Untertitel]
- N --> P
- O --> P
- M --> P
- P --> Q{Untertitel vorhanden?}
- Q -->|Ja| R{Sind Untertitel-Dateien gültig?}
- R -->|Nein| S[Warnung: Ungültige Untertitel]
- R -->|Ja| T[Füge Untertitel hinzu]
- Q -->|Nein| U[Keine Untertitel]
- S --> V[Generiere Player-HTML]
- T --> V
- U --> V
- V --> W{HTML erfolgreich generiert?}
- W -->|Nein| X[Fehler: HTML-Generierung fehlgeschlagen]
- W -->|Ja| Y[Zeige Video/Audio-Player]
- Y --> Z[Ende]
- D --> Z
- F --> Z
- X --> Z
-```
-
+[Mehr Infos zu den Workflows](https://github.com/FriendsOfREDAXO/github-workflows)
-## Autor(en)
+## License
-**Friends Of REDAXO**
+MIT License - See [LICENSE](LICENSE) file
-* http://www.redaxo.org
-* https://github.com/FriendsOfREDAXO
-* Ein bisschen KI 😎
+## Credits
+- Built with [Vidstack](https://vidstack.io)
+- Maintained by [Friends Of REDAXO](https://github.com/FriendsOfREDAXO)
+- For REDAXO CMS
-**Projektleitung**
+## Support
-[Thomas Skerbis](https://github.com/skerbis)
+- **Issues:** [GitHub Issues](https://github.com/FriendsOfREDAXO/vidstack/issues)
+- **REDAXO Slack:** #addons channel
+- **Forum:** [REDAXO Forum](https://www.redaxo.org/forum/)
-**Thanks to**
-[Vidstack.io](https://www.vidstack.io)
diff --git a/README_de.md b/README_de.md
new file mode 100644
index 0000000..efb19c1
--- /dev/null
+++ b/README_de.md
@@ -0,0 +1,391 @@
+# Vidstack Player für REDAXO
+
+Moderner Media-Player (Video & Audio) mit vollständiger [Vidstack.io](https://vidstack.io)-Unterstützung für REDAXO CMS.
+
+## Features
+
+✅ **Universelle Medienunterstützung** - Video UND Audio-Wiedergabe (nicht nur Video!)
+✅ **Moderne Fluent API** - Method-Chaining für sauberen, lesbaren Code
+✅ **Mehrere Plattformen** - Lokale Dateien, YouTube, Vimeo
+✅ **Untertitel & Captions** - Vollständige WebVTT-Track-Unterstützung
+✅ **Adaptive Qualität** - Mehrere Quellen für Qualitätswechsel
+✅ **Barrierefreiheit** - WCAG-konform mit ARIA-Labels
+✅ **FFmpeg-Integration** - Video-Infos und Tools im Backend-Mediapool
+✅ **CKE5-Integration** - Automatisches oEmbed-Parsing
+✅ **Cache-Busting** - Automatische Asset-Versionierung
+✅ **Performance** - Defer/Async-Script-Loading
+
+## Installation
+
+```bash
+# Via Composer (empfohlen)
+composer require friendsofredaxo/vidstack
+
+# Oder via REDAXO Installer
+# Suche nach "Vidstack Player" im AddOn-Installer
+```
+
+## Schnellstart
+
+### Einfaches Video
+
+```php
+title('Mein Video')
+ ->poster('poster.jpg')
+ ->attributes(['controls' => true]);
+
+echo $player->render();
+```
+
+### Einfaches Audio
+
+```php
+$player = (new VidstackPlayer('audio.mp3'))
+ ->title('Podcast Episode 1')
+ ->attributes(['controls' => true]);
+
+echo $player->render();
+```
+
+### YouTube-Video
+
+```php
+$player = (new VidstackPlayer('https://youtube.com/watch?v=dQw4w9WgXcQ'))
+ ->title('Never Gonna Give You Up');
+
+echo $player->render();
+```
+
+## Fluent API Referenz
+
+### Kern-Methoden
+
+```php
+$player = (new VidstackPlayer('source.mp4'))
+ ->title('Titel') // Titel setzen
+ ->lang('de') // Sprache setzen (Standard: 'de')
+ ->poster('poster.jpg', 'Alt-Text') // Poster-Bild setzen
+ ->aspectRatio('16/9') // Seitenverhältnis setzen
+ ->thumbnails('thumbs.vtt') // Thumbnail-Track setzen
+ ->attributes(['controls' => true]) // Mehrere Attribute setzen
+ ->attr('muted', true) // Einzelnes Attribut setzen
+ ->render(); // HTML generieren
+```
+
+### Untertitel & Captions
+
+```php
+$player->track(
+ src: 'subtitles.vtt',
+ label: 'Deutsch',
+ srclang: 'de',
+ kind: 'subtitles', // subtitles|captions|descriptions|chapters|metadata
+ default: true
+);
+```
+
+### Mehrere Quellen (Adaptive Qualität)
+
+```php
+$player->multipleSources([
+ ['src' => 'video-1080p.mp4', 'width' => 1920, 'height' => 1080, 'type' => 'video/mp4'],
+ ['src' => 'video-720p.mp4', 'width' => 1280, 'height' => 720, 'type' => 'video/mp4'],
+ ['src' => 'video-480p.mp4', 'width' => 854, 'height' => 480, 'type' => 'video/mp4']
+]);
+```
+
+## Frontend-Integration
+
+### Assets einbinden
+
+```php
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+### Asset Helper Optionen
+
+```php
+// CSS mit benutzerdefinierten Attributen
+echo AssetHelper::getCss(['media' => 'screen'], cachebuster: true);
+
+// JS mit defer (empfohlen für Performance)
+echo AssetHelper::getJs(defer: true);
+
+// JS mit async (mit Vorsicht verwenden)
+echo AssetHelper::getJs(async: true);
+
+// Benutzerdefinierte Attribute
+echo AssetHelper::getJs(
+ defer: true,
+ attributes: ['type' => 'module', 'crossorigin' => 'anonymous'],
+ cachebuster: true
+);
+
+// Cache-Busting deaktivieren
+echo AssetHelper::getCss(cachebuster: false);
+```
+
+## Erweiterte Beispiele
+
+### Vollständiges Video-Setup
+
+```php
+title('Mein Film')
+ ->lang('de')
+ ->poster('poster.jpg', 'Film-Poster')
+ ->aspectRatio('16/9')
+ ->thumbnails('thumbs.vtt')
+ ->track('subtitles_de.vtt', 'Deutsch', 'de', 'subtitles', true)
+ ->track('subtitles_en.vtt', 'English', 'en', 'subtitles')
+ ->multipleSources([
+ ['src' => 'movie-1080p.mp4', 'width' => 1920, 'height' => 1080, 'type' => 'video/mp4'],
+ ['src' => 'movie-720p.mp4', 'width' => 1280, 'height' => 720, 'type' => 'video/mp4']
+ ])
+ ->attributes([
+ 'controls' => true,
+ 'playsinline' => true,
+ 'preload' => 'metadata'
+ ]);
+
+echo $player->render();
+```
+
+### Podcast mit Kapiteln
+
+```php
+$podcast = (new VidstackPlayer('episode-01.mp3'))
+ ->title('Episode 1: Der Einstieg')
+ ->lang('de')
+ ->poster('cover.jpg', 'Podcast-Cover')
+ ->track('chapters.vtt', 'Kapitel', 'de', 'chapters', true)
+ ->attributes(['controls' => true]);
+
+echo $podcast->render();
+```
+
+## Utility-Klassen
+
+### Plattform-Erkennung
+
+```php
+ 'youtube', 'id' => 'abc']
+
+// Prüfen ob Audio
+$isAudio = PlatformDetector::isAudio('podcast.mp3'); // true
+
+// Prüfen ob gültiges Medium
+$isMedia = PlatformDetector::isMedia('video.mp4'); // true
+```
+
+### Utilities
+
+```php
+ true,
+ 'muted' => true,
+ 'data-id' => '123'
+]);
+// Gibt zurück: ' controls muted data-id="123"'
+
+// MIME-Type erkennen
+$mime = Utilities::detectMimeType('video.webm'); // 'video/webm'
+```
+
+## Backend-Features
+
+### Mediapool-Integration
+
+Das AddOn integriert sich automatisch in den REDAXO-Mediapool:
+
+- **Vorschau-Player** - Video/Audio-Vorschau in der Medien-Detail-Sidebar
+- **Video-Info** - Auflösung, Codec, Dauer, Dateigröße, Bitrate (benötigt FFmpeg-AddOn)
+- **Schnell-Tools** - Trimmen, Optimieren und Analysieren von Videos (benötigt FFmpeg-AddOn)
+
+### FFmpeg-Integration
+
+Installiere das [FFmpeg AddOn](https://github.com/FriendsOfREDAXO/ffmpeg) für erweiterte Features:
+
+```bash
+composer require friendsofredaxo/ffmpeg
+```
+
+Features mit FFmpeg:
+- Video-Informationsanzeige
+- Schnell-Trimm-Tool
+- Optimierungs-Tool
+- Detaillierte Video-Analyse
+
+### CKE5-Integration
+
+Das AddOn bietet einen oEmbed-Parser, um CKEditor 5 oEmbed-Tags in Vidstack-Player umzuwandeln.
+
+**Manuelle Aktivierung erforderlich** (z.B. in der `boot.php` deines Projekt-AddOns):
+
+```php
+
+
+
+
+...
+```
+
+**Warum manuelle Aktivierung?**
+- Gibt dir volle Kontrolle über wann und wie Videos eingebettet werden
+- Verhindert Konflikte mit bestehenden oEmbed-Handlern oder eigenen Implementierungen
+- Opt-in-Ansatz - aktiviere nur wenn du es brauchst
+
+## Architektur
+
+Das AddOn ist in fokussierte, zweckgebundene Klassen strukturiert:
+
+```
+lib/
+├── VidstackPlayer.php # Haupt-Player-Klasse (Fluent API)
+├── PlatformDetector.php # Plattform- & Medientyp-Erkennung
+├── AssetHelper.php # CSS/JS-Laden mit Cache-Busting
+├── Utilities.php # HTML-Attribute, MIME-Types
+├── Translator.php # i18n-Übersetzungs-Helper
+├── OembedParser.php # CKE5 oEmbed-Integration
+└── BackendIntegration.php # Mediapool-Sidebar & FFmpeg
+```
+
+**Kernprinzipien:**
+- ✅ Single Responsibility - Jede Klasse hat einen klaren Zweck
+- ✅ Keine "God Classes" - Getrennte Zuständigkeiten
+- ✅ Universelle Benennung - Nicht nur "Video", sondern "VidstackPlayer" (unterstützt auch Audio!)
+
+## Consent Management
+
+**Wichtig:** Dieses AddOn enthält KEIN Consent-Management für YouTube/Vimeo.
+
+Für DSGVO-konforme Embeds installiere den [Consent Manager](https://github.com/FriendsOfREDAXO/consent_manager):
+
+```bash
+composer require friendsofredaxo/consent_manager
+```
+
+Der Consent Manager übernimmt automatisch die Einwilligung für YouTube- und Vimeo-Embeds.
+
+## Browser-Unterstützung
+
+- Chrome 90+
+- Firefox 88+
+- Safari 14+
+- Edge 90+
+
+Für ältere Browser degradiert Vidstack elegant zu nativen Video/Audio-Elementen.
+
+## Migration von Vidstack 1.x
+
+Siehe [MIGRATION.md](MIGRATION.md) für detaillierte Migrations-Anweisungen.
+
+**Breaking Changes:**
+- Package-Name: `vidstack` → `vidstack`
+- Namespace: `FriendsOfRedaxo\VidStack\Video` → `FriendsOfRedaxo\VidstackPlayer\VidstackPlayer`
+- Klassen-Name: `Video` → `VidstackPlayer` (reflektiert Audio-Unterstützung)
+- API: Setter-Methoden → Fluent Method Chaining
+- Assets: Nutze `AssetHelper::getCss()` und `getJs()` statt manueller Einbindung
+
+## Entwicklung
+
+Dieses AddOn nutzt die [Standard GitHub Workflows für REDAXO AddOns](https://github.com/FriendsOfREDAXO/github-workflows).
+
+### Setup
+
+```bash
+# Das AddOn muss im REDAXO-Kontext entwickelt werden
+# Klone REDAXO und installiere das AddOn dort
+
+# Node-Dependencies installieren
+cd redaxo/src/addons/vidstack/build
+npm install
+```
+
+### Lokale Quality Checks
+
+**PHP** (im Docker Container oder REDAXO-Root):
+```bash
+# Im REDAXO Docker Container (empfohlen)
+docker exec coreweb bash -c "cd /var/www/html/public && \
+ vendor/bin/php-cs-fixer fix redaxo/src/addons/vidstack --config=redaxo/src/addons/vidstack/.php-cs-fixer.dist.php"
+
+docker exec coreweb bash -c "cd /var/www/html/public && \
+ php redaxo/src/addons/vidstack/.tools/rexstan.php && \
+ redaxo/bin/console rexstan:analyze"
+```
+
+**JavaScript:**
+```bash
+cd build
+npm run build # Assets bauen
+npx eslint config/*.js # JavaScript linten
+npx eslint config/*.js --fix # JavaScript auto-fixen
+```
+
+### GitHub Actions
+
+Automatisch bei jedem Push/PR:
+
+1. **Code Style** - PHP-CS-Fixer formatiert Code automatisch
+2. **Rexstan** - PHPStan für REDAXO (Level 9)
+3. **Build Assets** - ESLint + npm build
+4. **Publish to REDAXO** - Bei GitHub Release
+
+[Mehr Infos zu den Workflows](https://github.com/FriendsOfREDAXO/github-workflows)
+
+## Lizenz
+
+MIT License - Siehe [LICENSE](LICENSE)-Datei
+
+## Credits
+
+- Entwickelt mit [Vidstack](https://vidstack.io)
+- Maintained by [Friends Of REDAXO](https://github.com/FriendsOfREDAXO)
+- Für REDAXO CMS
+
+## Support
+
+- **Issues:** [GitHub Issues](https://github.com/FriendsOfREDAXO/vidstack/issues)
+- **REDAXO Slack:** #addons Channel
+- **Forum:** [REDAXO Forum](https://www.redaxo.org/forum/)
diff --git a/assets/vidstack_helper.css b/assets/src/vidstack_helper.css
similarity index 62%
rename from assets/vidstack_helper.css
rename to assets/src/vidstack_helper.css
index 2faac32..a1a5814 100644
--- a/assets/vidstack_helper.css
+++ b/assets/src/vidstack_helper.css
@@ -1,4 +1,13 @@
-.video-container {
+/**
+ * Vidstack Helper Styles
+ * Core styles for Vidstack player, accessibility, and FFmpeg integration
+ */
+
+/* ===================================
+ MEDIA PLAYER BASE
+ =================================== */
+
+.vidstack-video-container {
margin-bottom: 0px;
}
@@ -6,88 +15,33 @@
contain: layout;
}
-.video-container media-player {
+.vidstack-video-container media-player {
width: 100%;
}
-.video-description, .video-transcript, .alternative-links {
- margin-top: 10px;
- display: none;
-}
-.js .a11y-content {
- position: absolute;
- width: 1px;
- height: 1px;
- padding: 0;
- margin: -1px;
- overflow: hidden;
- clip: rect(0, 0, 0, 0);
- white-space: nowrap;
- border: 0;
-}
-.no-js .a11y-content {
- display: block;
-}
-.skip-link {
- position: absolute;
- top: -40px;
- left: 0;
- background: #000;
- color: white;
- padding: 8px;
- z-index: 100;
-}
-.skip-link:focus {
- top: 0;
-}
-.video-wrapper {
+
+/* ===================================
+ RESPONSIVE VIDEO WRAPPER
+ =================================== */
+
+.vidstack-video-wrapper {
position: relative;
padding-bottom: 56.25%; /* 16:9 Aspect Ratio */
height: 0;
overflow: hidden;
}
-.video-wrapper media-player {
+
+.vidstack-video-wrapper media-player {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
-.consent-placeholder {
- background-color: #f0f0f0;
- display: flex;
- flex-direction: column;
- justify-content: center;
- align-items: center;
- text-align: center;
- padding: 20px;
- box-sizing: border-box;
-}
-
-.consent-placeholder button {
- margin-top: 10px;
- padding: 10px 20px;
- background-color: #007bff;
- color: white;
- border: none;
- border-radius: 5px;
- cursor: pointer;
-}
-.consent-placeholder button:hover {
- background-color: #0056b3;
-}
-noscript .js .a11y-content {
- position: static;
- width: auto;
- height: auto;
- padding: initial;
- margin: initial;
- overflow: visible;
- clip: auto;
- white-space: normal;
-}
+/* ===================================
+ FFMPEG VIDEO INFO (BACKEND)
+ =================================== */
-/* Video-Informationen Styling */
.vidstack-video-info {
margin-top: 15px;
padding: 12px;
@@ -123,23 +77,23 @@ noscript .js .a11y-content {
display: inline-block;
}
-@media (max-width: 768px) {
- .vidstack-video-info {
- font-size: 11px;
- padding: 10px;
- }
-
- .vidstack-video-info h4 {
- font-size: 12px;
- }
-
- .vidstack-video-info .btn {
- font-size: 10px;
- padding: 2px 6px;
- }
+/* FFmpeg Action Buttons */
+.vidstack-action-buttons {
+ margin-top: 10px;
+ padding-top: 8px;
+ border-top: 1px solid #dee2e6;
+}
+
+.vidstack-action-buttons-label {
+ margin-bottom: 6px;
+}
+
+.vidstack-action-buttons-group {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 6px;
}
-/* Button-Styles für FFmpeg-Tools */
.vidstack-video-info .btn {
display: inline-flex;
align-items: center;
@@ -188,11 +142,40 @@ noscript .js .a11y-content {
color: white;
}
-/* CSP-compliant styles to replace inline styles */
+/* ===================================
+ VIDSTACK FIXES
+ =================================== */
+
+/* Fix for controls pointer events */
.vds-controls-group {
pointer-events: none;
}
+/* Fix for video slider thumbnails */
media-slider-video video {
max-width: unset;
}
+
+/* ===================================
+ RESPONSIVE ADJUSTMENTS
+ =================================== */
+
+@media (max-width: 768px) {
+ .vidstack-video-info {
+ font-size: 11px;
+ padding: 10px;
+ }
+
+ .vidstack-video-info h4 {
+ font-size: 12px;
+ }
+
+ .vidstack-video-info .btn {
+ font-size: 10px;
+ padding: 2px 6px;
+ }
+
+ .vidstack-action-buttons-group {
+ flex-direction: column;
+ }
+}
diff --git a/assets/src/vidstack_helper.js b/assets/src/vidstack_helper.js
new file mode 100644
index 0000000..117099e
--- /dev/null
+++ b/assets/src/vidstack_helper.js
@@ -0,0 +1,119 @@
+/**
+ * Vidstack Helper - Translations and Event Handling
+ *
+ * Handles loading and applying translations for Vidstack player
+ * Consent management should be handled by an external addon (e.g., Consent Manager)
+ */
+console.log('Vidstack Helper: Script loaded and executing!');
+
+(function () {
+ console.log('Vidstack Helper: IIFE starting');
+ let vidstackTranslations = {};
+
+ /**
+ * Load translations from JSON file
+ */
+ async function vidstackLoadTranslations() {
+ try {
+ vidstackTranslations = await (await fetch('/assets/addons/vidstack_player/translations.json')).json();
+ } catch (error) {
+ console.error('Vidstack: Error loading translations:', error);
+ vidstackTranslations = { de: {}, en: {} };
+ }
+ }
+
+ /**
+ * Apply translations to all media players
+ */
+ function vidstackApplyTranslations() {
+ console.log('Vidstack: Applying translations...');
+ ['media-video-layout', 'media-audio-layout'].forEach(selector => {
+ const layouts = document.querySelectorAll(selector);
+ console.log('Vidstack: Found', layouts.length, selector, 'elements');
+ layouts.forEach(layout => {
+ const player = layout.closest('media-player');
+ const lang = player?.getAttribute('lang') || document.documentElement.lang || 'en';
+
+ // Apply translations from loaded JSON
+ if (vidstackTranslations[lang]) {
+ layout.translations = vidstackTranslations[lang];
+ } else if (vidstackTranslations['en']) {
+ // Fallback to English
+ layout.translations = vidstackTranslations['en'];
+ }
+ });
+ });
+ }
+
+ /**
+ * Initialize media players with translations
+ */
+ function vidstackInitializeMediaPlayers() {
+ document.querySelectorAll('media-player').forEach(player => {
+ // Translations are applied automatically via vidstackApplyTranslations()
+ // No consent handling here - that should be managed by external addon
+ });
+
+ vidstackApplyTranslations();
+ }
+
+ /**
+ * Main initialization function
+ */
+ async function vidstackInitialize() {
+ console.log('Vidstack: Starting initialization...');
+ await vidstackLoadTranslations();
+ console.log('Vidstack: Translations loaded, found', Object.keys(vidstackTranslations).length, 'languages');
+ vidstackInitializeMediaPlayers();
+
+ // Watch for dynamically added players (AJAX, etc.)
+ const observer = new MutationObserver((mutations) => {
+ let needsUpdate = false;
+
+ mutations.forEach((mutation) => {
+ if (mutation.type === 'childList') {
+ mutation.addedNodes.forEach((node) => {
+ if (node.nodeType === Node.ELEMENT_NODE) {
+ if (node.tagName?.toLowerCase() === 'media-player' ||
+ node.querySelector?.('media-player')) {
+ needsUpdate = true;
+ }
+ }
+ });
+ }
+ });
+
+ if (needsUpdate) {
+ vidstackApplyTranslations();
+ }
+ });
+
+ observer.observe(document.body, { childList: true, subtree: true });
+ }
+
+ // Initialize on various events (cover different CMS scenarios)
+ ['DOMContentLoaded', 'load', 'vsrun'].forEach(event =>
+ document.addEventListener(event, vidstackInitialize)
+ );
+
+ // REDAXO backend support - multiple events to ensure initialization
+ if (typeof jQuery !== 'undefined') {
+ jQuery(document).on('rex:ready', function() {
+ vidstackInitialize();
+ });
+
+ // Also initialize on pjax updates (mediapool, content edit)
+ jQuery(document).on('pjax:end', function() {
+ vidstackInitialize();
+ });
+ }
+
+ // If DOM is already ready or script loads late (e.g., with defer), initialize immediately
+ if (document.readyState === 'complete' ||
+ (document.readyState === 'interactive' && document.body)) {
+ console.log('Vidstack: Initializing immediately (readyState:', document.readyState + ')');
+ vidstackInitialize();
+ } else {
+ console.log('Vidstack: Waiting for DOM (readyState:', document.readyState + ')');
+ }
+})();
diff --git a/assets/vidstack.css b/assets/vidstack.min.css
similarity index 100%
rename from assets/vidstack.css
rename to assets/vidstack.min.css
diff --git a/assets/vidstack.js b/assets/vidstack.min.js
similarity index 100%
rename from assets/vidstack.js
rename to assets/vidstack.min.js
diff --git a/assets/vidstack_helper.js b/assets/vidstack_helper.js
deleted file mode 100644
index d511445..0000000
--- a/assets/vidstack_helper.js
+++ /dev/null
@@ -1,211 +0,0 @@
-(function () {
- let translations = {};
-
- async function loadTranslations() {
- try {
- translations = await (await fetch('/assets/addons/vidstack/translations.json')).json();
- } catch (error) {
- console.error('Fehler beim Laden der Übersetzungen:', error);
- translations = { de: {}, en: {} };
- }
- }
-
- function getText(key, lang = 'en') {
- return (translations[lang] && translations[lang][key]) || key;
- }
-
- const consentKey = 'video_consent';
- let videoConsent = JSON.parse(localStorage.getItem(consentKey) || '{}');
-
- function setConsent(platform) {
- // Vidstack eigenes Consent-System
- videoConsent[platform] = true;
- localStorage.setItem(consentKey, JSON.stringify(videoConsent));
- document.cookie = `${platform}_consent=true; path=/; max-age=${30 * 24 * 60 * 60}; SameSite=Lax; Secure`;
-
- // Optional: Consent Manager Logging (falls installiert)
- if (typeof consent_manager_util !== 'undefined') {
- // Nutze Standard-UIDs: 'youtube' oder 'vimeo'
- try {
- // API Call für DSGVO-konformes Logging
- fetch('?rex-api-call=consent_manager_inline_log', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/x-www-form-urlencoded',
- },
- body: new URLSearchParams({
- service: platform,
- consent_type: 'inline',
- source: 'vidstack'
- })
- }).catch(err => console.warn('Vidstack: Consent Manager Logging fehlgeschlagen', err));
-
- // Consent Manager Cookie setzen (parallel zu Vidstack)
- consent_manager_util.set_consent(platform);
- } catch (e) {
- console.warn('Vidstack: Consent Manager Integration Fehler', e);
- }
- }
- }
-
- function hasConsent(platform) {
- return videoConsent[platform] === true || document.cookie.includes(`${platform}_consent=true`);
- }
-
- function rebuildMediaPlayer(existingPlayer, platform, videoId, placeholder) {
- // Store the original attributes and innerHTML, including the style attribute
- const attributes = {};
- for (let i = 0; i < existingPlayer.attributes.length; i++) {
- const attr = existingPlayer.attributes[i];
- if (attr.name !== 'src') { // Preserve all attributes except 'src'
- attributes[attr.name] = attr.value;
- }
- }
- const innerHTML = existingPlayer.innerHTML;
-
- // Remove the existing media player
- existingPlayer.remove();
-
- // Create a new media player element with the correct src from the start
- const newPlayer = document.createElement('media-player');
-
- // Set all attributes including the new src
- for (const [name, value] of Object.entries(attributes)) {
- newPlayer.setAttribute(name, value);
- }
- newPlayer.setAttribute('src', `${platform}/${videoId}`);
-
- // Set inner HTML
- newPlayer.innerHTML = innerHTML;
-
- // Insert the new player
- if (placeholder?.classList.contains('consent-placeholder')) {
- placeholder.insertAdjacentElement('afterend', newPlayer);
- placeholder.style.display = 'none';
- } else {
- // If no placeholder, insert at the end of the container
- const container = placeholder?.parentNode || document.body;
- container.appendChild(newPlayer);
- }
-
- return newPlayer;
- }
-
- function loadVideo(placeholder) {
- const { platform, videoId } = placeholder.dataset;
- const mediaPlayer = placeholder.nextElementSibling;
-
- if (mediaPlayer?.tagName.toLowerCase() === 'media-player') {
- rebuildMediaPlayer(mediaPlayer, platform, videoId, placeholder);
- } else {
- console.error('Media player element not found');
- }
- }
-
- function updatePlaceholder(placeholder, consented) {
- const button = placeholder.querySelector('.consent-button');
- const lang = document.documentElement.lang || 'en';
-
- if (button) {
- button.textContent = getText(consented ? 'Consent given' : 'Load Video', lang);
- button.disabled = consented;
- }
- }
-
- function initializePlaceholder(placeholder) {
- const platform = placeholder.dataset.platform;
- const consented = hasConsent(platform);
-
- updatePlaceholder(placeholder, consented);
-
- if (consented) {
- loadVideo(placeholder);
- } else {
- const consentButton = placeholder.querySelector('.consent-button');
- if (consentButton) {
- consentButton.addEventListener('click', () => {
- setConsent(platform);
- updatePlaceholder(placeholder, true);
- loadVideo(placeholder);
- });
- } else {
- console.error('Consent button not found in placeholder');
- }
- }
- }
-
- function initializeMediaPlayer(player) {
- const platform = player.dataset.videoPlatform;
- if (hasConsent(platform)) {
- const videoId = player.dataset.videoId;
-
- // Check if src is already set - if so, the player is already properly configured
- if (player.hasAttribute('src')) {
- // Make sure the player is visible and the placeholder is hidden
- player.style.display = '';
- const placeholder = player.previousElementSibling;
- if (placeholder?.classList.contains('consent-placeholder')) {
- placeholder.style.display = 'none';
- }
- return;
- }
-
- // Only recreate the player if no src is set
- const placeholder = player.previousElementSibling;
- rebuildMediaPlayer(player, platform, videoId, placeholder);
- }
- }
-
- function applyTranslations() {
- ['media-video-layout', 'media-audio-layout'].forEach(selector => {
- document.querySelectorAll(selector).forEach(layout => {
- const player = layout.closest('media-player');
- const lang = player?.getAttribute('lang') || 'en';
- layout.translations = translations[lang] || translations['en'];
- });
- });
- }
-
- function initializeElements() {
- document.querySelectorAll('.consent-placeholder').forEach(initializePlaceholder);
- document.querySelectorAll('media-player[data-video-platform]').forEach(initializeMediaPlayer);
- applyTranslations();
- }
-
- async function initializeScript() {
- await loadTranslations();
- initializeElements();
-
- const observer = new MutationObserver((mutations) => {
- mutations.forEach((mutation) => {
- if (mutation.type === 'childList') {
- mutation.addedNodes.forEach((node) => {
- if (node.nodeType === Node.ELEMENT_NODE) {
- if (node.classList.contains('consent-placeholder')) {
- initializePlaceholder(node);
- }
- if (node.tagName.toLowerCase() === 'media-player') {
- initializeMediaPlayer(node);
- applyTranslations();
- }
- }
- });
- }
- });
- });
-
- observer.observe(document.body, { childList: true, subtree: true });
- }
-
- ['DOMContentLoaded', 'vsrun'].forEach(event =>
- document.addEventListener(event, initializeScript)
- );
-
- if (typeof jQuery !== 'undefined') {
- jQuery(document).on('rex:ready', initializeScript);
- }
-
- if (['complete', 'interactive'].includes(document.readyState)) {
- initializeScript();
- }
-})();
diff --git a/assets/vs_js_out.css b/assets/vs_js_out.css
deleted file mode 100644
index ff58a30..0000000
--- a/assets/vs_js_out.css
+++ /dev/null
@@ -1,2167 +0,0 @@
-/* node_modules/vidstack/player/styles/default/theme.css */
-[data-media-player] {
- width: 100%;
- display: inline-flex;
- align-items: center;
- position: relative;
- contain: style;
- box-sizing: border-box;
- user-select: none;
-}
-[data-media-player] * {
- box-sizing: border-box;
-}
-:where([data-media-player][data-view-type=video]) {
- aspect-ratio: 16 / 9;
-}
-[data-media-player]:focus,
-[data-media-player]:focus-visible {
- outline: none;
-}
-[data-media-player][data-view-type=video][data-started]:not([data-controls]) {
- pointer-events: auto;
- cursor: none;
-}
-[data-media-player] slot {
- display: contents;
-}
-[data-media-provider] {
- display: flex;
- position: relative;
- box-sizing: border-box;
- align-items: center;
- border-radius: inherit;
- width: 100%;
- aspect-ratio: inherit;
- overflow: hidden;
-}
-[data-media-player]:not([data-view-type=audio]) [data-media-provider],
-[data-media-player][data-fullscreen] [data-media-provider] {
- height: 100%;
-}
-[data-media-player][data-view-type=audio] [data-media-provider] {
- display: contents;
- background-color: unset;
-}
-[data-media-provider] audio {
- width: 100%;
-}
-:where(video:not([width]):not([height]), iframe:not([width]):not([height])) {
- width: 100%;
- aspect-ratio: 16 / 9;
-}
-:where([data-media-provider] video),
-:where([data-media-provider] iframe) {
- aspect-ratio: inherit;
- display: inline-block;
- height: auto;
- object-fit: contain;
- touch-action: manipulation;
- border-radius: inherit;
- width: 100%;
-}
-[data-media-provider] iframe {
- height: 100%;
-}
-[data-media-player][data-view-type=audio] video,
-[data-media-player][data-view-type=audio] iframe {
- display: none;
-}
-[data-media-player][data-fullscreen] video {
- height: 100%;
-}
-[data-media-provider] iframe:not([src]) {
- display: none;
-}
-iframe.vds-youtube[data-no-controls] {
- height: 1000%;
-}
-.vds-blocker {
- position: absolute;
- inset: 0;
- width: 100%;
- height: auto;
- aspect-ratio: inherit;
- pointer-events: auto;
- border-radius: inherit;
- z-index: 1;
-}
-[data-ended] .vds-blocker {
- background-color: black;
-}
-.vds-icon:focus {
- outline: none;
-}
-.vds-google-cast {
- width: 100%;
- height: 100%;
- display: flex;
- align-items: center;
- justify-content: center;
- flex-direction: column;
- color: #dedede;
- font-family: sans-serif;
- font-weight: 500;
-}
-.vds-google-cast svg {
- --size: max(18%, 40px);
- width: var(--size);
- height: var(--size);
- margin-bottom: 8px;
-}
-.vds-google-cast-info {
- font-size: calc(var(--media-height) / 100 * 6);
-}
-:where(.vds-buffering-indicator) {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- display: flex;
- align-items: center;
- justify-content: center;
- pointer-events: none;
- z-index: 1;
-}
-:where(.vds-buffering-indicator) :where(.vds-buffering-icon, .vds-buffering-spinner) {
- opacity: 0;
- pointer-events: none;
- transition: var(--media-buffering-transition, opacity 200ms ease);
-}
-:where(.vds-buffering-indicator) :where(.vds-buffering-icon, svg.vds-buffering-spinner, .vds-buffering-spinner svg) {
- width: var(--media-buffering-size, 96px);
- height: var(--media-buffering-size, 96px);
-}
-:where(.vds-buffering-indicator) :where(.vds-buffering-track, circle[data-part=track]) {
- color: var(--media-buffering-track-color, #f5f5f5);
- opacity: var(--media-buffering-track-opacity, 0.25);
- stroke-width: var(--media-buffering-track-width, 8);
-}
-:where(.vds-buffering-indicator) :where(.vds-buffering-track-fill, circle[data-part=track-fill]) {
- color: var(--media-buffering-track-fill-color, var(--media-brand));
- opacity: var(--media-buffering-track-fill-opacity, 0.75);
- stroke-width: var(--media-buffering-track-fill-width, 9);
- stroke-dasharray: 100;
- stroke-dashoffset: var(--media-buffering-track-fill-offset, 50);
-}
-:where([data-buffering]) :where(.vds-buffering-icon, .vds-buffering-spinner) {
- opacity: 1;
- animation: var(--media-buffering-animation, vds-buffering-spin 1s linear infinite);
-}
-@keyframes vds-buffering-spin {
- to {
- transform: rotate(360deg);
- }
-}
-@media (prefers-reduced-motion) {
- :where([data-buffering]) :where(.vds-buffering-icon, .vds-buffering-spinner) {
- animation-duration: 8s;
- }
-}
-:where(.vds-button) {
- -webkit-tap-highlight-color: transparent;
- position: relative;
- display: inline-flex;
- justify-content: center;
- align-items: center;
- user-select: none;
- appearance: none;
- background: none;
- outline: none;
- border: none;
- border-radius: var(--media-button-border-radius, 8px);
- width: var(--media-button-size, 40px);
- height: var(--media-button-size, 40px);
- transition: transform 0.2s ease-out;
- contain: layout style;
- cursor: pointer;
- -webkit-user-select: none;
- -webkit-tap-highlight-color: transparent;
- touch-action: manipulation;
- flex-shrink: 0;
-}
-.vds-button {
- border: var(--media-button-border);
- color: var(--media-button-color, var(--media-controls-color, #f5f5f5));
- padding: var(--media-button-padding, 0px);
-}
-:where([data-fullscreen] .vds-button) {
- width: var(--media-fullscreen-button-size, 42px);
- height: var(--media-fullscreen-button-size, 42px);
-}
-@media screen and (max-width: 599px) {
- :where([data-fullscreen] .vds-button) {
- width: var(--media-sm-fullscreen-button-size, 42px);
- height: var(--media-sm-fullscreen-button-size, 42px);
- }
-}
-:where(.vds-button .vds-icon) {
- width: var(--media-button-icon-size, 80%);
- height: var(--media-button-icon-size, 80%);
- border-radius: var(--media-button-border-radius, 8px);
-}
-:where(.vds-menu-button .vds-icon) {
- display: flex !important;
-}
-:where(.vds-button[aria-hidden=true]) {
- display: none !important;
-}
-@media (hover: hover) and (pointer: fine) {
- .vds-button:hover {
- background-color: var(--media-button-hover-bg, rgb(255 255 255 / 0.2));
- }
- .vds-button:hover {
- transform: var(--media-button-hover-transform, scale(1.05));
- transition: var(--media-button-hover-transition, transform 0.2s ease-in);
- }
- .vds-button[aria-expanded=true] {
- transform: unset;
- }
-}
-@media (pointer: coarse) {
- .vds-button:hover {
- border-radius: var(--media-button-touch-hover-border-radius, 100%);
- background-color: var(--media-button-touch-hover-bg, rgb(255 255 255 / 0.2));
- }
-}
-:where(.vds-button:focus) {
- outline: none;
-}
-:where(.vds-button[data-focus], .vds-button:focus-visible) {
- box-shadow: var(--media-focus-ring);
-}
-:where(.vds-live-button) {
- min-width: auto;
- min-height: auto;
- width: var(--media-live-button-width, 40px);
- height: var(--media-live-button-height, 40px);
- padding: 0;
- display: flex;
- align-items: center;
- justify-content: center;
- cursor: pointer;
- user-select: none;
- appearance: none;
- background: none;
- outline: none;
- border: none;
-}
-:where(.vds-live-button-text) {
- font-family: var(--media-font-family, sans-serif);
- font-size: var(--media-live-button-font-size, 12px);
- font-weight: var(--media-live-button-font-weight, 600);
- letter-spacing: var(--media-live-button-letter-spacing, 1.5px);
- transition: color 0.3s ease;
-}
-.vds-live-button-text {
- background-color: var(--media-live-button-bg, #8a8a8a);
- border-radius: var(--media-live-button-border-radius, 2px);
- color: var(--media-live-button-color, #161616);
- padding: var(--media-live-button-padding, 1px 4px);
-}
-:where(.vds-live-button[data-focus] .vds-live-button-text) {
- box-shadow: var(--media-focus-ring);
-}
-:where(.vds-live-button[data-edge]) {
- cursor: unset;
-}
-.vds-live-button[data-edge] .vds-live-button-text {
- background-color: var(--media-live-button-edge-bg, #dc2626);
- color: var(--media-live-button-edge-color, #f5f5f5);
-}
-@media (pointer: fine) {
- :where(.vds-live-button:hover) {
- background-color: unset;
- }
-}
-.vds-button:not([data-paused]) .vds-play-icon,
-.vds-button[data-ended] .vds-play-icon,
-.vds-button[data-paused] .vds-pause-icon,
-.vds-button[data-ended] .vds-pause-icon,
-.vds-button:not([data-ended]) .vds-replay-icon,
-.vds-button[data-active] .vds-pip-enter-icon,
-.vds-button:not([data-active]) .vds-pip-exit-icon,
-.vds-button[data-active] .vds-fs-enter-icon,
-.vds-button:not([data-active]) .vds-fs-exit-icon,
-.vds-button:not([data-active]) .vds-cc-on-icon,
-.vds-button[data-active] .vds-cc-off-icon,
-.vds-button:not([data-muted]) .vds-mute-icon,
-.vds-button:not([data-state=low]) .vds-volume-low-icon,
-.vds-button:not([data-state=high]) .vds-volume-high-icon {
- display: none;
-}
-:where(.vds-captions) {
- --overlay-padding: var(--media-captions-padding, 1%);
- --cue-color: var(--media-user-text-color, var(--media-cue-color, white));
- --cue-bg-color: var(--media-user-text-bg, var(--media-cue-bg, rgba(0, 0, 0, 0.7)));
- --cue-default-font-size: var(--media-cue-font-size, calc(var(--overlay-height) / 100 * 4.5));
- --cue-font-size: calc(var(--cue-default-font-size) * var(--media-user-font-size, 1));
- --cue-line-height: var(--media-cue-line-height, calc(var(--cue-font-size) * 1.2));
- --cue-padding-x: var(--media-cue-padding-x, calc(var(--cue-font-size) * 0.6));
- --cue-padding-y: var(--media-cue-padding-x, calc(var(--cue-font-size) * 0.4));
- --cue-padding: var(--cue-padding-y) var(--cue-padding-x);
- position: absolute;
- inset: 0;
- z-index: 1;
- contain: layout style;
- margin: var(--overlay-padding);
- font-size: var(--cue-font-size);
- font-family: var(--media-user-font-family, sans-serif);
- box-sizing: border-box;
- pointer-events: none;
- user-select: none;
- word-spacing: normal;
- word-break: break-word;
-}
-:where([data-fullscreen][data-orientation=portrait] .vds-captions) {
- --cue-default-font-size: var(--media-cue-font-size, calc(var(--overlay-width) / 100 * 4.5));
-}
-:where([data-view-type=audio] .vds-captions) {
- position: relative;
- margin: 0;
-}
-:where(.vds-captions[aria-hidden=true]) {
- opacity: 0;
- visibility: hidden;
-}
-.vds-captions[data-example] {
- opacity: 1 !important;
- visibility: visible !important;
-}
-:where([data-view-type=video] .vds-captions [data-part=cue-display][data-example]) {
- --cue-text-align: center;
- --cue-width: 100%;
- --cue-top: 90%;
- --cue-left: 0%;
-}
-:where([data-view-type=audio] .vds-captions [data-part=cue-display]) {
- --cue-width: 100%;
- position: relative !important;
-}
-:where(.vds-captions [data-part=cue-display]) {
- position: absolute;
- direction: ltr;
- overflow: visible;
- contain: content;
- top: var(--cue-top);
- left: var(--cue-left);
- right: var(--cue-right);
- bottom: var(--cue-bottom);
- width: var(--cue-width, auto);
- height: var(--cue-height, auto);
- box-sizing: border-box;
- transform: var(--cue-transform);
- text-align: var(--cue-text-align);
- writing-mode: var(--cue-writing-mode, unset);
- white-space: pre-line;
- unicode-bidi: plaintext;
- min-width: min-content;
- min-height: min-content;
- background-color: var(--media-user-display-bg, var(--media-cue-display-bg));
- border-radius: var(--media-cue-display-border-radius);
-}
-.vds-captions [data-part=cue-display] {
- padding: var(--media-cue-display-padding);
-}
-:where(.vds-captions[data-dir=rtl] [data-part=cue-display]) {
- direction: rtl;
-}
-:where(.vds-captions [data-part=cue]) {
- display: inline-block;
- contain: content;
- font-variant: var(--media-user-font-variant);
- border: var(--media-cue-border, unset);
- border-radius: var(--media-cue-border-radius, 2px);
- backdrop-filter: var(--media-cue-backdrop, blur(8px));
- line-height: var(--cue-line-height);
- box-sizing: border-box;
- box-shadow: var(--media-cue-box-shadow, var(--cue-box-shadow));
- white-space: var(--cue-white-space, pre-wrap);
- outline: var(--cue-outline);
- text-shadow: var(--media-user-text-shadow, var(--cue-text-shadow));
-}
-.vds-captions [data-part=cue] {
- background-color: var(--cue-bg-color);
- color: var(--cue-color);
- padding: var(--cue-padding);
-}
-:where(.vds-captions [data-part=cue-display][data-vertical] [data-part=cue]) {
- --cue-padding: var(--cue-padding-x) var(--cue-padding-y);
-}
-:where(.vds-captions [data-part=region]) {
- --anchor-x-percent: calc(var(--region-anchor-x) / 100);
- --anchor-x: calc(var(--region-width) * var(--anchor-x-percent));
- --anchor-y-percent: calc(var(--region-anchor-y) / 100);
- --anchor-y: calc(var(--region-height) * var(--anchor-y-percent));
- --vp-anchor-x: calc(var(--region-viewport-anchor-x) * 1%);
- --vp-anchor-y-percent: calc(var(--region-viewport-anchor-y) / 100);
- --vp-anchor-y: calc(var(--overlay-height) * var(--vp-anchor-y-percent));
- position: absolute;
- display: inline-flex;
- flex-flow: column;
- justify-content: flex-start;
- width: var(--region-width);
- height: var(--region-height);
- min-height: 0px;
- max-height: var(--region-height);
- writing-mode: horizontal-tb;
- top: var(--region-top, calc(var(--vp-anchor-y) - var(--anchor-y)));
- left: var(--region-left, calc(var(--vp-anchor-x) - var(--anchor-x)));
- right: var(--region-right);
- bottom: var(--region-bottom);
- overflow: hidden;
- overflow-wrap: break-word;
- box-sizing: border-box;
-}
-:where(.vds-captions [data-part=region][data-active]) {
-}
-:where(.vds-captions [data-part=region][data-scroll=up]) {
- justify-content: end;
-}
-:where(.vds-captions [data-part=region][data-active][data-scroll=up]) {
- transition: top 0.433s;
-}
-:where(.vds-captions [data-part=region] > [data-part=cue-display]) {
- position: relative;
- width: auto;
- left: var(--cue-offset);
- height: var(--cue-height, auto);
- text-align: var(--cue-text-align);
- unicode-bidi: plaintext;
- margin-top: 2px;
-}
-:where(.vds-captions [data-part=region] [data-part=cue]) {
- position: relative;
- border-radius: 0px;
-}
-:where(.vds-chapter-title) {
- --color: var(--media-chapter-title-color, rgba(255 255 255 / 0.64));
- display: inline-block;
- font-family: var(--media-font-family, sans-serif);
- font-size: var(--media-chapter-title-font-size, 16px);
- font-weight: var(--media-chapter-title-font-weight, 400);
- color: var(--color);
- flex: 1 1 0%;
- padding-inline: 6px;
- overflow: hidden;
- text-align: start;
- white-space: nowrap;
- text-overflow: ellipsis;
-}
-.vds-chapter-title::before {
- content: var(--media-chapter-title-separator, "\2022");
- display: inline-block;
- margin-right: var(--media-chapter-title-separator-gap, 6px);
- color: var(--media-chapter-title-separator-color, var(--color));
-}
-.vds-chapter-title:empty::before {
- content: "";
- margin: 0;
-}
-:where(.vds-controls),
-:where(.vds-controls-group) {
- position: relative;
- display: inline-block;
- width: 100%;
- box-sizing: border-box;
-}
-:where([data-view-type=audio] .vds-controls) {
- display: inline-block;
- max-width: 100%;
-}
-:where([data-view-type=video] .vds-controls) {
- display: flex;
- position: absolute;
- flex-direction: column;
- inset: 0;
- width: 100%;
- height: 100%;
- z-index: 10;
- opacity: 0;
- visibility: hidden;
- pointer-events: none;
- padding: var(--media-controls-padding, 0px);
- transition: var(--media-controls-out-transition, opacity 0.2s ease-out);
-}
-:where([data-view-type=video] .vds-controls[data-visible]) {
- opacity: 1;
- visibility: visible;
- transition: var(--media-controls-in-transition, opacity 0.2s ease-in);
-}
-:where(.vds-controls-spacer) {
- flex: 1 1 0%;
- pointer-events: none;
-}
-:where(.vds-gestures) {
- display: contents;
-}
-:where(.vds-gesture) {
- position: absolute;
- display: block;
- contain: content;
- z-index: 0;
- opacity: 0;
- visibility: hidden;
- pointer-events: none !important;
-}
-:where(.vds-icon svg) {
- display: block;
- width: 100%;
- height: 100%;
- vertical-align: middle;
-}
-:where(.vds-kb-action.hidden) {
- opacity: 0;
-}
-:where(.vds-kb-text-wrapper) {
- text-align: center;
- position: absolute;
- left: 0;
- right: 0;
- top: var(--media-kb-text-top, 10%);
- z-index: 20;
- pointer-events: none;
-}
-:where(.vds-kb-text) {
- display: inline-block;
- font-size: var(--media-kb-text-size, 150%);
- font-family: var(--media-font-family, sans-serif);
- backdrop-filter: blur(2px);
- border-radius: var(--media-kb-border-radius, 2.5px);
- pointer-events: none;
-}
-.vds-kb-text {
- color: var(--media-kb-text-color, var(--default-color));
- background-color: var(--media-kb-text-bg, var(--default-bg));
- padding: var(--media-kb-text-padding, 10px 20px);
-}
-.light .vds-kb-text {
- --default-color: #1a1a1a;
- --default-bg: rgb(240 240 240 / 0.6);
-}
-.dark .vds-kb-text {
- --default-color: #f5f5f5;
- --default-bg: rgb(10 10 10 / 0.6);
-}
-:where(.vds-kb-text:empty) {
- display: none;
-}
-:where(.vds-kb-bezel) {
- --size: var(--media-kb-bezel-size, 52px);
- position: absolute;
- left: 50%;
- top: 45%;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- width: var(--size);
- height: var(--size);
- margin-left: calc(-1 * calc(var(--size) / 2));
- margin-right: calc(-1 * calc(var(--size) / 2));
- z-index: 20;
- backdrop-filter: blur(2px);
- background-color: var(--media-kb-bezel-bg, var(--default-bg));
- animation: var(--media-kb-bezel-animation, vds-bezel-fade 0.35s linear 1 normal forwards);
- border-radius: var(--media-kb-bezel-border-radius, calc(var(--size) / 2));
- pointer-events: none;
-}
-.vds-kb-bezel:not(:has(svg)) {
- display: none !important;
-}
-.light .vds-kb-bezel {
- --default-bg: rgb(255 255 255 / 0.6);
-}
-.dark .vds-kb-bezel {
- --default-bg: rgb(10 10 10 / 0.6);
-}
-@media (prefers-reduced-motion) {
- :where(.vds-kb-bezel) {
- animation: none;
- }
-}
-:where(.vds-kb-bezel:has(slot:empty)) {
- opacity: 0;
-}
-:where(.vds-kb-action[data-action=seek-forward] .vds-kb-bezel) {
- top: 45%;
- left: unset;
- right: 10%;
-}
-:where(.vds-kb-action[data-action=seek-backward] .vds-kb-bezel) {
- top: 45%;
- left: 10%;
-}
-:where(.vds-kb-icon) {
- --size: var(--media-kb-icon-size, 38px);
- width: var(--size);
- height: var(--size);
-}
-.vds-kb-icon {
- color: var(--media-kb-icon-color, var(--default-color));
-}
-.light .vds-kb-icon {
- --default-color: #1a1a1a;
-}
-.dark .vds-kb-icon {
- --default-color: #f5f5f5;
-}
-@keyframes vds-bezel-fade {
- 0% {
- opacity: 1;
- }
- 100% {
- opacity: 0;
- transform: scale(2);
- }
-}
-:where(.vds-menu-items) {
- --color-inverse: var(--media-menu-color-inverse, var(--default-inverse));
- --color-gray-50: var(--media-menu-color-gray-50, var(--default-gray-50));
- --color-gray-100: var(--media-menu-color-gray-100, var(--default-gray-100));
- --color-gray-200: var(--media-menu-color-gray-200, var(--default-gray-200));
- --color-gray-300: var(--media-menu-color-gray-300, var(--default-gray-300));
- --color-gray-400: var(--media-menu-color-gray-400, var(--default-gray-400));
- --text-color: var(--media-menu-text-color, var(--default-text));
- --text-secondary-color: var(--media-menu-text-secondary-color, var(--default-text-secondary));
- --root-border: var(--media-menu-border, var(--default-root-border));
-}
-.light .vds-menu-items {
- --default-inverse: black;
- --default-gray-50: rgb(80 80 80 / 0.15);
- --default-gray-100: rgb(80 80 80 / 0.45);
- --default-gray-200: rgb(235 235 235 / 0.6);
- --default-gray-300: rgb(238 238 238);
- --default-gray-400: rgb(250 250 250);
- --default-text: #1a1a1a;
- --default-text-secondary: #6b6b6b;
- --default-root-border: 1px solid rgb(10 10 10 / 0.1);
-}
-.dark .vds-menu-items {
- --default-inverse: white;
- --default-gray-50: rgb(245 245 245 / 0.1);
- --default-gray-100: rgb(245 245 245 / 0.45);
- --default-gray-200: rgb(10 10 10 / 0.6);
- --default-gray-300: rgb(27 27 27);
- --default-gray-400: rgb(10 10 10);
- --default-text: #f5f5f5;
- --default-text-secondary: #8a8a8a;
- --default-root-border: 1px solid rgb(255 255 255 / 0.1);
-}
-:where(.vds-menu-items) {
- --font-family: var(--media-font-family, sans-serif);
- --font-size: var(--media-menu-font-size, 14px);
- --font-weight: var(--media-menu-font-weight, 500);
- --root-bg: var(--media-menu-bg, var(--color-gray-400));
- --root-padding: var(--media-menu-padding, 12px);
- --root-border-radius: var(--media-menu-border-radius, 4px);
- --divider: var(--media-menu-divider, 1px solid var(--color-gray-50));
- --section-bg: var(--media-menu-section-bg, var(--color-gray-300));
- --section-border: var(--media-menu-section-border);
- --section-divider: var(--media-menu-section-divider, var(--divider));
- --top-bar-bg: var(--media-menu-top-bar-bg, var(--color-gray-200));
- --top-bar-divider: var(--media-menu-divider, transparent);
- --text-hint-color: var(--media-menu-hint-color, var(--text-secondary-color));
- --chapter-divider: var(--media-chapters-divider, var(--divider));
- --chapter-active-bg: var(--media-chapters-item-active-bg, var(--color-gray-50));
- --chapter-active-border-left: var(--media-chapters-item-active-border-left);
- --chapter-progress-bg: var(--media-chapters-progress-bg, var(--color-inverse));
- --chapter-time-font-size: var(--media-chapters-time-font-size, 12px);
- --chapter-time-font-weight: var(--media-chapters-time-font-weight, 500);
- --chapter-time-gap: var(--media-chapters-time-gap, 6px);
- --chapter-duration-bg: var(--media-chapters-duration-bg);
- --item-border: var(--media-menu-item-border, 0);
- --item-bg: var(--media-menu-item-bg, transparent);
- --item-hover-bg: var(--media-menu-item-hover-bg, var(--color-gray-50));
- --item-icon-size: var(--media-menu-item-icon-size, 18px);
- --item-padding: var(--media-menu-item-padding, 10px);
- --item-min-height: var(--media-menu-item-height, 40px);
- --item-border-radius: var(--media-menu-item-border-radius, 2px);
- --scrollbar-track-bg: var(--media-menu-scrollbar-track-bg, transparent);
- --scrollbar-thumb-bg: var(--media-menu-scrollbar-thumb-bg, var(--color-gray-50));
- --webkit-scrollbar-bg: var(--color-gray-400);
- --webkit-scrollbar-track-bg: var(--media-menu-scrollbar-track-bg, var(--color-gray-50));
- --checkbox-bg: var(--media-menu-checkbox-bg, var(--color-gray-100));
- --checkbox-active-bg: var(--media-menu-checkbox-bg-active, #1ba13f);
- --checkbox-handle-bg: var(--media-menu-checkbox-handle-bg, #f5f5f5);
- --checkbox-handle-border: var(--media-menu-checkbox-handle-border);
- --radio-icon-color: var(--media-menu-radio-icon-color, var(--text-color));
-}
-:where(.vds-menu[data-root] media-menu[data-root]) {
- display: contents;
-}
-:where(.vds-menu) {
- font-family: var(--font-family);
- font-size: var(--font-size);
- font-weight: var(--font-weight);
-}
-:where(.vds-menu[data-disabled][data-root]) {
- display: none;
-}
-:where(.vds-menu[data-submenu]) {
- display: inline-block;
-}
-:where(.vds-menu-items:focus) {
- outline: none;
-}
-:where(.vds-menu-item:focus, .vds-radio:focus) {
- outline: none;
-}
-:where(.vds-menu-item:focus-visible, .vds-menu-item[data-focus], .vds-radio:focus-visible, .vds-radio[data-focus]) {
- outline: none;
- box-shadow: var(--media-focus-ring);
-}
-:where(.vds-menu[data-open] .vds-tooltip-content) {
- display: none !important;
-}
-.vds-menu-items [data-hidden] {
- display: none !important;
-}
-@media (prefers-reduced-motion: no-preference) {
- :where(.vds-menu-items) {
- scroll-behavior: smooth;
- }
-}
-:where(.vds-menu-items) {
- box-sizing: border-box;
- min-width: var(--media-menu-min-width, 280px);
- scrollbar-width: thin;
- scrollbar-color: var(--scrollbar-thumb-bg) var(--scrollbar-track-bg);
- transform: translate3d(0, 0, 0);
-}
-:where(.vds-menu-items)::-webkit-scrollbar {
- background-color: var(--webkit-scrollbar-bg);
- border-radius: var(--root-border-radius);
- height: 6px;
- width: 5px;
-}
-:where(.vds-menu-items)::-webkit-scrollbar-track {
- background-color: var(--webkit-scrollbar-track-bg);
- border-radius: 4px;
-}
-:where(.vds-menu-items)::-webkit-scrollbar-thumb {
- background-color: var(--scrollbar-thumb-bg);
- border-radius: 4px;
-}
-:where(.vds-menu-items)::-webkit-scrollbar-corner {
- background-color: var(--scrollbar-thumb-bg);
-}
-:where(.vds-menu-button) {
- outline: none;
- box-sizing: border-box;
-}
-:where(.vds-menu-button .vds-rotate-icon) {
- transition: transform 0.2s ease-out;
-}
-:where(.vds-menu-button[aria-expanded=true] .vds-rotate-icon) {
- transform: rotate(var(--media-menu-icon-rotate-deg, 90deg));
- transition: transform 0.2s ease-in;
-}
-:where(.vds-menu-button) {
- display: inline-flex;
- align-items: center;
- justify-content: center;
-}
-@media (prefers-reduced-motion) {
- :where(.vds-menu-button .vds-rotate-icon) {
- transition: unset;
- }
-}
-:where(.vds-menu-items) {
- display: flex;
- align-items: center;
- flex-direction: column;
- font-family: var(--font-family);
- font-size: var(--font-size);
- font-weight: var(--font-weight);
- transition: height 0.35s ease;
-}
-@media (prefers-reduced-motion) {
- :where(.vds-menu-items) {
- transition: unset;
- }
-}
-:where(.vds-menu-items[data-root]) {
- background-color: var(--root-bg);
- border-radius: var(--root-border-radius);
- box-shadow: var(--media-menu-box-shadow);
- backdrop-filter: blur(4px);
- height: var(--menu-height, auto);
- will-change: width, height;
- overflow-y: auto;
- overscroll-behavior: contain;
- opacity: 0;
- z-index: 9999999;
- box-sizing: border-box;
- max-height: var(--media-menu-max-height, 250px);
- filter: var( --media-menu-filter, drop-shadow(0 4px 3px rgb(0 0 0 / 0.07)) drop-shadow(0 2px 2px rgb(0 0 0 / 0.06)) );
-}
-.vds-menu-items[data-root] {
- border: var(--root-border);
- padding: var(--root-padding);
-}
-:where([data-view-type=video]) :where(.vds-menu-items[data-root]) {
- max-height: var(--media-menu-video-max-height, calc(var(--player-height) * 0.7));
-}
-:where(.vds-menu-items[data-transition=height]) {
- --scrollbar-thumb-bg: rgba(0, 0, 0, 0);
- pointer-events: none;
- overflow: hidden;
-}
-.vds-menu-button[aria-disabled=true],
-.vds-menu-item[aria-disabled=true],
-.vds-menu-item[data-disabled] {
- display: none;
-}
-:where(.vds-menu-items[data-root]) {
- --enter-transform: translateY(0px);
- --exit-transform: translateY(12px);
-}
-:where(.vds-menu-items[data-root]:not([data-placement])) {
- --enter-transform: translateY(-24px);
-}
-:where(.vds-menu-items[data-root][aria-hidden=true]) {
- animation: var(--media-menu-exit-animation, vds-menu-exit 0.2s ease-out);
-}
-:where(.vds-menu-items[data-root][aria-hidden=false]) {
- animation: var(--media-menu-enter-animation, vds-menu-enter 0.3s ease-out);
- animation-fill-mode: forwards;
-}
-:where(.vds-menu-items[data-placement~=bottom]) {
- --enter-transform: translateY(0);
- --exit-transform: translateY(-12px);
-}
-@keyframes vds-menu-enter {
- from {
- opacity: 0;
- transform: var(--exit-transform);
- }
- to {
- opacity: 1;
- transform: var(--enter-transform);
- }
-}
-@keyframes vds-menu-exit {
- from {
- opacity: 1;
- transform: var(--enter-transform);
- }
- to {
- opacity: 0;
- transform: var(--exit-transform);
- }
-}
-@media (prefers-reduced-motion) {
- :where(.vds-menu-items) {
- animation: none;
- opacity: 1;
- }
-}
-:where(media-menu-portal) {
- display: contents;
-}
-:where(.vds-menu-items[data-root]:not([data-placement])) {
- position: fixed;
- left: 16px;
- right: 16px;
- top: unset;
- bottom: 0;
- max-height: var(--media-sm-menu-portrait-max-height, 40vh);
- max-height: var(--media-sm-menu-portrait-max-height, 40dvh);
-}
-:where(.vds-menu-items[data-root]:not([data-placement])) {
- max-width: 480px;
- margin: 0 auto;
-}
-@media (orientation: landscape) and (pointer: coarse) {
- :where(.vds-menu-items[data-root]:not([data-placement])) {
- max-height: var(--media-sm-menu-landscape-max-height, min(70vh, 400px));
- max-height: var(--media-sm-menu-landscape-max-height, min(70dvh, 400px));
- }
-}
-:where(.vds-menu[data-submenu] .vds-menu-button) {
- display: flex;
- align-items: center;
- justify-content: flex-start;
-}
-:where(.vds-menu-items[data-submenu]) {
- width: 100%;
-}
-:where(.vds-menu[aria-hidden=true]),
-:where(.vds-menu-items[data-submenu][aria-hidden=true]) {
- display: none;
-}
-:where(.vds-menu-item, .vds-radio) {
- position: relative;
- -webkit-tap-highlight-color: transparent;
- user-select: none;
- display: flex;
- align-items: center;
- justify-content: left;
- width: 100%;
- appearance: none;
- border: 0;
- border-radius: var(--item-border-radius);
- box-sizing: border-box;
- min-height: var(--item-min-height);
- font-size: var(--font-size);
- outline: none;
-}
-.vds-menu-item,
-.vds-radio {
- color: var(--text-color);
- background-color: var(--item-bg);
- padding: var(--item-padding);
-}
-.vds-menu-item:focus-visible,
-.vds-menu-item[data-focus],
-.vds-radio:focus-visible,
-.vds-radio[data-focus] {
- cursor: pointer;
- background-color: var(--item-hover-bg);
-}
-@media (hover: hover) and (pointer: fine) {
- .vds-menu-item[role]:hover,
- .vds-radio:hover {
- cursor: pointer;
- background-color: var(--item-hover-bg);
- }
-}
-:where(.vds-menu-items[data-submenu]) {
- align-items: flex-start;
- justify-content: center;
- flex-direction: column;
-}
-:where(.vds-menu-item[aria-expanded=true]) {
- font-weight: bold;
- border-radius: 0;
- border-top-left-radius: var(--item-border-radius);
- border-top-right-radius: var(--item-border-radius);
-}
-.vds-menu-item[aria-expanded=true] {
- border-bottom: var(--top-bar-divider);
-}
-:where(.vds-menu-item[aria-expanded=true]) {
- position: sticky;
- top: calc(-1 * var(--root-padding));
- left: 0;
- width: 100%;
- z-index: 10;
- backdrop-filter: blur(4px);
- margin-bottom: 4px;
-}
-.vds-menu-item[aria-expanded=true] {
- background-color: var(--top-bar-bg);
-}
-:where(.vds-menu-item-label) {
- flex: 1 0 0%;
- text-align: start;
-}
-:where(.vds-menu-item .vds-icon, .vds-radio .vds-icon) {
- --size: var(--item-icon-size);
- width: var(--size);
- height: var(--size);
- margin-right: var(--media-menu-item-icon-spacing, 6px);
-}
-:where(.vds-menu-open-icon, .vds-menu-close-icon) {
- --size: var(--media-menu-arrow-icon-size, 18px);
- width: var(--size);
- height: var(--size);
-}
-:where(.vds-menu-item-hint, .vds-menu-open-icon, .vds-radio-hint) {
- color: var(--text-hint-color);
- font-size: var(--media-menu-hint-font-size, 13px);
- font-weight: var(--media-menu-hint-font-weight, 400);
-}
-:where(.vds-menu-items .vds-menu-open-icon) {
- margin-right: 0;
-}
-:where(.vds-menu-items) :where(.vds-menu-item-hint, .vds-menu-open-icon) {
- margin-left: auto;
-}
-:where(.vds-menu-items) :where(.vds-menu-item-hint + .vds-menu-open-icon),
-:where(.vds-menu-item-hint + media-icon .vds-menu-open-icon),
-:where(.vds-menu-item-hint + slot > .vds-menu-open-icon) {
- margin-left: 2px;
-}
-:where(.vds-menu-item[aria-hidden=true]),
-:where(.vds-menu-item[aria-expanded=true] .vds-menu-open-icon) {
- display: none !important;
-}
-:where(.vds-menu-items) :where(.vds-menu-item[aria-disabled=true], .vds-menu-item[data-disabled]) :where(.vds-menu-open-icon) {
- opacity: 0;
-}
-:where(.vds-menu-close-icon),
-:where(.vds-menu-item[aria-expanded=true] > .vds-icon) {
- display: none !important;
-}
-:where(.vds-menu-item[aria-expanded=true] .vds-menu-close-icon) {
- display: inline !important;
- margin-left: calc(-1 * var(--item-padding) / 2);
-}
-:where(.vds-menu-checkbox) {
- --checkbox-width: var(--media-menu-checkbox-width, 40px);
- --checkbox-height: var(--media-menu-checkbox-height, 18px);
- --checkbox-top: calc((var(--checkbox-height) - var(--checkbox-diameter)) / 2);
- --checkbox-diameter: var( --media-menu-checkbox-handle-diameter, calc(var(--checkbox-height) - 2px) );
- --checkbox-gap: var(--media-menu-checkbox-gap, 2.5px);
- position: relative;
- display: inline-block;
- width: var(--checkbox-width);
- height: var(--checkbox-height);
- border-radius: calc(var(--checkbox-height) / 2);
- transition: 0.3s all ease-in-out;
- box-sizing: border-box;
- cursor: pointer;
- pointer-events: auto;
-}
-.vds-menu-checkbox {
- background-color: var(--checkbox-bg);
-}
-:where(.vds-menu-checkbox:focus-visible) {
- outline: none;
- box-shadow: var(--media-focus-ring);
-}
-.vds-menu-checkbox[aria-checked=true] {
- background-color: var(--checkbox-active-bg);
-}
-:where(.vds-menu-checkbox)::after {
- content: "";
- display: inline-block;
- width: var(--checkbox-diameter);
- height: var(--checkbox-diameter);
- border-radius: calc(var(--checkbox-diameter) / 2);
- position: absolute;
- top: var(--checkbox-top);
- transform: translateX(var(--checkbox-gap));
- transition: 0.3s all ease-in-out;
- border: var(--checkbox-handle-border);
- box-sizing: border-box;
-}
-.vds-menu-checkbox::after {
- background-color: var(--checkbox-handle-bg);
-}
-:where(.vds-menu-checkbox[aria-checked=true])::after {
- transform: translateX(calc(var(--checkbox-width) - var(--checkbox-diameter) - var(--checkbox-gap)));
-}
-@media (prefers-reduced-motion: no-preference) {
- :where(.vds-menu-checkbox[data-active])::after {
- width: calc(var(--checkbox-width) - calc(var(--checkbox-gap) * 2));
- }
-}
-:where(.vds-menu-checkbox[aria-checked=true][data-active])::after {
- transform: translateX(var(--checkbox-gap));
-}
-:where(.vds-menu-items .vds-slider) {
- --media-slider-track-bg: var(--media-menu-slider-track-bg, var(--color-gray-50));
- --media-slider-track-fill-bg: var(--media-menu-slider-track-fill-bg, var(--color-inverse));
- --media-slider-height: var(--media-menu-slider-height, 32px);
- --track-focus-height: var(--track-height) !important;
-}
-:where(.vds-menu-items .vds-slider-thumb) {
- opacity: 1 !important;
-}
-:where(.vds-menu-slider-item.group) {
- flex-direction: column;
-}
-:where(.vds-menu-slider-title) {
- margin-top: 4px;
-}
-:where(.vds-menu-slider-body) {
- width: 100%;
- display: flex;
- align-items: center;
- margin-top: 6px;
-}
-:where(.vds-menu-slider-item .vds-icon) {
- margin: 0;
- color: var(--text-hint-color);
-}
-:where(.vds-menu-slider-item[data-min] .vds-icon.down, .vds-menu-slider-item[data-max] .vds-icon.up) {
- color: var(--text-color);
- animation: 0.6s ease-in-out vds-slider-icon;
- transition: all 1.2s ease;
-}
-@keyframes vds-slider-icon {
- 0% {
- transform: scale(1);
- }
- 50% {
- transform: scale(1.25);
- }
- 100% {
- transform: scale(1);
- }
-}
-:where(.vds-menu-items .vds-slider-track-fill) {
- transition: opacity 0.3s ease;
-}
-:where(.vds-menu-items .vds-slider[data-active] .vds-slider-track-fill) {
- opacity: 0;
-}
-:where(.vds-radio-group) {
- box-sizing: border-box;
- width: 100%;
- display: flex;
- flex-direction: column;
-}
-.vds-radio {
- cursor: pointer;
- contain: content;
- padding-left: calc(var(--item-icon-size) + var(--item-padding));
-}
-.vds-radio[aria-checked=true] {
- padding-left: 0;
-}
-.vds-radio .vds-icon {
- display: none;
- color: var(--radio-icon-color);
-}
-.vds-radio[aria-checked=true] .vds-icon {
- display: inline-block;
- margin-left: 6px;
-}
-:where(.vds-radio-hint) {
- margin-left: auto;
-}
-.vds-color-picker {
- width: 32px;
- height: 32px;
- border: 0;
- background-color: transparent;
- outline: none;
-}
-.vds-color-picker::-webkit-color-swatch {
- border-radius: 2px;
-}
-.vds-color-picker::-moz-color-swatch {
- border-radius: 2px;
-}
-.vds-color-picker:focus-visible::-webkit-color-swatch {
- box-shadow: var(--media-focus-ring);
-}
-.vds-color-picker:focus-visible::-moz-color-swatch {
- box-shadow: var(--media-focus-ring);
-}
-:where(.vds-menu-section) {
- width: 100%;
-}
-:where(.vds-menu-item + .vds-menu-section) {
- margin-top: 8px;
-}
-:where(.vds-menu-section + .vds-menu-section) {
- margin-top: 24px;
-}
-:where(.vds-menu-section:first-child) {
- margin-top: 8px;
-}
-:where(.vds-menu-section:last-child) {
- margin-bottom: 8px;
-}
-:where(.vds-menu-section-title),
-:where(.vds-menu-slider-title) {
- width: 100%;
- display: flex;
- align-items: center;
- justify-content: space-between;
- color: var(--text-secondary-color);
- font-size: var(--media-menu-section-header-font-size, 12px);
- font-weight: var(--media-menu-section-header-font-weight, 500);
- padding-inline: 2px;
-}
-:where(.vds-menu-section-body) {
- width: 100%;
-}
-:where(.vds-menu-section-title + .vds-menu-section-body) {
- margin-top: var(--media-menu-section-gap, 8px);
-}
-.vds-menu-section-body {
- background-color: var(--section-bg);
- border: var(--section-border);
- border-radius: var(--media-menu-section-border-radius, 2px);
-}
-:where(.vds-menu-section:not([data-open]) .vds-menu-item:not(:last-child)) {
- border-bottom: var(--section-divider);
-}
-:where(.vds-menu-section-body .vds-menu:last-child > .vds-menu-item) {
- border-bottom: unset;
-}
-.vds-menu-section[data-open],
-.vds-menu-section[data-open] > .vds-menu-section-body {
- display: contents !important;
- background-color: transparent !important;
-}
-.vds-menu-section[data-open] > .vds-menu-section-title,
-.vds-menu-section[data-open] > .vds-menu-section-body > :not([data-open]) {
- display: none;
-}
-:where(.vds-chapters-menu-items) {
- min-width: var(--media-chapters-min-width, var(--media-menu-min-width, 220px));
-}
-.vds-chapters-menu-items {
- padding: var(--media-chapters-padding, 0);
-}
-:where(.vds-menu-items:has(.vds-chapters-radio-group[data-thumbnails])) {
- min-width: var(--media-chapters-with-thumbnails-min-width, 300px);
-}
-:where(.vds-chapter-radio) {
- border-radius: 0;
-}
-.vds-chapter-radio {
- border-bottom: var(--chapter-divider);
- padding: var(--item-padding);
-}
-.vds-chapter-radio[aria-checked=true] {
- padding-left: var(--item-padding);
-}
-:where(.vds-chapter-radio:last-child) {
- border-bottom: 0;
-}
-.vds-chapter-radio[aria-checked=true] {
- background-color: var(--chapter-active-bg);
- border-left: var(--chapter-active-border-left);
-}
-:where(.vds-chapter-radio[aria-checked=true]):after {
- content: " ";
- width: var(--progress);
- height: var(--media-chapters-progress-height, 4px);
- position: absolute;
- bottom: 0;
- left: 0;
-}
-.vds-chapter-radio[aria-checked=true]:after {
- border-radius: var(--media-chapters-progress-border-radius, 0);
- background-color: var(--chapter-progress-bg);
-}
-.vds-chapters-radio-group :where(.vds-thumbnail) {
- margin-right: var(--media-chapters-thumbnail-gap, 12px);
- flex-shrink: 0;
- min-width: var(--media-chapters-thumbnail-min-width, 100px);
- min-height: var(--media-chapters-thumbnail-min-height, 56px);
- max-width: var(--media-chapters-thumbnail-max-width, 120px);
- max-height: var(--media-chapters-thumbnail-max-height, 68px);
-}
-.vds-chapters-radio-group .vds-thumbnail {
- border: var(--media-chapters-thumbnail-border, 0);
-}
-:where(.vds-chapters-radio-group .vds-chapter-radio-label) {
- color: var(--text-secondary-color);
- font-size: var(--font-size);
- font-weight: var(--font-weight);
- white-space: nowrap;
-}
-:where(.vds-chapter-radio[aria-checked=true] .vds-chapter-radio-label) {
- color: var(--text-color);
-}
-:where(.vds-chapters-radio-group .vds-chapter-radio-start-time) {
- display: inline-block;
- letter-spacing: var(--media-chapters-start-time-letter-spacing, 0.4px);
- border-radius: var(--media-chapters-start-time-border-radius, 2px);
- font-size: var(--chapter-time-font-size);
- font-weight: var(--chapter-time-font-weight);
- margin-top: var(--chapter-time-gap);
-}
-.vds-chapters-radio-group .vds-chapter-radio-start-time {
- color: var(--text-secondary-color);
- background-color: var(--section-bg);
- padding: var(--media-chapters-start-time-padding, 1px 4px);
-}
-:where(.vds-chapters-radio-group .vds-chapter-radio-duration) {
- color: var(--text-hint-color);
- font-size: var(--chapter-time-font-size);
- font-weight: var(--chapter-time-font-weight);
- margin-top: var(--chapter-time-gap);
-}
-.vds-chapters-radio-group .vds-chapter-radio-duration {
- background-color: var(--chapter-duration-bg);
- border-radius: var(--media-chapters-duration-border-radius, 2px);
-}
-.vds-chapters-radio-group:not([data-thumbnails]) :where(.vds-thumbnail, media-thumbnail) {
- display: none;
-}
-:where(.vds-chapter-radio-content) {
- display: flex;
- align-items: flex-start;
- flex-direction: column;
-}
-:where(.vds-chapters-radio-group:not([data-thumbnails]) .vds-chapter-radio-content) {
- width: 100%;
- flex-direction: row;
- display: flex;
- flex-wrap: wrap;
- align-items: center;
-}
-:where(.vds-chapters-radio-group:not([data-thumbnails]) .vds-chapter-radio-start-time) {
- margin-top: 0;
- margin-left: auto;
-}
-:where(.vds-chapters-radio-group:not([data-thumbnails]) .vds-chapter-radio-duration) {
- margin-top: 4px;
- flex-basis: 100%;
-}
-.vds-menu-items[data-keyboard] .vds-chapters-radio-group:focus-within {
- padding: var(--media-chapters-focus-padding, 4px);
-}
-:where(.vds-poster) {
- display: block;
- contain: content;
- position: absolute;
- top: 50%;
- transform: translateY(-50%);
- left: 0;
- opacity: 0;
- width: 100%;
- height: 100%;
- z-index: 1;
- border: 0;
- pointer-events: none;
- box-sizing: border-box;
- transition: opacity 0.2s ease-out;
- background-color: var(--media-poster-bg, black);
-}
-:where(.vds-poster img) {
- object-fit: inherit;
- object-position: inherit;
- pointer-events: none;
- user-select: none;
- -webkit-user-select: none;
- box-sizing: border-box;
-}
-.vds-poster :where(img) {
- border: 0;
- width: 100%;
- height: 100%;
- object-fit: contain;
-}
-:where(.vds-poster[data-hidden]) {
- display: none;
-}
-:where(.vds-poster[data-visible]) {
- opacity: 1;
-}
-.vds-poster:not(:defined),
-.vds-poster img:not([src]) {
- display: none;
-}
-:where(.vds-slider) {
- --width: var(--media-slider-width, 100%);
- --height: var(--media-slider-height, 48px);
- --thumb-size: var(--media-slider-thumb-size, 15px);
- --thumb-focus-size: var(--media-slider-focused-thumb-size, calc(var(--thumb-size) * 1.1));
- --track-width: var(--media-slider-track-width, 100%);
- --track-height: var(--media-slider-track-height, 5px);
- --track-focus-width: var(--media-slider-focused-track-width, var(--track-width));
- --track-focus-height: var(--media-slider-focused-track-height, calc(var(--track-height) * 1.25));
- display: inline-flex;
- align-items: center;
- width: var(--width);
- height: var(--height);
- margin: 0 calc(var(--thumb-size) / 2);
- position: relative;
- contain: layout style;
- outline: none;
- pointer-events: auto;
- cursor: pointer;
- user-select: none;
- touch-action: none;
- -webkit-user-select: none;
- -webkit-tap-highlight-color: transparent;
-}
-:where(.vds-slider[aria-hidden=true]) {
- display: none !important;
-}
-:where(.vds-slider[aria-disabled=true]) {
- cursor: unset;
-}
-:where(.vds-slider:focus) {
- outline: none;
-}
-:where(.vds-slider:not([data-chapters])[data-focus], .vds-slider:not([data-chapters]):focus-visible) :where(.vds-slider-track) {
- box-shadow: var(--media-focus-ring);
-}
-:where(.vds-slider .vds-slider-track) {
- z-index: 0;
- position: absolute;
- width: var(--track-width);
- height: var(--track-height);
- top: 50%;
- left: 0;
- border-radius: var(--media-slider-track-border-radius, 2px);
- transform: translateY(-50%) translateZ(0);
- background-color: var(--media-slider-track-bg, rgb(255 255 255 / 0.3));
- contain: strict;
-}
-:where(.vds-slider[data-focus], .vds-slider:focus-visible) :where(.vds-slider-track) {
- outline-offset: var(--thumb-size);
-}
-:where(.vds-slider:not([data-chapters])[data-active] .vds-slider-track) {
- width: var(--track-focus-width);
- height: var(--track-focus-height);
-}
-:where(.vds-slider .vds-slider-track-fill) {
- z-index: 2;
- background-color: var(--media-slider-track-fill-bg, var(--media-brand));
- width: var(--slider-fill, 0%);
- will-change: width;
-}
-:where(.vds-slider .vds-slider-thumb) {
- position: absolute;
- top: 50%;
- left: var(--slider-fill);
- opacity: 0;
- contain: layout size style;
- width: var(--thumb-size);
- height: var(--thumb-size);
- border: var(--media-slider-thumb-border, 1px solid #cacaca);
- border-radius: var(--media-slider-thumb-border-radius, 9999px);
- background-color: var(--media-slider-thumb-bg, #fff);
- transform: translate(-50%, -50%) translateZ(0);
- transition: opacity 0.15s ease-in;
- pointer-events: none;
- will-change: left;
- z-index: 2;
-}
-:where(.vds-slider[data-dragging], .vds-slider[data-focus], .vds-slider:focus-visible) :where(.vds-slider-thumb) {
- box-shadow: var(--media-slider-focused-thumb-shadow, 0 0 0 4px hsla(0, 0%, 100%, 0.4));
-}
-:where(.vds-slider[data-active] .vds-slider-thumb) {
- opacity: 1;
- transition: var(--media-slider-thumb-transition, opacity 0.2s ease-in, box-shadow 0.2s ease);
-}
-:where(.vds-slider[data-dragging] .vds-slider-thumb) {
- width: var(--thumb-focus-size);
- height: var(--thumb-focus-size);
-}
-:where(.vds-slider-value) {
- display: inline-block;
- contain: content;
- font-size: 14px;
- font-family: var(--media-font-family, sans-serif);
-}
-:where(.vds-slider-thumbnail) {
- display: block;
- contain: content;
- box-sizing: border-box;
-}
-:where(.vds-slider-video) {
- background-color: black;
- box-sizing: border-box;
- contain: content;
- display: inline-block;
- border: var(--media-thumbnail-border, 1px solid white);
-}
-:where(.vds-slider-video video) {
- display: block;
- height: auto;
- width: 156px;
-}
-:where(.vds-slider-video[data-loading]) {
- opacity: 0;
-}
-:where(.vds-slider-video[data-hidden], .vds-slider-video[data-hidden] video) {
- display: none;
- width: 0px;
-}
-:where(.vds-slider .vds-slider-preview) {
- display: flex;
- flex-direction: column;
- align-items: center;
- opacity: 0;
- background-color: var(--media-slider-preview-bg);
- border-radius: var(--media-slider-preview-border-radius, 2px);
- pointer-events: none;
- transition: opacity 0.2s ease-out;
- will-change: left, opacity;
- contain: layout paint style;
-}
-:where(.vds-slider-preview[data-visible]) {
- opacity: 1;
- transition: opacity 0.2s ease-in;
-}
-.vds-slider-value {
- background-color: var(--media-slider-value-bg, black);
- border-radius: var(--media-slider-value-border-radius, 2px);
- border: var(--media-slider-value-border);
- color: var(--media-slider-value-color, white);
- padding: var(--media-slider-value-padding, 1px 10px);
-}
-:where(.vds-slider-video:not([data-hidden]) + .vds-slider-chapter-title, .vds-slider-thumbnail:not([data-hidden]) + .vds-slider-chapter-title) {
- margin-top: var(--media-slider-chapter-title-gap, 6px);
-}
-:where(.vds-slider-video:not([data-hidden]) + .vds-slider-value, .vds-slider-thumbnail:not([data-hidden]) + .vds-slider-value, .vds-slider-chapter-title + .vds-slider-value) {
- margin-top: var(--media-slider-value-gap, 2px);
-}
-:where(.vds-slider[aria-orientation=vertical]) {
- --width: var(--media-slider-width, 48px);
- --height: var(--media-slider-height, 100%);
- --track-width: var(--media-slider-track-width, 4px);
- --track-height: var(--media-slider-track-height, 100%);
- --track-focus-width: var(--media-slider-focused-track-width, calc(var(--track-width) * 1.25));
- --track-focus-height: var(--media-slider-focused-track-height, var(--track-height));
- margin: calc(var(--thumb-size) / 2) 0;
-}
-:where(.vds-slider[aria-orientation=vertical] .vds-slider-track) {
- top: unset;
- bottom: 0;
- left: 50%;
- transform: translateX(-50%) translateZ(0);
-}
-:where(.vds-slider[aria-orientation=vertical] .vds-slider-track-fill) {
- width: var(--track-width);
- height: var(--slider-fill);
- will-change: height;
- transform: translateX(-50%) translateZ(0);
-}
-:where(.vds-slider[aria-orientation=vertical] .vds-slider-progress) {
- top: unset;
- bottom: 0;
- width: var(--track-width);
- height: var(--slider-progress, 0%);
- will-change: height;
-}
-:where(.vds-slider[aria-orientation=vertical] .vds-slider-thumb) {
- top: unset;
- bottom: var(--slider-fill);
- left: 50%;
- will-change: bottom;
- transform: translate(-50%, 50%) translateZ(0);
-}
-:where(.vds-slider[aria-orientation=vertical] .vds-slider-preview) {
- will-change: bottom, opacity;
-}
-:where([data-live] .vds-time-slider .vds-slider-track-fill) {
- background-color: var(--media-slider-track-fill-live-bg, #dc2626);
-}
-:where(.vds-time-slider .vds-slider-progress) {
- z-index: 1;
- left: 0;
- width: var(--slider-progress, 0%);
- will-change: width;
- background-color: var(--media-slider-track-progress-bg, rgb(255 255 255 / 0.5));
-}
-:where([data-media-player]:not([data-can-play]) .vds-time-slider .vds-slider-value) {
- display: none;
-}
-:where(.vds-slider-steps) {
- display: flex;
- align-items: center;
- justify-content: space-between;
- width: 100%;
- height: 100%;
- position: absolute;
- top: 0;
- left: 0;
-}
-:where(.vds-slider-step) {
- width: var(--media-slider-step-width, 2.5px);
- height: calc(var(--track-height) + 1px);
- background-color: var(--media-slider-step-color, rgb(124, 124, 124));
- opacity: 0;
- transition: opacity 0.3s ease;
-}
-:where(.vds-slider[data-active] .vds-slider-step) {
- opacity: 1;
-}
-:where(.vds-time-slider .vds-slider-chapters) {
- position: relative;
- display: flex;
- align-items: center;
- width: 100%;
- height: 100%;
- contain: layout style;
- border-radius: var(--media-slider-track-border-radius, 1px);
-}
-:where(.vds-slider[data-focus], .vds-slider:focus-visible) :where(.vds-slider-chapters) {
- box-shadow: var(--media-focus-ring);
- height: var(--track-height);
-}
-:where(.vds-time-slider .vds-slider-chapter) {
- margin-right: 2px;
-}
-:where(.vds-time-slider .vds-slider-chapter:last-child) {
- margin-right: 0;
-}
-:where(.vds-time-slider .vds-slider-chapter) {
- position: relative;
- display: flex;
- align-items: center;
- width: 100%;
- height: 100%;
- will-change: height, transform;
- contain: layout style;
- border-radius: var(--media-slider-track-border-radius, 1px);
-}
-:where(.vds-time-slider .vds-slider-chapter .vds-slider-track-fill) {
- width: var(--chapter-fill, 0%);
- will-change: width;
-}
-:where(.vds-time-slider .vds-slider-chapter .vds-slider-progress) {
- width: var(--chapter-progress, 0%);
- will-change: width;
-}
-@media (hover: hover) and (pointer: fine) {
- :where(.vds-time-slider:hover .vds-slider-chapters) {
- contain: strict;
- }
- :where(.vds-time-slider .vds-slider-chapter:hover:not(:only-of-type)) {
- transform: var(--media-slider-chapter-hover-transform, scaleY(2));
- transition: var( --media-slider-chapter-hover-transition, transform 0.1s cubic-bezier(0.4, 0, 1, 1) );
- }
-}
-:where(.vds-time-slider .vds-slider-chapter-title) {
- font-family: var(--media-font-family, sans-serif);
- font-size: var(--media-slider-chapter-title-font-size, 14px);
- color: var(--media-slider-chapter-title-color, #f5f5f5);
- background-color: var(--media-slider-chapter-title-bg);
-}
-:where(.vds-thumbnail) {
- --min-width: var(--media-thumbnail-min-width, 140px);
- --max-width: var(--media-thumbnail-max-width, 180px);
- --aspect-ratio: var(--media-thumbnail-aspect-ratio, var(--thumbnail-aspect-ratio));
- display: block;
- width: var(--thumbnail-width);
- height: var(--thumbnail-height);
- background-color: var(--media-thumbnail-bg, black);
- contain: strict;
- overflow: hidden;
- box-sizing: border-box;
- min-width: var(--min-width);
- min-height: var(--media-thumbnail-min-height, calc(var(--min-width) / var(--aspect-ratio)));
- max-width: var(--max-width);
- max-height: var(--media-thumbnail-max-height, calc(var(--max-width) / var(--aspect-ratio)));
-}
-.vds-thumbnail {
- border: var(--media-thumbnail-border, 1px solid white);
-}
-:where(.vds-thumbnail img) {
- min-width: unset !important;
- max-width: unset !important;
- will-change:
- width,
- height,
- transform;
-}
-:where(.vds-thumbnail[data-loading] img) {
- opacity: 0;
-}
-:where(.vds-thumbnail[aria-hidden=true]) {
- display: none !important;
-}
-:where(.vds-time-group) {
- display: flex;
- align-items: center;
-}
-.vds-time-divider {
- margin: 0 var(--media-time-divider-gap, 2.5px);
- color: var(--media-time-divider-color, #e0e0e0);
-}
-:where(.vds-time) {
- display: inline-block;
- contain: content;
- font-size: var(--media-time-font-size, 15px);
- font-weight: var(--media-time-font-weight, 400);
- font-family: var(--media-font-family, sans-serif);
- border-radius: var(--media-time-border-radius, 2px);
- letter-spacing: var(--media-time-letter-spacing, 0.025em);
-}
-.vds-time {
- outline: 0;
- color: var(--media-time-color, var(--default-color));
- background-color: var(--media-time-bg);
- border: var(--media-time-border);
- padding: var(--media-time-padding, 2px);
-}
-:where(.vds-time:focus-visible) {
- box-shadow: var(--media-focus-ring);
-}
-.light .vds-time {
- --default-color: rgb(10 10 10);
-}
-.dark .vds-time {
- --default-color: #f5f5f5;
-}
-:where(.vds-tooltip, media-tooltip) {
- display: contents;
-}
-:where(.vds-tooltip-content) {
- display: inline-block;
- box-sizing: border-box;
- font-family: var(--media-font-family, sans-serif);
- font-size: var(--media-tooltip-font-size, 13px);
- font-weight: var(--media-tooltip-font-weight, 500);
- opacity: 0;
- pointer-events: none;
- white-space: nowrap;
- z-index: 10;
- will-change: transform, opacity;
-}
-.vds-tooltip-content {
- border-radius: var(--media-tooltip-border-radius, 2px);
- background-color: var(--media-tooltip-bg-color, var(--default-bg));
- border: var(--media-tooltip-border, var(--default-border));
- color: var(--media-tooltip-color, var(--default-color));
- padding: var(--media-tooltip-padding, 2px 8px);
-}
-.light .vds-tooltip-content {
- --default-color: #1a1a1a;
- --default-bg: white;
- --default-border: 1px solid rgb(0 0 0 / 0.2);
-}
-.dark .vds-tooltip-content {
- --default-color: #f5f5f5;
- --default-bg: black;
- --default-border: 1px solid rgb(255 255 255 / 0.1);
-}
-:where(.vds-menu .vds-menu-button[role=button][data-pressed] .vds-tooltip-content) {
- opacity: 0;
- display: none;
-}
-:where(.vds-tooltip-content) {
- --enter-transform: translateY(0px) scale(1);
- --exit-transform: translateY(12px) scale(0.8);
-}
-:where(.vds-tooltip-content:not([data-visible])) {
- animation: var(--media-tooltip-exit-animation, vds-tooltip-exit 0.2s ease-out);
-}
-:where(.vds-tooltip-content[data-visible]) {
- animation: var(--media-tooltip-enter-animation, vds-tooltip-enter 0.2s ease-in);
- animation-fill-mode: forwards;
-}
-:where(.vds-tooltip-content[data-placement~=bottom]) {
- --enter-transform: translateY(0) scale(1);
- --exit-transform: translateY(-12px) scale(0.8);
-}
-:where(.vds-tooltip-content[data-placement~=left]) {
- --enter-transform: translateX(0) scale(1);
- --exit-transform: translateX(12px) scale(0.8);
-}
-:where(.vds-tooltip-content[data-placement~=right]) {
- --enter-transform: translateX(0) scale(1);
- --exit-transform: translateX(-12px) scale(0.8);
-}
-@keyframes vds-tooltip-enter {
- from {
- opacity: 0;
- transform: var(--exit-transform);
- }
- to {
- opacity: 1;
- transform: var(--enter-transform);
- }
-}
-@keyframes vds-tooltip-exit {
- from {
- opacity: 1;
- transform: var(--enter-transform);
- }
- to {
- opacity: 0;
- transform: var(--exit-transform);
- }
-}
-@media (prefers-reduced-motion) {
- :where(.vds-tooltip-content) {
- animation: none;
- }
- :where(.vds-tooltip-content[data-visible]) {
- opacity: 1;
- }
-}
-[data-media-player]:not([data-paused]) .vds-play-tooltip-text,
-[data-media-player][data-paused] .vds-pause-tooltip-text,
-[data-media-player][data-pip] .vds-pip-enter-tooltip-text,
-[data-media-player]:not([data-pip]) .vds-pip-exit-tooltip-text,
-[data-media-player][data-fullscreen] .vds-fs-enter-tooltip-text,
-[data-media-player]:not([data-fullscreen]) .vds-fs-exit-tooltip-text,
-[data-media-player]:not([data-captions]) .vds-cc-on-tooltip-text,
-[data-media-player][data-captions] .vds-cc-off-tooltip-text,
-[data-media-player]:not([data-muted]) .vds-mute-tooltip-text,
-[data-media-player][data-muted] .vds-unmute-tooltip-text {
- display: none;
-}
-
-/* node_modules/vidstack/player/styles/default/layouts/video.css */
-[data-media-player] .vds-video-layout:not([data-match]) {
- display: none !important;
-}
-[data-media-player][data-layout=video] {
- background-color: var(--video-bg, black);
-}
-[data-media-player][data-layout=video]:not([data-fullscreen]) {
- border-radius: var(--video-border-radius, 6px);
- border: var(--video-border, 1px solid rgb(255 255 255 / 0.1));
-}
-:where(.vds-video-layout) {
- --media-brand: var(--video-brand, #f5f5f5);
- --media-font-family: var(--video-font-family, sans-serif);
- --media-controls-color: var(--video-controls-color, #f5f5f5);
- --media-tooltip-y-offset: 6px;
- --media-menu-y-offset: 6px;
- --media-focus-ring-color: var(--video-focus-ring-color, rgb(78 156 246));
- --media-focus-ring: var(--video-focus-ring, 0 0 0 3px var(--media-focus-ring-color));
- color: var(--video-controls-color, #f5f5f5);
- display: contents;
-}
-:where([data-media-player][data-focus]:not([data-playing]) .vds-video-layout .vds-controls) {
- border-radius: var(--video-border-radius, 6px);
- box-shadow: var(--media-focus-ring);
-}
-:where(.vds-video-layout .vds-controls[data-visible]) {
- border-radius: var(--video-border-radius, 6px);
- background-image:
- linear-gradient(
- to top,
- rgb(0 0 0 / 0.6),
- 10%,
- transparent,
- 95%,
- rgb(0 0 0 / 0.3));
-}
-.vds-video-layout .vds-controls-group {
- align-items: center;
- display: flex;
- pointer-events: auto;
- z-index: 0;
- padding: 4px 6px;
-}
-.vds-video-layout .vds-controls-group:first-child {
- z-index: 50;
-}
-.vds-video-layout .vds-controls-group:nth-last-child(2) {
- padding: 0 12px;
- z-index: 11;
- margin-bottom: -16px;
-}
-.vds-video-layout:not([data-sm]) .vds-controls-group:last-child {
- --media-menu-y-offset: 26px;
- --media-tooltip-y-offset: 26px;
- --media-slider-preview-offset: 26px;
- z-index: 10;
-}
-:where(.vds-video-layout .vds-button) {
- margin-right: 2.5px;
-}
-:where(.vds-video-layout[data-sm] .vds-chapter-title) {
- font-size: var(--video-sm-chapter-title-font-size, 15px);
-}
-:where([data-fullscreen] .vds-video-layout .vds-chapter-title) {
- font-size: var(--video-fullscreen-chapter-title-font-size, 16px);
-}
-:where(.vds-video-layout:not([data-sm]) .vds-mute-button) {
- margin-left: -2.5px;
- margin-right: -5px;
-}
-:where(.vds-video-layout[data-sm]) {
- --media-button-size: var(--video-sm-button-size, 36px);
-}
-:where(.vds-video-layout .vds-time-slider) {
- --media-slider-height: 45px;
- flex-grow: 0;
-}
-:where(.vds-video-layout .vds-slider-thumbnail) {
- --media-thumbnail-border: var(--video-slider-thumbnail-border, 1px solid #f5f5f5);
- border-radius: var(--video-slider-thumbnail-border-radius, 2px);
-}
-.vds-video-layout .vds-time-slider .vds-slider-value {
- background-color: var(--video-time-bg, unset);
- text-shadow:
- -1px -1px 0 #333333,
- 1px -1px 0 #333333,
- -1px 1px 0 #333333,
- 1px 1px 0 #333333;
-}
-:where(.vds-video-layout[data-sm] .vds-time) {
- text-shadow: unset;
-}
-:where(.vds-video-layout[data-lg] .vds-volume) {
- --gap: var(--video-volume-gap, 10px);
- display: contents;
-}
-:where(.vds-video-layout[data-lg] .vds-volume-popup) {
- display: contents;
-}
-:where(.vds-video-layout[data-lg] .vds-volume-slider) {
- margin: 0;
- max-width: 0;
- transition: all 0.15s ease;
-}
-:where(.vds-video-layout[data-lg] .vds-volume[data-active] .vds-volume-slider),
-:where(.vds-video-layout[data-lg] .vds-volume:has([data-active]) .vds-volume-slider) {
- margin-left: var(--gap);
- opacity: 1;
- visibility: visible;
- max-width: var(--video-volume-slider-max-width, 72px);
-}
-.vds-video-layout[data-lg] .vds-volume-slider::after {
- content: "";
- position: fixed;
- top: 0;
- left: calc(-1 * var(--gap));
- width: var(--gap);
- height: 100%;
- z-index: 1;
- pointer-events: auto;
-}
-:where(.vds-video-layout[data-sm] .vds-volume) {
- --media-slider-height: var(--video-volume-height, 96px);
- --media-slider-preview-offset: calc(-200% - 6px);
- --gap: var(--video-volume-gap, 10px);
- position: relative;
- display: flex;
- align-items: center;
- justify-content: center;
-}
-:where(.vds-video-layout[data-sm] .vds-volume-popup) {
- display: block;
- position: absolute;
- top: calc(100% + var(--gap));
- left: 50%;
- opacity: 0;
- transform: translateX(-50%);
- transition: opacity 150ms ease-out, visibility 150ms ease-out;
- border-radius: var(--video-volume-border-radius, 8px);
- filter: var(--media-volume-filter, drop-shadow(0 1px 1px rgb(0 0 0 / 0.05)));
- visibility: hidden;
-}
-.vds-video-layout[data-sm] .vds-mute-button::after {
- content: "";
- position: fixed;
- bottom: calc(-1 * var(--gap));
- right: 0;
- width: 100%;
- height: var(--gap);
- z-index: 1;
- pointer-events: auto;
-}
-.vds-video-layout .vds-volume-popup {
- background-color: var(--video-volume-bg, var(--media-menu-bg, var(--default-bg)));
- border: var(--video-volume-border, var(--default-border));
-}
-.light .vds-video-layout .vds-volume-popup,
-.vds-video-layout.light .vds-volume-popup {
- --default-bg: rgb(250 250 250);
- --default-border: 1px solid rgb(10 10 10 / 0.1);
-}
-.dark .vds-video-layout .vds-volume-popup,
-.vds-video-layout.dark .vds-volume-popup {
- --default-bg: rgb(10 10 10);
- --default-border: 1px solid rgb(255 255 255 / 0.1);
-}
-:where(.vds-video-layout[data-sm] .vds-volume[data-active] .vds-volume-popup),
-:where(.vds-video-layout[data-sm] .vds-volume:has([data-active]) .vds-volume-popup) {
- transition: opacity 150ms ease-in, visibility 150ms ease-in;
- opacity: 1;
- visibility: visible;
-}
-:where(.vds-video-layout[data-sm] .vds-volume[data-active] .vds-tooltip-content) {
- display: none !important;
-}
-:where(.vds-video-layout .vds-time[data-type=current]) {
- margin-right: 2px;
-}
-:where(.vds-video-layout .vds-time[data-type=current][remainder]) {
- margin-left: 2px;
-}
-.vds-video-layout .vds-time {
- --default-color: #f5f5f5 !important;
-}
-:where([data-preview] .vds-video-layout .vds-captions) {
- opacity: 0;
-}
-:where(.vds-video-layout .vds-captions) {
- z-index: 10;
- transition: var(--video-captions-transition, bottom 0.3s ease-in-out);
-}
-@media (min-width: 980px) {
- :where([data-fullscreen] .vds-video-layout .vds-captions) {
- bottom: var(--video-lg-fullscreen-captions-offset, 54px);
- }
-}
-:where([data-media-player][data-controls] .vds-video-layout .vds-captions) {
- bottom: var(--video-captions-offset, 78px);
-}
-:where([data-media-player][data-controls] .vds-video-layout[data-sm] .vds-captions) {
- bottom: var(--video-sm-captions-offset, 48px);
-}
-:where(.vds-video-layout .vds-time-slider .vds-slider-chapter-title) {
- width: 100%;
- text-align: center;
- text-shadow:
- -1px -1px 0 #212121,
- 1px -1px 0 #212121,
- -1px 1px 0 #212121,
- 1px 1px 0 #212121;
-}
-:where(.vds-video-layout .vds-gesture) {
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- z-index: 0;
-}
-:where(.vds-video-layout .vds-gesture[action="seek:-10"]) {
- width: var(--video-gesture-seek-width, 20%);
- z-index: 1;
-}
-:where(.vds-video-layout .vds-gesture[action="seek:10"]) {
- left: unset;
- right: 0;
- width: var(--video-gesture-seek-width, 20%);
- z-index: 1;
-}
-@media (pointer: coarse) {
- :where(.vds-video-layout .vds-gesture[action="toggle:paused"]) {
- display: none;
- }
-}
-@media not (pointer: coarse) {
- :where([data-media-player] .vds-video-layout .vds-gesture[action="toggle:controls"]) {
- display: none;
- }
-}
-:where(.vds-video-layout .vds-live-button) {
- margin-left: 12px;
-}
-:where(.vds-video-layout:not([data-sm]) .vds-time-group) {
- margin-left: 10px;
-}
-:where(.vds-video-layout[data-sm] .vds-time) {
- font-size: var(--video-sm-time-font-size, 14px);
-}
-:where([data-fullscreen] .vds-video-layout .vds-time) {
- font-size: var(--video-fullscreen-time-font-size, 16px);
-}
-:where(.vds-video-layout .vds-load-container) {
- position: absolute;
- inset: 0;
- width: 100%;
- height: 100%;
- display: none;
- align-items: center;
- justify-content: center;
- pointer-events: none;
- z-index: 99;
-}
-:where([data-media-player][data-load=play]:not([data-started]) .vds-video-layout[data-match] .vds-load-container) {
- display: flex;
-}
-:where(.vds-video-layout .vds-load-container .vds-play-button) {
- --size: var(--video-load-button-size, 56px);
- --color: var(--video-load-button-color, rgb(0 0 0 / 0.8));
- --bg-color: var(--video-load-button-bg, var(--media-brand));
- --media-button-hover-transform: 0;
- --media-button-border: var(--video-load-button-border, var(--color));
- --media-button-hover-bg: var(--video-load-button-bg, var(--media-brand));
- width: var(--size);
- height: var(--size);
- pointer-events: auto;
- margin-bottom: 2px;
- overflow: hidden;
-}
-.vds-video-layout .vds-load-container .vds-play-button {
- border-radius: var(--video-load-button-border-radius, 100%);
- color: var(--color);
-}
-.vds-video-layout .vds-load-container .vds-play-button {
- background-color: var(--bg-color);
-}
-:where(.vds-video-layout[data-sm] .vds-load-container .vds-play-button) {
- --size: var(--video-sm-load-button-size, 48px);
- --media-button-hover-transform: translateY(0%);
- width: var(--size);
- height: var(--size);
- transform: translateY(0%);
-}
-:where(.vds-video-layout[data-sm] .vds-controls-group:nth-last-child(2)) {
- pointer-events: none;
-}
-:where(.vds-video-layout[data-sm] .vds-controls-group:last-child) {
- z-index: 2;
- margin-top: -2.5px;
- margin-bottom: -6px;
-}
-:where([data-fullscreen] .vds-video-layout[data-sm] .vds-controls-group:last-child) {
- margin-bottom: 0;
-}
-.vds-video-layout[data-sm] .vds-controls-group {
- padding: 2px;
-}
-:where(.vds-video-layout[data-sm]) :where(.vds-button, .vds-slider:not(.vds-time-slider), .vds-time, .vds-time-divider, .vds-chapter-title) {
- transition: opacity 0.15s ease;
-}
-:where([data-media-player]:not([data-started]) .vds-video-layout[data-sm]) :where(.vds-button .vds-slider, .vds-time-group) {
- opacity: 0;
- visibility: hidden;
-}
-:where(.vds-video-layout[data-sm] .vds-time-slider) {
- transition: transform 0.1s linear;
-}
-@media (pointer: coarse) {
- :where([data-preview] .vds-video-layout:not([data-no-scrub-gesture])) :where(.vds-button, .vds-slider:not(.vds-time-slider), .vds-time, .vds-chapter-title, .vds-time-divider, .vds-captions, .vds-live-button) {
- opacity: 0;
- }
- :where([data-preview] .vds-video-layout:not([data-no-scrub-gesture]) .vds-time-slider) {
- --track-height: var(--video-sm-slider-focus-track-height, 12px);
- transform: translateY(-6px);
- transition: transform 0.1s linear;
- }
-}
-:where(.vds-video-layout[data-sm] .vds-controls .vds-play-button) {
- --size: var(--video-sm-play-button-size, 45px);
- --media-button-hover-transform: translateY(25%);
- width: var(--size);
- height: var(--size);
- transform: translateY(25%);
- border-radius: 100%;
- pointer-events: auto;
- margin-bottom: 2px;
- overflow: hidden;
-}
-.vds-video-layout[data-sm] .vds-controls .vds-play-button {
- background-color: var(--video-sm-play-button-bg, rgba(0 0 0 / 0.6));
-}
-:where([data-media-player]:not([data-started]) .vds-video-layout[data-sm] .vds-controls-group:not(:nth-child(3))) {
- opacity: 0;
- visibility: hidden;
-}
-:where(.vds-video-layout[data-sm] .vds-buffering-indicator) {
- --media-buffering-size: 64px;
- transform: translate(-2px, -4px);
-}
-:where(.vds-video-layout .vds-start-duration .vds-time) {
- position: absolute;
- right: 8px;
- bottom: 8px;
- margin-right: 8px;
- margin-bottom: 8px;
- z-index: 10;
-}
-.vds-video-layout .vds-start-duration .vds-time {
- padding: var(--video-sm-start-duration-padding, 3px 6px);
- color: var(--video-sm-start-duration-color, var(--video-controls-color));
- background-color: var(--video-sm-start-duration-bg, rgba(0 0 0 / 0.64));
-}
-:where([data-started] .vds-video-layout .vds-start-duration .vds-time) {
- display: none;
-}
-:where([data-media-player]:not([data-can-play]) .vds-video-layout .vds-start-duration .vds-time) {
- opacity: 0;
-}
-:where(.vds-video-layout[data-sm] .vds-time[data-type=current]) {
- margin-left: 8px;
-}
-:where([data-fullscreen] .vds-video-layout .vds-controls-group:nth-last-child(2)) {
- margin-bottom: -16px;
-}
-@media (orientation: portrait) {
- :where([data-fullscreen] .vds-video-layout .vds-captions) {
- bottom: 30lvh;
- bottom: 10dvh;
- }
-}
-@media (orientation: landscape) {
- :where([data-fullscreen] .vds-video-layout .vds-controls-group:nth-last-child(2)) {
- margin-bottom: -12px;
- }
-}
diff --git a/assets/vs_js_out.js b/assets/vs_js_out.js
deleted file mode 100644
index 75a771c..0000000
--- a/assets/vs_js_out.js
+++ /dev/null
@@ -1,21783 +0,0 @@
-(() => {
- var __defProp = Object.defineProperty;
- var __getOwnPropNames = Object.getOwnPropertyNames;
- var __esm = (fn, res) => function __init() {
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
- };
- var __export = (target, all) => {
- for (var name in all)
- __defProp(target, name, { get: all[name], enumerable: true });
- };
-
- // node_modules/vidstack/prod/chunks/vidstack-CRlI3Mh7.js
- function flushEffects() {
- scheduledEffects = true;
- queueMicrotask(runEffects);
- }
- function runEffects() {
- if (!effects.length) {
- scheduledEffects = false;
- return;
- }
- runningEffects = true;
- for (let i4 = 0; i4 < effects.length; i4++) {
- if (effects[i4].$st !== STATE_CLEAN)
- runTop(effects[i4]);
- }
- effects = [];
- scheduledEffects = false;
- runningEffects = false;
- }
- function runTop(node) {
- let ancestors = [node];
- while (node = node[SCOPE]) {
- if (node.$e && node.$st !== STATE_CLEAN)
- ancestors.push(node);
- }
- for (let i4 = ancestors.length - 1; i4 >= 0; i4--) {
- updateCheck(ancestors[i4]);
- }
- }
- function root(init) {
- const scope = createScope();
- return compute(scope, !init.length ? init : init.bind(null, dispose.bind(scope)), null);
- }
- function peek(fn) {
- return compute(currentScope, fn, null);
- }
- function untrack(fn) {
- return compute(null, fn, null);
- }
- function tick() {
- if (!runningEffects)
- runEffects();
- }
- function getScope() {
- return currentScope;
- }
- function scoped(run, scope) {
- try {
- return compute(scope, run, null);
- } catch (error) {
- handleError(scope, error);
- return;
- }
- }
- function getContext(key2, scope = currentScope) {
- return scope?.$cx[key2];
- }
- function setContext(key2, value, scope = currentScope) {
- if (scope)
- scope.$cx = { ...scope.$cx, [key2]: value };
- }
- function onDispose(disposable) {
- if (!disposable || !currentScope)
- return disposable || NOOP;
- const node = currentScope;
- if (!node.$d) {
- node.$d = disposable;
- } else if (Array.isArray(node.$d)) {
- node.$d.push(disposable);
- } else {
- node.$d = [node.$d, disposable];
- }
- return function removeDispose() {
- if (node.$st === STATE_DISPOSED)
- return;
- disposable.call(null);
- if (isFunction$1(node.$d)) {
- node.$d = null;
- } else if (Array.isArray(node.$d)) {
- node.$d.splice(node.$d.indexOf(disposable), 1);
- }
- };
- }
- function dispose(self = true) {
- if (this.$st === STATE_DISPOSED)
- return;
- if (this.$h) {
- if (Array.isArray(this.$h)) {
- for (let i4 = this.$h.length - 1; i4 >= 0; i4--) {
- dispose.call(this.$h[i4]);
- }
- } else {
- dispose.call(this.$h);
- }
- }
- if (self) {
- const parent = this[SCOPE];
- if (parent) {
- if (Array.isArray(parent.$h)) {
- parent.$h.splice(parent.$h.indexOf(this), 1);
- } else {
- parent.$h = null;
- }
- }
- disposeNode(this);
- }
- }
- function disposeNode(node) {
- node.$st = STATE_DISPOSED;
- if (node.$d)
- emptyDisposal(node);
- if (node.$s)
- removeSourceObservers(node, 0);
- node[SCOPE] = null;
- node.$s = null;
- node.$o = null;
- node.$h = null;
- node.$cx = defaultContext;
- node.$eh = null;
- }
- function emptyDisposal(scope) {
- try {
- if (Array.isArray(scope.$d)) {
- for (let i4 = scope.$d.length - 1; i4 >= 0; i4--) {
- const callable = scope.$d[i4];
- callable.call(callable);
- }
- } else {
- scope.$d.call(scope.$d);
- }
- scope.$d = null;
- } catch (error) {
- handleError(scope, error);
- }
- }
- function compute(scope, compute2, observer) {
- const prevScope = currentScope, prevObserver = currentObserver;
- currentScope = scope;
- currentObserver = observer;
- try {
- return compute2.call(scope);
- } finally {
- currentScope = prevScope;
- currentObserver = prevObserver;
- }
- }
- function handleError(scope, error) {
- if (!scope || !scope.$eh)
- throw error;
- let i4 = 0, len = scope.$eh.length, currentError = error;
- for (i4 = 0; i4 < len; i4++) {
- try {
- scope.$eh[i4](currentError);
- break;
- } catch (error2) {
- currentError = error2;
- }
- }
- if (i4 === len)
- throw currentError;
- }
- function read() {
- if (this.$st === STATE_DISPOSED)
- return this.$v;
- if (currentObserver && !this.$e) {
- if (!currentObservers && currentObserver.$s && currentObserver.$s[currentObserversIndex] == this) {
- currentObserversIndex++;
- } else if (!currentObservers)
- currentObservers = [this];
- else
- currentObservers.push(this);
- }
- if (this.$c)
- updateCheck(this);
- return this.$v;
- }
- function write(newValue) {
- const value = isFunction$1(newValue) ? newValue(this.$v) : newValue;
- if (this.$ch(this.$v, value)) {
- this.$v = value;
- if (this.$o) {
- for (let i4 = 0; i4 < this.$o.length; i4++) {
- notify(this.$o[i4], STATE_DIRTY);
- }
- }
- }
- return this.$v;
- }
- function createScope() {
- return new ScopeNode();
- }
- function createComputation(initialValue, compute2, options) {
- return new ComputeNode(initialValue, compute2, options);
- }
- function isNotEqual(a3, b2) {
- return a3 !== b2;
- }
- function isFunction$1(value) {
- return typeof value === "function";
- }
- function updateCheck(node) {
- if (node.$st === STATE_CHECK) {
- for (let i4 = 0; i4 < node.$s.length; i4++) {
- updateCheck(node.$s[i4]);
- if (node.$st === STATE_DIRTY) {
- break;
- }
- }
- }
- if (node.$st === STATE_DIRTY)
- update(node);
- else
- node.$st = STATE_CLEAN;
- }
- function cleanup(node) {
- if (node.$h)
- dispose.call(node, false);
- if (node.$d)
- emptyDisposal(node);
- node.$eh = node[SCOPE] ? node[SCOPE].$eh : null;
- }
- function update(node) {
- let prevObservers = currentObservers, prevObserversIndex = currentObserversIndex;
- currentObservers = null;
- currentObserversIndex = 0;
- try {
- cleanup(node);
- const result = compute(node, node.$c, node);
- updateObservers(node);
- if (!node.$e && node.$i) {
- write.call(node, result);
- } else {
- node.$v = result;
- node.$i = true;
- }
- } catch (error) {
- updateObservers(node);
- handleError(node, error);
- } finally {
- currentObservers = prevObservers;
- currentObserversIndex = prevObserversIndex;
- node.$st = STATE_CLEAN;
- }
- }
- function updateObservers(node) {
- if (currentObservers) {
- if (node.$s)
- removeSourceObservers(node, currentObserversIndex);
- if (node.$s && currentObserversIndex > 0) {
- node.$s.length = currentObserversIndex + currentObservers.length;
- for (let i4 = 0; i4 < currentObservers.length; i4++) {
- node.$s[currentObserversIndex + i4] = currentObservers[i4];
- }
- } else {
- node.$s = currentObservers;
- }
- let source;
- for (let i4 = currentObserversIndex; i4 < node.$s.length; i4++) {
- source = node.$s[i4];
- if (!source.$o)
- source.$o = [node];
- else
- source.$o.push(node);
- }
- } else if (node.$s && currentObserversIndex < node.$s.length) {
- removeSourceObservers(node, currentObserversIndex);
- node.$s.length = currentObserversIndex;
- }
- }
- function notify(node, state) {
- if (node.$st >= state)
- return;
- if (node.$e && node.$st === STATE_CLEAN) {
- effects.push(node);
- if (!scheduledEffects)
- flushEffects();
- }
- node.$st = state;
- if (node.$o) {
- for (let i4 = 0; i4 < node.$o.length; i4++) {
- notify(node.$o[i4], STATE_CHECK);
- }
- }
- }
- function removeSourceObservers(node, index) {
- let source, swap;
- for (let i4 = index; i4 < node.$s.length; i4++) {
- source = node.$s[i4];
- if (source.$o) {
- swap = source.$o.indexOf(node);
- source.$o[swap] = source.$o[source.$o.length - 1];
- source.$o.pop();
- }
- }
- }
- function noop(...args) {
- }
- function isNull(value) {
- return value === null;
- }
- function isUndefined(value) {
- return typeof value === "undefined";
- }
- function isNil(value) {
- return isNull(value) || isUndefined(value);
- }
- function isObject(value) {
- return value?.constructor === Object;
- }
- function isNumber(value) {
- return typeof value === "number" && !Number.isNaN(value);
- }
- function isString(value) {
- return typeof value === "string";
- }
- function isBoolean(value) {
- return typeof value === "boolean";
- }
- function isFunction(value) {
- return typeof value === "function";
- }
- function isArray(value) {
- return Array.isArray(value);
- }
- function isDOMEvent(event2) {
- return !!event2?.[DOM_EVENT];
- }
- function listenEvent(target, type, handler, options) {
- target.addEventListener(type, handler, options);
- return onDispose(() => target.removeEventListener(type, handler, options));
- }
- function anySignal(...signals) {
- const controller = new AbortController(), options = { signal: controller.signal };
- function onAbort(event2) {
- controller.abort(event2.target.reason);
- }
- for (const signal2 of signals) {
- if (signal2.aborted) {
- controller.abort(signal2.reason);
- break;
- }
- signal2.addEventListener("abort", onAbort, options);
- }
- return controller.signal;
- }
- function isPointerEvent(event2) {
- return !!event2?.type.startsWith("pointer");
- }
- function isTouchEvent(event2) {
- return !!event2?.type.startsWith("touch");
- }
- function isMouseEvent(event2) {
- return /^(click|mouse)/.test(event2?.type ?? "");
- }
- function isKeyboardEvent(event2) {
- return !!event2?.type.startsWith("key");
- }
- function wasEnterKeyPressed(event2) {
- return isKeyboardEvent(event2) && event2.key === "Enter";
- }
- function isKeyboardClick(event2) {
- return isKeyboardEvent(event2) && (event2.key === "Enter" || event2.key === " ");
- }
- function isDOMNode(node) {
- return node instanceof Node;
- }
- function setAttribute(host, name, value) {
- if (!host) return;
- else if (!value && value !== "" && value !== 0) {
- host.removeAttribute(name);
- } else {
- const attrValue = value === true ? "" : value + "";
- if (host.getAttribute(name) !== attrValue) {
- host.setAttribute(name, attrValue);
- }
- }
- }
- function setStyle(host, property, value) {
- if (!host) return;
- else if (!value && value !== 0) {
- host.style.removeProperty(property);
- } else {
- host.style.setProperty(property, value + "");
- }
- }
- function toggleClass(host, name, value) {
- host.classList[value ? "add" : "remove"](name);
- }
- function signal(initialValue, options) {
- const node = createComputation(initialValue, null, options), signal2 = read.bind(node);
- signal2[SCOPE] = true;
- signal2.set = write.bind(node);
- return signal2;
- }
- function isReadSignal(fn) {
- return isFunction$1(fn) && SCOPE in fn;
- }
- function computed(compute2, options) {
- const node = createComputation(
- options?.initial,
- compute2,
- options
- ), signal2 = read.bind(node);
- signal2[SCOPE] = true;
- return signal2;
- }
- function effect$1(effect2, options) {
- const signal2 = createComputation(
- null,
- function runEffect() {
- let effectResult = effect2();
- isFunction$1(effectResult) && onDispose(effectResult);
- return null;
- },
- void 0
- );
- signal2.$e = true;
- update(signal2);
- return dispose.bind(signal2, true);
- }
- function isWriteSignal(fn) {
- return isReadSignal(fn) && "set" in fn;
- }
- function createContext(provide) {
- return { id: Symbol(), provide };
- }
- function provideContext(context, value, scope = getScope()) {
- const hasProvidedValue = !isUndefined(value);
- setContext(context.id, hasProvidedValue ? value : context.provide?.(), scope);
- }
- function useContext(context) {
- const value = getContext(context.id);
- return value;
- }
- function hasProvidedContext(context) {
- return !isUndefined(getContext(context.id));
- }
- function createInstanceProps(props) {
- const $props = {};
- for (const name of Object.keys(props)) {
- const def = props[name];
- $props[name] = signal(def, def);
- }
- return $props;
- }
- function createComponent(Component2, init) {
- return root(() => {
- currentInstance.$$ = new Instance(Component2, getScope(), init);
- const component = new Component2();
- currentInstance.$$.component = component;
- currentInstance.$$ = null;
- return component;
- });
- }
- function prop(target, propertyKey, descriptor) {
- if (!target[PROPS]) target[PROPS] = /* @__PURE__ */ new Set();
- target[PROPS].add(propertyKey);
- }
- function method(target, propertyKey, descriptor) {
- if (!target[METHODS]) target[METHODS] = /* @__PURE__ */ new Set();
- target[METHODS].add(propertyKey);
- }
- function useState(state) {
- return useContext(state);
- }
- function runAll(fns, arg) {
- for (const fn of fns) fn(arg);
- }
- function camelToKebabCase(str) {
- return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
- }
- function kebabToCamelCase(str) {
- return str.replace(/-./g, (x2) => x2[1].toUpperCase());
- }
- function uppercaseFirstChar(str) {
- return str.charAt(0).toUpperCase() + str.slice(1);
- }
- function unwrap(fn) {
- return isFunction(fn) ? fn() : fn;
- }
- function ariaBool(value) {
- return value ? "true" : "false";
- }
- function keysOf(obj) {
- return Object.keys(obj);
- }
- function deferredPromise() {
- let resolve, reject;
- const promise = new Promise((res, rej) => {
- resolve = res;
- reject = rej;
- });
- return { promise, resolve, reject };
- }
- function waitTimeout(delay) {
- return new Promise((resolve) => setTimeout(resolve, delay));
- }
- function animationFrameThrottle(func) {
- let id3 = -1, lastArgs;
- function throttle2(...args) {
- lastArgs = args;
- if (id3 >= 0) return;
- id3 = window.requestAnimationFrame(() => {
- func.apply(this, lastArgs);
- id3 = -1;
- lastArgs = void 0;
- });
- }
- return throttle2;
- }
- function waitIdlePeriod(callback, options) {
- return new Promise((resolve) => {
- requestIdleCallback((deadline) => {
- callback?.(deadline);
- resolve();
- }, options);
- });
- }
- function throttle(fn, interval, options) {
- var timeoutId = null;
- var throttledFn = null;
- var leading = options && options.leading;
- var trailing = options && options.trailing;
- if (leading == null) {
- leading = true;
- }
- if (trailing == null) {
- trailing = !leading;
- }
- if (leading == true) {
- trailing = false;
- }
- var cancel = function() {
- if (timeoutId) {
- clearTimeout(timeoutId);
- timeoutId = null;
- }
- };
- var flush = function() {
- var call = throttledFn;
- cancel();
- if (call) {
- call();
- }
- };
- var throttleWrapper = function() {
- var callNow = leading && !timeoutId;
- var context = this;
- var args = arguments;
- throttledFn = function() {
- return fn.apply(context, args);
- };
- if (!timeoutId) {
- timeoutId = setTimeout(function() {
- timeoutId = null;
- if (trailing) {
- return throttledFn();
- }
- }, interval);
- }
- if (callNow) {
- callNow = false;
- return throttledFn();
- }
- };
- throttleWrapper.cancel = cancel;
- throttleWrapper.flush = flush;
- return throttleWrapper;
- }
- function debounce(fn, wait, callFirst) {
- var timeout = null;
- var debouncedFn = null;
- var clear = function() {
- if (timeout) {
- clearTimeout(timeout);
- debouncedFn = null;
- timeout = null;
- }
- };
- var flush = function() {
- var call = debouncedFn;
- clear();
- if (call) {
- call();
- }
- };
- var debounceWrapper = function() {
- if (!wait) {
- return fn.apply(this, arguments);
- }
- var context = this;
- var args = arguments;
- var callNow = callFirst && !timeout;
- clear();
- debouncedFn = function() {
- fn.apply(context, args);
- };
- timeout = setTimeout(function() {
- timeout = null;
- if (!callNow) {
- var call = debouncedFn;
- debouncedFn = null;
- return call();
- }
- }, wait);
- if (callNow) {
- return debouncedFn();
- }
- };
- debounceWrapper.cancel = clear;
- debounceWrapper.flush = flush;
- return debounceWrapper;
- }
- function inferAttributeConverter(value) {
- if (value === null) return NULLABLE_STRING;
- switch (typeof value) {
- case "undefined":
- return STRING;
- case "string":
- return STRING;
- case "boolean":
- return BOOLEAN;
- case "number":
- return NUMBER;
- case "function":
- return FUNCTION;
- case "object":
- return isArray(value) ? ARRAY : OBJECT;
- default:
- return STRING;
- }
- }
- function Host(Super, Component2) {
- class MaverickElement extends Super {
- static attrs;
- static [ATTRS] = null;
- static get observedAttributes() {
- if (!this[ATTRS] && Component2.props) {
- const map = /* @__PURE__ */ new Map();
- for (const propName of Object.keys(Component2.props)) {
- let attr = this.attrs?.[propName], attrName = isString(attr) ? attr : !attr ? attr : attr?.attr;
- if (attrName === false) continue;
- if (!attrName) attrName = camelToKebabCase(propName);
- map.set(attrName, {
- prop: propName,
- converter: attr && !isString(attr) && attr?.converter || inferAttributeConverter(Component2.props[propName])
- });
- }
- this[ATTRS] = map;
- }
- return this[ATTRS] ? Array.from(this[ATTRS].keys()) : [];
- }
- $;
- [SETUP_STATE] = SetupState.Idle;
- [SETUP_CALLBACKS] = null;
- keepAlive = false;
- forwardKeepAlive = true;
- get scope() {
- return this.$.$$.scope;
- }
- get attachScope() {
- return this.$.$$.attachScope;
- }
- get connectScope() {
- return this.$.$$.connectScope;
- }
- get $props() {
- return this.$.$$.props;
- }
- get $state() {
- return this.$.$$.$state;
- }
- get state() {
- return this.$.state;
- }
- constructor(...args) {
- super(...args);
- this.$ = scoped(() => createComponent(Component2), null);
- this.$.$$.addHooks(this);
- if (Component2.props) {
- const props = this.$props, descriptors = Object.getOwnPropertyDescriptors(this);
- for (const prop2 of Object.keys(descriptors)) {
- if (prop2 in Component2.props) {
- props[prop2].set(this[prop2]);
- delete this[prop2];
- }
- }
- }
- }
- attributeChangedCallback(name, _2, newValue) {
- const Ctor = this.constructor;
- if (!Ctor[ATTRS]) {
- super.attributeChangedCallback?.(name, _2, newValue);
- return;
- }
- const def = Ctor[ATTRS].get(name);
- if (def) this[def.prop] = def.converter(newValue);
- }
- connectedCallback() {
- const instance = this.$?.$$;
- if (!instance || instance.destroyed) return;
- if (this[SETUP_STATE] !== SetupState.Ready) {
- setup.call(this);
- return;
- }
- if (!this.isConnected) return;
- if (this.hasAttribute("keep-alive")) {
- this.keepAlive = true;
- }
- instance.connect();
- if (isArray(this[SETUP_CALLBACKS])) runAll(this[SETUP_CALLBACKS], this);
- this[SETUP_CALLBACKS] = null;
- const callback = super.connectedCallback;
- if (callback) scoped(() => callback.call(this), this.connectScope);
- return;
- }
- disconnectedCallback() {
- const instance = this.$?.$$;
- if (!instance || instance.destroyed) return;
- instance.disconnect();
- const callback = super.disconnectedCallback;
- if (callback) callback.call(this);
- if (!this.keepAlive && !this.hasAttribute("keep-alive")) {
- setTimeout(() => {
- requestAnimationFrame(() => {
- if (!this.isConnected) instance.destroy();
- });
- }, 0);
- }
- }
- [SETUP]() {
- const instance = this.$.$$, Ctor = this.constructor;
- if (instance.destroyed) return;
- const attrs = Ctor[ATTRS];
- if (attrs) {
- for (const attr of this.attributes) {
- let def = attrs.get(attr.name);
- if (def && def.converter) {
- instance.props[def.prop].set(def.converter(this.getAttribute(attr.name)));
- }
- }
- }
- instance.setup();
- instance.attach(this);
- this[SETUP_STATE] = SetupState.Ready;
- this.connectedCallback();
- }
- // @ts-expect-error
- subscribe(callback) {
- return this.$.subscribe(callback);
- }
- destroy() {
- this.disconnectedCallback();
- this.$.destroy();
- }
- }
- extendProto(MaverickElement, Component2);
- return MaverickElement;
- }
- function extendProto(Element2, Component2) {
- const ElementProto = Element2.prototype, ComponentProto = Component2.prototype;
- if (Component2.props) {
- for (const prop2 of Object.keys(Component2.props)) {
- Object.defineProperty(ElementProto, prop2, {
- enumerable: true,
- configurable: true,
- get() {
- return this.$props[prop2]();
- },
- set(value) {
- this.$props[prop2].set(value);
- }
- });
- }
- }
- if (ComponentProto[PROPS]) {
- for (const name of ComponentProto[PROPS]) {
- Object.defineProperty(ElementProto, name, {
- enumerable: true,
- configurable: true,
- get() {
- return this.$[name];
- },
- set(value) {
- this.$[name] = value;
- }
- });
- }
- }
- if (ComponentProto[METHODS]) {
- for (const name of ComponentProto[METHODS]) {
- ElementProto[name] = function(...args) {
- return this.$[name](...args);
- };
- }
- }
- }
- function setup() {
- if (this[SETUP_STATE] !== SetupState.Idle) return;
- this[SETUP_STATE] = SetupState.Pending;
- const parent = findParent(this), isParentRegistered = parent && window.customElements.get(parent.localName), isParentSetup = parent && parent[SETUP_STATE] === SetupState.Ready;
- if (parent && (!isParentRegistered || !isParentSetup)) {
- waitForParent.call(this, parent);
- return;
- }
- attach.call(this, parent);
- }
- async function waitForParent(parent) {
- await window.customElements.whenDefined(parent.localName);
- if (parent[SETUP_STATE] !== SetupState.Ready) {
- await new Promise((res) => (parent[SETUP_CALLBACKS] ??= []).push(res));
- }
- attach.call(this, parent);
- }
- function attach(parent) {
- if (!this.isConnected) return;
- if (parent) {
- if (parent.keepAlive && parent.forwardKeepAlive) {
- this.keepAlive = true;
- this.setAttribute("keep-alive", "");
- }
- const scope = this.$.$$.scope;
- if (scope) parent.$.$$.attachScope.append(scope);
- }
- this[SETUP]();
- }
- function findParent(host) {
- let node = host.parentNode, prefix = host.localName.split("-", 1)[0] + "-";
- while (node) {
- if (node.nodeType === 1 && node.localName.startsWith(prefix)) {
- return node;
- }
- node = node.parentNode;
- }
- return null;
- }
- function defineCustomElement(element, throws = false) {
- if (throws || !window.customElements.get(element.tagName)) {
- window.customElements.define(element.tagName, element);
- }
- }
- var SCOPE, scheduledEffects, runningEffects, currentScope, currentObserver, currentObservers, currentObserversIndex, effects, defaultContext, NOOP, STATE_CLEAN, STATE_CHECK, STATE_DIRTY, STATE_DISPOSED, ScopeNode, ScopeProto, ComputeNode, ComputeProto, EVENT, DOM_EVENT, DOMEvent, EventTriggers, EventsTarget, EventsController, effect, PROPS, METHODS, ON_DISPATCH, EMPTY_PROPS, Instance, currentInstance, ViewController, Component, State, requestIdleCallback, key, webkit, moz, ms, document$1, vendor, fscreen, functionThrottle, functionDebounce, t, e, n, o, l, r, STRING, NULLABLE_STRING, NUMBER, BOOLEAN, FUNCTION, ARRAY, OBJECT, ATTRS, SETUP, SETUP_STATE, SETUP_CALLBACKS, SetupState, Icon$24, Icon$0, Icon$5, Icon$8, Icon$11, Icon$13, Icon$16, Icon$19, Icon$22, Icon$26, Icon$27, Icon$31, Icon$33, Icon$34, Icon$35, Icon$39, Icon$40, Icon$53, Icon$54, Icon$56, Icon$59, Icon$60, Icon$61, Icon$62, Icon$63, Icon$74, Icon$77, Icon$81, Icon$88, Icon$104, Icon$105;
- var init_vidstack_CRlI3Mh7 = __esm({
- "node_modules/vidstack/prod/chunks/vidstack-CRlI3Mh7.js"() {
- SCOPE = Symbol(0);
- scheduledEffects = false;
- runningEffects = false;
- currentScope = null;
- currentObserver = null;
- currentObservers = null;
- currentObserversIndex = 0;
- effects = [];
- defaultContext = {};
- NOOP = () => {
- };
- STATE_CLEAN = 0;
- STATE_CHECK = 1;
- STATE_DIRTY = 2;
- STATE_DISPOSED = 3;
- ScopeNode = function Scope() {
- this[SCOPE] = null;
- this.$h = null;
- if (currentScope)
- currentScope.append(this);
- };
- ScopeProto = ScopeNode.prototype;
- ScopeProto.$cx = defaultContext;
- ScopeProto.$eh = null;
- ScopeProto.$c = null;
- ScopeProto.$d = null;
- ScopeProto.append = function(child) {
- child[SCOPE] = this;
- if (!this.$h) {
- this.$h = child;
- } else if (Array.isArray(this.$h)) {
- this.$h.push(child);
- } else {
- this.$h = [this.$h, child];
- }
- child.$cx = child.$cx === defaultContext ? this.$cx : { ...this.$cx, ...child.$cx };
- if (this.$eh) {
- child.$eh = !child.$eh ? this.$eh : [...child.$eh, ...this.$eh];
- }
- };
- ScopeProto.dispose = function() {
- dispose.call(this);
- };
- ComputeNode = function Computation(initialValue, compute2, options) {
- ScopeNode.call(this);
- this.$st = compute2 ? STATE_DIRTY : STATE_CLEAN;
- this.$i = false;
- this.$e = false;
- this.$s = null;
- this.$o = null;
- this.$v = initialValue;
- if (compute2)
- this.$c = compute2;
- if (options && options.dirty)
- this.$ch = options.dirty;
- };
- ComputeProto = ComputeNode.prototype;
- Object.setPrototypeOf(ComputeProto, ScopeProto);
- ComputeProto.$ch = isNotEqual;
- ComputeProto.call = read;
- EVENT = Event;
- DOM_EVENT = Symbol("DOM_EVENT");
- DOMEvent = class extends EVENT {
- [DOM_EVENT] = true;
- /**
- * The event detail.
- */
- detail;
- /**
- * The event trigger chain.
- */
- triggers = new EventTriggers();
- /**
- * The preceding event that was responsible for this event being fired.
- */
- get trigger() {
- return this.triggers.source;
- }
- /**
- * The origin event that lead to this event being fired.
- */
- get originEvent() {
- return this.triggers.origin;
- }
- /**
- * Whether the origin event was triggered by the user.
- *
- * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Event/isTrusted}
- */
- get isOriginTrusted() {
- return this.triggers.origin?.isTrusted ?? false;
- }
- constructor(type, ...init) {
- super(type, init[0]);
- this.detail = init[0]?.detail;
- const trigger = init[0]?.trigger;
- if (trigger) this.triggers.add(trigger);
- }
- };
- EventTriggers = class {
- chain = [];
- get source() {
- return this.chain[0];
- }
- get origin() {
- return this.chain[this.chain.length - 1];
- }
- /**
- * Appends the event to the end of the chain.
- */
- add(event2) {
- this.chain.push(event2);
- if (isDOMEvent(event2)) {
- this.chain.push(...event2.triggers);
- }
- }
- /**
- * Removes the event from the chain and returns it (if found).
- */
- remove(event2) {
- return this.chain.splice(this.chain.indexOf(event2), 1)[0];
- }
- /**
- * Returns whether the chain contains the given `event`.
- */
- has(event2) {
- return this.chain.some((e6) => e6 === event2);
- }
- /**
- * Returns whether the chain contains the given event type.
- */
- hasType(type) {
- return !!this.findType(type);
- }
- /**
- * Returns the first event with the given `type` found in the chain.
- */
- findType(type) {
- return this.chain.find((e6) => e6.type === type);
- }
- /**
- * Walks an event chain on a given `event`, and invokes the given `callback` for each trigger event.
- */
- walk(callback) {
- for (const event2 of this.chain) {
- const returnValue = callback(event2);
- if (returnValue) return [event2, returnValue];
- }
- }
- [Symbol.iterator]() {
- return this.chain.values();
- }
- };
- EventsTarget = class extends EventTarget {
- /** @internal type only */
- $ts__events;
- addEventListener(type, callback, options) {
- return super.addEventListener(type, callback, options);
- }
- removeEventListener(type, callback, options) {
- return super.removeEventListener(type, callback, options);
- }
- };
- EventsController = class {
- #target;
- #controller;
- get signal() {
- return this.#controller.signal;
- }
- constructor(target) {
- this.#target = target;
- this.#controller = new AbortController();
- onDispose(this.abort.bind(this));
- }
- add(type, handler, options) {
- if (this.signal.aborted) throw Error("aborted");
- this.#target.addEventListener(type, handler, {
- ...options,
- signal: options?.signal ? anySignal(this.signal, options.signal) : this.signal
- });
- return this;
- }
- remove(type, handler) {
- this.#target.removeEventListener(type, handler);
- return this;
- }
- abort(reason) {
- this.#controller.abort(reason);
- }
- };
- effect = effect$1;
- PROPS = /* @__PURE__ */ Symbol(0);
- METHODS = /* @__PURE__ */ Symbol(0);
- ON_DISPATCH = /* @__PURE__ */ Symbol(0);
- EMPTY_PROPS = {};
- Instance = class {
- /** @internal type only */
- $ts__events;
- /** @internal type only */
- $ts__vars;
- /* @internal */
- [ON_DISPATCH] = null;
- $el = signal(null);
- el = null;
- scope = null;
- attachScope = null;
- connectScope = null;
- component = null;
- destroyed = false;
- props = EMPTY_PROPS;
- attrs = null;
- styles = null;
- state;
- $state;
- #setupCallbacks = [];
- #attachCallbacks = [];
- #connectCallbacks = [];
- #destroyCallbacks = [];
- constructor(Component2, scope, init) {
- this.scope = scope;
- if (init?.scope) init.scope.append(scope);
- let stateFactory = Component2.state, props = Component2.props;
- if (stateFactory) {
- this.$state = stateFactory.create();
- this.state = new Proxy(this.$state, {
- get: (_2, prop2) => this.$state[prop2]()
- });
- provideContext(stateFactory, this.$state);
- }
- if (props) {
- this.props = createInstanceProps(props);
- if (init?.props) {
- for (const prop2 of Object.keys(init.props)) {
- this.props[prop2]?.set(init.props[prop2]);
- }
- }
- }
- onDispose(this.destroy.bind(this));
- }
- setup() {
- scoped(() => {
- for (const callback of this.#setupCallbacks) callback();
- }, this.scope);
- }
- attach(el) {
- if (this.el) return;
- this.el = el;
- this.$el.set(el);
- scoped(() => {
- this.attachScope = createScope();
- scoped(() => {
- for (const callback of this.#attachCallbacks) callback(this.el);
- this.#attachAttrs();
- this.#attachStyles();
- }, this.attachScope);
- }, this.scope);
- el.dispatchEvent(new Event("attached"));
- }
- detach() {
- this.attachScope?.dispose();
- this.attachScope = null;
- this.connectScope = null;
- this.el = null;
- this.$el.set(null);
- }
- connect() {
- if (!this.el || !this.attachScope || !this.#connectCallbacks.length) return;
- scoped(() => {
- this.connectScope = createScope();
- scoped(() => {
- for (const callback of this.#connectCallbacks) callback(this.el);
- }, this.connectScope);
- }, this.attachScope);
- }
- disconnect() {
- this.connectScope?.dispose();
- this.connectScope = null;
- }
- destroy() {
- if (this.destroyed) return;
- this.destroyed = true;
- scoped(() => {
- for (const callback of this.#destroyCallbacks) callback(this.el);
- }, this.scope);
- const el = this.el;
- this.detach();
- this.scope.dispose();
- this.#setupCallbacks.length = 0;
- this.#attachCallbacks.length = 0;
- this.#connectCallbacks.length = 0;
- this.#destroyCallbacks.length = 0;
- this.component = null;
- this.attrs = null;
- this.styles = null;
- this.props = EMPTY_PROPS;
- this.scope = null;
- this.state = EMPTY_PROPS;
- this.$state = null;
- if (el) delete el.$;
- }
- addHooks(target) {
- if (target.onSetup) this.#setupCallbacks.push(target.onSetup.bind(target));
- if (target.onAttach) this.#attachCallbacks.push(target.onAttach.bind(target));
- if (target.onConnect) this.#connectCallbacks.push(target.onConnect.bind(target));
- if (target.onDestroy) this.#destroyCallbacks.push(target.onDestroy.bind(target));
- }
- #attachAttrs() {
- if (!this.attrs) return;
- for (const name of Object.keys(this.attrs)) {
- if (isFunction(this.attrs[name])) {
- effect(this.#setAttr.bind(this, name));
- } else {
- setAttribute(this.el, name, this.attrs[name]);
- }
- }
- }
- #attachStyles() {
- if (!this.styles) return;
- for (const name of Object.keys(this.styles)) {
- if (isFunction(this.styles[name])) {
- effect(this.#setStyle.bind(this, name));
- } else {
- setStyle(this.el, name, this.styles[name]);
- }
- }
- }
- #setAttr(name) {
- setAttribute(this.el, name, this.attrs[name].call(this.component));
- }
- #setStyle(name) {
- setStyle(this.el, name, this.styles[name].call(this.component));
- }
- };
- currentInstance = { $$: null };
- ViewController = class extends EventTarget {
- /** @internal */
- $$;
- get el() {
- return this.$$.el;
- }
- get $el() {
- return this.$$.$el();
- }
- get scope() {
- return this.$$.scope;
- }
- get attachScope() {
- return this.$$.attachScope;
- }
- get connectScope() {
- return this.$$.connectScope;
- }
- /** @internal */
- get $props() {
- return this.$$.props;
- }
- /** @internal */
- get $state() {
- return this.$$.$state;
- }
- get state() {
- return this.$$.state;
- }
- constructor() {
- super();
- if (currentInstance.$$) this.attach(currentInstance);
- }
- attach({ $$ }) {
- this.$$ = $$;
- $$.addHooks(this);
- return this;
- }
- addEventListener(type, callback, options) {
- this.listen(type, callback, options);
- }
- removeEventListener(type, callback, options) {
- this.el?.removeEventListener(type, callback, options);
- }
- /**
- * The given callback is invoked when the component is ready to be set up.
- *
- * - This hook will run once.
- * - This hook is called both client-side and server-side.
- * - It's safe to use context inside this hook.
- * - The host element has not attached yet - wait for `onAttach`.
- */
- /**
- * This method can be used to specify attributes that should be set on the host element. Any
- * attributes that are assigned to a function will be considered a signal and updated accordingly.
- */
- setAttributes(attributes) {
- if (!this.$$.attrs) this.$$.attrs = {};
- Object.assign(this.$$.attrs, attributes);
- }
- /**
- * This method can be used to specify styles that should set be set on the host element. Any
- * styles that are assigned to a function will be considered a signal and updated accordingly.
- */
- setStyles(styles) {
- if (!this.$$.styles) this.$$.styles = {};
- Object.assign(this.$$.styles, styles);
- }
- /**
- * This method is used to satisfy the CSS variables contract specified on the current
- * component. Other CSS variables can be set via the `setStyles` method.
- */
- setCSSVars(vars) {
- this.setStyles(vars);
- }
- /**
- * Type-safe utility for creating component DOM events.
- */
- createEvent(type, ...init) {
- return new DOMEvent(type, init[0]);
- }
- /**
- * Creates a `DOMEvent` and dispatches it from the host element. This method is typed to
- * match all component events.
- */
- dispatch(type, ...init) {
- if (!this.el) return false;
- const event2 = type instanceof Event ? type : new DOMEvent(type, init[0]);
- Object.defineProperty(event2, "target", {
- get: () => this.$$.component
- });
- return untrack(() => {
- this.$$[ON_DISPATCH]?.(event2);
- return this.el.dispatchEvent(event2);
- });
- }
- dispatchEvent(event2) {
- return this.dispatch(event2);
- }
- /**
- * Adds an event listener for the given `type` and returns a function which can be invoked to
- * remove the event listener.
- *
- * - The listener is removed if the current scope is disposed.
- * - This method is safe to use on the server (noop).
- */
- listen(type, handler, options) {
- if (!this.el) return noop;
- return listenEvent(this.el, type, handler, options);
- }
- };
- Component = class extends ViewController {
- subscribe(callback) {
- return scoped(() => effect(() => callback(this.state)), this.$$.scope);
- }
- destroy() {
- this.$$.destroy();
- }
- };
- State = class {
- id = Symbol(0);
- record;
- #descriptors;
- constructor(record) {
- this.record = record;
- this.#descriptors = Object.getOwnPropertyDescriptors(record);
- }
- create() {
- const store = {}, state = new Proxy(store, { get: (_2, prop2) => store[prop2]() });
- for (const name of Object.keys(this.record)) {
- const getter = this.#descriptors[name].get;
- store[name] = getter ? computed(getter.bind(state)) : signal(this.record[name]);
- }
- return store;
- }
- reset(record, filter) {
- for (const name of Object.keys(record)) {
- if (!this.#descriptors[name].get && (!filter || filter(name))) {
- record[name].set(this.record[name]);
- }
- }
- }
- };
- requestIdleCallback = typeof window !== "undefined" ? "requestIdleCallback" in window ? window.requestIdleCallback : (cb) => window.setTimeout(cb, 1) : noop;
- key = {
- fullscreenEnabled: 0,
- fullscreenElement: 1,
- requestFullscreen: 2,
- exitFullscreen: 3,
- fullscreenchange: 4,
- fullscreenerror: 5,
- fullscreen: 6
- };
- webkit = [
- "webkitFullscreenEnabled",
- "webkitFullscreenElement",
- "webkitRequestFullscreen",
- "webkitExitFullscreen",
- "webkitfullscreenchange",
- "webkitfullscreenerror",
- "-webkit-full-screen"
- ];
- moz = [
- "mozFullScreenEnabled",
- "mozFullScreenElement",
- "mozRequestFullScreen",
- "mozCancelFullScreen",
- "mozfullscreenchange",
- "mozfullscreenerror",
- "-moz-full-screen"
- ];
- ms = [
- "msFullscreenEnabled",
- "msFullscreenElement",
- "msRequestFullscreen",
- "msExitFullscreen",
- "MSFullscreenChange",
- "MSFullscreenError",
- "-ms-fullscreen"
- ];
- document$1 = typeof window !== "undefined" && typeof window.document !== "undefined" ? window.document : {};
- vendor = "fullscreenEnabled" in document$1 && Object.keys(key) || webkit[0] in document$1 && webkit || moz[0] in document$1 && moz || ms[0] in document$1 && ms || [];
- fscreen = {
- requestFullscreen: function(element) {
- return element[vendor[key.requestFullscreen]]();
- },
- requestFullscreenFunction: function(element) {
- return element[vendor[key.requestFullscreen]];
- },
- get exitFullscreen() {
- return document$1[vendor[key.exitFullscreen]].bind(document$1);
- },
- get fullscreenPseudoClass() {
- return ":" + vendor[key.fullscreen];
- },
- addEventListener: function(type, handler, options) {
- return document$1.addEventListener(vendor[key[type]], handler, options);
- },
- removeEventListener: function(type, handler, options) {
- return document$1.removeEventListener(vendor[key[type]], handler, options);
- },
- get fullscreenEnabled() {
- return Boolean(document$1[vendor[key.fullscreenEnabled]]);
- },
- set fullscreenEnabled(val) {
- },
- get fullscreenElement() {
- return document$1[vendor[key.fullscreenElement]];
- },
- set fullscreenElement(val) {
- },
- get onfullscreenchange() {
- return document$1[("on" + vendor[key.fullscreenchange]).toLowerCase()];
- },
- set onfullscreenchange(handler) {
- return document$1[("on" + vendor[key.fullscreenchange]).toLowerCase()] = handler;
- },
- get onfullscreenerror() {
- return document$1[("on" + vendor[key.fullscreenerror]).toLowerCase()];
- },
- set onfullscreenerror(handler) {
- return document$1[("on" + vendor[key.fullscreenerror]).toLowerCase()] = handler;
- }
- };
- functionThrottle = throttle;
- functionDebounce = debounce;
- t = (t22) => "object" == typeof t22 && null != t22 && 1 === t22.nodeType;
- e = (t22, e22) => (!e22 || "hidden" !== t22) && ("visible" !== t22 && "clip" !== t22);
- n = (t22, n22) => {
- if (t22.clientHeight < t22.scrollHeight || t22.clientWidth < t22.scrollWidth) {
- const o22 = getComputedStyle(t22, null);
- return e(o22.overflowY, n22) || e(o22.overflowX, n22) || ((t32) => {
- const e22 = ((t42) => {
- if (!t42.ownerDocument || !t42.ownerDocument.defaultView) return null;
- try {
- return t42.ownerDocument.defaultView.frameElement;
- } catch (t5) {
- return null;
- }
- })(t32);
- return !!e22 && (e22.clientHeight < t32.scrollHeight || e22.clientWidth < t32.scrollWidth);
- })(t22);
- }
- return false;
- };
- o = (t22, e22, n22, o22, l22, r22, i4, s4) => r22 < t22 && i4 > e22 || r22 > t22 && i4 < e22 ? 0 : r22 <= t22 && s4 <= n22 || i4 >= e22 && s4 >= n22 ? r22 - t22 - o22 : i4 > e22 && s4 < n22 || r22 < t22 && s4 > n22 ? i4 - e22 + l22 : 0;
- l = (t22) => {
- const e22 = t22.parentElement;
- return null == e22 ? t22.getRootNode().host || null : e22;
- };
- r = (e22, r22) => {
- var i4, s4, d2, h4;
- if ("undefined" == typeof document) return [];
- const { scrollMode: c3, block: f2, inline: u2, boundary: a3, skipOverflowHiddenElements: g2 } = r22, p2 = "function" == typeof a3 ? a3 : (t22) => t22 !== a3;
- if (!t(e22)) throw new TypeError("Invalid target");
- const m2 = document.scrollingElement || document.documentElement, w2 = [];
- let W = e22;
- for (; t(W) && p2(W); ) {
- if (W = l(W), W === m2) {
- w2.push(W);
- break;
- }
- null != W && W === document.body && n(W) && !n(document.documentElement) || null != W && n(W, g2) && w2.push(W);
- }
- const b2 = null != (s4 = null == (i4 = window.visualViewport) ? void 0 : i4.width) ? s4 : innerWidth, H2 = null != (h4 = null == (d2 = window.visualViewport) ? void 0 : d2.height) ? h4 : innerHeight, { scrollX: y2, scrollY: M2 } = window, { height: v2, width: E2, top: x2, right: C2, bottom: I2, left: R2 } = e22.getBoundingClientRect(), { top: T2, right: B2, bottom: F, left: V2 } = ((t22) => {
- const e32 = window.getComputedStyle(t22);
- return { top: parseFloat(e32.scrollMarginTop) || 0, right: parseFloat(e32.scrollMarginRight) || 0, bottom: parseFloat(e32.scrollMarginBottom) || 0, left: parseFloat(e32.scrollMarginLeft) || 0 };
- })(e22);
- let k2 = "start" === f2 || "nearest" === f2 ? x2 - T2 : "end" === f2 ? I2 + F : x2 + v2 / 2 - T2 + F, D2 = "center" === u2 ? R2 + E2 / 2 - V2 + B2 : "end" === u2 ? C2 + B2 : R2 - V2;
- const L2 = [];
- for (let t22 = 0; t22 < w2.length; t22++) {
- const e32 = w2[t22], { height: n22, width: l22, top: r32, right: i22, bottom: s22, left: d22 } = e32.getBoundingClientRect();
- if ("if-needed" === c3 && x2 >= 0 && R2 >= 0 && I2 <= H2 && C2 <= b2 && x2 >= r32 && I2 <= s22 && R2 >= d22 && C2 <= i22) return L2;
- const h22 = getComputedStyle(e32), a22 = parseInt(h22.borderLeftWidth, 10), g22 = parseInt(h22.borderTopWidth, 10), p22 = parseInt(h22.borderRightWidth, 10), W2 = parseInt(h22.borderBottomWidth, 10);
- let T22 = 0, B22 = 0;
- const F2 = "offsetWidth" in e32 ? e32.offsetWidth - e32.clientWidth - a22 - p22 : 0, V22 = "offsetHeight" in e32 ? e32.offsetHeight - e32.clientHeight - g22 - W2 : 0, S2 = "offsetWidth" in e32 ? 0 === e32.offsetWidth ? 0 : l22 / e32.offsetWidth : 0, X = "offsetHeight" in e32 ? 0 === e32.offsetHeight ? 0 : n22 / e32.offsetHeight : 0;
- if (m2 === e32) T22 = "start" === f2 ? k2 : "end" === f2 ? k2 - H2 : "nearest" === f2 ? o(M2, M2 + H2, H2, g22, W2, M2 + k2, M2 + k2 + v2, v2) : k2 - H2 / 2, B22 = "start" === u2 ? D2 : "center" === u2 ? D2 - b2 / 2 : "end" === u2 ? D2 - b2 : o(y2, y2 + b2, b2, a22, p22, y2 + D2, y2 + D2 + E2, E2), T22 = Math.max(0, T22 + M2), B22 = Math.max(0, B22 + y2);
- else {
- T22 = "start" === f2 ? k2 - r32 - g22 : "end" === f2 ? k2 - s22 + W2 + V22 : "nearest" === f2 ? o(r32, s22, n22, g22, W2 + V22, k2, k2 + v2, v2) : k2 - (r32 + n22 / 2) + V22 / 2, B22 = "start" === u2 ? D2 - d22 - a22 : "center" === u2 ? D2 - (d22 + l22 / 2) + F2 / 2 : "end" === u2 ? D2 - i22 + p22 + F2 : o(d22, i22, l22, a22, p22 + F2, D2, D2 + E2, E2);
- const { scrollLeft: t32, scrollTop: h32 } = e32;
- T22 = 0 === X ? 0 : Math.max(0, Math.min(h32 + T22 / X, e32.scrollHeight - n22 / X + V22)), B22 = 0 === S2 ? 0 : Math.max(0, Math.min(t32 + B22 / S2, e32.scrollWidth - l22 / S2 + F2)), k2 += h32 - T22, D2 += t32 - B22;
- }
- L2.push({ el: e32, top: T22, left: B22 });
- }
- return L2;
- };
- STRING = (v2) => v2 === null ? "" : v2 + "";
- NULLABLE_STRING = (v2) => v2 === null ? null : v2 + "";
- NUMBER = (v2) => v2 === null ? 0 : Number(v2);
- BOOLEAN = (v2) => v2 !== null;
- FUNCTION = () => null;
- ARRAY = (v2) => v2 === null ? [] : JSON.parse(v2);
- OBJECT = (v2) => v2 === null ? {} : JSON.parse(v2);
- ATTRS = /* @__PURE__ */ Symbol(0);
- SETUP = /* @__PURE__ */ Symbol(0);
- SETUP_STATE = /* @__PURE__ */ Symbol(0);
- SETUP_CALLBACKS = /* @__PURE__ */ Symbol(0);
- (function(SetupState2) {
- const Idle = 0;
- SetupState2[SetupState2["Idle"] = Idle] = "Idle";
- const Pending = 1;
- SetupState2[SetupState2["Pending"] = Pending] = "Pending";
- const Ready = 2;
- SetupState2[SetupState2["Ready"] = Ready] = "Ready";
- })(SetupState || (SetupState = {}));
- Icon$24 = ` `;
- Icon$0 = ` `;
- Icon$5 = ` `;
- Icon$8 = ` `;
- Icon$11 = ` `;
- Icon$13 = ` `;
- Icon$16 = ` `;
- Icon$19 = ` `;
- Icon$22 = ` `;
- Icon$26 = ` `;
- Icon$27 = ` `;
- Icon$31 = ` `;
- Icon$33 = ` `;
- Icon$34 = ` `;
- Icon$35 = ` `;
- Icon$39 = ` `;
- Icon$40 = ` `;
- Icon$53 = ` `;
- Icon$54 = ` `;
- Icon$56 = ` `;
- Icon$59 = ` `;
- Icon$60 = ` `;
- Icon$61 = ` `;
- Icon$62 = ` `;
- Icon$63 = ` `;
- Icon$74 = ` `;
- Icon$77 = ` `;
- Icon$81 = ` `;
- Icon$88 = ` `;
- Icon$104 = ` `;
- Icon$105 = ` `;
- }
- });
-
- // node_modules/vidstack/prod/chunks/vidstack-Cpte_fRf.js
- function useMediaContext() {
- return useContext(mediaContext);
- }
- function useMediaState() {
- return useMediaContext().$state;
- }
- var mediaContext;
- var init_vidstack_Cpte_fRf = __esm({
- "node_modules/vidstack/prod/chunks/vidstack-Cpte_fRf.js"() {
- init_vidstack_CRlI3Mh7();
- mediaContext = createContext();
- }
- });
-
- // node_modules/vidstack/prod/chunks/vidstack-DwhHIY5e.js
- function canOrientScreen() {
- return canRotateScreen() && isFunction(screen.orientation.unlock);
- }
- function canRotateScreen() {
- return !isUndefined(window.screen.orientation) && !isUndefined(window.screen.orientation.lock);
- }
- function canPlayAudioType(audio, type) {
- if (!audio) audio = document.createElement("audio");
- return audio.canPlayType(type).length > 0;
- }
- function canPlayVideoType(video, type) {
- if (!video) video = document.createElement("video");
- return video.canPlayType(type).length > 0;
- }
- function canPlayHLSNatively(video) {
- if (!video) video = document.createElement("video");
- return video.canPlayType("application/vnd.apple.mpegurl").length > 0;
- }
- function canUsePictureInPicture(video) {
- return !!document.pictureInPictureEnabled && !video?.disablePictureInPicture;
- }
- function canUseVideoPresentation(video) {
- return isFunction(video?.webkitSupportsPresentationMode) && isFunction(video?.webkitSetPresentationMode);
- }
- async function canChangeVolume() {
- const video = document.createElement("video");
- video.volume = 0.5;
- await waitTimeout(0);
- return video.volume === 0.5;
- }
- function getMediaSource() {
- return window?.ManagedMediaSource ?? window?.MediaSource ?? window?.WebKitMediaSource;
- }
- function getSourceBuffer() {
- return window?.SourceBuffer ?? window?.WebKitSourceBuffer;
- }
- function isHLSSupported() {
- const MediaSource = getMediaSource();
- if (isUndefined(MediaSource)) return false;
- const isTypeSupported = MediaSource && isFunction(MediaSource.isTypeSupported) && MediaSource.isTypeSupported('video/mp4; codecs="avc1.42E01E,mp4a.40.2"');
- const SourceBuffer = getSourceBuffer();
- const isSourceBufferValid = isUndefined(SourceBuffer) || !isUndefined(SourceBuffer.prototype) && isFunction(SourceBuffer.prototype.appendBuffer) && isFunction(SourceBuffer.prototype.remove);
- return !!isTypeSupported && !!isSourceBufferValid;
- }
- function isDASHSupported() {
- return isHLSSupported();
- }
- function isAudioSrc({ src, type }) {
- return isString(src) ? AUDIO_EXTENSIONS.test(src) || AUDIO_TYPES.has(type) || src.startsWith("blob:") && type === "audio/object" : type === "audio/object";
- }
- function isVideoSrc(src) {
- return isString(src.src) ? VIDEO_EXTENSIONS.test(src.src) || VIDEO_TYPES.has(src.type) || src.src.startsWith("blob:") && src.type === "video/object" || isHLSSrc(src) && canPlayHLSNatively() : src.type === "video/object";
- }
- function isHLSSrc({ src, type }) {
- return isString(src) && HLS_VIDEO_EXTENSIONS.test(src) || HLS_VIDEO_TYPES.has(type);
- }
- function isDASHSrc({ src, type }) {
- return isString(src) && DASH_VIDEO_EXTENSIONS.test(src) || DASH_VIDEO_TYPES.has(type);
- }
- function canGoogleCastSrc(src) {
- return isString(src.src) && (isAudioSrc(src) || isVideoSrc(src) || isHLSSrc(src));
- }
- function isMediaStream(src) {
- return typeof window.MediaStream !== "undefined" && src instanceof window.MediaStream;
- }
- var UA, IS_IOS, IS_IPHONE, IS_CHROME, IS_SAFARI, AUDIO_EXTENSIONS, AUDIO_TYPES, VIDEO_EXTENSIONS, VIDEO_TYPES, HLS_VIDEO_EXTENSIONS, DASH_VIDEO_EXTENSIONS, HLS_VIDEO_TYPES, DASH_VIDEO_TYPES;
- var init_vidstack_DwhHIY5e = __esm({
- "node_modules/vidstack/prod/chunks/vidstack-DwhHIY5e.js"() {
- init_vidstack_CRlI3Mh7();
- UA = navigator?.userAgent.toLowerCase() || "";
- IS_IOS = /iphone|ipad|ipod|ios|crios|fxios/i.test(UA);
- IS_IPHONE = /(iphone|ipod)/gi.test(navigator?.platform || "");
- IS_CHROME = !!window.chrome;
- IS_SAFARI = !!window.safari || IS_IOS;
- AUDIO_EXTENSIONS = /\.(m4a|m4b|mp4a|mpga|mp2|mp2a|mp3|m2a|m3a|wav|weba|aac|oga|spx|flac)($|\?)/i;
- AUDIO_TYPES = /* @__PURE__ */ new Set([
- "audio/mpeg",
- "audio/ogg",
- "audio/3gp",
- "audio/mp3",
- "audio/webm",
- "audio/flac",
- "audio/m4a",
- "audio/m4b",
- "audio/mp4a",
- "audio/mp4"
- ]);
- VIDEO_EXTENSIONS = /\.(mp4|og[gv]|webm|mov|m4v)(#t=[,\d+]+)?($|\?)/i;
- VIDEO_TYPES = /* @__PURE__ */ new Set([
- "video/mp4",
- "video/webm",
- "video/3gp",
- "video/ogg",
- "video/avi",
- "video/mpeg"
- ]);
- HLS_VIDEO_EXTENSIONS = /\.(m3u8)($|\?)/i;
- DASH_VIDEO_EXTENSIONS = /\.(mpd)($|\?)/i;
- HLS_VIDEO_TYPES = /* @__PURE__ */ new Set([
- // Apple sanctioned
- "application/vnd.apple.mpegurl",
- // Apple sanctioned for backwards compatibility
- "audio/mpegurl",
- // Very common
- "audio/x-mpegurl",
- // Very common
- "application/x-mpegurl",
- // Included for completeness
- "video/x-mpegurl",
- "video/mpegurl",
- "application/mpegurl"
- ]);
- DASH_VIDEO_TYPES = /* @__PURE__ */ new Set(["application/dash+xml"]);
- }
- });
-
- // node_modules/vidstack/prod/chunks/vidstack-BmMUBVGQ.js
- function getTimeRangesStart(range) {
- if (!range.length) return null;
- let min2 = range.start(0);
- for (let i4 = 1; i4 < range.length; i4++) {
- const value = range.start(i4);
- if (value < min2) min2 = value;
- }
- return min2;
- }
- function getTimeRangesEnd(range) {
- if (!range.length) return null;
- let max2 = range.end(0);
- for (let i4 = 1; i4 < range.length; i4++) {
- const value = range.end(i4);
- if (value > max2) max2 = value;
- }
- return max2;
- }
- function normalizeTimeIntervals(intervals) {
- if (intervals.length <= 1) {
- return intervals;
- }
- intervals.sort((a3, b2) => a3[0] - b2[0]);
- let normalized = [], current = intervals[0];
- for (let i4 = 1; i4 < intervals.length; i4++) {
- const next = intervals[i4];
- if (current[1] >= next[0] - 1) {
- current = [current[0], Math.max(current[1], next[1])];
- } else {
- normalized.push(current);
- current = next;
- }
- }
- normalized.push(current);
- return normalized;
- }
- function updateTimeIntervals(intervals, interval, value) {
- let start = interval[0], end = interval[1];
- if (value < start) {
- return [value, -1];
- } else if (value === start) {
- return interval;
- } else if (start === -1) {
- interval[0] = value;
- return interval;
- } else if (value > start) {
- interval[1] = value;
- if (end === -1) intervals.push(interval);
- }
- normalizeTimeIntervals(intervals);
- return interval;
- }
- var TimeRange;
- var init_vidstack_BmMUBVGQ = __esm({
- "node_modules/vidstack/prod/chunks/vidstack-BmMUBVGQ.js"() {
- init_vidstack_CRlI3Mh7();
- TimeRange = class {
- #ranges;
- get length() {
- return this.#ranges.length;
- }
- constructor(start, end) {
- if (isArray(start)) {
- this.#ranges = start;
- } else if (!isUndefined(start) && !isUndefined(end)) {
- this.#ranges = [[start, end]];
- } else {
- this.#ranges = [];
- }
- }
- start(index) {
- return this.#ranges[index][0] ?? Infinity;
- }
- end(index) {
- return this.#ranges[index][1] ?? Infinity;
- }
- };
- }
- });
-
- // node_modules/vidstack/prod/chunks/vidstack-A9j--j6J.js
- function appendParamsToURL(baseUrl, params) {
- const url = new URL(baseUrl);
- for (const key2 of Object.keys(params)) {
- url.searchParams.set(key2, params[key2] + "");
- }
- return url.toString();
- }
- function preconnect(url, rel = "preconnect") {
- const exists = document.querySelector(`link[href="${url}"]`);
- if (!isNull(exists)) return true;
- const link = document.createElement("link");
- link.rel = rel;
- link.href = url;
- link.crossOrigin = "true";
- document.head.append(link);
- return true;
- }
- function loadScript(src) {
- if (pendingRequests[src]) return pendingRequests[src].promise;
- const promise = deferredPromise(), exists = document.querySelector(`script[src="${src}"]`);
- if (!isNull(exists)) {
- promise.resolve();
- return promise.promise;
- }
- pendingRequests[src] = promise;
- const script = document.createElement("script");
- script.src = src;
- script.onload = () => {
- promise.resolve();
- delete pendingRequests[src];
- };
- script.onerror = () => {
- promise.reject();
- delete pendingRequests[src];
- };
- setTimeout(() => document.head.append(script), 0);
- return promise.promise;
- }
- function getRequestCredentials(crossOrigin) {
- return crossOrigin === "use-credentials" ? "include" : isString(crossOrigin) ? "same-origin" : void 0;
- }
- function getDownloadFile({
- title,
- src,
- download
- }) {
- const url = isBoolean(download) || download === "" ? src.src : isString(download) ? download : download?.url;
- if (!isValidFileDownload({ url, src, download })) return null;
- return {
- url,
- name: !isBoolean(download) && !isString(download) && download?.filename || title.toLowerCase() || "media"
- };
- }
- function isValidFileDownload({
- url,
- src,
- download
- }) {
- return isString(url) && (download && download !== true || isAudioSrc(src) || isVideoSrc(src));
- }
- var pendingRequests;
- var init_vidstack_A9j_j6J = __esm({
- "node_modules/vidstack/prod/chunks/vidstack-A9j--j6J.js"() {
- init_vidstack_CRlI3Mh7();
- init_vidstack_DwhHIY5e();
- pendingRequests = {};
- }
- });
-
- // node_modules/vidstack/prod/chunks/vidstack-lwuXewh7.js
- function isCueActive(cue, time) {
- return time >= cue.startTime && time < cue.endTime;
- }
- function watchActiveTextTrack(tracks, kind, onChange) {
- let currentTrack = null, scope = getScope();
- function onModeChange() {
- const kinds = isString(kind) ? [kind] : kind, track = tracks.toArray().find((track2) => kinds.includes(track2.kind) && track2.mode === "showing");
- if (track === currentTrack) return;
- if (!track) {
- onChange(null);
- currentTrack = null;
- return;
- }
- if (track.readyState == 2) {
- onChange(track);
- } else {
- onChange(null);
- scoped(() => {
- const off = listenEvent(
- track,
- "load",
- () => {
- onChange(track);
- off();
- },
- { once: true }
- );
- }, scope);
- }
- currentTrack = track;
- }
- onModeChange();
- return listenEvent(tracks, "mode-change", onModeChange);
- }
- function watchCueTextChange(tracks, kind, callback) {
- watchActiveTextTrack(tracks, kind, (track) => {
- if (!track) {
- callback("");
- return;
- }
- const onCueChange = () => {
- const activeCue = track?.activeCues[0];
- callback(activeCue?.text || "");
- };
- onCueChange();
- listenEvent(track, "cue-change", onCueChange);
- });
- }
- var init_vidstack_lwuXewh7 = __esm({
- "node_modules/vidstack/prod/chunks/vidstack-lwuXewh7.js"() {
- init_vidstack_CRlI3Mh7();
- }
- });
-
- // node_modules/media-captions/dist/prod/srt-parser.js
- var srt_parser_exports = {};
- __export(srt_parser_exports, {
- SRTParser: () => SRTParser,
- default: () => createSRTParser
- });
- function createSRTParser() {
- return new SRTParser();
- }
- var MILLISECOND_SEP_RE, TIMESTAMP_SEP, SRTParser;
- var init_srt_parser = __esm({
- "node_modules/media-captions/dist/prod/srt-parser.js"() {
- init_prod();
- MILLISECOND_SEP_RE = /,/g;
- TIMESTAMP_SEP = "-->";
- SRTParser = class extends VTTParser {
- parse(line, lineCount) {
- if (line === "") {
- if (this.c) {
- this.l.push(this.c);
- this.h.onCue?.(this.c);
- this.c = null;
- }
- this.e = VTTBlock.None;
- } else if (this.e === VTTBlock.Cue) {
- this.c.text += (this.c.text ? "\n" : "") + line;
- } else if (line.includes(TIMESTAMP_SEP)) {
- const result = this.q(line, lineCount);
- if (result) {
- this.c = new VTTCue(result[0], result[1], result[2].join(" "));
- this.c.id = this.n;
- this.e = VTTBlock.Cue;
- }
- }
- this.n = line;
- }
- q(line, lineCount) {
- return super.q(line.replace(MILLISECOND_SEP_RE, "."), lineCount);
- }
- };
- }
- });
-
- // node_modules/media-captions/dist/prod/errors.js
- var errors_exports = {};
- __export(errors_exports, {
- ParseErrorBuilder: () => ParseErrorBuilder
- });
- var ParseErrorBuilder;
- var init_errors = __esm({
- "node_modules/media-captions/dist/prod/errors.js"() {
- init_prod();
- ParseErrorBuilder = {
- r() {
- return new ParseError({
- code: ParseErrorCode.BadSignature,
- reason: "missing WEBVTT file header",
- line: 1
- });
- },
- s(startTime, line) {
- return new ParseError({
- code: ParseErrorCode.BadTimestamp,
- reason: `cue start timestamp \`${startTime}\` is invalid on line ${line}`,
- line
- });
- },
- t(endTime, line) {
- return new ParseError({
- code: ParseErrorCode.BadTimestamp,
- reason: `cue end timestamp \`${endTime}\` is invalid on line ${line}`,
- line
- });
- },
- u(startTime, endTime, line) {
- return new ParseError({
- code: ParseErrorCode.BadTimestamp,
- reason: `cue end timestamp \`${endTime}\` is greater than start \`${startTime}\` on line ${line}`,
- line
- });
- },
- y(name, value, line) {
- return new ParseError({
- code: ParseErrorCode.BadSettingValue,
- reason: `invalid value for cue setting \`${name}\` on line ${line} (value: ${value})`,
- line
- });
- },
- x(name, value, line) {
- return new ParseError({
- code: ParseErrorCode.UnknownSetting,
- reason: `unknown cue setting \`${name}\` on line ${line} (value: ${value})`,
- line
- });
- },
- w(name, value, line) {
- return new ParseError({
- code: ParseErrorCode.BadSettingValue,
- reason: `invalid value for region setting \`${name}\` on line ${line} (value: ${value})`,
- line
- });
- },
- v(name, value, line) {
- return new ParseError({
- code: ParseErrorCode.UnknownSetting,
- reason: `unknown region setting \`${name}\` on line ${line} (value: ${value})`,
- line
- });
- },
- // SSA-specific errors
- T(type, line) {
- return new ParseError({
- code: ParseErrorCode.BadFormat,
- reason: `format missing for \`${type}\` block on line ${line}`,
- line
- });
- }
- };
- }
- });
-
- // node_modules/media-captions/dist/prod/ssa-parser.js
- var ssa_parser_exports = {};
- __export(ssa_parser_exports, {
- SSAParser: () => SSAParser,
- default: () => createSSAParser
- });
- function parseColor(color) {
- const abgr = parseInt(color.replace("&H", ""), 16);
- if (abgr >= 0) {
- const a3 = abgr >> 24 & 255 ^ 255;
- const alpha = a3 / 255;
- const b2 = abgr >> 16 & 255;
- const g2 = abgr >> 8 & 255;
- const r4 = abgr & 255;
- return "rgba(" + [r4, g2, b2, alpha].join(",") + ")";
- }
- return null;
- }
- function buildTextShadow(x2, y2, color) {
- const noOfShadows = Math.ceil(2 * Math.PI * x2);
- let textShadow = "";
- for (let i4 = 0; i4 < noOfShadows; i4++) {
- const theta = 2 * Math.PI * i4 / noOfShadows;
- textShadow += x2 * Math.cos(theta) + "px " + y2 * Math.sin(theta) + "px 0 " + color + (i4 == noOfShadows - 1 ? "" : ",");
- }
- return textShadow;
- }
- function createSSAParser() {
- return new SSAParser();
- }
- var FORMAT_START_RE, STYLE_START_RE, DIALOGUE_START_RE, FORMAT_SPLIT_RE, STYLE_FUNCTION_RE, NEW_LINE_RE, STYLES_SECTION_START_RE, EVENTS_SECTION_START_RE, SSAParser;
- var init_ssa_parser = __esm({
- "node_modules/media-captions/dist/prod/ssa-parser.js"() {
- init_prod();
- FORMAT_START_RE = /^Format:[\s\t]*/;
- STYLE_START_RE = /^Style:[\s\t]*/;
- DIALOGUE_START_RE = /^Dialogue:[\s\t]*/;
- FORMAT_SPLIT_RE = /[\s\t]*,[\s\t]*/;
- STYLE_FUNCTION_RE = /\{[^}]+\}/g;
- NEW_LINE_RE = /\\N/g;
- STYLES_SECTION_START_RE = /^\[(.*)[\s\t]?Styles\]$/;
- EVENTS_SECTION_START_RE = /^\[(.*)[\s\t]?Events\]$/;
- SSAParser = class {
- h;
- O = 0;
- c = null;
- l = [];
- m = [];
- N = null;
- f;
- P = {};
- async init(init) {
- this.h = init;
- if (init.errors)
- this.f = (await Promise.resolve().then(() => (init_errors(), errors_exports))).ParseErrorBuilder;
- }
- parse(line, lineCount) {
- if (this.O) {
- switch (this.O) {
- case 1:
- if (line === "") {
- this.O = 0;
- } else if (STYLE_START_RE.test(line)) {
- if (this.N) {
- const styles = line.replace(STYLE_START_RE, "").split(FORMAT_SPLIT_RE);
- this.S(styles);
- } else {
- this.g(this.f?.T("Style", lineCount));
- }
- } else if (FORMAT_START_RE.test(line)) {
- this.N = line.replace(FORMAT_START_RE, "").split(FORMAT_SPLIT_RE);
- } else if (EVENTS_SECTION_START_RE.test(line)) {
- this.N = null;
- this.O = 2;
- }
- break;
- case 2:
- if (line === "") {
- this.Q();
- } else if (DIALOGUE_START_RE.test(line)) {
- this.Q();
- if (this.N) {
- const dialogue = line.replace(DIALOGUE_START_RE, "").split(FORMAT_SPLIT_RE), cue = this.U(dialogue, lineCount);
- if (cue)
- this.c = cue;
- } else {
- this.g(this.f?.T("Dialogue", lineCount));
- }
- } else if (this.c) {
- this.c.text += "\n" + line.replace(STYLE_FUNCTION_RE, "").replace(NEW_LINE_RE, "\n");
- } else if (FORMAT_START_RE.test(line)) {
- this.N = line.replace(FORMAT_START_RE, "").split(FORMAT_SPLIT_RE);
- } else if (STYLES_SECTION_START_RE.test(line)) {
- this.N = null;
- this.O = 1;
- } else if (EVENTS_SECTION_START_RE.test(line)) {
- this.N = null;
- }
- }
- } else if (line === "") ;
- else if (STYLES_SECTION_START_RE.test(line)) {
- this.N = null;
- this.O = 1;
- } else if (EVENTS_SECTION_START_RE.test(line)) {
- this.N = null;
- this.O = 2;
- }
- }
- done() {
- return {
- metadata: {},
- cues: this.l,
- regions: [],
- errors: this.m
- };
- }
- Q() {
- if (!this.c)
- return;
- this.l.push(this.c);
- this.h.onCue?.(this.c);
- this.c = null;
- }
- S(values) {
- let name = "Default", styles = {}, outlineX, align = "center", vertical = "bottom", marginV, outlineY = 1.2, outlineColor, bgColor, borderStyle = 3, transform = [];
- for (let i4 = 0; i4 < this.N.length; i4++) {
- const field = this.N[i4], value = values[i4];
- switch (field) {
- case "Name":
- name = value;
- break;
- case "Fontname":
- styles["font-family"] = value;
- break;
- case "Fontsize":
- styles["font-size"] = `calc(${value} / var(--overlay-height))`;
- break;
- case "PrimaryColour":
- const color = parseColor(value);
- if (color)
- styles["--cue-color"] = color;
- break;
- case "BorderStyle":
- borderStyle = parseInt(value, 10);
- break;
- case "BackColour":
- bgColor = parseColor(value);
- break;
- case "OutlineColour":
- const _outlineColor = parseColor(value);
- if (_outlineColor)
- outlineColor = _outlineColor;
- break;
- case "Bold":
- if (parseInt(value))
- styles["font-weight"] = "bold";
- break;
- case "Italic":
- if (parseInt(value))
- styles["font-style"] = "italic";
- break;
- case "Underline":
- if (parseInt(value))
- styles["text-decoration"] = "underline";
- break;
- case "StrikeOut":
- if (parseInt(value))
- styles["text-decoration"] = "line-through";
- break;
- case "Spacing":
- styles["letter-spacing"] = value + "px";
- break;
- case "AlphaLevel":
- styles["opacity"] = parseFloat(value);
- break;
- case "ScaleX":
- transform.push(`scaleX(${parseFloat(value) / 100})`);
- break;
- case "ScaleY":
- transform.push(`scaleY(${parseFloat(value) / 100})`);
- break;
- case "Angle":
- transform.push(`rotate(${value}deg)`);
- break;
- case "Shadow":
- outlineY = parseInt(value, 10) * 1.2;
- break;
- case "MarginL":
- styles["--cue-width"] = "auto";
- styles["--cue-left"] = parseFloat(value) + "px";
- break;
- case "MarginR":
- styles["--cue-width"] = "auto";
- styles["--cue-right"] = parseFloat(value) + "px";
- break;
- case "MarginV":
- marginV = parseFloat(value);
- break;
- case "Outline":
- outlineX = parseInt(value, 10);
- break;
- case "Alignment":
- const alignment = parseInt(value, 10);
- if (alignment >= 4)
- vertical = alignment >= 7 ? "top" : "center";
- switch (alignment % 3) {
- case 1:
- align = "start";
- break;
- case 2:
- align = "center";
- break;
- case 3:
- align = "end";
- break;
- }
- }
- }
- styles.R = vertical;
- styles["--cue-white-space"] = "normal";
- styles["--cue-line-height"] = "normal";
- styles["--cue-text-align"] = align;
- if (vertical === "center") {
- styles[`--cue-top`] = "50%";
- transform.push("translateY(-50%)");
- } else {
- styles[`--cue-${vertical}`] = (marginV || 0) + "px";
- }
- if (borderStyle === 1) {
- styles["--cue-padding-y"] = "0";
- }
- if (borderStyle === 1 || bgColor) {
- styles["--cue-bg-color"] = borderStyle === 1 ? "none" : bgColor;
- }
- if (borderStyle === 3 && outlineColor) {
- styles["--cue-outline"] = `${outlineX}px solid ${outlineColor}`;
- }
- if (borderStyle === 1 && typeof outlineX === "number") {
- const color = bgColor ?? "#000";
- styles["--cue-text-shadow"] = [
- outlineColor && buildTextShadow(outlineX * 1.2, outlineY * 1.2, outlineColor),
- outlineColor ? buildTextShadow(outlineX * (outlineX / 2), outlineY * (outlineX / 2), color) : buildTextShadow(outlineX, outlineY, color)
- ].filter(Boolean).join(", ");
- }
- if (transform.length)
- styles["--cue-transform"] = transform.join(" ");
- this.P[name] = styles;
- }
- U(values, lineCount) {
- const fields = this.V(values);
- const timestamp = this.q(fields.Start, fields.End, lineCount);
- if (!timestamp)
- return;
- const cue = new VTTCue(timestamp[0], timestamp[1], ""), styles = { ...this.P[fields.Style] || {} }, voice = fields.Name ? `` : "";
- const vertical = styles.R, marginLeft = fields.MarginL && parseFloat(fields.MarginL), marginRight = fields.MarginR && parseFloat(fields.MarginR), marginV = fields.MarginV && parseFloat(fields.MarginV);
- if (marginLeft) {
- styles["--cue-width"] = "auto";
- styles["--cue-left"] = marginLeft + "px";
- }
- if (marginRight) {
- styles["--cue-width"] = "auto";
- styles["--cue-right"] = marginRight + "px";
- }
- if (marginV && vertical !== "center") {
- styles[`--cue-${vertical}`] = marginV + "px";
- }
- cue.text = voice + values.slice(this.N.length - 1).join(", ").replace(STYLE_FUNCTION_RE, "").replace(NEW_LINE_RE, "\n");
- delete styles.R;
- if (Object.keys(styles).length)
- cue.style = styles;
- return cue;
- }
- V(values) {
- const fields = {};
- for (let i4 = 0; i4 < this.N.length; i4++) {
- fields[this.N[i4]] = values[i4];
- }
- return fields;
- }
- q(startTimeText, endTimeText, lineCount) {
- const startTime = parseVTTTimestamp(startTimeText), endTime = parseVTTTimestamp(endTimeText);
- if (startTime !== null && endTime !== null && endTime > startTime) {
- return [startTime, endTime];
- } else {
- if (startTime === null) {
- this.g(this.f?.s(startTimeText, lineCount));
- }
- if (endTime === null) {
- this.g(this.f?.t(endTimeText, lineCount));
- }
- if (startTime != null && endTime !== null && endTime > startTime) {
- this.g(this.f?.u(startTime, endTime, lineCount));
- }
- }
- }
- g(error) {
- if (!error)
- return;
- this.m.push(error);
- if (this.h.strict) {
- this.h.cancel();
- throw error;
- } else {
- this.h.onError?.(error);
- }
- }
- };
- }
- });
-
- // node_modules/media-captions/dist/prod/index.js
- async function parseText(text, options) {
- const stream = new ReadableStream({
- start(controller) {
- const lines = text.split(LINE_TERMINATOR_RE);
- for (const line of lines)
- controller.enqueue(line);
- controller.close();
- }
- });
- return parseTextStream(stream, options);
- }
- async function parseTextStream(stream, options) {
- const type = options?.type ?? "vtt";
- let factory;
- if (typeof type === "string") {
- switch (type) {
- case "srt":
- factory = (await Promise.resolve().then(() => (init_srt_parser(), srt_parser_exports))).default;
- break;
- case "ssa":
- case "ass":
- factory = (await Promise.resolve().then(() => (init_ssa_parser(), ssa_parser_exports))).default;
- break;
- default:
- factory = (await Promise.resolve().then(function() {
- return vttParser;
- })).default;
- }
- } else {
- factory = type;
- }
- let result;
- const reader = stream.getReader(), parser = factory(), errors = !!options?.strict || !!options?.errors;
- await parser.init({
- strict: false,
- ...options,
- errors,
- type,
- cancel() {
- reader.cancel();
- result = parser.done(true);
- }
- });
- let i4 = 1;
- while (true) {
- const { value, done } = await reader.read();
- if (done) {
- parser.parse("", i4);
- result = parser.done(false);
- break;
- }
- parser.parse(value, i4);
- i4++;
- }
- return result;
- }
- async function parseResponse(response, options) {
- const res = await response;
- if (!res.ok || !res.body) {
- let error;
- return {
- metadata: {},
- cues: [],
- regions: [],
- errors: [error]
- };
- }
- const contentType = res.headers.get("content-type") || "", type = contentType.match(/text\/(.*?)(?:;|$)/)?.[1], encoding = contentType.match(/charset=(.*?)(?:;|$)/)?.[1];
- return parseByteStream(res.body, { type, encoding, ...options });
- }
- async function parseByteStream(stream, { encoding = "utf-8", ...options } = {}) {
- const textStream = stream.pipeThrough(new TextLineTransformStream(encoding));
- return parseTextStream(textStream, options);
- }
- function toNumber(text) {
- const num = parseInt(text, 10);
- return !Number.isNaN(num) ? num : null;
- }
- function toPercentage(text) {
- const num = parseInt(text.replace(PERCENT_SIGN$1, ""), 10);
- return !Number.isNaN(num) && num >= 0 && num <= 100 ? num : null;
- }
- function toCoords(text) {
- if (!text.includes(COMMA$1))
- return null;
- const [x2, y2] = text.split(COMMA$1).map(toPercentage);
- return x2 !== null && y2 !== null ? [x2, y2] : null;
- }
- function toFloat(text) {
- const num = parseFloat(text);
- return !Number.isNaN(num) ? num : null;
- }
- function parseVTTTimestamp(timestamp) {
- const match = timestamp.match(TIMESTAMP_RE);
- if (!match)
- return null;
- const hours = match[1] ? parseInt(match[1], 10) : 0, minutes = parseInt(match[2], 10), seconds = parseInt(match[3], 10), milliseconds = match[4] ? parseInt(match[4].padEnd(3, "0"), 10) : 0, total = hours * 3600 + minutes * 60 + seconds + milliseconds / 1e3;
- if (hours < 0 || minutes < 0 || seconds < 0 || milliseconds < 0 || minutes > 59 || seconds > 59) {
- return null;
- }
- return total;
- }
- function createVTTParser() {
- return new VTTParser();
- }
- function tokenizeVTTCue(cue) {
- let buffer = "", mode = 1, result = [], stack = [], node;
- for (let i4 = 0; i4 < cue.text.length; i4++) {
- const char = cue.text[i4];
- switch (mode) {
- case 1:
- if (char === "<") {
- addText();
- mode = 2;
- } else {
- buffer += char;
- }
- break;
- case 2:
- switch (char) {
- case "\n":
- case " ":
- case " ":
- addNode();
- mode = 4;
- break;
- case ".":
- addNode();
- mode = 3;
- break;
- case "/":
- mode = 5;
- break;
- case ">":
- addNode();
- mode = 1;
- break;
- default:
- if (!buffer && DIGIT_RE.test(char))
- mode = 6;
- buffer += char;
- break;
- }
- break;
- case 3:
- switch (char) {
- case " ":
- case " ":
- case "\n":
- addClass();
- if (node)
- node.class?.trim();
- mode = 4;
- break;
- case ".":
- addClass();
- break;
- case ">":
- addClass();
- if (node)
- node.class?.trim();
- mode = 1;
- break;
- default:
- buffer += char;
- }
- break;
- case 4:
- if (char === ">") {
- buffer = buffer.replace(MULTI_SPACE_RE, " ");
- if (node?.type === "v")
- node.voice = replaceHTMLEntities(buffer);
- else if (node?.type === "lang")
- node.lang = replaceHTMLEntities(buffer);
- buffer = "";
- mode = 1;
- } else {
- buffer += char;
- }
- break;
- case 5:
- if (char === ">") {
- buffer = "";
- node = stack.pop();
- mode = 1;
- }
- break;
- case 6:
- if (char === ">") {
- const time = parseVTTTimestamp(buffer);
- if (time !== null && time >= cue.startTime && time <= cue.endTime) {
- buffer = "timestamp";
- addNode();
- node.time = time;
- }
- buffer = "";
- mode = 1;
- } else {
- buffer += char;
- }
- break;
- }
- }
- function addNode() {
- if (BLOCK_TYPES.has(buffer)) {
- const parent = node;
- node = createBlockNode(buffer);
- if (parent) {
- if (stack[stack.length - 1] !== parent)
- stack.push(parent);
- parent.children.push(node);
- } else
- result.push(node);
- }
- buffer = "";
- mode = 1;
- }
- function addClass() {
- if (node && buffer) {
- const color = buffer.replace("bg_", "");
- if (COLORS.has(color)) {
- node[buffer.startsWith("bg_") ? "bgColor" : "color"] = color;
- } else {
- node.class = !node.class ? buffer : node.class + " " + buffer;
- }
- }
- buffer = "";
- }
- function addText() {
- if (!buffer)
- return;
- const text = { type: "text", data: replaceHTMLEntities(buffer) };
- node ? node.children.push(text) : result.push(text);
- buffer = "";
- }
- if (mode === 1)
- addText();
- return result;
- }
- function createBlockNode(type) {
- return {
- tagName: TAG_NAME[type],
- type,
- children: []
- };
- }
- function replaceHTMLEntities(text) {
- return text.replace(HTML_ENTITY_RE, (entity) => HTML_ENTITIES[entity] || "'");
- }
- function setCSSVar(el, name, value) {
- el.style.setProperty(`--${name}`, value + "");
- }
- function setDataAttr(el, name, value = true) {
- el.setAttribute(`data-${name}`, value === true ? "" : value + "");
- }
- function setPartAttr(el, name) {
- el.setAttribute("data-part", name);
- }
- function getLineHeight(el) {
- return parseFloat(getComputedStyle(el).lineHeight) || 0;
- }
- function createVTTCueTemplate(cue) {
- if (IS_SERVER) {
- throw Error(
- "[media-captions] called `createVTTCueTemplate` on the server - use `renderVTTCueString`"
- );
- }
- const template = document.createElement("template");
- template.innerHTML = renderVTTCueString(cue);
- return { cue, content: template.content };
- }
- function renderVTTCueString(cue, currentTime = 0) {
- return renderVTTTokensString(tokenizeVTTCue(cue), currentTime);
- }
- function renderVTTTokensString(tokens, currentTime = 0) {
- let attrs, result = "";
- for (const token of tokens) {
- if (token.type === "text") {
- result += token.data;
- } else {
- const isTimestamp = token.type === "timestamp";
- attrs = {};
- attrs.class = token.class;
- attrs.title = token.type === "v" && token.voice;
- attrs.lang = token.type === "lang" && token.lang;
- attrs["data-part"] = token.type === "v" && "voice";
- if (isTimestamp) {
- attrs["data-part"] = "timed";
- attrs["data-time"] = token.time;
- attrs["data-future"] = token.time > currentTime;
- attrs["data-past"] = token.time < currentTime;
- }
- attrs.style = `${token.color ? `color: ${token.color};` : ""}${token.bgColor ? `background-color: ${token.bgColor};` : ""}`;
- const attributes = Object.entries(attrs).filter((v2) => v2[1]).map((v2) => `${v2[0]}="${v2[1] === true ? "" : v2[1]}"`).join(" ");
- result += `<${token.tagName}${attributes ? " " + attributes : ""}>${renderVTTTokensString(
- token.children
- )}${token.tagName}>`;
- }
- }
- return result;
- }
- function updateTimedVTTCueNodes(root2, currentTime) {
- if (IS_SERVER)
- return;
- for (const el of root2.querySelectorAll('[data-part="timed"]')) {
- const time = Number(el.getAttribute("data-time"));
- if (Number.isNaN(time))
- continue;
- if (time > currentTime)
- setDataAttr(el, "future");
- else
- el.removeAttribute("data-future");
- if (time < currentTime)
- setDataAttr(el, "past");
- else
- el.removeAttribute("data-past");
- }
- }
- function debounce2(fn, delay) {
- let timeout = null, args;
- function run() {
- clear();
- fn(...args);
- args = void 0;
- }
- function clear() {
- clearTimeout(timeout);
- timeout = null;
- }
- function debounce22() {
- args = [].slice.call(arguments);
- clear();
- timeout = setTimeout(run, delay);
- }
- return debounce22;
- }
- function createBox(box) {
- if (box instanceof HTMLElement) {
- return {
- top: box.offsetTop,
- width: box.clientWidth,
- height: box.clientHeight,
- left: box.offsetLeft,
- right: box.offsetLeft + box.clientWidth,
- bottom: box.offsetTop + box.clientHeight
- };
- }
- return { ...box };
- }
- function moveBox(box, axis, delta) {
- switch (axis) {
- case "+x":
- box.left += delta;
- box.right += delta;
- break;
- case "-x":
- box.left -= delta;
- box.right -= delta;
- break;
- case "+y":
- box.top += delta;
- box.bottom += delta;
- break;
- case "-y":
- box.top -= delta;
- box.bottom -= delta;
- break;
- }
- }
- function isBoxCollision(a3, b2) {
- return a3.left <= b2.right && a3.right >= b2.left && a3.top <= b2.bottom && a3.bottom >= b2.top;
- }
- function isAnyBoxCollision(box, boxes) {
- for (let i4 = 0; i4 < boxes.length; i4++)
- if (isBoxCollision(box, boxes[i4]))
- return boxes[i4];
- return null;
- }
- function isWithinBox(container, box) {
- return box.top >= 0 && box.bottom <= container.height && box.left >= 0 && box.right <= container.width;
- }
- function isBoxOutOfBounds(container, box, axis) {
- switch (axis) {
- case "+x":
- return box.left < 0;
- case "-x":
- return box.right > container.width;
- case "+y":
- return box.top < 0;
- case "-y":
- return box.bottom > container.height;
- }
- }
- function calcBoxIntersectPercentage(container, box) {
- const x2 = Math.max(0, Math.min(container.width, box.right) - Math.max(0, box.left)), y2 = Math.max(0, Math.min(container.height, box.bottom) - Math.max(0, box.top)), intersectArea = x2 * y2;
- return intersectArea / (container.height * container.width);
- }
- function createCSSBox(container, box) {
- return {
- top: box.top / container.height,
- left: box.left / container.width,
- right: (container.width - box.right) / container.width,
- bottom: (container.height - box.bottom) / container.height
- };
- }
- function resolveRelativeBox(container, box) {
- box.top = box.top * container.height;
- box.left = box.left * container.width;
- box.right = container.width - box.right * container.width;
- box.bottom = container.height - box.bottom * container.height;
- return box;
- }
- function setBoxCSSVars(el, container, box, prefix) {
- const cssBox = createCSSBox(container, box);
- for (const side of BOX_SIDES) {
- setCSSVar(el, `${prefix}-${side}`, cssBox[side] * 100 + "%");
- }
- }
- function avoidBoxCollisions(container, box, boxes, axis) {
- let percentage = 1, positionedBox, startBox = { ...box };
- for (let i4 = 0; i4 < axis.length; i4++) {
- while (isBoxOutOfBounds(container, box, axis[i4]) || isWithinBox(container, box) && isAnyBoxCollision(box, boxes)) {
- moveBox(box, axis[i4], 1);
- }
- if (isWithinBox(container, box))
- return box;
- const intersection = calcBoxIntersectPercentage(container, box);
- if (percentage > intersection) {
- positionedBox = { ...box };
- percentage = intersection;
- }
- box = { ...startBox };
- }
- return positionedBox || startBox;
- }
- function positionCue(container, cue, displayEl, boxes) {
- let cueEl = displayEl.firstElementChild, line = computeCueLine(cue), displayBox, axis = [];
- if (!displayEl[STARTING_BOX]) {
- displayEl[STARTING_BOX] = createStartingBox(container, displayEl);
- }
- displayBox = resolveRelativeBox(container, { ...displayEl[STARTING_BOX] });
- if (displayEl[POSITION_OVERRIDE]) {
- axis = [displayEl[POSITION_OVERRIDE] === "top" ? "+y" : "-y", "+x", "-x"];
- } else if (cue.snapToLines) {
- let size2;
- switch (cue.vertical) {
- case "":
- axis = ["+y", "-y"];
- size2 = "height";
- break;
- case "rl":
- axis = ["+x", "-x"];
- size2 = "width";
- break;
- case "lr":
- axis = ["-x", "+x"];
- size2 = "width";
- break;
- }
- let step = getLineHeight(cueEl), position = step * Math.round(line), maxPosition = container[size2] + step, initialAxis = axis[0];
- if (Math.abs(position) > maxPosition) {
- position = position < 0 ? -1 : 1;
- position *= Math.ceil(maxPosition / step) * step;
- }
- if (line < 0) {
- position += cue.vertical === "" ? container.height : container.width;
- axis = axis.reverse();
- }
- moveBox(displayBox, initialAxis, position);
- } else {
- const isHorizontal = cue.vertical === "", posAxis = isHorizontal ? "+y" : "+x", size2 = isHorizontal ? displayBox.height : displayBox.width;
- moveBox(
- displayBox,
- posAxis,
- (isHorizontal ? container.height : container.width) * line / 100
- );
- moveBox(
- displayBox,
- posAxis,
- cue.lineAlign === "center" ? size2 / 2 : cue.lineAlign === "end" ? size2 : 0
- );
- axis = isHorizontal ? ["-y", "+y", "-x", "+x"] : ["-x", "+x", "-y", "+y"];
- }
- displayBox = avoidBoxCollisions(container, displayBox, boxes, axis);
- setBoxCSSVars(displayEl, container, displayBox, "cue");
- return displayBox;
- }
- function createStartingBox(container, cueEl) {
- const box = createBox(cueEl), pos = getStyledPositions(cueEl);
- cueEl[POSITION_OVERRIDE] = false;
- if (pos.top) {
- box.top = pos.top;
- box.bottom = pos.top + box.height;
- cueEl[POSITION_OVERRIDE] = "top";
- }
- if (pos.bottom) {
- const bottom = container.height - pos.bottom;
- box.top = bottom - box.height;
- box.bottom = bottom;
- cueEl[POSITION_OVERRIDE] = "bottom";
- }
- if (pos.left)
- box.left = pos.left;
- if (pos.right)
- box.right = container.width - pos.right;
- return createCSSBox(container, box);
- }
- function getStyledPositions(el) {
- const positions = {};
- for (const side of BOX_SIDES) {
- positions[side] = parseFloat(el.style.getPropertyValue(`--cue-${side}`));
- }
- return positions;
- }
- function computeCueLine(cue) {
- if (cue.line === "auto") {
- if (!cue.snapToLines) {
- return 100;
- } else {
- return -1;
- }
- }
- return cue.line;
- }
- function computeCuePosition(cue) {
- if (cue.position === "auto") {
- switch (cue.align) {
- case "start":
- case "left":
- return 0;
- case "right":
- case "end":
- return 100;
- default:
- return 50;
- }
- }
- return cue.position;
- }
- function computeCuePositionAlignment(cue, dir) {
- if (cue.positionAlign === "auto") {
- switch (cue.align) {
- case "start":
- return dir === "ltr" ? "line-left" : "line-right";
- case "end":
- return dir === "ltr" ? "line-right" : "line-left";
- case "center":
- return "center";
- default:
- return `line-${cue.align}`;
- }
- }
- return cue.positionAlign;
- }
- function positionRegion(container, region, regionEl, boxes) {
- let cues = Array.from(regionEl.querySelectorAll('[data-part="cue-display"]')), height = 0, limit = Math.max(0, cues.length - region.lines);
- for (let i4 = cues.length - 1; i4 >= limit; i4--) {
- height += cues[i4].offsetHeight;
- }
- setCSSVar(regionEl, "region-height", height + "px");
- if (!regionEl[STARTING_BOX]) {
- regionEl[STARTING_BOX] = createCSSBox(container, createBox(regionEl));
- }
- let box = { ...regionEl[STARTING_BOX] };
- box = resolveRelativeBox(container, box);
- box.width = regionEl.clientWidth;
- box.height = height;
- box.right = box.left + box.width;
- box.bottom = box.top + height;
- box = avoidBoxCollisions(container, box, boxes, REGION_AXIS);
- setBoxCSSVars(regionEl, container, box, "region");
- return box;
- }
- var ParseErrorCode, ParseError, LINE_TERMINATOR_RE, TextLineTransformStream, TextStreamLineIterator, TextCue, IS_SERVER, CueBase, VTTCue, VTTRegion, COMMA$1, PERCENT_SIGN$1, HEADER_MAGIC, COMMA, PERCENT_SIGN, SETTING_SEP_RE, SETTING_LINE_RE, NOTE_BLOCK_START, REGION_BLOCK_START, REGION_BLOCK_START_RE, SPACE_RE, TIMESTAMP_SEP2, TIMESTAMP_SEP_RE, ALIGN_RE, LINE_ALIGN_RE, POS_ALIGN_RE, TIMESTAMP_RE, VTTBlock, VTTParser, vttParser, DIGIT_RE, MULTI_SPACE_RE, TAG_NAME, HTML_ENTITIES, HTML_ENTITY_RE, COLORS, BLOCK_TYPES, STARTING_BOX, BOX_SIDES, POSITION_OVERRIDE, REGION_AXIS, CaptionsRenderer;
- var init_prod = __esm({
- "node_modules/media-captions/dist/prod/index.js"() {
- ParseErrorCode = {
- LoadFail: 0,
- BadSignature: 1,
- BadTimestamp: 2,
- BadSettingValue: 3,
- BadFormat: 4,
- UnknownSetting: 5
- };
- ParseError = class extends Error {
- code;
- line;
- constructor(init) {
- super(init.reason);
- this.code = init.code;
- this.line = init.line;
- }
- };
- LINE_TERMINATOR_RE = /\r?\n|\r/gm;
- TextLineTransformStream = class {
- writable;
- readable;
- constructor(encoding) {
- const transformer = new TextStreamLineIterator(encoding);
- this.writable = new WritableStream({
- write(chunk) {
- transformer.transform(chunk);
- },
- close() {
- transformer.close();
- }
- });
- this.readable = new ReadableStream({
- start(controller) {
- transformer.onLine = (line) => controller.enqueue(line);
- transformer.onClose = () => controller.close();
- }
- });
- }
- };
- TextStreamLineIterator = class {
- a = "";
- b;
- onLine;
- onClose;
- constructor(encoding) {
- this.b = new TextDecoder(encoding);
- }
- transform(chunk) {
- this.a += this.b.decode(chunk, { stream: true });
- const lines = this.a.split(LINE_TERMINATOR_RE);
- this.a = lines.pop() || "";
- for (let i4 = 0; i4 < lines.length; i4++)
- this.onLine(lines[i4].trim());
- }
- close() {
- if (this.a)
- this.onLine(this.a.trim());
- this.a = "";
- this.onClose();
- }
- };
- TextCue = class extends EventTarget {
- /**
- * A string that identifies the cue.
- *
- * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/TextTrackCue/id}
- */
- id = "";
- /**
- * A `double` that represents the video time that the cue will start being displayed, in seconds.
- *
- * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/TextTrackCue/startTime}
- */
- startTime;
- /**
- * A `double` that represents the video time that the cue will stop being displayed, in seconds.
- *
- * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/TextTrackCue/endTime}
- */
- endTime;
- /**
- * Returns a string with the contents of the cue.
- *
- * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/VTTCue/text}
- */
- text;
- /**
- * A `boolean` for whether the video will pause when this cue stops being displayed.
- *
- * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/TextTrackCue/pauseOnExit}
- */
- pauseOnExit = false;
- constructor(startTime, endTime, text) {
- super();
- this.startTime = startTime;
- this.endTime = endTime;
- this.text = text;
- }
- addEventListener(type, listener, options) {
- super.addEventListener(type, listener, options);
- }
- removeEventListener(type, listener, options) {
- super.removeEventListener(type, listener, options);
- }
- };
- IS_SERVER = typeof document === "undefined";
- CueBase = IS_SERVER ? TextCue : window.VTTCue;
- VTTCue = class extends CueBase {
- /**
- * A `VTTRegion` object describing the video's sub-region that the cue will be drawn onto,
- * or `null` if none is assigned.
- *
- * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/VTTCue/region}
- */
- region = null;
- /**
- * The cue writing direction.
- *
- * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/VTTCue/vertical}
- */
- vertical = "";
- /**
- * Returns `true` if the `VTTCue.line` attribute is an integer number of lines or a percentage
- * of the video size.
- *
- * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/VTTCue/snapToLines}
- */
- snapToLines = true;
- /**
- * Returns the line positioning of the cue. This can be the string `'auto'` or a number whose
- * interpretation depends on the value of `VTTCue.snapToLines`.
- *
- * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/VTTCue/line}
- */
- line = "auto";
- /**
- * Returns an enum representing the alignment of the `VTTCue.line`.
- *
- * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/VTTCue/lineAlign}
- */
- lineAlign = "start";
- /**
- * Returns the indentation of the cue within the line. This can be the string `'auto'` or a
- * number representing the percentage of the `VTTCue.region`, or the video size if `VTTCue`.region`
- * is `null`.
- *
- * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/VTTCue/position}
- */
- position = "auto";
- /**
- * Returns an enum representing the alignment of the cue. This is used to determine what
- * the `VTTCue.position` is anchored to. The default is `'auto'`.
- *
- * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/VTTCue/positionAlign}
- */
- positionAlign = "auto";
- /**
- * Returns a double representing the size of the cue, as a percentage of the video size.
- *
- * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/VTTCue/size}
- */
- size = 100;
- /**
- * Returns an enum representing the alignment of all the lines of text within the cue box.
- *
- * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/VTTCue/align}
- */
- align = "center";
- /**
- * Additional styles associated with the cue.
- */
- style;
- };
- VTTRegion = class {
- /**
- * A string that identifies the region.
- */
- id = "";
- /**
- * A `double` representing the width of the region, as a percentage of the video.
- */
- width = 100;
- /**
- * A `double` representing the height of the region, in number of lines.
- */
- lines = 3;
- /**
- * A `double` representing the region anchor X offset, as a percentage of the region.
- */
- regionAnchorX = 0;
- /**
- * A `double` representing the region anchor Y offset, as a percentage of the region.
- */
- regionAnchorY = 100;
- /**
- * A `double` representing the viewport anchor X offset, as a percentage of the video.
- */
- viewportAnchorX = 0;
- /**
- * A `double` representing the viewport anchor Y offset, as a percentage of the video.
- */
- viewportAnchorY = 100;
- /**
- * An enum representing how adding new cues will move existing cues.
- */
- scroll = "";
- };
- COMMA$1 = ",";
- PERCENT_SIGN$1 = "%";
- HEADER_MAGIC = "WEBVTT";
- COMMA = ",";
- PERCENT_SIGN = "%";
- SETTING_SEP_RE = /[:=]/;
- SETTING_LINE_RE = /^[\s\t]*(region|vertical|line|position|size|align)[:=]/;
- NOTE_BLOCK_START = "NOTE";
- REGION_BLOCK_START = "REGION";
- REGION_BLOCK_START_RE = /^REGION:?[\s\t]+/;
- SPACE_RE = /[\s\t]+/;
- TIMESTAMP_SEP2 = "-->";
- TIMESTAMP_SEP_RE = /[\s\t]*-->[\s\t]+/;
- ALIGN_RE = /start|center|end|left|right/;
- LINE_ALIGN_RE = /start|center|end/;
- POS_ALIGN_RE = /line-(?:left|right)|center|auto/;
- TIMESTAMP_RE = /^(?:(\d{1,2}):)?(\d{2}):(\d{2})(?:\.(\d{1,3}))?$/;
- VTTBlock = /* @__PURE__ */ ((VTTBlock2) => {
- VTTBlock2[VTTBlock2["None"] = 0] = "None";
- VTTBlock2[VTTBlock2["Header"] = 1] = "Header";
- VTTBlock2[VTTBlock2["Cue"] = 2] = "Cue";
- VTTBlock2[VTTBlock2["Region"] = 3] = "Region";
- VTTBlock2[VTTBlock2["Note"] = 4] = "Note";
- return VTTBlock2;
- })(VTTBlock || {});
- VTTParser = class {
- h;
- e = 0;
- i = {};
- j = {};
- l = [];
- c = null;
- d = null;
- m = [];
- f;
- n = "";
- async init(init) {
- this.h = init;
- if (init.strict)
- this.e = 1;
- if (init.errors)
- this.f = (await Promise.resolve().then(() => (init_errors(), errors_exports))).ParseErrorBuilder;
- }
- parse(line, lineCount) {
- if (line === "") {
- if (this.c) {
- this.l.push(this.c);
- this.h.onCue?.(this.c);
- this.c = null;
- } else if (this.d) {
- this.j[this.d.id] = this.d;
- this.h.onRegion?.(this.d);
- this.d = null;
- } else if (this.e === 1) {
- this.k(line, lineCount);
- this.h.onHeaderMetadata?.(this.i);
- }
- this.e = 0;
- } else if (this.e) {
- switch (this.e) {
- case 1:
- this.k(line, lineCount);
- break;
- case 2:
- if (this.c) {
- const hasText = this.c.text.length > 0;
- if (!hasText && SETTING_LINE_RE.test(line)) {
- this.o(line.split(SPACE_RE), lineCount);
- } else {
- this.c.text += (hasText ? "\n" : "") + line;
- }
- }
- break;
- case 3:
- this.p(line.split(SPACE_RE), lineCount);
- break;
- }
- } else if (line.startsWith(NOTE_BLOCK_START)) {
- this.e = 4;
- } else if (line.startsWith(REGION_BLOCK_START)) {
- this.e = 3;
- this.d = new VTTRegion();
- this.p(line.replace(REGION_BLOCK_START_RE, "").split(SPACE_RE), lineCount);
- } else if (line.includes(TIMESTAMP_SEP2)) {
- const result = this.q(line, lineCount);
- if (result) {
- this.c = new VTTCue(result[0], result[1], "");
- this.c.id = this.n;
- this.o(result[2], lineCount);
- }
- this.e = 2;
- } else if (lineCount === 1) {
- this.k(line, lineCount);
- }
- this.n = line;
- }
- done() {
- return {
- metadata: this.i,
- cues: this.l,
- regions: Object.values(this.j),
- errors: this.m
- };
- }
- k(line, lineCount) {
- if (lineCount > 1) {
- if (SETTING_SEP_RE.test(line)) {
- const [key2, value] = line.split(SETTING_SEP_RE);
- if (key2)
- this.i[key2] = (value || "").replace(SPACE_RE, "");
- }
- } else if (line.startsWith(HEADER_MAGIC)) {
- this.e = 1;
- } else {
- this.g(this.f?.r());
- }
- }
- q(line, lineCount) {
- const [startTimeText, trailingText = ""] = line.split(TIMESTAMP_SEP_RE), [endTimeText, ...settingsText] = trailingText.split(SPACE_RE), startTime = parseVTTTimestamp(startTimeText), endTime = parseVTTTimestamp(endTimeText);
- if (startTime !== null && endTime !== null && endTime > startTime) {
- return [startTime, endTime, settingsText];
- } else {
- if (startTime === null) {
- this.g(this.f?.s(startTimeText, lineCount));
- }
- if (endTime === null) {
- this.g(this.f?.t(endTimeText, lineCount));
- }
- if (startTime != null && endTime !== null && endTime > startTime) {
- this.g(this.f?.u(startTime, endTime, lineCount));
- }
- }
- }
- /**
- * @see {@link https://www.w3.org/TR/webvtt1/#region-settings-parsing}
- */
- p(settings, line) {
- let badValue;
- for (let i4 = 0; i4 < settings.length; i4++) {
- if (SETTING_SEP_RE.test(settings[i4])) {
- badValue = false;
- const [name, value] = settings[i4].split(SETTING_SEP_RE);
- switch (name) {
- case "id":
- this.d.id = value;
- break;
- case "width":
- const width = toPercentage(value);
- if (width !== null)
- this.d.width = width;
- else
- badValue = true;
- break;
- case "lines":
- const lines = toNumber(value);
- if (lines !== null)
- this.d.lines = lines;
- else
- badValue = true;
- break;
- case "regionanchor":
- const region = toCoords(value);
- if (region !== null) {
- this.d.regionAnchorX = region[0];
- this.d.regionAnchorY = region[1];
- } else
- badValue = true;
- break;
- case "viewportanchor":
- const viewport = toCoords(value);
- if (viewport !== null) {
- this.d.viewportAnchorX = viewport[0];
- this.d.viewportAnchorY = viewport[1];
- } else
- badValue = true;
- break;
- case "scroll":
- if (value === "up")
- this.d.scroll = "up";
- else
- badValue = true;
- break;
- default:
- this.g(this.f?.v(name, value, line));
- }
- if (badValue) {
- this.g(this.f?.w(name, value, line));
- }
- }
- }
- }
- /**
- * @see {@link https://www.w3.org/TR/webvtt1/#cue-timings-and-settings-parsing}
- */
- o(settings, line) {
- let badValue;
- for (let i4 = 0; i4 < settings.length; i4++) {
- badValue = false;
- if (SETTING_SEP_RE.test(settings[i4])) {
- const [name, value] = settings[i4].split(SETTING_SEP_RE);
- switch (name) {
- case "region":
- const region = this.j[value];
- if (region)
- this.c.region = region;
- break;
- case "vertical":
- if (value === "lr" || value === "rl") {
- this.c.vertical = value;
- this.c.region = null;
- } else
- badValue = true;
- break;
- case "line":
- const [linePos, lineAlign] = value.split(COMMA);
- if (linePos.includes(PERCENT_SIGN)) {
- const percentage = toPercentage(linePos);
- if (percentage !== null) {
- this.c.line = percentage;
- this.c.snapToLines = false;
- } else
- badValue = true;
- } else {
- const number = toFloat(linePos);
- if (number !== null)
- this.c.line = number;
- else
- badValue = true;
- }
- if (LINE_ALIGN_RE.test(lineAlign)) {
- this.c.lineAlign = lineAlign;
- } else if (lineAlign) {
- badValue = true;
- }
- if (this.c.line !== "auto")
- this.c.region = null;
- break;
- case "position":
- const [colPos, colAlign] = value.split(COMMA), position = toPercentage(colPos);
- if (position !== null)
- this.c.position = position;
- else
- badValue = true;
- if (colAlign && POS_ALIGN_RE.test(colAlign)) {
- this.c.positionAlign = colAlign;
- } else if (colAlign) {
- badValue = true;
- }
- break;
- case "size":
- const size2 = toPercentage(value);
- if (size2 !== null) {
- this.c.size = size2;
- if (size2 < 100)
- this.c.region = null;
- } else {
- badValue = true;
- }
- break;
- case "align":
- if (ALIGN_RE.test(value)) {
- this.c.align = value;
- } else {
- badValue = true;
- }
- break;
- default:
- this.g(this.f?.x(name, value, line));
- }
- if (badValue) {
- this.g(this.f?.y(name, value, line));
- }
- }
- }
- }
- g(error) {
- if (!error)
- return;
- this.m.push(error);
- if (this.h.strict) {
- this.h.cancel();
- throw error;
- } else {
- this.h.onError?.(error);
- }
- }
- };
- vttParser = /* @__PURE__ */ Object.freeze({
- __proto__: null,
- VTTBlock,
- VTTParser,
- default: createVTTParser,
- parseVTTTimestamp
- });
- DIGIT_RE = /[0-9]/;
- MULTI_SPACE_RE = /[\s\t]+/;
- TAG_NAME = {
- c: "span",
- i: "i",
- b: "b",
- u: "u",
- ruby: "ruby",
- rt: "rt",
- v: "span",
- lang: "span",
- timestamp: "span"
- };
- HTML_ENTITIES = {
- "&": "&",
- "<": "<",
- ">": ">",
- """: '"',
- "'": "'",
- " ": "\xA0",
- "": "\u200E",
- "": "\u200F"
- };
- HTML_ENTITY_RE = /&(?:amp|lt|gt|quot|#(0+)?39|nbsp|lrm|rlm);/g;
- COLORS = /* @__PURE__ */ new Set([
- "white",
- "lime",
- "cyan",
- "red",
- "yellow",
- "magenta",
- "blue",
- "black"
- ]);
- BLOCK_TYPES = /* @__PURE__ */ new Set(Object.keys(TAG_NAME));
- STARTING_BOX = Symbol(0);
- BOX_SIDES = ["top", "left", "right", "bottom"];
- POSITION_OVERRIDE = Symbol(0);
- REGION_AXIS = ["-y", "+y", "-x", "+x"];
- CaptionsRenderer = class {
- overlay;
- z;
- A = 0;
- C = "ltr";
- B = [];
- D = false;
- E;
- j = /* @__PURE__ */ new Map();
- l = /* @__PURE__ */ new Map();
- /* Text direction. */
- get dir() {
- return this.C;
- }
- set dir(dir) {
- this.C = dir;
- setDataAttr(this.overlay, "dir", dir);
- }
- get currentTime() {
- return this.A;
- }
- set currentTime(time) {
- this.A = time;
- this.update();
- }
- constructor(overlay, init) {
- this.overlay = overlay;
- this.dir = init?.dir ?? "ltr";
- overlay.setAttribute("translate", "yes");
- overlay.setAttribute("aria-live", "off");
- overlay.setAttribute("aria-atomic", "true");
- setPartAttr(overlay, "captions");
- this.G();
- this.E = new ResizeObserver(this.I.bind(this));
- this.E.observe(overlay);
- }
- changeTrack({ regions, cues }) {
- this.reset();
- this.J(regions);
- for (const cue of cues)
- this.l.set(cue, null);
- this.update();
- }
- addCue(cue) {
- this.l.set(cue, null);
- this.update();
- }
- removeCue(cue) {
- this.l.delete(cue);
- this.update();
- }
- update(forceUpdate = false) {
- this.H(forceUpdate);
- }
- reset() {
- this.l.clear();
- this.j.clear();
- this.B = [];
- this.overlay.textContent = "";
- }
- destroy() {
- this.reset();
- this.E.disconnect();
- }
- I() {
- this.D = true;
- this.K();
- }
- K = debounce2(() => {
- this.D = false;
- this.G();
- for (const el of this.j.values()) {
- el[STARTING_BOX] = null;
- }
- for (const el of this.l.values()) {
- if (el)
- el[STARTING_BOX] = null;
- }
- this.H(true);
- }, 50);
- G() {
- this.z = createBox(this.overlay);
- setCSSVar(this.overlay, "overlay-width", this.z.width + "px");
- setCSSVar(this.overlay, "overlay-height", this.z.height + "px");
- }
- H(forceUpdate = false) {
- if (!this.l.size || this.D)
- return;
- let cue, activeCues = [...this.l.keys()].filter((cue2) => this.A >= cue2.startTime && this.A <= cue2.endTime).sort(
- (cueA, cueB) => cueA.startTime !== cueB.startTime ? cueA.startTime - cueB.startTime : cueA.endTime - cueB.endTime
- ), activeRegions = activeCues.map((cue2) => cue2.region);
- for (let i4 = 0; i4 < this.B.length; i4++) {
- cue = this.B[i4];
- if (activeCues[i4] === cue)
- continue;
- if (cue.region && !activeRegions.includes(cue.region)) {
- const regionEl = this.j.get(cue.region.id);
- if (regionEl) {
- regionEl.removeAttribute("data-active");
- forceUpdate = true;
- }
- }
- const cueEl = this.l.get(cue);
- if (cueEl) {
- cueEl.remove();
- forceUpdate = true;
- }
- }
- for (let i4 = 0; i4 < activeCues.length; i4++) {
- cue = activeCues[i4];
- let cueEl = this.l.get(cue);
- if (!cueEl)
- this.l.set(cue, cueEl = this.L(cue));
- const regionEl = this.F(cue) && this.j.get(cue.region.id);
- if (regionEl && !regionEl.hasAttribute("data-active")) {
- requestAnimationFrame(() => setDataAttr(regionEl, "active"));
- forceUpdate = true;
- }
- if (!cueEl.isConnected) {
- (regionEl || this.overlay).append(cueEl);
- forceUpdate = true;
- }
- }
- if (forceUpdate) {
- const boxes = [], seen = /* @__PURE__ */ new Set();
- for (let i4 = activeCues.length - 1; i4 >= 0; i4--) {
- cue = activeCues[i4];
- if (seen.has(cue.region || cue))
- continue;
- const isRegion = this.F(cue), el = isRegion ? this.j.get(cue.region.id) : this.l.get(cue);
- if (isRegion) {
- boxes.push(positionRegion(this.z, cue.region, el, boxes));
- } else {
- boxes.push(positionCue(this.z, cue, el, boxes));
- }
- seen.add(isRegion ? cue.region : cue);
- }
- }
- updateTimedVTTCueNodes(this.overlay, this.A);
- this.B = activeCues;
- }
- J(regions) {
- if (!regions)
- return;
- for (const region of regions) {
- const el = this.M(region);
- this.j.set(region.id, el);
- this.overlay.append(el);
- }
- }
- M(region) {
- const el = document.createElement("div");
- setPartAttr(el, "region");
- setDataAttr(el, "id", region.id);
- setDataAttr(el, "scroll", region.scroll);
- setCSSVar(el, "region-width", region.width + "%");
- setCSSVar(el, "region-anchor-x", region.regionAnchorX);
- setCSSVar(el, "region-anchor-y", region.regionAnchorY);
- setCSSVar(el, "region-viewport-anchor-x", region.viewportAnchorX);
- setCSSVar(el, "region-viewport-anchor-y", region.viewportAnchorY);
- setCSSVar(el, "region-lines", region.lines);
- return el;
- }
- L(cue) {
- const display = document.createElement("div"), position = computeCuePosition(cue), positionAlignment = computeCuePositionAlignment(cue, this.C);
- setPartAttr(display, "cue-display");
- if (cue.vertical !== "")
- setDataAttr(display, "vertical");
- setCSSVar(display, "cue-text-align", cue.align);
- if (cue.style) {
- for (const prop2 of Object.keys(cue.style)) {
- display.style.setProperty(prop2, cue.style[prop2]);
- }
- }
- if (!this.F(cue)) {
- setCSSVar(
- display,
- "cue-writing-mode",
- cue.vertical === "" ? "horizontal-tb" : cue.vertical === "lr" ? "vertical-lr" : "vertical-rl"
- );
- if (!cue.style?.["--cue-width"]) {
- let maxSize = position;
- if (positionAlignment === "line-left") {
- maxSize = 100 - position;
- } else if (positionAlignment === "center" && position <= 50) {
- maxSize = position * 2;
- } else if (positionAlignment === "center" && position > 50) {
- maxSize = (100 - position) * 2;
- }
- const size2 = cue.size < maxSize ? cue.size : maxSize;
- if (cue.vertical === "")
- setCSSVar(display, "cue-width", size2 + "%");
- else
- setCSSVar(display, "cue-height", size2 + "%");
- }
- } else {
- setCSSVar(
- display,
- "cue-offset",
- `${position - (positionAlignment === "line-right" ? 100 : positionAlignment === "center" ? 50 : 0)}%`
- );
- }
- const el = document.createElement("div");
- setPartAttr(el, "cue");
- if (cue.id)
- setDataAttr(el, "id", cue.id);
- el.innerHTML = renderVTTCueString(cue);
- display.append(el);
- return display;
- }
- F(cue) {
- return cue.region && cue.size === 100 && cue.vertical === "" && cue.line === "auto";
- }
- };
- }
- });
-
- // node_modules/media-captions/dist/prod.js
- var prod_exports = {};
- __export(prod_exports, {
- CaptionsRenderer: () => CaptionsRenderer,
- ParseError: () => ParseError,
- ParseErrorCode: () => ParseErrorCode,
- TextCue: () => TextCue,
- VTTCue: () => VTTCue,
- VTTRegion: () => VTTRegion,
- createVTTCueTemplate: () => createVTTCueTemplate,
- parseByteStream: () => parseByteStream,
- parseResponse: () => parseResponse,
- parseText: () => parseText,
- parseTextStream: () => parseTextStream,
- parseVTTTimestamp: () => parseVTTTimestamp,
- renderVTTCueString: () => renderVTTCueString,
- renderVTTTokensString: () => renderVTTTokensString,
- tokenizeVTTCue: () => tokenizeVTTCue,
- updateTimedVTTCueNodes: () => updateTimedVTTCueNodes
- });
- var init_prod2 = __esm({
- "node_modules/media-captions/dist/prod.js"() {
- init_prod();
- }
- });
-
- // node_modules/vidstack/prod/chunks/vidstack-CP9ACpRU.js
- function isTrackCaptionKind(track) {
- return captionRE.test(track.kind);
- }
- function parseJSONCaptionsFile(json, Cue, Region) {
- const content = isString(json) ? JSON.parse(json) : json;
- let regions = [], cues = [];
- if (content.regions && Region) {
- regions = content.regions.map((region) => Object.assign(new Region(), region));
- }
- if (content.cues || isArray(content)) {
- cues = (isArray(content) ? content : content.cues).filter((content2) => isNumber(content2.startTime) && isNumber(content2.endTime)).map((cue) => Object.assign(new Cue(0, 0, ""), cue));
- }
- return { regions, cues };
- }
- var CROSS_ORIGIN, READY_STATE, UPDATE_ACTIVE_CUES, CAN_LOAD, ON_MODE_CHANGE, NATIVE, NATIVE_HLS, TextTrackSymbol, TextTrack, captionRE;
- var init_vidstack_CP9ACpRU = __esm({
- "node_modules/vidstack/prod/chunks/vidstack-CP9ACpRU.js"() {
- init_vidstack_CRlI3Mh7();
- init_vidstack_A9j_j6J();
- init_vidstack_lwuXewh7();
- CROSS_ORIGIN = Symbol(0);
- READY_STATE = Symbol(0);
- UPDATE_ACTIVE_CUES = Symbol(0);
- CAN_LOAD = Symbol(0);
- ON_MODE_CHANGE = Symbol(0);
- NATIVE = Symbol(0);
- NATIVE_HLS = Symbol(0);
- TextTrackSymbol = {
- crossOrigin: CROSS_ORIGIN,
- readyState: READY_STATE,
- updateActiveCues: UPDATE_ACTIVE_CUES,
- canLoad: CAN_LOAD,
- onModeChange: ON_MODE_CHANGE,
- native: NATIVE,
- nativeHLS: NATIVE_HLS
- };
- TextTrack = class extends EventsTarget {
- static createId(track) {
- return `vds-${track.type}-${track.kind}-${track.src ?? track.label ?? "?"}`;
- }
- src;
- content;
- type;
- encoding;
- id = "";
- label = "";
- language = "";
- kind;
- default = false;
- #canLoad = false;
- #currentTime = 0;
- #mode = "disabled";
- #metadata = {};
- #regions = [];
- #cues = [];
- #activeCues = [];
- /** @internal */
- [TextTrackSymbol.readyState] = 0;
- /** @internal */
- [TextTrackSymbol.crossOrigin];
- /** @internal */
- [TextTrackSymbol.onModeChange] = null;
- /** @internal */
- [TextTrackSymbol.native] = null;
- get metadata() {
- return this.#metadata;
- }
- get regions() {
- return this.#regions;
- }
- get cues() {
- return this.#cues;
- }
- get activeCues() {
- return this.#activeCues;
- }
- /**
- * - 0: Not Loading
- * - 1: Loading
- * - 2: Ready
- * - 3: Error
- */
- get readyState() {
- return this[TextTrackSymbol.readyState];
- }
- get mode() {
- return this.#mode;
- }
- set mode(mode) {
- this.setMode(mode);
- }
- constructor(init) {
- super();
- for (const prop2 of Object.keys(init)) this[prop2] = init[prop2];
- if (!this.type) this.type = "vtt";
- if (init.content) {
- this.#parseContent(init);
- } else if (!init.src) {
- this[TextTrackSymbol.readyState] = 2;
- }
- }
- addCue(cue, trigger) {
- let i4 = 0, length = this.#cues.length;
- for (i4 = 0; i4 < length; i4++) if (cue.endTime <= this.#cues[i4].startTime) break;
- if (i4 === length) this.#cues.push(cue);
- else this.#cues.splice(i4, 0, cue);
- if (!(cue instanceof TextTrackCue)) {
- this[TextTrackSymbol.native]?.track.addCue(cue);
- }
- this.dispatchEvent(new DOMEvent("add-cue", { detail: cue, trigger }));
- if (isCueActive(cue, this.#currentTime)) {
- this[TextTrackSymbol.updateActiveCues](this.#currentTime, trigger);
- }
- }
- removeCue(cue, trigger) {
- const index = this.#cues.indexOf(cue);
- if (index >= 0) {
- const isActive = this.#activeCues.includes(cue);
- this.#cues.splice(index, 1);
- this[TextTrackSymbol.native]?.track.removeCue(cue);
- this.dispatchEvent(new DOMEvent("remove-cue", { detail: cue, trigger }));
- if (isActive) {
- this[TextTrackSymbol.updateActiveCues](this.#currentTime, trigger);
- }
- }
- }
- setMode(mode, trigger) {
- if (this.#mode === mode) return;
- this.#mode = mode;
- if (mode === "disabled") {
- this.#activeCues = [];
- this.#activeCuesChanged();
- } else if (this.readyState === 2) {
- this[TextTrackSymbol.updateActiveCues](this.#currentTime, trigger);
- } else {
- this.#load();
- }
- this.dispatchEvent(new DOMEvent("mode-change", { detail: this, trigger }));
- this[TextTrackSymbol.onModeChange]?.();
- }
- /** @internal */
- [TextTrackSymbol.updateActiveCues](currentTime, trigger) {
- this.#currentTime = currentTime;
- if (this.mode === "disabled" || !this.#cues.length) return;
- const activeCues = [];
- for (let i4 = 0, length = this.#cues.length; i4 < length; i4++) {
- const cue = this.#cues[i4];
- if (isCueActive(cue, currentTime)) activeCues.push(cue);
- }
- let changed = activeCues.length !== this.#activeCues.length;
- if (!changed) {
- for (let i4 = 0; i4 < activeCues.length; i4++) {
- if (!this.#activeCues.includes(activeCues[i4])) {
- changed = true;
- break;
- }
- }
- }
- this.#activeCues = activeCues;
- if (changed) this.#activeCuesChanged(trigger);
- }
- /** @internal */
- [TextTrackSymbol.canLoad]() {
- this.#canLoad = true;
- if (this.#mode !== "disabled") this.#load();
- }
- #parseContent(init) {
- Promise.resolve().then(() => (init_prod2(), prod_exports)).then(({ parseText: parseText2, VTTCue: VTTCue2, VTTRegion: VTTRegion2 }) => {
- if (!isString(init.content) || init.type === "json") {
- this.#parseJSON(init.content, VTTCue2, VTTRegion2);
- if (this.readyState !== 3) this.#ready();
- } else {
- parseText2(init.content, { type: init.type }).then(({ cues, regions }) => {
- this.#cues = cues;
- this.#regions = regions;
- this.#ready();
- });
- }
- });
- }
- async #load() {
- if (!this.#canLoad || this[TextTrackSymbol.readyState] > 0) return;
- this[TextTrackSymbol.readyState] = 1;
- this.dispatchEvent(new DOMEvent("load-start"));
- if (!this.src) {
- this.#ready();
- return;
- }
- try {
- const { parseResponse: parseResponse2, VTTCue: VTTCue2, VTTRegion: VTTRegion2 } = await Promise.resolve().then(() => (init_prod2(), prod_exports)), crossOrigin = this[TextTrackSymbol.crossOrigin]?.();
- const response = fetch(this.src, {
- headers: this.type === "json" ? { "Content-Type": "application/json" } : void 0,
- credentials: getRequestCredentials(crossOrigin)
- });
- if (this.type === "json") {
- this.#parseJSON(await (await response).text(), VTTCue2, VTTRegion2);
- } else {
- const { errors, metadata, regions, cues } = await parseResponse2(response, {
- type: this.type,
- encoding: this.encoding
- });
- if (errors[0]?.code === 0) {
- throw errors[0];
- } else {
- this.#metadata = metadata;
- this.#regions = regions;
- this.#cues = cues;
- }
- }
- this.#ready();
- } catch (error) {
- this.#error(error);
- }
- }
- #ready() {
- this[TextTrackSymbol.readyState] = 2;
- if (!this.src || this.type !== "vtt") {
- const native = this[TextTrackSymbol.native];
- if (native && !native.managed) {
- for (const cue of this.#cues) native.track.addCue(cue);
- }
- }
- const loadEvent = new DOMEvent("load");
- this[TextTrackSymbol.updateActiveCues](this.#currentTime, loadEvent);
- this.dispatchEvent(loadEvent);
- }
- #error(error) {
- this[TextTrackSymbol.readyState] = 3;
- this.dispatchEvent(new DOMEvent("error", { detail: error }));
- }
- #parseJSON(json, VTTCue2, VTTRegion2) {
- try {
- const { regions, cues } = parseJSONCaptionsFile(json, VTTCue2, VTTRegion2);
- this.#regions = regions;
- this.#cues = cues;
- } catch (error) {
- this.#error(error);
- }
- }
- #activeCuesChanged(trigger) {
- this.dispatchEvent(new DOMEvent("cue-change", { trigger }));
- }
- };
- captionRE = /captions|subtitles/;
- }
- });
-
- // node_modules/vidstack/prod/chunks/vidstack-D5EzK014.js
- var ADD, REMOVE, RESET, SELECT, READONLY, SET_READONLY, ON_RESET, ON_REMOVE, ON_USER_SELECT, ListSymbol;
- var init_vidstack_D5EzK014 = __esm({
- "node_modules/vidstack/prod/chunks/vidstack-D5EzK014.js"() {
- ADD = Symbol(0);
- REMOVE = Symbol(0);
- RESET = Symbol(0);
- SELECT = Symbol(0);
- READONLY = Symbol(0);
- SET_READONLY = Symbol(0);
- ON_RESET = Symbol(0);
- ON_REMOVE = Symbol(0);
- ON_USER_SELECT = Symbol(0);
- ListSymbol = {
- add: ADD,
- remove: REMOVE,
- reset: RESET,
- select: SELECT,
- readonly: READONLY,
- setReadonly: SET_READONLY,
- onReset: ON_RESET,
- onRemove: ON_REMOVE,
- onUserSelect: ON_USER_SELECT
- };
- }
- });
-
- // node_modules/vidstack/prod/chunks/vidstack-B01xzxC4.js
- var SET_AUTO, ENABLE_AUTO, QualitySymbol;
- var init_vidstack_B01xzxC4 = __esm({
- "node_modules/vidstack/prod/chunks/vidstack-B01xzxC4.js"() {
- SET_AUTO = Symbol(0);
- ENABLE_AUTO = Symbol(0);
- QualitySymbol = {
- setAuto: SET_AUTO,
- enableAuto: ENABLE_AUTO
- };
- }
- });
-
- // node_modules/vidstack/prod/chunks/vidstack-C9vIqaYT.js
- function coerceToError(error) {
- return error instanceof Error ? error : Error(typeof error === "string" ? error : JSON.stringify(error));
- }
- function assert(condition, message) {
- if (!condition) {
- throw Error("Assertion failed.");
- }
- }
- var init_vidstack_C9vIqaYT = __esm({
- "node_modules/vidstack/prod/chunks/vidstack-C9vIqaYT.js"() {
- }
- });
-
- // node_modules/@floating-ui/utils/dist/floating-ui.utils.mjs
- function clamp(start, value, end) {
- return max(start, min(value, end));
- }
- function evaluate(value, param) {
- return typeof value === "function" ? value(param) : value;
- }
- function getSide(placement) {
- return placement.split("-")[0];
- }
- function getAlignment(placement) {
- return placement.split("-")[1];
- }
- function getOppositeAxis(axis) {
- return axis === "x" ? "y" : "x";
- }
- function getAxisLength(axis) {
- return axis === "y" ? "height" : "width";
- }
- function getSideAxis(placement) {
- return ["top", "bottom"].includes(getSide(placement)) ? "y" : "x";
- }
- function getAlignmentAxis(placement) {
- return getOppositeAxis(getSideAxis(placement));
- }
- function getAlignmentSides(placement, rects, rtl) {
- if (rtl === void 0) {
- rtl = false;
- }
- const alignment = getAlignment(placement);
- const alignmentAxis = getAlignmentAxis(placement);
- const length = getAxisLength(alignmentAxis);
- let mainAlignmentSide = alignmentAxis === "x" ? alignment === (rtl ? "end" : "start") ? "right" : "left" : alignment === "start" ? "bottom" : "top";
- if (rects.reference[length] > rects.floating[length]) {
- mainAlignmentSide = getOppositePlacement(mainAlignmentSide);
- }
- return [mainAlignmentSide, getOppositePlacement(mainAlignmentSide)];
- }
- function getExpandedPlacements(placement) {
- const oppositePlacement = getOppositePlacement(placement);
- return [getOppositeAlignmentPlacement(placement), oppositePlacement, getOppositeAlignmentPlacement(oppositePlacement)];
- }
- function getOppositeAlignmentPlacement(placement) {
- return placement.replace(/start|end/g, (alignment) => oppositeAlignmentMap[alignment]);
- }
- function getSideList(side, isStart, rtl) {
- const lr = ["left", "right"];
- const rl = ["right", "left"];
- const tb = ["top", "bottom"];
- const bt = ["bottom", "top"];
- switch (side) {
- case "top":
- case "bottom":
- if (rtl) return isStart ? rl : lr;
- return isStart ? lr : rl;
- case "left":
- case "right":
- return isStart ? tb : bt;
- default:
- return [];
- }
- }
- function getOppositeAxisPlacements(placement, flipAlignment, direction, rtl) {
- const alignment = getAlignment(placement);
- let list = getSideList(getSide(placement), direction === "start", rtl);
- if (alignment) {
- list = list.map((side) => side + "-" + alignment);
- if (flipAlignment) {
- list = list.concat(list.map(getOppositeAlignmentPlacement));
- }
- }
- return list;
- }
- function getOppositePlacement(placement) {
- return placement.replace(/left|right|bottom|top/g, (side) => oppositeSideMap[side]);
- }
- function expandPaddingObject(padding) {
- return {
- top: 0,
- right: 0,
- bottom: 0,
- left: 0,
- ...padding
- };
- }
- function getPaddingObject(padding) {
- return typeof padding !== "number" ? expandPaddingObject(padding) : {
- top: padding,
- right: padding,
- bottom: padding,
- left: padding
- };
- }
- function rectToClientRect(rect) {
- const {
- x: x2,
- y: y2,
- width,
- height
- } = rect;
- return {
- width,
- height,
- top: y2,
- left: x2,
- right: x2 + width,
- bottom: y2 + height,
- x: x2,
- y: y2
- };
- }
- var min, max, round, floor, createCoords, oppositeSideMap, oppositeAlignmentMap;
- var init_floating_ui_utils = __esm({
- "node_modules/@floating-ui/utils/dist/floating-ui.utils.mjs"() {
- min = Math.min;
- max = Math.max;
- round = Math.round;
- floor = Math.floor;
- createCoords = (v2) => ({
- x: v2,
- y: v2
- });
- oppositeSideMap = {
- left: "right",
- right: "left",
- bottom: "top",
- top: "bottom"
- };
- oppositeAlignmentMap = {
- start: "end",
- end: "start"
- };
- }
- });
-
- // node_modules/@floating-ui/core/dist/floating-ui.core.mjs
- function computeCoordsFromPlacement(_ref, placement, rtl) {
- let {
- reference,
- floating
- } = _ref;
- const sideAxis = getSideAxis(placement);
- const alignmentAxis = getAlignmentAxis(placement);
- const alignLength = getAxisLength(alignmentAxis);
- const side = getSide(placement);
- const isVertical = sideAxis === "y";
- const commonX = reference.x + reference.width / 2 - floating.width / 2;
- const commonY = reference.y + reference.height / 2 - floating.height / 2;
- const commonAlign = reference[alignLength] / 2 - floating[alignLength] / 2;
- let coords;
- switch (side) {
- case "top":
- coords = {
- x: commonX,
- y: reference.y - floating.height
- };
- break;
- case "bottom":
- coords = {
- x: commonX,
- y: reference.y + reference.height
- };
- break;
- case "right":
- coords = {
- x: reference.x + reference.width,
- y: commonY
- };
- break;
- case "left":
- coords = {
- x: reference.x - floating.width,
- y: commonY
- };
- break;
- default:
- coords = {
- x: reference.x,
- y: reference.y
- };
- }
- switch (getAlignment(placement)) {
- case "start":
- coords[alignmentAxis] -= commonAlign * (rtl && isVertical ? -1 : 1);
- break;
- case "end":
- coords[alignmentAxis] += commonAlign * (rtl && isVertical ? -1 : 1);
- break;
- }
- return coords;
- }
- async function detectOverflow(state, options) {
- var _await$platform$isEle;
- if (options === void 0) {
- options = {};
- }
- const {
- x: x2,
- y: y2,
- platform: platform2,
- rects,
- elements,
- strategy
- } = state;
- const {
- boundary = "clippingAncestors",
- rootBoundary = "viewport",
- elementContext = "floating",
- altBoundary = false,
- padding = 0
- } = evaluate(options, state);
- const paddingObject = getPaddingObject(padding);
- const altContext = elementContext === "floating" ? "reference" : "floating";
- const element = elements[altBoundary ? altContext : elementContext];
- const clippingClientRect = rectToClientRect(await platform2.getClippingRect({
- element: ((_await$platform$isEle = await (platform2.isElement == null ? void 0 : platform2.isElement(element))) != null ? _await$platform$isEle : true) ? element : element.contextElement || await (platform2.getDocumentElement == null ? void 0 : platform2.getDocumentElement(elements.floating)),
- boundary,
- rootBoundary,
- strategy
- }));
- const rect = elementContext === "floating" ? {
- x: x2,
- y: y2,
- width: rects.floating.width,
- height: rects.floating.height
- } : rects.reference;
- const offsetParent = await (platform2.getOffsetParent == null ? void 0 : platform2.getOffsetParent(elements.floating));
- const offsetScale = await (platform2.isElement == null ? void 0 : platform2.isElement(offsetParent)) ? await (platform2.getScale == null ? void 0 : platform2.getScale(offsetParent)) || {
- x: 1,
- y: 1
- } : {
- x: 1,
- y: 1
- };
- const elementClientRect = rectToClientRect(platform2.convertOffsetParentRelativeRectToViewportRelativeRect ? await platform2.convertOffsetParentRelativeRectToViewportRelativeRect({
- elements,
- rect,
- offsetParent,
- strategy
- }) : rect);
- return {
- top: (clippingClientRect.top - elementClientRect.top + paddingObject.top) / offsetScale.y,
- bottom: (elementClientRect.bottom - clippingClientRect.bottom + paddingObject.bottom) / offsetScale.y,
- left: (clippingClientRect.left - elementClientRect.left + paddingObject.left) / offsetScale.x,
- right: (elementClientRect.right - clippingClientRect.right + paddingObject.right) / offsetScale.x
- };
- }
- var computePosition, flip, shift;
- var init_floating_ui_core = __esm({
- "node_modules/@floating-ui/core/dist/floating-ui.core.mjs"() {
- init_floating_ui_utils();
- init_floating_ui_utils();
- computePosition = async (reference, floating, config) => {
- const {
- placement = "bottom",
- strategy = "absolute",
- middleware = [],
- platform: platform2
- } = config;
- const validMiddleware = middleware.filter(Boolean);
- const rtl = await (platform2.isRTL == null ? void 0 : platform2.isRTL(floating));
- let rects = await platform2.getElementRects({
- reference,
- floating,
- strategy
- });
- let {
- x: x2,
- y: y2
- } = computeCoordsFromPlacement(rects, placement, rtl);
- let statefulPlacement = placement;
- let middlewareData = {};
- let resetCount = 0;
- for (let i4 = 0; i4 < validMiddleware.length; i4++) {
- const {
- name,
- fn
- } = validMiddleware[i4];
- const {
- x: nextX,
- y: nextY,
- data,
- reset
- } = await fn({
- x: x2,
- y: y2,
- initialPlacement: placement,
- placement: statefulPlacement,
- strategy,
- middlewareData,
- rects,
- platform: platform2,
- elements: {
- reference,
- floating
- }
- });
- x2 = nextX != null ? nextX : x2;
- y2 = nextY != null ? nextY : y2;
- middlewareData = {
- ...middlewareData,
- [name]: {
- ...middlewareData[name],
- ...data
- }
- };
- if (reset && resetCount <= 50) {
- resetCount++;
- if (typeof reset === "object") {
- if (reset.placement) {
- statefulPlacement = reset.placement;
- }
- if (reset.rects) {
- rects = reset.rects === true ? await platform2.getElementRects({
- reference,
- floating,
- strategy
- }) : reset.rects;
- }
- ({
- x: x2,
- y: y2
- } = computeCoordsFromPlacement(rects, statefulPlacement, rtl));
- }
- i4 = -1;
- }
- }
- return {
- x: x2,
- y: y2,
- placement: statefulPlacement,
- strategy,
- middlewareData
- };
- };
- flip = function(options) {
- if (options === void 0) {
- options = {};
- }
- return {
- name: "flip",
- options,
- async fn(state) {
- var _middlewareData$arrow, _middlewareData$flip;
- const {
- placement,
- middlewareData,
- rects,
- initialPlacement,
- platform: platform2,
- elements
- } = state;
- const {
- mainAxis: checkMainAxis = true,
- crossAxis: checkCrossAxis = true,
- fallbackPlacements: specifiedFallbackPlacements,
- fallbackStrategy = "bestFit",
- fallbackAxisSideDirection = "none",
- flipAlignment = true,
- ...detectOverflowOptions
- } = evaluate(options, state);
- if ((_middlewareData$arrow = middlewareData.arrow) != null && _middlewareData$arrow.alignmentOffset) {
- return {};
- }
- const side = getSide(placement);
- const initialSideAxis = getSideAxis(initialPlacement);
- const isBasePlacement = getSide(initialPlacement) === initialPlacement;
- const rtl = await (platform2.isRTL == null ? void 0 : platform2.isRTL(elements.floating));
- const fallbackPlacements = specifiedFallbackPlacements || (isBasePlacement || !flipAlignment ? [getOppositePlacement(initialPlacement)] : getExpandedPlacements(initialPlacement));
- const hasFallbackAxisSideDirection = fallbackAxisSideDirection !== "none";
- if (!specifiedFallbackPlacements && hasFallbackAxisSideDirection) {
- fallbackPlacements.push(...getOppositeAxisPlacements(initialPlacement, flipAlignment, fallbackAxisSideDirection, rtl));
- }
- const placements2 = [initialPlacement, ...fallbackPlacements];
- const overflow = await detectOverflow(state, detectOverflowOptions);
- const overflows = [];
- let overflowsData = ((_middlewareData$flip = middlewareData.flip) == null ? void 0 : _middlewareData$flip.overflows) || [];
- if (checkMainAxis) {
- overflows.push(overflow[side]);
- }
- if (checkCrossAxis) {
- const sides2 = getAlignmentSides(placement, rects, rtl);
- overflows.push(overflow[sides2[0]], overflow[sides2[1]]);
- }
- overflowsData = [...overflowsData, {
- placement,
- overflows
- }];
- if (!overflows.every((side2) => side2 <= 0)) {
- var _middlewareData$flip2, _overflowsData$filter;
- const nextIndex = (((_middlewareData$flip2 = middlewareData.flip) == null ? void 0 : _middlewareData$flip2.index) || 0) + 1;
- const nextPlacement = placements2[nextIndex];
- if (nextPlacement) {
- return {
- data: {
- index: nextIndex,
- overflows: overflowsData
- },
- reset: {
- placement: nextPlacement
- }
- };
- }
- let resetPlacement = (_overflowsData$filter = overflowsData.filter((d2) => d2.overflows[0] <= 0).sort((a3, b2) => a3.overflows[1] - b2.overflows[1])[0]) == null ? void 0 : _overflowsData$filter.placement;
- if (!resetPlacement) {
- switch (fallbackStrategy) {
- case "bestFit": {
- var _overflowsData$filter2;
- const placement2 = (_overflowsData$filter2 = overflowsData.filter((d2) => {
- if (hasFallbackAxisSideDirection) {
- const currentSideAxis = getSideAxis(d2.placement);
- return currentSideAxis === initialSideAxis || // Create a bias to the `y` side axis due to horizontal
- // reading directions favoring greater width.
- currentSideAxis === "y";
- }
- return true;
- }).map((d2) => [d2.placement, d2.overflows.filter((overflow2) => overflow2 > 0).reduce((acc, overflow2) => acc + overflow2, 0)]).sort((a3, b2) => a3[1] - b2[1])[0]) == null ? void 0 : _overflowsData$filter2[0];
- if (placement2) {
- resetPlacement = placement2;
- }
- break;
- }
- case "initialPlacement":
- resetPlacement = initialPlacement;
- break;
- }
- }
- if (placement !== resetPlacement) {
- return {
- reset: {
- placement: resetPlacement
- }
- };
- }
- }
- return {};
- }
- };
- };
- shift = function(options) {
- if (options === void 0) {
- options = {};
- }
- return {
- name: "shift",
- options,
- async fn(state) {
- const {
- x: x2,
- y: y2,
- placement
- } = state;
- const {
- mainAxis: checkMainAxis = true,
- crossAxis: checkCrossAxis = false,
- limiter = {
- fn: (_ref) => {
- let {
- x: x3,
- y: y3
- } = _ref;
- return {
- x: x3,
- y: y3
- };
- }
- },
- ...detectOverflowOptions
- } = evaluate(options, state);
- const coords = {
- x: x2,
- y: y2
- };
- const overflow = await detectOverflow(state, detectOverflowOptions);
- const crossAxis = getSideAxis(getSide(placement));
- const mainAxis = getOppositeAxis(crossAxis);
- let mainAxisCoord = coords[mainAxis];
- let crossAxisCoord = coords[crossAxis];
- if (checkMainAxis) {
- const minSide = mainAxis === "y" ? "top" : "left";
- const maxSide = mainAxis === "y" ? "bottom" : "right";
- const min2 = mainAxisCoord + overflow[minSide];
- const max2 = mainAxisCoord - overflow[maxSide];
- mainAxisCoord = clamp(min2, mainAxisCoord, max2);
- }
- if (checkCrossAxis) {
- const minSide = crossAxis === "y" ? "top" : "left";
- const maxSide = crossAxis === "y" ? "bottom" : "right";
- const min2 = crossAxisCoord + overflow[minSide];
- const max2 = crossAxisCoord - overflow[maxSide];
- crossAxisCoord = clamp(min2, crossAxisCoord, max2);
- }
- const limitedCoords = limiter.fn({
- ...state,
- [mainAxis]: mainAxisCoord,
- [crossAxis]: crossAxisCoord
- });
- return {
- ...limitedCoords,
- data: {
- x: limitedCoords.x - x2,
- y: limitedCoords.y - y2,
- enabled: {
- [mainAxis]: checkMainAxis,
- [crossAxis]: checkCrossAxis
- }
- }
- };
- }
- };
- };
- }
- });
-
- // node_modules/@floating-ui/utils/dist/floating-ui.utils.dom.mjs
- function hasWindow() {
- return typeof window !== "undefined";
- }
- function getNodeName(node) {
- if (isNode(node)) {
- return (node.nodeName || "").toLowerCase();
- }
- return "#document";
- }
- function getWindow(node) {
- var _node$ownerDocument;
- return (node == null || (_node$ownerDocument = node.ownerDocument) == null ? void 0 : _node$ownerDocument.defaultView) || window;
- }
- function getDocumentElement(node) {
- var _ref;
- return (_ref = (isNode(node) ? node.ownerDocument : node.document) || window.document) == null ? void 0 : _ref.documentElement;
- }
- function isNode(value) {
- if (!hasWindow()) {
- return false;
- }
- return value instanceof Node || value instanceof getWindow(value).Node;
- }
- function isElement(value) {
- if (!hasWindow()) {
- return false;
- }
- return value instanceof Element || value instanceof getWindow(value).Element;
- }
- function isHTMLElement(value) {
- if (!hasWindow()) {
- return false;
- }
- return value instanceof HTMLElement || value instanceof getWindow(value).HTMLElement;
- }
- function isShadowRoot(value) {
- if (!hasWindow() || typeof ShadowRoot === "undefined") {
- return false;
- }
- return value instanceof ShadowRoot || value instanceof getWindow(value).ShadowRoot;
- }
- function isOverflowElement(element) {
- const {
- overflow,
- overflowX,
- overflowY,
- display
- } = getComputedStyle2(element);
- return /auto|scroll|overlay|hidden|clip/.test(overflow + overflowY + overflowX) && !["inline", "contents"].includes(display);
- }
- function isTableElement(element) {
- return ["table", "td", "th"].includes(getNodeName(element));
- }
- function isTopLayer(element) {
- return [":popover-open", ":modal"].some((selector) => {
- try {
- return element.matches(selector);
- } catch (e6) {
- return false;
- }
- });
- }
- function isContainingBlock(elementOrCss) {
- const webkit2 = isWebKit();
- const css = isElement(elementOrCss) ? getComputedStyle2(elementOrCss) : elementOrCss;
- return css.transform !== "none" || css.perspective !== "none" || (css.containerType ? css.containerType !== "normal" : false) || !webkit2 && (css.backdropFilter ? css.backdropFilter !== "none" : false) || !webkit2 && (css.filter ? css.filter !== "none" : false) || ["transform", "perspective", "filter"].some((value) => (css.willChange || "").includes(value)) || ["paint", "layout", "strict", "content"].some((value) => (css.contain || "").includes(value));
- }
- function getContainingBlock(element) {
- let currentNode = getParentNode(element);
- while (isHTMLElement(currentNode) && !isLastTraversableNode(currentNode)) {
- if (isContainingBlock(currentNode)) {
- return currentNode;
- } else if (isTopLayer(currentNode)) {
- return null;
- }
- currentNode = getParentNode(currentNode);
- }
- return null;
- }
- function isWebKit() {
- if (typeof CSS === "undefined" || !CSS.supports) return false;
- return CSS.supports("-webkit-backdrop-filter", "none");
- }
- function isLastTraversableNode(node) {
- return ["html", "body", "#document"].includes(getNodeName(node));
- }
- function getComputedStyle2(element) {
- return getWindow(element).getComputedStyle(element);
- }
- function getNodeScroll(element) {
- if (isElement(element)) {
- return {
- scrollLeft: element.scrollLeft,
- scrollTop: element.scrollTop
- };
- }
- return {
- scrollLeft: element.scrollX,
- scrollTop: element.scrollY
- };
- }
- function getParentNode(node) {
- if (getNodeName(node) === "html") {
- return node;
- }
- const result = (
- // Step into the shadow DOM of the parent of a slotted node.
- node.assignedSlot || // DOM Element detected.
- node.parentNode || // ShadowRoot detected.
- isShadowRoot(node) && node.host || // Fallback.
- getDocumentElement(node)
- );
- return isShadowRoot(result) ? result.host : result;
- }
- function getNearestOverflowAncestor(node) {
- const parentNode = getParentNode(node);
- if (isLastTraversableNode(parentNode)) {
- return node.ownerDocument ? node.ownerDocument.body : node.body;
- }
- if (isHTMLElement(parentNode) && isOverflowElement(parentNode)) {
- return parentNode;
- }
- return getNearestOverflowAncestor(parentNode);
- }
- function getOverflowAncestors(node, list, traverseIframes) {
- var _node$ownerDocument2;
- if (list === void 0) {
- list = [];
- }
- if (traverseIframes === void 0) {
- traverseIframes = true;
- }
- const scrollableAncestor = getNearestOverflowAncestor(node);
- const isBody = scrollableAncestor === ((_node$ownerDocument2 = node.ownerDocument) == null ? void 0 : _node$ownerDocument2.body);
- const win = getWindow(scrollableAncestor);
- if (isBody) {
- const frameElement = getFrameElement(win);
- return list.concat(win, win.visualViewport || [], isOverflowElement(scrollableAncestor) ? scrollableAncestor : [], frameElement && traverseIframes ? getOverflowAncestors(frameElement) : []);
- }
- return list.concat(scrollableAncestor, getOverflowAncestors(scrollableAncestor, [], traverseIframes));
- }
- function getFrameElement(win) {
- return win.parent && Object.getPrototypeOf(win.parent) ? win.frameElement : null;
- }
- var init_floating_ui_utils_dom = __esm({
- "node_modules/@floating-ui/utils/dist/floating-ui.utils.dom.mjs"() {
- }
- });
-
- // node_modules/@floating-ui/dom/dist/floating-ui.dom.mjs
- function getCssDimensions(element) {
- const css = getComputedStyle2(element);
- let width = parseFloat(css.width) || 0;
- let height = parseFloat(css.height) || 0;
- const hasOffset = isHTMLElement(element);
- const offsetWidth = hasOffset ? element.offsetWidth : width;
- const offsetHeight = hasOffset ? element.offsetHeight : height;
- const shouldFallback = round(width) !== offsetWidth || round(height) !== offsetHeight;
- if (shouldFallback) {
- width = offsetWidth;
- height = offsetHeight;
- }
- return {
- width,
- height,
- $: shouldFallback
- };
- }
- function unwrapElement(element) {
- return !isElement(element) ? element.contextElement : element;
- }
- function getScale(element) {
- const domElement = unwrapElement(element);
- if (!isHTMLElement(domElement)) {
- return createCoords(1);
- }
- const rect = domElement.getBoundingClientRect();
- const {
- width,
- height,
- $: $2
- } = getCssDimensions(domElement);
- let x2 = ($2 ? round(rect.width) : rect.width) / width;
- let y2 = ($2 ? round(rect.height) : rect.height) / height;
- if (!x2 || !Number.isFinite(x2)) {
- x2 = 1;
- }
- if (!y2 || !Number.isFinite(y2)) {
- y2 = 1;
- }
- return {
- x: x2,
- y: y2
- };
- }
- function getVisualOffsets(element) {
- const win = getWindow(element);
- if (!isWebKit() || !win.visualViewport) {
- return noOffsets;
- }
- return {
- x: win.visualViewport.offsetLeft,
- y: win.visualViewport.offsetTop
- };
- }
- function shouldAddVisualOffsets(element, isFixed, floatingOffsetParent) {
- if (isFixed === void 0) {
- isFixed = false;
- }
- if (!floatingOffsetParent || isFixed && floatingOffsetParent !== getWindow(element)) {
- return false;
- }
- return isFixed;
- }
- function getBoundingClientRect(element, includeScale, isFixedStrategy, offsetParent) {
- if (includeScale === void 0) {
- includeScale = false;
- }
- if (isFixedStrategy === void 0) {
- isFixedStrategy = false;
- }
- const clientRect = element.getBoundingClientRect();
- const domElement = unwrapElement(element);
- let scale = createCoords(1);
- if (includeScale) {
- if (offsetParent) {
- if (isElement(offsetParent)) {
- scale = getScale(offsetParent);
- }
- } else {
- scale = getScale(element);
- }
- }
- const visualOffsets = shouldAddVisualOffsets(domElement, isFixedStrategy, offsetParent) ? getVisualOffsets(domElement) : createCoords(0);
- let x2 = (clientRect.left + visualOffsets.x) / scale.x;
- let y2 = (clientRect.top + visualOffsets.y) / scale.y;
- let width = clientRect.width / scale.x;
- let height = clientRect.height / scale.y;
- if (domElement) {
- const win = getWindow(domElement);
- const offsetWin = offsetParent && isElement(offsetParent) ? getWindow(offsetParent) : offsetParent;
- let currentWin = win;
- let currentIFrame = getFrameElement(currentWin);
- while (currentIFrame && offsetParent && offsetWin !== currentWin) {
- const iframeScale = getScale(currentIFrame);
- const iframeRect = currentIFrame.getBoundingClientRect();
- const css = getComputedStyle2(currentIFrame);
- const left = iframeRect.left + (currentIFrame.clientLeft + parseFloat(css.paddingLeft)) * iframeScale.x;
- const top = iframeRect.top + (currentIFrame.clientTop + parseFloat(css.paddingTop)) * iframeScale.y;
- x2 *= iframeScale.x;
- y2 *= iframeScale.y;
- width *= iframeScale.x;
- height *= iframeScale.y;
- x2 += left;
- y2 += top;
- currentWin = getWindow(currentIFrame);
- currentIFrame = getFrameElement(currentWin);
- }
- }
- return rectToClientRect({
- width,
- height,
- x: x2,
- y: y2
- });
- }
- function getWindowScrollBarX(element, rect) {
- const leftScroll = getNodeScroll(element).scrollLeft;
- if (!rect) {
- return getBoundingClientRect(getDocumentElement(element)).left + leftScroll;
- }
- return rect.left + leftScroll;
- }
- function getHTMLOffset(documentElement, scroll, ignoreScrollbarX) {
- if (ignoreScrollbarX === void 0) {
- ignoreScrollbarX = false;
- }
- const htmlRect = documentElement.getBoundingClientRect();
- const x2 = htmlRect.left + scroll.scrollLeft - (ignoreScrollbarX ? 0 : (
- // RTL scrollbar.
- getWindowScrollBarX(documentElement, htmlRect)
- ));
- const y2 = htmlRect.top + scroll.scrollTop;
- return {
- x: x2,
- y: y2
- };
- }
- function convertOffsetParentRelativeRectToViewportRelativeRect(_ref) {
- let {
- elements,
- rect,
- offsetParent,
- strategy
- } = _ref;
- const isFixed = strategy === "fixed";
- const documentElement = getDocumentElement(offsetParent);
- const topLayer = elements ? isTopLayer(elements.floating) : false;
- if (offsetParent === documentElement || topLayer && isFixed) {
- return rect;
- }
- let scroll = {
- scrollLeft: 0,
- scrollTop: 0
- };
- let scale = createCoords(1);
- const offsets = createCoords(0);
- const isOffsetParentAnElement = isHTMLElement(offsetParent);
- if (isOffsetParentAnElement || !isOffsetParentAnElement && !isFixed) {
- if (getNodeName(offsetParent) !== "body" || isOverflowElement(documentElement)) {
- scroll = getNodeScroll(offsetParent);
- }
- if (isHTMLElement(offsetParent)) {
- const offsetRect = getBoundingClientRect(offsetParent);
- scale = getScale(offsetParent);
- offsets.x = offsetRect.x + offsetParent.clientLeft;
- offsets.y = offsetRect.y + offsetParent.clientTop;
- }
- }
- const htmlOffset = documentElement && !isOffsetParentAnElement && !isFixed ? getHTMLOffset(documentElement, scroll, true) : createCoords(0);
- return {
- width: rect.width * scale.x,
- height: rect.height * scale.y,
- x: rect.x * scale.x - scroll.scrollLeft * scale.x + offsets.x + htmlOffset.x,
- y: rect.y * scale.y - scroll.scrollTop * scale.y + offsets.y + htmlOffset.y
- };
- }
- function getClientRects(element) {
- return Array.from(element.getClientRects());
- }
- function getDocumentRect(element) {
- const html = getDocumentElement(element);
- const scroll = getNodeScroll(element);
- const body = element.ownerDocument.body;
- const width = max(html.scrollWidth, html.clientWidth, body.scrollWidth, body.clientWidth);
- const height = max(html.scrollHeight, html.clientHeight, body.scrollHeight, body.clientHeight);
- let x2 = -scroll.scrollLeft + getWindowScrollBarX(element);
- const y2 = -scroll.scrollTop;
- if (getComputedStyle2(body).direction === "rtl") {
- x2 += max(html.clientWidth, body.clientWidth) - width;
- }
- return {
- width,
- height,
- x: x2,
- y: y2
- };
- }
- function getViewportRect(element, strategy) {
- const win = getWindow(element);
- const html = getDocumentElement(element);
- const visualViewport = win.visualViewport;
- let width = html.clientWidth;
- let height = html.clientHeight;
- let x2 = 0;
- let y2 = 0;
- if (visualViewport) {
- width = visualViewport.width;
- height = visualViewport.height;
- const visualViewportBased = isWebKit();
- if (!visualViewportBased || visualViewportBased && strategy === "fixed") {
- x2 = visualViewport.offsetLeft;
- y2 = visualViewport.offsetTop;
- }
- }
- return {
- width,
- height,
- x: x2,
- y: y2
- };
- }
- function getInnerBoundingClientRect(element, strategy) {
- const clientRect = getBoundingClientRect(element, true, strategy === "fixed");
- const top = clientRect.top + element.clientTop;
- const left = clientRect.left + element.clientLeft;
- const scale = isHTMLElement(element) ? getScale(element) : createCoords(1);
- const width = element.clientWidth * scale.x;
- const height = element.clientHeight * scale.y;
- const x2 = left * scale.x;
- const y2 = top * scale.y;
- return {
- width,
- height,
- x: x2,
- y: y2
- };
- }
- function getClientRectFromClippingAncestor(element, clippingAncestor, strategy) {
- let rect;
- if (clippingAncestor === "viewport") {
- rect = getViewportRect(element, strategy);
- } else if (clippingAncestor === "document") {
- rect = getDocumentRect(getDocumentElement(element));
- } else if (isElement(clippingAncestor)) {
- rect = getInnerBoundingClientRect(clippingAncestor, strategy);
- } else {
- const visualOffsets = getVisualOffsets(element);
- rect = {
- x: clippingAncestor.x - visualOffsets.x,
- y: clippingAncestor.y - visualOffsets.y,
- width: clippingAncestor.width,
- height: clippingAncestor.height
- };
- }
- return rectToClientRect(rect);
- }
- function hasFixedPositionAncestor(element, stopNode) {
- const parentNode = getParentNode(element);
- if (parentNode === stopNode || !isElement(parentNode) || isLastTraversableNode(parentNode)) {
- return false;
- }
- return getComputedStyle2(parentNode).position === "fixed" || hasFixedPositionAncestor(parentNode, stopNode);
- }
- function getClippingElementAncestors(element, cache2) {
- const cachedResult = cache2.get(element);
- if (cachedResult) {
- return cachedResult;
- }
- let result = getOverflowAncestors(element, [], false).filter((el) => isElement(el) && getNodeName(el) !== "body");
- let currentContainingBlockComputedStyle = null;
- const elementIsFixed = getComputedStyle2(element).position === "fixed";
- let currentNode = elementIsFixed ? getParentNode(element) : element;
- while (isElement(currentNode) && !isLastTraversableNode(currentNode)) {
- const computedStyle = getComputedStyle2(currentNode);
- const currentNodeIsContaining = isContainingBlock(currentNode);
- if (!currentNodeIsContaining && computedStyle.position === "fixed") {
- currentContainingBlockComputedStyle = null;
- }
- const shouldDropCurrentNode = elementIsFixed ? !currentNodeIsContaining && !currentContainingBlockComputedStyle : !currentNodeIsContaining && computedStyle.position === "static" && !!currentContainingBlockComputedStyle && ["absolute", "fixed"].includes(currentContainingBlockComputedStyle.position) || isOverflowElement(currentNode) && !currentNodeIsContaining && hasFixedPositionAncestor(element, currentNode);
- if (shouldDropCurrentNode) {
- result = result.filter((ancestor) => ancestor !== currentNode);
- } else {
- currentContainingBlockComputedStyle = computedStyle;
- }
- currentNode = getParentNode(currentNode);
- }
- cache2.set(element, result);
- return result;
- }
- function getClippingRect(_ref) {
- let {
- element,
- boundary,
- rootBoundary,
- strategy
- } = _ref;
- const elementClippingAncestors = boundary === "clippingAncestors" ? isTopLayer(element) ? [] : getClippingElementAncestors(element, this._c) : [].concat(boundary);
- const clippingAncestors = [...elementClippingAncestors, rootBoundary];
- const firstClippingAncestor = clippingAncestors[0];
- const clippingRect = clippingAncestors.reduce((accRect, clippingAncestor) => {
- const rect = getClientRectFromClippingAncestor(element, clippingAncestor, strategy);
- accRect.top = max(rect.top, accRect.top);
- accRect.right = min(rect.right, accRect.right);
- accRect.bottom = min(rect.bottom, accRect.bottom);
- accRect.left = max(rect.left, accRect.left);
- return accRect;
- }, getClientRectFromClippingAncestor(element, firstClippingAncestor, strategy));
- return {
- width: clippingRect.right - clippingRect.left,
- height: clippingRect.bottom - clippingRect.top,
- x: clippingRect.left,
- y: clippingRect.top
- };
- }
- function getDimensions(element) {
- const {
- width,
- height
- } = getCssDimensions(element);
- return {
- width,
- height
- };
- }
- function getRectRelativeToOffsetParent(element, offsetParent, strategy) {
- const isOffsetParentAnElement = isHTMLElement(offsetParent);
- const documentElement = getDocumentElement(offsetParent);
- const isFixed = strategy === "fixed";
- const rect = getBoundingClientRect(element, true, isFixed, offsetParent);
- let scroll = {
- scrollLeft: 0,
- scrollTop: 0
- };
- const offsets = createCoords(0);
- if (isOffsetParentAnElement || !isOffsetParentAnElement && !isFixed) {
- if (getNodeName(offsetParent) !== "body" || isOverflowElement(documentElement)) {
- scroll = getNodeScroll(offsetParent);
- }
- if (isOffsetParentAnElement) {
- const offsetRect = getBoundingClientRect(offsetParent, true, isFixed, offsetParent);
- offsets.x = offsetRect.x + offsetParent.clientLeft;
- offsets.y = offsetRect.y + offsetParent.clientTop;
- } else if (documentElement) {
- offsets.x = getWindowScrollBarX(documentElement);
- }
- }
- const htmlOffset = documentElement && !isOffsetParentAnElement && !isFixed ? getHTMLOffset(documentElement, scroll) : createCoords(0);
- const x2 = rect.left + scroll.scrollLeft - offsets.x - htmlOffset.x;
- const y2 = rect.top + scroll.scrollTop - offsets.y - htmlOffset.y;
- return {
- x: x2,
- y: y2,
- width: rect.width,
- height: rect.height
- };
- }
- function isStaticPositioned(element) {
- return getComputedStyle2(element).position === "static";
- }
- function getTrueOffsetParent(element, polyfill) {
- if (!isHTMLElement(element) || getComputedStyle2(element).position === "fixed") {
- return null;
- }
- if (polyfill) {
- return polyfill(element);
- }
- let rawOffsetParent = element.offsetParent;
- if (getDocumentElement(element) === rawOffsetParent) {
- rawOffsetParent = rawOffsetParent.ownerDocument.body;
- }
- return rawOffsetParent;
- }
- function getOffsetParent(element, polyfill) {
- const win = getWindow(element);
- if (isTopLayer(element)) {
- return win;
- }
- if (!isHTMLElement(element)) {
- let svgOffsetParent = getParentNode(element);
- while (svgOffsetParent && !isLastTraversableNode(svgOffsetParent)) {
- if (isElement(svgOffsetParent) && !isStaticPositioned(svgOffsetParent)) {
- return svgOffsetParent;
- }
- svgOffsetParent = getParentNode(svgOffsetParent);
- }
- return win;
- }
- let offsetParent = getTrueOffsetParent(element, polyfill);
- while (offsetParent && isTableElement(offsetParent) && isStaticPositioned(offsetParent)) {
- offsetParent = getTrueOffsetParent(offsetParent, polyfill);
- }
- if (offsetParent && isLastTraversableNode(offsetParent) && isStaticPositioned(offsetParent) && !isContainingBlock(offsetParent)) {
- return win;
- }
- return offsetParent || getContainingBlock(element) || win;
- }
- function isRTL(element) {
- return getComputedStyle2(element).direction === "rtl";
- }
- function observeMove(element, onMove) {
- let io = null;
- let timeoutId;
- const root2 = getDocumentElement(element);
- function cleanup2() {
- var _io;
- clearTimeout(timeoutId);
- (_io = io) == null || _io.disconnect();
- io = null;
- }
- function refresh(skip, threshold) {
- if (skip === void 0) {
- skip = false;
- }
- if (threshold === void 0) {
- threshold = 1;
- }
- cleanup2();
- const {
- left,
- top,
- width,
- height
- } = element.getBoundingClientRect();
- if (!skip) {
- onMove();
- }
- if (!width || !height) {
- return;
- }
- const insetTop = floor(top);
- const insetRight = floor(root2.clientWidth - (left + width));
- const insetBottom = floor(root2.clientHeight - (top + height));
- const insetLeft = floor(left);
- const rootMargin = -insetTop + "px " + -insetRight + "px " + -insetBottom + "px " + -insetLeft + "px";
- const options = {
- rootMargin,
- threshold: max(0, min(1, threshold)) || 1
- };
- let isFirstUpdate = true;
- function handleObserve(entries) {
- const ratio = entries[0].intersectionRatio;
- if (ratio !== threshold) {
- if (!isFirstUpdate) {
- return refresh();
- }
- if (!ratio) {
- timeoutId = setTimeout(() => {
- refresh(false, 1e-7);
- }, 1e3);
- } else {
- refresh(false, ratio);
- }
- }
- isFirstUpdate = false;
- }
- try {
- io = new IntersectionObserver(handleObserve, {
- ...options,
- // Handle