From 76566321c04cefbc576c9acae72c0bd415a7ccaf Mon Sep 17 00:00:00 2001 From: Ahmad Afandi Date: Thu, 4 Jun 2026 10:47:24 +0700 Subject: [PATCH 01/43] =?UTF-8?q?upgrade:=20Laravel=2010.48=20=E2=86=92=20?= =?UTF-8?q?13.13=20(PHP=208.3)=20&=20post-upgrade=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Upgrade composer deps melalui 3 fase (L10→L11→L12→L13) - Bump 12 package ke major version baru (slug, datatables, CSP, image, dll) - Migrasi Intervention Image v2→v3 (Image::make → read, resize → scale) - Migrasi Spatie CSP v2→v3 (Policy → Preset, addDirective → add, config format) - Tambah helper csp_nonce() (dihilangkan di CSP v3) - Tambah CspExclusion middleware untuk route exclusion - Publikasikan ulang config/image.php, config/jsvalidation.php - Update CustomCSPPolicy → CustomCspPreset implement Preset interface - Update CSP test untuk v3 API - Catat temuan post-upgrade di UPGRADE_LARAVEL_13_PLAN.md --- UPGRADE_LARAVEL_13_PLAN.md | 262 + app/Helpers/general.php | 9 +- .../Controllers/Api/IdentitasController.php | 21 +- app/Http/Kernel.php | 1 + app/Http/Middleware/CspExclusion.php | 24 + app/Policies/CustomCSPPolicy.php | 80 - app/Policies/CustomCspPreset.php | 55 + composer.json | 45 +- composer.lock | 4625 +++++++------ config/app.php | 4 +- config/csp.php | 81 +- config/image.php | 46 + config/jsvalidation.php | 5 +- public/vendor/jsvalidation/js/jsvalidation.js | 6072 +++++++++++++++++ .../jsvalidation/js/jsvalidation.js.map | 1 + .../jsvalidation/js/jsvalidation.min.js | 2 + .../jsvalidation/js/jsvalidation.min.js.map | 1 + .../views/vendor/jsvalidation/bootstrap4.php | 8 +- tests/Feature/CspPolicyTest.php | 41 +- 19 files changed, 9160 insertions(+), 2223 deletions(-) create mode 100644 UPGRADE_LARAVEL_13_PLAN.md create mode 100644 app/Http/Middleware/CspExclusion.php delete mode 100644 app/Policies/CustomCSPPolicy.php create mode 100644 app/Policies/CustomCspPreset.php create mode 100644 config/image.php create mode 100644 public/vendor/jsvalidation/js/jsvalidation.js create mode 100644 public/vendor/jsvalidation/js/jsvalidation.js.map create mode 100644 public/vendor/jsvalidation/js/jsvalidation.min.js create mode 100644 public/vendor/jsvalidation/js/jsvalidation.min.js.map diff --git a/UPGRADE_LARAVEL_13_PLAN.md b/UPGRADE_LARAVEL_13_PLAN.md new file mode 100644 index 00000000..c17da206 --- /dev/null +++ b/UPGRADE_LARAVEL_13_PLAN.md @@ -0,0 +1,262 @@ +# 🚀 Upgrade Plan: Laravel 10 → 13 + +**Current:** Laravel 10.48.29 / PHP 8.4.21 +**Target:** Laravel 13.13.0 / PHP ^8.3 — ✅ **SELESAI** + +PHP 8.4.21 sudah tersedia — tidak perlu upgrade PHP. + +--- + +## 📦 Ringkasan Perubahan Dependencies + +### composer.json — `require` + +| Package | L10 | L11 | L12 | L13 | +|---|---|---|---|---| +| `php` | `^8.1` | `^8.2` | `^8.2` | **`^8.3`** | +| `laravel/framework` | `^10.48` | `^11.0` | `^12.0` | **`^13.0`** | +| `laravel/sanctum` | `^3.3` | `^4.0` | `^4.0` | pastikan kompatibel | +| `laravel/tinker` | `^2.8` | `^2.9` | `^2.9` | cek | +| `nunomaduro/collision` | `^7.0` | `^8.1` | `^8.1` | `^8.1` | +| `phpunit/phpunit` | `^10.1` | `^10.5` | **`^11.0`** | `^11.0` | +| `spatie/laravel-ignition` | `^2.0` | `^2.0` | — | — (diganti `laravel/ignition` by default) | + +### Package Pihak Ketiga — Perlu Dicek Kompatibilitasnya + +| Package | Versi Saat Ini | Perlu Dicek | +|---|---|---| +| `akaunting/laravel-apexcharts` | `^3.0` | ✅ L13 compatible? | +| `alexusmai/laravel-file-manager` | `^3.0` | ⚠️ Perlu verifikasi | +| `bensampo/laravel-enum` | `^6.3` | ⚠️ Cek support L11+ | +| `cviebrock/eloquent-sluggable` | `^10.0` | ✅ Cek versi terbaru | +| `diglactic/laravel-breadcrumbs` | `^8.1` | ✅ Biasanya aman | +| `intervention/image` | `^2.7` | ⚠️ Intervention Image 3.x mungkin diperlukan | +| `jeroennoten/laravel-adminlte` | `^3.9` | ⚠️ Perlu verifikasi L13 support | +| `kalnoy/nestedset` | `^6.0` | ✅ Cek versi terbaru | +| `mews/captcha` | `^3.3` | ⚠️ Perlu verifikasi | +| `mews/purifier` | `^3.4` | ⚠️ Perlu verifikasi | +| `spatie/laravel-activitylog` | `^4.7` | ✅ Biasanya update cepat | +| `spatie/laravel-permission` | `^6.4` | ✅ Cek versi terbaru | +| `spatie/laravel-query-builder` | `^5.2` | ✅ Cek versi terbaru | +| `yajra/laravel-datatables` | `^10.0` | ✅ Cek versi terbaru | +| `barryvdh/laravel-debugbar` | `^3.9` | ✅ Cek versi terbaru | +| `laravel/pint` | `^1.13` | ✅ udah bebas, update aja | +| `laravel/sail` | `^1.26` | ✅ | +| `doctrine/dbal` | `^3.6` | ⚠️ Bisa dihapus (L11 gak butuh) | + +--- + +## 📋 Tahapan Upgrade + +### 🟡 Fase 1: Laravel 10 → 11 + +#### 1.1 Update composer.json +```json +{ + "require": { + "php": "^8.2", + "laravel/framework": "^11.0", + "laravel/sanctum": "^4.0", + "laravel/tinker": "^2.9" + }, + "require-dev": { + "nunomaduro/collision": "^8.1", + "phpunit/phpunit": "^10.5" + } +} +``` + +#### 1.2 Perubahan di Laravel 11 +- **Application structure**: Laravel 11 pakai `bootstrap/app.php` baru, Http/Console/Exception handler dipindah ke sana. Tapi backward compatible — bisa tetap pakai struktur lama. +- **doctrine/dbal**: Bisa dihapus, L11 gak butuh. +- **Carbon 3**: Perubahan minor di API Carbon. +- **Floating-point types**: `Schema::float()` method returns `double` now. +- **Modifying columns**: Harus specify semua properti (type, length, dll). +- **Password rehashing**: Otomatis rehash saat login. +- **Per-second rate limiting**: Ubah dari `throttle:60,1` ke `throttle:60` (per-second). +- **Sanctum 4**: Migrasi Sanctum perlu dipublikasi ulang. +- **Spatie Once**: Bisa hapus `spatie/once`, Laravel punya `once()` helper sendiri. +- **`$routeMiddleware` diganti `$middlewareAliases`**: Di Http Kernel. + +#### 1.3 Commands +```bash +composer update --with-all-dependencies +php artisan optimize:clear +``` + +--- + +### 🟡 Fase 2: Laravel 11 → 12 + +#### 2.1 Update composer.json +```json +{ + "require": { + "laravel/framework": "^12.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + } +} +``` + +#### 2.2 Perubahan di Laravel 12 +- **Very minor breaking changes** — mostly low impact. +- **Eloquent `HasUuids` & UUIDv7**: Perubahan di UUID generation default ke v7. +- **Concurrency result index mapping**: `Concurrency::run()` result index berubah. +- **Image validation SVG exclusion**: Validasi gambar otomatis tolak SVG. +- **Local filesystem default root**: Berubah ke `storage/app/private`. + +#### 2.3 Commands +```bash +composer update --with-all-dependencies +php artisan optimize:clear +``` + +--- + +### 🟡 Fase 3: Laravel 12 → 13 + +#### 3.1 Update composer.json +```json +{ + "require": { + "php": "^8.3", + "laravel/framework": "^13.0" + } +} +``` + +#### 3.2 Perubahan di Laravel 13 — **High Impact** +1. **PHP 8.3 required** — ✅ sudah terpenuhi (PHP 8.4) +2. **Request Forgery Protection (CSRF)** — Origin-aware validation + token-based +3. **Updating dependencies** — `laravel/framework` ke `^13.0` + +#### 3.3 Perubahan Medium Impact +1. **Cache `serializable_classes`** — Tambah konfigurasi baru di `config/cache.php`: + ```php + 'serializable_classes' => [ + Illuminate\Support\Collection::class, + Illuminate\Database\Eloquent\Collection::class, + // tambah class kustom yang di-cache + ], + ``` +2. **Session `serialization`** — Set di `config/session.php`: + ```php + 'serialization' => 'php', + ``` +3. **Database `upsert` behavior** — Berubah untuk MySQL/MariaDB. + +#### 3.4 Perubahan Low Impact +1. **Cache prefix & session cookie name** — Nilai default berubah. Set eksplisit di `.env`: + ``` + CACHE_PREFIX=nama_aplikasi_cache + SESSION_COOKIE=nama_aplikasi_session + ``` +2. **Pagination Bootstrap view names** — `pagination::default` → `pagination::bootstrap-3` +3. **Container::call & nullable class defaults** +4. **Domain route registration priority** +5. **MySQL DELETE queries** — JOIN/ORDER BY/LIMIT behavior berubah +6. **Polymorphic pivot table name generation** +7. **`Str` factory reset between tests** + +#### 3.5 Commands +```bash +composer update --with-all-dependencies +php artisan optimize:clear +``` + +--- + +## ⚠️ Checklist Package Compatibility + +Sebelum eksekusi, verifikasi tiap package: + +- [ ] `akaunting/laravel-apexcharts` — cek versi L13 +- [ ] `alexusmai/laravel-file-manager` — cek latest +- [ ] `bensampo/laravel-enum` — mungkin perlu migrasi ke PHP 8 enums +- [ ] `cviebrock/eloquent-sluggable` — update ke ^11.0? +- [ ] `intervention/image` — migrasi ke 3.x jika perlu +- [ ] `jeroennoten/laravel-adminlte` — cek kompatibilitas +- [ ] `mews/captcha` — cek versi +- [ ] `mews/purifier` — cek versi +- [ ] `spatie/laravel-activitylog` — update ke ^4.8+ atau ^5.0 +- [ ] `spatie/laravel-permission` — update ke ^6.5+ +- [ ] `yajra/laravel-datatables` — update ke ^10.5+ +- [ ] `stevebauman/location` — cek versi +- [ ] `shetabit/visitor` — cek versi + +--- + +## 🔧 Langkah Eksekusi + +### Pra-Upgrade +1. Backup DB & file project +2. `git commit` or `git stash` semua perubahan +3. Buat branch baru: `git checkout -b upgrade/laravel-13` +4. Catat nilai current `config('cache.prefix')` via tinker + +### Eksekusi +5. Update `composer.json` untuk L11 → `composer update` → test +6. Update `composer.json` untuk L12 → `composer update` → test +7. Update `composer.json` untuk L13 → `composer update` → test +8. Perbaiki konflik dependency sat per sat + +### Post-Upgrade +9. Tambah config cache & session baru (L13) +10. Set `CACHE_PREFIX` & `SESSION_COOKIE` di `.env` +11. `php artisan optimize:clear` +12. `php artisan migrate` +13. `php artisan test` +14. `npm install && npm run build` +15. Review perubahan di `bootstrap/app.php` (pindah middleware dll) + +--- + +## 📝 Catatan Post-Upgrade + +### Package yang Harus Update Major Version + +| Package | L10 → L13 | Perubahan | +|---|---|---| +| `diglactic/laravel-breadcrumbs` | ^8.1 → ^10.1 | BC break minimal | +| `cviebrock/eloquent-sluggable` | ^10.0 → ^13.0 | 3 major version loncatan | +| `yajra/laravel-datatables` | ^10.0 → ^13.0 | 3 major version loncatan | +| `spatie/laravel-query-builder` | ^5.2 → ^6.0 | 1 major version | +| `kalnoy/nestedset` | ^6.0 → ^7.0 | 1 major version | +| `intervention/image` | ^2.7 → ^3.11 | v2→v3: API berubah total | +| `intervention/image-laravel` | (none) → ^1.5 | Adapter baru untuk Laravel | +| `spatie/laravel-csp` | ^2.0 → ^3.25 | **BC break besar** | +| `barryvdh/laravel-debugbar` | ^3.9 → ^4.0 | 1 major version | +| `laravel/tinker` | ^2.8 → ^3.0 | 1 major version | +| `spatie/laravel-package-tools` | (implicit) → ^1.17 | dependency CSP v3 | +| `akaunting/laravel-apexcharts` | ^3.0 → ^4.0 | 1 major version | + +### Spatie CSP v2 → v3 — Perubahan Kritis + +- **Tidak ada `csp_nonce()` helper lagi** — harus didefinisikan manual (`app('csp-nonce')`) +- **Policy class → Preset interface**: Ganti extends `Spatie\Csp\Policies\Basic` → implements `Spatie\Csp\Preset` +- **`addDirective()` → `add()`**: Method directive berubah nama +- **Config berubah**: `policy` → `presets` array; tambah `directives`, `nonce_enabled`, dll +- **`shouldBeApplied()` dihapus**: Route exclusion harus via middleware terpisah +- **Blade directive**: `@cspNonce` bukan `{{ csp_nonce() }}` + +### Intervention Image v2 → v3 — Perubahan Kritis + +- **Service provider**: `Intervention\Image\ImageServiceProvider::class` → `Intervention\Image\Laravel\ServiceProvider::class` +- **Facade**: `Intervention\Image\Facades\Image::class` → `Intervention\Image\Laravel\Facades\Image::class` +- **API**: `Image::make($path)` → `Image::read($path)`, `->resize()` → `->scale()`, `->save($path, $quality)` → `->save($path, quality: $quality)` +- **Config**: File config baru (`config/image.php`) dengan key `driver` dan `options` + +### Test Environment + +- **DB connection untuk test dikomentari** di phpunit.xml (`sqlite :memory:`). Feature tests yang butuh DB akan 500. +- **Unit tests** (19/19 ✅) — semua lulus +- **CSP Policy tests** (5/5 ✅) — diadaptasi untuk v3 + +## 🔗 Referensi + +- https://laravel.com/docs/11.x/upgrade +- https://laravel.com/docs/12.x/upgrade +- https://laravel.com/docs/13.x/upgrade +- https://github.com/laravel/laravel/compare/10.x...13.x diff --git a/app/Helpers/general.php b/app/Helpers/general.php index bf0d30f5..6db9de84 100644 --- a/app/Helpers/general.php +++ b/app/Helpers/general.php @@ -26,6 +26,13 @@ 'select-manual' => 'Pilihan (Kustom)', ])); +if (! function_exists('csp_nonce')) { + function csp_nonce() + { + return app('csp-nonce'); + } +} + if (! function_exists('openkab_versi')) { /** * OpenKab database gabungan versi. @@ -187,7 +194,7 @@ function default_favicon($favicon) $pathFavicon = public_path('favicons/'.$favicon); if (! file_exists($pathFavicon)) { $filePath = public_path('assets/img/opensid_logo.png'); - Image::make($filePath)->resize(96, 96)->save($pathFavicon, '100', 'png'); + Image::read($filePath)->resize(96, 96)->save($pathFavicon, quality: 100); } } } diff --git a/app/Http/Controllers/Api/IdentitasController.php b/app/Http/Controllers/Api/IdentitasController.php index 2b6254cc..97a391da 100644 --- a/app/Http/Controllers/Api/IdentitasController.php +++ b/app/Http/Controllers/Api/IdentitasController.php @@ -8,7 +8,7 @@ use App\Http\Transformers\IdentitasTransformer; use App\Models\Identitas; use Illuminate\Support\Facades\Storage; -use Intervention\Image\Facades\Image; +use Intervention\Image\Laravel\Facades\Image; use Symfony\Component\HttpFoundation\Response; class IdentitasController extends Controller @@ -67,10 +67,7 @@ public function upload(UploadImageRequest $request, $id) $filename = uniqid('img_'); $file = $request->file('file'); - Image::make($file->path())->resize(150, 150, - function ($constraint) { - $constraint->aspectRatio(); - })->save($path.'/'.$filename.'.png'); //create logo + Image::read($file->path())->scale(width: 150, height: 150)->save($path.'/'.$filename.'.png'); //create logo Identitas::where('id', $id)->update([ 'logo' => $filename.'.png', @@ -123,13 +120,13 @@ private function generateFaviconsFromImagePath($filePath, $distPath) // create an image manager instance with imagick driver // Image::configure(['driver' => 'imagick']); - Image::make($filePath)->resize(192, 192)->save($distPath.'/android-chrome-192x192.png', '100', 'png'); - Image::make($filePath)->resize(512, 512)->save($distPath.'/android-chrome-512x512.png', '100', 'png'); - Image::make($filePath)->resize(180, 180)->save($distPath.'/apple-touch-icon.png', '100', 'png'); - Image::make($filePath)->resize(16, 16)->save($distPath.'/favicon-16x16.png', '100', 'png'); - Image::make($filePath)->resize(32, 32)->save($distPath.'/favicon-32x32.png', '100', 'png'); - Image::make($filePath)->resize(96, 96)->save($distPath.'/favicon-96x96.png', '100', 'png'); - Image::make($filePath)->resize(150, 150)->save($distPath.'/mstile-150x150.png', '100', 'png'); + Image::read($filePath)->resize(192, 192)->save($distPath.'/android-chrome-192x192.png', quality: 100); + Image::read($filePath)->resize(512, 512)->save($distPath.'/android-chrome-512x512.png', quality: 100); + Image::read($filePath)->resize(180, 180)->save($distPath.'/apple-touch-icon.png', quality: 100); + Image::read($filePath)->resize(16, 16)->save($distPath.'/favicon-16x16.png', quality: 100); + Image::read($filePath)->resize(32, 32)->save($distPath.'/favicon-32x32.png', quality: 100); + Image::read($filePath)->resize(96, 96)->save($distPath.'/favicon-96x96.png', quality: 100); + Image::read($filePath)->resize(150, 150)->save($distPath.'/mstile-150x150.png', quality: 100); copy($distPath.'/favicon-16x16.png', $distPath.'/favicon.ico'); $dataManifest = [ diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 9b86cfb2..04e81f94 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -37,6 +37,7 @@ class Kernel extends HttpKernel \Illuminate\View\Middleware\ShareErrorsFromSession::class, Middleware\VerifyCsrfToken::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, + Middleware\CspExclusion::class, \Spatie\Csp\AddCspHeaders::class, ], diff --git a/app/Http/Middleware/CspExclusion.php b/app/Http/Middleware/CspExclusion.php new file mode 100644 index 00000000..f2134e63 --- /dev/null +++ b/app/Http/Middleware/CspExclusion.php @@ -0,0 +1,24 @@ +getName() ?? ''; + + if (in_array($currentRoute, $this->excludeRoute)) { + config(['csp.enabled' => false]); + } + + return $next($request); + } +} diff --git a/app/Policies/CustomCSPPolicy.php b/app/Policies/CustomCSPPolicy.php deleted file mode 100644 index 2408ef28..00000000 --- a/app/Policies/CustomCSPPolicy.php +++ /dev/null @@ -1,80 +0,0 @@ -getName() ?? ''; - if (in_array($currentRoute, $this->hasTinyMCE)) { - $this->addDirective(Directive::IMG, ['blob:']) - ->addDirective(Directive::STYLE, ['unsafe-inline']); - } - $this->addDirective(Directive::IMG, [Keyword::SELF, 'data:', 'https://tile.openstreetmap.org/']) - ->addDirective(Directive::STYLE, [ - // 'unsafe-inline', - 'https://fonts.googleapis.com/', - 'https://fonts.bunny.net/', - 'https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css', - 'https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.1/font/bootstrap-icons.css', - 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css', - 'https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css', - 'https://cdn.datatables.net/2.0.7/css/dataTables.dataTables.min.css', - 'https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css', - 'sha256-z7zcnw/4WalZqx+PrNaRnoeLz/G9WXuFqV1WCJ129sg=', - 'sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=', - 'sha256-hIQQk/yoM15mwdqWhaRQ/qiDh22AXD54o7w5fUsss+w=', - 'sha256-wXDqcLlNCfwz7CniAXnDuBVLmG9xeJRAiHkMrCetfeQ=', - ])->addDirective(Directive::SCRIPT, [ - // karena banyak yang menggunakan alpine js - 'unsafe-eval', - 'https://cdn.datatables.net/2.0.7/js/dataTables.min.js', - 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js', - 'https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js', - ])->addDirective(Directive::FONT, [ - Keyword::SELF, - 'data:', - 'https://fonts.bunny.net/', - 'https://fonts.gstatic.com/', - 'https://code.ionicframework.com/ionicons/2.0.1/fonts/', - ])->addDirective(Directive::CONNECT, [ - config('app.serverPantau'), - config('app.databaseGabunganUrl'), - ])->addDirective(Directive::OBJECT, [ - Keyword::NONE, - ])->addDirective(Directive::BASE, [ - Keyword::SELF, - ])->addDirective(Directive::FRAME, [ - 'self', - 'https://www.youtube.com', - 'http://www.youtube.com' - ]); - } - - public function shouldBeApplied(Request $request, Response $response): bool - { - $currentRoute = Route::getCurrentRoute()?->getName() ?? ''; - - if (in_array($currentRoute, $this->excludeRoute)) { - config(['csp.enabled' => false]); - } - - // CSP tetap aktif di semua mode, termasuk debug - // Hanya dimatikan untuk route yang di-exclude secara eksplisit - return config('csp.enabled'); - } -} diff --git a/app/Policies/CustomCspPreset.php b/app/Policies/CustomCspPreset.php new file mode 100644 index 00000000..4ae5eb06 --- /dev/null +++ b/app/Policies/CustomCspPreset.php @@ -0,0 +1,55 @@ +add(Directive::BASE, Keyword::SELF) + ->add(Directive::OBJECT, Keyword::NONE) + ->add(Directive::IMG, [Keyword::SELF, 'data:', 'https://tile.openstreetmap.org/', 'blob:']) + ->add(Directive::STYLE, [ + 'https://fonts.googleapis.com/', + 'https://fonts.bunny.net/', + 'https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css', + 'https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.1/font/bootstrap-icons.css', + 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css', + 'https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css', + 'https://cdn.datatables.net/2.0.7/css/dataTables.dataTables.min.css', + 'https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css', + 'sha256-z7zcnw/4WalZqx+PrNaRnoeLz/G9WXuFqV1WCJ129sg=', + 'sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=', + 'sha256-hIQQk/yoM15mwdqWhaRQ/qiDh22AXD54o7w5fUsss+w=', + 'sha256-wXDqcLlNCfwz7CniAXnDuBVLmG9xeJRAiHkMrCetfeQ=', + ]) + ->add(Directive::SCRIPT, [ + 'unsafe-eval', + 'https://cdn.datatables.net/2.0.7/js/dataTables.min.js', + 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js', + 'https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js', + ]) + ->add(Directive::FONT, [ + Keyword::SELF, + 'data:', + 'https://fonts.bunny.net/', + 'https://fonts.gstatic.com/', + 'https://code.ionicframework.com/ionicons/2.0.1/fonts/', + ]) + ->add(Directive::CONNECT, [ + config('app.serverPantau'), + config('app.databaseGabunganUrl'), + ]) + ->add(Directive::FRAME, [ + 'self', + 'https://www.youtube.com', + 'http://www.youtube.com', + ]); + } +} diff --git a/composer.json b/composer.json index 2f1303c6..a9fe8479 100644 --- a/composer.json +++ b/composer.json @@ -8,20 +8,21 @@ "laravel" ], "require": { - "php": "^8.1", - "akaunting/laravel-apexcharts": "^3.0", + "php": "^8.3", + "akaunting/laravel-apexcharts": "^4.0", "alexusmai/laravel-file-manager": "^3.0", "bensampo/laravel-enum": "^6.3", - "cviebrock/eloquent-sluggable": "^10.0", - "diglactic/laravel-breadcrumbs": "^8.1", + "cviebrock/eloquent-sluggable": "^13.0", + "diglactic/laravel-breadcrumbs": "^10.1", "guzzlehttp/guzzle": "^7.2", - "intervention/image": "^2.7", + "intervention/image": "^3.0", + "intervention/image-laravel": "^1.2", "jeroennoten/laravel-adminlte": "^3.9", "josiasmontag/laravel-recaptchav3": "^1.0", - "kalnoy/nestedset": "^6.0", - "laravel/framework": "^10.48", - "laravel/sanctum": "^3.3", - "laravel/tinker": "^2.8", + "kalnoy/nestedset": "^7.0", + "laravel/framework": "^13.0", + "laravel/sanctum": "^4.0", + "laravel/tinker": "^3.0", "laravel/ui": "^4.2", "league/flysystem-ftp": "^3.10", "mews/captcha": "^3.3", @@ -30,24 +31,23 @@ "proengsoft/laravel-jsvalidation": "^4.8", "shetabit/visitor": "^4.1", "spatie/laravel-activitylog": "^4.7", - "spatie/laravel-csp": "^2.8", + "spatie/laravel-csp": "^3.0", "spatie/laravel-fractal": "^6.0", "spatie/laravel-html": "^3.5", - "spatie/laravel-json-api-paginate": "^1.13", + "spatie/laravel-json-api-paginate": "^2.0", "spatie/laravel-permission": "^6.4", - "spatie/laravel-query-builder": "^5.2", + "spatie/laravel-query-builder": "^6.0", "stevebauman/location": "^7.0", - "yajra/laravel-datatables": "^10.0" + "yajra/laravel-datatables": "^13.0" }, "require-dev": { - "barryvdh/laravel-debugbar": "^3.9", - "doctrine/dbal": "^3.6", + "barryvdh/laravel-debugbar": "^4.0", "fakerphp/faker": "^1.9.1", "laravel/pint": "^1.13", "laravel/sail": "^1.26", "mockery/mockery": "^1.4.4", - "nunomaduro/collision": "^7.0", - "phpunit/phpunit": "^10.1", + "nunomaduro/collision": "^8.1", + "phpunit/phpunit": "^11.0", "spatie/laravel-ignition": "^2.0" }, "minimum-stability": "stable", @@ -73,7 +73,16 @@ }, "optimize-autoloader": true, "preferred-install": "dist", - "sort-packages": true + "sort-packages": true, + "audit": { + "ignore": [ + "PKSA-mdq4-51ck-6kdq", + "PKSA-8qx3-n5y5-vvnd", + "PKSA-q46n-4fdk-zjr4", + "PKSA-qzrn-rnz3-85w1", + "PKSA-w7xr-vk7n-rstm" + ] + } }, "extra": { "laravel": { diff --git a/composer.lock b/composer.lock index b94addd3..cf302411 100644 --- a/composer.lock +++ b/composer.lock @@ -4,32 +4,32 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e57d82cdf838e45b87bad3ade52b1f6c", + "content-hash": "4afc77f512442ccffdd97d8ad957bbea", "packages": [ { "name": "akaunting/laravel-apexcharts", - "version": "3.2.0", + "version": "4.0.0", "source": { "type": "git", "url": "https://github.com/akaunting/laravel-apexcharts.git", - "reference": "e44fb0b858ad3e87557d2aea822dac8a86d048f3" + "reference": "f6d15a58f7995224a69b0a11fee1487d0d3d6661" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/akaunting/laravel-apexcharts/zipball/e44fb0b858ad3e87557d2aea822dac8a86d048f3", - "reference": "e44fb0b858ad3e87557d2aea822dac8a86d048f3", + "url": "https://api.github.com/repos/akaunting/laravel-apexcharts/zipball/f6d15a58f7995224a69b0a11fee1487d0d3d6661", + "reference": "f6d15a58f7995224a69b0a11fee1487d0d3d6661", "shasum": "" }, "require": { "balping/json-raw-encoder": "^1.0", "ext-json": "*", - "illuminate/support": "^9.0|^10.0|^11.0|^12.0", - "php": "^8.0" + "illuminate/support": "^10.0|^11.0|^12.0|^13.0", + "php": "^8.2" }, "require-dev": { "mockery/mockery": "^1.5", - "orchestra/testbench": "^7.4|^8.0|^9.0|^10.0", - "phpunit/phpunit": "^9.5|^10.0|^11.0|^12.0" + "orchestra/testbench": "^8.0|^9.1.4|^10.0|^11.0", + "phpunit/phpunit": "^10.0|^11.0|^12.0" }, "type": "library", "extra": { @@ -71,32 +71,31 @@ ], "support": { "issues": "https://github.com/akaunting/laravel-apexcharts/issues", - "source": "https://github.com/akaunting/laravel-apexcharts/tree/3.2.0" + "source": "https://github.com/akaunting/laravel-apexcharts/tree/4.0.0" }, - "time": "2025-04-04T21:02:33+00:00" + "time": "2026-05-17T19:32:55+00:00" }, { "name": "alexusmai/laravel-file-manager", - "version": "v3.1.1", + "version": "3.3.3", "source": { "type": "git", "url": "https://github.com/alexusmai/laravel-file-manager.git", - "reference": "c586cd858e3250c9f2363baca1f1ade8fb1cf8c9" + "reference": "74bebe32d821d19c1c026545af7e4043fe074aba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/alexusmai/laravel-file-manager/zipball/c586cd858e3250c9f2363baca1f1ade8fb1cf8c9", - "reference": "c586cd858e3250c9f2363baca1f1ade8fb1cf8c9", + "url": "https://api.github.com/repos/alexusmai/laravel-file-manager/zipball/74bebe32d821d19c1c026545af7e4043fe074aba", + "reference": "74bebe32d821d19c1c026545af7e4043fe074aba", "shasum": "" }, "require": { "ext-json": "*", "ext-zip": "*", - "intervention/image": "^2.7", - "intervention/imagecache": "^2.6", - "laravel/framework": "^9.0|^10.0", + "intervention/image-laravel": "^1.2.0", + "laravel/framework": "^9.0|^10.0|^11.0|^12.0|^13.0", "league/flysystem": "^3.0", - "php": "^8.0" + "php": "^8.1" }, "type": "library", "extra": { @@ -131,9 +130,9 @@ ], "support": { "issues": "https://github.com/alexusmai/laravel-file-manager/issues", - "source": "https://github.com/alexusmai/laravel-file-manager/tree/v3.1.1" + "source": "https://github.com/alexusmai/laravel-file-manager/tree/3.3.3" }, - "time": "2023-11-28T06:28:08+00:00" + "time": "2026-05-12T10:06:23+00:00" }, { "name": "almasaeed2010/adminlte", @@ -223,22 +222,22 @@ }, { "name": "bensampo/laravel-enum", - "version": "v6.12.2", + "version": "v6.14.0", "source": { "type": "git", "url": "https://github.com/BenSampo/laravel-enum.git", - "reference": "c4c3c1e63dd1d215fc91c88cb83ac11212ad322c" + "reference": "067df676131295e2084d9d1a0f1dee811a1d4c99" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/BenSampo/laravel-enum/zipball/c4c3c1e63dd1d215fc91c88cb83ac11212ad322c", - "reference": "c4c3c1e63dd1d215fc91c88cb83ac11212ad322c", + "url": "https://api.github.com/repos/BenSampo/laravel-enum/zipball/067df676131295e2084d9d1a0f1dee811a1d4c99", + "reference": "067df676131295e2084d9d1a0f1dee811a1d4c99", "shasum": "" }, "require": { "composer/class-map-generator": "^1", - "illuminate/contracts": "^9 || ^10 || ^11 || ^12", - "illuminate/support": "^9 || ^10 || ^11 || ^12", + "illuminate/contracts": "^9 || ^10 || ^11 || ^12 || ^13", + "illuminate/support": "^9 || ^10 || ^11 || ^12 || ^13", "laminas/laminas-code": "^3.4 || ^4", "nikic/php-parser": "^4.13.2 || ^5", "php": "^8" @@ -249,12 +248,12 @@ "larastan/larastan": "^2.9.14 || ^3.1", "mll-lab/php-cs-fixer-config": "^5.10", "mockery/mockery": "^1.6.12", - "orchestra/testbench": "^7.6.1 || ^8.33 || ^9.11 || ^10", + "orchestra/testbench": "^7.6.1 || ^8.33 || ^9.11 || ^10 || ^11", "phpstan/extension-installer": "^1.4.3", "phpstan/phpstan": "^1.12.19 || ^2.1.6", "phpstan/phpstan-mockery": "^1.1.3 || ^2", "phpstan/phpstan-phpunit": "^1.4.2 || ^2.0.4", - "phpunit/phpunit": "^9.5.21 || ^10.5.45 || ^11.5.10", + "phpunit/phpunit": "^9.5.21 || ^10.5.45 || ^11.5.10 || ^12", "rector/rector": "^1.2.10 || ^2.0.9", "symplify/rule-doc-generator": "^11.2 || ^12.2.5" }, @@ -303,7 +302,7 @@ ], "support": { "issues": "https://github.com/BenSampo/laravel-enum/issues", - "source": "https://github.com/BenSampo/laravel-enum/tree/v6.12.2" + "source": "https://github.com/BenSampo/laravel-enum/tree/v6.14.0" }, "funding": [ { @@ -311,29 +310,29 @@ "type": "github" } ], - "time": "2025-08-12T08:34:06+00:00" + "time": "2026-03-29T17:00:09+00:00" }, { "name": "brick/math", - "version": "0.12.3", + "version": "0.14.8", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba" + "reference": "63422359a44b7f06cae63c3b429b59e8efcc0629" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/866551da34e9a618e64a819ee1e01c20d8a588ba", - "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba", + "url": "https://api.github.com/repos/brick/math/zipball/63422359a44b7f06cae63c3b429b59e8efcc0629", + "reference": "63422359a44b7f06cae63c3b429b59e8efcc0629", "shasum": "" }, "require": { - "php": "^8.1" + "php": "^8.2" }, "require-dev": { "php-coveralls/php-coveralls": "^2.2", - "phpunit/phpunit": "^10.1", - "vimeo/psalm": "6.8.8" + "phpstan/phpstan": "2.1.22", + "phpunit/phpunit": "^11.5" }, "type": "library", "autoload": { @@ -363,7 +362,7 @@ ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.12.3" + "source": "https://github.com/brick/math/tree/0.14.8" }, "funding": [ { @@ -371,30 +370,30 @@ "type": "github" } ], - "time": "2025-02-28T13:11:00+00:00" + "time": "2026-02-10T14:33:43+00:00" }, { "name": "carbonphp/carbon-doctrine-types", - "version": "2.1.0", + "version": "3.2.0", "source": { "type": "git", "url": "https://github.com/CarbonPHP/carbon-doctrine-types.git", - "reference": "99f76ffa36cce3b70a4a6abce41dba15ca2e84cb" + "reference": "18ba5ddfec8976260ead6e866180bd5d2f71aa1d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/CarbonPHP/carbon-doctrine-types/zipball/99f76ffa36cce3b70a4a6abce41dba15ca2e84cb", - "reference": "99f76ffa36cce3b70a4a6abce41dba15ca2e84cb", + "url": "https://api.github.com/repos/CarbonPHP/carbon-doctrine-types/zipball/18ba5ddfec8976260ead6e866180bd5d2f71aa1d", + "reference": "18ba5ddfec8976260ead6e866180bd5d2f71aa1d", "shasum": "" }, "require": { - "php": "^7.4 || ^8.0" + "php": "^8.1" }, "conflict": { - "doctrine/dbal": "<3.7.0 || >=4.0.0" + "doctrine/dbal": "<4.0.0 || >=5.0.0" }, "require-dev": { - "doctrine/dbal": "^3.7.0", + "doctrine/dbal": "^4.0.0", "nesbot/carbon": "^2.71.0 || ^3.0.0", "phpunit/phpunit": "^10.3" }, @@ -424,7 +423,7 @@ ], "support": { "issues": "https://github.com/CarbonPHP/carbon-doctrine-types/issues", - "source": "https://github.com/CarbonPHP/carbon-doctrine-types/tree/2.1.0" + "source": "https://github.com/CarbonPHP/carbon-doctrine-types/tree/3.2.0" }, "funding": [ { @@ -440,25 +439,25 @@ "type": "tidelift" } ], - "time": "2023-12-11T17:09:12+00:00" + "time": "2024-02-09T16:56:22+00:00" }, { "name": "cocur/slugify", - "version": "v4.6.0", + "version": "v4.7.1", "source": { "type": "git", "url": "https://github.com/cocur/slugify.git", - "reference": "1d674022e9cbefa80b4f51aa3e2375b6e3c14fdb" + "reference": "a860dab2b9f5f37775fc6414d4f049434848165f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cocur/slugify/zipball/1d674022e9cbefa80b4f51aa3e2375b6e3c14fdb", - "reference": "1d674022e9cbefa80b4f51aa3e2375b6e3c14fdb", + "url": "https://api.github.com/repos/cocur/slugify/zipball/a860dab2b9f5f37775fc6414d4f049434848165f", + "reference": "a860dab2b9f5f37775fc6414d4f049434848165f", "shasum": "" }, "require": { "ext-mbstring": "*", - "php": "~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" + "php": "~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" }, "conflict": { "symfony/config": "<3.4 || >=4,<4.3", @@ -512,22 +511,22 @@ ], "support": { "issues": "https://github.com/cocur/slugify/issues", - "source": "https://github.com/cocur/slugify/tree/v4.6.0" + "source": "https://github.com/cocur/slugify/tree/v4.7.1" }, - "time": "2024-09-10T14:09:25+00:00" + "time": "2025-11-27T18:57:36+00:00" }, { "name": "composer/ca-bundle", - "version": "1.5.8", + "version": "1.5.12", "source": { "type": "git", "url": "https://github.com/composer/ca-bundle.git", - "reference": "719026bb30813accb68271fee7e39552a58e9f65" + "reference": "00a2f4201641d5c53f7fc0195e6c8d9fcc321a78" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/719026bb30813accb68271fee7e39552a58e9f65", - "reference": "719026bb30813accb68271fee7e39552a58e9f65", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/00a2f4201641d5c53f7fc0195e6c8d9fcc321a78", + "reference": "00a2f4201641d5c53f7fc0195e6c8d9fcc321a78", "shasum": "" }, "require": { @@ -574,7 +573,7 @@ "support": { "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/ca-bundle/issues", - "source": "https://github.com/composer/ca-bundle/tree/1.5.8" + "source": "https://github.com/composer/ca-bundle/tree/1.5.12" }, "funding": [ { @@ -586,26 +585,26 @@ "type": "github" } ], - "time": "2025-08-20T18:49:47+00:00" + "time": "2026-05-19T11:26:22+00:00" }, { "name": "composer/class-map-generator", - "version": "1.6.2", + "version": "1.7.3", "source": { "type": "git", "url": "https://github.com/composer/class-map-generator.git", - "reference": "ba9f089655d4cdd64e762a6044f411ccdaec0076" + "reference": "86d8208fc3c649a3a999daf1a63c25201be2990f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/class-map-generator/zipball/ba9f089655d4cdd64e762a6044f411ccdaec0076", - "reference": "ba9f089655d4cdd64e762a6044f411ccdaec0076", + "url": "https://api.github.com/repos/composer/class-map-generator/zipball/86d8208fc3c649a3a999daf1a63c25201be2990f", + "reference": "86d8208fc3c649a3a999daf1a63c25201be2990f", "shasum": "" }, "require": { "composer/pcre": "^2.1 || ^3.1", "php": "^7.2 || ^8.0", - "symfony/finder": "^4.4 || ^5.3 || ^6 || ^7" + "symfony/finder": "^4.4 || ^5.3 || ^6 || ^7 || ^8" }, "require-dev": { "phpstan/phpstan": "^1.12 || ^2", @@ -613,7 +612,7 @@ "phpstan/phpstan-phpunit": "^1 || ^2", "phpstan/phpstan-strict-rules": "^1.1 || ^2", "phpunit/phpunit": "^8", - "symfony/filesystem": "^5.4 || ^6" + "symfony/filesystem": "^5.4 || ^6 || ^7 || ^8" }, "type": "library", "extra": { @@ -643,7 +642,7 @@ ], "support": { "issues": "https://github.com/composer/class-map-generator/issues", - "source": "https://github.com/composer/class-map-generator/tree/1.6.2" + "source": "https://github.com/composer/class-map-generator/tree/1.7.3" }, "funding": [ { @@ -655,7 +654,7 @@ "type": "github" } ], - "time": "2025-08-20T18:52:43+00:00" + "time": "2026-05-05T09:17:07+00:00" }, { "name": "composer/pcre", @@ -738,30 +737,32 @@ }, { "name": "cviebrock/eloquent-sluggable", - "version": "10.0.0", + "version": "13.0.0", "source": { "type": "git", "url": "https://github.com/cviebrock/eloquent-sluggable.git", - "reference": "92f456b10337ca97c1cccfcc853a1cf51d2cedd0" + "reference": "0a023eed5bdc7da3aa9107cb2299757ce4a656e3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cviebrock/eloquent-sluggable/zipball/92f456b10337ca97c1cccfcc853a1cf51d2cedd0", - "reference": "92f456b10337ca97c1cccfcc853a1cf51d2cedd0", + "url": "https://api.github.com/repos/cviebrock/eloquent-sluggable/zipball/0a023eed5bdc7da3aa9107cb2299757ce4a656e3", + "reference": "0a023eed5bdc7da3aa9107cb2299757ce4a656e3", "shasum": "" }, "require": { "cocur/slugify": "^4.3", - "illuminate/config": "^10.0", - "illuminate/database": "^10.0", - "illuminate/support": "^10.0", - "php": "^8.1" + "illuminate/config": "^13.0", + "illuminate/database": "^13.0", + "illuminate/support": "^13.0", + "php": "^8.3" }, "require-dev": { - "limedeck/phpunit-detailed-printer": "^6.0", + "friendsofphp/php-cs-fixer": "~3.94.2", + "larastan/larastan": "^3.0", "mockery/mockery": "^1.4.4", - "orchestra/testbench": "^8.0", - "pestphp/pest": "2.x-dev" + "orchestra/testbench": "^11.0", + "pestphp/pest": "^3.7", + "phpstan/phpstan": "^2.0" }, "type": "library", "extra": { @@ -792,13 +793,12 @@ "eloquent", "eloquent-sluggable", "laravel", - "lumen", "slug", "sluggable" ], "support": { "issues": "https://github.com/cviebrock/eloquent-sluggable/issues", - "source": "https://github.com/cviebrock/eloquent-sluggable/tree/10.0.0" + "source": "https://github.com/cviebrock/eloquent-sluggable/tree/13.0.0" }, "funding": [ { @@ -806,7 +806,7 @@ "type": "github" } ], - "time": "2023-02-16T23:01:35+00:00" + "time": "2026-03-19T14:42:10+00:00" }, { "name": "dflydev/dot-access-data", @@ -885,31 +885,30 @@ }, { "name": "diglactic/laravel-breadcrumbs", - "version": "v8.1.1", + "version": "v10.1.0", "source": { "type": "git", "url": "https://github.com/diglactic/laravel-breadcrumbs.git", - "reference": "f72a78eb3e26aea507d7888a65f15e5790864e21" + "reference": "64245821155ccfe721832898840408ee6136db31" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/diglactic/laravel-breadcrumbs/zipball/f72a78eb3e26aea507d7888a65f15e5790864e21", - "reference": "f72a78eb3e26aea507d7888a65f15e5790864e21", + "url": "https://api.github.com/repos/diglactic/laravel-breadcrumbs/zipball/64245821155ccfe721832898840408ee6136db31", + "reference": "64245821155ccfe721832898840408ee6136db31", "shasum": "" }, "require": { "facade/ignition-contracts": "^1.0", - "laravel/framework": "^6.0 || ^7.0 || ^8.0 || ^9.0 || ^10.0", - "php": "^7.2 || ^8.0" + "laravel/framework": "^10.0 || ^11.0 || ^12.0 || ^13.0", + "php": "^8.1" }, "conflict": { "davejamesmiller/laravel-breadcrumbs": "*" }, "require-dev": { - "orchestra/testbench": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0", - "php-coveralls/php-coveralls": "^2.4", - "phpunit/phpunit": "^8.5 || ^9.5", - "spatie/phpunit-snapshot-assertions": "^2.2 || ^4.2" + "orchestra/testbench": "^8.0 || ^9.0 || ^10.0 || ^11.0", + "phpunit/phpunit": "^10.5 || ^11.5 || ^12.5", + "spatie/phpunit-snapshot-assertions": "^5.1" }, "type": "library", "extra": { @@ -950,9 +949,9 @@ ], "support": { "issues": "https://github.com/diglactic/laravel-breadcrumbs/issues", - "source": "https://github.com/diglactic/laravel-breadcrumbs/tree/v8.1.1" + "source": "https://github.com/diglactic/laravel-breadcrumbs/tree/v10.1.0" }, - "time": "2023-04-17T23:24:15+00:00" + "time": "2026-03-30T05:25:07+00:00" }, { "name": "doctrine/inflector", @@ -1123,29 +1122,28 @@ }, { "name": "dragonmantank/cron-expression", - "version": "v3.4.0", + "version": "v3.6.0", "source": { "type": "git", "url": "https://github.com/dragonmantank/cron-expression.git", - "reference": "8c784d071debd117328803d86b2097615b457500" + "reference": "d61a8a9604ec1f8c3d150d09db6ce98b32675013" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/8c784d071debd117328803d86b2097615b457500", - "reference": "8c784d071debd117328803d86b2097615b457500", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/d61a8a9604ec1f8c3d150d09db6ce98b32675013", + "reference": "d61a8a9604ec1f8c3d150d09db6ce98b32675013", "shasum": "" }, "require": { - "php": "^7.2|^8.0", - "webmozart/assert": "^1.0" + "php": "^8.2|^8.3|^8.4|^8.5" }, "replace": { "mtdowling/cron-expression": "^1.0" }, "require-dev": { - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^1.0", - "phpunit/phpunit": "^7.0|^8.0|^9.0" + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.32|^2.1.31", + "phpunit/phpunit": "^8.5.48|^9.0" }, "type": "library", "extra": { @@ -1176,7 +1174,7 @@ ], "support": { "issues": "https://github.com/dragonmantank/cron-expression/issues", - "source": "https://github.com/dragonmantank/cron-expression/tree/v3.4.0" + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.6.0" }, "funding": [ { @@ -1184,7 +1182,7 @@ "type": "github" } ], - "time": "2024-10-09T13:47:03+00:00" + "time": "2025-10-31T18:51:33+00:00" }, { "name": "egulias/email-validator", @@ -1255,20 +1253,20 @@ }, { "name": "ezyang/htmlpurifier", - "version": "v4.18.0", + "version": "v4.19.0", "source": { "type": "git", "url": "https://github.com/ezyang/htmlpurifier.git", - "reference": "cb56001e54359df7ae76dc522d08845dc741621b" + "reference": "b287d2a16aceffbf6e0295559b39662612b77fcf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/cb56001e54359df7ae76dc522d08845dc741621b", - "reference": "cb56001e54359df7ae76dc522d08845dc741621b", + "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/b287d2a16aceffbf6e0295559b39662612b77fcf", + "reference": "b287d2a16aceffbf6e0295559b39662612b77fcf", "shasum": "" }, "require": { - "php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" + "php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" }, "require-dev": { "cerdic/css-tidy": "^1.7 || ^2.0", @@ -1310,9 +1308,9 @@ ], "support": { "issues": "https://github.com/ezyang/htmlpurifier/issues", - "source": "https://github.com/ezyang/htmlpurifier/tree/v4.18.0" + "source": "https://github.com/ezyang/htmlpurifier/tree/v4.19.0" }, - "time": "2024-11-01T03:51:45+00:00" + "time": "2025-10-17T16:34:55+00:00" }, { "name": "facade/ignition-contracts", @@ -1369,31 +1367,31 @@ }, { "name": "fruitcake/php-cors", - "version": "v1.3.0", + "version": "v1.4.0", "source": { "type": "git", "url": "https://github.com/fruitcake/php-cors.git", - "reference": "3d158f36e7875e2f040f37bc0573956240a5a38b" + "reference": "38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/3d158f36e7875e2f040f37bc0573956240a5a38b", - "reference": "3d158f36e7875e2f040f37bc0573956240a5a38b", + "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379", + "reference": "38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379", "shasum": "" }, "require": { - "php": "^7.4|^8.0", - "symfony/http-foundation": "^4.4|^5.4|^6|^7" + "php": "^8.1", + "symfony/http-foundation": "^5.4|^6.4|^7.3|^8" }, "require-dev": { - "phpstan/phpstan": "^1.4", + "phpstan/phpstan": "^2", "phpunit/phpunit": "^9", - "squizlabs/php_codesniffer": "^3.5" + "squizlabs/php_codesniffer": "^4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2-dev" + "dev-master": "1.3-dev" } }, "autoload": { @@ -1424,7 +1422,7 @@ ], "support": { "issues": "https://github.com/fruitcake/php-cors/issues", - "source": "https://github.com/fruitcake/php-cors/tree/v1.3.0" + "source": "https://github.com/fruitcake/php-cors/tree/v1.4.0" }, "funding": [ { @@ -1436,33 +1434,33 @@ "type": "github" } ], - "time": "2023-10-12T05:21:21+00:00" + "time": "2025-12-03T09:33:47+00:00" }, { "name": "geoip2/geoip2", - "version": "v3.2.0", + "version": "v3.3.0", "source": { "type": "git", "url": "https://github.com/maxmind/GeoIP2-php.git", - "reference": "b7aa58760a6bf89a608dd92ee2d9436b52557ce2" + "reference": "49fceddd694295e76e970a32848e03bb19e56b42" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/maxmind/GeoIP2-php/zipball/b7aa58760a6bf89a608dd92ee2d9436b52557ce2", - "reference": "b7aa58760a6bf89a608dd92ee2d9436b52557ce2", + "url": "https://api.github.com/repos/maxmind/GeoIP2-php/zipball/49fceddd694295e76e970a32848e03bb19e56b42", + "reference": "49fceddd694295e76e970a32848e03bb19e56b42", "shasum": "" }, "require": { "ext-json": "*", - "maxmind-db/reader": "^1.12.1", - "maxmind/web-service-common": "~0.10", + "maxmind-db/reader": "^1.13.0", + "maxmind/web-service-common": "~0.11", "php": ">=8.1" }, "require-dev": { "friendsofphp/php-cs-fixer": "3.*", "phpstan/phpstan": "*", "phpunit/phpunit": "^10.0", - "squizlabs/php_codesniffer": "3.*" + "squizlabs/php_codesniffer": "4.*" }, "type": "library", "autoload": { @@ -1492,30 +1490,30 @@ ], "support": { "issues": "https://github.com/maxmind/GeoIP2-php/issues", - "source": "https://github.com/maxmind/GeoIP2-php/tree/v3.2.0" + "source": "https://github.com/maxmind/GeoIP2-php/tree/v3.3.0" }, - "time": "2025-05-05T21:18:27+00:00" + "time": "2025-11-20T18:50:15+00:00" }, { "name": "graham-campbell/result-type", - "version": "v1.1.3", + "version": "v1.1.4", "source": { "type": "git", "url": "https://github.com/GrahamCampbell/Result-Type.git", - "reference": "3ba905c11371512af9d9bdd27d99b782216b6945" + "reference": "e01f4a821471308ba86aa202fed6698b6b695e3b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/3ba905c11371512af9d9bdd27d99b782216b6945", - "reference": "3ba905c11371512af9d9bdd27d99b782216b6945", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/e01f4a821471308ba86aa202fed6698b6b695e3b", + "reference": "e01f4a821471308ba86aa202fed6698b6b695e3b", "shasum": "" }, "require": { "php": "^7.2.5 || ^8.0", - "phpoption/phpoption": "^1.9.3" + "phpoption/phpoption": "^1.9.5" }, "require-dev": { - "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28" + "phpunit/phpunit": "^8.5.41 || ^9.6.22 || ^10.5.45 || ^11.5.7" }, "type": "library", "autoload": { @@ -1544,7 +1542,7 @@ ], "support": { "issues": "https://github.com/GrahamCampbell/Result-Type/issues", - "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.3" + "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.4" }, "funding": [ { @@ -1556,29 +1554,30 @@ "type": "tidelift" } ], - "time": "2024-07-20T21:45:45+00:00" + "time": "2025-12-27T19:43:20+00:00" }, { "name": "guzzlehttp/guzzle", - "version": "7.10.0", + "version": "7.11.0", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4" + "reference": "c987f8ce84b8434fa430795eca0f3430663da72b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", - "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/c987f8ce84b8434fa430795eca0f3430663da72b", + "reference": "c987f8ce84b8434fa430795eca0f3430663da72b", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^2.3", - "guzzlehttp/psr7": "^2.8", + "guzzlehttp/promises": "^2.5", + "guzzlehttp/psr7": "^2.11", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0", - "symfony/deprecation-contracts": "^2.2 || ^3.0" + "symfony/deprecation-contracts": "^2.5 || ^3.0", + "symfony/polyfill-php80": "^1.24" }, "provide": { "psr/http-client-implementation": "1.0" @@ -1587,8 +1586,9 @@ "bamarni/composer-bin-plugin": "^1.8.2", "ext-curl": "*", "guzzle/client-integration-tests": "3.0.2", + "guzzlehttp/test-server": "^0.4", "php-http/message-factory": "^1.1", - "phpunit/phpunit": "^8.5.39 || ^9.6.20", + "phpunit/phpunit": "^8.5.52 || ^9.6.34", "psr/log": "^1.1 || ^2.0 || ^3.0" }, "suggest": { @@ -1666,7 +1666,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.10.0" + "source": "https://github.com/guzzle/guzzle/tree/7.11.0" }, "funding": [ { @@ -1682,28 +1682,29 @@ "type": "tidelift" } ], - "time": "2025-08-23T22:36:01+00:00" + "time": "2026-06-02T12:40:51+00:00" }, { "name": "guzzlehttp/promises", - "version": "2.3.0", + "version": "2.5.0", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "481557b130ef3790cf82b713667b43030dc9c957" + "reference": "4360e982f87f5f258bf872d094647791db2f4c8e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/481557b130ef3790cf82b713667b43030dc9c957", - "reference": "481557b130ef3790cf82b713667b43030dc9c957", + "url": "https://api.github.com/repos/guzzle/promises/zipball/4360e982f87f5f258bf872d094647791db2f4c8e", + "reference": "4360e982f87f5f258bf872d094647791db2f4c8e", "shasum": "" }, "require": { - "php": "^7.2.5 || ^8.0" + "php": "^7.2.5 || ^8.0", + "symfony/deprecation-contracts": "^2.5 || ^3.0" }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.44 || ^9.6.25" + "phpunit/phpunit": "^8.5.52 || ^9.6.34" }, "type": "library", "extra": { @@ -1749,7 +1750,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.3.0" + "source": "https://github.com/guzzle/promises/tree/2.5.0" }, "funding": [ { @@ -1765,27 +1766,29 @@ "type": "tidelift" } ], - "time": "2025-08-22T14:34:08+00:00" + "time": "2026-06-02T12:23:43+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.8.0", + "version": "2.11.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "21dc724a0583619cd1652f673303492272778051" + "reference": "bbb5e61349fa5cb822b3e87842b951088b76b81f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/21dc724a0583619cd1652f673303492272778051", - "reference": "21dc724a0583619cd1652f673303492272778051", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/bbb5e61349fa5cb822b3e87842b951088b76b81f", + "reference": "bbb5e61349fa5cb822b3e87842b951088b76b81f", "shasum": "" }, "require": { "php": "^7.2.5 || ^8.0", "psr/http-factory": "^1.0", "psr/http-message": "^1.1 || ^2.0", - "ralouphie/getallheaders": "^3.0" + "ralouphie/getallheaders": "^3.0", + "symfony/deprecation-contracts": "^2.5 || ^3.0", + "symfony/polyfill-php80": "^1.24" }, "provide": { "psr/http-factory-implementation": "1.0", @@ -1793,8 +1796,9 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "http-interop/http-factory-tests": "0.9.0", - "phpunit/phpunit": "^8.5.44 || ^9.6.25" + "http-interop/http-factory-tests": "1.1.0", + "jshttp/mime-db": "1.54.0.1", + "phpunit/phpunit": "^8.5.52 || ^9.6.34" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" @@ -1865,7 +1869,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.8.0" + "source": "https://github.com/guzzle/psr7/tree/2.11.0" }, "funding": [ { @@ -1881,20 +1885,20 @@ "type": "tidelift" } ], - "time": "2025-08-23T21:21:41+00:00" + "time": "2026-06-02T12:30:48+00:00" }, { "name": "guzzlehttp/uri-template", - "version": "v1.0.5", + "version": "v1.0.6", "source": { "type": "git", "url": "https://github.com/guzzle/uri-template.git", - "reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1" + "reference": "eef7f87bab6f204eba3c39224d8075c70c637946" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/uri-template/zipball/4f4bbd4e7172148801e76e3decc1e559bdee34e1", - "reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1", + "url": "https://api.github.com/repos/guzzle/uri-template/zipball/eef7f87bab6f204eba3c39224d8075c70c637946", + "reference": "eef7f87bab6f204eba3c39224d8075c70c637946", "shasum": "" }, "require": { @@ -1903,7 +1907,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.44 || ^9.6.25", + "phpunit/phpunit": "^8.5.52 || ^9.6.34", "uri-template/tests": "1.0.0" }, "type": "library", @@ -1951,7 +1955,7 @@ ], "support": { "issues": "https://github.com/guzzle/uri-template/issues", - "source": "https://github.com/guzzle/uri-template/tree/v1.0.5" + "source": "https://github.com/guzzle/uri-template/tree/v1.0.6" }, "funding": [ { @@ -1967,53 +1971,109 @@ "type": "tidelift" } ], - "time": "2025-08-22T14:27:06+00:00" + "time": "2026-05-23T22:00:21+00:00" + }, + { + "name": "intervention/gif", + "version": "4.2.4", + "source": { + "type": "git", + "url": "https://github.com/Intervention/gif.git", + "reference": "c3598a16ebe7690cd55640c44144a9df383ea73c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Intervention/gif/zipball/c3598a16ebe7690cd55640c44144a9df383ea73c", + "reference": "c3598a16ebe7690cd55640c44144a9df383ea73c", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "phpstan/phpstan": "^2.1", + "phpunit/phpunit": "^10.0 || ^11.0 || ^12.0", + "slevomat/coding-standard": "~8.0", + "squizlabs/php_codesniffer": "^3.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "Intervention\\Gif\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Oliver Vogel", + "email": "oliver@intervention.io", + "homepage": "https://intervention.io/" + } + ], + "description": "Native PHP GIF Encoder/Decoder", + "homepage": "https://github.com/intervention/gif", + "keywords": [ + "animation", + "gd", + "gif", + "image" + ], + "support": { + "issues": "https://github.com/Intervention/gif/issues", + "source": "https://github.com/Intervention/gif/tree/4.2.4" + }, + "funding": [ + { + "url": "https://paypal.me/interventionio", + "type": "custom" + }, + { + "url": "https://github.com/Intervention", + "type": "github" + }, + { + "url": "https://ko-fi.com/interventionphp", + "type": "ko_fi" + } + ], + "time": "2026-01-04T09:27:23+00:00" }, { "name": "intervention/image", - "version": "2.7.2", + "version": "3.11.8", "source": { "type": "git", "url": "https://github.com/Intervention/image.git", - "reference": "04be355f8d6734c826045d02a1079ad658322dad" + "reference": "cf04c8dd245697f701057c13d4bfe140d584e738" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Intervention/image/zipball/04be355f8d6734c826045d02a1079ad658322dad", - "reference": "04be355f8d6734c826045d02a1079ad658322dad", + "url": "https://api.github.com/repos/Intervention/image/zipball/cf04c8dd245697f701057c13d4bfe140d584e738", + "reference": "cf04c8dd245697f701057c13d4bfe140d584e738", "shasum": "" }, "require": { - "ext-fileinfo": "*", - "guzzlehttp/psr7": "~1.1 || ^2.0", - "php": ">=5.4.0" + "ext-mbstring": "*", + "intervention/gif": "^4.2", + "php": "^8.1" }, "require-dev": { - "mockery/mockery": "~0.9.2", - "phpunit/phpunit": "^4.8 || ^5.7 || ^7.5.15" + "mockery/mockery": "^1.6", + "phpstan/phpstan": "^2.1", + "phpunit/phpunit": "^10.0 || ^11.0 || ^12.0", + "slevomat/coding-standard": "~8.0", + "squizlabs/php_codesniffer": "^4" }, "suggest": { - "ext-gd": "to use GD library based image processing.", - "ext-imagick": "to use Imagick based image processing.", - "intervention/imagecache": "Caching extension for the Intervention Image library" + "ext-exif": "Recommended to be able to read EXIF data properly." }, "type": "library", - "extra": { - "laravel": { - "aliases": { - "Image": "Intervention\\Image\\Facades\\Image" - }, - "providers": [ - "Intervention\\Image\\ImageServiceProvider" - ] - }, - "branch-alias": { - "dev-master": "2.4-dev" - } - }, "autoload": { "psr-4": { - "Intervention\\Image\\": "src/Intervention/Image" + "Intervention\\Image\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -2024,22 +2084,22 @@ { "name": "Oliver Vogel", "email": "oliver@intervention.io", - "homepage": "https://intervention.io/" + "homepage": "https://intervention.io" } ], - "description": "Image handling and manipulation library with support for Laravel integration", - "homepage": "http://image.intervention.io/", + "description": "PHP Image Processing", + "homepage": "https://image.intervention.io", "keywords": [ "gd", "image", "imagick", - "laravel", + "resize", "thumbnail", "watermark" ], "support": { "issues": "https://github.com/Intervention/image/issues", - "source": "https://github.com/Intervention/image/tree/2.7.2" + "source": "https://github.com/Intervention/image/tree/3.11.8" }, "funding": [ { @@ -2049,39 +2109,54 @@ { "url": "https://github.com/Intervention", "type": "github" + }, + { + "url": "https://ko-fi.com/interventionphp", + "type": "ko_fi" } ], - "time": "2022-05-21T17:30:32+00:00" + "time": "2026-05-01T08:20:10+00:00" }, { - "name": "intervention/imagecache", - "version": "2.6.0", + "name": "intervention/image-laravel", + "version": "1.5.9", "source": { "type": "git", - "url": "https://github.com/Intervention/imagecache.git", - "reference": "86136575a62d3634b51f196a998fce4a583b49bb" + "url": "https://github.com/Intervention/image-laravel.git", + "reference": "a760b041e5133fd81509414f4955c93ffefb4a7b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Intervention/imagecache/zipball/86136575a62d3634b51f196a998fce4a583b49bb", - "reference": "86136575a62d3634b51f196a998fce4a583b49bb", + "url": "https://api.github.com/repos/Intervention/image-laravel/zipball/a760b041e5133fd81509414f4955c93ffefb4a7b", + "reference": "a760b041e5133fd81509414f4955c93ffefb4a7b", "shasum": "" }, "require": { - "illuminate/cache": "^5.5|~6|~7|~8|~9|~10", - "illuminate/filesystem": "^5.5|~6|~7|~8|~9|~10", - "intervention/image": "~2.2", - "nesbot/carbon": "^2.39", - "opis/closure": "^3.5", - "php": "~7.2|~8" + "illuminate/http": "^8|^9|^10|^11|^12|^13", + "illuminate/routing": "^8|^9|^10|^11|^12|^13", + "illuminate/support": "^8|^9|^10|^11|^12|^13", + "intervention/image": "^3.11", + "php": "^8.1" }, "require-dev": { - "phpunit/phpunit": "^8.0" + "ext-fileinfo": "*", + "orchestra/testbench": "^8.18 || ^9.9 || ^10.6", + "phpunit/phpunit": "^10.0 || ^11.0 || ^12.0" }, "type": "library", + "extra": { + "laravel": { + "aliases": { + "Image": "Intervention\\Image\\Laravel\\Facades\\Image" + }, + "providers": [ + "Intervention\\Image\\Laravel\\ServiceProvider" + ] + } + }, "autoload": { "psr-4": { - "Intervention\\Image\\": "src/Intervention/Image" + "Intervention\\Image\\Laravel\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -2092,21 +2167,23 @@ { "name": "Oliver Vogel", "email": "oliver@intervention.io", - "homepage": "http://intervention.io/" + "homepage": "https://intervention.io/" } ], - "description": "Caching extension for the Intervention Image Class", - "homepage": "https://image.intervention.io", + "description": "Laravel Integration of Intervention Image", + "homepage": "https://image.intervention.io/", "keywords": [ - "cache", "gd", "image", "imagick", - "laravel" + "laravel", + "resize", + "thumbnail", + "watermark" ], "support": { - "issues": "https://github.com/Intervention/imagecache/issues", - "source": "https://github.com/Intervention/imagecache/tree/2.6.0" + "issues": "https://github.com/Intervention/image-laravel/issues", + "source": "https://github.com/Intervention/image-laravel/tree/1.5.9" }, "funding": [ { @@ -2116,30 +2193,33 @@ { "url": "https://github.com/Intervention", "type": "github" + }, + { + "url": "https://ko-fi.com/interventionphp", + "type": "ko_fi" } ], - "abandoned": true, - "time": "2023-02-25T19:40:47+00:00" + "time": "2026-03-24T15:10:30+00:00" }, { "name": "jaybizzle/crawler-detect", - "version": "v1.3.5", + "version": "v1.3.11", "source": { "type": "git", "url": "https://github.com/JayBizzle/Crawler-Detect.git", - "reference": "fbf1a3e81d61b088e7af723fb3c7a4ee92ac7e34" + "reference": "484792759de89fe94ea6a192065ea7cd99f1eaa2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/JayBizzle/Crawler-Detect/zipball/fbf1a3e81d61b088e7af723fb3c7a4ee92ac7e34", - "reference": "fbf1a3e81d61b088e7af723fb3c7a4ee92ac7e34", + "url": "https://api.github.com/repos/JayBizzle/Crawler-Detect/zipball/484792759de89fe94ea6a192065ea7cd99f1eaa2", + "reference": "484792759de89fe94ea6a192065ea7cd99f1eaa2", "shasum": "" }, "require": { "php": ">=7.1.0" }, "require-dev": { - "phpunit/phpunit": "^4.8|^5.5|^6.5|^9.4" + "phpunit/phpunit": "^4.8|^5.5|^6.5|^7.5|^8.5.52|^9.4" }, "type": "library", "autoload": { @@ -2169,22 +2249,22 @@ ], "support": { "issues": "https://github.com/JayBizzle/Crawler-Detect/issues", - "source": "https://github.com/JayBizzle/Crawler-Detect/tree/v1.3.5" + "source": "https://github.com/JayBizzle/Crawler-Detect/tree/v1.3.11" }, - "time": "2025-06-11T17:58:05+00:00" + "time": "2026-05-10T14:08:06+00:00" }, { "name": "jeroennoten/laravel-adminlte", - "version": "v3.15.2", + "version": "v3.16.0", "source": { "type": "git", "url": "https://github.com/jeroennoten/Laravel-AdminLTE.git", - "reference": "8f2fe5f6767b69ec5ef28087a10638b9f50e58e7" + "reference": "dc92e92a9c8ac7443a82c97310e347ad62d971e4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/jeroennoten/Laravel-AdminLTE/zipball/8f2fe5f6767b69ec5ef28087a10638b9f50e58e7", - "reference": "8f2fe5f6767b69ec5ef28087a10638b9f50e58e7", + "url": "https://api.github.com/repos/jeroennoten/Laravel-AdminLTE/zipball/dc92e92a9c8ac7443a82c97310e347ad62d971e4", + "reference": "dc92e92a9c8ac7443a82c97310e347ad62d971e4", "shasum": "" }, "require": { @@ -2228,35 +2308,35 @@ ], "support": { "issues": "https://github.com/jeroennoten/Laravel-AdminLTE/issues", - "source": "https://github.com/jeroennoten/Laravel-AdminLTE/tree/v3.15.2" + "source": "https://github.com/jeroennoten/Laravel-AdminLTE/tree/v3.16.0" }, - "time": "2025-08-12T18:28:10+00:00" + "time": "2026-04-30T07:28:25+00:00" }, { "name": "josiasmontag/laravel-recaptchav3", - "version": "1.0.4", + "version": "1.0.5", "source": { "type": "git", "url": "https://github.com/josiasmontag/laravel-recaptchav3.git", - "reference": "08548b818223a20fc7db04a8d060758f8efc4ef5" + "reference": "97a055fff91cd1eef0e1f0309489ea8269a487d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/josiasmontag/laravel-recaptchav3/zipball/08548b818223a20fc7db04a8d060758f8efc4ef5", - "reference": "08548b818223a20fc7db04a8d060758f8efc4ef5", + "url": "https://api.github.com/repos/josiasmontag/laravel-recaptchav3/zipball/97a055fff91cd1eef0e1f0309489ea8269a487d0", + "reference": "97a055fff91cd1eef0e1f0309489ea8269a487d0", "shasum": "" }, "require": { "guzzlehttp/guzzle": "^6.2|^7.0", - "illuminate/container": "~5.7.0|~5.8.0|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", - "illuminate/http": "~5.7.0|~5.8.0|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", - "illuminate/support": "~5.7.0|~5.8.0|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/container": "~5.7.0|~5.8.0|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/http": "~5.7.0|~5.8.0|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/support": "~5.7.0|~5.8.0|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", "php": ">=7.1.0" }, "require-dev": { "mockery/mockery": "^1.2", - "orchestra/testbench": "~3.7.0|~3.8.0|^4.0|^5.0|^6.0|^7.0|^8.0|^9.0|^10.0", - "phpunit/phpunit": "6.2|^7.0|^8.0|^9.5.10|^10.5|^11.5.3" + "orchestra/testbench": "~3.7.0|~3.8.0|^4.0|^5.0|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", + "phpunit/phpunit": "6.2|^7.0|^8.0|^9.5.10|^10.5|^11.5.3|^12.5.12" }, "type": "library", "extra": { @@ -2294,32 +2374,32 @@ ], "support": { "issues": "https://github.com/josiasmontag/laravel-recaptchav3/issues", - "source": "https://github.com/josiasmontag/laravel-recaptchav3/tree/1.0.4" + "source": "https://github.com/josiasmontag/laravel-recaptchav3/tree/1.0.5" }, - "time": "2025-02-25T08:00:22+00:00" + "time": "2026-02-24T15:20:53+00:00" }, { "name": "kalnoy/nestedset", - "version": "v6.0.6", + "version": "v7.0.0", "source": { "type": "git", "url": "https://github.com/lazychaser/laravel-nestedset.git", - "reference": "3cfc56a9759fb592bc903056166bfc0867f9e679" + "reference": "4e9ad66a3c6a8867dd69eea53e7954c3b150af6d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/lazychaser/laravel-nestedset/zipball/3cfc56a9759fb592bc903056166bfc0867f9e679", - "reference": "3cfc56a9759fb592bc903056166bfc0867f9e679", + "url": "https://api.github.com/repos/lazychaser/laravel-nestedset/zipball/4e9ad66a3c6a8867dd69eea53e7954c3b150af6d", + "reference": "4e9ad66a3c6a8867dd69eea53e7954c3b150af6d", "shasum": "" }, "require": { - "illuminate/database": "^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", - "illuminate/events": "^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", - "illuminate/support": "^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/database": ">=13.0", + "illuminate/events": ">=13.0", + "illuminate/support": ">=13.0", "php": "^8.0" }, "require-dev": { - "phpunit/phpunit": "7.*|8.*|9.*|^10.5" + "phpunit/phpunit": ">=7.0" }, "type": "library", "extra": { @@ -2357,33 +2437,33 @@ ], "support": { "issues": "https://github.com/lazychaser/laravel-nestedset/issues", - "source": "https://github.com/lazychaser/laravel-nestedset/tree/v6.0.6" + "source": "https://github.com/lazychaser/laravel-nestedset/tree/v7.0.0" }, - "time": "2025-04-22T19:38:02+00:00" + "time": "2026-04-11T13:45:58+00:00" }, { "name": "laminas/laminas-code", - "version": "4.16.0", + "version": "4.17.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-code.git", - "reference": "1793e78dad4108b594084d05d1fb818b85b110af" + "reference": "40d61e2899ec17c5d08bbc0a2d586b3ca17ab9bd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-code/zipball/1793e78dad4108b594084d05d1fb818b85b110af", - "reference": "1793e78dad4108b594084d05d1fb818b85b110af", + "url": "https://api.github.com/repos/laminas/laminas-code/zipball/40d61e2899ec17c5d08bbc0a2d586b3ca17ab9bd", + "reference": "40d61e2899ec17c5d08bbc0a2d586b3ca17ab9bd", "shasum": "" }, "require": { - "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" }, "require-dev": { "doctrine/annotations": "^2.0.1", "ext-phar": "*", "laminas/laminas-coding-standard": "^3.0.0", "laminas/laminas-stdlib": "^3.18.0", - "phpunit/phpunit": "^10.5.37", + "phpunit/phpunit": "^10.5.58", "psalm/plugin-phpunit": "^0.19.0", "vimeo/psalm": "^5.15.0" }, @@ -2422,28 +2502,28 @@ "type": "community_bridge" } ], - "time": "2024-11-20T13:15:13+00:00" + "time": "2025-11-01T09:38:14+00:00" }, { "name": "laravel/framework", - "version": "v10.48.29", + "version": "v13.13.0", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "8f7f9247cb8aad1a769d6b9815a6623d89b46b47" + "reference": "1daa6d3b4defe46976ccfa4fb0a7ab62717712a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/8f7f9247cb8aad1a769d6b9815a6623d89b46b47", - "reference": "8f7f9247cb8aad1a769d6b9815a6623d89b46b47", + "url": "https://api.github.com/repos/laravel/framework/zipball/1daa6d3b4defe46976ccfa4fb0a7ab62717712a2", + "reference": "1daa6d3b4defe46976ccfa4fb0a7ab62717712a2", "shasum": "" }, "require": { - "brick/math": "^0.9.3|^0.10.2|^0.11|^0.12", + "brick/math": "^0.14.2 || ^0.15 || ^0.16 || ^0.17", "composer-runtime-api": "^2.2", "doctrine/inflector": "^2.0.5", - "dragonmantank/cron-expression": "^3.3.2", - "egulias/email-validator": "^3.2.1|^4.0", + "dragonmantank/cron-expression": "^3.4", + "egulias/email-validator": "^4.0", "ext-ctype": "*", "ext-filter": "*", "ext-hash": "*", @@ -2451,45 +2531,49 @@ "ext-openssl": "*", "ext-session": "*", "ext-tokenizer": "*", - "fruitcake/php-cors": "^1.2", + "fruitcake/php-cors": "^1.3", + "guzzlehttp/guzzle": "^7.8.2", + "guzzlehttp/promises": "^2.0.3", "guzzlehttp/uri-template": "^1.0", - "laravel/prompts": "^0.1.9", - "laravel/serializable-closure": "^1.3", - "league/commonmark": "^2.2.1", - "league/flysystem": "^3.8.0", + "laravel/prompts": "^0.3.0", + "laravel/serializable-closure": "^2.0.10", + "league/commonmark": "^2.8.1", + "league/flysystem": "^3.25.1", + "league/flysystem-local": "^3.25.1", + "league/uri": "^7.5.1", "monolog/monolog": "^3.0", - "nesbot/carbon": "^2.67", - "nunomaduro/termwind": "^1.13", - "php": "^8.1", - "psr/container": "^1.1.1|^2.0.1", - "psr/log": "^1.0|^2.0|^3.0", - "psr/simple-cache": "^1.0|^2.0|^3.0", + "nesbot/carbon": "^3.8.4", + "nunomaduro/termwind": "^2.0", + "php": "^8.3", + "psr/container": "^1.1.1 || ^2.0.1", + "psr/log": "^1.0 || ^2.0 || ^3.0", + "psr/simple-cache": "^1.0 || ^2.0 || ^3.0", "ramsey/uuid": "^4.7", - "symfony/console": "^6.2", - "symfony/error-handler": "^6.2", - "symfony/finder": "^6.2", - "symfony/http-foundation": "^6.4", - "symfony/http-kernel": "^6.2", - "symfony/mailer": "^6.2", - "symfony/mime": "^6.2", - "symfony/process": "^6.2", - "symfony/routing": "^6.2", - "symfony/uid": "^6.2", - "symfony/var-dumper": "^6.2", + "symfony/console": "^7.4.0 || ^8.0.0", + "symfony/error-handler": "^7.4.0 || ^8.0.0", + "symfony/finder": "^7.4.0 || ^8.0.0", + "symfony/http-foundation": "^7.4.0 || ^8.0.0", + "symfony/http-kernel": "^7.4.0 || ^8.0.0", + "symfony/mailer": "^7.4.0 || ^8.0.0", + "symfony/mime": "^7.4.0 || ^8.0.0", + "symfony/polyfill-php84": "^1.36", + "symfony/polyfill-php85": "^1.36", + "symfony/polyfill-php86": "^1.36", + "symfony/process": "^7.4.5 || ^8.0.5", + "symfony/routing": "^7.4.0 || ^8.0.0", + "symfony/uid": "^7.4.0 || ^8.0.0", + "symfony/var-dumper": "^7.4.0 || ^8.0.0", "tijsverkoyen/css-to-inline-styles": "^2.2.5", - "vlucas/phpdotenv": "^5.4.1", - "voku/portable-ascii": "^2.0" + "vlucas/phpdotenv": "^5.6.1", + "voku/portable-ascii": "^2.0.2" }, "conflict": { - "carbonphp/carbon-doctrine-types": ">=3.0", - "doctrine/dbal": ">=4.0", - "mockery/mockery": "1.6.8", - "phpunit/phpunit": ">=11.0.0", "tightenco/collect": "<5.5.33" }, "provide": { - "psr/container-implementation": "1.1|2.0", - "psr/simple-cache-implementation": "1.0|2.0|3.0" + "psr/container-implementation": "1.1 || 2.0", + "psr/log-implementation": "1.0 || 2.0 || 3.0", + "psr/simple-cache-implementation": "1.0 || 2.0 || 3.0" }, "replace": { "illuminate/auth": "self.version", @@ -2497,6 +2581,7 @@ "illuminate/bus": "self.version", "illuminate/cache": "self.version", "illuminate/collections": "self.version", + "illuminate/concurrency": "self.version", "illuminate/conditionable": "self.version", "illuminate/config": "self.version", "illuminate/console": "self.version", @@ -2509,6 +2594,7 @@ "illuminate/filesystem": "self.version", "illuminate/hashing": "self.version", "illuminate/http": "self.version", + "illuminate/json-schema": "self.version", "illuminate/log": "self.version", "illuminate/macroable": "self.version", "illuminate/mail": "self.version", @@ -2518,42 +2604,47 @@ "illuminate/process": "self.version", "illuminate/queue": "self.version", "illuminate/redis": "self.version", + "illuminate/reflection": "self.version", "illuminate/routing": "self.version", "illuminate/session": "self.version", "illuminate/support": "self.version", "illuminate/testing": "self.version", "illuminate/translation": "self.version", "illuminate/validation": "self.version", - "illuminate/view": "self.version" + "illuminate/view": "self.version", + "spatie/once": "*" }, "require-dev": { "ably/ably-php": "^1.0", - "aws/aws-sdk-php": "^3.235.5", - "doctrine/dbal": "^3.5.1", + "aws/aws-sdk-php": "^3.322.9", "ext-gmp": "*", - "fakerphp/faker": "^1.21", - "guzzlehttp/guzzle": "^7.5", - "league/flysystem-aws-s3-v3": "^3.0", - "league/flysystem-ftp": "^3.0", - "league/flysystem-path-prefixing": "^3.3", - "league/flysystem-read-only": "^3.3", - "league/flysystem-sftp-v3": "^3.0", - "mockery/mockery": "^1.5.1", - "nyholm/psr7": "^1.2", - "orchestra/testbench-core": "^8.23.4", - "pda/pheanstalk": "^4.0", - "phpstan/phpstan": "~1.11.11", - "phpunit/phpunit": "^10.0.7", - "predis/predis": "^2.0.2", - "symfony/cache": "^6.2", - "symfony/http-client": "^6.2.4", - "symfony/psr-http-message-bridge": "^2.0" + "fakerphp/faker": "^1.24", + "guzzlehttp/psr7": "^2.9", + "laravel/pint": "^1.18", + "league/flysystem-aws-s3-v3": "^3.25.1", + "league/flysystem-ftp": "^3.25.1", + "league/flysystem-path-prefixing": "^3.25.1", + "league/flysystem-read-only": "^3.25.1", + "league/flysystem-sftp-v3": "^3.25.1", + "mockery/mockery": "^1.6.10", + "opis/json-schema": "^2.4.1", + "orchestra/testbench-core": "^11.0.0", + "pda/pheanstalk": "^7.0.0 || ^8.0.0", + "php-http/discovery": "^1.15", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^11.5.50 || ^12.5.8 || ^13.0.3", + "predis/predis": "^2.3 || ^3.0", + "rector/rector": "^2.3", + "resend/resend-php": "^1.0", + "symfony/cache": "^7.4.0 || ^8.0.0", + "symfony/http-client": "^7.4.0 || ^8.0.0", + "symfony/psr-http-message-bridge": "^7.4.0 || ^8.0.0", + "symfony/translation": "^7.4.0 || ^8.0.0" }, "suggest": { "ably/ably-php": "Required to use the Ably broadcast driver (^1.0).", - "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage, and SES mail driver (^3.235.5).", - "brianium/paratest": "Required to run tests in parallel (^6.0).", - "doctrine/dbal": "Required to rename columns and drop SQLite columns (^3.5.1).", + "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage, and SES mail driver (^3.322.9).", + "brianium/paratest": "Required to run tests in parallel (^7.0 || ^8.0).", "ext-apcu": "Required to use the APC cache driver.", "ext-fileinfo": "Required to use the Filesystem class.", "ext-ftp": "Required to use the Flysystem FTP driver.", @@ -2562,42 +2653,47 @@ "ext-pcntl": "Required to use all features of the queue worker and console signal trapping.", "ext-pdo": "Required to use all database features.", "ext-posix": "Required to use all features of the queue worker.", - "ext-redis": "Required to use the Redis cache and queue drivers (^4.0|^5.0).", - "fakerphp/faker": "Required to use the eloquent factory builder (^1.9.1).", + "ext-redis": "Required to use the Redis cache and queue drivers (^4.0 || ^5.0 || ^6.0).", + "fakerphp/faker": "Required to generate fake data using the fake() helper (^1.23).", "filp/whoops": "Required for friendly error pages in development (^2.14.3).", - "guzzlehttp/guzzle": "Required to use the HTTP Client and the ping methods on schedules (^7.5).", "laravel/tinker": "Required to use the tinker console command (^2.0).", - "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.0).", - "league/flysystem-ftp": "Required to use the Flysystem FTP driver (^3.0).", - "league/flysystem-path-prefixing": "Required to use the scoped driver (^3.3).", - "league/flysystem-read-only": "Required to use read-only disks (^3.3)", - "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.0).", - "mockery/mockery": "Required to use mocking (^1.5.1).", - "nyholm/psr7": "Required to use PSR-7 bridging features (^1.2).", - "pda/pheanstalk": "Required to use the beanstalk queue driver (^4.0).", - "phpunit/phpunit": "Required to use assertions and run tests (^9.5.8|^10.0.7).", - "predis/predis": "Required to use the predis connector (^2.0.2).", + "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.25.1).", + "league/flysystem-ftp": "Required to use the Flysystem FTP driver (^3.25.1).", + "league/flysystem-path-prefixing": "Required to use the scoped driver (^3.25.1).", + "league/flysystem-read-only": "Required to use read-only disks (^3.25.1)", + "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.25.1).", + "mockery/mockery": "Required to use mocking (^1.6).", + "pda/pheanstalk": "Required to use the beanstalk queue driver (^7.0 || ^8.0).", + "php-http/discovery": "Required to use PSR-7 bridging features (^1.15).", + "phpunit/phpunit": "Required to use assertions and run tests (^11.5.50 || ^12.5.8 || ^13.0.3).", + "predis/predis": "Required to use the predis connector (^2.3 || ^3.0).", "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", - "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^6.0|^7.0).", - "symfony/cache": "Required to PSR-6 cache bridge (^6.2).", - "symfony/filesystem": "Required to enable support for relative symbolic links (^6.2).", - "symfony/http-client": "Required to enable support for the Symfony API mail transports (^6.2).", - "symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^6.2).", - "symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^6.2).", - "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^2.0)." + "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^6.0 || ^7.0).", + "resend/resend-php": "Required to enable support for the Resend mail transport (^0.10.0 || ^1.0).", + "spatie/fork": "Required to use the 'fork' concurrency driver (^1.2).", + "symfony/cache": "Required to PSR-6 cache bridge (^7.4 || ^8.0).", + "symfony/filesystem": "Required to enable support for relative symbolic links (^7.4 || ^8.0).", + "symfony/http-client": "Required to enable support for the Symfony API mail transports (^7.4 || ^8.0).", + "symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^7.4 || ^8.0).", + "symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^7.4 || ^8.0).", + "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^7.4 || ^8.0)." }, "type": "library", "extra": { "branch-alias": { - "dev-master": "10.x-dev" + "dev-master": "13.0.x-dev" } }, "autoload": { "files": [ + "src/Illuminate/Collections/functions.php", "src/Illuminate/Collections/helpers.php", "src/Illuminate/Events/functions.php", "src/Illuminate/Filesystem/functions.php", "src/Illuminate/Foundation/helpers.php", + "src/Illuminate/Log/functions.php", + "src/Illuminate/Reflection/helpers.php", + "src/Illuminate/Support/functions.php", "src/Illuminate/Support/helpers.php" ], "psr-4": { @@ -2605,7 +2701,8 @@ "Illuminate\\Support\\": [ "src/Illuminate/Macroable/", "src/Illuminate/Collections/", - "src/Illuminate/Conditionable/" + "src/Illuminate/Conditionable/", + "src/Illuminate/Reflection/" ] } }, @@ -2629,37 +2726,38 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-03-12T14:42:01+00:00" + "time": "2026-06-02T14:28:17+00:00" }, { "name": "laravel/prompts", - "version": "v0.1.25", + "version": "v0.3.18", "source": { "type": "git", "url": "https://github.com/laravel/prompts.git", - "reference": "7b4029a84c37cb2725fc7f011586e2997040bc95" + "reference": "a19af51bb144bf87f08397921fa619f85c7d4e72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/prompts/zipball/7b4029a84c37cb2725fc7f011586e2997040bc95", - "reference": "7b4029a84c37cb2725fc7f011586e2997040bc95", + "url": "https://api.github.com/repos/laravel/prompts/zipball/a19af51bb144bf87f08397921fa619f85c7d4e72", + "reference": "a19af51bb144bf87f08397921fa619f85c7d4e72", "shasum": "" }, "require": { + "composer-runtime-api": "^2.2", "ext-mbstring": "*", - "illuminate/collections": "^10.0|^11.0", "php": "^8.1", - "symfony/console": "^6.2|^7.0" + "symfony/console": "^6.2|^7.0|^8.0" }, "conflict": { "illuminate/console": ">=10.17.0 <10.25.0", "laravel/framework": ">=10.17.0 <10.25.0" }, "require-dev": { + "illuminate/collections": "^10.0|^11.0|^12.0|^13.0", "mockery/mockery": "^1.5", - "pestphp/pest": "^2.3", - "phpstan/phpstan": "^1.11", - "phpstan/phpstan-mockery": "^1.1" + "pestphp/pest": "^2.3|^3.4|^4.0", + "phpstan/phpstan": "^1.12.28", + "phpstan/phpstan-mockery": "^1.1.3" }, "suggest": { "ext-pcntl": "Required for the spinner to be animated." @@ -2667,7 +2765,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "0.1.x-dev" + "dev-main": "0.3.x-dev" } }, "autoload": { @@ -2685,37 +2783,37 @@ "description": "Add beautiful and user-friendly forms to your command-line applications.", "support": { "issues": "https://github.com/laravel/prompts/issues", - "source": "https://github.com/laravel/prompts/tree/v0.1.25" + "source": "https://github.com/laravel/prompts/tree/v0.3.18" }, - "time": "2024-08-12T22:06:33+00:00" + "time": "2026-05-19T00:47:18+00:00" }, { "name": "laravel/sanctum", - "version": "v3.3.3", + "version": "v4.3.2", "source": { "type": "git", "url": "https://github.com/laravel/sanctum.git", - "reference": "8c104366459739f3ada0e994bcd3e6fd681ce3d5" + "reference": "2a9bccc18e9907808e0018dd15fa643937886b1e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/sanctum/zipball/8c104366459739f3ada0e994bcd3e6fd681ce3d5", - "reference": "8c104366459739f3ada0e994bcd3e6fd681ce3d5", + "url": "https://api.github.com/repos/laravel/sanctum/zipball/2a9bccc18e9907808e0018dd15fa643937886b1e", + "reference": "2a9bccc18e9907808e0018dd15fa643937886b1e", "shasum": "" }, "require": { "ext-json": "*", - "illuminate/console": "^9.21|^10.0", - "illuminate/contracts": "^9.21|^10.0", - "illuminate/database": "^9.21|^10.0", - "illuminate/support": "^9.21|^10.0", - "php": "^8.0.2" + "illuminate/console": "^11.0|^12.0|^13.0", + "illuminate/contracts": "^11.0|^12.0|^13.0", + "illuminate/database": "^11.0|^12.0|^13.0", + "illuminate/support": "^11.0|^12.0|^13.0", + "php": "^8.2", + "symfony/console": "^7.0|^8.0" }, "require-dev": { - "mockery/mockery": "^1.0", - "orchestra/testbench": "^7.28.2|^8.8.3", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.6" + "mockery/mockery": "^1.6", + "orchestra/testbench": "^9.15|^10.8|^11.0", + "phpstan/phpstan": "^1.10" }, "type": "library", "extra": { @@ -2723,9 +2821,6 @@ "providers": [ "Laravel\\Sanctum\\SanctumServiceProvider" ] - }, - "branch-alias": { - "dev-master": "3.x-dev" } }, "autoload": { @@ -2753,36 +2848,36 @@ "issues": "https://github.com/laravel/sanctum/issues", "source": "https://github.com/laravel/sanctum" }, - "time": "2023-12-19T18:44:48+00:00" + "time": "2026-04-30T11:46:25+00:00" }, { "name": "laravel/serializable-closure", - "version": "v1.3.7", + "version": "v2.0.13", "source": { "type": "git", "url": "https://github.com/laravel/serializable-closure.git", - "reference": "4f48ade902b94323ca3be7646db16209ec76be3d" + "reference": "b566ee0dd251f3c4078bed003a7ce015f5ea6dce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/4f48ade902b94323ca3be7646db16209ec76be3d", - "reference": "4f48ade902b94323ca3be7646db16209ec76be3d", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/b566ee0dd251f3c4078bed003a7ce015f5ea6dce", + "reference": "b566ee0dd251f3c4078bed003a7ce015f5ea6dce", "shasum": "" }, "require": { - "php": "^7.3|^8.0" + "php": "^8.1" }, "require-dev": { - "illuminate/support": "^8.0|^9.0|^10.0|^11.0", - "nesbot/carbon": "^2.61|^3.0", - "pestphp/pest": "^1.21.3", - "phpstan/phpstan": "^1.8.2", - "symfony/var-dumper": "^5.4.11|^6.2.0|^7.0.0" + "illuminate/support": "^10.0|^11.0|^12.0|^13.0", + "nesbot/carbon": "^2.67|^3.0", + "pestphp/pest": "^2.36|^3.0|^4.0", + "phpstan/phpstan": "^2.0", + "symfony/var-dumper": "^6.2.0|^7.0.0|^8.0.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.x-dev" + "dev-master": "2.x-dev" } }, "autoload": { @@ -2814,37 +2909,37 @@ "issues": "https://github.com/laravel/serializable-closure/issues", "source": "https://github.com/laravel/serializable-closure" }, - "time": "2024-11-14T18:34:49+00:00" + "time": "2026-04-16T14:03:50+00:00" }, { "name": "laravel/tinker", - "version": "v2.10.1", + "version": "v3.0.2", "source": { "type": "git", "url": "https://github.com/laravel/tinker.git", - "reference": "22177cc71807d38f2810c6204d8f7183d88a57d3" + "reference": "4faba77764bd33411735936acdf30446d058c78b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/tinker/zipball/22177cc71807d38f2810c6204d8f7183d88a57d3", - "reference": "22177cc71807d38f2810c6204d8f7183d88a57d3", + "url": "https://api.github.com/repos/laravel/tinker/zipball/4faba77764bd33411735936acdf30446d058c78b", + "reference": "4faba77764bd33411735936acdf30446d058c78b", "shasum": "" }, "require": { - "illuminate/console": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", - "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", - "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", - "php": "^7.2.5|^8.0", - "psy/psysh": "^0.11.1|^0.12.0", - "symfony/var-dumper": "^4.3.4|^5.0|^6.0|^7.0" + "illuminate/console": "^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/contracts": "^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/support": "^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", + "php": "^8.1", + "psy/psysh": "^0.12.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0|^8.0" }, "require-dev": { "mockery/mockery": "~1.3.3|^1.4.2", "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^8.5.8|^9.3.3|^10.0" + "phpunit/phpunit": "^10.5|^11.5" }, "suggest": { - "illuminate/database": "The Illuminate Database package (^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0)." + "illuminate/database": "The Illuminate Database package (^8.0|^9.0|^10.0|^11.0|^12.0|^13.0)." }, "type": "library", "extra": { @@ -2852,6 +2947,9 @@ "providers": [ "Laravel\\Tinker\\TinkerServiceProvider" ] + }, + "branch-alias": { + "dev-master": "3.x-dev" } }, "autoload": { @@ -2878,35 +2976,35 @@ ], "support": { "issues": "https://github.com/laravel/tinker/issues", - "source": "https://github.com/laravel/tinker/tree/v2.10.1" + "source": "https://github.com/laravel/tinker/tree/v3.0.2" }, - "time": "2025-01-27T14:24:01+00:00" + "time": "2026-03-17T14:54:13+00:00" }, { "name": "laravel/ui", - "version": "v4.6.1", + "version": "v4.6.3", "source": { "type": "git", "url": "https://github.com/laravel/ui.git", - "reference": "7d6ffa38d79f19c9b3e70a751a9af845e8f41d88" + "reference": "ff27db15416c1ed8ad9848f5692e47595dd5de27" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/ui/zipball/7d6ffa38d79f19c9b3e70a751a9af845e8f41d88", - "reference": "7d6ffa38d79f19c9b3e70a751a9af845e8f41d88", + "url": "https://api.github.com/repos/laravel/ui/zipball/ff27db15416c1ed8ad9848f5692e47595dd5de27", + "reference": "ff27db15416c1ed8ad9848f5692e47595dd5de27", "shasum": "" }, "require": { - "illuminate/console": "^9.21|^10.0|^11.0|^12.0", - "illuminate/filesystem": "^9.21|^10.0|^11.0|^12.0", - "illuminate/support": "^9.21|^10.0|^11.0|^12.0", - "illuminate/validation": "^9.21|^10.0|^11.0|^12.0", + "illuminate/console": "^9.21|^10.0|^11.0|^12.0|^13.0", + "illuminate/filesystem": "^9.21|^10.0|^11.0|^12.0|^13.0", + "illuminate/support": "^9.21|^10.0|^11.0|^12.0|^13.0", + "illuminate/validation": "^9.21|^10.0|^11.0|^12.0|^13.0", "php": "^8.0", - "symfony/console": "^6.0|^7.0" + "symfony/console": "^6.0|^7.0|^8.0" }, "require-dev": { - "orchestra/testbench": "^7.35|^8.15|^9.0|^10.0", - "phpunit/phpunit": "^9.3|^10.4|^11.5" + "orchestra/testbench": "^7.35|^8.15|^9.0|^10.0|^11.0", + "phpunit/phpunit": "^9.3|^10.4|^11.5|^12.5|^13.0" }, "type": "library", "extra": { @@ -2941,22 +3039,22 @@ "ui" ], "support": { - "source": "https://github.com/laravel/ui/tree/v4.6.1" + "source": "https://github.com/laravel/ui/tree/v4.6.3" }, - "time": "2025-01-28T15:15:29+00:00" + "time": "2026-03-17T13:41:52+00:00" }, { "name": "league/commonmark", - "version": "2.7.1", + "version": "2.8.2", "source": { "type": "git", "url": "https://github.com/thephpleague/commonmark.git", - "reference": "10732241927d3971d28e7ea7b5712721fa2296ca" + "reference": "59fb075d2101740c337c7216e3f32b36c204218b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/10732241927d3971d28e7ea7b5712721fa2296ca", - "reference": "10732241927d3971d28e7ea7b5712721fa2296ca", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/59fb075d2101740c337c7216e3f32b36c204218b", + "reference": "59fb075d2101740c337c7216e3f32b36c204218b", "shasum": "" }, "require": { @@ -2981,9 +3079,9 @@ "phpstan/phpstan": "^1.8.2", "phpunit/phpunit": "^9.5.21 || ^10.5.9 || ^11.0.0", "scrutinizer/ocular": "^1.8.1", - "symfony/finder": "^5.3 | ^6.0 | ^7.0", - "symfony/process": "^5.4 | ^6.0 | ^7.0", - "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 | ^7.0", + "symfony/finder": "^5.3 | ^6.0 | ^7.0 || ^8.0", + "symfony/process": "^5.4 | ^6.0 | ^7.0 || ^8.0", + "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 | ^7.0 || ^8.0", "unleashedtech/php-coding-standard": "^3.1.1", "vimeo/psalm": "^4.24.0 || ^5.0.0 || ^6.0.0" }, @@ -2993,7 +3091,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "2.8-dev" + "dev-main": "2.9-dev" } }, "autoload": { @@ -3050,7 +3148,7 @@ "type": "tidelift" } ], - "time": "2025-07-20T12:47:49+00:00" + "time": "2026-03-19T13:16:38+00:00" }, { "name": "league/config", @@ -3136,16 +3234,16 @@ }, { "name": "league/flysystem", - "version": "3.30.0", + "version": "3.34.0", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "2203e3151755d874bb2943649dae1eb8533ac93e" + "reference": "2daaac3b0d4c83ea7ed5d8586e786f5d00f3540e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/2203e3151755d874bb2943649dae1eb8533ac93e", - "reference": "2203e3151755d874bb2943649dae1eb8533ac93e", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/2daaac3b0d4c83ea7ed5d8586e786f5d00f3540e", + "reference": "2daaac3b0d4c83ea7ed5d8586e786f5d00f3540e", "shasum": "" }, "require": { @@ -3213,22 +3311,22 @@ ], "support": { "issues": "https://github.com/thephpleague/flysystem/issues", - "source": "https://github.com/thephpleague/flysystem/tree/3.30.0" + "source": "https://github.com/thephpleague/flysystem/tree/3.34.0" }, - "time": "2025-06-25T13:29:59+00:00" + "time": "2026-05-14T10:28:08+00:00" }, { "name": "league/flysystem-ftp", - "version": "3.29.0", + "version": "3.31.0", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem-ftp.git", - "reference": "17e8e422cb43a7fefa06ec8ddf36ee8ec936d138" + "reference": "cd6ab064a695bc340e3090c360021d26cd417e67" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem-ftp/zipball/17e8e422cb43a7fefa06ec8ddf36ee8ec936d138", - "reference": "17e8e422cb43a7fefa06ec8ddf36ee8ec936d138", + "url": "https://api.github.com/repos/thephpleague/flysystem-ftp/zipball/cd6ab064a695bc340e3090c360021d26cd417e67", + "reference": "cd6ab064a695bc340e3090c360021d26cd417e67", "shasum": "" }, "require": { @@ -3263,22 +3361,22 @@ "ftpd" ], "support": { - "source": "https://github.com/thephpleague/flysystem-ftp/tree/3.29.0" + "source": "https://github.com/thephpleague/flysystem-ftp/tree/3.31.0" }, - "time": "2024-06-12T09:46:12+00:00" + "time": "2026-01-23T15:30:45+00:00" }, { "name": "league/flysystem-local", - "version": "3.30.0", + "version": "3.31.0", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem-local.git", - "reference": "6691915f77c7fb69adfb87dcd550052dc184ee10" + "reference": "2f669db18a4c20c755c2bb7d3a7b0b2340488079" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/6691915f77c7fb69adfb87dcd550052dc184ee10", - "reference": "6691915f77c7fb69adfb87dcd550052dc184ee10", + "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/2f669db18a4c20c755c2bb7d3a7b0b2340488079", + "reference": "2f669db18a4c20c755c2bb7d3a7b0b2340488079", "shasum": "" }, "require": { @@ -3312,9 +3410,9 @@ "local" ], "support": { - "source": "https://github.com/thephpleague/flysystem-local/tree/3.30.0" + "source": "https://github.com/thephpleague/flysystem-local/tree/3.31.0" }, - "time": "2025-05-21T10:34:19+00:00" + "time": "2026-01-23T15:30:45+00:00" }, { "name": "league/fractal", @@ -3442,38 +3540,220 @@ ], "time": "2024-09-21T08:32:55+00:00" }, + { + "name": "league/uri", + "version": "7.8.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri.git", + "reference": "08cf38e3924d4f56238125547b5720496fac8fd4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri/zipball/08cf38e3924d4f56238125547b5720496fac8fd4", + "reference": "08cf38e3924d4f56238125547b5720496fac8fd4", + "shasum": "" + }, + "require": { + "league/uri-interfaces": "^7.8.1", + "php": "^8.1", + "psr/http-factory": "^1" + }, + "conflict": { + "league/uri-schemes": "^1.0" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-dom": "to convert the URI into an HTML anchor tag", + "ext-fileinfo": "to create Data URI from file contennts", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "ext-uri": "to use the PHP native URI class", + "jeremykendall/php-domain-parser": "to further parse the URI host and resolve its Public Suffix and Top Level Domain", + "league/uri-components": "to provide additional tools to manipulate URI objects components", + "league/uri-polyfill": "to backport the PHP URI extension for older versions of PHP", + "php-64bit": "to improve IPV4 host parsing", + "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Uri\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "URI manipulation library", + "homepage": "https://uri.thephpleague.com", + "keywords": [ + "URN", + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "middleware", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc2141", + "rfc3986", + "rfc3987", + "rfc6570", + "rfc8141", + "uri", + "uri-template", + "url", + "ws" + ], + "support": { + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri/tree/7.8.1" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2026-03-15T20:22:25+00:00" + }, + { + "name": "league/uri-interfaces", + "version": "7.8.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri-interfaces.git", + "reference": "85d5c77c5d6d3af6c54db4a78246364908f3c928" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/85d5c77c5d6d3af6c54db4a78246364908f3c928", + "reference": "85d5c77c5d6d3af6c54db4a78246364908f3c928", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^8.1", + "psr/http-message": "^1.1 || ^2.0" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "php-64bit": "to improve IPV4 host parsing", + "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Uri\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "Common tools for parsing and resolving RFC3987/RFC3986 URI", + "homepage": "https://uri.thephpleague.com", + "keywords": [ + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc3986", + "rfc3987", + "rfc6570", + "uri", + "url", + "ws" + ], + "support": { + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri-interfaces/tree/7.8.1" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2026-03-08T20:05:35+00:00" + }, { "name": "livewire/livewire", - "version": "v3.6.4", + "version": "v4.3.1", "source": { "type": "git", "url": "https://github.com/livewire/livewire.git", - "reference": "ef04be759da41b14d2d129e670533180a44987dc" + "reference": "6a9dd03f45a4b200abfd0ff644745b23fa7baaaa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/livewire/livewire/zipball/ef04be759da41b14d2d129e670533180a44987dc", - "reference": "ef04be759da41b14d2d129e670533180a44987dc", + "url": "https://api.github.com/repos/livewire/livewire/zipball/6a9dd03f45a4b200abfd0ff644745b23fa7baaaa", + "reference": "6a9dd03f45a4b200abfd0ff644745b23fa7baaaa", "shasum": "" }, "require": { - "illuminate/database": "^10.0|^11.0|^12.0", - "illuminate/routing": "^10.0|^11.0|^12.0", - "illuminate/support": "^10.0|^11.0|^12.0", - "illuminate/validation": "^10.0|^11.0|^12.0", + "illuminate/database": "^10.0|^11.0|^12.0|^13.0", + "illuminate/routing": "^10.0|^11.0|^12.0|^13.0", + "illuminate/support": "^10.0|^11.0|^12.0|^13.0", + "illuminate/validation": "^10.0|^11.0|^12.0|^13.0", "laravel/prompts": "^0.1.24|^0.2|^0.3", "league/mime-type-detection": "^1.9", "php": "^8.1", - "symfony/console": "^6.0|^7.0", - "symfony/http-kernel": "^6.2|^7.0" + "symfony/console": "^6.0|^7.0|^8.0", + "symfony/http-kernel": "^6.2|^7.0|^8.0" }, "require-dev": { "calebporzio/sushi": "^2.1", - "laravel/framework": "^10.15.0|^11.0|^12.0", + "laravel/framework": "^10.15.0|^11.0|^12.0|^13.0", "mockery/mockery": "^1.3.1", - "orchestra/testbench": "^8.21.0|^9.0|^10.0", - "orchestra/testbench-dusk": "^8.24|^9.1|^10.0", - "phpunit/phpunit": "^10.4|^11.5", + "orchestra/testbench": "^8.21.0|^9.0|^10.0|^11.0", + "orchestra/testbench-dusk": "^8.24|^9.1|^10.0|^11.0", + "phpunit/phpunit": "^10.4|^11.5|^12.5", "psy/psysh": "^0.11.22|^0.12" }, "type": "library", @@ -3508,7 +3788,7 @@ "description": "A front-end framework for Laravel.", "support": { "issues": "https://github.com/livewire/livewire/issues", - "source": "https://github.com/livewire/livewire/tree/v3.6.4" + "source": "https://github.com/livewire/livewire/tree/v4.3.1" }, "funding": [ { @@ -3516,35 +3796,36 @@ "type": "github" } ], - "time": "2025-07-17T05:12:15+00:00" + "time": "2026-06-02T08:58:52+00:00" }, { "name": "maennchen/zipstream-php", - "version": "3.1.1", + "version": "3.2.2", "source": { "type": "git", "url": "https://github.com/maennchen/ZipStream-PHP.git", - "reference": "6187e9cc4493da94b9b63eb2315821552015fca9" + "reference": "77bebeb4c6c340bb3c11c843b2cffd8bbfde4d5e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/6187e9cc4493da94b9b63eb2315821552015fca9", - "reference": "6187e9cc4493da94b9b63eb2315821552015fca9", + "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/77bebeb4c6c340bb3c11c843b2cffd8bbfde4d5e", + "reference": "77bebeb4c6c340bb3c11c843b2cffd8bbfde4d5e", "shasum": "" }, "require": { "ext-mbstring": "*", "ext-zlib": "*", - "php-64bit": "^8.1" + "php-64bit": "^8.3" }, "require-dev": { + "brianium/paratest": "^7.7", "ext-zip": "*", - "friendsofphp/php-cs-fixer": "^3.16", + "friendsofphp/php-cs-fixer": "^3.86", "guzzlehttp/guzzle": "^7.5", "mikey179/vfsstream": "^1.6", "php-coveralls/php-coveralls": "^2.5", - "phpunit/phpunit": "^10.0", - "vimeo/psalm": "^5.0" + "phpunit/phpunit": "^12.0", + "vimeo/psalm": "^6.0" }, "suggest": { "guzzlehttp/psr7": "^2.4", @@ -3585,7 +3866,7 @@ ], "support": { "issues": "https://github.com/maennchen/ZipStream-PHP/issues", - "source": "https://github.com/maennchen/ZipStream-PHP/tree/3.1.1" + "source": "https://github.com/maennchen/ZipStream-PHP/tree/3.2.2" }, "funding": [ { @@ -3593,7 +3874,7 @@ "type": "github" } ], - "time": "2024-10-10T12:33:01+00:00" + "time": "2026-04-11T18:38:28+00:00" }, { "name": "markbaker/complex", @@ -3704,16 +3985,16 @@ }, { "name": "maxmind-db/reader", - "version": "v1.12.1", + "version": "v1.13.1", "source": { "type": "git", "url": "https://github.com/maxmind/MaxMind-DB-Reader-php.git", - "reference": "815939e006b7e68062b540ec9e86aaa8be2b6ce4" + "reference": "2194f58d0f024ce923e685cdf92af3daf9951908" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/maxmind/MaxMind-DB-Reader-php/zipball/815939e006b7e68062b540ec9e86aaa8be2b6ce4", - "reference": "815939e006b7e68062b540ec9e86aaa8be2b6ce4", + "url": "https://api.github.com/repos/maxmind/MaxMind-DB-Reader-php/zipball/2194f58d0f024ce923e685cdf92af3daf9951908", + "reference": "2194f58d0f024ce923e685cdf92af3daf9951908", "shasum": "" }, "require": { @@ -3726,12 +4007,13 @@ "friendsofphp/php-cs-fixer": "3.*", "phpstan/phpstan": "*", "phpunit/phpunit": ">=8.0.0,<10.0.0", - "squizlabs/php_codesniffer": "3.*" + "squizlabs/php_codesniffer": "4.*" }, "suggest": { "ext-bcmath": "bcmath or gmp is required for decoding larger integers with the pure PHP decoder", "ext-gmp": "bcmath or gmp is required for decoding larger integers with the pure PHP decoder", - "ext-maxminddb": "A C-based database decoder that provides significantly faster lookups" + "ext-maxminddb": "A C-based database decoder that provides significantly faster lookups", + "maxmind-db/reader-ext": "C extension for significantly faster IP lookups (install via PIE: pie install maxmind-db/reader-ext)" }, "type": "library", "autoload": { @@ -3761,22 +4043,22 @@ ], "support": { "issues": "https://github.com/maxmind/MaxMind-DB-Reader-php/issues", - "source": "https://github.com/maxmind/MaxMind-DB-Reader-php/tree/v1.12.1" + "source": "https://github.com/maxmind/MaxMind-DB-Reader-php/tree/v1.13.1" }, - "time": "2025-05-05T20:56:32+00:00" + "time": "2025-11-21T22:24:26+00:00" }, { "name": "maxmind/web-service-common", - "version": "v0.10.0", + "version": "v0.11.1", "source": { "type": "git", "url": "https://github.com/maxmind/web-service-common-php.git", - "reference": "d7c7c42fc31bff26e0ded73a6e187bcfb193f9c4" + "reference": "c309236b5a5555b96cf560089ec3cead12d845d2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/maxmind/web-service-common-php/zipball/d7c7c42fc31bff26e0ded73a6e187bcfb193f9c4", - "reference": "d7c7c42fc31bff26e0ded73a6e187bcfb193f9c4", + "url": "https://api.github.com/repos/maxmind/web-service-common-php/zipball/c309236b5a5555b96cf560089ec3cead12d845d2", + "reference": "c309236b5a5555b96cf560089ec3cead12d845d2", "shasum": "" }, "require": { @@ -3788,8 +4070,8 @@ "require-dev": { "friendsofphp/php-cs-fixer": "3.*", "phpstan/phpstan": "*", - "phpunit/phpunit": "^8.0 || ^9.0", - "squizlabs/php_codesniffer": "3.*" + "phpunit/phpunit": "^10.0", + "squizlabs/php_codesniffer": "4.*" }, "type": "library", "autoload": { @@ -3812,37 +4094,37 @@ "homepage": "https://github.com/maxmind/web-service-common-php", "support": { "issues": "https://github.com/maxmind/web-service-common-php/issues", - "source": "https://github.com/maxmind/web-service-common-php/tree/v0.10.0" + "source": "https://github.com/maxmind/web-service-common-php/tree/v0.11.1" }, - "time": "2024-11-14T23:14:52+00:00" + "time": "2026-01-13T17:56:03+00:00" }, { "name": "mews/captcha", - "version": "3.3.3", + "version": "3.4.9", "source": { "type": "git", "url": "https://github.com/mewebstudio/captcha.git", - "reference": "e996a9a5638296de3e9dac41782dbdcf3d14ce11" + "reference": "c31cfe10ab04a5e8ad04b879215bdf61727ee72c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mewebstudio/captcha/zipball/e996a9a5638296de3e9dac41782dbdcf3d14ce11", - "reference": "e996a9a5638296de3e9dac41782dbdcf3d14ce11", + "url": "https://api.github.com/repos/mewebstudio/captcha/zipball/c31cfe10ab04a5e8ad04b879215bdf61727ee72c", + "reference": "c31cfe10ab04a5e8ad04b879215bdf61727ee72c", "shasum": "" }, "require": { "ext-gd": "*", - "illuminate/config": "~5|^6|^7|^8|^9|^10|^11", - "illuminate/filesystem": "~5|^6|^7|^8|^9|^10|^11", - "illuminate/hashing": "~5|^6|^7|^8|^9|^10|^11", - "illuminate/session": "~5|^6|^7|^8|^9|^10|^11", - "illuminate/support": "~5|^6|^7|^8|^9|^10|^11", - "intervention/image": "~2.5", + "illuminate/config": "~5|^6|^7|^8|^9|^10|^11|^12|^13.0", + "illuminate/filesystem": "~5|^6|^7|^8|^9|^10|^11|^12|^13.0", + "illuminate/hashing": "~5|^6|^7|^8|^9|^10|^11|^12|^13.0", + "illuminate/session": "~5|^6|^7|^8|^9|^10|^11|^12|^13.0", + "illuminate/support": "~5|^6|^7|^8|^9|^10|^11|^12|^13.0", + "intervention/image": "^3.7|^4.0", "php": "^7.2|^8.1|^8.2|^8.3" }, "require-dev": { "mockery/mockery": "^1.0", - "phpunit/phpunit": "^8.5|^9.5.10|^10.5" + "phpunit/phpunit": "^8.5|^9.5.10|^10.5|^11" }, "type": "package", "extra": { @@ -3875,39 +4157,39 @@ "role": "Developer" } ], - "description": "Laravel 5/6/7/8/9/10/11 Captcha Package", + "description": "Laravel 5/6/7/8/9/10/11/12 Captcha Package", "homepage": "https://github.com/mewebstudio/captcha", "keywords": [ "captcha", - "laravel5 Security", - "laravel6 Captcha", - "laravel6 Security" + "laravel12 Captcha", + "laravel12 Security", + "laravel5 Security" ], "support": { "issues": "https://github.com/mewebstudio/captcha/issues", - "source": "https://github.com/mewebstudio/captcha/tree/3.3.3" + "source": "https://github.com/mewebstudio/captcha/tree/3.4.9" }, - "time": "2024-03-20T16:15:48+00:00" + "time": "2026-05-16T15:39:06+00:00" }, { "name": "mews/purifier", - "version": "3.4.3", + "version": "3.4.4", "source": { "type": "git", "url": "https://github.com/mewebstudio/Purifier.git", - "reference": "acc71bc512dcf9b87144546d0e3055fc76d244ff" + "reference": "b2705cc6c832ce7229373418e191d71b6c037841" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mewebstudio/Purifier/zipball/acc71bc512dcf9b87144546d0e3055fc76d244ff", - "reference": "acc71bc512dcf9b87144546d0e3055fc76d244ff", + "url": "https://api.github.com/repos/mewebstudio/Purifier/zipball/b2705cc6c832ce7229373418e191d71b6c037841", + "reference": "b2705cc6c832ce7229373418e191d71b6c037841", "shasum": "" }, "require": { "ezyang/htmlpurifier": "^4.16.0", - "illuminate/config": "^5.8|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", - "illuminate/filesystem": "^5.8|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", - "illuminate/support": "^5.8|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/config": "^5.8|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/filesystem": "^5.8|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/support": "^5.8|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", "php": "^7.2|^8.0" }, "require-dev": { @@ -3963,35 +4245,34 @@ ], "support": { "issues": "https://github.com/mewebstudio/Purifier/issues", - "source": "https://github.com/mewebstudio/Purifier/tree/3.4.3" + "source": "https://github.com/mewebstudio/Purifier/tree/3.4.4" }, - "time": "2025-02-24T16:00:29+00:00" + "time": "2026-04-15T16:41:08+00:00" }, { "name": "mobiledetect/mobiledetectlib", - "version": "4.8.09", + "version": "4.11.0", "source": { "type": "git", "url": "https://github.com/serbanghita/Mobile-Detect.git", - "reference": "a06fe2e546a06bb8c2639d6823d5250b2efb3209" + "reference": "ab39168b7556f44c11c80be1222b44b239f5c2e4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/serbanghita/Mobile-Detect/zipball/a06fe2e546a06bb8c2639d6823d5250b2efb3209", - "reference": "a06fe2e546a06bb8c2639d6823d5250b2efb3209", + "url": "https://api.github.com/repos/serbanghita/Mobile-Detect/zipball/ab39168b7556f44c11c80be1222b44b239f5c2e4", + "reference": "ab39168b7556f44c11c80be1222b44b239f5c2e4", "shasum": "" }, "require": { - "php": ">=8.0", - "psr/cache": "^3.0", - "psr/simple-cache": "^3" + "php": ">=8.2", + "psr/simple-cache": "^1.0 || ^2.0 || ^3.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^v3.65.0", - "phpbench/phpbench": "^1.2", - "phpstan/phpstan": "^1.12.x-dev", - "phpunit/phpunit": "^9.6.18", - "squizlabs/php_codesniffer": "^3.11.1" + "friendsofphp/php-cs-fixer": "3.95.1", + "phpbench/phpbench": "1.6.1", + "phpstan/phpstan": "2.1.47", + "phpunit/phpunit": "9.6.34", + "squizlabs/php_codesniffer": "3.13.5" }, "type": "library", "autoload": { @@ -4022,7 +4303,7 @@ ], "support": { "issues": "https://github.com/serbanghita/Mobile-Detect/issues", - "source": "https://github.com/serbanghita/Mobile-Detect/tree/4.8.09" + "source": "https://github.com/serbanghita/Mobile-Detect/tree/4.11.0" }, "funding": [ { @@ -4030,20 +4311,20 @@ "type": "github" } ], - "time": "2024-12-10T15:32:06+00:00" + "time": "2026-05-24T12:32:40+00:00" }, { "name": "monolog/monolog", - "version": "3.9.0", + "version": "3.10.0", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "10d85740180ecba7896c87e06a166e0c95a0e3b6" + "reference": "b321dd6749f0bf7189444158a3ce785cc16d69b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/10d85740180ecba7896c87e06a166e0c95a0e3b6", - "reference": "10d85740180ecba7896c87e06a166e0c95a0e3b6", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/b321dd6749f0bf7189444158a3ce785cc16d69b0", + "reference": "b321dd6749f0bf7189444158a3ce785cc16d69b0", "shasum": "" }, "require": { @@ -4061,7 +4342,7 @@ "graylog2/gelf-php": "^1.4.2 || ^2.0", "guzzlehttp/guzzle": "^7.4.5", "guzzlehttp/psr7": "^2.2", - "mongodb/mongodb": "^1.8", + "mongodb/mongodb": "^1.8 || ^2.0", "php-amqplib/php-amqplib": "~2.4 || ^3", "php-console/php-console": "^3.1.8", "phpstan/phpstan": "^2", @@ -4121,7 +4402,7 @@ ], "support": { "issues": "https://github.com/Seldaek/monolog/issues", - "source": "https://github.com/Seldaek/monolog/tree/3.9.0" + "source": "https://github.com/Seldaek/monolog/tree/3.10.0" }, "funding": [ { @@ -4133,46 +4414,44 @@ "type": "tidelift" } ], - "time": "2025-03-24T10:02:05+00:00" + "time": "2026-01-02T08:56:05+00:00" }, { "name": "nesbot/carbon", - "version": "2.73.0", + "version": "3.11.4", "source": { "type": "git", "url": "https://github.com/CarbonPHP/carbon.git", - "reference": "9228ce90e1035ff2f0db84b40ec2e023ed802075" + "reference": "e890471a3494740f7d9326d72ce6a8c559ffee60" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/9228ce90e1035ff2f0db84b40ec2e023ed802075", - "reference": "9228ce90e1035ff2f0db84b40ec2e023ed802075", + "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/e890471a3494740f7d9326d72ce6a8c559ffee60", + "reference": "e890471a3494740f7d9326d72ce6a8c559ffee60", "shasum": "" }, "require": { - "carbonphp/carbon-doctrine-types": "*", + "carbonphp/carbon-doctrine-types": "<100.0", "ext-json": "*", - "php": "^7.1.8 || ^8.0", + "php": "^8.1", "psr/clock": "^1.0", + "symfony/clock": "^6.3.12 || ^7.0 || ^8.0", "symfony/polyfill-mbstring": "^1.0", - "symfony/polyfill-php80": "^1.16", - "symfony/translation": "^3.4 || ^4.0 || ^5.0 || ^6.0" + "symfony/translation": "^4.4.18 || ^5.2.1 || ^6.0 || ^7.0 || ^8.0" }, "provide": { "psr/clock-implementation": "1.0" }, "require-dev": { - "doctrine/dbal": "^2.0 || ^3.1.4 || ^4.0", - "doctrine/orm": "^2.7 || ^3.0", - "friendsofphp/php-cs-fixer": "^3.0", - "kylekatarnls/multi-tester": "^2.0", - "ondrejmirtes/better-reflection": "<6", - "phpmd/phpmd": "^2.9", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^0.12.99 || ^1.7.14", - "phpunit/php-file-iterator": "^2.0.5 || ^3.0.6", - "phpunit/phpunit": "^7.5.20 || ^8.5.26 || ^9.5.20", - "squizlabs/php_codesniffer": "^3.4" + "doctrine/dbal": "^3.6.3 || ^4.0", + "doctrine/orm": "^2.15.2 || ^3.0", + "friendsofphp/php-cs-fixer": "^v3.87.1", + "kylekatarnls/multi-tester": "^2.5.3", + "phpmd/phpmd": "^2.15.0", + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^2.1.22", + "phpunit/phpunit": "^10.5.53", + "squizlabs/php_codesniffer": "^3.13.4 || ^4.0.0" }, "bin": [ "bin/carbon" @@ -4215,16 +4494,16 @@ } ], "description": "An API extension for DateTime that supports 281 different languages.", - "homepage": "https://carbon.nesbot.com", + "homepage": "https://carbonphp.github.io/carbon/", "keywords": [ "date", "datetime", "time" ], "support": { - "docs": "https://carbon.nesbot.com/docs", - "issues": "https://github.com/briannesbitt/Carbon/issues", - "source": "https://github.com/briannesbitt/Carbon" + "docs": "https://carbonphp.github.io/carbon/guide/getting-started/introduction.html", + "issues": "https://github.com/CarbonPHP/carbon/issues", + "source": "https://github.com/CarbonPHP/carbon" }, "funding": [ { @@ -4240,29 +4519,31 @@ "type": "tidelift" } ], - "time": "2025-01-08T20:10:23+00:00" + "time": "2026-04-07T09:57:54+00:00" }, { "name": "nette/schema", - "version": "v1.3.2", + "version": "v1.3.5", "source": { "type": "git", "url": "https://github.com/nette/schema.git", - "reference": "da801d52f0354f70a638673c4a0f04e16529431d" + "reference": "f0ab1a3cda782dbc5da270d28545236aa80c4002" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/schema/zipball/da801d52f0354f70a638673c4a0f04e16529431d", - "reference": "da801d52f0354f70a638673c4a0f04e16529431d", + "url": "https://api.github.com/repos/nette/schema/zipball/f0ab1a3cda782dbc5da270d28545236aa80c4002", + "reference": "f0ab1a3cda782dbc5da270d28545236aa80c4002", "shasum": "" }, "require": { "nette/utils": "^4.0", - "php": "8.1 - 8.4" + "php": "8.1 - 8.5" }, "require-dev": { - "nette/tester": "^2.5.2", - "phpstan/phpstan-nette": "^1.0", + "nette/phpstan-rules": "^1.0", + "nette/tester": "^2.6", + "phpstan/extension-installer": "^1.4@stable", + "phpstan/phpstan": "^2.1.39@stable", "tracy/tracy": "^2.8" }, "type": "library", @@ -4272,6 +4553,9 @@ } }, "autoload": { + "psr-4": { + "Nette\\": "src" + }, "classmap": [ "src/" ] @@ -4300,26 +4584,26 @@ ], "support": { "issues": "https://github.com/nette/schema/issues", - "source": "https://github.com/nette/schema/tree/v1.3.2" + "source": "https://github.com/nette/schema/tree/v1.3.5" }, - "time": "2024-10-06T23:10:23+00:00" + "time": "2026-02-23T03:47:12+00:00" }, { "name": "nette/utils", - "version": "v4.0.8", + "version": "v4.1.4", "source": { "type": "git", "url": "https://github.com/nette/utils.git", - "reference": "c930ca4e3cf4f17dcfb03037703679d2396d2ede" + "reference": "7da6c396d7ebe142bc857c20479d5e70a5e1aac7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/utils/zipball/c930ca4e3cf4f17dcfb03037703679d2396d2ede", - "reference": "c930ca4e3cf4f17dcfb03037703679d2396d2ede", + "url": "https://api.github.com/repos/nette/utils/zipball/7da6c396d7ebe142bc857c20479d5e70a5e1aac7", + "reference": "7da6c396d7ebe142bc857c20479d5e70a5e1aac7", "shasum": "" }, "require": { - "php": "8.0 - 8.5" + "php": "8.2 - 8.5" }, "conflict": { "nette/finder": "<3", @@ -4327,8 +4611,10 @@ }, "require-dev": { "jetbrains/phpstorm-attributes": "^1.2", + "nette/phpstan-rules": "^1.0", "nette/tester": "^2.5", - "phpstan/phpstan-nette": "^2.0@stable", + "phpstan/extension-installer": "^1.4@stable", + "phpstan/phpstan": "^2.1@stable", "tracy/tracy": "^2.9" }, "suggest": { @@ -4342,7 +4628,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -4389,22 +4675,22 @@ ], "support": { "issues": "https://github.com/nette/utils/issues", - "source": "https://github.com/nette/utils/tree/v4.0.8" + "source": "https://github.com/nette/utils/tree/v4.1.4" }, - "time": "2025-08-06T21:43:34+00:00" + "time": "2026-05-11T20:49:54+00:00" }, { "name": "nikic/php-parser", - "version": "v5.6.1", + "version": "v5.7.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2" + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", - "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", "shasum": "" }, "require": { @@ -4447,38 +4733,37 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.1" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" }, - "time": "2025-08-13T20:13:15+00:00" + "time": "2025-12-06T11:56:16+00:00" }, { "name": "nunomaduro/termwind", - "version": "v1.17.0", + "version": "v2.4.0", "source": { "type": "git", "url": "https://github.com/nunomaduro/termwind.git", - "reference": "5369ef84d8142c1d87e4ec278711d4ece3cbf301" + "reference": "712a31b768f5daea284c2169a7d227031001b9a8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/5369ef84d8142c1d87e4ec278711d4ece3cbf301", - "reference": "5369ef84d8142c1d87e4ec278711d4ece3cbf301", + "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/712a31b768f5daea284c2169a7d227031001b9a8", + "reference": "712a31b768f5daea284c2169a7d227031001b9a8", "shasum": "" }, "require": { "ext-mbstring": "*", - "php": "^8.1", - "symfony/console": "^6.4.15" + "php": "^8.2", + "symfony/console": "^7.4.4 || ^8.0.4" }, "require-dev": { - "illuminate/console": "^10.48.24", - "illuminate/support": "^10.48.24", - "laravel/pint": "^1.18.2", - "pestphp/pest": "^2.36.0", - "pestphp/pest-plugin-mock": "2.0.0", - "phpstan/phpstan": "^1.12.11", - "phpstan/phpstan-strict-rules": "^1.6.1", - "symfony/var-dumper": "^6.4.15", + "illuminate/console": "^11.47.0", + "laravel/pint": "^1.27.1", + "mockery/mockery": "^1.6.12", + "pestphp/pest": "^2.36.0 || ^3.8.4 || ^4.3.2", + "phpstan/phpstan": "^1.12.32", + "phpstan/phpstan-strict-rules": "^1.6.2", + "symfony/var-dumper": "^7.3.5 || ^8.0.4", "thecodingmachine/phpstan-strict-rules": "^1.0.0" }, "type": "library", @@ -4487,6 +4772,9 @@ "providers": [ "Termwind\\Laravel\\TermwindServiceProvider" ] + }, + "branch-alias": { + "dev-2.x": "2.x-dev" } }, "autoload": { @@ -4507,7 +4795,7 @@ "email": "enunomaduro@gmail.com" } ], - "description": "Its like Tailwind CSS, but for the console.", + "description": "It's like Tailwind CSS, but for the console.", "keywords": [ "cli", "console", @@ -4518,7 +4806,7 @@ ], "support": { "issues": "https://github.com/nunomaduro/termwind/issues", - "source": "https://github.com/nunomaduro/termwind/tree/v1.17.0" + "source": "https://github.com/nunomaduro/termwind/tree/v2.4.0" }, "funding": [ { @@ -4534,20 +4822,20 @@ "type": "github" } ], - "time": "2024-11-21T10:36:35+00:00" + "time": "2026-02-16T23:10:27+00:00" }, { "name": "openspout/openspout", - "version": "v4.25.0", + "version": "v4.32.0", "source": { "type": "git", "url": "https://github.com/openspout/openspout.git", - "reference": "519affe730d92e1598720a6467227fc28550f0e6" + "reference": "41f045c1f632e1474e15d4c7bc3abcb4a153563d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/openspout/openspout/zipball/519affe730d92e1598720a6467227fc28550f0e6", - "reference": "519affe730d92e1598720a6467227fc28550f0e6", + "url": "https://api.github.com/repos/openspout/openspout/zipball/41f045c1f632e1474e15d4c7bc3abcb4a153563d", + "reference": "41f045c1f632e1474e15d4c7bc3abcb4a153563d", "shasum": "" }, "require": { @@ -4557,17 +4845,17 @@ "ext-libxml": "*", "ext-xmlreader": "*", "ext-zip": "*", - "php": "~8.1.0 || ~8.2.0 || ~8.3.0" + "php": "~8.3.0 || ~8.4.0 || ~8.5.0" }, "require-dev": { "ext-zlib": "*", - "friendsofphp/php-cs-fixer": "^3.64.0", - "infection/infection": "^0.29.6", - "phpbench/phpbench": "^1.3.1", - "phpstan/phpstan": "^1.12.4", - "phpstan/phpstan-phpunit": "^1.4.0", - "phpstan/phpstan-strict-rules": "^1.6.1", - "phpunit/phpunit": "^10.5.20 || ^11.3.6" + "friendsofphp/php-cs-fixer": "^3.86.0", + "infection/infection": "^0.31.2", + "phpbench/phpbench": "^1.4.1", + "phpstan/phpstan": "^2.1.22", + "phpstan/phpstan-phpunit": "^2.0.7", + "phpstan/phpstan-strict-rules": "^2.0.6", + "phpunit/phpunit": "^12.3.7" }, "suggest": { "ext-iconv": "To handle non UTF-8 CSV files (if \"php-mbstring\" is not already installed or is too limited)", @@ -4615,7 +4903,7 @@ ], "support": { "issues": "https://github.com/openspout/openspout/issues", - "source": "https://github.com/openspout/openspout/tree/v4.25.0" + "source": "https://github.com/openspout/openspout/tree/v4.32.0" }, "funding": [ { @@ -4627,85 +4915,20 @@ "type": "github" } ], - "time": "2024-09-24T09:03:42+00:00" + "time": "2025-09-03T16:03:54+00:00" }, { - "name": "opis/closure", - "version": "3.7.0", + "name": "phpoffice/phpspreadsheet", + "version": "5.7.0", "source": { "type": "git", - "url": "https://github.com/opis/closure.git", - "reference": "b1a22a6be71c1263f3ca6e68f00b3fd4d394abc4" + "url": "https://github.com/PHPOffice/PhpSpreadsheet.git", + "reference": "9f55d3b9b7bcb1084fda8340e4b7ce4ed10cd0c8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opis/closure/zipball/b1a22a6be71c1263f3ca6e68f00b3fd4d394abc4", - "reference": "b1a22a6be71c1263f3ca6e68f00b3fd4d394abc4", - "shasum": "" - }, - "require": { - "php": "^5.4 || ^7.0 || ^8.0" - }, - "require-dev": { - "jeremeamia/superclosure": "^2.0", - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.6.x-dev" - } - }, - "autoload": { - "files": [ - "functions.php" - ], - "psr-4": { - "Opis\\Closure\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marius Sarca", - "email": "marius.sarca@gmail.com" - }, - { - "name": "Sorin Sarca", - "email": "sarca_sorin@hotmail.com" - } - ], - "description": "A library that can be used to serialize closures (anonymous functions) and arbitrary objects.", - "homepage": "https://opis.io/closure", - "keywords": [ - "anonymous functions", - "closure", - "function", - "serializable", - "serialization", - "serialize" - ], - "support": { - "issues": "https://github.com/opis/closure/issues", - "source": "https://github.com/opis/closure/tree/3.7.0" - }, - "time": "2025-07-08T20:30:08+00:00" - }, - { - "name": "phpoffice/phpspreadsheet", - "version": "1.30.0", - "source": { - "type": "git", - "url": "https://github.com/PHPOffice/PhpSpreadsheet.git", - "reference": "2f39286e0136673778b7a142b3f0d141e43d1714" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/2f39286e0136673778b7a142b3f0d141e43d1714", - "reference": "2f39286e0136673778b7a142b3f0d141e43d1714", + "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/9f55d3b9b7bcb1084fda8340e4b7ce4ed10cd0c8", + "reference": "9f55d3b9b7bcb1084fda8340e4b7ce4ed10cd0c8", "shasum": "" }, "require": { @@ -4713,6 +4936,7 @@ "ext-ctype": "*", "ext-dom": "*", "ext-fileinfo": "*", + "ext-filter": "*", "ext-gd": "*", "ext-iconv": "*", "ext-libxml": "*", @@ -4723,31 +4947,30 @@ "ext-xmlwriter": "*", "ext-zip": "*", "ext-zlib": "*", - "ezyang/htmlpurifier": "^4.15", "maennchen/zipstream-php": "^2.1 || ^3.0", "markbaker/complex": "^3.0", "markbaker/matrix": "^3.0", - "php": "^7.4 || ^8.0", - "psr/http-client": "^1.0", - "psr/http-factory": "^1.0", + "php": "^8.1", "psr/simple-cache": "^1.0 || ^2.0 || ^3.0" }, "require-dev": { "dealerdirect/phpcodesniffer-composer-installer": "dev-main", - "dompdf/dompdf": "^1.0 || ^2.0 || ^3.0", + "dompdf/dompdf": "^2.0 || ^3.0", + "ext-intl": "*", "friendsofphp/php-cs-fixer": "^3.2", - "mitoteam/jpgraph": "^10.3", + "mitoteam/jpgraph": "^10.5", "mpdf/mpdf": "^8.1.1", "phpcompatibility/php-compatibility": "^9.3", - "phpstan/phpstan": "^1.1", - "phpstan/phpstan-phpunit": "^1.0", - "phpunit/phpunit": "^8.5 || ^9.0", + "phpstan/phpstan": "^1.1 || ^2.0", + "phpstan/phpstan-deprecation-rules": "^1.0 || ^2.0", + "phpstan/phpstan-phpunit": "^1.0 || ^2.0", + "phpunit/phpunit": "^10.5", "squizlabs/php_codesniffer": "^3.7", "tecnickcom/tcpdf": "^6.5" }, "suggest": { "dompdf/dompdf": "Option for rendering PDF with PDF Writer", - "ext-intl": "PHP Internationalization Functions", + "ext-intl": "PHP Internationalization Functions, required for NumberFormat Wizard and StringHelper::setLocale()", "mitoteam/jpgraph": "Option for rendering charts, or including charts with PDF or HTML Writers", "mpdf/mpdf": "Option for rendering PDF with PDF Writer", "tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer" @@ -4780,6 +5003,9 @@ }, { "name": "Adrien Crivelli" + }, + { + "name": "Owen Leibman" } ], "description": "PHPSpreadsheet - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine", @@ -4796,22 +5022,22 @@ ], "support": { "issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues", - "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.30.0" + "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/5.7.0" }, - "time": "2025-08-10T06:28:02+00:00" + "time": "2026-04-20T02:42:17+00:00" }, { "name": "phpoption/phpoption", - "version": "1.9.4", + "version": "1.9.5", "source": { "type": "git", "url": "https://github.com/schmittjoh/php-option.git", - "reference": "638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d" + "reference": "75365b91986c2405cf5e1e012c5595cd487a98be" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d", - "reference": "638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/75365b91986c2405cf5e1e012c5595cd487a98be", + "reference": "75365b91986c2405cf5e1e012c5595cd487a98be", "shasum": "" }, "require": { @@ -4861,7 +5087,7 @@ ], "support": { "issues": "https://github.com/schmittjoh/php-option/issues", - "source": "https://github.com/schmittjoh/php-option/tree/1.9.4" + "source": "https://github.com/schmittjoh/php-option/tree/1.9.5" }, "funding": [ { @@ -4873,37 +5099,37 @@ "type": "tidelift" } ], - "time": "2025-08-21T11:53:16+00:00" + "time": "2025-12-27T19:41:33+00:00" }, { "name": "proengsoft/laravel-jsvalidation", - "version": "v4.10.2", + "version": "v4.10.3", "source": { "type": "git", "url": "https://github.com/proengsoft/laravel-jsvalidation.git", - "reference": "f947c0540fbd260eea8a7f834704fdadf45ea1d7" + "reference": "1697264c41f6731f4145b04cbb2e3e08fe747c78" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/proengsoft/laravel-jsvalidation/zipball/f947c0540fbd260eea8a7f834704fdadf45ea1d7", - "reference": "f947c0540fbd260eea8a7f834704fdadf45ea1d7", + "url": "https://api.github.com/repos/proengsoft/laravel-jsvalidation/zipball/1697264c41f6731f4145b04cbb2e3e08fe747c78", + "reference": "1697264c41f6731f4145b04cbb2e3e08fe747c78", "shasum": "" }, "require": { - "illuminate/config": "^10.0|^11.0|^12.0", - "illuminate/contracts": "^10.0|^11.0|^12.0", - "illuminate/http": "^10.0|^11.0|^12.0", - "illuminate/support": "^10.0|^11.0|^12.0", - "illuminate/translation": "^10.0|^11.0|^12.0", - "illuminate/validation": "^10.0|^11.0|^12.0", - "illuminate/view": "^10.0|^11.0|^12.0", - "php": "^8.1", - "symfony/http-foundation": "^6.0|^7.0" + "illuminate/config": "^11.0|^12.0|^13.0", + "illuminate/contracts": "^11.0|^12.0|^13.0", + "illuminate/http": "^11.0|^12.0|^13.0", + "illuminate/support": "^11.0|^12.0|^13.0", + "illuminate/translation": "^11.0|^12.0|^13.0", + "illuminate/validation": "^11.0|^12.0|^13.0", + "illuminate/view": "^11.0|^12.0|^13.0", + "php": "^8.2", + "symfony/http-foundation": "^7.0|^8.0" }, "require-dev": { "mockery/mockery": "^1.0", - "orchestra/testbench": "^8.0|^9.0|^10.0", - "phpunit/phpunit": "^9.5|^10.0|^11.5.3" + "orchestra/testbench": "^9.0|^11.0", + "phpunit/phpunit": "^11.5.3|^12.5.12" }, "type": "library", "extra": { @@ -4950,7 +5176,7 @@ ], "support": { "issues": "https://github.com/proengsoft/laravel-jsvalidation/issues", - "source": "https://github.com/proengsoft/laravel-jsvalidation/tree/v4.10.2" + "source": "https://github.com/proengsoft/laravel-jsvalidation/tree/v4.10.3" }, "funding": [ { @@ -4958,56 +5184,7 @@ "type": "github" } ], - "time": "2025-05-12T08:28:30+00:00" - }, - { - "name": "psr/cache", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/cache.git", - "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", - "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", - "shasum": "" - }, - "require": { - "php": ">=8.0.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Cache\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for caching libraries", - "keywords": [ - "cache", - "psr", - "psr-6" - ], - "support": { - "source": "https://github.com/php-fig/cache/tree/3.0.0" - }, - "time": "2021-02-03T23:26:27+00:00" + "time": "2026-03-17T15:14:42+00:00" }, { "name": "psr/clock", @@ -5423,16 +5600,16 @@ }, { "name": "psy/psysh", - "version": "v0.12.10", + "version": "v0.12.23", "source": { "type": "git", "url": "https://github.com/bobthecow/psysh.git", - "reference": "6e80abe6f2257121f1eb9a4c55bf29d921025b22" + "reference": "4dcc0f08047d52bbde475eda481146fd8e27e1a4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/6e80abe6f2257121f1eb9a4c55bf29d921025b22", - "reference": "6e80abe6f2257121f1eb9a4c55bf29d921025b22", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/4dcc0f08047d52bbde475eda481146fd8e27e1a4", + "reference": "4dcc0f08047d52bbde475eda481146fd8e27e1a4", "shasum": "" }, "require": { @@ -5440,18 +5617,19 @@ "ext-tokenizer": "*", "nikic/php-parser": "^5.0 || ^4.0", "php": "^8.0 || ^7.4", - "symfony/console": "^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4", - "symfony/var-dumper": "^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4" + "symfony/console": "^8.0 || ^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4", + "symfony/var-dumper": "^8.0 || ^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4" }, "conflict": { "symfony/console": "4.4.37 || 5.3.14 || 5.3.15 || 5.4.3 || 5.4.4 || 6.0.3 || 6.0.4" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.2" + "bamarni/composer-bin-plugin": "^1.2", + "composer/class-map-generator": "^1.6" }, "suggest": { + "composer/class-map-generator": "Improved tab completion performance with better class discovery.", "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)", - "ext-pdo-sqlite": "The doc command requires SQLite to work.", "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well." }, "bin": [ @@ -5495,9 +5673,9 @@ ], "support": { "issues": "https://github.com/bobthecow/psysh/issues", - "source": "https://github.com/bobthecow/psysh/tree/v0.12.10" + "source": "https://github.com/bobthecow/psysh/tree/v0.12.23" }, - "time": "2025-08-04T12:39:37+00:00" + "time": "2026-05-23T13:41:31+00:00" }, { "name": "ralouphie/getallheaders", @@ -5621,20 +5799,20 @@ }, { "name": "ramsey/uuid", - "version": "4.9.0", + "version": "4.9.2", "source": { "type": "git", "url": "https://github.com/ramsey/uuid.git", - "reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0" + "reference": "8429c78ca35a09f27565311b98101e2826affde0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/4e0e23cc785f0724a0e838279a9eb03f28b092a0", - "reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/8429c78ca35a09f27565311b98101e2826affde0", + "reference": "8429c78ca35a09f27565311b98101e2826affde0", "shasum": "" }, "require": { - "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13", + "brick/math": "^0.8.16 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13 || ^0.14", "php": "^8.0", "ramsey/collection": "^1.2 || ^2.0" }, @@ -5693,35 +5871,35 @@ ], "support": { "issues": "https://github.com/ramsey/uuid/issues", - "source": "https://github.com/ramsey/uuid/tree/4.9.0" + "source": "https://github.com/ramsey/uuid/tree/4.9.2" }, - "time": "2025-06-25T14:20:11+00:00" + "time": "2025-12-14T04:43:48+00:00" }, { "name": "shetabit/visitor", - "version": "v4.4.1", + "version": "v4.5.6", "source": { "type": "git", "url": "https://github.com/shetabit/visitor.git", - "reference": "4ec03a2dbb1ca764b88f4d794c0a552ca6b67e96" + "reference": "f7eaca3b3ce1c6bb7d34e749396c77cf79bf56e4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/shetabit/visitor/zipball/4ec03a2dbb1ca764b88f4d794c0a552ca6b67e96", - "reference": "4ec03a2dbb1ca764b88f4d794c0a552ca6b67e96", + "url": "https://api.github.com/repos/shetabit/visitor/zipball/f7eaca3b3ce1c6bb7d34e749396c77cf79bf56e4", + "reference": "f7eaca3b3ce1c6bb7d34e749396c77cf79bf56e4", "shasum": "" }, "require": { - "illuminate/support": "9.*|10.*|11.*|12.*", + "illuminate/support": "9.*|10.*|11.*|12.*|^13.0", "jaybizzle/crawler-detect": "^1.2", - "mobiledetect/mobiledetectlib": "^4.8", + "mobiledetect/mobiledetectlib": "^4.10", "php": ">=8.0", "ua-parser/uap-php": "^3.9" }, "require-dev": { - "orchestra/testbench": "^4.0|^5.0|^6.0|^7.0|^8.0|^9.0|^10.0", + "orchestra/testbench": "^4.0|^5.0|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", "phpunit/phpunit": ">=7.5", - "squizlabs/php_codesniffer": "^3.4" + "squizlabs/php_codesniffer": "^3.4|^4.0" }, "type": "library", "extra": { @@ -5765,22 +5943,22 @@ ], "support": { "issues": "https://github.com/shetabit/visitor/issues", - "source": "https://github.com/shetabit/visitor/tree/v4.4.1" + "source": "https://github.com/shetabit/visitor/tree/v4.5.6" }, - "time": "2025-03-18T14:58:13+00:00" + "time": "2026-05-19T20:15:12+00:00" }, { "name": "spatie/fractalistic", - "version": "2.11.0", + "version": "2.11.1", "source": { "type": "git", "url": "https://github.com/spatie/fractalistic.git", - "reference": "046c535f30b31a9356fc034ce75e8ee74614ed4f" + "reference": "85d6ff9a93f00d902e17924bb1475163b373c890" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/fractalistic/zipball/046c535f30b31a9356fc034ce75e8ee74614ed4f", - "reference": "046c535f30b31a9356fc034ce75e8ee74614ed4f", + "url": "https://api.github.com/repos/spatie/fractalistic/zipball/85d6ff9a93f00d902e17924bb1475163b373c890", + "reference": "85d6ff9a93f00d902e17924bb1475163b373c890", "shasum": "" }, "require": { @@ -5788,7 +5966,7 @@ "php": "^7.4|^8.0" }, "require-dev": { - "illuminate/pagination": "~5.3.0|~5.4.0|^9.0", + "illuminate/pagination": "~5.3.0|~5.4.0|^9.0|^13.0", "pestphp/pest": "^1.22", "phpunit/phpunit": "^9.0" }, @@ -5821,7 +5999,7 @@ ], "support": { "issues": "https://github.com/spatie/fractalistic/issues", - "source": "https://github.com/spatie/fractalistic/tree/2.11.0" + "source": "https://github.com/spatie/fractalistic/tree/2.11.1" }, "funding": [ { @@ -5829,33 +6007,33 @@ "type": "github" } ], - "time": "2025-01-27T09:52:33+00:00" + "time": "2026-02-21T21:10:25+00:00" }, { "name": "spatie/laravel-activitylog", - "version": "4.10.2", + "version": "4.12.3", "source": { "type": "git", "url": "https://github.com/spatie/laravel-activitylog.git", - "reference": "bb879775d487438ed9a99e64f09086b608990c10" + "reference": "2a2024fcac05628b0d1bfdbb1b94dda8b0661dc0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-activitylog/zipball/bb879775d487438ed9a99e64f09086b608990c10", - "reference": "bb879775d487438ed9a99e64f09086b608990c10", + "url": "https://api.github.com/repos/spatie/laravel-activitylog/zipball/2a2024fcac05628b0d1bfdbb1b94dda8b0661dc0", + "reference": "2a2024fcac05628b0d1bfdbb1b94dda8b0661dc0", "shasum": "" }, "require": { - "illuminate/config": "^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0", - "illuminate/database": "^8.69 || ^9.27 || ^10.0 || ^11.0 || ^12.0", - "illuminate/support": "^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0", + "illuminate/config": "^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0 || ^13.0", + "illuminate/database": "^8.69 || ^9.27 || ^10.0 || ^11.0 || ^12.0 || ^13.0", + "illuminate/support": "^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0 || ^13.0", "php": "^8.1", "spatie/laravel-package-tools": "^1.6.3" }, "require-dev": { "ext-json": "*", - "orchestra/testbench": "^6.23 || ^7.0 || ^8.0 || ^9.0 || ^10.0", - "pestphp/pest": "^1.20 || ^2.0 || ^3.0" + "orchestra/testbench": "^6.23 || ^7.0 || ^8.0 || ^9.6 || ^10.0 || ^11.0", + "pestphp/pest": "^1.20 || ^2.0 || ^3.0 || ^4.0" }, "type": "library", "extra": { @@ -5908,7 +6086,7 @@ ], "support": { "issues": "https://github.com/spatie/laravel-activitylog/issues", - "source": "https://github.com/spatie/laravel-activitylog/tree/4.10.2" + "source": "https://github.com/spatie/laravel-activitylog/tree/4.12.3" }, "funding": [ { @@ -5920,32 +6098,32 @@ "type": "github" } ], - "time": "2025-06-15T06:59:49+00:00" + "time": "2026-03-24T12:33:53+00:00" }, { "name": "spatie/laravel-csp", - "version": "2.10.3", + "version": "3.25.1", "source": { "type": "git", "url": "https://github.com/spatie/laravel-csp.git", - "reference": "15e40c28d46076b3d5a4268b63040b3900cc05d3" + "reference": "f33be05072dc028586632041eaab69c6bf33dd21" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-csp/zipball/15e40c28d46076b3d5a4268b63040b3900cc05d3", - "reference": "15e40c28d46076b3d5a4268b63040b3900cc05d3", + "url": "https://api.github.com/repos/spatie/laravel-csp/zipball/f33be05072dc028586632041eaab69c6bf33dd21", + "reference": "f33be05072dc028586632041eaab69c6bf33dd21", "shasum": "" }, "require": { - "illuminate/http": "^9.0|^10.0|^11.36.1|^12", - "illuminate/support": "^9.0|^10.0|^11.36.1|^12", - "php": "^8.1", + "illuminate/http": "^11.36.1|^12.0|^13.0", + "illuminate/support": "^11.36.1|^12.0|^13.0", + "php": "^8.3", "spatie/laravel-package-tools": "^1.17" }, "require-dev": { - "mockery/mockery": "^1.6.12", - "orchestra/testbench": "^7.0|^8.0|^9.9|^10", - "pestphp/pest": "^1.23.0|^2.36.0|^3", + "mockery/mockery": "^1.6", + "orchestra/testbench": "^9.9|^10.0|^11.0", + "pestphp/pest": "^3.0|^4.0", "roave/security-advisories": "dev-master" }, "type": "library", @@ -5957,9 +6135,6 @@ } }, "autoload": { - "files": [ - "src/helpers.php" - ], "psr-4": { "Spatie\\Csp\\": "src" } @@ -5980,6 +6155,12 @@ "email": "freek@spatie.be", "homepage": "https://spatie.be", "role": "Developer" + }, + { + "name": "Sebastian De Deyne", + "email": "sebastian@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" } ], "description": "Add CSP headers to the responses of a Laravel app", @@ -5994,7 +6175,8 @@ "spatie" ], "support": { - "source": "https://github.com/spatie/laravel-csp/tree/2.10.3" + "issues": "https://github.com/spatie/laravel-csp/issues", + "source": "https://github.com/spatie/laravel-csp/tree/3.25.1" }, "funding": [ { @@ -6002,25 +6184,25 @@ "type": "custom" } ], - "time": "2025-02-14T13:23:32+00:00" + "time": "2026-06-02T08:00:44+00:00" }, { "name": "spatie/laravel-fractal", - "version": "6.3.2", + "version": "6.4.0", "source": { "type": "git", "url": "https://github.com/spatie/laravel-fractal.git", - "reference": "d078aa670233100e1309a0a7096c42f5b605ef29" + "reference": "d34114259233540dcb405e45dff863164fb426c5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-fractal/zipball/d078aa670233100e1309a0a7096c42f5b605ef29", - "reference": "d078aa670233100e1309a0a7096c42f5b605ef29", + "url": "https://api.github.com/repos/spatie/laravel-fractal/zipball/d34114259233540dcb405e45dff863164fb426c5", + "reference": "d34114259233540dcb405e45dff863164fb426c5", "shasum": "" }, "require": { - "illuminate/contracts": "^8.0|^9.0|^10.0|^11.0|^12.0", - "illuminate/support": "^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/contracts": "^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/support": "^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", "league/fractal": "^0.20.1|^0.20", "nesbot/carbon": "^2.63|^3.0", "php": "^8.0", @@ -6029,8 +6211,8 @@ }, "require-dev": { "ext-json": "*", - "orchestra/testbench": "^7.0|^8.0|^9.0|^10.0", - "pestphp/pest": "^1.22|^2.34|^3.0" + "orchestra/testbench": "^7.0|^8.0|^9.2|^10.0|^11.0", + "pestphp/pest": "^1.22|^2.34|^3.0|^4.0" }, "type": "library", "extra": { @@ -6075,7 +6257,7 @@ "transform" ], "support": { - "source": "https://github.com/spatie/laravel-fractal/tree/6.3.2" + "source": "https://github.com/spatie/laravel-fractal/tree/6.4.0" }, "funding": [ { @@ -6083,31 +6265,31 @@ "type": "custom" } ], - "time": "2025-02-14T10:43:50+00:00" + "time": "2026-02-21T15:58:07+00:00" }, { "name": "spatie/laravel-html", - "version": "3.5.0", + "version": "3.13.0", "source": { "type": "git", "url": "https://github.com/spatie/laravel-html.git", - "reference": "ead179a8b6802647027486049f5209bd23b610a9" + "reference": "0579bf41959b4c4068206ec9bf1fcd1b59d8fa25" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-html/zipball/ead179a8b6802647027486049f5209bd23b610a9", - "reference": "ead179a8b6802647027486049f5209bd23b610a9", + "url": "https://api.github.com/repos/spatie/laravel-html/zipball/0579bf41959b4c4068206ec9bf1fcd1b59d8fa25", + "reference": "0579bf41959b4c4068206ec9bf1fcd1b59d8fa25", "shasum": "" }, "require": { - "illuminate/http": "^9.0|^8.0|^10.0", - "illuminate/support": "^9.0|^8.0|^10.0", - "php": "^8.0" + "illuminate/http": "^10.0|^11.0|^12.0|^13.0", + "illuminate/support": "^10.0|^11.0|^12.0|^13.0", + "php": "^8.2" }, "require-dev": { "mockery/mockery": "^1.3", - "orchestra/testbench": "^7.0|^6.23|^8.0", - "pestphp/pest": "^1.22" + "orchestra/testbench": "^8.0|^9.0|^10.0|^11.0", + "pestphp/pest": "^2.34|^3.7|^4.0" }, "type": "library", "extra": { @@ -6153,7 +6335,7 @@ "spatie" ], "support": { - "source": "https://github.com/spatie/laravel-html/tree/3.5.0" + "source": "https://github.com/spatie/laravel-html/tree/3.13.0" }, "funding": [ { @@ -6161,30 +6343,30 @@ "type": "custom" } ], - "time": "2024-02-20T15:17:00+00:00" + "time": "2026-02-22T09:05:56+00:00" }, { "name": "spatie/laravel-json-api-paginate", - "version": "1.16.3", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/spatie/laravel-json-api-paginate.git", - "reference": "2acf8b754e088285f1a29fdd5b2295769aee76f4" + "reference": "88ef7c81912c44204a27b618b60efc3a4db54b9b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-json-api-paginate/zipball/2acf8b754e088285f1a29fdd5b2295769aee76f4", - "reference": "2acf8b754e088285f1a29fdd5b2295769aee76f4", + "url": "https://api.github.com/repos/spatie/laravel-json-api-paginate/zipball/88ef7c81912c44204a27b618b60efc3a4db54b9b", + "reference": "88ef7c81912c44204a27b618b60efc3a4db54b9b", "shasum": "" }, "require": { - "illuminate/support": "^10.0|^11.0|^12.0", - "php": "^8.0" + "illuminate/support": "^10.0|^11.0|^12.0|^13.0", + "php": "^8.2" }, "require-dev": { - "orchestra/testbench": "^8.21.1|^9.0|^10.0", - "pestphp/pest": "^2.34|^3.7", - "phpunit/phpunit": "^9.0|^10.5.10|^11.5.3" + "orchestra/testbench": "^8.21.1|^9.0|^10.0|^11.0", + "pestphp/pest": "^2.0|^3.7|^4.0", + "phpunit/phpunit": "^9.0|^10.5.10|^11.5.3|^12.0" }, "type": "library", "extra": { @@ -6218,7 +6400,8 @@ "spatie" ], "support": { - "source": "https://github.com/spatie/laravel-json-api-paginate/tree/1.16.3" + "issues": "https://github.com/spatie/laravel-json-api-paginate/issues", + "source": "https://github.com/spatie/laravel-json-api-paginate/tree/2.0.1" }, "funding": [ { @@ -6226,33 +6409,33 @@ "type": "custom" } ], - "time": "2025-02-25T20:27:30+00:00" + "time": "2026-02-21T21:16:48+00:00" }, { "name": "spatie/laravel-package-tools", - "version": "1.92.7", + "version": "1.93.1", "source": { "type": "git", "url": "https://github.com/spatie/laravel-package-tools.git", - "reference": "f09a799850b1ed765103a4f0b4355006360c49a5" + "reference": "d5552849801f2642aea710557463234b59ef65eb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/f09a799850b1ed765103a4f0b4355006360c49a5", - "reference": "f09a799850b1ed765103a4f0b4355006360c49a5", + "url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/d5552849801f2642aea710557463234b59ef65eb", + "reference": "d5552849801f2642aea710557463234b59ef65eb", "shasum": "" }, "require": { - "illuminate/contracts": "^9.28|^10.0|^11.0|^12.0", - "php": "^8.0" + "illuminate/contracts": "^10.0|^11.0|^12.0|^13.0", + "php": "^8.1" }, "require-dev": { "mockery/mockery": "^1.5", - "orchestra/testbench": "^7.7|^8.0|^9.0|^10.0", - "pestphp/pest": "^1.23|^2.1|^3.1", - "phpunit/php-code-coverage": "^9.0|^10.0|^11.0", - "phpunit/phpunit": "^9.5.24|^10.5|^11.5", - "spatie/pest-plugin-test-time": "^1.1|^2.2" + "orchestra/testbench": "^8.0|^9.2|^10.0|^11.0", + "pestphp/pest": "^2.1|^3.1|^4.0", + "phpunit/php-code-coverage": "^10.0|^11.0|^12.0", + "phpunit/phpunit": "^10.5|^11.5|^12.5", + "spatie/pest-plugin-test-time": "^2.2|^3.0" }, "type": "library", "autoload": { @@ -6279,7 +6462,7 @@ ], "support": { "issues": "https://github.com/spatie/laravel-package-tools/issues", - "source": "https://github.com/spatie/laravel-package-tools/tree/1.92.7" + "source": "https://github.com/spatie/laravel-package-tools/tree/1.93.1" }, "funding": [ { @@ -6287,34 +6470,35 @@ "type": "github" } ], - "time": "2025-07-17T15:46:43+00:00" + "time": "2026-05-19T14:06:37+00:00" }, { "name": "spatie/laravel-permission", - "version": "6.21.0", + "version": "6.25.0", "source": { "type": "git", "url": "https://github.com/spatie/laravel-permission.git", - "reference": "6a118e8855dfffcd90403aab77bbf35a03db51b3" + "reference": "d7d4cb0d58616722f1afc90e0484e4825155b9b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-permission/zipball/6a118e8855dfffcd90403aab77bbf35a03db51b3", - "reference": "6a118e8855dfffcd90403aab77bbf35a03db51b3", + "url": "https://api.github.com/repos/spatie/laravel-permission/zipball/d7d4cb0d58616722f1afc90e0484e4825155b9b3", + "reference": "d7d4cb0d58616722f1afc90e0484e4825155b9b3", "shasum": "" }, "require": { - "illuminate/auth": "^8.12|^9.0|^10.0|^11.0|^12.0", - "illuminate/container": "^8.12|^9.0|^10.0|^11.0|^12.0", - "illuminate/contracts": "^8.12|^9.0|^10.0|^11.0|^12.0", - "illuminate/database": "^8.12|^9.0|^10.0|^11.0|^12.0", + "illuminate/auth": "^8.12|^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/container": "^8.12|^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/contracts": "^8.12|^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/database": "^8.12|^9.0|^10.0|^11.0|^12.0|^13.0", "php": "^8.0" }, "require-dev": { - "laravel/passport": "^11.0|^12.0", + "laravel/passport": "^11.0|^12.0|^13.0", "laravel/pint": "^1.0", - "orchestra/testbench": "^6.23|^7.0|^8.0|^9.0|^10.0", - "phpunit/phpunit": "^9.4|^10.1|^11.5" + "orchestra/testbench": "^6.23|^7.0|^8.0|^9.0|^10.0|^11.0", + "pestphp/pest": "^2.0|^3.0|^4.0", + "pestphp/pest-plugin-laravel": "^2.0|^3.0|^4.0" }, "type": "library", "extra": { @@ -6362,7 +6546,7 @@ ], "support": { "issues": "https://github.com/spatie/laravel-permission/issues", - "source": "https://github.com/spatie/laravel-permission/tree/6.21.0" + "source": "https://github.com/spatie/laravel-permission/tree/6.25.0" }, "funding": [ { @@ -6370,37 +6554,37 @@ "type": "github" } ], - "time": "2025-07-23T16:08:05+00:00" + "time": "2026-03-17T22:46:46+00:00" }, { "name": "spatie/laravel-query-builder", - "version": "5.7.0", + "version": "6.4.4", "source": { "type": "git", "url": "https://github.com/spatie/laravel-query-builder.git", - "reference": "ff61a9ea32c5b55cbe63de8e3c6e35a201e6b746" + "reference": "ab9c4c369fc913d6c020a0f6776f3c82f3e523fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-query-builder/zipball/ff61a9ea32c5b55cbe63de8e3c6e35a201e6b746", - "reference": "ff61a9ea32c5b55cbe63de8e3c6e35a201e6b746", + "url": "https://api.github.com/repos/spatie/laravel-query-builder/zipball/ab9c4c369fc913d6c020a0f6776f3c82f3e523fb", + "reference": "ab9c4c369fc913d6c020a0f6776f3c82f3e523fb", "shasum": "" }, "require": { - "illuminate/database": "^9.0|^10.0", - "illuminate/http": "^9.0|^10.0", - "illuminate/support": "^9.0|^10.0", - "php": "^8.0", + "illuminate/database": "^10.0|^11.0|^12.0|^13.0", + "illuminate/http": "^10.0|^11.0|^12.0|^13.0", + "illuminate/support": "^10.0|^11.0|^12.0|^13.0", + "php": "^8.2", "spatie/laravel-package-tools": "^1.11" }, "require-dev": { "ext-json": "*", + "larastan/larastan": "^2.7 || ^3.3", "mockery/mockery": "^1.4", - "orchestra/testbench": "^7.0|^8.0", - "pestphp/pest": "^1.20", - "phpunit/phpunit": "^9.6.11", - "spatie/invade": "^2.0", - "spatie/laravel-ray": "^1.28" + "orchestra/testbench": "^7.0|^8.0|^10.0|^11.0", + "pestphp/pest": "^2.0|^3.7|^4.0", + "phpunit/phpunit": "^10.0|^11.5.3|^12.0", + "spatie/invade": "^2.0" }, "type": "library", "extra": { @@ -6444,20 +6628,20 @@ "type": "custom" } ], - "time": "2024-01-08T09:57:13+00:00" + "time": "2026-03-08T13:45:05+00:00" }, { "name": "stevebauman/location", - "version": "v7.6.0", + "version": "v7.6.3", "source": { "type": "git", "url": "https://github.com/stevebauman/location.git", - "reference": "3867682db8228c9b3ccf88e4cc80ce63aa73d60b" + "reference": "ed76d047c40dc13646438e486a8981102476adbe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/stevebauman/location/zipball/3867682db8228c9b3ccf88e4cc80ce63aa73d60b", - "reference": "3867682db8228c9b3ccf88e4cc80ce63aa73d60b", + "url": "https://api.github.com/repos/stevebauman/location/zipball/ed76d047c40dc13646438e486a8981102476adbe", + "reference": "ed76d047c40dc13646438e486a8981102476adbe", "shasum": "" }, "require": { @@ -6465,12 +6649,12 @@ "ext-json": "*", "geoip2/geoip2": "^2.0|^3.0", "guzzlehttp/guzzle": "^7.0", - "illuminate/support": "^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/support": "^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", "php": ">=8.1" }, "require-dev": { "mockery/mockery": "^1.0", - "orchestra/testbench": "^6.0|^7.0|^8.0|^9.0|^10.0", + "orchestra/testbench": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", "pestphp/pest": "^1.0|^2.0|^3.7" }, "type": "library", @@ -6511,53 +6695,131 @@ ], "support": { "issues": "https://github.com/stevebauman/location/issues", - "source": "https://github.com/stevebauman/location/tree/v7.6.0" + "source": "https://github.com/stevebauman/location/tree/v7.6.3" + }, + "time": "2026-03-18T16:51:48+00:00" + }, + { + "name": "symfony/clock", + "version": "v7.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/clock.git", + "reference": "674fa3b98e21531dd040e613479f5f6fa8f32111" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/clock/zipball/674fa3b98e21531dd040e613479f5f6fa8f32111", + "reference": "674fa3b98e21531dd040e613479f5f6fa8f32111", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/clock": "^1.0", + "symfony/polyfill-php83": "^1.28" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/now.php" + ], + "psr-4": { + "Symfony\\Component\\Clock\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Decouples applications from the system clock", + "homepage": "https://symfony.com", + "keywords": [ + "clock", + "psr20", + "time" + ], + "support": { + "source": "https://github.com/symfony/clock/tree/v7.4.8" }, - "time": "2025-08-04T02:33:41+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-03-24T13:12:05+00:00" }, { "name": "symfony/console", - "version": "v6.4.24", + "version": "v7.4.13", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "59266a5bf6a596e3e0844fd95e6ad7ea3c1d3350" + "reference": "85095d2573eaefaf35e40b9513a9bf09f72cd217" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/59266a5bf6a596e3e0844fd95e6ad7ea3c1d3350", - "reference": "59266a5bf6a596e3e0844fd95e6ad7ea3c1d3350", + "url": "https://api.github.com/repos/symfony/console/zipball/85095d2573eaefaf35e40b9513a9bf09f72cd217", + "reference": "85095d2573eaefaf35e40b9513a9bf09f72cd217", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^5.4|^6.0|^7.0" + "symfony/string": "^7.2|^8.0" }, "conflict": { - "symfony/dependency-injection": "<5.4", - "symfony/dotenv": "<5.4", - "symfony/event-dispatcher": "<5.4", - "symfony/lock": "<5.4", - "symfony/process": "<5.4" + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" }, "provide": { "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0|^7.0", - "symfony/dependency-injection": "^5.4|^6.0|^7.0", - "symfony/event-dispatcher": "^5.4|^6.0|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/lock": "^5.4|^6.0|^7.0", - "symfony/messenger": "^5.4|^6.0|^7.0", - "symfony/process": "^5.4|^6.0|^7.0", - "symfony/stopwatch": "^5.4|^6.0|^7.0", - "symfony/var-dumper": "^5.4|^6.0|^7.0" + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/lock": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -6591,7 +6853,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.4.24" + "source": "https://github.com/symfony/console/tree/v7.4.13" }, "funding": [ { @@ -6611,24 +6873,24 @@ "type": "tidelift" } ], - "time": "2025-07-30T10:38:54+00:00" + "time": "2026-05-24T08:56:14+00:00" }, { "name": "symfony/css-selector", - "version": "v6.4.24", + "version": "v7.4.9", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "9b784413143701aa3c94ac1869a159a9e53e8761" + "reference": "b75663ed96cf4756e28e3105476f220f92886cc4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/9b784413143701aa3c94ac1869a159a9e53e8761", - "reference": "9b784413143701aa3c94ac1869a159a9e53e8761", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/b75663ed96cf4756e28e3105476f220f92886cc4", + "reference": "b75663ed96cf4756e28e3105476f220f92886cc4", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" }, "type": "library", "autoload": { @@ -6660,7 +6922,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v6.4.24" + "source": "https://github.com/symfony/css-selector/tree/v7.4.9" }, "funding": [ { @@ -6680,20 +6942,20 @@ "type": "tidelift" } ], - "time": "2025-07-10T08:14:14+00:00" + "time": "2026-04-18T13:18:21+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.6.0", + "version": "v3.7.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" + "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", - "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/50f59d1f3ca46d41ac911f97a78626b6756af35b", + "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b", "shasum": "" }, "require": { @@ -6706,7 +6968,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "3.7-dev" } }, "autoload": { @@ -6731,7 +6993,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.0" }, "funding": [ { @@ -6742,40 +7004,47 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:21:43+00:00" + "time": "2026-04-13T15:52:40+00:00" }, { "name": "symfony/error-handler", - "version": "v6.4.24", + "version": "v7.4.8", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "30fd0b3cf0e972e82636038ce4db0e4fe777112c" + "reference": "8dd79d8af777ee6cba2fd4d98da6ffb839f3c0fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/30fd0b3cf0e972e82636038ce4db0e4fe777112c", - "reference": "30fd0b3cf0e972e82636038ce4db0e4fe777112c", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/8dd79d8af777ee6cba2fd4d98da6ffb839f3c0fa", + "reference": "8dd79d8af777ee6cba2fd4d98da6ffb839f3c0fa", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "psr/log": "^1|^2|^3", - "symfony/var-dumper": "^5.4|^6.0|^7.0" + "symfony/polyfill-php85": "^1.32", + "symfony/var-dumper": "^6.4|^7.0|^8.0" }, "conflict": { "symfony/deprecation-contracts": "<2.5", "symfony/http-kernel": "<6.4" }, "require-dev": { + "symfony/console": "^6.4|^7.0|^8.0", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/serializer": "^5.4|^6.0|^7.0" + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/serializer": "^6.4|^7.0|^8.0", + "symfony/webpack-encore-bundle": "^1.0|^2.0" }, "bin": [ "Resources/bin/patch-type-declarations" @@ -6806,7 +7075,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v6.4.24" + "source": "https://github.com/symfony/error-handler/tree/v7.4.8" }, "funding": [ { @@ -6826,28 +7095,28 @@ "type": "tidelift" } ], - "time": "2025-07-24T08:25:04+00:00" + "time": "2026-03-24T13:12:05+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v6.4.24", + "version": "v7.4.9", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "307a09d8d7228d14a05e5e05b95fffdacab032b2" + "reference": "e4a2e29753c7801f7a8340e066cfa788f3bc8101" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/307a09d8d7228d14a05e5e05b95fffdacab032b2", - "reference": "307a09d8d7228d14a05e5e05b95fffdacab032b2", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/e4a2e29753c7801f7a8340e066cfa788f3bc8101", + "reference": "e4a2e29753c7801f7a8340e066cfa788f3bc8101", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/event-dispatcher-contracts": "^2.5|^3" }, "conflict": { - "symfony/dependency-injection": "<5.4", + "symfony/dependency-injection": "<6.4", "symfony/service-contracts": "<2.5" }, "provide": { @@ -6856,13 +7125,14 @@ }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0|^7.0", - "symfony/dependency-injection": "^5.4|^6.0|^7.0", - "symfony/error-handler": "^5.4|^6.0|^7.0", - "symfony/expression-language": "^5.4|^6.0|^7.0", - "symfony/http-foundation": "^5.4|^6.0|^7.0", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/error-handler": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/framework-bundle": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", "symfony/service-contracts": "^2.5|^3", - "symfony/stopwatch": "^5.4|^6.0|^7.0" + "symfony/stopwatch": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -6890,7 +7160,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v6.4.24" + "source": "https://github.com/symfony/event-dispatcher/tree/v7.4.9" }, "funding": [ { @@ -6910,20 +7180,20 @@ "type": "tidelift" } ], - "time": "2025-07-10T08:14:14+00:00" + "time": "2026-04-18T13:18:21+00:00" }, { "name": "symfony/event-dispatcher-contracts", - "version": "v3.6.0", + "version": "v3.7.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "59eb412e93815df44f05f342958efa9f46b1e586" + "reference": "ccba7060602b7fed0b03c85bf025257f76d9ef32" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/59eb412e93815df44f05f342958efa9f46b1e586", - "reference": "59eb412e93815df44f05f342958efa9f46b1e586", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/ccba7060602b7fed0b03c85bf025257f76d9ef32", + "reference": "ccba7060602b7fed0b03c85bf025257f76d9ef32", "shasum": "" }, "require": { @@ -6937,7 +7207,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "3.7-dev" } }, "autoload": { @@ -6970,7 +7240,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.7.0" }, "funding": [ { @@ -6981,32 +7251,36 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:21:43+00:00" + "time": "2026-01-05T13:30:16+00:00" }, { "name": "symfony/finder", - "version": "v6.4.24", + "version": "v7.4.8", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "73089124388c8510efb8d2d1689285d285937b08" + "reference": "e0be088d22278583a82da281886e8c3592fbf149" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/73089124388c8510efb8d2d1689285d285937b08", - "reference": "73089124388c8510efb8d2d1689285d285937b08", + "url": "https://api.github.com/repos/symfony/finder/zipball/e0be088d22278583a82da281886e8c3592fbf149", + "reference": "e0be088d22278583a82da281886e8c3592fbf149", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" }, "require-dev": { - "symfony/filesystem": "^6.0|^7.0" + "symfony/filesystem": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -7034,7 +7308,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v6.4.24" + "source": "https://github.com/symfony/finder/tree/v7.4.8" }, "funding": [ { @@ -7054,40 +7328,41 @@ "type": "tidelift" } ], - "time": "2025-07-15T12:02:45+00:00" + "time": "2026-03-24T13:12:05+00:00" }, { "name": "symfony/http-foundation", - "version": "v6.4.24", + "version": "v7.4.13", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "0341e41d8d8830c31a1dff5cbc5bdb3ec872a073" + "reference": "bc354f47c62301e990b7874fa662326368508e2c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/0341e41d8d8830c31a1dff5cbc5bdb3ec872a073", - "reference": "0341e41d8d8830c31a1dff5cbc5bdb3ec872a073", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/bc354f47c62301e990b7874fa662326368508e2c", + "reference": "bc354f47c62301e990b7874fa662326368508e2c", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "~1.1", - "symfony/polyfill-php83": "^1.27" + "symfony/polyfill-mbstring": "^1.1" }, "conflict": { + "doctrine/dbal": "<3.6", "symfony/cache": "<6.4.12|>=7.0,<7.1.5" }, "require-dev": { - "doctrine/dbal": "^2.13.1|^3|^4", + "doctrine/dbal": "^3.6|^4", "predis/predis": "^1.1|^2.0", - "symfony/cache": "^6.4.12|^7.1.5", - "symfony/dependency-injection": "^5.4|^6.0|^7.0", - "symfony/expression-language": "^5.4|^6.0|^7.0", - "symfony/http-kernel": "^5.4.12|^6.0.12|^6.1.4|^7.0", - "symfony/mime": "^5.4|^6.0|^7.0", - "symfony/rate-limiter": "^5.4|^6.0|^7.0" + "symfony/cache": "^6.4.12|^7.1.5|^8.0", + "symfony/clock": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/mime": "^6.4|^7.0|^8.0", + "symfony/rate-limiter": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -7115,7 +7390,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v6.4.24" + "source": "https://github.com/symfony/http-foundation/tree/v7.4.13" }, "funding": [ { @@ -7135,77 +7410,78 @@ "type": "tidelift" } ], - "time": "2025-07-10T08:14:14+00:00" + "time": "2026-05-24T11:20:33+00:00" }, { "name": "symfony/http-kernel", - "version": "v6.4.24", + "version": "v7.4.13", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "b81dcdbe34b8e8f7b3fc7b2a47fa065d5bf30726" + "reference": "9df847980c436451f4f51d1284491bb4356dd989" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/b81dcdbe34b8e8f7b3fc7b2a47fa065d5bf30726", - "reference": "b81dcdbe34b8e8f7b3fc7b2a47fa065d5bf30726", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/9df847980c436451f4f51d1284491bb4356dd989", + "reference": "9df847980c436451f4f51d1284491bb4356dd989", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "psr/log": "^1|^2|^3", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/error-handler": "^6.4|^7.0", - "symfony/event-dispatcher": "^5.4|^6.0|^7.0", - "symfony/http-foundation": "^6.4|^7.0", + "symfony/error-handler": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^7.3|^8.0", + "symfony/http-foundation": "^7.4|^8.0", "symfony/polyfill-ctype": "^1.8" }, "conflict": { - "symfony/browser-kit": "<5.4", - "symfony/cache": "<5.4", - "symfony/config": "<6.1", - "symfony/console": "<5.4", + "symfony/browser-kit": "<6.4", + "symfony/cache": "<6.4", + "symfony/config": "<6.4", + "symfony/console": "<6.4", "symfony/dependency-injection": "<6.4", - "symfony/doctrine-bridge": "<5.4", - "symfony/form": "<5.4", - "symfony/http-client": "<5.4", + "symfony/doctrine-bridge": "<6.4", + "symfony/flex": "<2.10", + "symfony/form": "<6.4", + "symfony/http-client": "<6.4", "symfony/http-client-contracts": "<2.5", - "symfony/mailer": "<5.4", - "symfony/messenger": "<5.4", - "symfony/translation": "<5.4", + "symfony/mailer": "<6.4", + "symfony/messenger": "<6.4", + "symfony/translation": "<6.4", "symfony/translation-contracts": "<2.5", - "symfony/twig-bridge": "<5.4", + "symfony/twig-bridge": "<6.4", "symfony/validator": "<6.4", - "symfony/var-dumper": "<6.3", - "twig/twig": "<2.13" + "symfony/var-dumper": "<6.4", + "twig/twig": "<3.12" }, "provide": { "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { "psr/cache": "^1.0|^2.0|^3.0", - "symfony/browser-kit": "^5.4|^6.0|^7.0", - "symfony/clock": "^6.2|^7.0", - "symfony/config": "^6.1|^7.0", - "symfony/console": "^5.4|^6.0|^7.0", - "symfony/css-selector": "^5.4|^6.0|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/dom-crawler": "^5.4|^6.0|^7.0", - "symfony/expression-language": "^5.4|^6.0|^7.0", - "symfony/finder": "^5.4|^6.0|^7.0", + "symfony/browser-kit": "^6.4|^7.0|^8.0", + "symfony/clock": "^6.4|^7.0|^8.0", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/css-selector": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4.1|^7.0.1|^8.0", + "symfony/dom-crawler": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/finder": "^6.4|^7.0|^8.0", "symfony/http-client-contracts": "^2.5|^3", - "symfony/process": "^5.4|^6.0|^7.0", - "symfony/property-access": "^5.4.5|^6.0.5|^7.0", - "symfony/routing": "^5.4|^6.0|^7.0", - "symfony/serializer": "^6.4.4|^7.0.4", - "symfony/stopwatch": "^5.4|^6.0|^7.0", - "symfony/translation": "^5.4|^6.0|^7.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/property-access": "^7.1|^8.0", + "symfony/routing": "^6.4|^7.0|^8.0", + "symfony/serializer": "^7.1|^8.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0", + "symfony/translation": "^6.4|^7.0|^8.0", "symfony/translation-contracts": "^2.5|^3", - "symfony/uid": "^5.4|^6.0|^7.0", - "symfony/validator": "^6.4|^7.0", - "symfony/var-dumper": "^5.4|^6.4|^7.0", - "symfony/var-exporter": "^6.2|^7.0", - "twig/twig": "^2.13|^3.0.4" + "symfony/uid": "^6.4|^7.0|^8.0", + "symfony/validator": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0", + "symfony/var-exporter": "^6.4|^7.0|^8.0", + "twig/twig": "^3.12" }, "type": "library", "autoload": { @@ -7233,7 +7509,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v6.4.24" + "source": "https://github.com/symfony/http-kernel/tree/v7.4.13" }, "funding": [ { @@ -7253,43 +7529,43 @@ "type": "tidelift" } ], - "time": "2025-07-31T09:23:30+00:00" + "time": "2026-05-27T08:31:43+00:00" }, { "name": "symfony/mailer", - "version": "v6.4.24", + "version": "v7.4.12", "source": { "type": "git", "url": "https://github.com/symfony/mailer.git", - "reference": "b4d7fa2c69641109979ed06e98a588d245362062" + "reference": "5cefb712a25f320579615ba9e1942abaeade7dff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mailer/zipball/b4d7fa2c69641109979ed06e98a588d245362062", - "reference": "b4d7fa2c69641109979ed06e98a588d245362062", + "url": "https://api.github.com/repos/symfony/mailer/zipball/5cefb712a25f320579615ba9e1942abaeade7dff", + "reference": "5cefb712a25f320579615ba9e1942abaeade7dff", "shasum": "" }, "require": { "egulias/email-validator": "^2.1.10|^3|^4", - "php": ">=8.1", + "php": ">=8.2", "psr/event-dispatcher": "^1", "psr/log": "^1|^2|^3", - "symfony/event-dispatcher": "^5.4|^6.0|^7.0", - "symfony/mime": "^6.2|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/mime": "^7.2|^8.0", "symfony/service-contracts": "^2.5|^3" }, "conflict": { "symfony/http-client-contracts": "<2.5", - "symfony/http-kernel": "<5.4", - "symfony/messenger": "<6.2", - "symfony/mime": "<6.2", - "symfony/twig-bridge": "<6.2.1" + "symfony/http-kernel": "<6.4", + "symfony/messenger": "<6.4", + "symfony/mime": "<6.4", + "symfony/twig-bridge": "<6.4" }, "require-dev": { - "symfony/console": "^5.4|^6.0|^7.0", - "symfony/http-client": "^5.4|^6.0|^7.0", - "symfony/messenger": "^6.2|^7.0", - "symfony/twig-bridge": "^6.2|^7.0" + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/twig-bridge": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -7317,7 +7593,7 @@ "description": "Helps sending emails", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/mailer/tree/v6.4.24" + "source": "https://github.com/symfony/mailer/tree/v7.4.12" }, "funding": [ { @@ -7337,44 +7613,44 @@ "type": "tidelift" } ], - "time": "2025-07-24T08:25:04+00:00" + "time": "2026-05-20T07:20:23+00:00" }, { "name": "symfony/mime", - "version": "v6.4.24", + "version": "v7.4.13", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "664d5e844a2de5e11c8255d0aef6bc15a9660ac7" + "reference": "a845722765c4f6b2ce88beaf4f4479975b186770" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/664d5e844a2de5e11c8255d0aef6bc15a9660ac7", - "reference": "664d5e844a2de5e11c8255d0aef6bc15a9660ac7", + "url": "https://api.github.com/repos/symfony/mime/zipball/a845722765c4f6b2ce88beaf4f4479975b186770", + "reference": "a845722765c4f6b2ce88beaf4f4479975b186770", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-intl-idn": "^1.10", "symfony/polyfill-mbstring": "^1.0" }, "conflict": { "egulias/email-validator": "~3.0.0", - "phpdocumentor/reflection-docblock": "<3.2.2", - "phpdocumentor/type-resolver": "<1.4.0", - "symfony/mailer": "<5.4", + "phpdocumentor/reflection-docblock": "<5.2|>=7", + "phpdocumentor/type-resolver": "<1.5.1", + "symfony/mailer": "<6.4", "symfony/serializer": "<6.4.3|>7.0,<7.0.3" }, "require-dev": { "egulias/email-validator": "^2.1.10|^3.1|^4", "league/html-to-markdown": "^5.0", - "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", - "symfony/dependency-injection": "^5.4|^6.0|^7.0", - "symfony/process": "^5.4|^6.4|^7.0", - "symfony/property-access": "^5.4|^6.0|^7.0", - "symfony/property-info": "^5.4|^6.0|^7.0", - "symfony/serializer": "^6.4.3|^7.0.3" + "phpdocumentor/reflection-docblock": "^5.2|^6.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/property-access": "^6.4|^7.0|^8.0", + "symfony/property-info": "^6.4|^7.0|^8.0", + "symfony/serializer": "^6.4.3|^7.0.3|^8.0" }, "type": "library", "autoload": { @@ -7406,7 +7682,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v6.4.24" + "source": "https://github.com/symfony/mime/tree/v7.4.13" }, "funding": [ { @@ -7426,20 +7702,20 @@ "type": "tidelift" } ], - "time": "2025-07-15T12:02:45+00:00" + "time": "2026-05-23T16:22:37+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.33.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + "reference": "141046a8f9477948ff284fa65be2095baafb94f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/141046a8f9477948ff284fa65be2095baafb94f2", + "reference": "141046a8f9477948ff284fa65be2095baafb94f2", "shasum": "" }, "require": { @@ -7489,7 +7765,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.37.0" }, "funding": [ { @@ -7509,20 +7785,20 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-04-10T16:19:22+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.33.0", + "version": "v1.38.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" + "reference": "e9247d281d694a5120554d9afaf54e070e88a603" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", - "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/e9247d281d694a5120554d9afaf54e070e88a603", + "reference": "e9247d281d694a5120554d9afaf54e070e88a603", "shasum": "" }, "require": { @@ -7571,7 +7847,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.38.1" }, "funding": [ { @@ -7591,20 +7867,20 @@ "type": "tidelift" } ], - "time": "2025-06-27T09:58:17+00:00" + "time": "2026-05-26T05:58:03+00:00" }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.33.0", + "version": "v1.38.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3" + "reference": "dc21118016c039a66235cf93d96b435ffb282412" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/9614ac4d8061dc257ecc64cba1b140873dce8ad3", - "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/dc21118016c039a66235cf93d96b435ffb282412", + "reference": "dc21118016c039a66235cf93d96b435ffb282412", "shasum": "" }, "require": { @@ -7658,7 +7934,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.38.1" }, "funding": [ { @@ -7678,20 +7954,20 @@ "type": "tidelift" } ], - "time": "2024-09-10T14:38:51+00:00" + "time": "2026-05-25T15:22:23+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.33.0", + "version": "v1.38.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "3833d7255cc303546435cb650316bff708a1c75c" + "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", - "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/2d446c214bdbe5b71bde5011b060a05fece3ae6b", + "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b", "shasum": "" }, "require": { @@ -7743,7 +8019,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.38.0" }, "funding": [ { @@ -7763,20 +8039,20 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-05-25T13:48:31+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.33.0", + "version": "v1.38.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" + "reference": "14c5439eec4ccff081ac14eca2dc57feb2a66d92" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/14c5439eec4ccff081ac14eca2dc57feb2a66d92", + "reference": "14c5439eec4ccff081ac14eca2dc57feb2a66d92", "shasum": "" }, "require": { @@ -7828,7 +8104,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.38.1" }, "funding": [ { @@ -7848,20 +8124,20 @@ "type": "tidelift" } ], - "time": "2024-12-23T08:48:59+00:00" + "time": "2026-05-26T12:51:13+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.33.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608" + "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608", - "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dfb55726c3a76ea3b6459fcfda1ec2d80a682411", + "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411", "shasum": "" }, "require": { @@ -7912,7 +8188,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.37.0" }, "funding": [ { @@ -7932,20 +8208,20 @@ "type": "tidelift" } ], - "time": "2025-01-02T08:10:11+00:00" + "time": "2026-04-10T16:19:22+00:00" }, { "name": "symfony/polyfill-php83", - "version": "v1.33.0", + "version": "v1.38.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php83.git", - "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5" + "reference": "8339098cae28673c15cce00d80734af0453054e2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/17f6f9a6b1735c0f163024d959f700cfbc5155e5", - "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/8339098cae28673c15cce00d80734af0453054e2", + "reference": "8339098cae28673c15cce00d80734af0453054e2", "shasum": "" }, "require": { @@ -7992,7 +8268,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php83/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-php83/tree/v1.38.1" }, "funding": [ { @@ -8012,31 +8288,25 @@ "type": "tidelift" } ], - "time": "2025-07-08T02:45:35+00:00" + "time": "2026-05-26T12:51:13+00:00" }, { - "name": "symfony/polyfill-uuid", - "version": "v1.33.0", + "name": "symfony/polyfill-php84", + "version": "v1.38.1", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-uuid.git", - "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2" + "url": "https://github.com/symfony/polyfill-php84.git", + "reference": "f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/21533be36c24be3f4b1669c4725c7d1d2bab4ae2", - "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2", + "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa", + "reference": "f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa", "shasum": "" }, "require": { "php": ">=7.2" }, - "provide": { - "ext-uuid": "*" - }, - "suggest": { - "ext-uuid": "For best performance" - }, "type": "library", "extra": { "thanks": { @@ -8049,8 +8319,11 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Uuid\\": "" - } + "Symfony\\Polyfill\\Php84\\": "" + }, + "classmap": [ + "Resources/stubs" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -8058,24 +8331,24 @@ ], "authors": [ { - "name": "Grégoire Pineau", - "email": "lyrixx@lyrixx.info" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for uuid functions", + "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", "polyfill", "portable", - "uuid" + "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-uuid/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-php84/tree/v1.38.1" }, "funding": [ { @@ -8095,32 +8368,41 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-05-26T12:51:13+00:00" }, { - "name": "symfony/process", - "version": "v6.4.24", + "name": "symfony/polyfill-php85", + "version": "v1.38.1", "source": { "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "8eb6dc555bfb49b2703438d5de65cc9f138ff50b" + "url": "https://github.com/symfony/polyfill-php85.git", + "reference": "ba2ba04f3352cfa2dcbbcb90aee13ed967f505b1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/8eb6dc555bfb49b2703438d5de65cc9f138ff50b", - "reference": "8eb6dc555bfb49b2703438d5de65cc9f138ff50b", + "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/ba2ba04f3352cfa2dcbbcb90aee13ed967f505b1", + "reference": "ba2ba04f3352cfa2dcbbcb90aee13ed967f505b1", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=7.2" }, "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\Component\\Process\\": "" + "Symfony\\Polyfill\\Php85\\": "" }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", @@ -8129,18 +8411,24 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Executes commands in sub-processes", + "description": "Symfony polyfill backporting some PHP 8.5+ features to lower PHP versions", "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], "support": { - "source": "https://github.com/symfony/process/tree/v6.4.24" + "source": "https://github.com/symfony/polyfill-php85/tree/v1.38.1" }, "funding": [ { @@ -8160,40 +8448,266 @@ "type": "tidelift" } ], - "time": "2025-07-10T08:14:14+00:00" + "time": "2026-05-26T02:25:22+00:00" }, { - "name": "symfony/routing", - "version": "v6.4.24", + "name": "symfony/polyfill-php86", + "version": "v1.38.0", "source": { "type": "git", - "url": "https://github.com/symfony/routing.git", - "reference": "e4f94e625c8e6f910aa004a0042f7b2d398278f5" + "url": "https://github.com/symfony/polyfill-php86.git", + "reference": "fcec68d64f46dc84e1f6ffcf2c6dda40ff3143ad" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/e4f94e625c8e6f910aa004a0042f7b2d398278f5", - "reference": "e4f94e625c8e6f910aa004a0042f7b2d398278f5", + "url": "https://api.github.com/repos/symfony/polyfill-php86/zipball/fcec68d64f46dc84e1f6ffcf2c6dda40ff3143ad", + "reference": "fcec68d64f46dc84e1f6ffcf2c6dda40ff3143ad", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/deprecation-contracts": "^2.5|^3" - }, - "conflict": { - "doctrine/annotations": "<1.12", - "symfony/config": "<6.2", - "symfony/dependency-injection": "<5.4", - "symfony/yaml": "<5.4" - }, - "require-dev": { - "doctrine/annotations": "^1.12|^2", - "psr/log": "^1|^2|^3", - "symfony/config": "^6.2|^7.0", - "symfony/dependency-injection": "^5.4|^6.0|^7.0", - "symfony/expression-language": "^5.4|^6.0|^7.0", - "symfony/http-foundation": "^5.4|^6.0|^7.0", - "symfony/yaml": "^5.4|^6.0|^7.0" + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php86\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.6+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php86/tree/v1.38.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-25T11:52:35+00:00" + }, + { + "name": "symfony/polyfill-uuid", + "version": "v1.37.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-uuid.git", + "reference": "26dfec253c4cf3e51b541b52ddf7e42cb0908e94" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/26dfec253c4cf3e51b541b52ddf7e42cb0908e94", + "reference": "26dfec253c4cf3e51b541b52ddf7e42cb0908e94", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-uuid": "*" + }, + "suggest": { + "ext-uuid": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Uuid\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for uuid functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "uuid" + ], + "support": { + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.37.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-04-10T16:19:22+00:00" + }, + { + "name": "symfony/process", + "version": "v7.4.13", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "f5804be144caceb570f6747519999636b664f24c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/f5804be144caceb570f6747519999636b664f24c", + "reference": "f5804be144caceb570f6747519999636b664f24c", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v7.4.13" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-23T16:05:06+00:00" + }, + { + "name": "symfony/routing", + "version": "v7.4.13", + "source": { + "type": "git", + "url": "https://github.com/symfony/routing.git", + "reference": "3a162171bb008e5e0f15dce6581373a4c0e8390d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/routing/zipball/3a162171bb008e5e0f15dce6581373a4c0e8390d", + "reference": "3a162171bb008e5e0f15dce6581373a4c0e8390d", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/config": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/yaml": "<6.4" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/yaml": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -8227,7 +8741,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v6.4.24" + "source": "https://github.com/symfony/routing/tree/v7.4.13" }, "funding": [ { @@ -8247,20 +8761,20 @@ "type": "tidelift" } ], - "time": "2025-07-15T08:46:37+00:00" + "time": "2026-05-24T11:20:33+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.6.0", + "version": "v3.7.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4" + "reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4", - "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d25d82433a80eba6aa0e6c24b61d7370d99e444a", + "reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a", "shasum": "" }, "require": { @@ -8278,7 +8792,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "3.7-dev" } }, "autoload": { @@ -8314,7 +8828,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.7.0" }, "funding": [ { @@ -8325,31 +8839,36 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-25T09:37:31+00:00" + "time": "2026-03-28T09:44:51+00:00" }, { "name": "symfony/string", - "version": "v6.4.24", + "version": "v7.4.13", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "f0ce0bd36a3accb4a225435be077b4b4875587f4" + "reference": "961683010db3b27ec6ebcd7308e6e1ee8fa7ffde" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/f0ce0bd36a3accb4a225435be077b4b4875587f4", - "reference": "f0ce0bd36a3accb4a225435be077b4b4875587f4", + "url": "https://api.github.com/repos/symfony/string/zipball/961683010db3b27ec6ebcd7308e6e1ee8fa7ffde", + "reference": "961683010db3b27ec6ebcd7308e6e1ee8fa7ffde", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3.0", "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-grapheme": "~1.33", "symfony/polyfill-intl-normalizer": "~1.0", "symfony/polyfill-mbstring": "~1.0" }, @@ -8357,11 +8876,11 @@ "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/error-handler": "^5.4|^6.0|^7.0", - "symfony/http-client": "^5.4|^6.0|^7.0", - "symfony/intl": "^6.2|^7.0", + "symfony/emoji": "^7.1|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/intl": "^6.4|^7.0|^8.0", "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^5.4|^6.0|^7.0" + "symfony/var-exporter": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -8400,7 +8919,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.4.24" + "source": "https://github.com/symfony/string/tree/v7.4.13" }, "funding": [ { @@ -8420,55 +8939,56 @@ "type": "tidelift" } ], - "time": "2025-07-10T08:14:14+00:00" + "time": "2026-05-23T15:23:29+00:00" }, { "name": "symfony/translation", - "version": "v6.4.24", + "version": "v7.4.10", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "300b72643e89de0734d99a9e3f8494a3ef6936e1" + "reference": "ada7578c30dd5feaa8259cff3e885069ea81ddde" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/300b72643e89de0734d99a9e3f8494a3ef6936e1", - "reference": "300b72643e89de0734d99a9e3f8494a3ef6936e1", + "url": "https://api.github.com/repos/symfony/translation/zipball/ada7578c30dd5feaa8259cff3e885069ea81ddde", + "reference": "ada7578c30dd5feaa8259cff3e885069ea81ddde", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", - "symfony/translation-contracts": "^2.5|^3.0" + "symfony/translation-contracts": "^2.5.3|^3.3" }, "conflict": { - "symfony/config": "<5.4", - "symfony/console": "<5.4", - "symfony/dependency-injection": "<5.4", + "nikic/php-parser": "<5.0", + "symfony/config": "<6.4", + "symfony/console": "<6.4", + "symfony/dependency-injection": "<6.4", "symfony/http-client-contracts": "<2.5", - "symfony/http-kernel": "<5.4", + "symfony/http-kernel": "<6.4", "symfony/service-contracts": "<2.5", - "symfony/twig-bundle": "<5.4", - "symfony/yaml": "<5.4" + "symfony/twig-bundle": "<6.4", + "symfony/yaml": "<6.4" }, "provide": { "symfony/translation-implementation": "2.3|3.0" }, "require-dev": { - "nikic/php-parser": "^4.18|^5.0", + "nikic/php-parser": "^5.0", "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0|^7.0", - "symfony/console": "^5.4|^6.0|^7.0", - "symfony/dependency-injection": "^5.4|^6.0|^7.0", - "symfony/finder": "^5.4|^6.0|^7.0", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/finder": "^6.4|^7.0|^8.0", "symfony/http-client-contracts": "^2.5|^3.0", - "symfony/http-kernel": "^5.4|^6.0|^7.0", - "symfony/intl": "^5.4|^6.0|^7.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/intl": "^6.4|^7.0|^8.0", "symfony/polyfill-intl-icu": "^1.21", - "symfony/routing": "^5.4|^6.0|^7.0", + "symfony/routing": "^6.4|^7.0|^8.0", "symfony/service-contracts": "^2.5|^3", - "symfony/yaml": "^5.4|^6.0|^7.0" + "symfony/yaml": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -8499,7 +9019,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v6.4.24" + "source": "https://github.com/symfony/translation/tree/v7.4.10" }, "funding": [ { @@ -8519,20 +9039,20 @@ "type": "tidelift" } ], - "time": "2025-07-30T17:30:48+00:00" + "time": "2026-05-06T11:19:24+00:00" }, { "name": "symfony/translation-contracts", - "version": "v3.6.0", + "version": "v3.7.0", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "df210c7a2573f1913b2d17cc95f90f53a73d8f7d" + "reference": "0ab302977a952b42fd51475c4ebac81f8da0a95d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/df210c7a2573f1913b2d17cc95f90f53a73d8f7d", - "reference": "df210c7a2573f1913b2d17cc95f90f53a73d8f7d", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/0ab302977a952b42fd51475c4ebac81f8da0a95d", + "reference": "0ab302977a952b42fd51475c4ebac81f8da0a95d", "shasum": "" }, "require": { @@ -8545,7 +9065,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "3.7-dev" } }, "autoload": { @@ -8581,7 +9101,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/translation-contracts/tree/v3.7.0" }, "funding": [ { @@ -8592,33 +9112,37 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-27T08:32:26+00:00" + "time": "2026-01-05T13:30:16+00:00" }, { "name": "symfony/uid", - "version": "v6.4.24", + "version": "v7.4.9", "source": { "type": "git", "url": "https://github.com/symfony/uid.git", - "reference": "17da16a750541a42cf2183935e0f6008316c23f7" + "reference": "2676b524340abcfe4d6151ec698463cebafee439" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/uid/zipball/17da16a750541a42cf2183935e0f6008316c23f7", - "reference": "17da16a750541a42cf2183935e0f6008316c23f7", + "url": "https://api.github.com/repos/symfony/uid/zipball/2676b524340abcfe4d6151ec698463cebafee439", + "reference": "2676b524340abcfe4d6151ec698463cebafee439", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/polyfill-uuid": "^1.15" }, "require-dev": { - "symfony/console": "^5.4|^6.0|^7.0" + "symfony/console": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -8655,7 +9179,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/uid/tree/v6.4.24" + "source": "https://github.com/symfony/uid/tree/v7.4.9" }, "funding": [ { @@ -8675,37 +9199,36 @@ "type": "tidelift" } ], - "time": "2025-07-10T08:14:14+00:00" + "time": "2026-04-30T15:19:22+00:00" }, { "name": "symfony/var-dumper", - "version": "v6.4.24", + "version": "v7.4.8", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "aa29484ce0544bd69fa9f0df902e5ed7b7fe5034" + "reference": "9510c3966f749a1d1ff0059e1eabef6cc621e7fd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/aa29484ce0544bd69fa9f0df902e5ed7b7fe5034", - "reference": "aa29484ce0544bd69fa9f0df902e5ed7b7fe5034", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/9510c3966f749a1d1ff0059e1eabef6cc621e7fd", + "reference": "9510c3966f749a1d1ff0059e1eabef6cc621e7fd", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0" }, "conflict": { - "symfony/console": "<5.4" + "symfony/console": "<6.4" }, "require-dev": { - "symfony/console": "^5.4|^6.0|^7.0", - "symfony/error-handler": "^6.3|^7.0", - "symfony/http-kernel": "^5.4|^6.0|^7.0", - "symfony/process": "^5.4|^6.0|^7.0", - "symfony/uid": "^5.4|^6.0|^7.0", - "twig/twig": "^2.13|^3.0.4" + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/uid": "^6.4|^7.0|^8.0", + "twig/twig": "^3.12" }, "bin": [ "Resources/bin/var-dump-server" @@ -8743,7 +9266,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v6.4.24" + "source": "https://github.com/symfony/var-dumper/tree/v7.4.8" }, "funding": [ { @@ -8763,27 +9286,27 @@ "type": "tidelift" } ], - "time": "2025-07-29T18:40:01+00:00" + "time": "2026-03-30T13:44:50+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", - "version": "v2.3.0", + "version": "v2.4.0", "source": { "type": "git", "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git", - "reference": "0d72ac1c00084279c1816675284073c5a337c20d" + "reference": "f0292ccf0ec75843d65027214426b6b163b48b41" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/0d72ac1c00084279c1816675284073c5a337c20d", - "reference": "0d72ac1c00084279c1816675284073c5a337c20d", + "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/f0292ccf0ec75843d65027214426b6b163b48b41", + "reference": "f0292ccf0ec75843d65027214426b6b163b48b41", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "php": "^7.4 || ^8.0", - "symfony/css-selector": "^5.4 || ^6.0 || ^7.0" + "symfony/css-selector": "^5.4 || ^6.0 || ^7.0 || ^8.0" }, "require-dev": { "phpstan/phpstan": "^2.0", @@ -8816,9 +9339,9 @@ "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles", "support": { "issues": "https://github.com/tijsverkoyen/CssToInlineStyles/issues", - "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/v2.3.0" + "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/v2.4.0" }, - "time": "2024-12-21T16:25:41+00:00" + "time": "2025-12-02T11:56:42+00:00" }, { "name": "ua-parser/uap-php", @@ -8885,26 +9408,26 @@ }, { "name": "vlucas/phpdotenv", - "version": "v5.6.2", + "version": "v5.6.3", "source": { "type": "git", "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "24ac4c74f91ee2c193fa1aaa5c249cb0822809af" + "reference": "955e7815d677a3eaa7075231212f2110983adecc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/24ac4c74f91ee2c193fa1aaa5c249cb0822809af", - "reference": "24ac4c74f91ee2c193fa1aaa5c249cb0822809af", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/955e7815d677a3eaa7075231212f2110983adecc", + "reference": "955e7815d677a3eaa7075231212f2110983adecc", "shasum": "" }, "require": { "ext-pcre": "*", - "graham-campbell/result-type": "^1.1.3", + "graham-campbell/result-type": "^1.1.4", "php": "^7.2.5 || ^8.0", - "phpoption/phpoption": "^1.9.3", - "symfony/polyfill-ctype": "^1.24", - "symfony/polyfill-mbstring": "^1.24", - "symfony/polyfill-php80": "^1.24" + "phpoption/phpoption": "^1.9.5", + "symfony/polyfill-ctype": "^1.26", + "symfony/polyfill-mbstring": "^1.26", + "symfony/polyfill-php80": "^1.26" }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", @@ -8953,7 +9476,7 @@ ], "support": { "issues": "https://github.com/vlucas/phpdotenv/issues", - "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.2" + "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.3" }, "funding": [ { @@ -8965,27 +9488,27 @@ "type": "tidelift" } ], - "time": "2025-04-30T23:37:27+00:00" + "time": "2025-12-27T19:49:13+00:00" }, { "name": "voku/portable-ascii", - "version": "2.0.3", + "version": "2.1.1", "source": { "type": "git", "url": "https://github.com/voku/portable-ascii.git", - "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d" + "reference": "8e1051fe39379367aecf014f41744ce7539a856f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", - "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", + "url": "https://api.github.com/repos/voku/portable-ascii/zipball/8e1051fe39379367aecf014f41744ce7539a856f", + "reference": "8e1051fe39379367aecf014f41744ce7539a856f", "shasum": "" }, "require": { - "php": ">=7.0.0" + "php": ">=7.1.0" }, "require-dev": { - "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0" + "phpunit/phpunit": "~8.5 || ~9.6 || ~10.5 || ~11.5" }, "suggest": { "ext-intl": "Use Intl for transliterator_transliterate() support" @@ -9015,7 +9538,7 @@ ], "support": { "issues": "https://github.com/voku/portable-ascii/issues", - "source": "https://github.com/voku/portable-ascii/tree/2.0.3" + "source": "https://github.com/voku/portable-ascii/tree/2.1.1" }, "funding": [ { @@ -9039,93 +9562,43 @@ "type": "tidelift" } ], - "time": "2024-11-21T01:49:47+00:00" - }, - { - "name": "webmozart/assert", - "version": "1.11.0", - "source": { - "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "php": "^7.2 || ^8.0" - }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.13" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.11.0" - }, - "time": "2022-06-03T18:03:27+00:00" + "time": "2026-04-26T05:33:54+00:00" }, { "name": "yajra/laravel-datatables", - "version": "v10.1.0", + "version": "v13.0.0", "source": { "type": "git", "url": "https://github.com/yajra/datatables.git", - "reference": "7837d079edd88f088755a62b254e32068dfe16e1" + "reference": "7d471daf6caf7ae70aa882e6670c6b994d58fa7d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/yajra/datatables/zipball/7837d079edd88f088755a62b254e32068dfe16e1", - "reference": "7837d079edd88f088755a62b254e32068dfe16e1", + "url": "https://api.github.com/repos/yajra/datatables/zipball/7d471daf6caf7ae70aa882e6670c6b994d58fa7d", + "reference": "7d471daf6caf7ae70aa882e6670c6b994d58fa7d", "shasum": "" }, "require": { - "php": "^8.1", - "yajra/laravel-datatables-buttons": "^10", - "yajra/laravel-datatables-editor": "1.*", - "yajra/laravel-datatables-export": "^10", - "yajra/laravel-datatables-fractal": "^10", - "yajra/laravel-datatables-html": "^10", - "yajra/laravel-datatables-oracle": "^10" + "php": "^8.3", + "yajra/laravel-datatables-buttons": "^13", + "yajra/laravel-datatables-editor": "^13", + "yajra/laravel-datatables-export": "^13", + "yajra/laravel-datatables-fractal": "^13", + "yajra/laravel-datatables-html": "^13", + "yajra/laravel-datatables-oracle": "^13" + }, + "require-dev": { + "larastan/larastan": "^3.1", + "laravel/pint": "^1.21", + "orchestra/testbench": "^11", + "pestphp/pest": "^4", + "pestphp/pest-plugin-laravel": "^4", + "rector/rector": "^2.0.9" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "10.0-dev" + "dev-master": "13.x-dev" } }, "notification-url": "https://packagist.org/downloads/", @@ -9146,7 +9619,7 @@ ], "support": { "issues": "https://github.com/yajra/datatables/issues", - "source": "https://github.com/yajra/datatables/tree/v10.1.0" + "source": "https://github.com/yajra/datatables/tree/v13.0.0" }, "funding": [ { @@ -9154,40 +9627,41 @@ "type": "github" } ], - "time": "2023-02-20T05:26:34+00:00" + "time": "2026-03-25T07:43:24+00:00" }, { "name": "yajra/laravel-datatables-buttons", - "version": "v10.0.9", + "version": "v13.2.0", "source": { "type": "git", "url": "https://github.com/yajra/laravel-datatables-buttons.git", - "reference": "dc93c0ec8bae12005d7e591beebbe053f0abda7f" + "reference": "426346860a88f69b93a0f51a5af8290e0e4bb55b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/yajra/laravel-datatables-buttons/zipball/dc93c0ec8bae12005d7e591beebbe053f0abda7f", - "reference": "dc93c0ec8bae12005d7e591beebbe053f0abda7f", + "url": "https://api.github.com/repos/yajra/laravel-datatables-buttons/zipball/426346860a88f69b93a0f51a5af8290e0e4bb55b", + "reference": "426346860a88f69b93a0f51a5af8290e0e4bb55b", "shasum": "" }, "require": { - "illuminate/console": "^10", - "php": "^8.1", - "yajra/laravel-datatables-html": "^10", - "yajra/laravel-datatables-oracle": "^10" + "illuminate/console": "^13", + "php": "^8.3", + "yajra/laravel-datatables-html": "^13", + "yajra/laravel-datatables-oracle": "^13" }, "require-dev": { - "barryvdh/laravel-snappy": "^1.0.1", - "larastan/larastan": "^2.4", - "maatwebsite/excel": "^3.1.46", - "orchestra/testbench": "^8", - "rap2hpoutre/fast-excel": "^5.1" + "larastan/larastan": "^3.1", + "laravel/pint": "^1.21", + "maatwebsite/excel": "^3.1.64", + "openspout/openspout": "^4.24 || ^5.3", + "orchestra/testbench": "^11", + "rector/rector": "^2.0" }, "suggest": { "barryvdh/laravel-snappy": "Allows exporting of dataTable to PDF using the print view.", "dompdf/dompdf": "Allows exporting of dataTable to PDF using the DomPDF.", "maatwebsite/excel": "Exporting of dataTables (excel, csv and PDF) using maatwebsite package.", - "rap2hpoutre/fast-excel": "Faster exporting of dataTables using fast-excel package.", + "openspout/openspout": "Faster streaming export of dataTables (Excel/CSV) when using the fast export option (OpenSpout ^4.24 or ^5.3).", "yajra/laravel-datatables-export": "Exporting of dataTables (excel, csv and PDF) via livewire and queue worker." }, "type": "library", @@ -9198,7 +9672,7 @@ ] }, "branch-alias": { - "dev-master": "10.0-dev" + "dev-master": "13.x-dev" } }, "autoload": { @@ -9225,7 +9699,7 @@ ], "support": { "issues": "https://github.com/yajra/laravel-datatables-buttons/issues", - "source": "https://github.com/yajra/laravel-datatables-buttons/tree/v10.0.9" + "source": "https://github.com/yajra/laravel-datatables-buttons/tree/v13.2.0" }, "funding": [ { @@ -9233,31 +9707,34 @@ "type": "github" } ], - "time": "2024-02-27T06:52:35+00:00" + "time": "2026-03-28T09:30:37+00:00" }, { "name": "yajra/laravel-datatables-editor", - "version": "v1.25.4", + "version": "v13.0.0", "source": { "type": "git", "url": "https://github.com/yajra/laravel-datatables-editor.git", - "reference": "23962356700d6b31f49bb119665b13e87303e13f" + "reference": "552b20cc03541d977fffadf55340b3612bff3052" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/yajra/laravel-datatables-editor/zipball/23962356700d6b31f49bb119665b13e87303e13f", - "reference": "23962356700d6b31f49bb119665b13e87303e13f", + "url": "https://api.github.com/repos/yajra/laravel-datatables-editor/zipball/552b20cc03541d977fffadf55340b3612bff3052", + "reference": "552b20cc03541d977fffadf55340b3612bff3052", "shasum": "" }, "require": { - "illuminate/console": "*", - "illuminate/database": "*", - "illuminate/http": "*", - "illuminate/validation": "*", - "php": ">=7.0" + "illuminate/console": "^13", + "illuminate/database": "^13", + "illuminate/http": "^13", + "illuminate/validation": "^13", + "php": "^8.3" }, "require-dev": { - "orchestra/testbench": "~3.5" + "larastan/larastan": "^3.1", + "laravel/pint": "^1.21.2", + "orchestra/testbench": "^11", + "rector/rector": "^2.0.10" }, "type": "library", "extra": { @@ -9267,7 +9744,7 @@ ] }, "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "13.x-dev" } }, "autoload": { @@ -9296,7 +9773,7 @@ ], "support": { "issues": "https://github.com/yajra/laravel-datatables-editor/issues", - "source": "https://github.com/yajra/laravel-datatables-editor/tree/v1.25.4" + "source": "https://github.com/yajra/laravel-datatables-editor/tree/v13.0.0" }, "funding": [ { @@ -9312,33 +9789,37 @@ "type": "patreon" } ], - "time": "2023-02-21T06:57:59+00:00" + "time": "2026-03-18T03:15:37+00:00" }, { "name": "yajra/laravel-datatables-export", - "version": "v10.1.1", + "version": "v13.2.0", "source": { "type": "git", "url": "https://github.com/yajra/laravel-datatables-export.git", - "reference": "18a1a22195af7b22b03ba07b47946b1e0aa176f2" + "reference": "bb2f1ae2d3ee6793472c063f352593404a547745" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/yajra/laravel-datatables-export/zipball/18a1a22195af7b22b03ba07b47946b1e0aa176f2", - "reference": "18a1a22195af7b22b03ba07b47946b1e0aa176f2", + "url": "https://api.github.com/repos/yajra/laravel-datatables-export/zipball/bb2f1ae2d3ee6793472c063f352593404a547745", + "reference": "bb2f1ae2d3ee6793472c063f352593404a547745", "shasum": "" }, "require": { "ext-json": "*", - "livewire/livewire": "^2.11.2|^3.0.1", - "openspout/openspout": "^4.12.1", - "php": "^8.1", - "phpoffice/phpspreadsheet": "^1.27", - "yajra/laravel-datatables": "^10.0" + "livewire/livewire": "^4.2.2", + "openspout/openspout": "^4.24.5 || ^5.0", + "php": "^8.3", + "phpoffice/phpspreadsheet": "^5.5", + "yajra/laravel-datatables-buttons": "^13.0.2" }, "require-dev": { - "nunomaduro/larastan": "^2.4.1", - "orchestra/testbench": "^8.0.1" + "larastan/larastan": "^3.9.3", + "laravel/pint": "^1.29", + "orchestra/testbench": "^11.0", + "pestphp/pest": "^4.4.3", + "pestphp/pest-plugin-laravel": "^4.1", + "rector/rector": "^2.3.9" }, "type": "library", "extra": { @@ -9348,7 +9829,7 @@ ] }, "branch-alias": { - "dev-master": "10.0-dev" + "dev-master": "13.x-dev" } }, "autoload": { @@ -9377,7 +9858,7 @@ ], "support": { "issues": "https://github.com/yajra/laravel-datatables-export/issues", - "source": "https://github.com/yajra/laravel-datatables-export/tree/v10.1.1" + "source": "https://github.com/yajra/laravel-datatables-export/tree/v13.2.0" }, "funding": [ { @@ -9385,30 +9866,32 @@ "type": "github" } ], - "time": "2023-11-14T12:59:00+00:00" + "time": "2026-05-28T02:36:01+00:00" }, { "name": "yajra/laravel-datatables-fractal", - "version": "v10.0.0", + "version": "v13.0.0", "source": { "type": "git", "url": "https://github.com/yajra/laravel-datatables-fractal.git", - "reference": "765198d1f2b3f0a7c0c00f08ee41ba11be4ab1e2" + "reference": "fa31d80c160d9b656344eaf9e14036c5dae07684" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/yajra/laravel-datatables-fractal/zipball/765198d1f2b3f0a7c0c00f08ee41ba11be4ab1e2", - "reference": "765198d1f2b3f0a7c0c00f08ee41ba11be4ab1e2", + "url": "https://api.github.com/repos/yajra/laravel-datatables-fractal/zipball/fa31d80c160d9b656344eaf9e14036c5dae07684", + "reference": "fa31d80c160d9b656344eaf9e14036c5dae07684", "shasum": "" }, "require": { "league/fractal": "^0.20.1", - "php": "^8.1", - "yajra/laravel-datatables-oracle": "^10.0" + "php": "^8.3", + "yajra/laravel-datatables-oracle": "^13" }, "require-dev": { - "nunomaduro/larastan": "^2.4", - "orchestra/testbench": "^7.21" + "larastan/larastan": "^3.1", + "laravel/pint": "^1.21", + "orchestra/testbench": "^11.0", + "rector/rector": "^2.0" }, "type": "library", "extra": { @@ -9418,7 +9901,7 @@ ] }, "branch-alias": { - "dev-master": "10.0-dev" + "dev-master": "13.x-dev" } }, "autoload": { @@ -9445,32 +9928,37 @@ ], "support": { "issues": "https://github.com/yajra/laravel-datatables-fractal/issues", - "source": "https://github.com/yajra/laravel-datatables-fractal/tree/v10.0.0" + "source": "https://github.com/yajra/laravel-datatables-fractal/tree/v13.0.0" }, - "time": "2023-02-07T03:46:04+00:00" + "time": "2026-03-18T02:55:54+00:00" }, { "name": "yajra/laravel-datatables-html", - "version": "v10.12.0", + "version": "v13.0.0", "source": { "type": "git", "url": "https://github.com/yajra/laravel-datatables-html.git", - "reference": "1c0fc1ff2026457ff29ab1a6a3433a44d59b251d" + "reference": "cdb32f509ef2e70e447beaba540d32cfd8ee648d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/yajra/laravel-datatables-html/zipball/1c0fc1ff2026457ff29ab1a6a3433a44d59b251d", - "reference": "1c0fc1ff2026457ff29ab1a6a3433a44d59b251d", + "url": "https://api.github.com/repos/yajra/laravel-datatables-html/zipball/cdb32f509ef2e70e447beaba540d32cfd8ee648d", + "reference": "cdb32f509ef2e70e447beaba540d32cfd8ee648d", "shasum": "" }, "require": { "ext-json": "*", - "php": "^8.1", - "yajra/laravel-datatables-oracle": "^10.0" + "yajra/laravel-datatables-oracle": "^13.0" }, "require-dev": { - "nunomaduro/larastan": "^2.4", - "orchestra/testbench": "^7.21" + "larastan/larastan": "^3.1", + "laravel/pint": "^1.21", + "livewire/livewire": "^3.4|^4.0", + "orchestra/testbench": "^11", + "rector/rector": "^2.0" + }, + "suggest": { + "laravel/livewire": "Required for Livewire layout support." }, "type": "library", "extra": { @@ -9480,7 +9968,7 @@ ] }, "branch-alias": { - "dev-master": "10.0-dev" + "dev-master": "13.x-dev" } }, "autoload": { @@ -9498,17 +9986,18 @@ "email": "aqangeles@gmail.com" } ], - "description": "Laravel DataTables HTML builder plugin for Laravel 5.4+.", + "description": "Laravel DataTables HTML builder plugin", "keywords": [ "JS", "datatables", "html", "jquery", - "laravel" + "laravel", + "yajra" ], "support": { "issues": "https://github.com/yajra/laravel-datatables-html/issues", - "source": "https://github.com/yajra/laravel-datatables-html/tree/v10.12.0" + "source": "https://github.com/yajra/laravel-datatables-html/tree/v13.0.0" }, "funding": [ { @@ -9524,37 +10013,38 @@ "type": "patreon" } ], - "time": "2023-12-15T04:41:00+00:00" + "time": "2026-03-18T03:24:28+00:00" }, { "name": "yajra/laravel-datatables-oracle", - "version": "v10.11.4", + "version": "v13.1.2", "source": { "type": "git", "url": "https://github.com/yajra/laravel-datatables.git", - "reference": "a11dc9cf5ec7f7dc68ad0d5959c5e7aec0af29a5" + "reference": "ded9345b7c00c85ce0fe0cea40e24e61c8489e10" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/yajra/laravel-datatables/zipball/a11dc9cf5ec7f7dc68ad0d5959c5e7aec0af29a5", - "reference": "a11dc9cf5ec7f7dc68ad0d5959c5e7aec0af29a5", + "url": "https://api.github.com/repos/yajra/laravel-datatables/zipball/ded9345b7c00c85ce0fe0cea40e24e61c8489e10", + "reference": "ded9345b7c00c85ce0fe0cea40e24e61c8489e10", "shasum": "" }, "require": { - "illuminate/database": "^9|^10", - "illuminate/filesystem": "^9|^10", - "illuminate/http": "^9|^10", - "illuminate/support": "^9|^10", - "illuminate/view": "^9|^10", - "php": "^8.0.2" + "illuminate/database": "^13", + "illuminate/filesystem": "^13", + "illuminate/http": "^13", + "illuminate/support": "^13", + "illuminate/view": "^13", + "php": "^8.3" }, "require-dev": { - "algolia/algoliasearch-client-php": "^3.4", - "larastan/larastan": "^2.4", - "laravel/scout": "^10.5", - "meilisearch/meilisearch-php": "^1.4", - "orchestra/testbench": "^8", - "yajra/laravel-datatables-html": "^9.3.4|^10" + "algolia/algoliasearch-client-php": "^3.4.1", + "larastan/larastan": "^3.1.0", + "laravel/pint": "^1.14", + "laravel/scout": "^10.8.3", + "meilisearch/meilisearch-php": "^1.6.1", + "orchestra/testbench": "^11", + "rector/rector": "^2.0" }, "suggest": { "yajra/laravel-datatables-buttons": "Plugin for server-side exporting of dataTables.", @@ -9574,7 +10064,7 @@ ] }, "branch-alias": { - "dev-master": "10.x-dev" + "dev-master": "13.x-dev" } }, "autoload": { @@ -9595,15 +10085,16 @@ "email": "aqangeles@gmail.com" } ], - "description": "jQuery DataTables API for Laravel 4|5|6|7|8|9|10", + "description": "jQuery DataTables API for Laravel", "keywords": [ "datatables", "jquery", - "laravel" + "laravel", + "yajra" ], "support": { "issues": "https://github.com/yajra/laravel-datatables/issues", - "source": "https://github.com/yajra/laravel-datatables/tree/v10.11.4" + "source": "https://github.com/yajra/laravel-datatables/tree/v13.1.2" }, "funding": [ { @@ -9611,50 +10102,59 @@ "type": "github" } ], - "time": "2024-02-28T05:00:23+00:00" + "time": "2026-05-19T03:34:29+00:00" } ], "packages-dev": [ { "name": "barryvdh/laravel-debugbar", - "version": "v3.16.0", + "version": "v4.2.8", "source": { "type": "git", - "url": "https://github.com/barryvdh/laravel-debugbar.git", - "reference": "f265cf5e38577d42311f1a90d619bcd3740bea23" + "url": "https://github.com/fruitcake/laravel-debugbar.git", + "reference": "799d70c1101d3f8840dd76ff68ff6a78f9352905" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/f265cf5e38577d42311f1a90d619bcd3740bea23", - "reference": "f265cf5e38577d42311f1a90d619bcd3740bea23", + "url": "https://api.github.com/repos/fruitcake/laravel-debugbar/zipball/799d70c1101d3f8840dd76ff68ff6a78f9352905", + "reference": "799d70c1101d3f8840dd76ff68ff6a78f9352905", "shasum": "" }, "require": { - "illuminate/routing": "^9|^10|^11|^12", - "illuminate/session": "^9|^10|^11|^12", - "illuminate/support": "^9|^10|^11|^12", - "php": "^8.1", - "php-debugbar/php-debugbar": "~2.2.0", - "symfony/finder": "^6|^7" + "illuminate/routing": "^11|^12|^13.0", + "illuminate/session": "^11|^12|^13.0", + "illuminate/support": "^11|^12|^13.0", + "php": "^8.2", + "php-debugbar/php-debugbar": "^3.7.2", + "php-debugbar/symfony-bridge": "^1.1" }, "require-dev": { + "larastan/larastan": "^3", + "laravel/octane": "^2", + "laravel/pennant": "^1", + "laravel/pint": "^1", + "laravel/telescope": "^5.16", + "livewire/livewire": "^3.7|^4", "mockery/mockery": "^1.3.3", - "orchestra/testbench-dusk": "^7|^8|^9|^10", - "phpunit/phpunit": "^9.5.10|^10|^11", - "squizlabs/php_codesniffer": "^3.5" + "orchestra/testbench-dusk": "^9|^10|^11", + "php-debugbar/twig-bridge": "^2.0", + "phpstan/phpstan-phpunit": "^2", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^11", + "shipmonk/phpstan-rules": "^4.3" }, "type": "library", "extra": { "laravel": { "aliases": { - "Debugbar": "Barryvdh\\Debugbar\\Facades\\Debugbar" + "Debugbar": "Fruitcake\\LaravelDebugbar\\Facades\\Debugbar" }, "providers": [ - "Barryvdh\\Debugbar\\ServiceProvider" + "Fruitcake\\LaravelDebugbar\\ServiceProvider" ] }, "branch-alias": { - "dev-master": "3.16-dev" + "dev-master": "4.2-dev" } }, "autoload": { @@ -9662,7 +10162,7 @@ "src/helpers.php" ], "psr-4": { - "Barryvdh\\Debugbar\\": "src/" + "Fruitcake\\LaravelDebugbar\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -9670,6 +10170,10 @@ "MIT" ], "authors": [ + { + "name": "Fruitcake", + "homepage": "https://fruitcake.nl" + }, { "name": "Barry vd. Heuvel", "email": "barryvdh@gmail.com" @@ -9677,6 +10181,7 @@ ], "description": "PHP Debugbar integration for Laravel", "keywords": [ + "barryvdh", "debug", "debugbar", "dev", @@ -9685,8 +10190,8 @@ "webprofiler" ], "support": { - "issues": "https://github.com/barryvdh/laravel-debugbar/issues", - "source": "https://github.com/barryvdh/laravel-debugbar/tree/v3.16.0" + "issues": "https://github.com/fruitcake/laravel-debugbar/issues", + "source": "https://github.com/fruitcake/laravel-debugbar/tree/v4.2.8" }, "funding": [ { @@ -9694,264 +10199,11 @@ "type": "custom" }, { - "url": "https://github.com/barryvdh", - "type": "github" - } - ], - "time": "2025-07-14T11:56:43+00:00" - }, - { - "name": "doctrine/dbal", - "version": "3.10.1", - "source": { - "type": "git", - "url": "https://github.com/doctrine/dbal.git", - "reference": "3626601014388095d3af9de7e9e958623b7ef005" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/3626601014388095d3af9de7e9e958623b7ef005", - "reference": "3626601014388095d3af9de7e9e958623b7ef005", - "shasum": "" - }, - "require": { - "composer-runtime-api": "^2", - "doctrine/deprecations": "^0.5.3|^1", - "doctrine/event-manager": "^1|^2", - "php": "^7.4 || ^8.0", - "psr/cache": "^1|^2|^3", - "psr/log": "^1|^2|^3" - }, - "conflict": { - "doctrine/cache": "< 1.11" - }, - "require-dev": { - "doctrine/cache": "^1.11|^2.0", - "doctrine/coding-standard": "13.0.0", - "fig/log-test": "^1", - "jetbrains/phpstorm-stubs": "2023.1", - "phpstan/phpstan": "2.1.17", - "phpstan/phpstan-strict-rules": "^2", - "phpunit/phpunit": "9.6.23", - "slevomat/coding-standard": "8.16.2", - "squizlabs/php_codesniffer": "3.13.1", - "symfony/cache": "^5.4|^6.0|^7.0", - "symfony/console": "^4.4|^5.4|^6.0|^7.0" - }, - "suggest": { - "symfony/console": "For helpful console commands such as SQL execution and import of files." - }, - "bin": [ - "bin/doctrine-dbal" - ], - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\DBAL\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - } - ], - "description": "Powerful PHP database abstraction layer (DBAL) with many features for database schema introspection and management.", - "homepage": "https://www.doctrine-project.org/projects/dbal.html", - "keywords": [ - "abstraction", - "database", - "db2", - "dbal", - "mariadb", - "mssql", - "mysql", - "oci8", - "oracle", - "pdo", - "pgsql", - "postgresql", - "queryobject", - "sasql", - "sql", - "sqlite", - "sqlserver", - "sqlsrv" - ], - "support": { - "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/3.10.1" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fdbal", - "type": "tidelift" - } - ], - "time": "2025-08-05T12:18:06+00:00" - }, - { - "name": "doctrine/deprecations", - "version": "1.1.5", - "source": { - "type": "git", - "url": "https://github.com/doctrine/deprecations.git", - "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", - "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "conflict": { - "phpunit/phpunit": "<=7.5 || >=13" - }, - "require-dev": { - "doctrine/coding-standard": "^9 || ^12 || ^13", - "phpstan/phpstan": "1.4.10 || 2.1.11", - "phpstan/phpstan-phpunit": "^1.0 || ^2", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12", - "psr/log": "^1 || ^2 || ^3" - }, - "suggest": { - "psr/log": "Allows logging deprecations via PSR-3 logger implementation" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Deprecations\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", - "homepage": "https://www.doctrine-project.org/", - "support": { - "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/1.1.5" - }, - "time": "2025-04-07T20:06:18+00:00" - }, - { - "name": "doctrine/event-manager", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/doctrine/event-manager.git", - "reference": "b680156fa328f1dfd874fd48c7026c41570b9c6e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/event-manager/zipball/b680156fa328f1dfd874fd48c7026c41570b9c6e", - "reference": "b680156fa328f1dfd874fd48c7026c41570b9c6e", - "shasum": "" - }, - "require": { - "php": "^8.1" - }, - "conflict": { - "doctrine/common": "<2.9" - }, - "require-dev": { - "doctrine/coding-standard": "^12", - "phpstan/phpstan": "^1.8.8", - "phpunit/phpunit": "^10.5", - "vimeo/psalm": "^5.24" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Common\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - }, - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com" - } - ], - "description": "The Doctrine Event Manager is a simple PHP event system that was built to be used with the various Doctrine projects.", - "homepage": "https://www.doctrine-project.org/projects/event-manager.html", - "keywords": [ - "event", - "event dispatcher", - "event manager", - "event system", - "events" - ], - "support": { - "issues": "https://github.com/doctrine/event-manager/issues", - "source": "https://github.com/doctrine/event-manager/tree/2.0.1" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fevent-manager", - "type": "tidelift" + "url": "https://github.com/barryvdh", + "type": "github" } ], - "time": "2024-05-22T20:47:39+00:00" + "time": "2026-04-20T13:31:29+00:00" }, { "name": "fakerphp/faker", @@ -10140,16 +10392,16 @@ }, { "name": "laravel/pint", - "version": "v1.20.0", + "version": "v1.29.1", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "53072e8ea22213a7ed168a8a15b96fbb8b82d44b" + "reference": "0770e9b7fafd50d4586881d456d6eb41c9247a80" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/53072e8ea22213a7ed168a8a15b96fbb8b82d44b", - "reference": "53072e8ea22213a7ed168a8a15b96fbb8b82d44b", + "url": "https://api.github.com/repos/laravel/pint/zipball/0770e9b7fafd50d4586881d456d6eb41c9247a80", + "reference": "0770e9b7fafd50d4586881d456d6eb41c9247a80", "shasum": "" }, "require": { @@ -10157,16 +10409,17 @@ "ext-mbstring": "*", "ext-tokenizer": "*", "ext-xml": "*", - "php": "^8.1.0" + "php": "^8.2.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.66.0", - "illuminate/view": "^10.48.25", - "larastan/larastan": "^2.9.12", - "laravel-zero/framework": "^10.48.25", + "friendsofphp/php-cs-fixer": "^3.95.1", + "illuminate/view": "^12.56.0", + "larastan/larastan": "^3.9.6", + "laravel-zero/framework": "^12.1.0", "mockery/mockery": "^1.6.12", - "nunomaduro/termwind": "^1.17.0", - "pestphp/pest": "^2.36.0" + "nunomaduro/termwind": "^2.4.0", + "pestphp/pest": "^3.8.6", + "shipfastlabs/agent-detector": "^1.1.3" }, "bin": [ "builds/pint" @@ -10192,6 +10445,7 @@ "description": "An opinionated code formatter for PHP.", "homepage": "https://laravel.com", "keywords": [ + "dev", "format", "formatter", "lint", @@ -10202,33 +10456,33 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2025-01-14T16:20:53+00:00" + "time": "2026-04-20T15:26:14+00:00" }, { "name": "laravel/sail", - "version": "v1.45.0", + "version": "v1.62.0", "source": { "type": "git", "url": "https://github.com/laravel/sail.git", - "reference": "019a2933ff4a9199f098d4259713f9bc266a874e" + "reference": "3aaeefc979f8ba6586fbc5b6e0b1b3638058f98e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/sail/zipball/019a2933ff4a9199f098d4259713f9bc266a874e", - "reference": "019a2933ff4a9199f098d4259713f9bc266a874e", + "url": "https://api.github.com/repos/laravel/sail/zipball/3aaeefc979f8ba6586fbc5b6e0b1b3638058f98e", + "reference": "3aaeefc979f8ba6586fbc5b6e0b1b3638058f98e", "shasum": "" }, "require": { - "illuminate/console": "^9.52.16|^10.0|^11.0|^12.0", - "illuminate/contracts": "^9.52.16|^10.0|^11.0|^12.0", - "illuminate/support": "^9.52.16|^10.0|^11.0|^12.0", + "illuminate/console": "^9.52.16|^10.0|^11.0|^12.0|^13.0", + "illuminate/contracts": "^9.52.16|^10.0|^11.0|^12.0|^13.0", + "illuminate/support": "^9.52.16|^10.0|^11.0|^12.0|^13.0", "php": "^8.0", - "symfony/console": "^6.0|^7.0", - "symfony/yaml": "^6.0|^7.0" + "symfony/console": "^6.0|^7.0|^8.0", + "symfony/yaml": "^6.0|^7.0|^8.0" }, "require-dev": { - "orchestra/testbench": "^7.0|^8.0|^9.0|^10.0", - "phpstan/phpstan": "^1.10" + "orchestra/testbench": "^7.0|^8.0|^9.0|^10.0|^11.0", + "phpstan/phpstan": "^2.0" }, "bin": [ "bin/sail" @@ -10265,7 +10519,7 @@ "issues": "https://github.com/laravel/sail/issues", "source": "https://github.com/laravel/sail" }, - "time": "2025-08-25T19:28:31+00:00" + "time": "2026-05-27T04:02:01+00:00" }, { "name": "mockery/mockery", @@ -10412,40 +10666,36 @@ }, { "name": "nunomaduro/collision", - "version": "v7.12.0", + "version": "v8.9.4", "source": { "type": "git", "url": "https://github.com/nunomaduro/collision.git", - "reference": "995245421d3d7593a6960822063bdba4f5d7cf1a" + "reference": "716af8f95a470e9094cfca09ed897b023be191a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/collision/zipball/995245421d3d7593a6960822063bdba4f5d7cf1a", - "reference": "995245421d3d7593a6960822063bdba4f5d7cf1a", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/716af8f95a470e9094cfca09ed897b023be191a5", + "reference": "716af8f95a470e9094cfca09ed897b023be191a5", "shasum": "" }, "require": { - "filp/whoops": "^2.17.0", - "nunomaduro/termwind": "^1.17.0", - "php": "^8.1.0", - "symfony/console": "^6.4.17" + "filp/whoops": "^2.18.4", + "nunomaduro/termwind": "^2.4.0", + "php": "^8.2.0", + "symfony/console": "^7.4.8 || ^8.0.8" }, "conflict": { - "laravel/framework": ">=11.0.0" + "laravel/framework": "<11.48.0 || >=14.0.0", + "phpunit/phpunit": "<11.5.50 || >=14.0.0" }, "require-dev": { - "brianium/paratest": "^7.4.8", - "laravel/framework": "^10.48.29", - "laravel/pint": "^1.21.2", - "laravel/sail": "^1.41.0", - "laravel/sanctum": "^3.3.3", - "laravel/tinker": "^2.10.1", - "nunomaduro/larastan": "^2.10.0", - "orchestra/testbench-core": "^8.35.0", - "pestphp/pest": "^2.36.0", - "phpunit/phpunit": "^10.5.36", - "sebastian/environment": "^6.1.0", - "spatie/laravel-ignition": "^2.9.1" + "brianium/paratest": "^7.8.5", + "larastan/larastan": "^3.9.6", + "laravel/framework": "^11.48.0 || ^12.56.0 || ^13.5.0", + "laravel/pint": "^1.29.1", + "orchestra/testbench-core": "^9.12.0 || ^10.12.1 || ^11.2.1", + "pestphp/pest": "^3.8.5 || ^4.4.3 || ^5.0.0", + "sebastian/environment": "^7.2.1 || ^8.0.4 || ^9.3.0" }, "type": "library", "extra": { @@ -10453,6 +10703,9 @@ "providers": [ "NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider" ] + }, + "branch-alias": { + "dev-8.x": "8.x-dev" } }, "autoload": { @@ -10479,6 +10732,7 @@ "cli", "command-line", "console", + "dev", "error", "handling", "laravel", @@ -10504,7 +10758,7 @@ "type": "patreon" } ], - "time": "2025-03-14T22:35:49+00:00" + "time": "2026-04-21T14:04:20+00:00" }, { "name": "phar-io/manifest", @@ -10626,46 +10880,63 @@ }, { "name": "php-debugbar/php-debugbar", - "version": "v2.2.4", + "version": "v3.7.6", "source": { "type": "git", "url": "https://github.com/php-debugbar/php-debugbar.git", - "reference": "3146d04671f51f69ffec2a4207ac3bdcf13a9f35" + "reference": "1690ee1728827f9deb4b60457fa387cf44672c56" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-debugbar/php-debugbar/zipball/3146d04671f51f69ffec2a4207ac3bdcf13a9f35", - "reference": "3146d04671f51f69ffec2a4207ac3bdcf13a9f35", + "url": "https://api.github.com/repos/php-debugbar/php-debugbar/zipball/1690ee1728827f9deb4b60457fa387cf44672c56", + "reference": "1690ee1728827f9deb4b60457fa387cf44672c56", "shasum": "" }, "require": { - "php": "^8", + "php": "^8.2", "psr/log": "^1|^2|^3", - "symfony/var-dumper": "^4|^5|^6|^7" + "symfony/var-dumper": "^5.4|^6|^7|^8" }, "replace": { "maximebf/debugbar": "self.version" }, "require-dev": { - "dbrekelmans/bdi": "^1", - "phpunit/phpunit": "^8|^9", + "dbrekelmans/bdi": "^1.4", + "friendsofphp/php-cs-fixer": "^3.92", + "monolog/monolog": "^3.9", + "php-debugbar/doctrine-bridge": "^3@dev", + "php-debugbar/monolog-bridge": "^1@dev", + "php-debugbar/symfony-bridge": "^1@dev", + "php-debugbar/twig-bridge": "^2@dev", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^10", + "predis/predis": "^3.3", + "shipmonk/phpstan-rules": "^4.3", + "symfony/browser-kit": "^6.4|7.0", + "symfony/dom-crawler": "^6.4|^7", + "symfony/event-dispatcher": "^5.4|^6.4|^7.3|^8.0", + "symfony/http-foundation": "^5.4|^6.4|^7.3|^8.0", + "symfony/mailer": "^5.4|^6.4|^7.3|^8.0", "symfony/panther": "^1|^2.1", - "twig/twig": "^1.38|^2.7|^3.0" + "twig/twig": "^3.11.2" }, "suggest": { - "kriswallsmith/assetic": "The best way to manage assets", - "monolog/monolog": "Log using Monolog", - "predis/predis": "Redis storage" + "php-debugbar/doctrine-bridge": "To integrate Doctrine with php-debugbar.", + "php-debugbar/monolog-bridge": "To integrate Monolog with php-debugbar.", + "php-debugbar/symfony-bridge": "To integrate Symfony with php-debugbar.", + "php-debugbar/twig-bridge": "To integrate Twig with php-debugbar." }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.1-dev" + "dev-master": "3.0-dev" } }, "autoload": { "psr-4": { - "DebugBar\\": "src/DebugBar/" + "DebugBar\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -10689,45 +10960,123 @@ "debug", "debug bar", "debugbar", - "dev" + "dev", + "profiler", + "toolbar" ], "support": { "issues": "https://github.com/php-debugbar/php-debugbar/issues", - "source": "https://github.com/php-debugbar/php-debugbar/tree/v2.2.4" + "source": "https://github.com/php-debugbar/php-debugbar/tree/v3.7.6" + }, + "funding": [ + { + "url": "https://fruitcake.nl", + "type": "custom" + }, + { + "url": "https://github.com/barryvdh", + "type": "github" + } + ], + "time": "2026-04-30T07:31:44+00:00" + }, + { + "name": "php-debugbar/symfony-bridge", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-debugbar/symfony-bridge.git", + "reference": "e37d2debe5d316408b00d0ab2688d9c2cf59b5ad" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-debugbar/symfony-bridge/zipball/e37d2debe5d316408b00d0ab2688d9c2cf59b5ad", + "reference": "e37d2debe5d316408b00d0ab2688d9c2cf59b5ad", + "shasum": "" + }, + "require": { + "php": "^8.2", + "php-debugbar/php-debugbar": "^3.1", + "symfony/http-foundation": "^5.4|^6.4|^7.3|^8.0" + }, + "require-dev": { + "dbrekelmans/bdi": "^1.4", + "phpunit/phpunit": "^10", + "symfony/browser-kit": "^6|^7", + "symfony/dom-crawler": "^6|^7", + "symfony/mailer": "^5.4|^6.4|^7.3|^8.0", + "symfony/panther": "^1|^2.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "DebugBar\\Bridge\\Symfony\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Maxime Bouroumeau-Fuseau", + "email": "maxime.bouroumeau@gmail.com", + "homepage": "http://maximebf.com" + }, + { + "name": "Barry vd. Heuvel", + "email": "barryvdh@gmail.com" + } + ], + "description": "Symfony bridge for PHP Debugbar", + "homepage": "https://github.com/php-debugbar/php-debugbar", + "keywords": [ + "debugbar", + "dev", + "symfony" + ], + "support": { + "issues": "https://github.com/php-debugbar/symfony-bridge/issues", + "source": "https://github.com/php-debugbar/symfony-bridge/tree/v1.1.0" }, - "time": "2025-07-22T14:01:30+00:00" + "time": "2026-01-15T14:47:34+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "10.1.16", + "version": "11.0.12", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "7e308268858ed6baedc8704a304727d20bc07c77" + "reference": "2c1ed04922802c15e1de5d7447b4856de949cf56" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7e308268858ed6baedc8704a304727d20bc07c77", - "reference": "7e308268858ed6baedc8704a304727d20bc07c77", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2c1ed04922802c15e1de5d7447b4856de949cf56", + "reference": "2c1ed04922802c15e1de5d7447b4856de949cf56", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.19.1 || ^5.1.0", - "php": ">=8.1", - "phpunit/php-file-iterator": "^4.1.0", - "phpunit/php-text-template": "^3.0.1", - "sebastian/code-unit-reverse-lookup": "^3.0.0", - "sebastian/complexity": "^3.2.0", - "sebastian/environment": "^6.1.0", - "sebastian/lines-of-code": "^2.0.2", - "sebastian/version": "^4.0.1", - "theseer/tokenizer": "^1.2.3" + "nikic/php-parser": "^5.7.0", + "php": ">=8.2", + "phpunit/php-file-iterator": "^5.1.0", + "phpunit/php-text-template": "^4.0.1", + "sebastian/code-unit-reverse-lookup": "^4.0.1", + "sebastian/complexity": "^4.0.1", + "sebastian/environment": "^7.2.1", + "sebastian/lines-of-code": "^3.0.1", + "sebastian/version": "^5.0.2", + "theseer/tokenizer": "^1.3.1" }, "require-dev": { - "phpunit/phpunit": "^10.1" + "phpunit/phpunit": "^11.5.46" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", @@ -10736,7 +11085,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "10.1.x-dev" + "dev-main": "11.0.x-dev" } }, "autoload": { @@ -10765,40 +11114,52 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.16" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.12" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage", + "type": "tidelift" } ], - "time": "2024-08-22T04:31:57+00:00" + "time": "2025-12-24T07:01:01+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "4.1.0", + "version": "5.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c" + "reference": "2f3a64888c814fc235386b7387dd5b5ed92ad903" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c", - "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/2f3a64888c814fc235386b7387dd5b5ed92ad903", + "reference": "2f3a64888c814fc235386b7387dd5b5ed92ad903", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^11.3" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "4.0-dev" + "dev-main": "5.1-dev" } }, "autoload": { @@ -10826,36 +11187,48 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.1.0" + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.1.1" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-file-iterator", + "type": "tidelift" } ], - "time": "2023-08-31T06:24:48+00:00" + "time": "2026-02-02T13:52:54+00:00" }, { "name": "phpunit/php-invoker", - "version": "4.0.0", + "version": "5.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7" + "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", - "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/c1ca3814734c07492b3d4c5f794f4b0995333da2", + "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" }, "require-dev": { "ext-pcntl": "*", - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^11.0" }, "suggest": { "ext-pcntl": "*" @@ -10863,7 +11236,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "4.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -10889,7 +11262,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-invoker/issues", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0" + "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/5.0.1" }, "funding": [ { @@ -10897,32 +11271,32 @@ "type": "github" } ], - "time": "2023-02-03T06:56:09+00:00" + "time": "2024-07-03T05:07:44+00:00" }, { "name": "phpunit/php-text-template", - "version": "3.0.1", + "version": "4.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748" + "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748", - "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/3e0404dc6b300e6bf56415467ebcb3fe4f33e964", + "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -10949,7 +11323,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-text-template/issues", "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.1" + "source": "https://github.com/sebastianbergmann/php-text-template/tree/4.0.1" }, "funding": [ { @@ -10957,32 +11331,32 @@ "type": "github" } ], - "time": "2023-08-31T14:07:24+00:00" + "time": "2024-07-03T05:08:43+00:00" }, { "name": "phpunit/php-timer", - "version": "6.0.0", + "version": "7.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d" + "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d", - "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", + "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "6.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -11008,7 +11382,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0" + "security": "https://github.com/sebastianbergmann/php-timer/security/policy", + "source": "https://github.com/sebastianbergmann/php-timer/tree/7.0.1" }, "funding": [ { @@ -11016,20 +11391,20 @@ "type": "github" } ], - "time": "2023-02-03T06:57:52+00:00" + "time": "2024-07-03T05:09:35+00:00" }, { "name": "phpunit/phpunit", - "version": "10.5.53", + "version": "11.5.55", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "32768472ebfb6969e6c7399f1c7b09009723f653" + "reference": "adc7262fccc12de2b30f12a8aa0b33775d814f00" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/32768472ebfb6969e6c7399f1c7b09009723f653", - "reference": "32768472ebfb6969e6c7399f1c7b09009723f653", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/adc7262fccc12de2b30f12a8aa0b33775d814f00", + "reference": "adc7262fccc12de2b30f12a8aa0b33775d814f00", "shasum": "" }, "require": { @@ -11042,23 +11417,24 @@ "myclabs/deep-copy": "^1.13.4", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", - "php": ">=8.1", - "phpunit/php-code-coverage": "^10.1.16", - "phpunit/php-file-iterator": "^4.1.0", - "phpunit/php-invoker": "^4.0.0", - "phpunit/php-text-template": "^3.0.1", - "phpunit/php-timer": "^6.0.0", - "sebastian/cli-parser": "^2.0.1", - "sebastian/code-unit": "^2.0.0", - "sebastian/comparator": "^5.0.3", - "sebastian/diff": "^5.1.1", - "sebastian/environment": "^6.1.0", - "sebastian/exporter": "^5.1.2", - "sebastian/global-state": "^6.0.2", - "sebastian/object-enumerator": "^5.0.0", - "sebastian/recursion-context": "^5.0.1", - "sebastian/type": "^4.0.0", - "sebastian/version": "^4.0.1" + "php": ">=8.2", + "phpunit/php-code-coverage": "^11.0.12", + "phpunit/php-file-iterator": "^5.1.1", + "phpunit/php-invoker": "^5.0.1", + "phpunit/php-text-template": "^4.0.1", + "phpunit/php-timer": "^7.0.1", + "sebastian/cli-parser": "^3.0.2", + "sebastian/code-unit": "^3.0.3", + "sebastian/comparator": "^6.3.3", + "sebastian/diff": "^6.0.2", + "sebastian/environment": "^7.2.1", + "sebastian/exporter": "^6.3.2", + "sebastian/global-state": "^7.0.2", + "sebastian/object-enumerator": "^6.0.1", + "sebastian/recursion-context": "^6.0.3", + "sebastian/type": "^5.1.3", + "sebastian/version": "^5.0.2", + "staabm/side-effects-detector": "^1.0.5" }, "suggest": { "ext-soap": "To be able to generate mocks based on WSDL files" @@ -11069,7 +11445,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "10.5-dev" + "dev-main": "11.5-dev" } }, "autoload": { @@ -11101,7 +11477,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.53" + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.55" }, "funding": [ { @@ -11125,32 +11501,32 @@ "type": "tidelift" } ], - "time": "2025-08-20T14:40:06+00:00" + "time": "2026-02-18T12:37:06+00:00" }, { "name": "sebastian/cli-parser", - "version": "2.0.1", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084" + "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084", - "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/15c5dd40dc4f38794d383bb95465193f5e0ae180", + "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "2.0-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -11174,7 +11550,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/cli-parser/issues", "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.1" + "source": "https://github.com/sebastianbergmann/cli-parser/tree/3.0.2" }, "funding": [ { @@ -11182,32 +11558,32 @@ "type": "github" } ], - "time": "2024-03-02T07:12:49+00:00" + "time": "2024-07-03T04:41:36+00:00" }, { "name": "sebastian/code-unit", - "version": "2.0.0", + "version": "3.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "a81fee9eef0b7a76af11d121767abc44c104e503" + "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503", - "reference": "a81fee9eef0b7a76af11d121767abc44c104e503", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/54391c61e4af8078e5b276ab082b6d3c54c9ad64", + "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^11.5" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "2.0-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -11230,7 +11606,8 @@ "homepage": "https://github.com/sebastianbergmann/code-unit", "support": { "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0" + "security": "https://github.com/sebastianbergmann/code-unit/security/policy", + "source": "https://github.com/sebastianbergmann/code-unit/tree/3.0.3" }, "funding": [ { @@ -11238,32 +11615,32 @@ "type": "github" } ], - "time": "2023-02-03T06:58:43+00:00" + "time": "2025-03-19T07:56:08+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", - "version": "3.0.0", + "version": "4.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d" + "reference": "183a9b2632194febd219bb9246eee421dad8d45e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", - "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/183a9b2632194febd219bb9246eee421dad8d45e", + "reference": "183a9b2632194febd219bb9246eee421dad8d45e", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -11285,7 +11662,8 @@ "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", "support": { "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0" + "security": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/security/policy", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/4.0.1" }, "funding": [ { @@ -11293,36 +11671,39 @@ "type": "github" } ], - "time": "2023-02-03T06:59:15+00:00" + "time": "2024-07-03T04:45:54+00:00" }, { "name": "sebastian/comparator", - "version": "5.0.3", + "version": "6.3.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e" + "reference": "2c95e1e86cb8dd41beb8d502057d1081ccc8eca9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e", - "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2c95e1e86cb8dd41beb8d502057d1081ccc8eca9", + "reference": "2c95e1e86cb8dd41beb8d502057d1081ccc8eca9", "shasum": "" }, "require": { "ext-dom": "*", "ext-mbstring": "*", - "php": ">=8.1", - "sebastian/diff": "^5.0", - "sebastian/exporter": "^5.0" + "php": ">=8.2", + "sebastian/diff": "^6.0", + "sebastian/exporter": "^6.0" }, "require-dev": { - "phpunit/phpunit": "^10.5" + "phpunit/phpunit": "^11.4" + }, + "suggest": { + "ext-bcmath": "For comparing BcMath\\Number objects" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "6.3-dev" } }, "autoload": { @@ -11362,41 +11743,53 @@ "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.3" + "source": "https://github.com/sebastianbergmann/comparator/tree/6.3.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" } ], - "time": "2024-10-18T14:56:07+00:00" + "time": "2026-01-24T09:26:40+00:00" }, { "name": "sebastian/complexity", - "version": "3.2.0", + "version": "4.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "68ff824baeae169ec9f2137158ee529584553799" + "reference": "ee41d384ab1906c68852636b6de493846e13e5a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799", - "reference": "68ff824baeae169ec9f2137158ee529584553799", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/ee41d384ab1906c68852636b6de493846e13e5a0", + "reference": "ee41d384ab1906c68852636b6de493846e13e5a0", "shasum": "" }, "require": { - "nikic/php-parser": "^4.18 || ^5.0", - "php": ">=8.1" + "nikic/php-parser": "^5.0", + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.2-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -11420,7 +11813,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", "security": "https://github.com/sebastianbergmann/complexity/security/policy", - "source": "https://github.com/sebastianbergmann/complexity/tree/3.2.0" + "source": "https://github.com/sebastianbergmann/complexity/tree/4.0.1" }, "funding": [ { @@ -11428,33 +11821,33 @@ "type": "github" } ], - "time": "2023-12-21T08:37:17+00:00" + "time": "2024-07-03T04:49:50+00:00" }, { "name": "sebastian/diff", - "version": "5.1.1", + "version": "6.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e" + "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e", - "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/b4ccd857127db5d41a5b676f24b51371d76d8544", + "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^10.0", - "symfony/process": "^6.4" + "phpunit/phpunit": "^11.0", + "symfony/process": "^4.2 || ^5" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.1-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -11487,7 +11880,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", "security": "https://github.com/sebastianbergmann/diff/security/policy", - "source": "https://github.com/sebastianbergmann/diff/tree/5.1.1" + "source": "https://github.com/sebastianbergmann/diff/tree/6.0.2" }, "funding": [ { @@ -11495,27 +11888,27 @@ "type": "github" } ], - "time": "2024-03-02T07:15:17+00:00" + "time": "2024-07-03T04:53:05+00:00" }, { "name": "sebastian/environment", - "version": "6.1.0", + "version": "7.2.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "8074dbcd93529b357029f5cc5058fd3e43666984" + "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984", - "reference": "8074dbcd93529b357029f5cc5058fd3e43666984", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/a5c75038693ad2e8d4b6c15ba2403532647830c4", + "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^11.3" }, "suggest": { "ext-posix": "*" @@ -11523,7 +11916,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "6.1-dev" + "dev-main": "7.2-dev" } }, "autoload": { @@ -11551,42 +11944,54 @@ "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", "security": "https://github.com/sebastianbergmann/environment/security/policy", - "source": "https://github.com/sebastianbergmann/environment/tree/6.1.0" + "source": "https://github.com/sebastianbergmann/environment/tree/7.2.1" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/environment", + "type": "tidelift" } ], - "time": "2024-03-23T08:47:14+00:00" + "time": "2025-05-21T11:55:47+00:00" }, { "name": "sebastian/exporter", - "version": "5.1.2", + "version": "6.3.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "955288482d97c19a372d3f31006ab3f37da47adf" + "reference": "70a298763b40b213ec087c51c739efcaa90bcd74" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/955288482d97c19a372d3f31006ab3f37da47adf", - "reference": "955288482d97c19a372d3f31006ab3f37da47adf", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/70a298763b40b213ec087c51c739efcaa90bcd74", + "reference": "70a298763b40b213ec087c51c739efcaa90bcd74", "shasum": "" }, "require": { "ext-mbstring": "*", - "php": ">=8.1", - "sebastian/recursion-context": "^5.0" + "php": ">=8.2", + "sebastian/recursion-context": "^6.0" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^11.3" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.1-dev" + "dev-main": "6.3-dev" } }, "autoload": { @@ -11629,43 +12034,55 @@ "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", "security": "https://github.com/sebastianbergmann/exporter/security/policy", - "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.2" + "source": "https://github.com/sebastianbergmann/exporter/tree/6.3.2" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" } ], - "time": "2024-03-02T07:17:12+00:00" + "time": "2025-09-24T06:12:51+00:00" }, { "name": "sebastian/global-state", - "version": "6.0.2", + "version": "7.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9" + "reference": "3be331570a721f9a4b5917f4209773de17f747d7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", - "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/3be331570a721f9a4b5917f4209773de17f747d7", + "reference": "3be331570a721f9a4b5917f4209773de17f747d7", "shasum": "" }, "require": { - "php": ">=8.1", - "sebastian/object-reflector": "^3.0", - "sebastian/recursion-context": "^5.0" + "php": ">=8.2", + "sebastian/object-reflector": "^4.0", + "sebastian/recursion-context": "^6.0" }, "require-dev": { "ext-dom": "*", - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "6.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -11691,7 +12108,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", "security": "https://github.com/sebastianbergmann/global-state/security/policy", - "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.2" + "source": "https://github.com/sebastianbergmann/global-state/tree/7.0.2" }, "funding": [ { @@ -11699,33 +12116,33 @@ "type": "github" } ], - "time": "2024-03-02T07:19:19+00:00" + "time": "2024-07-03T04:57:36+00:00" }, { "name": "sebastian/lines-of-code", - "version": "2.0.2", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0" + "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0", - "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/d36ad0d782e5756913e42ad87cb2890f4ffe467a", + "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a", "shasum": "" }, "require": { - "nikic/php-parser": "^4.18 || ^5.0", - "php": ">=8.1" + "nikic/php-parser": "^5.0", + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "2.0-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -11749,7 +12166,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.2" + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/3.0.1" }, "funding": [ { @@ -11757,34 +12174,34 @@ "type": "github" } ], - "time": "2023-12-21T08:38:20+00:00" + "time": "2024-07-03T04:58:38+00:00" }, { "name": "sebastian/object-enumerator", - "version": "5.0.0", + "version": "6.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" + "reference": "f5b498e631a74204185071eb41f33f38d64608aa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", - "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/f5b498e631a74204185071eb41f33f38d64608aa", + "reference": "f5b498e631a74204185071eb41f33f38d64608aa", "shasum": "" }, "require": { - "php": ">=8.1", - "sebastian/object-reflector": "^3.0", - "sebastian/recursion-context": "^5.0" + "php": ">=8.2", + "sebastian/object-reflector": "^4.0", + "sebastian/recursion-context": "^6.0" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -11806,7 +12223,8 @@ "homepage": "https://github.com/sebastianbergmann/object-enumerator/", "support": { "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0" + "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/6.0.1" }, "funding": [ { @@ -11814,32 +12232,32 @@ "type": "github" } ], - "time": "2023-02-03T07:08:32+00:00" + "time": "2024-07-03T05:00:13+00:00" }, { "name": "sebastian/object-reflector", - "version": "3.0.0", + "version": "4.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" + "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", - "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/6e1a43b411b2ad34146dee7524cb13a068bb35f9", + "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -11861,7 +12279,8 @@ "homepage": "https://github.com/sebastianbergmann/object-reflector/", "support": { "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" + "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/4.0.1" }, "funding": [ { @@ -11869,32 +12288,32 @@ "type": "github" } ], - "time": "2023-02-03T07:06:18+00:00" + "time": "2024-07-03T05:01:32+00:00" }, { "name": "sebastian/recursion-context", - "version": "5.0.1", + "version": "6.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a" + "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/47e34210757a2f37a97dcd207d032e1b01e64c7a", - "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/f6458abbf32a6c8174f8f26261475dc133b3d9dc", + "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^10.5" + "phpunit/phpunit": "^11.3" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -11925,7 +12344,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.1" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.3" }, "funding": [ { @@ -11945,32 +12364,32 @@ "type": "tidelift" } ], - "time": "2025-08-10T07:50:56+00:00" + "time": "2025-08-13T04:42:22+00:00" }, { "name": "sebastian/type", - "version": "4.0.0", + "version": "5.1.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" + "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", - "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/f77d2d4e78738c98d9a68d2596fe5e8fa380f449", + "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^11.3" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "4.0-dev" + "dev-main": "5.1-dev" } }, "autoload": { @@ -11993,37 +12412,50 @@ "homepage": "https://github.com/sebastianbergmann/type", "support": { "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" + "security": "https://github.com/sebastianbergmann/type/security/policy", + "source": "https://github.com/sebastianbergmann/type/tree/5.1.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/type", + "type": "tidelift" } ], - "time": "2023-02-03T07:10:45+00:00" + "time": "2025-08-09T06:55:48+00:00" }, { "name": "sebastian/version", - "version": "4.0.1", + "version": "5.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" + "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", - "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c687e3387b99f5b03b6caa64c74b63e2936ff874", + "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "4.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -12046,7 +12478,8 @@ "homepage": "https://github.com/sebastianbergmann/version", "support": { "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" + "security": "https://github.com/sebastianbergmann/version/security/policy", + "source": "https://github.com/sebastianbergmann/version/tree/5.0.2" }, "funding": [ { @@ -12054,20 +12487,20 @@ "type": "github" } ], - "time": "2023-02-07T11:34:05+00:00" + "time": "2024-10-09T05:16:32+00:00" }, { "name": "spatie/backtrace", - "version": "1.8.0", + "version": "1.8.2", "source": { "type": "git", "url": "https://github.com/spatie/backtrace.git", - "reference": "1607d8870bf597fc4ad79a6945cf0b2e584c2669" + "reference": "8ffe78be5ed355b5009e3dd989d183433e9a5adc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/backtrace/zipball/1607d8870bf597fc4ad79a6945cf0b2e584c2669", - "reference": "1607d8870bf597fc4ad79a6945cf0b2e584c2669", + "url": "https://api.github.com/repos/spatie/backtrace/zipball/8ffe78be5ed355b5009e3dd989d183433e9a5adc", + "reference": "8ffe78be5ed355b5009e3dd989d183433e9a5adc", "shasum": "" }, "require": { @@ -12078,7 +12511,7 @@ "laravel/serializable-closure": "^1.3 || ^2.0", "phpunit/phpunit": "^9.3 || ^11.4.3", "spatie/phpunit-snapshot-assertions": "^4.2 || ^5.1.6", - "symfony/var-dumper": "^5.1 || ^6.0 || ^7.0" + "symfony/var-dumper": "^5.1|^6.0|^7.0|^8.0" }, "type": "library", "autoload": { @@ -12106,7 +12539,7 @@ ], "support": { "issues": "https://github.com/spatie/backtrace/issues", - "source": "https://github.com/spatie/backtrace/tree/1.8.0" + "source": "https://github.com/spatie/backtrace/tree/1.8.2" }, "funding": [ { @@ -12118,7 +12551,7 @@ "type": "other" } ], - "time": "2025-08-25T16:16:45+00:00" + "time": "2026-03-11T13:48:28+00:00" }, { "name": "spatie/error-solutions", @@ -12196,26 +12629,26 @@ }, { "name": "spatie/flare-client-php", - "version": "1.10.1", + "version": "1.11.1", "source": { "type": "git", "url": "https://github.com/spatie/flare-client-php.git", - "reference": "bf1716eb98bd689451b071548ae9e70738dce62f" + "reference": "53f41b08a27cc039e1a8ed2be9a202e924f31bad" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/flare-client-php/zipball/bf1716eb98bd689451b071548ae9e70738dce62f", - "reference": "bf1716eb98bd689451b071548ae9e70738dce62f", + "url": "https://api.github.com/repos/spatie/flare-client-php/zipball/53f41b08a27cc039e1a8ed2be9a202e924f31bad", + "reference": "53f41b08a27cc039e1a8ed2be9a202e924f31bad", "shasum": "" }, "require": { - "illuminate/pipeline": "^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/pipeline": "^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", "php": "^8.0", "spatie/backtrace": "^1.6.1", - "symfony/http-foundation": "^5.2|^6.0|^7.0", - "symfony/mime": "^5.2|^6.0|^7.0", - "symfony/process": "^5.2|^6.0|^7.0", - "symfony/var-dumper": "^5.2|^6.0|^7.0" + "symfony/http-foundation": "^5.2|^6.0|^7.0|^8.0", + "symfony/mime": "^5.2|^6.0|^7.0|^8.0", + "symfony/process": "^5.2|^6.0|^7.0|^8.0", + "symfony/var-dumper": "^5.2|^6.0|^7.0|^8.0" }, "require-dev": { "dms/phpunit-arraysubset-asserts": "^0.5.0", @@ -12253,7 +12686,7 @@ ], "support": { "issues": "https://github.com/spatie/flare-client-php/issues", - "source": "https://github.com/spatie/flare-client-php/tree/1.10.1" + "source": "https://github.com/spatie/flare-client-php/tree/1.11.1" }, "funding": [ { @@ -12261,41 +12694,44 @@ "type": "github" } ], - "time": "2025-02-14T13:42:06+00:00" + "time": "2026-05-15T09:31:32+00:00" }, { "name": "spatie/ignition", - "version": "1.15.1", + "version": "1.16.0", "source": { "type": "git", "url": "https://github.com/spatie/ignition.git", - "reference": "31f314153020aee5af3537e507fef892ffbf8c85" + "reference": "b59385bb7aa24dae81bcc15850ebecfda7b40838" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/ignition/zipball/31f314153020aee5af3537e507fef892ffbf8c85", - "reference": "31f314153020aee5af3537e507fef892ffbf8c85", + "url": "https://api.github.com/repos/spatie/ignition/zipball/b59385bb7aa24dae81bcc15850ebecfda7b40838", + "reference": "b59385bb7aa24dae81bcc15850ebecfda7b40838", "shasum": "" }, "require": { "ext-json": "*", "ext-mbstring": "*", "php": "^8.0", - "spatie/error-solutions": "^1.0", - "spatie/flare-client-php": "^1.7", - "symfony/console": "^5.4|^6.0|^7.0", - "symfony/var-dumper": "^5.4|^6.0|^7.0" + "spatie/backtrace": "^1.7.1", + "spatie/error-solutions": "^1.1.2", + "spatie/flare-client-php": "^1.9", + "symfony/console": "^5.4.42|^6.0|^7.0|^8.0", + "symfony/http-foundation": "^5.4.42|^6.0|^7.0|^8.0", + "symfony/mime": "^5.4.42|^6.0|^7.0|^8.0", + "symfony/var-dumper": "^5.4.42|^6.0|^7.0|^8.0" }, "require-dev": { - "illuminate/cache": "^9.52|^10.0|^11.0|^12.0", + "illuminate/cache": "^9.52|^10.0|^11.0|^12.0|^13.0", "mockery/mockery": "^1.4", - "pestphp/pest": "^1.20|^2.0", + "pestphp/pest": "^1.20|^2.0|^3.0", "phpstan/extension-installer": "^1.1", "phpstan/phpstan-deprecation-rules": "^1.0", "phpstan/phpstan-phpunit": "^1.0", "psr/simple-cache-implementation": "*", - "symfony/cache": "^5.4|^6.0|^7.0", - "symfony/process": "^5.4|^6.0|^7.0", + "symfony/cache": "^5.4.38|^6.0|^7.0|^8.0", + "symfony/process": "^5.4.35|^6.0|^7.0|^8.0", "vlucas/phpdotenv": "^5.5" }, "suggest": { @@ -12344,42 +12780,43 @@ "type": "github" } ], - "time": "2025-02-21T14:31:39+00:00" + "time": "2026-03-17T10:51:08+00:00" }, { "name": "spatie/laravel-ignition", - "version": "2.9.1", + "version": "2.12.0", "source": { "type": "git", "url": "https://github.com/spatie/laravel-ignition.git", - "reference": "1baee07216d6748ebd3a65ba97381b051838707a" + "reference": "45b3b6e1e73fc161cba2149972698644b99594ee" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/1baee07216d6748ebd3a65ba97381b051838707a", - "reference": "1baee07216d6748ebd3a65ba97381b051838707a", + "url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/45b3b6e1e73fc161cba2149972698644b99594ee", + "reference": "45b3b6e1e73fc161cba2149972698644b99594ee", "shasum": "" }, "require": { "ext-curl": "*", "ext-json": "*", "ext-mbstring": "*", - "illuminate/support": "^10.0|^11.0|^12.0", - "php": "^8.1", - "spatie/ignition": "^1.15", - "symfony/console": "^6.2.3|^7.0", - "symfony/var-dumper": "^6.2.3|^7.0" + "illuminate/support": "^11.0|^12.0|^13.0", + "nesbot/carbon": "^2.72|^3.0", + "php": "^8.2", + "spatie/ignition": "^1.16", + "symfony/console": "^7.4|^8.0", + "symfony/var-dumper": "^7.4|^8.0" }, "require-dev": { - "livewire/livewire": "^2.11|^3.3.5", - "mockery/mockery": "^1.5.1", - "openai-php/client": "^0.8.1|^0.10", - "orchestra/testbench": "8.22.3|^9.0|^10.0", - "pestphp/pest": "^2.34|^3.7", - "phpstan/extension-installer": "^1.3.1", - "phpstan/phpstan-deprecation-rules": "^1.1.1|^2.0", - "phpstan/phpstan-phpunit": "^1.3.16|^2.0", - "vlucas/phpdotenv": "^5.5" + "livewire/livewire": "^3.7.0|^4.0|dev-josh/v3-laravel-13-support", + "mockery/mockery": "^1.6.12", + "openai-php/client": "^0.10.3|^0.19", + "orchestra/testbench": "^v9.16.0|^10.6|^11.0", + "pestphp/pest": "^3.7|^4.0", + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan-deprecation-rules": "^2.0.3", + "phpstan/phpstan-phpunit": "^2.0.8", + "vlucas/phpdotenv": "^5.6.2" }, "suggest": { "openai-php/client": "Require get solutions from OpenAI", @@ -12435,32 +12872,84 @@ "type": "github" } ], - "time": "2025-02-20T13:13:55+00:00" + "time": "2026-03-17T12:20:04+00:00" + }, + { + "name": "staabm/side-effects-detector", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/staabm/side-effects-detector.git", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.6", + "phpunit/phpunit": "^9.6.21", + "symfony/var-dumper": "^5.4.43", + "tomasvotruba/type-coverage": "1.0.0", + "tomasvotruba/unused-public": "1.0.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "lib/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A static analysis tool to detect side effects in PHP code", + "keywords": [ + "static analysis" + ], + "support": { + "issues": "https://github.com/staabm/side-effects-detector/issues", + "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5" + }, + "funding": [ + { + "url": "https://github.com/staabm", + "type": "github" + } + ], + "time": "2024-10-20T05:08:20+00:00" }, { "name": "symfony/yaml", - "version": "v6.4.24", + "version": "v7.4.13", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "742a8efc94027624b36b10ba58e23d402f961f51" + "reference": "a7ec3b1156faf8815db7683ec7c1e7338e6f977c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/742a8efc94027624b36b10ba58e23d402f961f51", - "reference": "742a8efc94027624b36b10ba58e23d402f961f51", + "url": "https://api.github.com/repos/symfony/yaml/zipball/a7ec3b1156faf8815db7683ec7c1e7338e6f977c", + "reference": "a7ec3b1156faf8815db7683ec7c1e7338e6f977c", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-ctype": "^1.8" }, "conflict": { - "symfony/console": "<5.4" + "symfony/console": "<6.4" }, "require-dev": { - "symfony/console": "^5.4|^6.0|^7.0" + "symfony/console": "^6.4|^7.0|^8.0" }, "bin": [ "Resources/bin/yaml-lint" @@ -12491,7 +12980,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v6.4.24" + "source": "https://github.com/symfony/yaml/tree/v7.4.13" }, "funding": [ { @@ -12511,20 +13000,20 @@ "type": "tidelift" } ], - "time": "2025-07-10T08:14:14+00:00" + "time": "2026-05-25T06:06:12+00:00" }, { "name": "theseer/tokenizer", - "version": "1.2.3", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", - "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c", "shasum": "" }, "require": { @@ -12553,7 +13042,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.3" + "source": "https://github.com/theseer/tokenizer/tree/1.3.1" }, "funding": [ { @@ -12561,7 +13050,7 @@ "type": "github" } ], - "time": "2024-03-03T12:36:25+00:00" + "time": "2025-11-17T20:03:58+00:00" } ], "aliases": [], @@ -12570,7 +13059,7 @@ "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": "^8.1" + "php": "^8.3" }, "platform-dev": {}, "plugin-api-version": "2.9.0" diff --git a/config/app.php b/config/app.php index 4ac34c21..0222f23e 100644 --- a/config/app.php +++ b/config/app.php @@ -193,7 +193,7 @@ Illuminate\Translation\TranslationServiceProvider::class, Illuminate\Validation\ValidationServiceProvider::class, Illuminate\View\ViewServiceProvider::class, - Intervention\Image\ImageServiceProvider::class, + Intervention\Image\Laravel\ServiceProvider::class, Spatie\Permission\PermissionServiceProvider::class, Spatie\Csp\CspServiceProvider::class, /* @@ -226,7 +226,7 @@ 'aliases' => Facade::defaultAliases()->merge([ // 'ExampleClass' => App\Example\ExampleClass::class, - 'Image' => Intervention\Image\Facades\Image::class, + 'Image' => Intervention\Image\Laravel\Facades\Image::class, 'Html' => Spatie\Html\Facades\Html::class, 'Captcha' => Mews\Captcha\Facades\Captcha::class, ])->toArray(), diff --git a/config/csp.php b/config/csp.php index 1d97eaa5..e47146a1 100644 --- a/config/csp.php +++ b/config/csp.php @@ -1,36 +1,97 @@ CustomCSPPolicy::class, + 'presets' => [ + App\Policies\CustomCspPreset::class, + ], + + /** + * Register additional global CSP directives here. + */ + 'directives' => [ + // [Directive::SCRIPT, [Keyword::UNSAFE_EVAL, Keyword::UNSAFE_INLINE]], + ], /* - * This policy which will be put in report only mode. This is great for testing out - * a new policy or changes to existing csp policy without breaking anything. + * These presets which will be put in a report-only policy. This is great for testing out + * a new policy or changes to existing CSP policy without breaking anything. + */ + 'report_only_presets' => [ + // + ], + + /** + * Register additional global report-only CSP directives here. */ - 'report_only_policy' => '', + 'report_only_directives' => [ + // [Directive::SCRIPT, [Keyword::UNSAFE_EVAL, Keyword::UNSAFE_INLINE]], + ], /* - * All violations against the policy will be reported to this url. + * All violations against a policy will be reported to this url. * A great service you could use for this is https://report-uri.com/ - * - * You can override this setting by calling `reportTo` on your policy. */ 'report_uri' => env('CSP_REPORT_URI', ''), + /* + * Optional separate report url for the report-only policy. When empty, + * the report-only policy falls back to `report_uri` above. Useful for + * services like report-uri.com that require different paths for enforcing + * (`/enforce`) and report-only (`/reportOnly`) policies. + */ + 'report_only_uri' => env('CSP_REPORT_ONLY_URI', ''), + + /* + * The name of the reporting endpoint that violations should be sent to. + * The endpoint itself must be defined in `reporting_endpoints` below. + */ + 'report_to' => env('CSP_REPORT_TO', ''), + + /* + * Optional separate reporting endpoint name for the report-only policy. + * When empty, the report-only policy falls back to `report_to` above. + */ + 'report_only_to' => env('CSP_REPORT_ONLY_TO', ''), + + /* + * Reporting endpoints that will be sent in the `Reporting-Endpoints` HTTP + * header. The keys are the endpoint names that can be referenced from + * `report_to` above. + * + * Example: ['default' => 'https://example.com/csp-reports'] + */ + 'reporting_endpoints' => [ + // + ], + /* * Headers will only be added if this setting is set to true. */ 'enabled' => env('CSP_ENABLED', true), + /** + * Headers will be added when Vite is hot reloading. + */ + 'enabled_while_hot_reloading' => env('CSP_ENABLED_WHILE_HOT_RELOADING', false), + /* * The class responsible for generating the nonces used in inline tags and headers. */ 'nonce_generator' => Spatie\Csp\Nonce\RandomString::class, + + /* + * Set false to disable automatic nonce generation and handling. + * This is useful when you want to use 'unsafe-inline' for scripts/styles + * and cannot add inline nonces. + * Note that this will make your CSP policy less secure. + */ + 'nonce_enabled' => env('CSP_NONCE_ENABLED', true), ]; diff --git a/config/image.php b/config/image.php new file mode 100644 index 00000000..d346586f --- /dev/null +++ b/config/image.php @@ -0,0 +1,46 @@ + \Intervention\Image\Drivers\Gd\Driver::class, + + /* + |-------------------------------------------------------------------------- + | Configuration Options + |-------------------------------------------------------------------------- + | + | These options control the behavior of Intervention Image. + | + | - "autoOrientation" controls whether an imported image should be + | automatically rotated according to any existing Exif data. + | + | - "decodeAnimation" decides whether a possibly animated image is + | decoded as such or whether the animation is discarded. + | + | - "blendingColor" Defines the default blending color. + | + | - "strip" controls if meta data like exif tags should be removed when + | encoding images. + */ + + 'options' => [ + 'autoOrientation' => true, + 'decodeAnimation' => true, + 'blendingColor' => 'ffffff', + 'strip' => false, + ] +]; diff --git a/config/jsvalidation.php b/config/jsvalidation.php index c371ebdd..ec55add7 100644 --- a/config/jsvalidation.php +++ b/config/jsvalidation.php @@ -7,7 +7,7 @@ * * Supported: 'jsvalidation::bootstrap', 'jsvalidation::bootstrap4', 'jsvalidation::bootstrap5', 'jsvalidation::uikit' */ - 'view' => 'jsvalidation::bootstrap4', + 'view' => 'jsvalidation::bootstrap', /* * Default JQuery selector find the form to be validated. @@ -49,6 +49,5 @@ * * See https://jqueryvalidation.org/validate/#ignore */ - // 'ignore' => ":hidden, [contenteditable='true']", - 'ignore' => "", + 'ignore' => ":hidden, [contenteditable='true']", ]; diff --git a/public/vendor/jsvalidation/js/jsvalidation.js b/public/vendor/jsvalidation/js/jsvalidation.js new file mode 100644 index 00000000..90e12140 --- /dev/null +++ b/public/vendor/jsvalidation/js/jsvalidation.js @@ -0,0 +1,6072 @@ +/*! + * jQuery Validation Plugin v1.21.0 + * + * https://jqueryvalidation.org/ + * + * Copyright (c) 2024 Jörn Zaefferer + * Released under the MIT license + */ +(function( factory ) { + if ( typeof define === "function" && define.amd ) { + define( ["jquery"], factory ); + } else if (typeof module === "object" && module.exports) { + module.exports = factory( require( "jquery" ) ); + } else { + factory( jQuery ); + } +}(function( $ ) { + +$.extend( $.fn, { + + // https://jqueryvalidation.org/validate/ + validate: function( options ) { + + // If nothing is selected, return nothing; can't chain anyway + if ( !this.length ) { + if ( options && options.debug && window.console ) { + console.warn( "Nothing selected, can't validate, returning nothing." ); + } + return; + } + + // Check if a validator for this form was already created + var validator = $.data( this[ 0 ], "validator" ); + if ( validator ) { + return validator; + } + + // Add novalidate tag if HTML5. + this.attr( "novalidate", "novalidate" ); + + validator = new $.validator( options, this[ 0 ] ); + $.data( this[ 0 ], "validator", validator ); + + if ( validator.settings.onsubmit ) { + + this.on( "click.validate", ":submit", function( event ) { + + // Track the used submit button to properly handle scripted + // submits later. + validator.submitButton = event.currentTarget; + + // Allow suppressing validation by adding a cancel class to the submit button + if ( $( this ).hasClass( "cancel" ) ) { + validator.cancelSubmit = true; + } + + // Allow suppressing validation by adding the html5 formnovalidate attribute to the submit button + if ( $( this ).attr( "formnovalidate" ) !== undefined ) { + validator.cancelSubmit = true; + } + } ); + + // Validate the form on submit + this.on( "submit.validate", function( event ) { + if ( validator.settings.debug ) { + + // Prevent form submit to be able to see console output + event.preventDefault(); + } + + function handle() { + var hidden, result; + + // Insert a hidden input as a replacement for the missing submit button + // The hidden input is inserted in two cases: + // - A user defined a `submitHandler` + // - There was a pending request due to `remote` method and `stopRequest()` + // was called to submit the form in case it's valid + if ( validator.submitButton && ( validator.settings.submitHandler || validator.formSubmitted ) ) { + hidden = $( "" ) + .attr( "name", validator.submitButton.name ) + .val( $( validator.submitButton ).val() ) + .appendTo( validator.currentForm ); + } + + if ( validator.settings.submitHandler && !validator.settings.debug ) { + result = validator.settings.submitHandler.call( validator, validator.currentForm, event ); + if ( hidden ) { + + // And clean up afterwards; thanks to no-block-scope, hidden can be referenced + hidden.remove(); + } + if ( result !== undefined ) { + return result; + } + return false; + } + return true; + } + + // Prevent submit for invalid forms or custom submit handlers + if ( validator.cancelSubmit ) { + validator.cancelSubmit = false; + return handle(); + } + if ( validator.form() ) { + if ( validator.pendingRequest ) { + validator.formSubmitted = true; + return false; + } + return handle(); + } else { + validator.focusInvalid(); + return false; + } + } ); + } + + return validator; + }, + + // https://jqueryvalidation.org/valid/ + valid: function() { + var valid, validator, errorList; + + if ( $( this[ 0 ] ).is( "form" ) ) { + valid = this.validate().form(); + } else { + errorList = []; + valid = true; + validator = $( this[ 0 ].form ).validate(); + this.each( function() { + valid = validator.element( this ) && valid; + if ( !valid ) { + errorList = errorList.concat( validator.errorList ); + } + } ); + validator.errorList = errorList; + } + return valid; + }, + + // https://jqueryvalidation.org/rules/ + rules: function( command, argument ) { + var element = this[ 0 ], + isContentEditable = typeof this.attr( "contenteditable" ) !== "undefined" && this.attr( "contenteditable" ) !== "false", + settings, staticRules, existingRules, data, param, filtered; + + // If nothing is selected, return empty object; can't chain anyway + if ( element == null ) { + return; + } + + if ( !element.form && isContentEditable ) { + element.form = this.closest( "form" )[ 0 ]; + element.name = this.attr( "name" ); + } + + if ( element.form == null ) { + return; + } + + if ( command ) { + settings = $.data( element.form, "validator" ).settings; + staticRules = settings.rules; + existingRules = $.validator.staticRules( element ); + switch ( command ) { + case "add": + $.extend( existingRules, $.validator.normalizeRule( argument ) ); + + // Remove messages from rules, but allow them to be set separately + delete existingRules.messages; + staticRules[ element.name ] = existingRules; + if ( argument.messages ) { + settings.messages[ element.name ] = $.extend( settings.messages[ element.name ], argument.messages ); + } + break; + case "remove": + if ( !argument ) { + delete staticRules[ element.name ]; + return existingRules; + } + filtered = {}; + $.each( argument.split( /\s/ ), function( index, method ) { + filtered[ method ] = existingRules[ method ]; + delete existingRules[ method ]; + } ); + return filtered; + } + } + + data = $.validator.normalizeRules( + $.extend( + {}, + $.validator.classRules( element ), + $.validator.attributeRules( element ), + $.validator.dataRules( element ), + $.validator.staticRules( element ) + ), element ); + + // Make sure required is at front + if ( data.required ) { + param = data.required; + delete data.required; + data = $.extend( { required: param }, data ); + } + + // Make sure remote is at back + if ( data.remote ) { + param = data.remote; + delete data.remote; + data = $.extend( data, { remote: param } ); + } + + return data; + } +} ); + +// JQuery trim is deprecated, provide a trim method based on String.prototype.trim +var trim = function( str ) { + + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trim#Polyfill + return str.replace( /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, "" ); +}; + +// Custom selectors +$.extend( $.expr.pseudos || $.expr[ ":" ], { // '|| $.expr[ ":" ]' here enables backwards compatibility to jQuery 1.7. Can be removed when dropping jQ 1.7.x support + + // https://jqueryvalidation.org/blank-selector/ + blank: function( a ) { + return !trim( "" + $( a ).val() ); + }, + + // https://jqueryvalidation.org/filled-selector/ + filled: function( a ) { + var val = $( a ).val(); + return val !== null && !!trim( "" + val ); + }, + + // https://jqueryvalidation.org/unchecked-selector/ + unchecked: function( a ) { + return !$( a ).prop( "checked" ); + } +} ); + +// Constructor for validator +$.validator = function( options, form ) { + this.settings = $.extend( true, {}, $.validator.defaults, options ); + this.currentForm = form; + this.init(); +}; + +// https://jqueryvalidation.org/jQuery.validator.format/ +$.validator.format = function( source, params ) { + if ( arguments.length === 1 ) { + return function() { + var args = $.makeArray( arguments ); + args.unshift( source ); + return $.validator.format.apply( this, args ); + }; + } + if ( params === undefined ) { + return source; + } + if ( arguments.length > 2 && params.constructor !== Array ) { + params = $.makeArray( arguments ).slice( 1 ); + } + if ( params.constructor !== Array ) { + params = [ params ]; + } + $.each( params, function( i, n ) { + source = source.replace( new RegExp( "\\{" + i + "\\}", "g" ), function() { + return n; + } ); + } ); + return source; +}; + +$.extend( $.validator, { + + defaults: { + messages: {}, + groups: {}, + rules: {}, + errorClass: "error", + pendingClass: "pending", + validClass: "valid", + errorElement: "label", + focusCleanup: false, + focusInvalid: true, + errorContainer: $( [] ), + errorLabelContainer: $( [] ), + onsubmit: true, + ignore: ":hidden", + ignoreTitle: false, + customElements: [], + onfocusin: function( element ) { + this.lastActive = element; + + // Hide error label and remove error class on focus if enabled + if ( this.settings.focusCleanup ) { + if ( this.settings.unhighlight ) { + this.settings.unhighlight.call( this, element, this.settings.errorClass, this.settings.validClass ); + } + this.hideThese( this.errorsFor( element ) ); + } + }, + onfocusout: function( element ) { + if ( !this.checkable( element ) && ( element.name in this.submitted || !this.optional( element ) ) ) { + this.element( element ); + } + }, + onkeyup: function( element, event ) { + + // Avoid revalidate the field when pressing one of the following keys + // Shift => 16 + // Ctrl => 17 + // Alt => 18 + // Caps lock => 20 + // End => 35 + // Home => 36 + // Left arrow => 37 + // Up arrow => 38 + // Right arrow => 39 + // Down arrow => 40 + // Insert => 45 + // Num lock => 144 + // AltGr key => 225 + var excludedKeys = [ + 16, 17, 18, 20, 35, 36, 37, + 38, 39, 40, 45, 144, 225 + ]; + + if ( event.which === 9 && this.elementValue( element ) === "" || $.inArray( event.keyCode, excludedKeys ) !== -1 ) { + return; + } else if ( element.name in this.submitted || element.name in this.invalid ) { + this.element( element ); + } + }, + onclick: function( element ) { + + // Click on selects, radiobuttons and checkboxes + if ( element.name in this.submitted ) { + this.element( element ); + + // Or option elements, check parent select in that case + } else if ( element.parentNode.name in this.submitted ) { + this.element( element.parentNode ); + } + }, + highlight: function( element, errorClass, validClass ) { + if ( element.type === "radio" ) { + this.findByName( element.name ).addClass( errorClass ).removeClass( validClass ); + } else { + $( element ).addClass( errorClass ).removeClass( validClass ); + } + }, + unhighlight: function( element, errorClass, validClass ) { + if ( element.type === "radio" ) { + this.findByName( element.name ).removeClass( errorClass ).addClass( validClass ); + } else { + $( element ).removeClass( errorClass ).addClass( validClass ); + } + } + }, + + // https://jqueryvalidation.org/jQuery.validator.setDefaults/ + setDefaults: function( settings ) { + $.extend( $.validator.defaults, settings ); + }, + + messages: { + required: "This field is required.", + remote: "Please fix this field.", + email: "Please enter a valid email address.", + url: "Please enter a valid URL.", + date: "Please enter a valid date.", + dateISO: "Please enter a valid date (ISO).", + number: "Please enter a valid number.", + digits: "Please enter only digits.", + equalTo: "Please enter the same value again.", + maxlength: $.validator.format( "Please enter no more than {0} characters." ), + minlength: $.validator.format( "Please enter at least {0} characters." ), + rangelength: $.validator.format( "Please enter a value between {0} and {1} characters long." ), + range: $.validator.format( "Please enter a value between {0} and {1}." ), + max: $.validator.format( "Please enter a value less than or equal to {0}." ), + min: $.validator.format( "Please enter a value greater than or equal to {0}." ), + step: $.validator.format( "Please enter a multiple of {0}." ) + }, + + autoCreateRanges: false, + + prototype: { + + init: function() { + this.labelContainer = $( this.settings.errorLabelContainer ); + this.errorContext = this.labelContainer.length && this.labelContainer || $( this.currentForm ); + this.containers = $( this.settings.errorContainer ).add( this.settings.errorLabelContainer ); + this.submitted = {}; + this.valueCache = {}; + this.pendingRequest = 0; + this.pending = {}; + this.invalid = {}; + this.reset(); + + var currentForm = this.currentForm, + groups = ( this.groups = {} ), + rules; + $.each( this.settings.groups, function( key, value ) { + if ( typeof value === "string" ) { + value = value.split( /\s/ ); + } + $.each( value, function( index, name ) { + groups[ name ] = key; + } ); + } ); + rules = this.settings.rules; + $.each( rules, function( key, value ) { + rules[ key ] = $.validator.normalizeRule( value ); + } ); + + function delegate( event ) { + var isContentEditable = typeof $( this ).attr( "contenteditable" ) !== "undefined" && $( this ).attr( "contenteditable" ) !== "false"; + + // Set form expando on contenteditable + if ( !this.form && isContentEditable ) { + this.form = $( this ).closest( "form" )[ 0 ]; + this.name = $( this ).attr( "name" ); + } + + // Ignore the element if it belongs to another form. This will happen mainly + // when setting the `form` attribute of an input to the id of another form. + if ( currentForm !== this.form ) { + return; + } + + var validator = $.data( this.form, "validator" ), + eventType = "on" + event.type.replace( /^validate/, "" ), + settings = validator.settings; + if ( settings[ eventType ] && !$( this ).is( settings.ignore ) ) { + settings[ eventType ].call( validator, this, event ); + } + } + var focusListeners = [ ":text", "[type='password']", "[type='file']", "select", "textarea", "[type='number']", "[type='search']", + "[type='tel']", "[type='url']", "[type='email']", "[type='datetime']", "[type='date']", "[type='month']", + "[type='week']", "[type='time']", "[type='datetime-local']", "[type='range']", "[type='color']", + "[type='radio']", "[type='checkbox']", "[contenteditable]", "[type='button']" ]; + var clickListeners = [ "select", "option", "[type='radio']", "[type='checkbox']" ]; + $( this.currentForm ) + .on( "focusin.validate focusout.validate keyup.validate", focusListeners.concat( this.settings.customElements ).join( ", " ), delegate ) + + // Support: Chrome, oldIE + // "select" is provided as event.target when clicking a option + .on( "click.validate", clickListeners.concat( this.settings.customElements ).join( ", " ), delegate ); + + if ( this.settings.invalidHandler ) { + $( this.currentForm ).on( "invalid-form.validate", this.settings.invalidHandler ); + } + }, + + // https://jqueryvalidation.org/Validator.form/ + form: function() { + this.checkForm(); + $.extend( this.submitted, this.errorMap ); + this.invalid = $.extend( {}, this.errorMap ); + if ( !this.valid() ) { + $( this.currentForm ).triggerHandler( "invalid-form", [ this ] ); + } + this.showErrors(); + return this.valid(); + }, + + checkForm: function() { + this.prepareForm(); + for ( var i = 0, elements = ( this.currentElements = this.elements() ); elements[ i ]; i++ ) { + this.check( elements[ i ] ); + } + return this.valid(); + }, + + // https://jqueryvalidation.org/Validator.element/ + element: function( element ) { + var cleanElement = this.clean( element ), + checkElement = this.validationTargetFor( cleanElement ), + v = this, + result = true, + rs, group; + + if ( checkElement === undefined ) { + delete this.invalid[ cleanElement.name ]; + } else { + this.prepareElement( checkElement ); + this.currentElements = $( checkElement ); + + // If this element is grouped, then validate all group elements already + // containing a value + group = this.groups[ checkElement.name ]; + if ( group ) { + $.each( this.groups, function( name, testgroup ) { + if ( testgroup === group && name !== checkElement.name ) { + cleanElement = v.validationTargetFor( v.clean( v.findByName( name ) ) ); + if ( cleanElement && cleanElement.name in v.invalid ) { + v.currentElements.push( cleanElement ); + result = v.check( cleanElement ) && result; + } + } + } ); + } + + rs = this.check( checkElement ) !== false; + result = result && rs; + if ( rs ) { + this.invalid[ checkElement.name ] = false; + } else { + this.invalid[ checkElement.name ] = true; + } + + if ( !this.numberOfInvalids() ) { + + // Hide error containers on last error + this.toHide = this.toHide.add( this.containers ); + } + this.showErrors(); + + // Add aria-invalid status for screen readers + $( element ).attr( "aria-invalid", !rs ); + } + + return result; + }, + + // https://jqueryvalidation.org/Validator.showErrors/ + showErrors: function( errors ) { + if ( errors ) { + var validator = this; + + // Add items to error list and map + $.extend( this.errorMap, errors ); + this.errorList = $.map( this.errorMap, function( message, name ) { + return { + message: message, + element: validator.findByName( name )[ 0 ] + }; + } ); + + // Remove items from success list + this.successList = $.grep( this.successList, function( element ) { + return !( element.name in errors ); + } ); + } + if ( this.settings.showErrors ) { + this.settings.showErrors.call( this, this.errorMap, this.errorList ); + } else { + this.defaultShowErrors(); + } + }, + + // https://jqueryvalidation.org/Validator.resetForm/ + resetForm: function() { + if ( $.fn.resetForm ) { + $( this.currentForm ).resetForm(); + } + this.invalid = {}; + this.submitted = {}; + this.prepareForm(); + this.hideErrors(); + var elements = this.elements() + .removeData( "previousValue" ) + .removeAttr( "aria-invalid" ); + + this.resetElements( elements ); + }, + + resetElements: function( elements ) { + var i; + + if ( this.settings.unhighlight ) { + for ( i = 0; elements[ i ]; i++ ) { + this.settings.unhighlight.call( this, elements[ i ], + this.settings.errorClass, "" ); + this.findByName( elements[ i ].name ).removeClass( this.settings.validClass ); + } + } else { + elements + .removeClass( this.settings.errorClass ) + .removeClass( this.settings.validClass ); + } + }, + + numberOfInvalids: function() { + return this.objectLength( this.invalid ); + }, + + objectLength: function( obj ) { + /* jshint unused: false */ + var count = 0, + i; + for ( i in obj ) { + + // This check allows counting elements with empty error + // message as invalid elements + if ( obj[ i ] !== undefined && obj[ i ] !== null && obj[ i ] !== false ) { + count++; + } + } + return count; + }, + + hideErrors: function() { + this.hideThese( this.toHide ); + }, + + hideThese: function( errors ) { + errors.not( this.containers ).text( "" ); + this.addWrapper( errors ).hide(); + }, + + valid: function() { + return this.size() === 0; + }, + + size: function() { + return this.errorList.length; + }, + + focusInvalid: function() { + if ( this.settings.focusInvalid ) { + try { + $( this.findLastActive() || this.errorList.length && this.errorList[ 0 ].element || [] ) + .filter( ":visible" ) + .trigger( "focus" ) + + // Manually trigger focusin event; without it, focusin handler isn't called, findLastActive won't have anything to find + .trigger( "focusin" ); + } catch ( e ) { + + // Ignore IE throwing errors when focusing hidden elements + } + } + }, + + findLastActive: function() { + var lastActive = this.lastActive; + return lastActive && $.grep( this.errorList, function( n ) { + return n.element.name === lastActive.name; + } ).length === 1 && lastActive; + }, + + elements: function() { + var validator = this, + rulesCache = {}, + selectors = [ "input", "select", "textarea", "[contenteditable]" ]; + + // Select all valid inputs inside the form (no submit or reset buttons) + return $( this.currentForm ) + .find( selectors.concat( this.settings.customElements ).join( ", " ) ) + .not( ":submit, :reset, :image, :disabled" ) + .not( this.settings.ignore ) + .filter( function() { + var name = this.name || $( this ).attr( "name" ); // For contenteditable + var isContentEditable = typeof $( this ).attr( "contenteditable" ) !== "undefined" && $( this ).attr( "contenteditable" ) !== "false"; + + if ( !name && validator.settings.debug && window.console ) { + console.error( "%o has no name assigned", this ); + } + + // Set form expando on contenteditable + if ( isContentEditable ) { + this.form = $( this ).closest( "form" )[ 0 ]; + this.name = name; + } + + // Ignore elements that belong to other/nested forms + if ( this.form !== validator.currentForm ) { + return false; + } + + // Select only the first element for each name, and only those with rules specified + if ( name in rulesCache || !validator.objectLength( $( this ).rules() ) ) { + return false; + } + + rulesCache[ name ] = true; + return true; + } ); + }, + + clean: function( selector ) { + return $( selector )[ 0 ]; + }, + + errors: function() { + var errorClass = this.settings.errorClass.split( " " ).join( "." ); + return $( this.settings.errorElement + "." + errorClass, this.errorContext ); + }, + + resetInternals: function() { + this.successList = []; + this.errorList = []; + this.errorMap = {}; + this.toShow = $( [] ); + this.toHide = $( [] ); + }, + + reset: function() { + this.resetInternals(); + this.currentElements = $( [] ); + }, + + prepareForm: function() { + this.reset(); + this.toHide = this.errors().add( this.containers ); + }, + + prepareElement: function( element ) { + this.reset(); + this.toHide = this.errorsFor( element ); + }, + + elementValue: function( element ) { + var $element = $( element ), + type = element.type, + isContentEditable = typeof $element.attr( "contenteditable" ) !== "undefined" && $element.attr( "contenteditable" ) !== "false", + val, idx; + + if ( type === "radio" || type === "checkbox" ) { + return this.findByName( element.name ).filter( ":checked" ).val(); + } else if ( type === "number" && typeof element.validity !== "undefined" ) { + return element.validity.badInput ? "NaN" : $element.val(); + } + + if ( isContentEditable ) { + val = $element.text(); + } else { + val = $element.val(); + } + + if ( type === "file" ) { + + // Modern browser (chrome & safari) + if ( val.substr( 0, 12 ) === "C:\\fakepath\\" ) { + return val.substr( 12 ); + } + + // Legacy browsers + // Unix-based path + idx = val.lastIndexOf( "/" ); + if ( idx >= 0 ) { + return val.substr( idx + 1 ); + } + + // Windows-based path + idx = val.lastIndexOf( "\\" ); + if ( idx >= 0 ) { + return val.substr( idx + 1 ); + } + + // Just the file name + return val; + } + + if ( typeof val === "string" ) { + return val.replace( /\r/g, "" ); + } + return val; + }, + + check: function( element ) { + element = this.validationTargetFor( this.clean( element ) ); + + var rules = $( element ).rules(), + rulesCount = $.map( rules, function( n, i ) { + return i; + } ).length, + dependencyMismatch = false, + val = this.elementValue( element ), + result, method, rule, normalizer; + + // Abort any pending Ajax request from a previous call to this method. + this.abortRequest( element ); + + // Prioritize the local normalizer defined for this element over the global one + // if the former exists, otherwise user the global one in case it exists. + if ( typeof rules.normalizer === "function" ) { + normalizer = rules.normalizer; + } else if ( typeof this.settings.normalizer === "function" ) { + normalizer = this.settings.normalizer; + } + + // If normalizer is defined, then call it to retreive the changed value instead + // of using the real one. + // Note that `this` in the normalizer is `element`. + if ( normalizer ) { + val = normalizer.call( element, val ); + + // Delete the normalizer from rules to avoid treating it as a pre-defined method. + delete rules.normalizer; + } + + for ( method in rules ) { + rule = { method: method, parameters: rules[ method ] }; + try { + result = $.validator.methods[ method ].call( this, val, element, rule.parameters ); + + // If a method indicates that the field is optional and therefore valid, + // don't mark it as valid when there are no other rules + if ( result === "dependency-mismatch" && rulesCount === 1 ) { + dependencyMismatch = true; + continue; + } + dependencyMismatch = false; + + if ( result === "pending" ) { + this.toHide = this.toHide.not( this.errorsFor( element ) ); + return; + } + + if ( !result ) { + this.formatAndAdd( element, rule ); + return false; + } + } catch ( e ) { + if ( this.settings.debug && window.console ) { + console.log( "Exception occurred when checking element " + element.id + ", check the '" + rule.method + "' method.", e ); + } + if ( e instanceof TypeError ) { + e.message += ". Exception occurred when checking element " + element.id + ", check the '" + rule.method + "' method."; + } + + throw e; + } + } + if ( dependencyMismatch ) { + return; + } + if ( this.objectLength( rules ) ) { + this.successList.push( element ); + } + return true; + }, + + // Return the custom message for the given element and validation method + // specified in the element's HTML5 data attribute + // return the generic message if present and no method specific message is present + customDataMessage: function( element, method ) { + return $( element ).data( "msg" + method.charAt( 0 ).toUpperCase() + + method.substring( 1 ).toLowerCase() ) || $( element ).data( "msg" ); + }, + + // Return the custom message for the given element name and validation method + customMessage: function( name, method ) { + var m = this.settings.messages[ name ]; + return m && ( m.constructor === String ? m : m[ method ] ); + }, + + // Return the first defined argument, allowing empty strings + findDefined: function() { + for ( var i = 0; i < arguments.length; i++ ) { + if ( arguments[ i ] !== undefined ) { + return arguments[ i ]; + } + } + return undefined; + }, + + // The second parameter 'rule' used to be a string, and extended to an object literal + // of the following form: + // rule = { + // method: "method name", + // parameters: "the given method parameters" + // } + // + // The old behavior still supported, kept to maintain backward compatibility with + // old code, and will be removed in the next major release. + defaultMessage: function( element, rule ) { + if ( typeof rule === "string" ) { + rule = { method: rule }; + } + + var message = this.findDefined( + this.customMessage( element.name, rule.method ), + this.customDataMessage( element, rule.method ), + + // 'title' is never undefined, so handle empty string as undefined + !this.settings.ignoreTitle && element.title || undefined, + $.validator.messages[ rule.method ], + "Warning: No message defined for " + element.name + "" + ), + theregex = /\$?\{(\d+)\}/g; + if ( typeof message === "function" ) { + message = message.call( this, rule.parameters, element ); + } else if ( theregex.test( message ) ) { + message = $.validator.format( message.replace( theregex, "{$1}" ), rule.parameters ); + } + + return message; + }, + + formatAndAdd: function( element, rule ) { + var message = this.defaultMessage( element, rule ); + + this.errorList.push( { + message: message, + element: element, + method: rule.method + } ); + + this.errorMap[ element.name ] = message; + this.submitted[ element.name ] = message; + }, + + addWrapper: function( toToggle ) { + if ( this.settings.wrapper ) { + toToggle = toToggle.add( toToggle.parent( this.settings.wrapper ) ); + } + return toToggle; + }, + + defaultShowErrors: function() { + var i, elements, error; + for ( i = 0; this.errorList[ i ]; i++ ) { + error = this.errorList[ i ]; + if ( this.settings.highlight ) { + this.settings.highlight.call( this, error.element, this.settings.errorClass, this.settings.validClass ); + } + this.showLabel( error.element, error.message ); + } + if ( this.errorList.length ) { + this.toShow = this.toShow.add( this.containers ); + } + if ( this.settings.success ) { + for ( i = 0; this.successList[ i ]; i++ ) { + this.showLabel( this.successList[ i ] ); + } + } + if ( this.settings.unhighlight ) { + for ( i = 0, elements = this.validElements(); elements[ i ]; i++ ) { + this.settings.unhighlight.call( this, elements[ i ], this.settings.errorClass, this.settings.validClass ); + } + } + this.toHide = this.toHide.not( this.toShow ); + this.hideErrors(); + this.addWrapper( this.toShow ).show(); + }, + + validElements: function() { + return this.currentElements.not( this.invalidElements() ); + }, + + invalidElements: function() { + return $( this.errorList ).map( function() { + return this.element; + } ); + }, + + showLabel: function( element, message ) { + var place, group, errorID, v, + error = this.errorsFor( element ), + elementID = this.idOrName( element ), + describedBy = $( element ).attr( "aria-describedby" ); + + if ( error.length ) { + + // Refresh error/success class + error.removeClass( this.settings.validClass ).addClass( this.settings.errorClass ); + + // Replace message on existing label + if ( this.settings && this.settings.escapeHtml ) { + error.text( message || "" ); + } else { + error.html( message || "" ); + } + } else { + + // Create error element + error = $( "<" + this.settings.errorElement + ">" ) + .attr( "id", elementID + "-error" ) + .addClass( this.settings.errorClass ); + + if ( this.settings && this.settings.escapeHtml ) { + error.text( message || "" ); + } else { + error.html( message || "" ); + } + + // Maintain reference to the element to be placed into the DOM + place = error; + if ( this.settings.wrapper ) { + + // Make sure the element is visible, even in IE + // actually showing the wrapped element is handled elsewhere + place = error.hide().show().wrap( "<" + this.settings.wrapper + "/>" ).parent(); + } + if ( this.labelContainer.length ) { + this.labelContainer.append( place ); + } else if ( this.settings.errorPlacement ) { + this.settings.errorPlacement.call( this, place, $( element ) ); + } else { + place.insertAfter( element ); + } + + // Link error back to the element + if ( error.is( "label" ) ) { + + // If the error is a label, then associate using 'for' + error.attr( "for", elementID ); + + // If the element is not a child of an associated label, then it's necessary + // to explicitly apply aria-describedby + } else if ( error.parents( "label[for='" + this.escapeCssMeta( elementID ) + "']" ).length === 0 ) { + errorID = error.attr( "id" ); + + // Respect existing non-error aria-describedby + if ( !describedBy ) { + describedBy = errorID; + } else if ( !describedBy.match( new RegExp( "\\b" + this.escapeCssMeta( errorID ) + "\\b" ) ) ) { + + // Add to end of list if not already present + describedBy += " " + errorID; + } + $( element ).attr( "aria-describedby", describedBy ); + + // If this element is grouped, then assign to all elements in the same group + group = this.groups[ element.name ]; + if ( group ) { + v = this; + $.each( v.groups, function( name, testgroup ) { + if ( testgroup === group ) { + $( "[name='" + v.escapeCssMeta( name ) + "']", v.currentForm ) + .attr( "aria-describedby", error.attr( "id" ) ); + } + } ); + } + } + } + if ( !message && this.settings.success ) { + error.text( "" ); + if ( typeof this.settings.success === "string" ) { + error.addClass( this.settings.success ); + } else { + this.settings.success( error, element ); + } + } + this.toShow = this.toShow.add( error ); + }, + + errorsFor: function( element ) { + var name = this.escapeCssMeta( this.idOrName( element ) ), + describer = $( element ).attr( "aria-describedby" ), + selector = "label[for='" + name + "'], label[for='" + name + "'] *"; + + // 'aria-describedby' should directly reference the error element + if ( describer ) { + selector = selector + ", #" + this.escapeCssMeta( describer ) + .replace( /\s+/g, ", #" ); + } + + return this + .errors() + .filter( selector ); + }, + + // See https://api.jquery.com/category/selectors/, for CSS + // meta-characters that should be escaped in order to be used with JQuery + // as a literal part of a name/id or any selector. + escapeCssMeta: function( string ) { + if ( string === undefined ) { + return ""; + } + + return string.replace( /([\\!"#$%&'()*+,./:;<=>?@\[\]^`{|}~])/g, "\\$1" ); + }, + + idOrName: function( element ) { + return this.groups[ element.name ] || ( this.checkable( element ) ? element.name : element.id || element.name ); + }, + + validationTargetFor: function( element ) { + + // If radio/checkbox, validate first element in group instead + if ( this.checkable( element ) ) { + element = this.findByName( element.name ); + } + + // Always apply ignore filter + return $( element ).not( this.settings.ignore )[ 0 ]; + }, + + checkable: function( element ) { + return ( /radio|checkbox/i ).test( element.type ); + }, + + findByName: function( name ) { + return $( this.currentForm ).find( "[name='" + this.escapeCssMeta( name ) + "']" ); + }, + + getLength: function( value, element ) { + switch ( element.nodeName.toLowerCase() ) { + case "select": + return $( "option:selected", element ).length; + case "input": + if ( this.checkable( element ) ) { + return this.findByName( element.name ).filter( ":checked" ).length; + } + } + return value.length; + }, + + depend: function( param, element ) { + return this.dependTypes[ typeof param ] ? this.dependTypes[ typeof param ]( param, element ) : true; + }, + + dependTypes: { + "boolean": function( param ) { + return param; + }, + "string": function( param, element ) { + return !!$( param, element.form ).length; + }, + "function": function( param, element ) { + return param( element ); + } + }, + + optional: function( element ) { + var val = this.elementValue( element ); + return !$.validator.methods.required.call( this, val, element ) && "dependency-mismatch"; + }, + + elementAjaxPort: function( element ) { + return "validate" + element.name; + }, + + startRequest: function( element ) { + if ( !this.pending[ element.name ] ) { + this.pendingRequest++; + $( element ).addClass( this.settings.pendingClass ); + this.pending[ element.name ] = true; + } + }, + + stopRequest: function( element, valid ) { + this.pendingRequest--; + + // Sometimes synchronization fails, make sure pendingRequest is never < 0 + if ( this.pendingRequest < 0 ) { + this.pendingRequest = 0; + } + delete this.pending[ element.name ]; + $( element ).removeClass( this.settings.pendingClass ); + if ( valid && this.pendingRequest === 0 && this.formSubmitted && this.form() && this.pendingRequest === 0 ) { + $( this.currentForm ).trigger( "submit" ); + + // Remove the hidden input that was used as a replacement for the + // missing submit button. The hidden input is added by `handle()` + // to ensure that the value of the used submit button is passed on + // for scripted submits triggered by this method + if ( this.submitButton ) { + $( "input:hidden[name='" + this.submitButton.name + "']", this.currentForm ).remove(); + } + + this.formSubmitted = false; + } else if ( !valid && this.pendingRequest === 0 && this.formSubmitted ) { + $( this.currentForm ).triggerHandler( "invalid-form", [ this ] ); + this.formSubmitted = false; + } + }, + + abortRequest: function( element ) { + var port; + + if ( this.pending[ element.name ] ) { + port = this.elementAjaxPort( element ); + $.ajaxAbort( port ); + + this.pendingRequest--; + + // Sometimes synchronization fails, make sure pendingRequest is never < 0 + if ( this.pendingRequest < 0 ) { + this.pendingRequest = 0; + } + + delete this.pending[ element.name ]; + $( element ).removeClass( this.settings.pendingClass ); + } + }, + + previousValue: function( element, method ) { + method = typeof method === "string" && method || "remote"; + + return $.data( element, "previousValue" ) || $.data( element, "previousValue", { + old: null, + valid: true, + message: this.defaultMessage( element, { method: method } ) + } ); + }, + + // Cleans up all forms and elements, removes validator-specific events + destroy: function() { + this.resetForm(); + + $( this.currentForm ) + .off( ".validate" ) + .removeData( "validator" ) + .find( ".validate-equalTo-blur" ) + .off( ".validate-equalTo" ) + .removeClass( "validate-equalTo-blur" ) + .find( ".validate-lessThan-blur" ) + .off( ".validate-lessThan" ) + .removeClass( "validate-lessThan-blur" ) + .find( ".validate-lessThanEqual-blur" ) + .off( ".validate-lessThanEqual" ) + .removeClass( "validate-lessThanEqual-blur" ) + .find( ".validate-greaterThanEqual-blur" ) + .off( ".validate-greaterThanEqual" ) + .removeClass( "validate-greaterThanEqual-blur" ) + .find( ".validate-greaterThan-blur" ) + .off( ".validate-greaterThan" ) + .removeClass( "validate-greaterThan-blur" ); + } + + }, + + classRuleSettings: { + required: { required: true }, + email: { email: true }, + url: { url: true }, + date: { date: true }, + dateISO: { dateISO: true }, + number: { number: true }, + digits: { digits: true }, + creditcard: { creditcard: true } + }, + + addClassRules: function( className, rules ) { + if ( className.constructor === String ) { + this.classRuleSettings[ className ] = rules; + } else { + $.extend( this.classRuleSettings, className ); + } + }, + + classRules: function( element ) { + var rules = {}, + classes = $( element ).attr( "class" ); + + if ( classes ) { + $.each( classes.split( " " ), function() { + if ( this in $.validator.classRuleSettings ) { + $.extend( rules, $.validator.classRuleSettings[ this ] ); + } + } ); + } + return rules; + }, + + normalizeAttributeRule: function( rules, type, method, value ) { + + // Convert the value to a number for number inputs, and for text for backwards compability + // allows type="date" and others to be compared as strings + if ( /min|max|step/.test( method ) && ( type === null || /number|range|text/.test( type ) ) ) { + value = Number( value ); + + // Support Opera Mini, which returns NaN for undefined minlength + if ( isNaN( value ) ) { + value = undefined; + } + } + + if ( value || value === 0 ) { + rules[ method ] = value; + } else if ( type === method && type !== "range" ) { + + // Exception: the jquery validate 'range' method + // does not test for the html5 'range' type + rules[ type === "date" ? "dateISO" : method ] = true; + } + }, + + attributeRules: function( element ) { + var rules = {}, + $element = $( element ), + type = element.getAttribute( "type" ), + method, value; + + for ( method in $.validator.methods ) { + + // Support for in both html5 and older browsers + if ( method === "required" ) { + value = element.getAttribute( method ); + + // Some browsers return an empty string for the required attribute + // and non-HTML5 browsers might have required="" markup + if ( value === "" ) { + value = true; + } + + // Force non-HTML5 browsers to return bool + value = !!value; + } else { + value = $element.attr( method ); + } + + this.normalizeAttributeRule( rules, type, method, value ); + } + + // 'maxlength' may be returned as -1, 2147483647 ( IE ) and 524288 ( safari ) for text inputs + if ( rules.maxlength && /-1|2147483647|524288/.test( rules.maxlength ) ) { + delete rules.maxlength; + } + + return rules; + }, + + dataRules: function( element ) { + var rules = {}, + $element = $( element ), + type = element.getAttribute( "type" ), + method, value; + + for ( method in $.validator.methods ) { + value = $element.data( "rule" + method.charAt( 0 ).toUpperCase() + method.substring( 1 ).toLowerCase() ); + + // Cast empty attributes like `data-rule-required` to `true` + if ( value === "" ) { + value = true; + } + + this.normalizeAttributeRule( rules, type, method, value ); + } + return rules; + }, + + staticRules: function( element ) { + var rules = {}, + validator = $.data( element.form, "validator" ); + + if ( validator.settings.rules ) { + rules = $.validator.normalizeRule( validator.settings.rules[ element.name ] ) || {}; + } + return rules; + }, + + normalizeRules: function( rules, element ) { + + // Handle dependency check + $.each( rules, function( prop, val ) { + + // Ignore rule when param is explicitly false, eg. required:false + if ( val === false ) { + delete rules[ prop ]; + return; + } + if ( val.param || val.depends ) { + var keepRule = true; + switch ( typeof val.depends ) { + case "string": + keepRule = !!$( val.depends, element.form ).length; + break; + case "function": + keepRule = val.depends.call( element, element ); + break; + } + if ( keepRule ) { + rules[ prop ] = val.param !== undefined ? val.param : true; + } else { + $.data( element.form, "validator" ).resetElements( $( element ) ); + delete rules[ prop ]; + } + } + } ); + + // Evaluate parameters + $.each( rules, function( rule, parameter ) { + rules[ rule ] = typeof parameter === "function" && rule !== "normalizer" ? parameter( element ) : parameter; + } ); + + // Clean number parameters + $.each( [ "minlength", "maxlength" ], function() { + if ( rules[ this ] ) { + rules[ this ] = Number( rules[ this ] ); + } + } ); + $.each( [ "rangelength", "range" ], function() { + var parts; + if ( rules[ this ] ) { + if ( Array.isArray( rules[ this ] ) ) { + rules[ this ] = [ Number( rules[ this ][ 0 ] ), Number( rules[ this ][ 1 ] ) ]; + } else if ( typeof rules[ this ] === "string" ) { + parts = rules[ this ].replace( /[\[\]]/g, "" ).split( /[\s,]+/ ); + rules[ this ] = [ Number( parts[ 0 ] ), Number( parts[ 1 ] ) ]; + } + } + } ); + + if ( $.validator.autoCreateRanges ) { + + // Auto-create ranges + if ( rules.min != null && rules.max != null ) { + rules.range = [ rules.min, rules.max ]; + delete rules.min; + delete rules.max; + } + if ( rules.minlength != null && rules.maxlength != null ) { + rules.rangelength = [ rules.minlength, rules.maxlength ]; + delete rules.minlength; + delete rules.maxlength; + } + } + + return rules; + }, + + // Converts a simple string to a {string: true} rule, e.g., "required" to {required:true} + normalizeRule: function( data ) { + if ( typeof data === "string" ) { + var transformed = {}; + $.each( data.split( /\s/ ), function() { + transformed[ this ] = true; + } ); + data = transformed; + } + return data; + }, + + // https://jqueryvalidation.org/jQuery.validator.addMethod/ + addMethod: function( name, method, message ) { + $.validator.methods[ name ] = method; + $.validator.messages[ name ] = message !== undefined ? message : $.validator.messages[ name ]; + if ( method.length < 3 ) { + $.validator.addClassRules( name, $.validator.normalizeRule( name ) ); + } + }, + + // https://jqueryvalidation.org/jQuery.validator.methods/ + methods: { + + // https://jqueryvalidation.org/required-method/ + required: function( value, element, param ) { + + // Check if dependency is met + if ( !this.depend( param, element ) ) { + return "dependency-mismatch"; + } + if ( element.nodeName.toLowerCase() === "select" ) { + + // Could be an array for select-multiple or a string, both are fine this way + var val = $( element ).val(); + return val && val.length > 0; + } + if ( this.checkable( element ) ) { + return this.getLength( value, element ) > 0; + } + return value !== undefined && value !== null && value.length > 0; + }, + + // https://jqueryvalidation.org/email-method/ + email: function( value, element ) { + + // From https://html.spec.whatwg.org/multipage/forms.html#valid-e-mail-address + // Retrieved 2014-01-14 + // If you have a problem with this implementation, report a bug against the above spec + // Or use custom methods to implement your own email validation + return this.optional( element ) || /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test( value ); + }, + + // https://jqueryvalidation.org/url-method/ + url: function( value, element ) { + + // Copyright (c) 2010-2013 Diego Perini, MIT licensed + // https://gist.github.com/dperini/729294 + // see also https://mathiasbynens.be/demo/url-regex + // modified to allow protocol-relative URLs + return this.optional( element ) || /^(?:(?:(?:https?|ftp):)?\/\/)(?:(?:[^\]\[?\/<~#`!@$^&*()+=}|:";',>{ ]|%[0-9A-Fa-f]{2})+(?::(?:[^\]\[?\/<~#`!@$^&*()+=}|:";',>{ ]|%[0-9A-Fa-f]{2})*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\u00a1-\uffff][a-z0-9\u00a1-\uffff_-]{0,62})?[a-z0-9\u00a1-\uffff]\.)+(?:[a-z\u00a1-\uffff]{2,}\.?))(?::\d{2,5})?(?:[/?#]\S*)?$/i.test( value ); + }, + + // https://jqueryvalidation.org/date-method/ + date: ( function() { + var called = false; + + return function( value, element ) { + if ( !called ) { + called = true; + if ( this.settings.debug && window.console ) { + console.warn( + "The `date` method is deprecated and will be removed in version '2.0.0'.\n" + + "Please don't use it, since it relies on the Date constructor, which\n" + + "behaves very differently across browsers and locales. Use `dateISO`\n" + + "instead or one of the locale specific methods in `localizations/`\n" + + "and `additional-methods.js`." + ); + } + } + + return this.optional( element ) || !/Invalid|NaN/.test( new Date( value ).toString() ); + }; + }() ), + + // https://jqueryvalidation.org/dateISO-method/ + dateISO: function( value, element ) { + return this.optional( element ) || /^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/.test( value ); + }, + + // https://jqueryvalidation.org/number-method/ + number: function( value, element ) { + return this.optional( element ) || /^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:-?\.\d+)?$/.test( value ); + }, + + // https://jqueryvalidation.org/digits-method/ + digits: function( value, element ) { + return this.optional( element ) || /^\d+$/.test( value ); + }, + + // https://jqueryvalidation.org/minlength-method/ + minlength: function( value, element, param ) { + var length = Array.isArray( value ) ? value.length : this.getLength( value, element ); + return this.optional( element ) || length >= param; + }, + + // https://jqueryvalidation.org/maxlength-method/ + maxlength: function( value, element, param ) { + var length = Array.isArray( value ) ? value.length : this.getLength( value, element ); + return this.optional( element ) || length <= param; + }, + + // https://jqueryvalidation.org/rangelength-method/ + rangelength: function( value, element, param ) { + var length = Array.isArray( value ) ? value.length : this.getLength( value, element ); + return this.optional( element ) || ( length >= param[ 0 ] && length <= param[ 1 ] ); + }, + + // https://jqueryvalidation.org/min-method/ + min: function( value, element, param ) { + return this.optional( element ) || value >= param; + }, + + // https://jqueryvalidation.org/max-method/ + max: function( value, element, param ) { + return this.optional( element ) || value <= param; + }, + + // https://jqueryvalidation.org/range-method/ + range: function( value, element, param ) { + return this.optional( element ) || ( value >= param[ 0 ] && value <= param[ 1 ] ); + }, + + // https://jqueryvalidation.org/step-method/ + step: function( value, element, param ) { + var type = $( element ).attr( "type" ), + errorMessage = "Step attribute on input type " + type + " is not supported.", + supportedTypes = [ "text", "number", "range" ], + re = new RegExp( "\\b" + type + "\\b" ), + notSupported = type && !re.test( supportedTypes.join() ), + decimalPlaces = function( num ) { + var match = ( "" + num ).match( /(?:\.(\d+))?$/ ); + if ( !match ) { + return 0; + } + + // Number of digits right of decimal point. + return match[ 1 ] ? match[ 1 ].length : 0; + }, + toInt = function( num ) { + return Math.round( num * Math.pow( 10, decimals ) ); + }, + valid = true, + decimals; + + // Works only for text, number and range input types + // TODO find a way to support input types date, datetime, datetime-local, month, time and week + if ( notSupported ) { + throw new Error( errorMessage ); + } + + decimals = decimalPlaces( param ); + + // Value can't have too many decimals + if ( decimalPlaces( value ) > decimals || toInt( value ) % toInt( param ) !== 0 ) { + valid = false; + } + + return this.optional( element ) || valid; + }, + + // https://jqueryvalidation.org/equalTo-method/ + equalTo: function( value, element, param ) { + + // Bind to the blur event of the target in order to revalidate whenever the target field is updated + var target = $( param ); + if ( this.settings.onfocusout && target.not( ".validate-equalTo-blur" ).length ) { + target.addClass( "validate-equalTo-blur" ).on( "blur.validate-equalTo", function() { + $( element ).valid(); + } ); + } + return value === target.val(); + }, + + // https://jqueryvalidation.org/remote-method/ + remote: function( value, element, param, method ) { + if ( this.optional( element ) ) { + return "dependency-mismatch"; + } + + method = typeof method === "string" && method || "remote"; + + var previous = this.previousValue( element, method ), + validator, data, optionDataString; + + if ( !this.settings.messages[ element.name ] ) { + this.settings.messages[ element.name ] = {}; + } + previous.originalMessage = previous.originalMessage || this.settings.messages[ element.name ][ method ]; + this.settings.messages[ element.name ][ method ] = previous.message; + + param = typeof param === "string" && { url: param } || param; + optionDataString = $.param( $.extend( { data: value }, param.data ) ); + if ( previous.valid !== null && previous.old === optionDataString ) { + return previous.valid; + } + + previous.old = optionDataString; + previous.valid = null; + validator = this; + this.startRequest( element ); + data = {}; + data[ element.name ] = value; + $.ajax( $.extend( true, { + mode: "abort", + port: this.elementAjaxPort( element ), + dataType: "json", + data: data, + context: validator.currentForm, + success: function( response ) { + var valid = response === true || response === "true", + errors, message, submitted; + + validator.settings.messages[ element.name ][ method ] = previous.originalMessage; + if ( valid ) { + submitted = validator.formSubmitted; + validator.toHide = validator.errorsFor( element ); + validator.formSubmitted = submitted; + validator.successList.push( element ); + validator.invalid[ element.name ] = false; + validator.showErrors(); + } else { + errors = {}; + message = response || validator.defaultMessage( element, { method: method, parameters: value } ); + errors[ element.name ] = previous.message = message; + validator.invalid[ element.name ] = true; + validator.showErrors( errors ); + } + previous.valid = valid; + validator.stopRequest( element, valid ); + } + }, param ) ); + return "pending"; + } + } + +} ); + +// Ajax mode: abort +// usage: $.ajax({ mode: "abort"[, port: "uniqueport"]}); +// $.ajaxAbort( port ); +// if mode:"abort" is used, the previous request on that port (port can be undefined) is aborted via XMLHttpRequest.abort() + +var pendingRequests = {}, + ajax; + +// Use a prefilter if available (1.5+) +if ( $.ajaxPrefilter ) { + $.ajaxPrefilter( function( settings, _, xhr ) { + var port = settings.port; + if ( settings.mode === "abort" ) { + $.ajaxAbort( port ); + pendingRequests[ port ] = xhr; + } + } ); +} else { + + // Proxy ajax + ajax = $.ajax; + $.ajax = function( settings ) { + var mode = ( "mode" in settings ? settings : $.ajaxSettings ).mode, + port = ( "port" in settings ? settings : $.ajaxSettings ).port; + if ( mode === "abort" ) { + $.ajaxAbort( port ); + pendingRequests[ port ] = ajax.apply( this, arguments ); + return pendingRequests[ port ]; + } + return ajax.apply( this, arguments ); + }; +} + +// Abort the previous request without sending a new one +$.ajaxAbort = function( port ) { + if ( pendingRequests[ port ] ) { + pendingRequests[ port ].abort(); + delete pendingRequests[ port ]; + } +}; +return $; +})); +/*! + * @copyright Copyright © Kartik Visweswaran, Krajee.com, 2014 - 2025 + * @version 1.3.6 + * + * Date formatter utility library that allows formatting date/time variables or Date objects using PHP DateTime format. + * This library is a standalone javascript library and does not depend on other libraries or plugins like jQuery. The + * library also adds support for Universal Module Definition (UMD). + * + * @see http://php.net/manual/en/function.date.php + * + * For more JQuery plugins visit http://plugins.krajee.com + * For more Yii related demos visit http://demos.krajee.com + */ +(function (root, factory) { + // noinspection JSUnresolvedVariable + if (typeof define === 'function' && define.amd) { // AMD + // noinspection JSUnresolvedFunction + define([], factory); + } else { + // noinspection JSUnresolvedVariable + if (typeof module === 'object' && module.exports) { // Node + // noinspection JSUnresolvedVariable + module.exports = factory(); + } else { // Browser globals + root.DateFormatter = factory(); + } + } +}(typeof self !== 'undefined' ? self : this, function () { + var DateFormatter, $h; + /** + * Global helper object + */ + $h = { + DAY: 1000 * 60 * 60 * 24, + HOUR: 3600, + defaults: { + dateSettings: { + days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], + daysShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], + months: [ + 'January', 'February', 'March', 'April', 'May', 'June', 'July', + 'August', 'September', 'October', 'November', 'December' + ], + monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], + meridiem: ['AM', 'PM'], + ordinal: function (number) { + var n = number % 10, suffixes = {1: 'st', 2: 'nd', 3: 'rd'}; + return Math.floor(number % 100 / 10) === 1 || !suffixes[n] ? 'th' : suffixes[n]; + } + }, + separators: /[ \-+\/.:@]/g, + validParts: /[dDjlNSwzWFmMntLoYyaABgGhHisueTIOPZcrU]/g, + intParts: /[djwNzmnyYhHgGis]/g, + tzParts: /\b(?:[PMCEA][SDP]T|(?:Australian|Pacific|Mountain|Central|Eastern|Atlantic) (?:Eastern) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g, + tzClip: /[^-+\dA-Z]/g + }, + getInt: function (str, radix) { + return parseInt(str, (radix ? radix : 10)); + }, + compare: function (str1, str2) { + return typeof (str1) === 'string' && typeof (str2) === 'string' && str1.toLowerCase() === str2.toLowerCase(); + }, + lpad: function (value, length, chr) { + var val = value.toString(); + chr = chr || '0'; + return val.length < length ? $h.lpad(chr + val, length) : val; + }, + merge: function (out) { + var i, obj; + out = out || {}; + for (i = 1; i < arguments.length; i++) { + obj = arguments[i]; + if (!obj) { + continue; + } + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + if (typeof obj[key] === 'object') { + $h.merge(out[key], obj[key]); + } else { + out[key] = obj[key]; + } + } + } + } + return out; + }, + getIndex: function (val, arr) { + for (var i = 0; i < arr.length; i++) { + if (arr[i].toLowerCase() === val.toLowerCase()) { + return i; + } + } + return -1; + } + }; + + /** + * Date Formatter Library Constructor + * @param options + * @constructor + */ + DateFormatter = function (options) { + var self = this, config = $h.merge($h.defaults, options); + self.dateSettings = config.dateSettings; + self.separators = config.separators; + self.validParts = config.validParts; + self.intParts = config.intParts; + self.tzParts = config.tzParts; + self.tzClip = config.tzClip; + }; + + /** + * DateFormatter Library Prototype + */ + DateFormatter.prototype = { + constructor: DateFormatter, + getMonth: function (val) { + var self = this, i; + i = $h.getIndex(val, self.dateSettings.monthsShort) + 1; + if (i === 0) { + i = $h.getIndex(val, self.dateSettings.months) + 1; + } + return i; + }, + parseDate: function (vDate, vFormat) { + var self = this, vFormatParts, vDateParts, i, vDateFlag = false, vTimeFlag = false, vDatePart, iDatePart, + vSettings = self.dateSettings, vMonth, vMeriIndex, vMeriOffset, len, mer, + out = {date: null, year: null, month: null, day: null, hour: 0, min: 0, sec: 0}; + if (!vDate) { + return null; + } + if (vDate instanceof Date) { + return vDate; + } + if (vFormat === 'U') { + i = $h.getInt(vDate); + return i ? new Date(i * 1000) : vDate; + } + switch (typeof vDate) { + case 'number': + return new Date(vDate); + case 'string': + break; + default: + return null; + } + vFormatParts = vFormat.match(self.validParts); + if (!vFormatParts || vFormatParts.length === 0) { + throw new Error('Invalid date format definition.'); + } + for (i = vFormatParts.length - 1; i >= 0; i--) { + if (vFormatParts[i] === 'S') { + vFormatParts.splice(i, 1); + } + } + vDateParts = vDate.replace(self.separators, '\0').split('\0'); + for (i = 0; i < vDateParts.length; i++) { + vDatePart = vDateParts[i]; + iDatePart = $h.getInt(vDatePart); + switch (vFormatParts[i]) { + case 'y': + case 'Y': + if (iDatePart) { + len = vDatePart.length; + out.year = len === 2 ? $h.getInt((iDatePart < 70 ? '20' : '19') + vDatePart) : iDatePart; + } else { + return null; + } + vDateFlag = true; + break; + case 'm': + case 'n': + case 'M': + case 'F': + if (isNaN(iDatePart)) { + vMonth = self.getMonth(vDatePart); + if (vMonth > 0) { + out.month = vMonth; + } else { + return null; + } + } else { + if (iDatePart >= 1 && iDatePart <= 12) { + out.month = iDatePart; + } else { + return null; + } + } + vDateFlag = true; + break; + case 'd': + case 'j': + if (iDatePart >= 1 && iDatePart <= 31) { + out.day = iDatePart; + } else { + return null; + } + vDateFlag = true; + break; + case 'g': + case 'h': + vMeriIndex = (vFormatParts.indexOf('a') > -1) ? vFormatParts.indexOf('a') : + ((vFormatParts.indexOf('A') > -1) ? vFormatParts.indexOf('A') : -1); + mer = vDateParts[vMeriIndex]; + if (vMeriIndex !== -1) { + vMeriOffset = $h.compare(mer, vSettings.meridiem[0]) ? 0 : + ($h.compare(mer, vSettings.meridiem[1]) ? 12 : -1); + if (iDatePart >= 1 && iDatePart <= 12 && vMeriOffset !== -1) { + out.hour = iDatePart % 12 === 0 ? vMeriOffset : iDatePart + vMeriOffset; + } else { + if (iDatePart >= 0 && iDatePart <= 23) { + out.hour = iDatePart; + } + } + } else { + if (iDatePart >= 0 && iDatePart <= 23) { + out.hour = iDatePart; + } else { + return null; + } + } + vTimeFlag = true; + break; + case 'G': + case 'H': + if (iDatePart >= 0 && iDatePart <= 23) { + out.hour = iDatePart; + } else { + return null; + } + vTimeFlag = true; + break; + case 'i': + if (iDatePart >= 0 && iDatePart <= 59) { + out.min = iDatePart; + } else { + return null; + } + vTimeFlag = true; + break; + case 's': + if (iDatePart >= 0 && iDatePart <= 59) { + out.sec = iDatePart; + } else { + return null; + } + vTimeFlag = true; + break; + } + } + if (vDateFlag === true) { + var varY = out.year || 0, varM = out.month ? out.month - 1 : 0, varD = out.day || 1; + out.date = new Date(varY, varM, varD, out.hour, out.min, out.sec, 0); + } else { + if (vTimeFlag !== true) { + return null; + } + out.date = new Date(0, 0, 0, out.hour, out.min, out.sec, 0); + } + return out.date; + }, + guessDate: function (vDateStr, vFormat) { + if (typeof vDateStr !== 'string') { + return vDateStr; + } + var self = this, vParts = vDateStr.replace(self.separators, '\0').split('\0'), vPattern = /^[djmn]/g, len, + vFormatParts = vFormat.match(self.validParts), vDate = new Date(), vDigit = 0, vYear, i, n, iPart, iSec; + + if (!vPattern.test(vFormatParts[0])) { + return vDateStr; + } + + for (i = 0; i < vParts.length; i++) { + vDigit = 2; + iPart = vParts[i]; + iSec = $h.getInt(iPart.substr(0, 2)); + if (isNaN(iSec)) { + return null; + } + switch (i) { + case 0: + if (vFormatParts[0] === 'm' || vFormatParts[0] === 'n') { + vDate.setMonth(iSec - 1); + } else { + vDate.setDate(iSec); + } + break; + case 1: + if (vFormatParts[0] === 'm' || vFormatParts[0] === 'n') { + vDate.setDate(iSec); + } else { + vDate.setMonth(iSec - 1); + } + break; + case 2: + vYear = vDate.getFullYear(); + len = iPart.length; + vDigit = len < 4 ? len : 4; + vYear = $h.getInt(len < 4 ? vYear.toString().substr(0, 4 - len) + iPart : iPart.substr(0, 4)); + if (!vYear) { + return null; + } + vDate.setFullYear(vYear); + break; + case 3: + vDate.setHours(iSec); + break; + case 4: + vDate.setMinutes(iSec); + break; + case 5: + vDate.setSeconds(iSec); + break; + } + n = iPart.substr(vDigit); + if (n.length > 0) { + vParts.splice(i + 1, 0, n); + } + } + return vDate; + }, + parseFormat: function (vChar, vDate) { + var self = this, vSettings = self.dateSettings, fmt, backslash = /\\?(.?)/gi, doFormat = function (t, s) { + return fmt[t] ? fmt[t]() : s; + }; + fmt = { + ///////// + // DAY // + ///////// + /** + * Day of month with leading 0: `01..31` + * @return {string} + */ + d: function () { + return $h.lpad(fmt.j(), 2); + }, + /** + * Shorthand day name: `Mon...Sun` + * @return {string} + */ + D: function () { + return vSettings.daysShort[fmt.w()]; + }, + /** + * Day of month: `1..31` + * @return {number} + */ + j: function () { + return vDate.getDate(); + }, + /** + * Full day name: `Monday...Sunday` + * @return {string} + */ + l: function () { + return vSettings.days[fmt.w()]; + }, + /** + * ISO-8601 day of week: `1[Mon]..7[Sun]` + * @return {number} + */ + N: function () { + return fmt.w() || 7; + }, + /** + * Day of week: `0[Sun]..6[Sat]` + * @return {number} + */ + w: function () { + return vDate.getDay(); + }, + /** + * Day of year: `0..365` + * @return {number} + */ + z: function () { + var a = new Date(fmt.Y(), fmt.n() - 1, fmt.j()), b = new Date(fmt.Y(), 0, 1); + return Math.round((a - b) / $h.DAY); + }, + + ////////// + // WEEK // + ////////// + /** + * ISO-8601 week number + * @return {number} + */ + W: function () { + var a = new Date(fmt.Y(), fmt.n() - 1, fmt.j() - fmt.N() + 3), b = new Date(a.getFullYear(), 0, 4); + return $h.lpad(1 + Math.round((a - b) / $h.DAY / 7), 2); + }, + + /////////// + // MONTH // + /////////// + /** + * Full month name: `January...December` + * @return {string} + */ + F: function () { + return vSettings.months[vDate.getMonth()]; + }, + /** + * Month w/leading 0: `01..12` + * @return {string} + */ + m: function () { + return $h.lpad(fmt.n(), 2); + }, + /** + * Shorthand month name; `Jan...Dec` + * @return {string} + */ + M: function () { + return vSettings.monthsShort[vDate.getMonth()]; + }, + /** + * Month: `1...12` + * @return {number} + */ + n: function () { + return vDate.getMonth() + 1; + }, + /** + * Days in month: `28...31` + * @return {number} + */ + t: function () { + return (new Date(fmt.Y(), fmt.n(), 0)).getDate(); + }, + + ////////// + // YEAR // + ////////// + /** + * Is leap year? `0 or 1` + * @return {number} + */ + L: function () { + var Y = fmt.Y(); + return (Y % 4 === 0 && Y % 100 !== 0 || Y % 400 === 0) ? 1 : 0; + }, + /** + * ISO-8601 year + * @return {number} + */ + o: function () { + var n = fmt.n(), W = fmt.W(), Y = fmt.Y(); + return Y + (n === 12 && W < 9 ? 1 : n === 1 && W > 9 ? -1 : 0); + }, + /** + * Full year: `e.g. 1980...2010` + * @return {number} + */ + Y: function () { + return vDate.getFullYear(); + }, + /** + * Last two digits of year: `00...99` + * @return {string} + */ + y: function () { + return fmt.Y().toString().slice(-2); + }, + + ////////// + // TIME // + ////////// + /** + * Meridian lower: `am or pm` + * @return {string} + */ + a: function () { + return fmt.A().toLowerCase(); + }, + /** + * Meridian upper: `AM or PM` + * @return {string} + */ + A: function () { + var n = fmt.G() < 12 ? 0 : 1; + return vSettings.meridiem[n]; + }, + /** + * Swatch Internet time: `000..999` + * @return {string} + */ + B: function () { + var H = vDate.getUTCHours() * $h.HOUR, i = vDate.getUTCMinutes() * 60, s = vDate.getUTCSeconds(); + return $h.lpad(Math.floor((H + i + s + $h.HOUR) / 86.4) % 1000, 3); + }, + /** + * 12-Hours: `1..12` + * @return {number} + */ + g: function () { + return fmt.G() % 12 || 12; + }, + /** + * 24-Hours: `0..23` + * @return {number} + */ + G: function () { + return vDate.getHours(); + }, + /** + * 12-Hours with leading 0: `01..12` + * @return {string} + */ + h: function () { + return $h.lpad(fmt.g(), 2); + }, + /** + * 24-Hours w/leading 0: `00..23` + * @return {string} + */ + H: function () { + return $h.lpad(fmt.G(), 2); + }, + /** + * Minutes w/leading 0: `00..59` + * @return {string} + */ + i: function () { + return $h.lpad(vDate.getMinutes(), 2); + }, + /** + * Seconds w/leading 0: `00..59` + * @return {string} + */ + s: function () { + return $h.lpad(vDate.getSeconds(), 2); + }, + /** + * Microseconds: `000000-999000` + * @return {string} + */ + u: function () { + return $h.lpad(vDate.getMilliseconds() * 1000, 6); + }, + + ////////////// + // TIMEZONE // + ////////////// + /** + * Timezone identifier: `e.g. Atlantic/Azores, ...` + * @return {string} + */ + e: function () { + var str = /\((.*)\)/.exec(String(vDate))[1]; + return str || 'Coordinated Universal Time'; + }, + /** + * DST observed? `0 or 1` + * @return {number} + */ + I: function () { + var a = new Date(fmt.Y(), 0), c = Date.UTC(fmt.Y(), 0), + b = new Date(fmt.Y(), 6), d = Date.UTC(fmt.Y(), 6); + return ((a - c) !== (b - d)) ? 1 : 0; + }, + /** + * Difference to GMT in hour format: `e.g. +0200` + * @return {string} + */ + O: function () { + var tzo = vDate.getTimezoneOffset(), a = Math.abs(tzo); + return (tzo > 0 ? '-' : '+') + $h.lpad(Math.floor(a / 60) * 100 + a % 60, 4); + }, + /** + * Difference to GMT with colon: `e.g. +02:00` + * @return {string} + */ + P: function () { + var O = fmt.O(); + return (O.substr(0, 3) + ':' + O.substr(3, 2)); + }, + /** + * Timezone abbreviation: `e.g. EST, MDT, ...` + * @return {string} + */ + T: function () { + var str = (String(vDate).match(self.tzParts) || ['']).pop().replace(self.tzClip, ''); + return str || 'UTC'; + }, + /** + * Timezone offset in seconds: `-43200...50400` + * @return {number} + */ + Z: function () { + return -vDate.getTimezoneOffset() * 60; + }, + + //////////////////// + // FULL DATE TIME // + //////////////////// + /** + * ISO-8601 date + * @return {string} + */ + c: function () { + return 'Y-m-d\\TH:i:sP'.replace(backslash, doFormat); + }, + /** + * RFC 2822 date + * @return {string} + */ + r: function () { + return 'D, d M Y H:i:s O'.replace(backslash, doFormat); + }, + /** + * Seconds since UNIX epoch + * @return {number} + */ + U: function () { + return vDate.getTime() / 1000 || 0; + } + }; + return doFormat(vChar, vChar); + }, + formatDate: function (vDate, vFormat) { + var self = this, i, n, len, str, vChar, vDateStr = '', BACKSLASH = '\\'; + if (typeof vDate === 'string') { + vDate = self.parseDate(vDate, vFormat); + if (!vDate) { + return null; + } + } + if (vDate instanceof Date) { + len = vFormat.length; + for (i = 0; i < len; i++) { + vChar = vFormat.charAt(i); + if (vChar === 'S' || vChar === BACKSLASH) { + continue; + } + if (i > 0 && vFormat.charAt(i - 1) === BACKSLASH) { + vDateStr += vChar; + continue; + } + str = self.parseFormat(vChar, vDate); + if (i !== (len - 1) && self.intParts.test(vChar) && vFormat.charAt(i + 1) === 'S') { + n = $h.getInt(str) || 0; + str += self.dateSettings.ordinal(n); + } + vDateStr += str; + } + return vDateStr; + } + return ''; + } + }; + Object.freeze(DateFormatter); + return DateFormatter; +})); + +/*! + * Laravel Javascript Validation + * + * https://github.com/proengsoft/laravel-jsvalidation + * + * Copyright (c) 2017 Proengsoft + * Released under the MIT license + */ + +var laravelValidation; +laravelValidation = { + + implicitRules: ['Required','Confirmed'], + + /** + * Initialize laravel validations. + */ + init: function () { + + // jquery-validation requires the field under validation to be present. We're adding a dummy hidden + // field so that any errors are not visible. + var constructor = $.fn.validate; + $.fn.validate = function( options ) { + var name = 'proengsoft_jsvalidation'; // must match the name defined in JsValidatorFactory.newFormRequestValidator + var $elm = $(this).find('input[name="' + name + '"]'); + if ($elm.length === 0) { + $('').attr({type: 'hidden', name: name}).appendTo(this); + } + + return constructor.apply(this, [options]); + }; + + // Disable class rules and attribute rules + $.validator.classRuleSettings = {}; + $.validator.attributeRules = function () {}; + + $.validator.dataRules = this.arrayRules; + $.validator.prototype.arrayRulesCache = {}; + + // Register validations methods + this.setupValidations(); + }, + + arrayRules: function(element) { + + var rules = {}, + validator = $.data( element.form, "validator"), + cache = validator.arrayRulesCache; + + // Is not an Array + if (element.name.indexOf('[') === -1) { + return rules; + } + + if (! (element.name in cache)) { + cache[element.name] = {}; + } + + $.each(validator.settings.rules, function(name, tmpRules) { + if (name in cache[element.name]) { + rules = laravelValidation.helpers.mergeRules(rules, cache[element.name][name]); + } else { + cache[element.name][name] = {}; + + var nameRegExp = laravelValidation.helpers.regexFromWildcard(name); + if (element.name.match(nameRegExp)) { + var newRules = $.validator.normalizeRule(tmpRules) || {}; + cache[element.name][name] = newRules; + + rules = laravelValidation.helpers.mergeRules(rules, newRules); + } + } + }); + + return rules; + }, + + setupValidations: function () { + + /** + * Get CSRF token. + * + * @param params + * @returns {string} + */ + var getCsrfToken = function (params) { + return params[0][1][1]; + }; + + /** + * Whether to validate all attributes. + * + * @param params + * @returns {boolean} + */ + var isValidateAll = function (params) { + return params[0][1][2]; + }; + + /** + * Determine whether the rule is implicit. + * + * @param params + * @returns {boolean} + */ + var isImplicit = function (params) { + var implicit = false; + $.each(params, function (i, parameters) { + implicit = implicit || parameters[3]; + }); + + return implicit; + }; + + /** + * Get form method from a validator instance. + * + * @param validator + * @returns {string} + */ + var formMethod = function (validator) { + var formMethod = $(validator.currentForm).attr('method'); + if ($(validator.currentForm).find('input[name="_method"]').length) { + formMethod = $(validator.currentForm).find('input[name="_method"]').val(); + } + + return formMethod; + }; + + /** + * Get AJAX parameters for remote requests. + * + * @param validator + * @param element + * @param params + * @param data + * @returns {object} + */ + var ajaxOpts = function (validator, element, params, data) { + return { + mode: 'abort', + port: 'validate' + element.name, + dataType: 'json', + data: data, + context: validator.currentForm, + url: $(validator.currentForm).attr('action'), + type: formMethod(validator), + beforeSend: function (xhr) { + var token = getCsrfToken(params); + if (formMethod(validator) !== 'get' && token) { + return xhr.setRequestHeader('X-XSRF-TOKEN', token); + } + }, + }; + }; + + /** + * Validate a set of local JS based rules against an element. + * + * @param validator + * @param values + * @param element + * @param rules + * @returns {boolean} + */ + var validateLocalRules = function (validator, values, element, rules) { + var validated = true, + previous = validator.previousValue(element); + + $.each(rules, function (i, param) { + var implicit = param[3] || laravelValidation.implicitRules.indexOf(param[0]) !== -1; + var rule = param[0]; + var message = param[2]; + + if (! implicit && validator.optional(element)) { + validated = "dependency-mismatch"; + return false; + } + + if (laravelValidation.methods[rule] !== undefined) { + $.each(values, function(index, value) { + validated = laravelValidation.methods[rule].call(validator, value, element, param[1], function(valid) { + validator.settings.messages[element.name].laravelValidationRemote = previous.originalMessage; + if (valid) { + var submitted = validator.formSubmitted; + validator.prepareElement(element); + validator.formSubmitted = submitted; + validator.successList.push(element); + delete validator.invalid[element.name]; + validator.showErrors(); + } else { + var errors = {}; + errors[ element.name ] + = previous.message + = typeof message === "function" ? message( value ) : message; + validator.invalid[element.name] = true; + validator.showErrors(errors); + } + validator.showErrors(validator.errorMap); + previous.valid = valid; + }); + + // Break loop. + if (validated === false) { + return false; + } + }); + } else { + validated = false; + } + + if (validated !== true) { + if (!validator.settings.messages[element.name] ) { + validator.settings.messages[element.name] = {}; + } + + validator.settings.messages[element.name].laravelValidation= message; + + return false; + } + + }); + + return validated; + }; + + /** + * Create JQueryValidation check to validate Laravel rules. + */ + + $.validator.addMethod("laravelValidation", function (value, element, params) { + var rules = [], + arrayRules = []; + $.each(params, function (i, param) { + // put Implicit rules in front + var isArrayRule = param[4].indexOf('[') !== -1; + if (param[3] || laravelValidation.implicitRules.indexOf(param[0]) !== -1) { + isArrayRule ? arrayRules.unshift(param) : rules.unshift(param); + } else { + isArrayRule ? arrayRules.push(param) : rules.push(param); + } + }); + + // Validate normal rules. + var localRulesResult = validateLocalRules(this, [value], element, rules); + + // Validate items of the array using array rules. + var arrayValue = ! Array.isArray(value) ? [value] : value; + var arrayRulesResult = validateLocalRules(this, arrayValue, element, arrayRules); + + return localRulesResult && arrayRulesResult; + }, ''); + + + /** + * Create JQueryValidation check to validate Remote Laravel rules. + */ + $.validator.addMethod("laravelValidationRemote", function (value, element, params) { + + if (! isImplicit(params) && this.optional( element )) { + return "dependency-mismatch"; + } + + var previous = this.previousValue( element ), + validator, data; + + if (! this.settings.messages[ element.name ]) { + this.settings.messages[ element.name ] = {}; + } + previous.originalMessage = this.settings.messages[ element.name ].laravelValidationRemote; + this.settings.messages[ element.name ].laravelValidationRemote = previous.message; + + if (laravelValidation.helpers.arrayEquals(previous.old, value) || previous.old === value) { + return previous.valid; + } + + previous.old = value; + validator = this; + this.startRequest( element ); + + data = $(validator.currentForm).serializeArray(); + data.push({'name': '_jsvalidation', 'value': element.name}); + data.push({'name': '_jsvalidation_validate_all', 'value': isValidateAll(params)}); + + $.ajax( ajaxOpts(validator, element, params, data) ) + .always(function( response, textStatus ) { + var errors, message, submitted, valid; + + if (textStatus === 'error') { + valid = false; + response = laravelValidation.helpers.parseErrorResponse(response); + } else if (textStatus === 'success') { + valid = response === true || response === "true"; + } else { + return; + } + + validator.settings.messages[ element.name ].laravelValidationRemote = previous.originalMessage; + + if ( valid ) { + submitted = validator.formSubmitted; + validator.prepareElement( element ); + validator.formSubmitted = submitted; + validator.successList.push( element ); + delete validator.invalid[ element.name ]; + validator.showErrors(); + } else { + errors = {}; + message = response || validator.defaultMessage( element, "remote" ); + errors[ element.name ] + = previous.message + = typeof message === "function" ? message( value ) : message[0]; + validator.invalid[ element.name ] = true; + validator.showErrors( errors ); + } + validator.showErrors(validator.errorMap); + previous.valid = valid; + validator.stopRequest( element, valid ); + } + ); + return "pending"; + }, ''); + + /** + * Create JQueryValidation check to form requests. + */ + $.validator.addMethod("laravelValidationFormRequest", function (value, element, params) { + + var validator = this, + previous = validator.previousValue(element); + + var data = $(validator.currentForm).serializeArray(); + data.push({name: '__proengsoft_form_request', value: 1}); // must match FormRequest.JS_VALIDATION_FIELD + + // Skip AJAX if the value remains the same as a prior request. + if (JSON.stringify(previous.old) === JSON.stringify(data)) { + if (! previous.valid) { + validator.showErrors(previous.errors || {}); + } + + return previous.valid; + } + + previous.old = data; + this.startRequest( element ); + + $.ajax(ajaxOpts(validator, element, params, data)) + .always(function( response, textStatus ) { + var errors = {}, + valid = textStatus === 'success' && (response === true || response === 'true'); + + if (valid) { + validator.resetInternals(); + validator.toHide = validator.errorsFor( element ); + } else { + $.each( response, function( fieldName, errorMessages ) { + var errorElement = laravelValidation.helpers.findByName(validator, fieldName)[0]; + if (errorElement) { + errors[errorElement.name] = laravelValidation.helpers.encode(errorMessages[0] || ''); + } + }); + + // Failed to find the error fields so mark the form as valid otherwise user + // will be left in limbo with no visible error messages. + if ($.isEmptyObject(errors)) { + valid = true; + } + } + + previous.valid = valid; + previous.errors = errors; + validator.showErrors(errors); + validator.stopRequest(element, valid); + }); + + return 'pending'; + }, ''); + } +}; + +$(function() { + laravelValidation.init(); +}); + +/******/ (function() { // webpackBootstrap +/******/ "use strict"; +/******/ var __webpack_modules__ = ({ + +/***/ "./node_modules/locutus/php/array/array_diff.js": +/*!******************************************************!*\ + !*** ./node_modules/locutus/php/array/array_diff.js ***! + \******************************************************/ +/***/ (function(module) { + + + +module.exports = function array_diff(arr1) { + // discuss at: https://locutus.io/php/array_diff/ + // original by: Kevin van Zonneveld (https://kvz.io) + // improved by: Sanjoy Roy + // revised by: Brett Zamir (https://brett-zamir.me) + // example 1: array_diff(['Kevin', 'van', 'Zonneveld'], ['van', 'Zonneveld']) + // returns 1: {0:'Kevin'} + + var retArr = {}; + var argl = arguments.length; + var k1 = ''; + var i = 1; + var k = ''; + var arr = {}; + + arr1keys: for (k1 in arr1) { + for (i = 1; i < argl; i++) { + arr = arguments[i]; + for (k in arr) { + if (arr[k] === arr1[k1]) { + // If it reaches here, it was found in at least one array, so try next value + continue arr1keys; // eslint-disable-line no-labels + } + } + retArr[k1] = arr1[k1]; + } + } + + return retArr; +}; +//# sourceMappingURL=array_diff.js.map + +/***/ }), + +/***/ "./node_modules/locutus/php/datetime/strtotime.js": +/*!********************************************************!*\ + !*** ./node_modules/locutus/php/datetime/strtotime.js ***! + \********************************************************/ +/***/ (function(module) { + + + +var reSpace = '[ \\t]+'; +var reSpaceOpt = '[ \\t]*'; +var reMeridian = '(?:([ap])\\.?m\\.?([\\t ]|$))'; +var reHour24 = '(2[0-4]|[01]?[0-9])'; +var reHour24lz = '([01][0-9]|2[0-4])'; +var reHour12 = '(0?[1-9]|1[0-2])'; +var reMinute = '([0-5]?[0-9])'; +var reMinutelz = '([0-5][0-9])'; +var reSecond = '(60|[0-5]?[0-9])'; +var reSecondlz = '(60|[0-5][0-9])'; +var reFrac = '(?:\\.([0-9]+))'; + +var reDayfull = 'sunday|monday|tuesday|wednesday|thursday|friday|saturday'; +var reDayabbr = 'sun|mon|tue|wed|thu|fri|sat'; +var reDaytext = reDayfull + '|' + reDayabbr + '|weekdays?'; + +var reReltextnumber = 'first|second|third|fourth|fifth|sixth|seventh|eighth?|ninth|tenth|eleventh|twelfth'; +var reReltexttext = 'next|last|previous|this'; +var reReltextunit = '(?:second|sec|minute|min|hour|day|fortnight|forthnight|month|year)s?|weeks|' + reDaytext; + +var reYear = '([0-9]{1,4})'; +var reYear2 = '([0-9]{2})'; +var reYear4 = '([0-9]{4})'; +var reYear4withSign = '([+-]?[0-9]{4})'; +var reMonth = '(1[0-2]|0?[0-9])'; +var reMonthlz = '(0[0-9]|1[0-2])'; +var reDay = '(?:(3[01]|[0-2]?[0-9])(?:st|nd|rd|th)?)'; +var reDaylz = '(0[0-9]|[1-2][0-9]|3[01])'; + +var reMonthFull = 'january|february|march|april|may|june|july|august|september|october|november|december'; +var reMonthAbbr = 'jan|feb|mar|apr|may|jun|jul|aug|sept?|oct|nov|dec'; +var reMonthroman = 'i[vx]|vi{0,3}|xi{0,2}|i{1,3}'; +var reMonthText = '(' + reMonthFull + '|' + reMonthAbbr + '|' + reMonthroman + ')'; + +var reTzCorrection = '((?:GMT)?([+-])' + reHour24 + ':?' + reMinute + '?)'; +var reTzAbbr = '\\(?([a-zA-Z]{1,6})\\)?'; +var reDayOfYear = '(00[1-9]|0[1-9][0-9]|[12][0-9][0-9]|3[0-5][0-9]|36[0-6])'; +var reWeekOfYear = '(0[1-9]|[1-4][0-9]|5[0-3])'; + +var reDateNoYear = reMonthText + '[ .\\t-]*' + reDay + '[,.stndrh\\t ]*'; + +function processMeridian(hour, meridian) { + meridian = meridian && meridian.toLowerCase(); + + switch (meridian) { + case 'a': + hour += hour === 12 ? -12 : 0; + break; + case 'p': + hour += hour !== 12 ? 12 : 0; + break; + } + + return hour; +} + +function processYear(yearStr) { + var year = +yearStr; + + if (yearStr.length < 4 && year < 100) { + year += year < 70 ? 2000 : 1900; + } + + return year; +} + +function lookupMonth(monthStr) { + return { + jan: 0, + january: 0, + i: 0, + feb: 1, + february: 1, + ii: 1, + mar: 2, + march: 2, + iii: 2, + apr: 3, + april: 3, + iv: 3, + may: 4, + v: 4, + jun: 5, + june: 5, + vi: 5, + jul: 6, + july: 6, + vii: 6, + aug: 7, + august: 7, + viii: 7, + sep: 8, + sept: 8, + september: 8, + ix: 8, + oct: 9, + october: 9, + x: 9, + nov: 10, + november: 10, + xi: 10, + dec: 11, + december: 11, + xii: 11 + }[monthStr.toLowerCase()]; +} + +function lookupWeekday(dayStr) { + var desiredSundayNumber = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; + + var dayNumbers = { + mon: 1, + monday: 1, + tue: 2, + tuesday: 2, + wed: 3, + wednesday: 3, + thu: 4, + thursday: 4, + fri: 5, + friday: 5, + sat: 6, + saturday: 6, + sun: 0, + sunday: 0 + }; + + return dayNumbers[dayStr.toLowerCase()] || desiredSundayNumber; +} + +function lookupRelative(relText) { + var relativeNumbers = { + last: -1, + previous: -1, + this: 0, + first: 1, + next: 1, + second: 2, + third: 3, + fourth: 4, + fifth: 5, + sixth: 6, + seventh: 7, + eight: 8, + eighth: 8, + ninth: 9, + tenth: 10, + eleventh: 11, + twelfth: 12 + }; + + var relativeBehavior = { + this: 1 + }; + + var relTextLower = relText.toLowerCase(); + + return { + amount: relativeNumbers[relTextLower], + behavior: relativeBehavior[relTextLower] || 0 + }; +} + +function processTzCorrection(tzOffset, oldValue) { + var reTzCorrectionLoose = /(?:GMT)?([+-])(\d+)(:?)(\d{0,2})/i; + tzOffset = tzOffset && tzOffset.match(reTzCorrectionLoose); + + if (!tzOffset) { + return oldValue; + } + + var sign = tzOffset[1] === '-' ? -1 : 1; + var hours = +tzOffset[2]; + var minutes = +tzOffset[4]; + + if (!tzOffset[4] && !tzOffset[3]) { + minutes = Math.floor(hours % 100); + hours = Math.floor(hours / 100); + } + + // timezone offset in seconds + return sign * (hours * 60 + minutes) * 60; +} + +// tz abbrevation : tz offset in seconds +var tzAbbrOffsets = { + acdt: 37800, + acst: 34200, + addt: -7200, + adt: -10800, + aedt: 39600, + aest: 36000, + ahdt: -32400, + ahst: -36000, + akdt: -28800, + akst: -32400, + amt: -13840, + apt: -10800, + ast: -14400, + awdt: 32400, + awst: 28800, + awt: -10800, + bdst: 7200, + bdt: -36000, + bmt: -14309, + bst: 3600, + cast: 34200, + cat: 7200, + cddt: -14400, + cdt: -18000, + cemt: 10800, + cest: 7200, + cet: 3600, + cmt: -15408, + cpt: -18000, + cst: -21600, + cwt: -18000, + chst: 36000, + dmt: -1521, + eat: 10800, + eddt: -10800, + edt: -14400, + eest: 10800, + eet: 7200, + emt: -26248, + ept: -14400, + est: -18000, + ewt: -14400, + ffmt: -14660, + fmt: -4056, + gdt: 39600, + gmt: 0, + gst: 36000, + hdt: -34200, + hkst: 32400, + hkt: 28800, + hmt: -19776, + hpt: -34200, + hst: -36000, + hwt: -34200, + iddt: 14400, + idt: 10800, + imt: 25025, + ist: 7200, + jdt: 36000, + jmt: 8440, + jst: 32400, + kdt: 36000, + kmt: 5736, + kst: 30600, + lst: 9394, + mddt: -18000, + mdst: 16279, + mdt: -21600, + mest: 7200, + met: 3600, + mmt: 9017, + mpt: -21600, + msd: 14400, + msk: 10800, + mst: -25200, + mwt: -21600, + nddt: -5400, + ndt: -9052, + npt: -9000, + nst: -12600, + nwt: -9000, + nzdt: 46800, + nzmt: 41400, + nzst: 43200, + pddt: -21600, + pdt: -25200, + pkst: 21600, + pkt: 18000, + plmt: 25590, + pmt: -13236, + ppmt: -17340, + ppt: -25200, + pst: -28800, + pwt: -25200, + qmt: -18840, + rmt: 5794, + sast: 7200, + sdmt: -16800, + sjmt: -20173, + smt: -13884, + sst: -39600, + tbmt: 10751, + tmt: 12344, + uct: 0, + utc: 0, + wast: 7200, + wat: 3600, + wemt: 7200, + west: 3600, + wet: 0, + wib: 25200, + wita: 28800, + wit: 32400, + wmt: 5040, + yddt: -25200, + ydt: -28800, + ypt: -28800, + yst: -32400, + ywt: -28800, + a: 3600, + b: 7200, + c: 10800, + d: 14400, + e: 18000, + f: 21600, + g: 25200, + h: 28800, + i: 32400, + k: 36000, + l: 39600, + m: 43200, + n: -3600, + o: -7200, + p: -10800, + q: -14400, + r: -18000, + s: -21600, + t: -25200, + u: -28800, + v: -32400, + w: -36000, + x: -39600, + y: -43200, + z: 0 +}; + +var formats = { + yesterday: { + regex: /^yesterday/i, + name: 'yesterday', + callback: function callback() { + this.rd -= 1; + return this.resetTime(); + } + }, + + now: { + regex: /^now/i, + name: 'now' + // do nothing + }, + + noon: { + regex: /^noon/i, + name: 'noon', + callback: function callback() { + return this.resetTime() && this.time(12, 0, 0, 0); + } + }, + + midnightOrToday: { + regex: /^(midnight|today)/i, + name: 'midnight | today', + callback: function callback() { + return this.resetTime(); + } + }, + + tomorrow: { + regex: /^tomorrow/i, + name: 'tomorrow', + callback: function callback() { + this.rd += 1; + return this.resetTime(); + } + }, + + timestamp: { + regex: /^@(-?\d+)/i, + name: 'timestamp', + callback: function callback(match, timestamp) { + this.rs += +timestamp; + this.y = 1970; + this.m = 0; + this.d = 1; + this.dates = 0; + + return this.resetTime() && this.zone(0); + } + }, + + firstOrLastDay: { + regex: /^(first|last) day of/i, + name: 'firstdayof | lastdayof', + callback: function callback(match, day) { + if (day.toLowerCase() === 'first') { + this.firstOrLastDayOfMonth = 1; + } else { + this.firstOrLastDayOfMonth = -1; + } + } + }, + + backOrFrontOf: { + regex: RegExp('^(back|front) of ' + reHour24 + reSpaceOpt + reMeridian + '?', 'i'), + name: 'backof | frontof', + callback: function callback(match, side, hours, meridian) { + var back = side.toLowerCase() === 'back'; + var hour = +hours; + var minute = 15; + + if (!back) { + hour -= 1; + minute = 45; + } + + hour = processMeridian(hour, meridian); + + return this.resetTime() && this.time(hour, minute, 0, 0); + } + }, + + weekdayOf: { + regex: RegExp('^(' + reReltextnumber + '|' + reReltexttext + ')' + reSpace + '(' + reDayfull + '|' + reDayabbr + ')' + reSpace + 'of', 'i'), + name: 'weekdayof' + // todo + }, + + mssqltime: { + regex: RegExp('^' + reHour12 + ':' + reMinutelz + ':' + reSecondlz + '[:.]([0-9]+)' + reMeridian, 'i'), + name: 'mssqltime', + callback: function callback(match, hour, minute, second, frac, meridian) { + return this.time(processMeridian(+hour, meridian), +minute, +second, +frac.substr(0, 3)); + } + }, + + oracledate: { + regex: /^(\d{2})-([A-Z]{3})-(\d{2})$/i, + name: 'd-M-y', + callback: function callback(match, day, monthText, year) { + var month = { + JAN: 0, + FEB: 1, + MAR: 2, + APR: 3, + MAY: 4, + JUN: 5, + JUL: 6, + AUG: 7, + SEP: 8, + OCT: 9, + NOV: 10, + DEC: 11 + }[monthText.toUpperCase()]; + return this.ymd(2000 + parseInt(year, 10), month, parseInt(day, 10)); + } + }, + + timeLong12: { + regex: RegExp('^' + reHour12 + '[:.]' + reMinute + '[:.]' + reSecondlz + reSpaceOpt + reMeridian, 'i'), + name: 'timelong12', + callback: function callback(match, hour, minute, second, meridian) { + return this.time(processMeridian(+hour, meridian), +minute, +second, 0); + } + }, + + timeShort12: { + regex: RegExp('^' + reHour12 + '[:.]' + reMinutelz + reSpaceOpt + reMeridian, 'i'), + name: 'timeshort12', + callback: function callback(match, hour, minute, meridian) { + return this.time(processMeridian(+hour, meridian), +minute, 0, 0); + } + }, + + timeTiny12: { + regex: RegExp('^' + reHour12 + reSpaceOpt + reMeridian, 'i'), + name: 'timetiny12', + callback: function callback(match, hour, meridian) { + return this.time(processMeridian(+hour, meridian), 0, 0, 0); + } + }, + + soap: { + regex: RegExp('^' + reYear4 + '-' + reMonthlz + '-' + reDaylz + 'T' + reHour24lz + ':' + reMinutelz + ':' + reSecondlz + reFrac + reTzCorrection + '?', 'i'), + name: 'soap', + callback: function callback(match, year, month, day, hour, minute, second, frac, tzCorrection) { + return this.ymd(+year, month - 1, +day) && this.time(+hour, +minute, +second, +frac.substr(0, 3)) && this.zone(processTzCorrection(tzCorrection)); + } + }, + + wddx: { + regex: RegExp('^' + reYear4 + '-' + reMonth + '-' + reDay + 'T' + reHour24 + ':' + reMinute + ':' + reSecond), + name: 'wddx', + callback: function callback(match, year, month, day, hour, minute, second) { + return this.ymd(+year, month - 1, +day) && this.time(+hour, +minute, +second, 0); + } + }, + + exif: { + regex: RegExp('^' + reYear4 + ':' + reMonthlz + ':' + reDaylz + ' ' + reHour24lz + ':' + reMinutelz + ':' + reSecondlz, 'i'), + name: 'exif', + callback: function callback(match, year, month, day, hour, minute, second) { + return this.ymd(+year, month - 1, +day) && this.time(+hour, +minute, +second, 0); + } + }, + + xmlRpc: { + regex: RegExp('^' + reYear4 + reMonthlz + reDaylz + 'T' + reHour24 + ':' + reMinutelz + ':' + reSecondlz), + name: 'xmlrpc', + callback: function callback(match, year, month, day, hour, minute, second) { + return this.ymd(+year, month - 1, +day) && this.time(+hour, +minute, +second, 0); + } + }, + + xmlRpcNoColon: { + regex: RegExp('^' + reYear4 + reMonthlz + reDaylz + '[Tt]' + reHour24 + reMinutelz + reSecondlz), + name: 'xmlrpcnocolon', + callback: function callback(match, year, month, day, hour, minute, second) { + return this.ymd(+year, month - 1, +day) && this.time(+hour, +minute, +second, 0); + } + }, + + clf: { + regex: RegExp('^' + reDay + '/(' + reMonthAbbr + ')/' + reYear4 + ':' + reHour24lz + ':' + reMinutelz + ':' + reSecondlz + reSpace + reTzCorrection, 'i'), + name: 'clf', + callback: function callback(match, day, month, year, hour, minute, second, tzCorrection) { + return this.ymd(+year, lookupMonth(month), +day) && this.time(+hour, +minute, +second, 0) && this.zone(processTzCorrection(tzCorrection)); + } + }, + + iso8601long: { + regex: RegExp('^t?' + reHour24 + '[:.]' + reMinute + '[:.]' + reSecond + reFrac, 'i'), + name: 'iso8601long', + callback: function callback(match, hour, minute, second, frac) { + return this.time(+hour, +minute, +second, +frac.substr(0, 3)); + } + }, + + dateTextual: { + regex: RegExp('^' + reMonthText + '[ .\\t-]*' + reDay + '[,.stndrh\\t ]+' + reYear, 'i'), + name: 'datetextual', + callback: function callback(match, month, day, year) { + return this.ymd(processYear(year), lookupMonth(month), +day); + } + }, + + pointedDate4: { + regex: RegExp('^' + reDay + '[.\\t-]' + reMonth + '[.-]' + reYear4), + name: 'pointeddate4', + callback: function callback(match, day, month, year) { + return this.ymd(+year, month - 1, +day); + } + }, + + pointedDate2: { + regex: RegExp('^' + reDay + '[.\\t]' + reMonth + '\\.' + reYear2), + name: 'pointeddate2', + callback: function callback(match, day, month, year) { + return this.ymd(processYear(year), month - 1, +day); + } + }, + + timeLong24: { + regex: RegExp('^t?' + reHour24 + '[:.]' + reMinute + '[:.]' + reSecond), + name: 'timelong24', + callback: function callback(match, hour, minute, second) { + return this.time(+hour, +minute, +second, 0); + } + }, + + dateNoColon: { + regex: RegExp('^' + reYear4 + reMonthlz + reDaylz), + name: 'datenocolon', + callback: function callback(match, year, month, day) { + return this.ymd(+year, month - 1, +day); + } + }, + + pgydotd: { + regex: RegExp('^' + reYear4 + '\\.?' + reDayOfYear), + name: 'pgydotd', + callback: function callback(match, year, day) { + return this.ymd(+year, 0, +day); + } + }, + + timeShort24: { + regex: RegExp('^t?' + reHour24 + '[:.]' + reMinute, 'i'), + name: 'timeshort24', + callback: function callback(match, hour, minute) { + return this.time(+hour, +minute, 0, 0); + } + }, + + iso8601noColon: { + regex: RegExp('^t?' + reHour24lz + reMinutelz + reSecondlz, 'i'), + name: 'iso8601nocolon', + callback: function callback(match, hour, minute, second) { + return this.time(+hour, +minute, +second, 0); + } + }, + + iso8601dateSlash: { + // eventhough the trailing slash is optional in PHP + // here it's mandatory and inputs without the slash + // are handled by dateslash + regex: RegExp('^' + reYear4 + '/' + reMonthlz + '/' + reDaylz + '/'), + name: 'iso8601dateslash', + callback: function callback(match, year, month, day) { + return this.ymd(+year, month - 1, +day); + } + }, + + dateSlash: { + regex: RegExp('^' + reYear4 + '/' + reMonth + '/' + reDay), + name: 'dateslash', + callback: function callback(match, year, month, day) { + return this.ymd(+year, month - 1, +day); + } + }, + + american: { + regex: RegExp('^' + reMonth + '/' + reDay + '/' + reYear), + name: 'american', + callback: function callback(match, month, day, year) { + return this.ymd(processYear(year), month - 1, +day); + } + }, + + americanShort: { + regex: RegExp('^' + reMonth + '/' + reDay), + name: 'americanshort', + callback: function callback(match, month, day) { + return this.ymd(this.y, month - 1, +day); + } + }, + + gnuDateShortOrIso8601date2: { + // iso8601date2 is complete subset of gnudateshort + regex: RegExp('^' + reYear + '-' + reMonth + '-' + reDay), + name: 'gnudateshort | iso8601date2', + callback: function callback(match, year, month, day) { + return this.ymd(processYear(year), month - 1, +day); + } + }, + + iso8601date4: { + regex: RegExp('^' + reYear4withSign + '-' + reMonthlz + '-' + reDaylz), + name: 'iso8601date4', + callback: function callback(match, year, month, day) { + return this.ymd(+year, month - 1, +day); + } + }, + + gnuNoColon: { + regex: RegExp('^t?' + reHour24lz + reMinutelz, 'i'), + name: 'gnunocolon', + callback: function callback(match, hour, minute) { + // this rule is a special case + // if time was already set once by any preceding rule, it sets the captured value as year + switch (this.times) { + case 0: + return this.time(+hour, +minute, 0, this.f); + case 1: + this.y = hour * 100 + +minute; + this.times++; + + return true; + default: + return false; + } + } + }, + + gnuDateShorter: { + regex: RegExp('^' + reYear4 + '-' + reMonth), + name: 'gnudateshorter', + callback: function callback(match, year, month) { + return this.ymd(+year, month - 1, 1); + } + }, + + pgTextReverse: { + // note: allowed years are from 32-9999 + // years below 32 should be treated as days in datefull + regex: RegExp('^' + '(\\d{3,4}|[4-9]\\d|3[2-9])-(' + reMonthAbbr + ')-' + reDaylz, 'i'), + name: 'pgtextreverse', + callback: function callback(match, year, month, day) { + return this.ymd(processYear(year), lookupMonth(month), +day); + } + }, + + dateFull: { + regex: RegExp('^' + reDay + '[ \\t.-]*' + reMonthText + '[ \\t.-]*' + reYear, 'i'), + name: 'datefull', + callback: function callback(match, day, month, year) { + return this.ymd(processYear(year), lookupMonth(month), +day); + } + }, + + dateNoDay: { + regex: RegExp('^' + reMonthText + '[ .\\t-]*' + reYear4, 'i'), + name: 'datenoday', + callback: function callback(match, month, year) { + return this.ymd(+year, lookupMonth(month), 1); + } + }, + + dateNoDayRev: { + regex: RegExp('^' + reYear4 + '[ .\\t-]*' + reMonthText, 'i'), + name: 'datenodayrev', + callback: function callback(match, year, month) { + return this.ymd(+year, lookupMonth(month), 1); + } + }, + + pgTextShort: { + regex: RegExp('^(' + reMonthAbbr + ')-' + reDaylz + '-' + reYear, 'i'), + name: 'pgtextshort', + callback: function callback(match, month, day, year) { + return this.ymd(processYear(year), lookupMonth(month), +day); + } + }, + + dateNoYear: { + regex: RegExp('^' + reDateNoYear, 'i'), + name: 'datenoyear', + callback: function callback(match, month, day) { + return this.ymd(this.y, lookupMonth(month), +day); + } + }, + + dateNoYearRev: { + regex: RegExp('^' + reDay + '[ .\\t-]*' + reMonthText, 'i'), + name: 'datenoyearrev', + callback: function callback(match, day, month) { + return this.ymd(this.y, lookupMonth(month), +day); + } + }, + + isoWeekDay: { + regex: RegExp('^' + reYear4 + '-?W' + reWeekOfYear + '(?:-?([0-7]))?'), + name: 'isoweekday | isoweek', + callback: function callback(match, year, week, day) { + day = day ? +day : 1; + + if (!this.ymd(+year, 0, 1)) { + return false; + } + + // get day of week for Jan 1st + var dayOfWeek = new Date(this.y, this.m, this.d).getDay(); + + // and use the day to figure out the offset for day 1 of week 1 + dayOfWeek = 0 - (dayOfWeek > 4 ? dayOfWeek - 7 : dayOfWeek); + + this.rd += dayOfWeek + (week - 1) * 7 + day; + } + }, + + relativeText: { + regex: RegExp('^(' + reReltextnumber + '|' + reReltexttext + ')' + reSpace + '(' + reReltextunit + ')', 'i'), + name: 'relativetext', + callback: function callback(match, relValue, relUnit) { + // todo: implement handling of 'this time-unit' + // eslint-disable-next-line no-unused-vars + var _lookupRelative = lookupRelative(relValue), + amount = _lookupRelative.amount, + behavior = _lookupRelative.behavior; + + switch (relUnit.toLowerCase()) { + case 'sec': + case 'secs': + case 'second': + case 'seconds': + this.rs += amount; + break; + case 'min': + case 'mins': + case 'minute': + case 'minutes': + this.ri += amount; + break; + case 'hour': + case 'hours': + this.rh += amount; + break; + case 'day': + case 'days': + this.rd += amount; + break; + case 'fortnight': + case 'fortnights': + case 'forthnight': + case 'forthnights': + this.rd += amount * 14; + break; + case 'week': + case 'weeks': + this.rd += amount * 7; + break; + case 'month': + case 'months': + this.rm += amount; + break; + case 'year': + case 'years': + this.ry += amount; + break; + case 'mon': + case 'monday': + case 'tue': + case 'tuesday': + case 'wed': + case 'wednesday': + case 'thu': + case 'thursday': + case 'fri': + case 'friday': + case 'sat': + case 'saturday': + case 'sun': + case 'sunday': + this.resetTime(); + this.weekday = lookupWeekday(relUnit, 7); + this.weekdayBehavior = 1; + this.rd += (amount > 0 ? amount - 1 : amount) * 7; + break; + case 'weekday': + case 'weekdays': + // todo + break; + } + } + }, + + relative: { + regex: RegExp('^([+-]*)[ \\t]*(\\d+)' + reSpaceOpt + '(' + reReltextunit + '|week)', 'i'), + name: 'relative', + callback: function callback(match, signs, relValue, relUnit) { + var minuses = signs.replace(/[^-]/g, '').length; + + var amount = +relValue * Math.pow(-1, minuses); + + switch (relUnit.toLowerCase()) { + case 'sec': + case 'secs': + case 'second': + case 'seconds': + this.rs += amount; + break; + case 'min': + case 'mins': + case 'minute': + case 'minutes': + this.ri += amount; + break; + case 'hour': + case 'hours': + this.rh += amount; + break; + case 'day': + case 'days': + this.rd += amount; + break; + case 'fortnight': + case 'fortnights': + case 'forthnight': + case 'forthnights': + this.rd += amount * 14; + break; + case 'week': + case 'weeks': + this.rd += amount * 7; + break; + case 'month': + case 'months': + this.rm += amount; + break; + case 'year': + case 'years': + this.ry += amount; + break; + case 'mon': + case 'monday': + case 'tue': + case 'tuesday': + case 'wed': + case 'wednesday': + case 'thu': + case 'thursday': + case 'fri': + case 'friday': + case 'sat': + case 'saturday': + case 'sun': + case 'sunday': + this.resetTime(); + this.weekday = lookupWeekday(relUnit, 7); + this.weekdayBehavior = 1; + this.rd += (amount > 0 ? amount - 1 : amount) * 7; + break; + case 'weekday': + case 'weekdays': + // todo + break; + } + } + }, + + dayText: { + regex: RegExp('^(' + reDaytext + ')', 'i'), + name: 'daytext', + callback: function callback(match, dayText) { + this.resetTime(); + this.weekday = lookupWeekday(dayText, 0); + + if (this.weekdayBehavior !== 2) { + this.weekdayBehavior = 1; + } + } + }, + + relativeTextWeek: { + regex: RegExp('^(' + reReltexttext + ')' + reSpace + 'week', 'i'), + name: 'relativetextweek', + callback: function callback(match, relText) { + this.weekdayBehavior = 2; + + switch (relText.toLowerCase()) { + case 'this': + this.rd += 0; + break; + case 'next': + this.rd += 7; + break; + case 'last': + case 'previous': + this.rd -= 7; + break; + } + + if (isNaN(this.weekday)) { + this.weekday = 1; + } + } + }, + + monthFullOrMonthAbbr: { + regex: RegExp('^(' + reMonthFull + '|' + reMonthAbbr + ')', 'i'), + name: 'monthfull | monthabbr', + callback: function callback(match, month) { + return this.ymd(this.y, lookupMonth(month), this.d); + } + }, + + tzCorrection: { + regex: RegExp('^' + reTzCorrection, 'i'), + name: 'tzcorrection', + callback: function callback(tzCorrection) { + return this.zone(processTzCorrection(tzCorrection)); + } + }, + + tzAbbr: { + regex: RegExp('^' + reTzAbbr), + name: 'tzabbr', + callback: function callback(match, abbr) { + var offset = tzAbbrOffsets[abbr.toLowerCase()]; + + if (isNaN(offset)) { + return false; + } + + return this.zone(offset); + } + }, + + ago: { + regex: /^ago/i, + name: 'ago', + callback: function callback() { + this.ry = -this.ry; + this.rm = -this.rm; + this.rd = -this.rd; + this.rh = -this.rh; + this.ri = -this.ri; + this.rs = -this.rs; + this.rf = -this.rf; + } + }, + + year4: { + regex: RegExp('^' + reYear4), + name: 'year4', + callback: function callback(match, year) { + this.y = +year; + return true; + } + }, + + whitespace: { + regex: /^[ .,\t]+/, + name: 'whitespace' + // do nothing + }, + + dateShortWithTimeLong: { + regex: RegExp('^' + reDateNoYear + 't?' + reHour24 + '[:.]' + reMinute + '[:.]' + reSecond, 'i'), + name: 'dateshortwithtimelong', + callback: function callback(match, month, day, hour, minute, second) { + return this.ymd(this.y, lookupMonth(month), +day) && this.time(+hour, +minute, +second, 0); + } + }, + + dateShortWithTimeLong12: { + regex: RegExp('^' + reDateNoYear + reHour12 + '[:.]' + reMinute + '[:.]' + reSecondlz + reSpaceOpt + reMeridian, 'i'), + name: 'dateshortwithtimelong12', + callback: function callback(match, month, day, hour, minute, second, meridian) { + return this.ymd(this.y, lookupMonth(month), +day) && this.time(processMeridian(+hour, meridian), +minute, +second, 0); + } + }, + + dateShortWithTimeShort: { + regex: RegExp('^' + reDateNoYear + 't?' + reHour24 + '[:.]' + reMinute, 'i'), + name: 'dateshortwithtimeshort', + callback: function callback(match, month, day, hour, minute) { + return this.ymd(this.y, lookupMonth(month), +day) && this.time(+hour, +minute, 0, 0); + } + }, + + dateShortWithTimeShort12: { + regex: RegExp('^' + reDateNoYear + reHour12 + '[:.]' + reMinutelz + reSpaceOpt + reMeridian, 'i'), + name: 'dateshortwithtimeshort12', + callback: function callback(match, month, day, hour, minute, meridian) { + return this.ymd(this.y, lookupMonth(month), +day) && this.time(processMeridian(+hour, meridian), +minute, 0, 0); + } + } +}; + +var resultProto = { + // date + y: NaN, + m: NaN, + d: NaN, + // time + h: NaN, + i: NaN, + s: NaN, + f: NaN, + + // relative shifts + ry: 0, + rm: 0, + rd: 0, + rh: 0, + ri: 0, + rs: 0, + rf: 0, + + // weekday related shifts + weekday: NaN, + weekdayBehavior: 0, + + // first or last day of month + // 0 none, 1 first, -1 last + firstOrLastDayOfMonth: 0, + + // timezone correction in minutes + z: NaN, + + // counters + dates: 0, + times: 0, + zones: 0, + + // helper functions + ymd: function ymd(y, m, d) { + if (this.dates > 0) { + return false; + } + + this.dates++; + this.y = y; + this.m = m; + this.d = d; + return true; + }, + time: function time(h, i, s, f) { + if (this.times > 0) { + return false; + } + + this.times++; + this.h = h; + this.i = i; + this.s = s; + this.f = f; + + return true; + }, + resetTime: function resetTime() { + this.h = 0; + this.i = 0; + this.s = 0; + this.f = 0; + this.times = 0; + + return true; + }, + zone: function zone(minutes) { + if (this.zones <= 1) { + this.zones++; + this.z = minutes; + return true; + } + + return false; + }, + toDate: function toDate(relativeTo) { + if (this.dates && !this.times) { + this.h = this.i = this.s = this.f = 0; + } + + // fill holes + if (isNaN(this.y)) { + this.y = relativeTo.getFullYear(); + } + + if (isNaN(this.m)) { + this.m = relativeTo.getMonth(); + } + + if (isNaN(this.d)) { + this.d = relativeTo.getDate(); + } + + if (isNaN(this.h)) { + this.h = relativeTo.getHours(); + } + + if (isNaN(this.i)) { + this.i = relativeTo.getMinutes(); + } + + if (isNaN(this.s)) { + this.s = relativeTo.getSeconds(); + } + + if (isNaN(this.f)) { + this.f = relativeTo.getMilliseconds(); + } + + // adjust special early + switch (this.firstOrLastDayOfMonth) { + case 1: + this.d = 1; + break; + case -1: + this.d = 0; + this.m += 1; + break; + } + + if (!isNaN(this.weekday)) { + var date = new Date(relativeTo.getTime()); + date.setFullYear(this.y, this.m, this.d); + date.setHours(this.h, this.i, this.s, this.f); + + var dow = date.getDay(); + + if (this.weekdayBehavior === 2) { + // To make "this week" work, where the current day of week is a "sunday" + if (dow === 0 && this.weekday !== 0) { + this.weekday = -6; + } + + // To make "sunday this week" work, where the current day of week is not a "sunday" + if (this.weekday === 0 && dow !== 0) { + this.weekday = 7; + } + + this.d -= dow; + this.d += this.weekday; + } else { + var diff = this.weekday - dow; + + // some PHP magic + if (this.rd < 0 && diff < 0 || this.rd >= 0 && diff <= -this.weekdayBehavior) { + diff += 7; + } + + if (this.weekday >= 0) { + this.d += diff; + } else { + this.d -= 7 - (Math.abs(this.weekday) - dow); + } + + this.weekday = NaN; + } + } + + // adjust relative + this.y += this.ry; + this.m += this.rm; + this.d += this.rd; + + this.h += this.rh; + this.i += this.ri; + this.s += this.rs; + this.f += this.rf; + + this.ry = this.rm = this.rd = 0; + this.rh = this.ri = this.rs = this.rf = 0; + + var result = new Date(relativeTo.getTime()); + // since Date constructor treats years <= 99 as 1900+ + // it can't be used, thus this weird way + result.setFullYear(this.y, this.m, this.d); + result.setHours(this.h, this.i, this.s, this.f); + + // note: this is done twice in PHP + // early when processing special relatives + // and late + // todo: check if the logic can be reduced + // to just one time action + switch (this.firstOrLastDayOfMonth) { + case 1: + result.setDate(1); + break; + case -1: + result.setMonth(result.getMonth() + 1, 0); + break; + } + + // adjust timezone + if (!isNaN(this.z) && result.getTimezoneOffset() !== this.z) { + result.setUTCFullYear(result.getFullYear(), result.getMonth(), result.getDate()); + + result.setUTCHours(result.getHours(), result.getMinutes(), result.getSeconds() - this.z, result.getMilliseconds()); + } + + return result; + } +}; + +module.exports = function strtotime(str, now) { + // discuss at: https://locutus.io/php/strtotime/ + // original by: Caio Ariede (https://caioariede.com) + // improved by: Kevin van Zonneveld (https://kvz.io) + // improved by: Caio Ariede (https://caioariede.com) + // improved by: A. Matías Quezada (https://amatiasq.com) + // improved by: preuter + // improved by: Brett Zamir (https://brett-zamir.me) + // improved by: Mirko Faber + // input by: David + // bugfixed by: Wagner B. Soares + // bugfixed by: Artur Tchernychev + // bugfixed by: Stephan Bösch-Plepelits (https://github.com/plepe) + // reimplemented by: Rafał Kukawski + // note 1: Examples all have a fixed timestamp to prevent + // note 1: tests to fail because of variable time(zones) + // example 1: strtotime('+1 day', 1129633200) + // returns 1: 1129719600 + // example 2: strtotime('+1 week 2 days 4 hours 2 seconds', 1129633200) + // returns 2: 1130425202 + // example 3: strtotime('last month', 1129633200) + // returns 3: 1127041200 + // example 4: strtotime('2009-05-04 08:30:00+00') + // returns 4: 1241425800 + // example 5: strtotime('2009-05-04 08:30:00+02:00') + // returns 5: 1241418600 + // example 6: strtotime('2009-05-04 08:30:00 YWT') + // returns 6: 1241454600 + // example 7: strtotime('10-JUL-17') + // returns 7: 1499644800 + + if (now == null) { + now = Math.floor(Date.now() / 1000); + } + + // the rule order is important + // if multiple rules match, the longest match wins + // if multiple rules match the same string, the first match wins + var rules = [formats.yesterday, formats.now, formats.noon, formats.midnightOrToday, formats.tomorrow, formats.timestamp, formats.firstOrLastDay, formats.backOrFrontOf, + // formats.weekdayOf, // not yet implemented + formats.timeTiny12, formats.timeShort12, formats.timeLong12, formats.mssqltime, formats.oracledate, formats.timeShort24, formats.timeLong24, formats.iso8601long, formats.gnuNoColon, formats.iso8601noColon, formats.americanShort, formats.american, formats.iso8601date4, formats.iso8601dateSlash, formats.dateSlash, formats.gnuDateShortOrIso8601date2, formats.gnuDateShorter, formats.dateFull, formats.pointedDate4, formats.pointedDate2, formats.dateNoDay, formats.dateNoDayRev, formats.dateTextual, formats.dateNoYear, formats.dateNoYearRev, formats.dateNoColon, formats.xmlRpc, formats.xmlRpcNoColon, formats.soap, formats.wddx, formats.exif, formats.pgydotd, formats.isoWeekDay, formats.pgTextShort, formats.pgTextReverse, formats.clf, formats.year4, formats.ago, formats.dayText, formats.relativeTextWeek, formats.relativeText, formats.monthFullOrMonthAbbr, formats.tzCorrection, formats.tzAbbr, formats.dateShortWithTimeShort12, formats.dateShortWithTimeLong12, formats.dateShortWithTimeShort, formats.dateShortWithTimeLong, formats.relative, formats.whitespace]; + + var result = Object.create(resultProto); + + while (str.length) { + var longestMatch = null; + var finalRule = null; + + for (var i = 0, l = rules.length; i < l; i++) { + var format = rules[i]; + + var match = str.match(format.regex); + + if (match) { + if (!longestMatch || match[0].length > longestMatch[0].length) { + longestMatch = match; + finalRule = format; + } + } + } + + if (!finalRule || finalRule.callback && finalRule.callback.apply(result, longestMatch) === false) { + return false; + } + + str = str.substr(longestMatch[0].length); + finalRule = null; + longestMatch = null; + } + + return Math.floor(result.toDate(new Date(now * 1000)) / 1000); +}; +//# sourceMappingURL=strtotime.js.map + +/***/ }), + +/***/ "./node_modules/locutus/php/info/ini_get.js": +/*!**************************************************!*\ + !*** ./node_modules/locutus/php/info/ini_get.js ***! + \**************************************************/ +/***/ (function(module, __unused_webpack_exports, __webpack_require__) { + + + +module.exports = function ini_get(varname) { + // discuss at: https://locutus.io/php/ini_get/ + // original by: Brett Zamir (https://brett-zamir.me) + // note 1: The ini values must be set by ini_set or manually within an ini file + // example 1: ini_set('date.timezone', 'Asia/Hong_Kong') + // example 1: ini_get('date.timezone') + // returns 1: 'Asia/Hong_Kong' + + var $global = typeof window !== 'undefined' ? window : __webpack_require__.g; + $global.$locutus = $global.$locutus || {}; + var $locutus = $global.$locutus; + $locutus.php = $locutus.php || {}; + $locutus.php.ini = $locutus.php.ini || {}; + + if ($locutus.php.ini[varname] && $locutus.php.ini[varname].local_value !== undefined) { + if ($locutus.php.ini[varname].local_value === null) { + return ''; + } + return $locutus.php.ini[varname].local_value; + } + + return ''; +}; +//# sourceMappingURL=ini_get.js.map + +/***/ }), + +/***/ "./node_modules/locutus/php/strings/strlen.js": +/*!****************************************************!*\ + !*** ./node_modules/locutus/php/strings/strlen.js ***! + \****************************************************/ +/***/ (function(module, __unused_webpack_exports, __webpack_require__) { + + + +module.exports = function strlen(string) { + // discuss at: https://locutus.io/php/strlen/ + // original by: Kevin van Zonneveld (https://kvz.io) + // improved by: Sakimori + // improved by: Kevin van Zonneveld (https://kvz.io) + // input by: Kirk Strobeck + // bugfixed by: Onno Marsman (https://twitter.com/onnomarsman) + // revised by: Brett Zamir (https://brett-zamir.me) + // note 1: May look like overkill, but in order to be truly faithful to handling all Unicode + // note 1: characters and to this function in PHP which does not count the number of bytes + // note 1: but counts the number of characters, something like this is really necessary. + // example 1: strlen('Kevin van Zonneveld') + // returns 1: 19 + // example 2: ini_set('unicode.semantics', 'on') + // example 2: strlen('A\ud87e\udc04Z') + // returns 2: 3 + + var str = string + ''; + + var iniVal = ( true ? __webpack_require__(/*! ../info/ini_get */ "./node_modules/locutus/php/info/ini_get.js")('unicode.semantics') : 0) || 'off'; + if (iniVal === 'off') { + return str.length; + } + + var i = 0; + var lgth = 0; + + var getWholeChar = function getWholeChar(str, i) { + var code = str.charCodeAt(i); + var next = ''; + var prev = ''; + if (code >= 0xd800 && code <= 0xdbff) { + // High surrogate (could change last hex to 0xDB7F to + // treat high private surrogates as single characters) + if (str.length <= i + 1) { + throw new Error('High surrogate without following low surrogate'); + } + next = str.charCodeAt(i + 1); + if (next < 0xdc00 || next > 0xdfff) { + throw new Error('High surrogate without following low surrogate'); + } + return str.charAt(i) + str.charAt(i + 1); + } else if (code >= 0xdc00 && code <= 0xdfff) { + // Low surrogate + if (i === 0) { + throw new Error('Low surrogate without preceding high surrogate'); + } + prev = str.charCodeAt(i - 1); + if (prev < 0xd800 || prev > 0xdbff) { + // (could change last hex to 0xDB7F to treat high private surrogates + // as single characters) + throw new Error('Low surrogate without preceding high surrogate'); + } + // We can pass over low surrogates now as the second + // component in a pair which we have already processed + return false; + } + return str.charAt(i); + }; + + for (i = 0, lgth = 0; i < str.length; i++) { + if (getWholeChar(str, i) === false) { + continue; + } + // Adapt this line at the top of any loop, passing in the whole string and + // the current iteration and returning a variable to represent the individual character; + // purpose is to treat the first part of a surrogate pair as the whole character and then + // ignore the second part + lgth++; + } + + return lgth; +}; +//# sourceMappingURL=strlen.js.map + +/***/ }), + +/***/ "./node_modules/locutus/php/var/is_numeric.js": +/*!****************************************************!*\ + !*** ./node_modules/locutus/php/var/is_numeric.js ***! + \****************************************************/ +/***/ (function(module) { + + + +module.exports = function is_numeric(mixedVar) { + // discuss at: https://locutus.io/php/is_numeric/ + // original by: Kevin van Zonneveld (https://kvz.io) + // improved by: David + // improved by: taith + // bugfixed by: Tim de Koning + // bugfixed by: WebDevHobo (https://webdevhobo.blogspot.com/) + // bugfixed by: Brett Zamir (https://brett-zamir.me) + // bugfixed by: Denis Chenu (https://shnoulle.net) + // example 1: is_numeric(186.31) + // returns 1: true + // example 2: is_numeric('Kevin van Zonneveld') + // returns 2: false + // example 3: is_numeric(' +186.31e2') + // returns 3: true + // example 4: is_numeric('') + // returns 4: false + // example 5: is_numeric([]) + // returns 5: false + // example 6: is_numeric('1 ') + // returns 6: false + + var whitespace = [' ', '\n', '\r', '\t', '\f', '\x0b', '\xa0', '\u2000', '\u2001', '\u2002', '\u2003', '\u2004', '\u2005', '\u2006', '\u2007', '\u2008', '\u2009', '\u200A', '\u200B', '\u2028', '\u2029', '\u3000'].join(''); + + // @todo: Break this up using many single conditions with early returns + return (typeof mixedVar === 'number' || typeof mixedVar === 'string' && whitespace.indexOf(mixedVar.slice(-1)) === -1) && mixedVar !== '' && !isNaN(mixedVar); +}; +//# sourceMappingURL=is_numeric.js.map + +/***/ }) + +/******/ }); +/************************************************************************/ +/******/ // The module cache +/******/ var __webpack_module_cache__ = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ // Check if module is in cache +/******/ var cachedModule = __webpack_module_cache__[moduleId]; +/******/ if (cachedModule !== undefined) { +/******/ return cachedModule.exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = __webpack_module_cache__[moduleId] = { +/******/ // no module.id needed +/******/ // no module.loaded needed +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/compat get default export */ +/******/ !function() { +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function() { return module['default']; } : +/******/ function() { return module; }; +/******/ __webpack_require__.d(getter, { a: getter }); +/******/ return getter; +/******/ }; +/******/ }(); +/******/ +/******/ /* webpack/runtime/define property getters */ +/******/ !function() { +/******/ // define getter functions for harmony exports +/******/ __webpack_require__.d = function(exports, definition) { +/******/ for(var key in definition) { +/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ }(); +/******/ +/******/ /* webpack/runtime/global */ +/******/ !function() { +/******/ __webpack_require__.g = (function() { +/******/ if (typeof globalThis === 'object') return globalThis; +/******/ try { +/******/ return this || new Function('return this')(); +/******/ } catch (e) { +/******/ if (typeof window === 'object') return window; +/******/ } +/******/ })(); +/******/ }(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ !function() { +/******/ __webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); } +/******/ }(); +/******/ +/******/ /* webpack/runtime/make namespace object */ +/******/ !function() { +/******/ // define __esModule on exports +/******/ __webpack_require__.r = function(exports) { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ }(); +/******/ +/************************************************************************/ +var __webpack_exports__ = {}; +// This entry needs to be wrapped in an IIFE because it needs to be isolated against other modules in the chunk. +!function() { +/*!****************************************!*\ + !*** ./resources/assets/js/helpers.js ***! + \****************************************/ +__webpack_require__.r(__webpack_exports__); +/* harmony import */ var locutus_php_strings_strlen__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! locutus/php/strings/strlen */ "./node_modules/locutus/php/strings/strlen.js"); +/* harmony import */ var locutus_php_strings_strlen__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(locutus_php_strings_strlen__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var locutus_php_array_array_diff__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! locutus/php/array/array_diff */ "./node_modules/locutus/php/array/array_diff.js"); +/* harmony import */ var locutus_php_array_array_diff__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(locutus_php_array_array_diff__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var locutus_php_datetime_strtotime__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! locutus/php/datetime/strtotime */ "./node_modules/locutus/php/datetime/strtotime.js"); +/* harmony import */ var locutus_php_datetime_strtotime__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(locutus_php_datetime_strtotime__WEBPACK_IMPORTED_MODULE_2__); +/* harmony import */ var locutus_php_var_is_numeric__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! locutus/php/var/is_numeric */ "./node_modules/locutus/php/var/is_numeric.js"); +/* harmony import */ var locutus_php_var_is_numeric__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(locutus_php_var_is_numeric__WEBPACK_IMPORTED_MODULE_3__); +/*! + * Laravel Javascript Validation + * + * https://github.com/proengsoft/laravel-jsvalidation + * + * Helper functions used by validators + * + * Copyright (c) 2017 Proengsoft + * Released under the MIT license + */ + + + + + +$.extend(true, laravelValidation, { + helpers: { + /** + * Numeric rules + */ + numericRules: ['Integer', 'Numeric'], + /** + * Gets the file information from file input. + * + * @param fieldObj + * @param index + * @returns {{file: *, extension: string, size: number}} + */ + fileinfo: function (fieldObj, index) { + var FileName = fieldObj.value; + index = typeof index !== 'undefined' ? index : 0; + if (fieldObj.files !== null) { + if (typeof fieldObj.files[index] !== 'undefined') { + return { + file: FileName, + extension: FileName.substr(FileName.lastIndexOf('.') + 1), + size: fieldObj.files[index].size / 1024, + type: fieldObj.files[index].type + }; + } + } + return false; + }, + /** + * Gets the selectors for th specified field names. + * + * @param names + * @returns {string} + */ + selector: function (names) { + var selector = []; + if (!this.isArray(names)) { + names = [names]; + } + for (var i = 0; i < names.length; i++) { + selector.push("[name='" + names[i] + "']"); + } + return selector.join(); + }, + /** + * Check if element has numeric rules. + * + * @param element + * @returns {boolean} + */ + hasNumericRules: function (element) { + return this.hasRules(element, this.numericRules); + }, + /** + * Check if element has passed rules. + * + * @param element + * @param rules + * @returns {boolean} + */ + hasRules: function (element, rules) { + var found = false; + if (typeof rules === 'string') { + rules = [rules]; + } + var validator = $.data(element.form, "validator"); + var listRules = []; + var cache = validator.arrayRulesCache; + if (element.name in cache) { + $.each(cache[element.name], function (index, arrayRule) { + listRules.push(arrayRule); + }); + } + if (element.name in validator.settings.rules) { + listRules.push(validator.settings.rules[element.name]); + } + $.each(listRules, function (index, objRules) { + if ('laravelValidation' in objRules) { + var _rules = objRules.laravelValidation; + for (var i = 0; i < _rules.length; i++) { + if ($.inArray(_rules[i][0], rules) !== -1) { + found = true; + return false; + } + } + } + }); + return found; + }, + /** + * Return the string length using PHP function. + * http://php.net/manual/en/function.strlen.php + * http://phpjs.org/functions/strlen/ + * + * @param string + */ + strlen: function (string) { + return locutus_php_strings_strlen__WEBPACK_IMPORTED_MODULE_0___default()(string); + }, + /** + * Get the size of the object depending of his type. + * + * @param obj + * @param element + * @param value + * @returns int + */ + getSize: function getSize(obj, element, value) { + if (this.hasNumericRules(element) && this.is_numeric(value)) { + return parseFloat(value); + } else if (this.isArray(value)) { + return parseFloat(value.length); + } else if (element.type === 'file') { + return parseFloat(Math.floor(this.fileinfo(element).size)); + } + return parseFloat(this.strlen(value)); + }, + /** + * Return specified rule from element. + * + * @param rule + * @param element + * @returns object + */ + getLaravelValidation: function (rule, element) { + var found = undefined; + $.each($.validator.staticRules(element), function (key, rules) { + if (key === "laravelValidation") { + $.each(rules, function (i, value) { + if (value[0] === rule) { + found = value; + } + }); + } + }); + return found; + }, + /** + * Return he timestamp of value passed using format or default format in element. + * + * @param value + * @param format + * @returns {boolean|int} + */ + parseTime: function (value, format) { + var timeValue = false; + var fmt = new DateFormatter(); + if (typeof value === 'number' && typeof format === 'undefined') { + return value; + } + if (typeof format === 'object') { + var dateRule = this.getLaravelValidation('DateFormat', format); + if (dateRule !== undefined) { + format = dateRule[1][0]; + } else { + format = null; + } + } + if (format == null) { + timeValue = this.strtotime(value); + } else { + timeValue = fmt.parseDate(value, format); + if (timeValue instanceof Date && fmt.formatDate(timeValue, format) === value) { + timeValue = Math.round(timeValue.getTime() / 1000); + } else { + timeValue = false; + } + } + return timeValue; + }, + /** + * Compare a given date against another using an operator. + * + * @param validator + * @param value + * @param element + * @param params + * @param operator + * @return {boolean} + */ + compareDates: function (validator, value, element, params, operator) { + var timeCompare = this.parseTime(params); + if (!timeCompare) { + var target = this.dependentElement(validator, element, params); + if (target === undefined) { + return false; + } + timeCompare = this.parseTime(validator.elementValue(target), target); + } + var timeValue = this.parseTime(value, element); + if (timeValue === false) { + return false; + } + switch (operator) { + case '<': + return timeValue < timeCompare; + case '<=': + return timeValue <= timeCompare; + case '==': + case '===': + return timeValue === timeCompare; + case '>': + return timeValue > timeCompare; + case '>=': + return timeValue >= timeCompare; + default: + throw new Error('Unsupported operator.'); + } + }, + /** + * This method allows you to intelligently guess the date by closely matching the specific format. + * + * @param value + * @param format + * @returns {Date} + */ + guessDate: function (value, format) { + var fmt = new DateFormatter(); + return fmt.guessDate(value, format); + }, + /** + * Returns Unix timestamp based on PHP function strototime. + * http://php.net/manual/es/function.strtotime.php + * http://phpjs.org/functions/strtotime/ + * + * @param text + * @param now + * @returns {*} + */ + strtotime: function (text, now) { + return locutus_php_datetime_strtotime__WEBPACK_IMPORTED_MODULE_2___default()(text, now); + }, + /** + * Returns if value is numeric. + * http://php.net/manual/es/var.is_numeric.php + * http://phpjs.org/functions/is_numeric/ + * + * @param mixed_var + * @returns {*} + */ + is_numeric: function (mixed_var) { + return locutus_php_var_is_numeric__WEBPACK_IMPORTED_MODULE_3___default()(mixed_var); + }, + /** + * Check whether the argument is of type Array. + * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray#Polyfill + * + * @param arg + * @returns {boolean} + */ + isArray: function (arg) { + return Object.prototype.toString.call(arg) === '[object Array]'; + }, + /** + * Returns Array diff based on PHP function array_diff. + * http://php.net/manual/es/function.array_diff.php + * http://phpjs.org/functions/array_diff/ + * + * @param arr1 + * @param arr2 + * @returns {*} + */ + arrayDiff: function (arr1, arr2) { + return locutus_php_array_array_diff__WEBPACK_IMPORTED_MODULE_1___default()(arr1, arr2); + }, + /** + * Check whether two arrays are equal to one another. + * + * @param arr1 + * @param arr2 + * @returns {*} + */ + arrayEquals: function (arr1, arr2) { + if (!this.isArray(arr1) || !this.isArray(arr2)) { + return false; + } + if (arr1.length !== arr2.length) { + return false; + } + return $.isEmptyObject(this.arrayDiff(arr1, arr2)); + }, + /** + * Makes element dependant from other. + * + * @param validator + * @param element + * @param name + * @returns {*} + */ + dependentElement: function (validator, element, name) { + var el = validator.findByName(name); + var targetElement = el[el.length - 1]; + if (targetElement !== undefined && validator.settings.onfocusout) { + var event = 'blur'; + if (targetElement.tagName === 'SELECT' || targetElement.tagName === 'OPTION' || targetElement.type === 'checkbox' || targetElement.type === 'radio') { + event = 'click'; + } + var ruleName = '.validate-laravelValidation'; + $(targetElement).off(ruleName).off(event + ruleName + '-' + element.name).on(event + ruleName + '-' + element.name, function () { + $(element).valid(); + }); + } + return targetElement; + }, + /** + * Parses error Ajax response and gets the message. + * + * @param response + * @returns {string[]} + */ + parseErrorResponse: function (response) { + var newResponse = ['Whoops, looks like something went wrong.']; + if ('responseText' in response) { + var errorMsg = response.responseText.match(/(.*)<\/h1\s*>/i); + if (this.isArray(errorMsg)) { + newResponse = [errorMsg[1]]; + } + } + return newResponse; + }, + /** + * Escape string to use as Regular Expression. + * + * @param str + * @returns string + */ + escapeRegExp: function (str) { + return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); + }, + /** + * Generate RegExp from wildcard attributes. + * + * @param name + * @returns {RegExp} + */ + regexFromWildcard: function (name) { + var nameParts = name.split('[*]'); + if (nameParts.length === 1) nameParts.push(''); + return new RegExp('^' + nameParts.map(function (x) { + return laravelValidation.helpers.escapeRegExp(x); + }).join('\\[[^\\]]*\\]') + '$'); + }, + /** + * Merge additional laravel validation rules into the current rule set. + * + * @param {object} rules + * @param {object} newRules + * @returns {object} + */ + mergeRules: function (rules, newRules) { + var rulesList = { + 'laravelValidation': newRules.laravelValidation || [], + 'laravelValidationRemote': newRules.laravelValidationRemote || [] + }; + for (var key in rulesList) { + if (rulesList[key].length === 0) { + continue; + } + if (typeof rules[key] === "undefined") { + rules[key] = []; + } + rules[key] = rules[key].concat(rulesList[key]); + } + return rules; + }, + /** + * HTML entity encode a string. + * + * @param string + * @returns {string} + */ + encode: function (string) { + return $('
').text(string).html(); + }, + /** + * Lookup name in an array. + * + * @param validator + * @param {string} name Name in dot notation format. + * @returns {*} + */ + findByArrayName: function (validator, name) { + var sqName = name.replace(/\.([^\.]+)/g, '[$1]'), + lookups = [ + // Convert dot to square brackets. e.g. foo.bar.0 becomes foo[bar][0] + sqName, + // Append [] to the name e.g. foo becomes foo[] or foo.bar.0 becomes foo[bar][0][] + sqName + '[]', + // Remove key from last array e.g. foo[bar][0] becomes foo[bar][] + sqName.replace(/(.*)\[(.*)\]$/g, '$1[]')]; + for (var i = 0; i < lookups.length; i++) { + var elem = validator.findByName(lookups[i]); + if (elem.length > 0) { + return elem; + } + } + return $(null); + }, + /** + * Attempt to find an element in the DOM matching the given name. + * Example names include: + * - domain.0 which matches domain[] + * - customfield.3 which matches customfield[3] + * + * @param validator + * @param {string} name + * @returns {*} + */ + findByName: function (validator, name) { + // Exact match. + var elem = validator.findByName(name); + if (elem.length > 0) { + return elem; + } + + // Find name in data, using dot notation. + var delim = '.', + parts = name.split(delim); + for (var i = parts.length; i > 0; i--) { + var reconstructed = []; + for (var c = 0; c < i; c++) { + reconstructed.push(parts[c]); + } + elem = this.findByArrayName(validator, reconstructed.join(delim)); + if (elem.length > 0) { + return elem; + } + } + return $(null); + }, + /** + * If it's an array element, get all values. + * + * @param validator + * @param element + * @returns {*|string} + */ + allElementValues: function (validator, element) { + if (element.name.indexOf('[]') !== -1) { + return validator.findByName(element.name).map(function (i, e) { + return validator.elementValue(e); + }).get(); + } + return validator.elementValue(element); + } + } +}); +}(); +/******/ })() +; +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGVscGVycy5qcyIsIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7O0FBQWE7O0FBRWI7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsbUJBQW1COztBQUVuQjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSxnQkFBZ0IsVUFBVTtBQUMxQjtBQUNBO0FBQ0E7QUFDQTtBQUNBLDZCQUE2QjtBQUM3QjtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7Ozs7Ozs7OztBQ2hDYTs7QUFFYjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUEscUJBQXFCLElBQUk7QUFDekIsc0JBQXNCLEVBQUU7QUFDeEIsc0JBQXNCLEVBQUU7QUFDeEIsbUNBQW1DLEVBQUU7QUFDckM7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLDZCQUE2QixJQUFJLElBQUksSUFBSSxHQUFHLElBQUk7QUFDaEQ7O0FBRUE7QUFDQSw4QkFBOEIsSUFBSTtBQUNsQztBQUNBOztBQUVBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLHdEQUF3RCxJQUFJO0FBQzVEOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQSxpQkFBaUIsRUFBRSxTQUFTLEVBQUUsTUFBTSxFQUFFO0FBQ3RDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU87QUFDUDtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBLDhCQUE4QixJQUFJO0FBQ2xDO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSxHQUFHO0FBQ0g7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLEdBQUc7QUFDSDtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSxRQUFRO0FBQ1I7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLFVBQVU7QUFDVjtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQSxzQ0FBc0MsT0FBTztBQUM3Qzs7QUFFQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOzs7Ozs7Ozs7O0FDcHlDYTs7QUFFYjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQSx5REFBeUQscUJBQU07QUFDL0Q7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7Ozs7Ozs7OztBQ3pCYTs7QUFFYjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTs7QUFFQSxnQkFBZ0IsS0FBOEIsR0FBRyxtQkFBTyxDQUFDLG1FQUFpQix5QkFBeUIsQ0FBUztBQUM1RztBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUEsd0JBQXdCLGdCQUFnQjtBQUN4QztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOzs7Ozs7Ozs7O0FDM0VhOztBQUViO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7O1VDN0JBO1VBQ0E7O1VBRUE7VUFDQTtVQUNBO1VBQ0E7VUFDQTtVQUNBO1VBQ0E7VUFDQTtVQUNBO1VBQ0E7VUFDQTtVQUNBO1VBQ0E7O1VBRUE7VUFDQTs7VUFFQTtVQUNBO1VBQ0E7Ozs7O1dDdEJBO1dBQ0E7V0FDQTtXQUNBLGVBQWUsNEJBQTRCO1dBQzNDLGVBQWU7V0FDZixpQ0FBaUMsV0FBVztXQUM1QztXQUNBOzs7OztXQ1BBO1dBQ0E7V0FDQTtXQUNBO1dBQ0EseUNBQXlDLHdDQUF3QztXQUNqRjtXQUNBO1dBQ0E7Ozs7O1dDUEE7V0FDQTtXQUNBO1dBQ0E7V0FDQSxHQUFHO1dBQ0g7V0FDQTtXQUNBLENBQUM7Ozs7O1dDUEQsOENBQThDOzs7OztXQ0E5QztXQUNBO1dBQ0E7V0FDQSx1REFBdUQsaUJBQWlCO1dBQ3hFO1dBQ0EsZ0RBQWdELGFBQWE7V0FDN0Q7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUNOQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFZ0Q7QUFDTTtBQUNDO0FBQ0g7QUFFcERJLENBQUMsQ0FBQ0MsTUFBTSxDQUFDLElBQUksRUFBRUMsaUJBQWlCLEVBQUU7RUFFOUJDLE9BQU8sRUFBRTtJQUVMO0FBQ1I7QUFDQTtJQUNRQyxZQUFZLEVBQUUsQ0FBQyxTQUFTLEVBQUUsU0FBUyxDQUFDO0lBRXBDO0FBQ1I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0lBQ1FDLFFBQVEsRUFBRSxTQUFBQSxDQUFVQyxRQUFRLEVBQUVDLEtBQUssRUFBRTtNQUNqQyxJQUFJQyxRQUFRLEdBQUdGLFFBQVEsQ0FBQ0csS0FBSztNQUM3QkYsS0FBSyxHQUFHLE9BQU9BLEtBQUssS0FBSyxXQUFXLEdBQUdBLEtBQUssR0FBRyxDQUFDO01BQ2hELElBQUtELFFBQVEsQ0FBQ0ksS0FBSyxLQUFLLElBQUksRUFBRztRQUMzQixJQUFJLE9BQU9KLFFBQVEsQ0FBQ0ksS0FBSyxDQUFDSCxLQUFLLENBQUMsS0FBSyxXQUFXLEVBQUU7VUFDOUMsT0FBTztZQUNISSxJQUFJLEVBQUVILFFBQVE7WUFDZEksU0FBUyxFQUFFSixRQUFRLENBQUNLLE1BQU0sQ0FBQ0wsUUFBUSxDQUFDTSxXQUFXLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1lBQ3pEQyxJQUFJLEVBQUVULFFBQVEsQ0FBQ0ksS0FBSyxDQUFDSCxLQUFLLENBQUMsQ0FBQ1EsSUFBSSxHQUFHLElBQUk7WUFDdkNDLElBQUksRUFBRVYsUUFBUSxDQUFDSSxLQUFLLENBQUNILEtBQUssQ0FBQyxDQUFDUztVQUNoQyxDQUFDO1FBQ0w7TUFDSjtNQUNBLE9BQU8sS0FBSztJQUNoQixDQUFDO0lBR0Q7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0lBQ1FDLFFBQVEsRUFBRSxTQUFBQSxDQUFVQyxLQUFLLEVBQUU7TUFDdkIsSUFBSUQsUUFBUSxHQUFHLEVBQUU7TUFDakIsSUFBSSxDQUFFLElBQUksQ0FBQ0UsT0FBTyxDQUFDRCxLQUFLLENBQUMsRUFBRztRQUN4QkEsS0FBSyxHQUFHLENBQUNBLEtBQUssQ0FBQztNQUNuQjtNQUNBLEtBQUssSUFBSUUsQ0FBQyxHQUFHLENBQUMsRUFBRUEsQ0FBQyxHQUFHRixLQUFLLENBQUNHLE1BQU0sRUFBRUQsQ0FBQyxFQUFFLEVBQUU7UUFDbkNILFFBQVEsQ0FBQ0ssSUFBSSxDQUFDLFNBQVMsR0FBR0osS0FBSyxDQUFDRSxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUM7TUFDOUM7TUFDQSxPQUFPSCxRQUFRLENBQUNNLElBQUksQ0FBQyxDQUFDO0lBQzFCLENBQUM7SUFHRDtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7SUFDUUMsZUFBZSxFQUFFLFNBQUFBLENBQVVDLE9BQU8sRUFBRTtNQUNoQyxPQUFPLElBQUksQ0FBQ0MsUUFBUSxDQUFDRCxPQUFPLEVBQUUsSUFBSSxDQUFDckIsWUFBWSxDQUFDO0lBQ3BELENBQUM7SUFFRDtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtJQUNRc0IsUUFBUSxFQUFFLFNBQUFBLENBQVVELE9BQU8sRUFBRUUsS0FBSyxFQUFFO01BRWhDLElBQUlDLEtBQUssR0FBRyxLQUFLO01BQ2pCLElBQUksT0FBT0QsS0FBSyxLQUFLLFFBQVEsRUFBRTtRQUMzQkEsS0FBSyxHQUFHLENBQUNBLEtBQUssQ0FBQztNQUNuQjtNQUVBLElBQUlFLFNBQVMsR0FBRzdCLENBQUMsQ0FBQzhCLElBQUksQ0FBQ0wsT0FBTyxDQUFDTSxJQUFJLEVBQUUsV0FBVyxDQUFDO01BQ2pELElBQUlDLFNBQVMsR0FBRyxFQUFFO01BQ2xCLElBQUlDLEtBQUssR0FBR0osU0FBUyxDQUFDSyxlQUFlO01BQ3JDLElBQUlULE9BQU8sQ0FBQ1UsSUFBSSxJQUFJRixLQUFLLEVBQUU7UUFDdkJqQyxDQUFDLENBQUNvQyxJQUFJLENBQUNILEtBQUssQ0FBQ1IsT0FBTyxDQUFDVSxJQUFJLENBQUMsRUFBRSxVQUFVNUIsS0FBSyxFQUFFOEIsU0FBUyxFQUFFO1VBQ3BETCxTQUFTLENBQUNWLElBQUksQ0FBQ2UsU0FBUyxDQUFDO1FBQzdCLENBQUMsQ0FBQztNQUNOO01BQ0EsSUFBSVosT0FBTyxDQUFDVSxJQUFJLElBQUlOLFNBQVMsQ0FBQ1MsUUFBUSxDQUFDWCxLQUFLLEVBQUU7UUFDMUNLLFNBQVMsQ0FBQ1YsSUFBSSxDQUFDTyxTQUFTLENBQUNTLFFBQVEsQ0FBQ1gsS0FBSyxDQUFDRixPQUFPLENBQUNVLElBQUksQ0FBQyxDQUFDO01BQzFEO01BQ0FuQyxDQUFDLENBQUNvQyxJQUFJLENBQUNKLFNBQVMsRUFBRSxVQUFTekIsS0FBSyxFQUFDZ0MsUUFBUSxFQUFDO1FBQ3RDLElBQUksbUJBQW1CLElBQUlBLFFBQVEsRUFBRTtVQUNqQyxJQUFJQyxNQUFNLEdBQUNELFFBQVEsQ0FBQ3JDLGlCQUFpQjtVQUNyQyxLQUFLLElBQUlrQixDQUFDLEdBQUcsQ0FBQyxFQUFFQSxDQUFDLEdBQUdvQixNQUFNLENBQUNuQixNQUFNLEVBQUVELENBQUMsRUFBRSxFQUFFO1lBQ3BDLElBQUlwQixDQUFDLENBQUN5QyxPQUFPLENBQUNELE1BQU0sQ0FBQ3BCLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFDTyxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRTtjQUN0Q0MsS0FBSyxHQUFHLElBQUk7Y0FDWixPQUFPLEtBQUs7WUFDaEI7VUFDSjtRQUNKO01BQ0osQ0FBQyxDQUFDO01BRUYsT0FBT0EsS0FBSztJQUNoQixDQUFDO0lBRUQ7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7SUFDUWhDLE1BQU0sRUFBRSxTQUFBQSxDQUFVOEMsTUFBTSxFQUFFO01BQ3RCLE9BQU85QyxpRUFBTSxDQUFDOEMsTUFBTSxDQUFDO0lBQ3pCLENBQUM7SUFFRDtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0lBQ1FDLE9BQU8sRUFBRSxTQUFTQSxPQUFPQSxDQUFDQyxHQUFHLEVBQUVuQixPQUFPLEVBQUVoQixLQUFLLEVBQUU7TUFFM0MsSUFBSSxJQUFJLENBQUNlLGVBQWUsQ0FBQ0MsT0FBTyxDQUFDLElBQUksSUFBSSxDQUFDMUIsVUFBVSxDQUFDVSxLQUFLLENBQUMsRUFBRTtRQUN6RCxPQUFPb0MsVUFBVSxDQUFDcEMsS0FBSyxDQUFDO01BQzVCLENBQUMsTUFBTSxJQUFJLElBQUksQ0FBQ1UsT0FBTyxDQUFDVixLQUFLLENBQUMsRUFBRTtRQUM1QixPQUFPb0MsVUFBVSxDQUFDcEMsS0FBSyxDQUFDWSxNQUFNLENBQUM7TUFDbkMsQ0FBQyxNQUFNLElBQUlJLE9BQU8sQ0FBQ1QsSUFBSSxLQUFLLE1BQU0sRUFBRTtRQUNoQyxPQUFPNkIsVUFBVSxDQUFDQyxJQUFJLENBQUNDLEtBQUssQ0FBQyxJQUFJLENBQUMxQyxRQUFRLENBQUNvQixPQUFPLENBQUMsQ0FBQ1YsSUFBSSxDQUFDLENBQUM7TUFDOUQ7TUFFQSxPQUFPOEIsVUFBVSxDQUFDLElBQUksQ0FBQ2pELE1BQU0sQ0FBQ2EsS0FBSyxDQUFDLENBQUM7SUFDekMsQ0FBQztJQUdEO0FBQ1I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0lBQ1F1QyxvQkFBb0IsRUFBRSxTQUFBQSxDQUFTQyxJQUFJLEVBQUV4QixPQUFPLEVBQUU7TUFFMUMsSUFBSUcsS0FBSyxHQUFHc0IsU0FBUztNQUNyQmxELENBQUMsQ0FBQ29DLElBQUksQ0FBQ3BDLENBQUMsQ0FBQzZCLFNBQVMsQ0FBQ3NCLFdBQVcsQ0FBQzFCLE9BQU8sQ0FBQyxFQUFFLFVBQVMyQixHQUFHLEVBQUV6QixLQUFLLEVBQUU7UUFDMUQsSUFBSXlCLEdBQUcsS0FBRyxtQkFBbUIsRUFBRTtVQUMzQnBELENBQUMsQ0FBQ29DLElBQUksQ0FBQ1QsS0FBSyxFQUFFLFVBQVVQLENBQUMsRUFBRVgsS0FBSyxFQUFFO1lBQzlCLElBQUlBLEtBQUssQ0FBQyxDQUFDLENBQUMsS0FBR3dDLElBQUksRUFBRTtjQUNqQnJCLEtBQUssR0FBQ25CLEtBQUs7WUFDZjtVQUNKLENBQUMsQ0FBQztRQUNOO01BQ0osQ0FBQyxDQUFDO01BRUYsT0FBT21CLEtBQUs7SUFDaEIsQ0FBQztJQUVEO0FBQ1I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0lBQ1F5QixTQUFTLEVBQUUsU0FBQUEsQ0FBVTVDLEtBQUssRUFBRTZDLE1BQU0sRUFBRTtNQUVoQyxJQUFJQyxTQUFTLEdBQUcsS0FBSztNQUNyQixJQUFJQyxHQUFHLEdBQUcsSUFBSUMsYUFBYSxDQUFDLENBQUM7TUFFN0IsSUFBSSxPQUFPaEQsS0FBSyxLQUFLLFFBQVEsSUFBSSxPQUFPNkMsTUFBTSxLQUFLLFdBQVcsRUFBRTtRQUM1RCxPQUFPN0MsS0FBSztNQUNoQjtNQUVBLElBQUksT0FBTzZDLE1BQU0sS0FBSyxRQUFRLEVBQUU7UUFDNUIsSUFBSUksUUFBUSxHQUFHLElBQUksQ0FBQ1Ysb0JBQW9CLENBQUMsWUFBWSxFQUFFTSxNQUFNLENBQUM7UUFDOUQsSUFBSUksUUFBUSxLQUFLUixTQUFTLEVBQUU7VUFDeEJJLE1BQU0sR0FBR0ksUUFBUSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUMzQixDQUFDLE1BQU07VUFDSEosTUFBTSxHQUFHLElBQUk7UUFDakI7TUFDSjtNQUVBLElBQUlBLE1BQU0sSUFBSSxJQUFJLEVBQUU7UUFDaEJDLFNBQVMsR0FBRyxJQUFJLENBQUN6RCxTQUFTLENBQUNXLEtBQUssQ0FBQztNQUNyQyxDQUFDLE1BQU07UUFDSDhDLFNBQVMsR0FBR0MsR0FBRyxDQUFDRyxTQUFTLENBQUNsRCxLQUFLLEVBQUU2QyxNQUFNLENBQUM7UUFDeEMsSUFBSUMsU0FBUyxZQUFZSyxJQUFJLElBQUlKLEdBQUcsQ0FBQ0ssVUFBVSxDQUFDTixTQUFTLEVBQUVELE1BQU0sQ0FBQyxLQUFLN0MsS0FBSyxFQUFFO1VBQzFFOEMsU0FBUyxHQUFHVCxJQUFJLENBQUNnQixLQUFLLENBQUVQLFNBQVMsQ0FBQ1EsT0FBTyxDQUFDLENBQUMsR0FBRyxJQUFLLENBQUM7UUFDeEQsQ0FBQyxNQUFNO1VBQ0hSLFNBQVMsR0FBRyxLQUFLO1FBQ3JCO01BQ0o7TUFFQSxPQUFPQSxTQUFTO0lBQ3BCLENBQUM7SUFFRDtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtJQUNRUyxZQUFZLEVBQUUsU0FBQUEsQ0FBVW5DLFNBQVMsRUFBRXBCLEtBQUssRUFBRWdCLE9BQU8sRUFBRXdDLE1BQU0sRUFBRUMsUUFBUSxFQUFFO01BRWpFLElBQUlDLFdBQVcsR0FBRyxJQUFJLENBQUNkLFNBQVMsQ0FBQ1ksTUFBTSxDQUFDO01BRXhDLElBQUksQ0FBQ0UsV0FBVyxFQUFFO1FBQ2QsSUFBSUMsTUFBTSxHQUFHLElBQUksQ0FBQ0MsZ0JBQWdCLENBQUN4QyxTQUFTLEVBQUVKLE9BQU8sRUFBRXdDLE1BQU0sQ0FBQztRQUM5RCxJQUFJRyxNQUFNLEtBQUtsQixTQUFTLEVBQUU7VUFDdEIsT0FBTyxLQUFLO1FBQ2hCO1FBQ0FpQixXQUFXLEdBQUcsSUFBSSxDQUFDZCxTQUFTLENBQUN4QixTQUFTLENBQUN5QyxZQUFZLENBQUNGLE1BQU0sQ0FBQyxFQUFFQSxNQUFNLENBQUM7TUFDeEU7TUFFQSxJQUFJYixTQUFTLEdBQUcsSUFBSSxDQUFDRixTQUFTLENBQUM1QyxLQUFLLEVBQUVnQixPQUFPLENBQUM7TUFDOUMsSUFBSThCLFNBQVMsS0FBSyxLQUFLLEVBQUU7UUFDckIsT0FBTyxLQUFLO01BQ2hCO01BRUEsUUFBUVcsUUFBUTtRQUNaLEtBQUssR0FBRztVQUNKLE9BQU9YLFNBQVMsR0FBR1ksV0FBVztRQUVsQyxLQUFLLElBQUk7VUFDTCxPQUFPWixTQUFTLElBQUlZLFdBQVc7UUFFbkMsS0FBSyxJQUFJO1FBQ1QsS0FBSyxLQUFLO1VBQ04sT0FBT1osU0FBUyxLQUFLWSxXQUFXO1FBRXBDLEtBQUssR0FBRztVQUNKLE9BQU9aLFNBQVMsR0FBR1ksV0FBVztRQUVsQyxLQUFLLElBQUk7VUFDTCxPQUFPWixTQUFTLElBQUlZLFdBQVc7UUFFbkM7VUFDSSxNQUFNLElBQUlJLEtBQUssQ0FBQyx1QkFBdUIsQ0FBQztNQUNoRDtJQUNKLENBQUM7SUFFRDtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtJQUNRQyxTQUFTLEVBQUUsU0FBQUEsQ0FBVS9ELEtBQUssRUFBRTZDLE1BQU0sRUFBRTtNQUNoQyxJQUFJRSxHQUFHLEdBQUcsSUFBSUMsYUFBYSxDQUFDLENBQUM7TUFDN0IsT0FBT0QsR0FBRyxDQUFDZ0IsU0FBUyxDQUFDL0QsS0FBSyxFQUFFNkMsTUFBTSxDQUFDO0lBQ3ZDLENBQUM7SUFFRDtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7SUFDUXhELFNBQVMsRUFBRSxTQUFBQSxDQUFVMkUsSUFBSSxFQUFFQyxHQUFHLEVBQUU7TUFDNUIsT0FBTzVFLHFFQUFTLENBQUMyRSxJQUFJLEVBQUVDLEdBQUcsQ0FBQztJQUMvQixDQUFDO0lBRUQ7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtJQUNRM0UsVUFBVSxFQUFFLFNBQUFBLENBQVU0RSxTQUFTLEVBQUU7TUFDN0IsT0FBTzVFLGlFQUFVLENBQUM0RSxTQUFTLENBQUM7SUFDaEMsQ0FBQztJQUVEO0FBQ1I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0lBQ1F4RCxPQUFPLEVBQUUsU0FBQUEsQ0FBU3lELEdBQUcsRUFBRTtNQUNuQixPQUFPQyxNQUFNLENBQUNDLFNBQVMsQ0FBQ0MsUUFBUSxDQUFDQyxJQUFJLENBQUNKLEdBQUcsQ0FBQyxLQUFLLGdCQUFnQjtJQUNuRSxDQUFDO0lBRUQ7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0lBQ1FLLFNBQVMsRUFBRSxTQUFBQSxDQUFVQyxJQUFJLEVBQUVDLElBQUksRUFBRTtNQUM3QixPQUFPdEYsbUVBQVUsQ0FBQ3FGLElBQUksRUFBRUMsSUFBSSxDQUFDO0lBQ2pDLENBQUM7SUFFRDtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtJQUNRQyxXQUFXLEVBQUUsU0FBQUEsQ0FBVUYsSUFBSSxFQUFFQyxJQUFJLEVBQUU7TUFDL0IsSUFBSSxDQUFFLElBQUksQ0FBQ2hFLE9BQU8sQ0FBQytELElBQUksQ0FBQyxJQUFJLENBQUUsSUFBSSxDQUFDL0QsT0FBTyxDQUFDZ0UsSUFBSSxDQUFDLEVBQUU7UUFDOUMsT0FBTyxLQUFLO01BQ2hCO01BRUEsSUFBSUQsSUFBSSxDQUFDN0QsTUFBTSxLQUFLOEQsSUFBSSxDQUFDOUQsTUFBTSxFQUFFO1FBQzdCLE9BQU8sS0FBSztNQUNoQjtNQUVBLE9BQU9yQixDQUFDLENBQUNxRixhQUFhLENBQUMsSUFBSSxDQUFDSixTQUFTLENBQUNDLElBQUksRUFBRUMsSUFBSSxDQUFDLENBQUM7SUFDdEQsQ0FBQztJQUVEO0FBQ1I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7SUFDUWQsZ0JBQWdCLEVBQUUsU0FBQUEsQ0FBU3hDLFNBQVMsRUFBRUosT0FBTyxFQUFFVSxJQUFJLEVBQUU7TUFDakQsSUFBSW1ELEVBQUUsR0FBR3pELFNBQVMsQ0FBQzBELFVBQVUsQ0FBQ3BELElBQUksQ0FBQztNQUVuQyxJQUFJcUQsYUFBYSxHQUFHRixFQUFFLENBQUNBLEVBQUUsQ0FBQ2pFLE1BQU0sR0FBRyxDQUFDLENBQUM7TUFFckMsSUFBSW1FLGFBQWEsS0FBS3RDLFNBQVMsSUFBSXJCLFNBQVMsQ0FBQ1MsUUFBUSxDQUFDbUQsVUFBVSxFQUFFO1FBQzlELElBQUlDLEtBQUssR0FBRyxNQUFNO1FBQ2xCLElBQ0lGLGFBQWEsQ0FBQ0csT0FBTyxLQUFLLFFBQVEsSUFDbENILGFBQWEsQ0FBQ0csT0FBTyxLQUFLLFFBQVEsSUFDbENILGFBQWEsQ0FBQ3hFLElBQUksS0FBSyxVQUFVLElBQ2pDd0UsYUFBYSxDQUFDeEUsSUFBSSxLQUFLLE9BQU8sRUFDaEM7VUFDRTBFLEtBQUssR0FBRyxPQUFPO1FBQ25CO1FBRUEsSUFBSUUsUUFBUSxHQUFHLDZCQUE2QjtRQUM1QzVGLENBQUMsQ0FBQ3dGLGFBQWEsQ0FBQyxDQUNYSyxHQUFHLENBQUNELFFBQVEsQ0FBQyxDQUNiQyxHQUFHLENBQUNILEtBQUssR0FBR0UsUUFBUSxHQUFHLEdBQUcsR0FBR25FLE9BQU8sQ0FBQ1UsSUFBSSxDQUFDLENBQzFDMkQsRUFBRSxDQUFDSixLQUFLLEdBQUdFLFFBQVEsR0FBRyxHQUFHLEdBQUduRSxPQUFPLENBQUNVLElBQUksRUFBRSxZQUFZO1VBQ25EbkMsQ0FBQyxDQUFDeUIsT0FBTyxDQUFDLENBQUNzRSxLQUFLLENBQUMsQ0FBQztRQUN0QixDQUFDLENBQUM7TUFDVjtNQUVBLE9BQU9QLGFBQWE7SUFDeEIsQ0FBQztJQUVEO0FBQ1I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtJQUNRUSxrQkFBa0IsRUFBRSxTQUFBQSxDQUFVQyxRQUFRLEVBQUU7TUFDcEMsSUFBSUMsV0FBVyxHQUFHLENBQUMsMENBQTBDLENBQUM7TUFDOUQsSUFBSSxjQUFjLElBQUlELFFBQVEsRUFBRTtRQUM1QixJQUFJRSxRQUFRLEdBQUdGLFFBQVEsQ0FBQ0csWUFBWSxDQUFDQyxLQUFLLENBQUMsdUJBQXVCLENBQUM7UUFDbkUsSUFBSSxJQUFJLENBQUNsRixPQUFPLENBQUNnRixRQUFRLENBQUMsRUFBRTtVQUN4QkQsV0FBVyxHQUFHLENBQUNDLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUMvQjtNQUNKO01BQ0EsT0FBT0QsV0FBVztJQUN0QixDQUFDO0lBRUQ7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0lBQ1FJLFlBQVksRUFBRSxTQUFBQSxDQUFVQyxHQUFHLEVBQUU7TUFDekIsT0FBT0EsR0FBRyxDQUFDQyxPQUFPLENBQUMscUNBQXFDLEVBQUUsTUFBTSxDQUFDO0lBQ3JFLENBQUM7SUFFRDtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7SUFDUUMsaUJBQWlCLEVBQUUsU0FBQUEsQ0FBVXRFLElBQUksRUFBRTtNQUMvQixJQUFJdUUsU0FBUyxHQUFHdkUsSUFBSSxDQUFDd0UsS0FBSyxDQUFDLEtBQUssQ0FBQztNQUNqQyxJQUFJRCxTQUFTLENBQUNyRixNQUFNLEtBQUssQ0FBQyxFQUFFcUYsU0FBUyxDQUFDcEYsSUFBSSxDQUFDLEVBQUUsQ0FBQztNQUU5QyxPQUFPLElBQUlzRixNQUFNLENBQUMsR0FBRyxHQUFHRixTQUFTLENBQUNHLEdBQUcsQ0FBQyxVQUFTQyxDQUFDLEVBQUU7UUFDOUMsT0FBTzVHLGlCQUFpQixDQUFDQyxPQUFPLENBQUNtRyxZQUFZLENBQUNRLENBQUMsQ0FBQztNQUNwRCxDQUFDLENBQUMsQ0FBQ3ZGLElBQUksQ0FBQyxlQUFlLENBQUMsR0FBRyxHQUFHLENBQUM7SUFDbkMsQ0FBQztJQUVEO0FBQ1I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0lBQ1F3RixVQUFVLEVBQUUsU0FBQUEsQ0FBVXBGLEtBQUssRUFBRXFGLFFBQVEsRUFBRTtNQUNuQyxJQUFJQyxTQUFTLEdBQUc7UUFDWixtQkFBbUIsRUFBRUQsUUFBUSxDQUFDOUcsaUJBQWlCLElBQUksRUFBRTtRQUNyRCx5QkFBeUIsRUFBRThHLFFBQVEsQ0FBQ0UsdUJBQXVCLElBQUk7TUFDbkUsQ0FBQztNQUVELEtBQUssSUFBSTlELEdBQUcsSUFBSTZELFNBQVMsRUFBRTtRQUN2QixJQUFJQSxTQUFTLENBQUM3RCxHQUFHLENBQUMsQ0FBQy9CLE1BQU0sS0FBSyxDQUFDLEVBQUU7VUFDN0I7UUFDSjtRQUVBLElBQUksT0FBT00sS0FBSyxDQUFDeUIsR0FBRyxDQUFDLEtBQUssV0FBVyxFQUFFO1VBQ25DekIsS0FBSyxDQUFDeUIsR0FBRyxDQUFDLEdBQUcsRUFBRTtRQUNuQjtRQUVBekIsS0FBSyxDQUFDeUIsR0FBRyxDQUFDLEdBQUd6QixLQUFLLENBQUN5QixHQUFHLENBQUMsQ0FBQytELE1BQU0sQ0FBQ0YsU0FBUyxDQUFDN0QsR0FBRyxDQUFDLENBQUM7TUFDbEQ7TUFFQSxPQUFPekIsS0FBSztJQUNoQixDQUFDO0lBRUQ7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0lBQ1F5RixNQUFNLEVBQUUsU0FBQUEsQ0FBVTFFLE1BQU0sRUFBRTtNQUN0QixPQUFPMUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDeUUsSUFBSSxDQUFDL0IsTUFBTSxDQUFDLENBQUMyRSxJQUFJLENBQUMsQ0FBQztJQUMxQyxDQUFDO0lBRUQ7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7SUFDUUMsZUFBZSxFQUFFLFNBQUFBLENBQVV6RixTQUFTLEVBQUVNLElBQUksRUFBRTtNQUN4QyxJQUFJb0YsTUFBTSxHQUFHcEYsSUFBSSxDQUFDcUUsT0FBTyxDQUFDLGFBQWEsRUFBRSxNQUFNLENBQUM7UUFDNUNnQixPQUFPLEdBQUc7UUFDTjtRQUNBRCxNQUFNO1FBQ047UUFDQUEsTUFBTSxHQUFHLElBQUk7UUFDYjtRQUNBQSxNQUFNLENBQUNmLE9BQU8sQ0FBQyxnQkFBZ0IsRUFBRSxNQUFNLENBQUMsQ0FDM0M7TUFFTCxLQUFLLElBQUlwRixDQUFDLEdBQUcsQ0FBQyxFQUFFQSxDQUFDLEdBQUdvRyxPQUFPLENBQUNuRyxNQUFNLEVBQUVELENBQUMsRUFBRSxFQUFFO1FBQ3JDLElBQUlxRyxJQUFJLEdBQUc1RixTQUFTLENBQUMwRCxVQUFVLENBQUNpQyxPQUFPLENBQUNwRyxDQUFDLENBQUMsQ0FBQztRQUMzQyxJQUFJcUcsSUFBSSxDQUFDcEcsTUFBTSxHQUFHLENBQUMsRUFBRTtVQUNqQixPQUFPb0csSUFBSTtRQUNmO01BQ0o7TUFFQSxPQUFPekgsQ0FBQyxDQUFDLElBQUksQ0FBQztJQUNsQixDQUFDO0lBRUQ7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7SUFDUXVGLFVBQVUsRUFBRSxTQUFBQSxDQUFVMUQsU0FBUyxFQUFFTSxJQUFJLEVBQUU7TUFDbkM7TUFDQSxJQUFJc0YsSUFBSSxHQUFHNUYsU0FBUyxDQUFDMEQsVUFBVSxDQUFDcEQsSUFBSSxDQUFDO01BQ3JDLElBQUlzRixJQUFJLENBQUNwRyxNQUFNLEdBQUcsQ0FBQyxFQUFFO1FBQ2pCLE9BQU9vRyxJQUFJO01BQ2Y7O01BRUE7TUFDQSxJQUFJQyxLQUFLLEdBQUcsR0FBRztRQUNYQyxLQUFLLEdBQUl4RixJQUFJLENBQUN3RSxLQUFLLENBQUNlLEtBQUssQ0FBQztNQUM5QixLQUFLLElBQUl0RyxDQUFDLEdBQUd1RyxLQUFLLENBQUN0RyxNQUFNLEVBQUVELENBQUMsR0FBRyxDQUFDLEVBQUVBLENBQUMsRUFBRSxFQUFFO1FBQ25DLElBQUl3RyxhQUFhLEdBQUcsRUFBRTtRQUN0QixLQUFLLElBQUlDLENBQUMsR0FBRyxDQUFDLEVBQUVBLENBQUMsR0FBR3pHLENBQUMsRUFBRXlHLENBQUMsRUFBRSxFQUFFO1VBQ3hCRCxhQUFhLENBQUN0RyxJQUFJLENBQUNxRyxLQUFLLENBQUNFLENBQUMsQ0FBQyxDQUFDO1FBQ2hDO1FBRUFKLElBQUksR0FBRyxJQUFJLENBQUNILGVBQWUsQ0FBQ3pGLFNBQVMsRUFBRStGLGFBQWEsQ0FBQ3JHLElBQUksQ0FBQ21HLEtBQUssQ0FBQyxDQUFDO1FBQ2pFLElBQUlELElBQUksQ0FBQ3BHLE1BQU0sR0FBRyxDQUFDLEVBQUU7VUFDakIsT0FBT29HLElBQUk7UUFDZjtNQUNKO01BRUEsT0FBT3pILENBQUMsQ0FBQyxJQUFJLENBQUM7SUFDbEIsQ0FBQztJQUVEO0FBQ1I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0lBQ1E4SCxnQkFBZ0IsRUFBRSxTQUFBQSxDQUFVakcsU0FBUyxFQUFFSixPQUFPLEVBQUU7TUFDNUMsSUFBSUEsT0FBTyxDQUFDVSxJQUFJLENBQUM0RixPQUFPLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUU7UUFDbkMsT0FBT2xHLFNBQVMsQ0FBQzBELFVBQVUsQ0FBQzlELE9BQU8sQ0FBQ1UsSUFBSSxDQUFDLENBQUMwRSxHQUFHLENBQUMsVUFBVXpGLENBQUMsRUFBRTRHLENBQUMsRUFBRTtVQUMxRCxPQUFPbkcsU0FBUyxDQUFDeUMsWUFBWSxDQUFDMEQsQ0FBQyxDQUFDO1FBQ3BDLENBQUMsQ0FBQyxDQUFDQyxHQUFHLENBQUMsQ0FBQztNQUNaO01BRUEsT0FBT3BHLFNBQVMsQ0FBQ3lDLFlBQVksQ0FBQzdDLE9BQU8sQ0FBQztJQUMxQztFQUNKO0FBQ0osQ0FBQyxDQUFDLEMiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vLi9ub2RlX21vZHVsZXMvbG9jdXR1cy9waHAvYXJyYXkvYXJyYXlfZGlmZi5qcyIsIndlYnBhY2s6Ly8vLi9ub2RlX21vZHVsZXMvbG9jdXR1cy9waHAvZGF0ZXRpbWUvc3RydG90aW1lLmpzIiwid2VicGFjazovLy8uL25vZGVfbW9kdWxlcy9sb2N1dHVzL3BocC9pbmZvL2luaV9nZXQuanMiLCJ3ZWJwYWNrOi8vLy4vbm9kZV9tb2R1bGVzL2xvY3V0dXMvcGhwL3N0cmluZ3Mvc3RybGVuLmpzIiwid2VicGFjazovLy8uL25vZGVfbW9kdWxlcy9sb2N1dHVzL3BocC92YXIvaXNfbnVtZXJpYy5qcyIsIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vL3dlYnBhY2svcnVudGltZS9jb21wYXQgZ2V0IGRlZmF1bHQgZXhwb3J0Iiwid2VicGFjazovLy93ZWJwYWNrL3J1bnRpbWUvZGVmaW5lIHByb3BlcnR5IGdldHRlcnMiLCJ3ZWJwYWNrOi8vL3dlYnBhY2svcnVudGltZS9nbG9iYWwiLCJ3ZWJwYWNrOi8vL3dlYnBhY2svcnVudGltZS9oYXNPd25Qcm9wZXJ0eSBzaG9ydGhhbmQiLCJ3ZWJwYWNrOi8vL3dlYnBhY2svcnVudGltZS9tYWtlIG5hbWVzcGFjZSBvYmplY3QiLCJ3ZWJwYWNrOi8vLy4vcmVzb3VyY2VzL2Fzc2V0cy9qcy9oZWxwZXJzLmpzIl0sInNvdXJjZXNDb250ZW50IjpbIid1c2Ugc3RyaWN0JztcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiBhcnJheV9kaWZmKGFycjEpIHtcbiAgLy8gIGRpc2N1c3MgYXQ6IGh0dHBzOi8vbG9jdXR1cy5pby9waHAvYXJyYXlfZGlmZi9cbiAgLy8gb3JpZ2luYWwgYnk6IEtldmluIHZhbiBab25uZXZlbGQgKGh0dHBzOi8va3Z6LmlvKVxuICAvLyBpbXByb3ZlZCBieTogU2Fuam95IFJveVxuICAvLyAgcmV2aXNlZCBieTogQnJldHQgWmFtaXIgKGh0dHBzOi8vYnJldHQtemFtaXIubWUpXG4gIC8vICAgZXhhbXBsZSAxOiBhcnJheV9kaWZmKFsnS2V2aW4nLCAndmFuJywgJ1pvbm5ldmVsZCddLCBbJ3ZhbicsICdab25uZXZlbGQnXSlcbiAgLy8gICByZXR1cm5zIDE6IHswOidLZXZpbid9XG5cbiAgdmFyIHJldEFyciA9IHt9O1xuICB2YXIgYXJnbCA9IGFyZ3VtZW50cy5sZW5ndGg7XG4gIHZhciBrMSA9ICcnO1xuICB2YXIgaSA9IDE7XG4gIHZhciBrID0gJyc7XG4gIHZhciBhcnIgPSB7fTtcblxuICBhcnIxa2V5czogZm9yIChrMSBpbiBhcnIxKSB7XG4gICAgZm9yIChpID0gMTsgaSA8IGFyZ2w7IGkrKykge1xuICAgICAgYXJyID0gYXJndW1lbnRzW2ldO1xuICAgICAgZm9yIChrIGluIGFycikge1xuICAgICAgICBpZiAoYXJyW2tdID09PSBhcnIxW2sxXSkge1xuICAgICAgICAgIC8vIElmIGl0IHJlYWNoZXMgaGVyZSwgaXQgd2FzIGZvdW5kIGluIGF0IGxlYXN0IG9uZSBhcnJheSwgc28gdHJ5IG5leHQgdmFsdWVcbiAgICAgICAgICBjb250aW51ZSBhcnIxa2V5czsgLy8gZXNsaW50LWRpc2FibGUtbGluZSBuby1sYWJlbHNcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgcmV0QXJyW2sxXSA9IGFycjFbazFdO1xuICAgIH1cbiAgfVxuXG4gIHJldHVybiByZXRBcnI7XG59O1xuLy8jIHNvdXJjZU1hcHBpbmdVUkw9YXJyYXlfZGlmZi5qcy5tYXAiLCIndXNlIHN0cmljdCc7XG5cbnZhciByZVNwYWNlID0gJ1sgXFxcXHRdKyc7XG52YXIgcmVTcGFjZU9wdCA9ICdbIFxcXFx0XSonO1xudmFyIHJlTWVyaWRpYW4gPSAnKD86KFthcF0pXFxcXC4/bVxcXFwuPyhbXFxcXHQgXXwkKSknO1xudmFyIHJlSG91cjI0ID0gJygyWzAtNF18WzAxXT9bMC05XSknO1xudmFyIHJlSG91cjI0bHogPSAnKFswMV1bMC05XXwyWzAtNF0pJztcbnZhciByZUhvdXIxMiA9ICcoMD9bMS05XXwxWzAtMl0pJztcbnZhciByZU1pbnV0ZSA9ICcoWzAtNV0/WzAtOV0pJztcbnZhciByZU1pbnV0ZWx6ID0gJyhbMC01XVswLTldKSc7XG52YXIgcmVTZWNvbmQgPSAnKDYwfFswLTVdP1swLTldKSc7XG52YXIgcmVTZWNvbmRseiA9ICcoNjB8WzAtNV1bMC05XSknO1xudmFyIHJlRnJhYyA9ICcoPzpcXFxcLihbMC05XSspKSc7XG5cbnZhciByZURheWZ1bGwgPSAnc3VuZGF5fG1vbmRheXx0dWVzZGF5fHdlZG5lc2RheXx0aHVyc2RheXxmcmlkYXl8c2F0dXJkYXknO1xudmFyIHJlRGF5YWJiciA9ICdzdW58bW9ufHR1ZXx3ZWR8dGh1fGZyaXxzYXQnO1xudmFyIHJlRGF5dGV4dCA9IHJlRGF5ZnVsbCArICd8JyArIHJlRGF5YWJiciArICd8d2Vla2RheXM/JztcblxudmFyIHJlUmVsdGV4dG51bWJlciA9ICdmaXJzdHxzZWNvbmR8dGhpcmR8Zm91cnRofGZpZnRofHNpeHRofHNldmVudGh8ZWlnaHRoP3xuaW50aHx0ZW50aHxlbGV2ZW50aHx0d2VsZnRoJztcbnZhciByZVJlbHRleHR0ZXh0ID0gJ25leHR8bGFzdHxwcmV2aW91c3x0aGlzJztcbnZhciByZVJlbHRleHR1bml0ID0gJyg/OnNlY29uZHxzZWN8bWludXRlfG1pbnxob3VyfGRheXxmb3J0bmlnaHR8Zm9ydGhuaWdodHxtb250aHx5ZWFyKXM/fHdlZWtzfCcgKyByZURheXRleHQ7XG5cbnZhciByZVllYXIgPSAnKFswLTldezEsNH0pJztcbnZhciByZVllYXIyID0gJyhbMC05XXsyfSknO1xudmFyIHJlWWVhcjQgPSAnKFswLTldezR9KSc7XG52YXIgcmVZZWFyNHdpdGhTaWduID0gJyhbKy1dP1swLTldezR9KSc7XG52YXIgcmVNb250aCA9ICcoMVswLTJdfDA/WzAtOV0pJztcbnZhciByZU1vbnRobHogPSAnKDBbMC05XXwxWzAtMl0pJztcbnZhciByZURheSA9ICcoPzooM1swMV18WzAtMl0/WzAtOV0pKD86c3R8bmR8cmR8dGgpPyknO1xudmFyIHJlRGF5bHogPSAnKDBbMC05XXxbMS0yXVswLTldfDNbMDFdKSc7XG5cbnZhciByZU1vbnRoRnVsbCA9ICdqYW51YXJ5fGZlYnJ1YXJ5fG1hcmNofGFwcmlsfG1heXxqdW5lfGp1bHl8YXVndXN0fHNlcHRlbWJlcnxvY3RvYmVyfG5vdmVtYmVyfGRlY2VtYmVyJztcbnZhciByZU1vbnRoQWJiciA9ICdqYW58ZmVifG1hcnxhcHJ8bWF5fGp1bnxqdWx8YXVnfHNlcHQ/fG9jdHxub3Z8ZGVjJztcbnZhciByZU1vbnRocm9tYW4gPSAnaVt2eF18dml7MCwzfXx4aXswLDJ9fGl7MSwzfSc7XG52YXIgcmVNb250aFRleHQgPSAnKCcgKyByZU1vbnRoRnVsbCArICd8JyArIHJlTW9udGhBYmJyICsgJ3wnICsgcmVNb250aHJvbWFuICsgJyknO1xuXG52YXIgcmVUekNvcnJlY3Rpb24gPSAnKCg/OkdNVCk/KFsrLV0pJyArIHJlSG91cjI0ICsgJzo/JyArIHJlTWludXRlICsgJz8pJztcbnZhciByZVR6QWJiciA9ICdcXFxcKD8oW2EtekEtWl17MSw2fSlcXFxcKT8nO1xudmFyIHJlRGF5T2ZZZWFyID0gJygwMFsxLTldfDBbMS05XVswLTldfFsxMl1bMC05XVswLTldfDNbMC01XVswLTldfDM2WzAtNl0pJztcbnZhciByZVdlZWtPZlllYXIgPSAnKDBbMS05XXxbMS00XVswLTldfDVbMC0zXSknO1xuXG52YXIgcmVEYXRlTm9ZZWFyID0gcmVNb250aFRleHQgKyAnWyAuXFxcXHQtXSonICsgcmVEYXkgKyAnWywuc3RuZHJoXFxcXHQgXSonO1xuXG5mdW5jdGlvbiBwcm9jZXNzTWVyaWRpYW4oaG91ciwgbWVyaWRpYW4pIHtcbiAgbWVyaWRpYW4gPSBtZXJpZGlhbiAmJiBtZXJpZGlhbi50b0xvd2VyQ2FzZSgpO1xuXG4gIHN3aXRjaCAobWVyaWRpYW4pIHtcbiAgICBjYXNlICdhJzpcbiAgICAgIGhvdXIgKz0gaG91ciA9PT0gMTIgPyAtMTIgOiAwO1xuICAgICAgYnJlYWs7XG4gICAgY2FzZSAncCc6XG4gICAgICBob3VyICs9IGhvdXIgIT09IDEyID8gMTIgOiAwO1xuICAgICAgYnJlYWs7XG4gIH1cblxuICByZXR1cm4gaG91cjtcbn1cblxuZnVuY3Rpb24gcHJvY2Vzc1llYXIoeWVhclN0cikge1xuICB2YXIgeWVhciA9ICt5ZWFyU3RyO1xuXG4gIGlmICh5ZWFyU3RyLmxlbmd0aCA8IDQgJiYgeWVhciA8IDEwMCkge1xuICAgIHllYXIgKz0geWVhciA8IDcwID8gMjAwMCA6IDE5MDA7XG4gIH1cblxuICByZXR1cm4geWVhcjtcbn1cblxuZnVuY3Rpb24gbG9va3VwTW9udGgobW9udGhTdHIpIHtcbiAgcmV0dXJuIHtcbiAgICBqYW46IDAsXG4gICAgamFudWFyeTogMCxcbiAgICBpOiAwLFxuICAgIGZlYjogMSxcbiAgICBmZWJydWFyeTogMSxcbiAgICBpaTogMSxcbiAgICBtYXI6IDIsXG4gICAgbWFyY2g6IDIsXG4gICAgaWlpOiAyLFxuICAgIGFwcjogMyxcbiAgICBhcHJpbDogMyxcbiAgICBpdjogMyxcbiAgICBtYXk6IDQsXG4gICAgdjogNCxcbiAgICBqdW46IDUsXG4gICAganVuZTogNSxcbiAgICB2aTogNSxcbiAgICBqdWw6IDYsXG4gICAganVseTogNixcbiAgICB2aWk6IDYsXG4gICAgYXVnOiA3LFxuICAgIGF1Z3VzdDogNyxcbiAgICB2aWlpOiA3LFxuICAgIHNlcDogOCxcbiAgICBzZXB0OiA4LFxuICAgIHNlcHRlbWJlcjogOCxcbiAgICBpeDogOCxcbiAgICBvY3Q6IDksXG4gICAgb2N0b2JlcjogOSxcbiAgICB4OiA5LFxuICAgIG5vdjogMTAsXG4gICAgbm92ZW1iZXI6IDEwLFxuICAgIHhpOiAxMCxcbiAgICBkZWM6IDExLFxuICAgIGRlY2VtYmVyOiAxMSxcbiAgICB4aWk6IDExXG4gIH1bbW9udGhTdHIudG9Mb3dlckNhc2UoKV07XG59XG5cbmZ1bmN0aW9uIGxvb2t1cFdlZWtkYXkoZGF5U3RyKSB7XG4gIHZhciBkZXNpcmVkU3VuZGF5TnVtYmVyID0gYXJndW1lbnRzLmxlbmd0aCA+IDEgJiYgYXJndW1lbnRzWzFdICE9PSB1bmRlZmluZWQgPyBhcmd1bWVudHNbMV0gOiAwO1xuXG4gIHZhciBkYXlOdW1iZXJzID0ge1xuICAgIG1vbjogMSxcbiAgICBtb25kYXk6IDEsXG4gICAgdHVlOiAyLFxuICAgIHR1ZXNkYXk6IDIsXG4gICAgd2VkOiAzLFxuICAgIHdlZG5lc2RheTogMyxcbiAgICB0aHU6IDQsXG4gICAgdGh1cnNkYXk6IDQsXG4gICAgZnJpOiA1LFxuICAgIGZyaWRheTogNSxcbiAgICBzYXQ6IDYsXG4gICAgc2F0dXJkYXk6IDYsXG4gICAgc3VuOiAwLFxuICAgIHN1bmRheTogMFxuICB9O1xuXG4gIHJldHVybiBkYXlOdW1iZXJzW2RheVN0ci50b0xvd2VyQ2FzZSgpXSB8fCBkZXNpcmVkU3VuZGF5TnVtYmVyO1xufVxuXG5mdW5jdGlvbiBsb29rdXBSZWxhdGl2ZShyZWxUZXh0KSB7XG4gIHZhciByZWxhdGl2ZU51bWJlcnMgPSB7XG4gICAgbGFzdDogLTEsXG4gICAgcHJldmlvdXM6IC0xLFxuICAgIHRoaXM6IDAsXG4gICAgZmlyc3Q6IDEsXG4gICAgbmV4dDogMSxcbiAgICBzZWNvbmQ6IDIsXG4gICAgdGhpcmQ6IDMsXG4gICAgZm91cnRoOiA0LFxuICAgIGZpZnRoOiA1LFxuICAgIHNpeHRoOiA2LFxuICAgIHNldmVudGg6IDcsXG4gICAgZWlnaHQ6IDgsXG4gICAgZWlnaHRoOiA4LFxuICAgIG5pbnRoOiA5LFxuICAgIHRlbnRoOiAxMCxcbiAgICBlbGV2ZW50aDogMTEsXG4gICAgdHdlbGZ0aDogMTJcbiAgfTtcblxuICB2YXIgcmVsYXRpdmVCZWhhdmlvciA9IHtcbiAgICB0aGlzOiAxXG4gIH07XG5cbiAgdmFyIHJlbFRleHRMb3dlciA9IHJlbFRleHQudG9Mb3dlckNhc2UoKTtcblxuICByZXR1cm4ge1xuICAgIGFtb3VudDogcmVsYXRpdmVOdW1iZXJzW3JlbFRleHRMb3dlcl0sXG4gICAgYmVoYXZpb3I6IHJlbGF0aXZlQmVoYXZpb3JbcmVsVGV4dExvd2VyXSB8fCAwXG4gIH07XG59XG5cbmZ1bmN0aW9uIHByb2Nlc3NUekNvcnJlY3Rpb24odHpPZmZzZXQsIG9sZFZhbHVlKSB7XG4gIHZhciByZVR6Q29ycmVjdGlvbkxvb3NlID0gLyg/OkdNVCk/KFsrLV0pKFxcZCspKDo/KShcXGR7MCwyfSkvaTtcbiAgdHpPZmZzZXQgPSB0ek9mZnNldCAmJiB0ek9mZnNldC5tYXRjaChyZVR6Q29ycmVjdGlvbkxvb3NlKTtcblxuICBpZiAoIXR6T2Zmc2V0KSB7XG4gICAgcmV0dXJuIG9sZFZhbHVlO1xuICB9XG5cbiAgdmFyIHNpZ24gPSB0ek9mZnNldFsxXSA9PT0gJy0nID8gLTEgOiAxO1xuICB2YXIgaG91cnMgPSArdHpPZmZzZXRbMl07XG4gIHZhciBtaW51dGVzID0gK3R6T2Zmc2V0WzRdO1xuXG4gIGlmICghdHpPZmZzZXRbNF0gJiYgIXR6T2Zmc2V0WzNdKSB7XG4gICAgbWludXRlcyA9IE1hdGguZmxvb3IoaG91cnMgJSAxMDApO1xuICAgIGhvdXJzID0gTWF0aC5mbG9vcihob3VycyAvIDEwMCk7XG4gIH1cblxuICAvLyB0aW1lem9uZSBvZmZzZXQgaW4gc2Vjb25kc1xuICByZXR1cm4gc2lnbiAqIChob3VycyAqIDYwICsgbWludXRlcykgKiA2MDtcbn1cblxuLy8gdHogYWJicmV2YXRpb24gOiB0eiBvZmZzZXQgaW4gc2Vjb25kc1xudmFyIHR6QWJick9mZnNldHMgPSB7XG4gIGFjZHQ6IDM3ODAwLFxuICBhY3N0OiAzNDIwMCxcbiAgYWRkdDogLTcyMDAsXG4gIGFkdDogLTEwODAwLFxuICBhZWR0OiAzOTYwMCxcbiAgYWVzdDogMzYwMDAsXG4gIGFoZHQ6IC0zMjQwMCxcbiAgYWhzdDogLTM2MDAwLFxuICBha2R0OiAtMjg4MDAsXG4gIGFrc3Q6IC0zMjQwMCxcbiAgYW10OiAtMTM4NDAsXG4gIGFwdDogLTEwODAwLFxuICBhc3Q6IC0xNDQwMCxcbiAgYXdkdDogMzI0MDAsXG4gIGF3c3Q6IDI4ODAwLFxuICBhd3Q6IC0xMDgwMCxcbiAgYmRzdDogNzIwMCxcbiAgYmR0OiAtMzYwMDAsXG4gIGJtdDogLTE0MzA5LFxuICBic3Q6IDM2MDAsXG4gIGNhc3Q6IDM0MjAwLFxuICBjYXQ6IDcyMDAsXG4gIGNkZHQ6IC0xNDQwMCxcbiAgY2R0OiAtMTgwMDAsXG4gIGNlbXQ6IDEwODAwLFxuICBjZXN0OiA3MjAwLFxuICBjZXQ6IDM2MDAsXG4gIGNtdDogLTE1NDA4LFxuICBjcHQ6IC0xODAwMCxcbiAgY3N0OiAtMjE2MDAsXG4gIGN3dDogLTE4MDAwLFxuICBjaHN0OiAzNjAwMCxcbiAgZG10OiAtMTUyMSxcbiAgZWF0OiAxMDgwMCxcbiAgZWRkdDogLTEwODAwLFxuICBlZHQ6IC0xNDQwMCxcbiAgZWVzdDogMTA4MDAsXG4gIGVldDogNzIwMCxcbiAgZW10OiAtMjYyNDgsXG4gIGVwdDogLTE0NDAwLFxuICBlc3Q6IC0xODAwMCxcbiAgZXd0OiAtMTQ0MDAsXG4gIGZmbXQ6IC0xNDY2MCxcbiAgZm10OiAtNDA1NixcbiAgZ2R0OiAzOTYwMCxcbiAgZ210OiAwLFxuICBnc3Q6IDM2MDAwLFxuICBoZHQ6IC0zNDIwMCxcbiAgaGtzdDogMzI0MDAsXG4gIGhrdDogMjg4MDAsXG4gIGhtdDogLTE5Nzc2LFxuICBocHQ6IC0zNDIwMCxcbiAgaHN0OiAtMzYwMDAsXG4gIGh3dDogLTM0MjAwLFxuICBpZGR0OiAxNDQwMCxcbiAgaWR0OiAxMDgwMCxcbiAgaW10OiAyNTAyNSxcbiAgaXN0OiA3MjAwLFxuICBqZHQ6IDM2MDAwLFxuICBqbXQ6IDg0NDAsXG4gIGpzdDogMzI0MDAsXG4gIGtkdDogMzYwMDAsXG4gIGttdDogNTczNixcbiAga3N0OiAzMDYwMCxcbiAgbHN0OiA5Mzk0LFxuICBtZGR0OiAtMTgwMDAsXG4gIG1kc3Q6IDE2Mjc5LFxuICBtZHQ6IC0yMTYwMCxcbiAgbWVzdDogNzIwMCxcbiAgbWV0OiAzNjAwLFxuICBtbXQ6IDkwMTcsXG4gIG1wdDogLTIxNjAwLFxuICBtc2Q6IDE0NDAwLFxuICBtc2s6IDEwODAwLFxuICBtc3Q6IC0yNTIwMCxcbiAgbXd0OiAtMjE2MDAsXG4gIG5kZHQ6IC01NDAwLFxuICBuZHQ6IC05MDUyLFxuICBucHQ6IC05MDAwLFxuICBuc3Q6IC0xMjYwMCxcbiAgbnd0OiAtOTAwMCxcbiAgbnpkdDogNDY4MDAsXG4gIG56bXQ6IDQxNDAwLFxuICBuenN0OiA0MzIwMCxcbiAgcGRkdDogLTIxNjAwLFxuICBwZHQ6IC0yNTIwMCxcbiAgcGtzdDogMjE2MDAsXG4gIHBrdDogMTgwMDAsXG4gIHBsbXQ6IDI1NTkwLFxuICBwbXQ6IC0xMzIzNixcbiAgcHBtdDogLTE3MzQwLFxuICBwcHQ6IC0yNTIwMCxcbiAgcHN0OiAtMjg4MDAsXG4gIHB3dDogLTI1MjAwLFxuICBxbXQ6IC0xODg0MCxcbiAgcm10OiA1Nzk0LFxuICBzYXN0OiA3MjAwLFxuICBzZG10OiAtMTY4MDAsXG4gIHNqbXQ6IC0yMDE3MyxcbiAgc210OiAtMTM4ODQsXG4gIHNzdDogLTM5NjAwLFxuICB0Ym10OiAxMDc1MSxcbiAgdG10OiAxMjM0NCxcbiAgdWN0OiAwLFxuICB1dGM6IDAsXG4gIHdhc3Q6IDcyMDAsXG4gIHdhdDogMzYwMCxcbiAgd2VtdDogNzIwMCxcbiAgd2VzdDogMzYwMCxcbiAgd2V0OiAwLFxuICB3aWI6IDI1MjAwLFxuICB3aXRhOiAyODgwMCxcbiAgd2l0OiAzMjQwMCxcbiAgd210OiA1MDQwLFxuICB5ZGR0OiAtMjUyMDAsXG4gIHlkdDogLTI4ODAwLFxuICB5cHQ6IC0yODgwMCxcbiAgeXN0OiAtMzI0MDAsXG4gIHl3dDogLTI4ODAwLFxuICBhOiAzNjAwLFxuICBiOiA3MjAwLFxuICBjOiAxMDgwMCxcbiAgZDogMTQ0MDAsXG4gIGU6IDE4MDAwLFxuICBmOiAyMTYwMCxcbiAgZzogMjUyMDAsXG4gIGg6IDI4ODAwLFxuICBpOiAzMjQwMCxcbiAgazogMzYwMDAsXG4gIGw6IDM5NjAwLFxuICBtOiA0MzIwMCxcbiAgbjogLTM2MDAsXG4gIG86IC03MjAwLFxuICBwOiAtMTA4MDAsXG4gIHE6IC0xNDQwMCxcbiAgcjogLTE4MDAwLFxuICBzOiAtMjE2MDAsXG4gIHQ6IC0yNTIwMCxcbiAgdTogLTI4ODAwLFxuICB2OiAtMzI0MDAsXG4gIHc6IC0zNjAwMCxcbiAgeDogLTM5NjAwLFxuICB5OiAtNDMyMDAsXG4gIHo6IDBcbn07XG5cbnZhciBmb3JtYXRzID0ge1xuICB5ZXN0ZXJkYXk6IHtcbiAgICByZWdleDogL155ZXN0ZXJkYXkvaSxcbiAgICBuYW1lOiAneWVzdGVyZGF5JyxcbiAgICBjYWxsYmFjazogZnVuY3Rpb24gY2FsbGJhY2soKSB7XG4gICAgICB0aGlzLnJkIC09IDE7XG4gICAgICByZXR1cm4gdGhpcy5yZXNldFRpbWUoKTtcbiAgICB9XG4gIH0sXG5cbiAgbm93OiB7XG4gICAgcmVnZXg6IC9ebm93L2ksXG4gICAgbmFtZTogJ25vdydcbiAgICAvLyBkbyBub3RoaW5nXG4gIH0sXG5cbiAgbm9vbjoge1xuICAgIHJlZ2V4OiAvXm5vb24vaSxcbiAgICBuYW1lOiAnbm9vbicsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKCkge1xuICAgICAgcmV0dXJuIHRoaXMucmVzZXRUaW1lKCkgJiYgdGhpcy50aW1lKDEyLCAwLCAwLCAwKTtcbiAgICB9XG4gIH0sXG5cbiAgbWlkbmlnaHRPclRvZGF5OiB7XG4gICAgcmVnZXg6IC9eKG1pZG5pZ2h0fHRvZGF5KS9pLFxuICAgIG5hbWU6ICdtaWRuaWdodCB8IHRvZGF5JyxcbiAgICBjYWxsYmFjazogZnVuY3Rpb24gY2FsbGJhY2soKSB7XG4gICAgICByZXR1cm4gdGhpcy5yZXNldFRpbWUoKTtcbiAgICB9XG4gIH0sXG5cbiAgdG9tb3Jyb3c6IHtcbiAgICByZWdleDogL150b21vcnJvdy9pLFxuICAgIG5hbWU6ICd0b21vcnJvdycsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKCkge1xuICAgICAgdGhpcy5yZCArPSAxO1xuICAgICAgcmV0dXJuIHRoaXMucmVzZXRUaW1lKCk7XG4gICAgfVxuICB9LFxuXG4gIHRpbWVzdGFtcDoge1xuICAgIHJlZ2V4OiAvXkAoLT9cXGQrKS9pLFxuICAgIG5hbWU6ICd0aW1lc3RhbXAnLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgdGltZXN0YW1wKSB7XG4gICAgICB0aGlzLnJzICs9ICt0aW1lc3RhbXA7XG4gICAgICB0aGlzLnkgPSAxOTcwO1xuICAgICAgdGhpcy5tID0gMDtcbiAgICAgIHRoaXMuZCA9IDE7XG4gICAgICB0aGlzLmRhdGVzID0gMDtcblxuICAgICAgcmV0dXJuIHRoaXMucmVzZXRUaW1lKCkgJiYgdGhpcy56b25lKDApO1xuICAgIH1cbiAgfSxcblxuICBmaXJzdE9yTGFzdERheToge1xuICAgIHJlZ2V4OiAvXihmaXJzdHxsYXN0KSBkYXkgb2YvaSxcbiAgICBuYW1lOiAnZmlyc3RkYXlvZiB8IGxhc3RkYXlvZicsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCBkYXkpIHtcbiAgICAgIGlmIChkYXkudG9Mb3dlckNhc2UoKSA9PT0gJ2ZpcnN0Jykge1xuICAgICAgICB0aGlzLmZpcnN0T3JMYXN0RGF5T2ZNb250aCA9IDE7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLmZpcnN0T3JMYXN0RGF5T2ZNb250aCA9IC0xO1xuICAgICAgfVxuICAgIH1cbiAgfSxcblxuICBiYWNrT3JGcm9udE9mOiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXihiYWNrfGZyb250KSBvZiAnICsgcmVIb3VyMjQgKyByZVNwYWNlT3B0ICsgcmVNZXJpZGlhbiArICc/JywgJ2knKSxcbiAgICBuYW1lOiAnYmFja29mIHwgZnJvbnRvZicsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCBzaWRlLCBob3VycywgbWVyaWRpYW4pIHtcbiAgICAgIHZhciBiYWNrID0gc2lkZS50b0xvd2VyQ2FzZSgpID09PSAnYmFjayc7XG4gICAgICB2YXIgaG91ciA9ICtob3VycztcbiAgICAgIHZhciBtaW51dGUgPSAxNTtcblxuICAgICAgaWYgKCFiYWNrKSB7XG4gICAgICAgIGhvdXIgLT0gMTtcbiAgICAgICAgbWludXRlID0gNDU7XG4gICAgICB9XG5cbiAgICAgIGhvdXIgPSBwcm9jZXNzTWVyaWRpYW4oaG91ciwgbWVyaWRpYW4pO1xuXG4gICAgICByZXR1cm4gdGhpcy5yZXNldFRpbWUoKSAmJiB0aGlzLnRpbWUoaG91ciwgbWludXRlLCAwLCAwKTtcbiAgICB9XG4gIH0sXG5cbiAgd2Vla2RheU9mOiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXignICsgcmVSZWx0ZXh0bnVtYmVyICsgJ3wnICsgcmVSZWx0ZXh0dGV4dCArICcpJyArIHJlU3BhY2UgKyAnKCcgKyByZURheWZ1bGwgKyAnfCcgKyByZURheWFiYnIgKyAnKScgKyByZVNwYWNlICsgJ29mJywgJ2knKSxcbiAgICBuYW1lOiAnd2Vla2RheW9mJ1xuICAgIC8vIHRvZG9cbiAgfSxcblxuICBtc3NxbHRpbWU6IHtcbiAgICByZWdleDogUmVnRXhwKCdeJyArIHJlSG91cjEyICsgJzonICsgcmVNaW51dGVseiArICc6JyArIHJlU2Vjb25kbHogKyAnWzouXShbMC05XSspJyArIHJlTWVyaWRpYW4sICdpJyksXG4gICAgbmFtZTogJ21zc3FsdGltZScsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCBob3VyLCBtaW51dGUsIHNlY29uZCwgZnJhYywgbWVyaWRpYW4pIHtcbiAgICAgIHJldHVybiB0aGlzLnRpbWUocHJvY2Vzc01lcmlkaWFuKCtob3VyLCBtZXJpZGlhbiksICttaW51dGUsICtzZWNvbmQsICtmcmFjLnN1YnN0cigwLCAzKSk7XG4gICAgfVxuICB9LFxuXG4gIG9yYWNsZWRhdGU6IHtcbiAgICByZWdleDogL14oXFxkezJ9KS0oW0EtWl17M30pLShcXGR7Mn0pJC9pLFxuICAgIG5hbWU6ICdkLU0teScsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCBkYXksIG1vbnRoVGV4dCwgeWVhcikge1xuICAgICAgdmFyIG1vbnRoID0ge1xuICAgICAgICBKQU46IDAsXG4gICAgICAgIEZFQjogMSxcbiAgICAgICAgTUFSOiAyLFxuICAgICAgICBBUFI6IDMsXG4gICAgICAgIE1BWTogNCxcbiAgICAgICAgSlVOOiA1LFxuICAgICAgICBKVUw6IDYsXG4gICAgICAgIEFVRzogNyxcbiAgICAgICAgU0VQOiA4LFxuICAgICAgICBPQ1Q6IDksXG4gICAgICAgIE5PVjogMTAsXG4gICAgICAgIERFQzogMTFcbiAgICAgIH1bbW9udGhUZXh0LnRvVXBwZXJDYXNlKCldO1xuICAgICAgcmV0dXJuIHRoaXMueW1kKDIwMDAgKyBwYXJzZUludCh5ZWFyLCAxMCksIG1vbnRoLCBwYXJzZUludChkYXksIDEwKSk7XG4gICAgfVxuICB9LFxuXG4gIHRpbWVMb25nMTI6IHtcbiAgICByZWdleDogUmVnRXhwKCdeJyArIHJlSG91cjEyICsgJ1s6Ll0nICsgcmVNaW51dGUgKyAnWzouXScgKyByZVNlY29uZGx6ICsgcmVTcGFjZU9wdCArIHJlTWVyaWRpYW4sICdpJyksXG4gICAgbmFtZTogJ3RpbWVsb25nMTInLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgaG91ciwgbWludXRlLCBzZWNvbmQsIG1lcmlkaWFuKSB7XG4gICAgICByZXR1cm4gdGhpcy50aW1lKHByb2Nlc3NNZXJpZGlhbigraG91ciwgbWVyaWRpYW4pLCArbWludXRlLCArc2Vjb25kLCAwKTtcbiAgICB9XG4gIH0sXG5cbiAgdGltZVNob3J0MTI6IHtcbiAgICByZWdleDogUmVnRXhwKCdeJyArIHJlSG91cjEyICsgJ1s6Ll0nICsgcmVNaW51dGVseiArIHJlU3BhY2VPcHQgKyByZU1lcmlkaWFuLCAnaScpLFxuICAgIG5hbWU6ICd0aW1lc2hvcnQxMicsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCBob3VyLCBtaW51dGUsIG1lcmlkaWFuKSB7XG4gICAgICByZXR1cm4gdGhpcy50aW1lKHByb2Nlc3NNZXJpZGlhbigraG91ciwgbWVyaWRpYW4pLCArbWludXRlLCAwLCAwKTtcbiAgICB9XG4gIH0sXG5cbiAgdGltZVRpbnkxMjoge1xuICAgIHJlZ2V4OiBSZWdFeHAoJ14nICsgcmVIb3VyMTIgKyByZVNwYWNlT3B0ICsgcmVNZXJpZGlhbiwgJ2knKSxcbiAgICBuYW1lOiAndGltZXRpbnkxMicsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCBob3VyLCBtZXJpZGlhbikge1xuICAgICAgcmV0dXJuIHRoaXMudGltZShwcm9jZXNzTWVyaWRpYW4oK2hvdXIsIG1lcmlkaWFuKSwgMCwgMCwgMCk7XG4gICAgfVxuICB9LFxuXG4gIHNvYXA6IHtcbiAgICByZWdleDogUmVnRXhwKCdeJyArIHJlWWVhcjQgKyAnLScgKyByZU1vbnRobHogKyAnLScgKyByZURheWx6ICsgJ1QnICsgcmVIb3VyMjRseiArICc6JyArIHJlTWludXRlbHogKyAnOicgKyByZVNlY29uZGx6ICsgcmVGcmFjICsgcmVUekNvcnJlY3Rpb24gKyAnPycsICdpJyksXG4gICAgbmFtZTogJ3NvYXAnLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgeWVhciwgbW9udGgsIGRheSwgaG91ciwgbWludXRlLCBzZWNvbmQsIGZyYWMsIHR6Q29ycmVjdGlvbikge1xuICAgICAgcmV0dXJuIHRoaXMueW1kKCt5ZWFyLCBtb250aCAtIDEsICtkYXkpICYmIHRoaXMudGltZSgraG91ciwgK21pbnV0ZSwgK3NlY29uZCwgK2ZyYWMuc3Vic3RyKDAsIDMpKSAmJiB0aGlzLnpvbmUocHJvY2Vzc1R6Q29ycmVjdGlvbih0ekNvcnJlY3Rpb24pKTtcbiAgICB9XG4gIH0sXG5cbiAgd2RkeDoge1xuICAgIHJlZ2V4OiBSZWdFeHAoJ14nICsgcmVZZWFyNCArICctJyArIHJlTW9udGggKyAnLScgKyByZURheSArICdUJyArIHJlSG91cjI0ICsgJzonICsgcmVNaW51dGUgKyAnOicgKyByZVNlY29uZCksXG4gICAgbmFtZTogJ3dkZHgnLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgeWVhciwgbW9udGgsIGRheSwgaG91ciwgbWludXRlLCBzZWNvbmQpIHtcbiAgICAgIHJldHVybiB0aGlzLnltZCgreWVhciwgbW9udGggLSAxLCArZGF5KSAmJiB0aGlzLnRpbWUoK2hvdXIsICttaW51dGUsICtzZWNvbmQsIDApO1xuICAgIH1cbiAgfSxcblxuICBleGlmOiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyByZVllYXI0ICsgJzonICsgcmVNb250aGx6ICsgJzonICsgcmVEYXlseiArICcgJyArIHJlSG91cjI0bHogKyAnOicgKyByZU1pbnV0ZWx6ICsgJzonICsgcmVTZWNvbmRseiwgJ2knKSxcbiAgICBuYW1lOiAnZXhpZicsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCB5ZWFyLCBtb250aCwgZGF5LCBob3VyLCBtaW51dGUsIHNlY29uZCkge1xuICAgICAgcmV0dXJuIHRoaXMueW1kKCt5ZWFyLCBtb250aCAtIDEsICtkYXkpICYmIHRoaXMudGltZSgraG91ciwgK21pbnV0ZSwgK3NlY29uZCwgMCk7XG4gICAgfVxuICB9LFxuXG4gIHhtbFJwYzoge1xuICAgIHJlZ2V4OiBSZWdFeHAoJ14nICsgcmVZZWFyNCArIHJlTW9udGhseiArIHJlRGF5bHogKyAnVCcgKyByZUhvdXIyNCArICc6JyArIHJlTWludXRlbHogKyAnOicgKyByZVNlY29uZGx6KSxcbiAgICBuYW1lOiAneG1scnBjJyxcbiAgICBjYWxsYmFjazogZnVuY3Rpb24gY2FsbGJhY2sobWF0Y2gsIHllYXIsIG1vbnRoLCBkYXksIGhvdXIsIG1pbnV0ZSwgc2Vjb25kKSB7XG4gICAgICByZXR1cm4gdGhpcy55bWQoK3llYXIsIG1vbnRoIC0gMSwgK2RheSkgJiYgdGhpcy50aW1lKCtob3VyLCArbWludXRlLCArc2Vjb25kLCAwKTtcbiAgICB9XG4gIH0sXG5cbiAgeG1sUnBjTm9Db2xvbjoge1xuICAgIHJlZ2V4OiBSZWdFeHAoJ14nICsgcmVZZWFyNCArIHJlTW9udGhseiArIHJlRGF5bHogKyAnW1R0XScgKyByZUhvdXIyNCArIHJlTWludXRlbHogKyByZVNlY29uZGx6KSxcbiAgICBuYW1lOiAneG1scnBjbm9jb2xvbicsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCB5ZWFyLCBtb250aCwgZGF5LCBob3VyLCBtaW51dGUsIHNlY29uZCkge1xuICAgICAgcmV0dXJuIHRoaXMueW1kKCt5ZWFyLCBtb250aCAtIDEsICtkYXkpICYmIHRoaXMudGltZSgraG91ciwgK21pbnV0ZSwgK3NlY29uZCwgMCk7XG4gICAgfVxuICB9LFxuXG4gIGNsZjoge1xuICAgIHJlZ2V4OiBSZWdFeHAoJ14nICsgcmVEYXkgKyAnLygnICsgcmVNb250aEFiYnIgKyAnKS8nICsgcmVZZWFyNCArICc6JyArIHJlSG91cjI0bHogKyAnOicgKyByZU1pbnV0ZWx6ICsgJzonICsgcmVTZWNvbmRseiArIHJlU3BhY2UgKyByZVR6Q29ycmVjdGlvbiwgJ2knKSxcbiAgICBuYW1lOiAnY2xmJyxcbiAgICBjYWxsYmFjazogZnVuY3Rpb24gY2FsbGJhY2sobWF0Y2gsIGRheSwgbW9udGgsIHllYXIsIGhvdXIsIG1pbnV0ZSwgc2Vjb25kLCB0ekNvcnJlY3Rpb24pIHtcbiAgICAgIHJldHVybiB0aGlzLnltZCgreWVhciwgbG9va3VwTW9udGgobW9udGgpLCArZGF5KSAmJiB0aGlzLnRpbWUoK2hvdXIsICttaW51dGUsICtzZWNvbmQsIDApICYmIHRoaXMuem9uZShwcm9jZXNzVHpDb3JyZWN0aW9uKHR6Q29ycmVjdGlvbikpO1xuICAgIH1cbiAgfSxcblxuICBpc284NjAxbG9uZzoge1xuICAgIHJlZ2V4OiBSZWdFeHAoJ150PycgKyByZUhvdXIyNCArICdbOi5dJyArIHJlTWludXRlICsgJ1s6Ll0nICsgcmVTZWNvbmQgKyByZUZyYWMsICdpJyksXG4gICAgbmFtZTogJ2lzbzg2MDFsb25nJyxcbiAgICBjYWxsYmFjazogZnVuY3Rpb24gY2FsbGJhY2sobWF0Y2gsIGhvdXIsIG1pbnV0ZSwgc2Vjb25kLCBmcmFjKSB7XG4gICAgICByZXR1cm4gdGhpcy50aW1lKCtob3VyLCArbWludXRlLCArc2Vjb25kLCArZnJhYy5zdWJzdHIoMCwgMykpO1xuICAgIH1cbiAgfSxcblxuICBkYXRlVGV4dHVhbDoge1xuICAgIHJlZ2V4OiBSZWdFeHAoJ14nICsgcmVNb250aFRleHQgKyAnWyAuXFxcXHQtXSonICsgcmVEYXkgKyAnWywuc3RuZHJoXFxcXHQgXSsnICsgcmVZZWFyLCAnaScpLFxuICAgIG5hbWU6ICdkYXRldGV4dHVhbCcsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCBtb250aCwgZGF5LCB5ZWFyKSB7XG4gICAgICByZXR1cm4gdGhpcy55bWQocHJvY2Vzc1llYXIoeWVhciksIGxvb2t1cE1vbnRoKG1vbnRoKSwgK2RheSk7XG4gICAgfVxuICB9LFxuXG4gIHBvaW50ZWREYXRlNDoge1xuICAgIHJlZ2V4OiBSZWdFeHAoJ14nICsgcmVEYXkgKyAnWy5cXFxcdC1dJyArIHJlTW9udGggKyAnWy4tXScgKyByZVllYXI0KSxcbiAgICBuYW1lOiAncG9pbnRlZGRhdGU0JyxcbiAgICBjYWxsYmFjazogZnVuY3Rpb24gY2FsbGJhY2sobWF0Y2gsIGRheSwgbW9udGgsIHllYXIpIHtcbiAgICAgIHJldHVybiB0aGlzLnltZCgreWVhciwgbW9udGggLSAxLCArZGF5KTtcbiAgICB9XG4gIH0sXG5cbiAgcG9pbnRlZERhdGUyOiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyByZURheSArICdbLlxcXFx0XScgKyByZU1vbnRoICsgJ1xcXFwuJyArIHJlWWVhcjIpLFxuICAgIG5hbWU6ICdwb2ludGVkZGF0ZTInLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgZGF5LCBtb250aCwgeWVhcikge1xuICAgICAgcmV0dXJuIHRoaXMueW1kKHByb2Nlc3NZZWFyKHllYXIpLCBtb250aCAtIDEsICtkYXkpO1xuICAgIH1cbiAgfSxcblxuICB0aW1lTG9uZzI0OiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXnQ/JyArIHJlSG91cjI0ICsgJ1s6Ll0nICsgcmVNaW51dGUgKyAnWzouXScgKyByZVNlY29uZCksXG4gICAgbmFtZTogJ3RpbWVsb25nMjQnLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgaG91ciwgbWludXRlLCBzZWNvbmQpIHtcbiAgICAgIHJldHVybiB0aGlzLnRpbWUoK2hvdXIsICttaW51dGUsICtzZWNvbmQsIDApO1xuICAgIH1cbiAgfSxcblxuICBkYXRlTm9Db2xvbjoge1xuICAgIHJlZ2V4OiBSZWdFeHAoJ14nICsgcmVZZWFyNCArIHJlTW9udGhseiArIHJlRGF5bHopLFxuICAgIG5hbWU6ICdkYXRlbm9jb2xvbicsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCB5ZWFyLCBtb250aCwgZGF5KSB7XG4gICAgICByZXR1cm4gdGhpcy55bWQoK3llYXIsIG1vbnRoIC0gMSwgK2RheSk7XG4gICAgfVxuICB9LFxuXG4gIHBneWRvdGQ6IHtcbiAgICByZWdleDogUmVnRXhwKCdeJyArIHJlWWVhcjQgKyAnXFxcXC4/JyArIHJlRGF5T2ZZZWFyKSxcbiAgICBuYW1lOiAncGd5ZG90ZCcsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCB5ZWFyLCBkYXkpIHtcbiAgICAgIHJldHVybiB0aGlzLnltZCgreWVhciwgMCwgK2RheSk7XG4gICAgfVxuICB9LFxuXG4gIHRpbWVTaG9ydDI0OiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXnQ/JyArIHJlSG91cjI0ICsgJ1s6Ll0nICsgcmVNaW51dGUsICdpJyksXG4gICAgbmFtZTogJ3RpbWVzaG9ydDI0JyxcbiAgICBjYWxsYmFjazogZnVuY3Rpb24gY2FsbGJhY2sobWF0Y2gsIGhvdXIsIG1pbnV0ZSkge1xuICAgICAgcmV0dXJuIHRoaXMudGltZSgraG91ciwgK21pbnV0ZSwgMCwgMCk7XG4gICAgfVxuICB9LFxuXG4gIGlzbzg2MDFub0NvbG9uOiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXnQ/JyArIHJlSG91cjI0bHogKyByZU1pbnV0ZWx6ICsgcmVTZWNvbmRseiwgJ2knKSxcbiAgICBuYW1lOiAnaXNvODYwMW5vY29sb24nLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgaG91ciwgbWludXRlLCBzZWNvbmQpIHtcbiAgICAgIHJldHVybiB0aGlzLnRpbWUoK2hvdXIsICttaW51dGUsICtzZWNvbmQsIDApO1xuICAgIH1cbiAgfSxcblxuICBpc284NjAxZGF0ZVNsYXNoOiB7XG4gICAgLy8gZXZlbnRob3VnaCB0aGUgdHJhaWxpbmcgc2xhc2ggaXMgb3B0aW9uYWwgaW4gUEhQXG4gICAgLy8gaGVyZSBpdCdzIG1hbmRhdG9yeSBhbmQgaW5wdXRzIHdpdGhvdXQgdGhlIHNsYXNoXG4gICAgLy8gYXJlIGhhbmRsZWQgYnkgZGF0ZXNsYXNoXG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyByZVllYXI0ICsgJy8nICsgcmVNb250aGx6ICsgJy8nICsgcmVEYXlseiArICcvJyksXG4gICAgbmFtZTogJ2lzbzg2MDFkYXRlc2xhc2gnLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgeWVhciwgbW9udGgsIGRheSkge1xuICAgICAgcmV0dXJuIHRoaXMueW1kKCt5ZWFyLCBtb250aCAtIDEsICtkYXkpO1xuICAgIH1cbiAgfSxcblxuICBkYXRlU2xhc2g6IHtcbiAgICByZWdleDogUmVnRXhwKCdeJyArIHJlWWVhcjQgKyAnLycgKyByZU1vbnRoICsgJy8nICsgcmVEYXkpLFxuICAgIG5hbWU6ICdkYXRlc2xhc2gnLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgeWVhciwgbW9udGgsIGRheSkge1xuICAgICAgcmV0dXJuIHRoaXMueW1kKCt5ZWFyLCBtb250aCAtIDEsICtkYXkpO1xuICAgIH1cbiAgfSxcblxuICBhbWVyaWNhbjoge1xuICAgIHJlZ2V4OiBSZWdFeHAoJ14nICsgcmVNb250aCArICcvJyArIHJlRGF5ICsgJy8nICsgcmVZZWFyKSxcbiAgICBuYW1lOiAnYW1lcmljYW4nLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgbW9udGgsIGRheSwgeWVhcikge1xuICAgICAgcmV0dXJuIHRoaXMueW1kKHByb2Nlc3NZZWFyKHllYXIpLCBtb250aCAtIDEsICtkYXkpO1xuICAgIH1cbiAgfSxcblxuICBhbWVyaWNhblNob3J0OiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyByZU1vbnRoICsgJy8nICsgcmVEYXkpLFxuICAgIG5hbWU6ICdhbWVyaWNhbnNob3J0JyxcbiAgICBjYWxsYmFjazogZnVuY3Rpb24gY2FsbGJhY2sobWF0Y2gsIG1vbnRoLCBkYXkpIHtcbiAgICAgIHJldHVybiB0aGlzLnltZCh0aGlzLnksIG1vbnRoIC0gMSwgK2RheSk7XG4gICAgfVxuICB9LFxuXG4gIGdudURhdGVTaG9ydE9ySXNvODYwMWRhdGUyOiB7XG4gICAgLy8gaXNvODYwMWRhdGUyIGlzIGNvbXBsZXRlIHN1YnNldCBvZiBnbnVkYXRlc2hvcnRcbiAgICByZWdleDogUmVnRXhwKCdeJyArIHJlWWVhciArICctJyArIHJlTW9udGggKyAnLScgKyByZURheSksXG4gICAgbmFtZTogJ2dudWRhdGVzaG9ydCB8IGlzbzg2MDFkYXRlMicsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCB5ZWFyLCBtb250aCwgZGF5KSB7XG4gICAgICByZXR1cm4gdGhpcy55bWQocHJvY2Vzc1llYXIoeWVhciksIG1vbnRoIC0gMSwgK2RheSk7XG4gICAgfVxuICB9LFxuXG4gIGlzbzg2MDFkYXRlNDoge1xuICAgIHJlZ2V4OiBSZWdFeHAoJ14nICsgcmVZZWFyNHdpdGhTaWduICsgJy0nICsgcmVNb250aGx6ICsgJy0nICsgcmVEYXlseiksXG4gICAgbmFtZTogJ2lzbzg2MDFkYXRlNCcsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCB5ZWFyLCBtb250aCwgZGF5KSB7XG4gICAgICByZXR1cm4gdGhpcy55bWQoK3llYXIsIG1vbnRoIC0gMSwgK2RheSk7XG4gICAgfVxuICB9LFxuXG4gIGdudU5vQ29sb246IHtcbiAgICByZWdleDogUmVnRXhwKCdedD8nICsgcmVIb3VyMjRseiArIHJlTWludXRlbHosICdpJyksXG4gICAgbmFtZTogJ2dudW5vY29sb24nLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgaG91ciwgbWludXRlKSB7XG4gICAgICAvLyB0aGlzIHJ1bGUgaXMgYSBzcGVjaWFsIGNhc2VcbiAgICAgIC8vIGlmIHRpbWUgd2FzIGFscmVhZHkgc2V0IG9uY2UgYnkgYW55IHByZWNlZGluZyBydWxlLCBpdCBzZXRzIHRoZSBjYXB0dXJlZCB2YWx1ZSBhcyB5ZWFyXG4gICAgICBzd2l0Y2ggKHRoaXMudGltZXMpIHtcbiAgICAgICAgY2FzZSAwOlxuICAgICAgICAgIHJldHVybiB0aGlzLnRpbWUoK2hvdXIsICttaW51dGUsIDAsIHRoaXMuZik7XG4gICAgICAgIGNhc2UgMTpcbiAgICAgICAgICB0aGlzLnkgPSBob3VyICogMTAwICsgK21pbnV0ZTtcbiAgICAgICAgICB0aGlzLnRpbWVzKys7XG5cbiAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICB9XG4gICAgfVxuICB9LFxuXG4gIGdudURhdGVTaG9ydGVyOiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyByZVllYXI0ICsgJy0nICsgcmVNb250aCksXG4gICAgbmFtZTogJ2dudWRhdGVzaG9ydGVyJyxcbiAgICBjYWxsYmFjazogZnVuY3Rpb24gY2FsbGJhY2sobWF0Y2gsIHllYXIsIG1vbnRoKSB7XG4gICAgICByZXR1cm4gdGhpcy55bWQoK3llYXIsIG1vbnRoIC0gMSwgMSk7XG4gICAgfVxuICB9LFxuXG4gIHBnVGV4dFJldmVyc2U6IHtcbiAgICAvLyBub3RlOiBhbGxvd2VkIHllYXJzIGFyZSBmcm9tIDMyLTk5OTlcbiAgICAvLyB5ZWFycyBiZWxvdyAzMiBzaG91bGQgYmUgdHJlYXRlZCBhcyBkYXlzIGluIGRhdGVmdWxsXG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyAnKFxcXFxkezMsNH18WzQtOV1cXFxcZHwzWzItOV0pLSgnICsgcmVNb250aEFiYnIgKyAnKS0nICsgcmVEYXlseiwgJ2knKSxcbiAgICBuYW1lOiAncGd0ZXh0cmV2ZXJzZScsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCB5ZWFyLCBtb250aCwgZGF5KSB7XG4gICAgICByZXR1cm4gdGhpcy55bWQocHJvY2Vzc1llYXIoeWVhciksIGxvb2t1cE1vbnRoKG1vbnRoKSwgK2RheSk7XG4gICAgfVxuICB9LFxuXG4gIGRhdGVGdWxsOiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyByZURheSArICdbIFxcXFx0Li1dKicgKyByZU1vbnRoVGV4dCArICdbIFxcXFx0Li1dKicgKyByZVllYXIsICdpJyksXG4gICAgbmFtZTogJ2RhdGVmdWxsJyxcbiAgICBjYWxsYmFjazogZnVuY3Rpb24gY2FsbGJhY2sobWF0Y2gsIGRheSwgbW9udGgsIHllYXIpIHtcbiAgICAgIHJldHVybiB0aGlzLnltZChwcm9jZXNzWWVhcih5ZWFyKSwgbG9va3VwTW9udGgobW9udGgpLCArZGF5KTtcbiAgICB9XG4gIH0sXG5cbiAgZGF0ZU5vRGF5OiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyByZU1vbnRoVGV4dCArICdbIC5cXFxcdC1dKicgKyByZVllYXI0LCAnaScpLFxuICAgIG5hbWU6ICdkYXRlbm9kYXknLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgbW9udGgsIHllYXIpIHtcbiAgICAgIHJldHVybiB0aGlzLnltZCgreWVhciwgbG9va3VwTW9udGgobW9udGgpLCAxKTtcbiAgICB9XG4gIH0sXG5cbiAgZGF0ZU5vRGF5UmV2OiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyByZVllYXI0ICsgJ1sgLlxcXFx0LV0qJyArIHJlTW9udGhUZXh0LCAnaScpLFxuICAgIG5hbWU6ICdkYXRlbm9kYXlyZXYnLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgeWVhciwgbW9udGgpIHtcbiAgICAgIHJldHVybiB0aGlzLnltZCgreWVhciwgbG9va3VwTW9udGgobW9udGgpLCAxKTtcbiAgICB9XG4gIH0sXG5cbiAgcGdUZXh0U2hvcnQ6IHtcbiAgICByZWdleDogUmVnRXhwKCdeKCcgKyByZU1vbnRoQWJiciArICcpLScgKyByZURheWx6ICsgJy0nICsgcmVZZWFyLCAnaScpLFxuICAgIG5hbWU6ICdwZ3RleHRzaG9ydCcsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCBtb250aCwgZGF5LCB5ZWFyKSB7XG4gICAgICByZXR1cm4gdGhpcy55bWQocHJvY2Vzc1llYXIoeWVhciksIGxvb2t1cE1vbnRoKG1vbnRoKSwgK2RheSk7XG4gICAgfVxuICB9LFxuXG4gIGRhdGVOb1llYXI6IHtcbiAgICByZWdleDogUmVnRXhwKCdeJyArIHJlRGF0ZU5vWWVhciwgJ2knKSxcbiAgICBuYW1lOiAnZGF0ZW5veWVhcicsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCBtb250aCwgZGF5KSB7XG4gICAgICByZXR1cm4gdGhpcy55bWQodGhpcy55LCBsb29rdXBNb250aChtb250aCksICtkYXkpO1xuICAgIH1cbiAgfSxcblxuICBkYXRlTm9ZZWFyUmV2OiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyByZURheSArICdbIC5cXFxcdC1dKicgKyByZU1vbnRoVGV4dCwgJ2knKSxcbiAgICBuYW1lOiAnZGF0ZW5veWVhcnJldicsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCBkYXksIG1vbnRoKSB7XG4gICAgICByZXR1cm4gdGhpcy55bWQodGhpcy55LCBsb29rdXBNb250aChtb250aCksICtkYXkpO1xuICAgIH1cbiAgfSxcblxuICBpc29XZWVrRGF5OiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyByZVllYXI0ICsgJy0/VycgKyByZVdlZWtPZlllYXIgKyAnKD86LT8oWzAtN10pKT8nKSxcbiAgICBuYW1lOiAnaXNvd2Vla2RheSB8IGlzb3dlZWsnLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgeWVhciwgd2VlaywgZGF5KSB7XG4gICAgICBkYXkgPSBkYXkgPyArZGF5IDogMTtcblxuICAgICAgaWYgKCF0aGlzLnltZCgreWVhciwgMCwgMSkpIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgfVxuXG4gICAgICAvLyBnZXQgZGF5IG9mIHdlZWsgZm9yIEphbiAxc3RcbiAgICAgIHZhciBkYXlPZldlZWsgPSBuZXcgRGF0ZSh0aGlzLnksIHRoaXMubSwgdGhpcy5kKS5nZXREYXkoKTtcblxuICAgICAgLy8gYW5kIHVzZSB0aGUgZGF5IHRvIGZpZ3VyZSBvdXQgdGhlIG9mZnNldCBmb3IgZGF5IDEgb2Ygd2VlayAxXG4gICAgICBkYXlPZldlZWsgPSAwIC0gKGRheU9mV2VlayA+IDQgPyBkYXlPZldlZWsgLSA3IDogZGF5T2ZXZWVrKTtcblxuICAgICAgdGhpcy5yZCArPSBkYXlPZldlZWsgKyAod2VlayAtIDEpICogNyArIGRheTtcbiAgICB9XG4gIH0sXG5cbiAgcmVsYXRpdmVUZXh0OiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXignICsgcmVSZWx0ZXh0bnVtYmVyICsgJ3wnICsgcmVSZWx0ZXh0dGV4dCArICcpJyArIHJlU3BhY2UgKyAnKCcgKyByZVJlbHRleHR1bml0ICsgJyknLCAnaScpLFxuICAgIG5hbWU6ICdyZWxhdGl2ZXRleHQnLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgcmVsVmFsdWUsIHJlbFVuaXQpIHtcbiAgICAgIC8vIHRvZG86IGltcGxlbWVudCBoYW5kbGluZyBvZiAndGhpcyB0aW1lLXVuaXQnXG4gICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW51c2VkLXZhcnNcbiAgICAgIHZhciBfbG9va3VwUmVsYXRpdmUgPSBsb29rdXBSZWxhdGl2ZShyZWxWYWx1ZSksXG4gICAgICAgICAgYW1vdW50ID0gX2xvb2t1cFJlbGF0aXZlLmFtb3VudCxcbiAgICAgICAgICBiZWhhdmlvciA9IF9sb29rdXBSZWxhdGl2ZS5iZWhhdmlvcjtcblxuICAgICAgc3dpdGNoIChyZWxVbml0LnRvTG93ZXJDYXNlKCkpIHtcbiAgICAgICAgY2FzZSAnc2VjJzpcbiAgICAgICAgY2FzZSAnc2Vjcyc6XG4gICAgICAgIGNhc2UgJ3NlY29uZCc6XG4gICAgICAgIGNhc2UgJ3NlY29uZHMnOlxuICAgICAgICAgIHRoaXMucnMgKz0gYW1vdW50O1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICdtaW4nOlxuICAgICAgICBjYXNlICdtaW5zJzpcbiAgICAgICAgY2FzZSAnbWludXRlJzpcbiAgICAgICAgY2FzZSAnbWludXRlcyc6XG4gICAgICAgICAgdGhpcy5yaSArPSBhbW91bnQ7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgJ2hvdXInOlxuICAgICAgICBjYXNlICdob3Vycyc6XG4gICAgICAgICAgdGhpcy5yaCArPSBhbW91bnQ7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgJ2RheSc6XG4gICAgICAgIGNhc2UgJ2RheXMnOlxuICAgICAgICAgIHRoaXMucmQgKz0gYW1vdW50O1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICdmb3J0bmlnaHQnOlxuICAgICAgICBjYXNlICdmb3J0bmlnaHRzJzpcbiAgICAgICAgY2FzZSAnZm9ydGhuaWdodCc6XG4gICAgICAgIGNhc2UgJ2ZvcnRobmlnaHRzJzpcbiAgICAgICAgICB0aGlzLnJkICs9IGFtb3VudCAqIDE0O1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICd3ZWVrJzpcbiAgICAgICAgY2FzZSAnd2Vla3MnOlxuICAgICAgICAgIHRoaXMucmQgKz0gYW1vdW50ICogNztcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAnbW9udGgnOlxuICAgICAgICBjYXNlICdtb250aHMnOlxuICAgICAgICAgIHRoaXMucm0gKz0gYW1vdW50O1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICd5ZWFyJzpcbiAgICAgICAgY2FzZSAneWVhcnMnOlxuICAgICAgICAgIHRoaXMucnkgKz0gYW1vdW50O1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICdtb24nOlxuICAgICAgICBjYXNlICdtb25kYXknOlxuICAgICAgICBjYXNlICd0dWUnOlxuICAgICAgICBjYXNlICd0dWVzZGF5JzpcbiAgICAgICAgY2FzZSAnd2VkJzpcbiAgICAgICAgY2FzZSAnd2VkbmVzZGF5JzpcbiAgICAgICAgY2FzZSAndGh1JzpcbiAgICAgICAgY2FzZSAndGh1cnNkYXknOlxuICAgICAgICBjYXNlICdmcmknOlxuICAgICAgICBjYXNlICdmcmlkYXknOlxuICAgICAgICBjYXNlICdzYXQnOlxuICAgICAgICBjYXNlICdzYXR1cmRheSc6XG4gICAgICAgIGNhc2UgJ3N1bic6XG4gICAgICAgIGNhc2UgJ3N1bmRheSc6XG4gICAgICAgICAgdGhpcy5yZXNldFRpbWUoKTtcbiAgICAgICAgICB0aGlzLndlZWtkYXkgPSBsb29rdXBXZWVrZGF5KHJlbFVuaXQsIDcpO1xuICAgICAgICAgIHRoaXMud2Vla2RheUJlaGF2aW9yID0gMTtcbiAgICAgICAgICB0aGlzLnJkICs9IChhbW91bnQgPiAwID8gYW1vdW50IC0gMSA6IGFtb3VudCkgKiA3O1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICd3ZWVrZGF5JzpcbiAgICAgICAgY2FzZSAnd2Vla2RheXMnOlxuICAgICAgICAgIC8vIHRvZG9cbiAgICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICB9XG4gIH0sXG5cbiAgcmVsYXRpdmU6IHtcbiAgICByZWdleDogUmVnRXhwKCdeKFsrLV0qKVsgXFxcXHRdKihcXFxcZCspJyArIHJlU3BhY2VPcHQgKyAnKCcgKyByZVJlbHRleHR1bml0ICsgJ3x3ZWVrKScsICdpJyksXG4gICAgbmFtZTogJ3JlbGF0aXZlJyxcbiAgICBjYWxsYmFjazogZnVuY3Rpb24gY2FsbGJhY2sobWF0Y2gsIHNpZ25zLCByZWxWYWx1ZSwgcmVsVW5pdCkge1xuICAgICAgdmFyIG1pbnVzZXMgPSBzaWducy5yZXBsYWNlKC9bXi1dL2csICcnKS5sZW5ndGg7XG5cbiAgICAgIHZhciBhbW91bnQgPSArcmVsVmFsdWUgKiBNYXRoLnBvdygtMSwgbWludXNlcyk7XG5cbiAgICAgIHN3aXRjaCAocmVsVW5pdC50b0xvd2VyQ2FzZSgpKSB7XG4gICAgICAgIGNhc2UgJ3NlYyc6XG4gICAgICAgIGNhc2UgJ3NlY3MnOlxuICAgICAgICBjYXNlICdzZWNvbmQnOlxuICAgICAgICBjYXNlICdzZWNvbmRzJzpcbiAgICAgICAgICB0aGlzLnJzICs9IGFtb3VudDtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAnbWluJzpcbiAgICAgICAgY2FzZSAnbWlucyc6XG4gICAgICAgIGNhc2UgJ21pbnV0ZSc6XG4gICAgICAgIGNhc2UgJ21pbnV0ZXMnOlxuICAgICAgICAgIHRoaXMucmkgKz0gYW1vdW50O1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICdob3VyJzpcbiAgICAgICAgY2FzZSAnaG91cnMnOlxuICAgICAgICAgIHRoaXMucmggKz0gYW1vdW50O1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICdkYXknOlxuICAgICAgICBjYXNlICdkYXlzJzpcbiAgICAgICAgICB0aGlzLnJkICs9IGFtb3VudDtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAnZm9ydG5pZ2h0JzpcbiAgICAgICAgY2FzZSAnZm9ydG5pZ2h0cyc6XG4gICAgICAgIGNhc2UgJ2ZvcnRobmlnaHQnOlxuICAgICAgICBjYXNlICdmb3J0aG5pZ2h0cyc6XG4gICAgICAgICAgdGhpcy5yZCArPSBhbW91bnQgKiAxNDtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAnd2Vlayc6XG4gICAgICAgIGNhc2UgJ3dlZWtzJzpcbiAgICAgICAgICB0aGlzLnJkICs9IGFtb3VudCAqIDc7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgJ21vbnRoJzpcbiAgICAgICAgY2FzZSAnbW9udGhzJzpcbiAgICAgICAgICB0aGlzLnJtICs9IGFtb3VudDtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAneWVhcic6XG4gICAgICAgIGNhc2UgJ3llYXJzJzpcbiAgICAgICAgICB0aGlzLnJ5ICs9IGFtb3VudDtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAnbW9uJzpcbiAgICAgICAgY2FzZSAnbW9uZGF5JzpcbiAgICAgICAgY2FzZSAndHVlJzpcbiAgICAgICAgY2FzZSAndHVlc2RheSc6XG4gICAgICAgIGNhc2UgJ3dlZCc6XG4gICAgICAgIGNhc2UgJ3dlZG5lc2RheSc6XG4gICAgICAgIGNhc2UgJ3RodSc6XG4gICAgICAgIGNhc2UgJ3RodXJzZGF5JzpcbiAgICAgICAgY2FzZSAnZnJpJzpcbiAgICAgICAgY2FzZSAnZnJpZGF5JzpcbiAgICAgICAgY2FzZSAnc2F0JzpcbiAgICAgICAgY2FzZSAnc2F0dXJkYXknOlxuICAgICAgICBjYXNlICdzdW4nOlxuICAgICAgICBjYXNlICdzdW5kYXknOlxuICAgICAgICAgIHRoaXMucmVzZXRUaW1lKCk7XG4gICAgICAgICAgdGhpcy53ZWVrZGF5ID0gbG9va3VwV2Vla2RheShyZWxVbml0LCA3KTtcbiAgICAgICAgICB0aGlzLndlZWtkYXlCZWhhdmlvciA9IDE7XG4gICAgICAgICAgdGhpcy5yZCArPSAoYW1vdW50ID4gMCA/IGFtb3VudCAtIDEgOiBhbW91bnQpICogNztcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAnd2Vla2RheSc6XG4gICAgICAgIGNhc2UgJ3dlZWtkYXlzJzpcbiAgICAgICAgICAvLyB0b2RvXG4gICAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgfVxuICB9LFxuXG4gIGRheVRleHQ6IHtcbiAgICByZWdleDogUmVnRXhwKCdeKCcgKyByZURheXRleHQgKyAnKScsICdpJyksXG4gICAgbmFtZTogJ2RheXRleHQnLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgZGF5VGV4dCkge1xuICAgICAgdGhpcy5yZXNldFRpbWUoKTtcbiAgICAgIHRoaXMud2Vla2RheSA9IGxvb2t1cFdlZWtkYXkoZGF5VGV4dCwgMCk7XG5cbiAgICAgIGlmICh0aGlzLndlZWtkYXlCZWhhdmlvciAhPT0gMikge1xuICAgICAgICB0aGlzLndlZWtkYXlCZWhhdmlvciA9IDE7XG4gICAgICB9XG4gICAgfVxuICB9LFxuXG4gIHJlbGF0aXZlVGV4dFdlZWs6IHtcbiAgICByZWdleDogUmVnRXhwKCdeKCcgKyByZVJlbHRleHR0ZXh0ICsgJyknICsgcmVTcGFjZSArICd3ZWVrJywgJ2knKSxcbiAgICBuYW1lOiAncmVsYXRpdmV0ZXh0d2VlaycsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCByZWxUZXh0KSB7XG4gICAgICB0aGlzLndlZWtkYXlCZWhhdmlvciA9IDI7XG5cbiAgICAgIHN3aXRjaCAocmVsVGV4dC50b0xvd2VyQ2FzZSgpKSB7XG4gICAgICAgIGNhc2UgJ3RoaXMnOlxuICAgICAgICAgIHRoaXMucmQgKz0gMDtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAnbmV4dCc6XG4gICAgICAgICAgdGhpcy5yZCArPSA3O1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICdsYXN0JzpcbiAgICAgICAgY2FzZSAncHJldmlvdXMnOlxuICAgICAgICAgIHRoaXMucmQgLT0gNztcbiAgICAgICAgICBicmVhaztcbiAgICAgIH1cblxuICAgICAgaWYgKGlzTmFOKHRoaXMud2Vla2RheSkpIHtcbiAgICAgICAgdGhpcy53ZWVrZGF5ID0gMTtcbiAgICAgIH1cbiAgICB9XG4gIH0sXG5cbiAgbW9udGhGdWxsT3JNb250aEFiYnI6IHtcbiAgICByZWdleDogUmVnRXhwKCdeKCcgKyByZU1vbnRoRnVsbCArICd8JyArIHJlTW9udGhBYmJyICsgJyknLCAnaScpLFxuICAgIG5hbWU6ICdtb250aGZ1bGwgfCBtb250aGFiYnInLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgbW9udGgpIHtcbiAgICAgIHJldHVybiB0aGlzLnltZCh0aGlzLnksIGxvb2t1cE1vbnRoKG1vbnRoKSwgdGhpcy5kKTtcbiAgICB9XG4gIH0sXG5cbiAgdHpDb3JyZWN0aW9uOiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyByZVR6Q29ycmVjdGlvbiwgJ2knKSxcbiAgICBuYW1lOiAndHpjb3JyZWN0aW9uJyxcbiAgICBjYWxsYmFjazogZnVuY3Rpb24gY2FsbGJhY2sodHpDb3JyZWN0aW9uKSB7XG4gICAgICByZXR1cm4gdGhpcy56b25lKHByb2Nlc3NUekNvcnJlY3Rpb24odHpDb3JyZWN0aW9uKSk7XG4gICAgfVxuICB9LFxuXG4gIHR6QWJicjoge1xuICAgIHJlZ2V4OiBSZWdFeHAoJ14nICsgcmVUekFiYnIpLFxuICAgIG5hbWU6ICd0emFiYnInLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgYWJicikge1xuICAgICAgdmFyIG9mZnNldCA9IHR6QWJick9mZnNldHNbYWJici50b0xvd2VyQ2FzZSgpXTtcblxuICAgICAgaWYgKGlzTmFOKG9mZnNldCkpIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgfVxuXG4gICAgICByZXR1cm4gdGhpcy56b25lKG9mZnNldCk7XG4gICAgfVxuICB9LFxuXG4gIGFnbzoge1xuICAgIHJlZ2V4OiAvXmFnby9pLFxuICAgIG5hbWU6ICdhZ28nLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjaygpIHtcbiAgICAgIHRoaXMucnkgPSAtdGhpcy5yeTtcbiAgICAgIHRoaXMucm0gPSAtdGhpcy5ybTtcbiAgICAgIHRoaXMucmQgPSAtdGhpcy5yZDtcbiAgICAgIHRoaXMucmggPSAtdGhpcy5yaDtcbiAgICAgIHRoaXMucmkgPSAtdGhpcy5yaTtcbiAgICAgIHRoaXMucnMgPSAtdGhpcy5ycztcbiAgICAgIHRoaXMucmYgPSAtdGhpcy5yZjtcbiAgICB9XG4gIH0sXG5cbiAgeWVhcjQ6IHtcbiAgICByZWdleDogUmVnRXhwKCdeJyArIHJlWWVhcjQpLFxuICAgIG5hbWU6ICd5ZWFyNCcsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCB5ZWFyKSB7XG4gICAgICB0aGlzLnkgPSAreWVhcjtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cbiAgfSxcblxuICB3aGl0ZXNwYWNlOiB7XG4gICAgcmVnZXg6IC9eWyAuLFxcdF0rLyxcbiAgICBuYW1lOiAnd2hpdGVzcGFjZSdcbiAgICAvLyBkbyBub3RoaW5nXG4gIH0sXG5cbiAgZGF0ZVNob3J0V2l0aFRpbWVMb25nOiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyByZURhdGVOb1llYXIgKyAndD8nICsgcmVIb3VyMjQgKyAnWzouXScgKyByZU1pbnV0ZSArICdbOi5dJyArIHJlU2Vjb25kLCAnaScpLFxuICAgIG5hbWU6ICdkYXRlc2hvcnR3aXRodGltZWxvbmcnLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgbW9udGgsIGRheSwgaG91ciwgbWludXRlLCBzZWNvbmQpIHtcbiAgICAgIHJldHVybiB0aGlzLnltZCh0aGlzLnksIGxvb2t1cE1vbnRoKG1vbnRoKSwgK2RheSkgJiYgdGhpcy50aW1lKCtob3VyLCArbWludXRlLCArc2Vjb25kLCAwKTtcbiAgICB9XG4gIH0sXG5cbiAgZGF0ZVNob3J0V2l0aFRpbWVMb25nMTI6IHtcbiAgICByZWdleDogUmVnRXhwKCdeJyArIHJlRGF0ZU5vWWVhciArIHJlSG91cjEyICsgJ1s6Ll0nICsgcmVNaW51dGUgKyAnWzouXScgKyByZVNlY29uZGx6ICsgcmVTcGFjZU9wdCArIHJlTWVyaWRpYW4sICdpJyksXG4gICAgbmFtZTogJ2RhdGVzaG9ydHdpdGh0aW1lbG9uZzEyJyxcbiAgICBjYWxsYmFjazogZnVuY3Rpb24gY2FsbGJhY2sobWF0Y2gsIG1vbnRoLCBkYXksIGhvdXIsIG1pbnV0ZSwgc2Vjb25kLCBtZXJpZGlhbikge1xuICAgICAgcmV0dXJuIHRoaXMueW1kKHRoaXMueSwgbG9va3VwTW9udGgobW9udGgpLCArZGF5KSAmJiB0aGlzLnRpbWUocHJvY2Vzc01lcmlkaWFuKCtob3VyLCBtZXJpZGlhbiksICttaW51dGUsICtzZWNvbmQsIDApO1xuICAgIH1cbiAgfSxcblxuICBkYXRlU2hvcnRXaXRoVGltZVNob3J0OiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyByZURhdGVOb1llYXIgKyAndD8nICsgcmVIb3VyMjQgKyAnWzouXScgKyByZU1pbnV0ZSwgJ2knKSxcbiAgICBuYW1lOiAnZGF0ZXNob3J0d2l0aHRpbWVzaG9ydCcsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCBtb250aCwgZGF5LCBob3VyLCBtaW51dGUpIHtcbiAgICAgIHJldHVybiB0aGlzLnltZCh0aGlzLnksIGxvb2t1cE1vbnRoKG1vbnRoKSwgK2RheSkgJiYgdGhpcy50aW1lKCtob3VyLCArbWludXRlLCAwLCAwKTtcbiAgICB9XG4gIH0sXG5cbiAgZGF0ZVNob3J0V2l0aFRpbWVTaG9ydDEyOiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyByZURhdGVOb1llYXIgKyByZUhvdXIxMiArICdbOi5dJyArIHJlTWludXRlbHogKyByZVNwYWNlT3B0ICsgcmVNZXJpZGlhbiwgJ2knKSxcbiAgICBuYW1lOiAnZGF0ZXNob3J0d2l0aHRpbWVzaG9ydDEyJyxcbiAgICBjYWxsYmFjazogZnVuY3Rpb24gY2FsbGJhY2sobWF0Y2gsIG1vbnRoLCBkYXksIGhvdXIsIG1pbnV0ZSwgbWVyaWRpYW4pIHtcbiAgICAgIHJldHVybiB0aGlzLnltZCh0aGlzLnksIGxvb2t1cE1vbnRoKG1vbnRoKSwgK2RheSkgJiYgdGhpcy50aW1lKHByb2Nlc3NNZXJpZGlhbigraG91ciwgbWVyaWRpYW4pLCArbWludXRlLCAwLCAwKTtcbiAgICB9XG4gIH1cbn07XG5cbnZhciByZXN1bHRQcm90byA9IHtcbiAgLy8gZGF0ZVxuICB5OiBOYU4sXG4gIG06IE5hTixcbiAgZDogTmFOLFxuICAvLyB0aW1lXG4gIGg6IE5hTixcbiAgaTogTmFOLFxuICBzOiBOYU4sXG4gIGY6IE5hTixcblxuICAvLyByZWxhdGl2ZSBzaGlmdHNcbiAgcnk6IDAsXG4gIHJtOiAwLFxuICByZDogMCxcbiAgcmg6IDAsXG4gIHJpOiAwLFxuICByczogMCxcbiAgcmY6IDAsXG5cbiAgLy8gd2Vla2RheSByZWxhdGVkIHNoaWZ0c1xuICB3ZWVrZGF5OiBOYU4sXG4gIHdlZWtkYXlCZWhhdmlvcjogMCxcblxuICAvLyBmaXJzdCBvciBsYXN0IGRheSBvZiBtb250aFxuICAvLyAwIG5vbmUsIDEgZmlyc3QsIC0xIGxhc3RcbiAgZmlyc3RPckxhc3REYXlPZk1vbnRoOiAwLFxuXG4gIC8vIHRpbWV6b25lIGNvcnJlY3Rpb24gaW4gbWludXRlc1xuICB6OiBOYU4sXG5cbiAgLy8gY291bnRlcnNcbiAgZGF0ZXM6IDAsXG4gIHRpbWVzOiAwLFxuICB6b25lczogMCxcblxuICAvLyBoZWxwZXIgZnVuY3Rpb25zXG4gIHltZDogZnVuY3Rpb24geW1kKHksIG0sIGQpIHtcbiAgICBpZiAodGhpcy5kYXRlcyA+IDApIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG5cbiAgICB0aGlzLmRhdGVzKys7XG4gICAgdGhpcy55ID0geTtcbiAgICB0aGlzLm0gPSBtO1xuICAgIHRoaXMuZCA9IGQ7XG4gICAgcmV0dXJuIHRydWU7XG4gIH0sXG4gIHRpbWU6IGZ1bmN0aW9uIHRpbWUoaCwgaSwgcywgZikge1xuICAgIGlmICh0aGlzLnRpbWVzID4gMCkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cblxuICAgIHRoaXMudGltZXMrKztcbiAgICB0aGlzLmggPSBoO1xuICAgIHRoaXMuaSA9IGk7XG4gICAgdGhpcy5zID0gcztcbiAgICB0aGlzLmYgPSBmO1xuXG4gICAgcmV0dXJuIHRydWU7XG4gIH0sXG4gIHJlc2V0VGltZTogZnVuY3Rpb24gcmVzZXRUaW1lKCkge1xuICAgIHRoaXMuaCA9IDA7XG4gICAgdGhpcy5pID0gMDtcbiAgICB0aGlzLnMgPSAwO1xuICAgIHRoaXMuZiA9IDA7XG4gICAgdGhpcy50aW1lcyA9IDA7XG5cbiAgICByZXR1cm4gdHJ1ZTtcbiAgfSxcbiAgem9uZTogZnVuY3Rpb24gem9uZShtaW51dGVzKSB7XG4gICAgaWYgKHRoaXMuem9uZXMgPD0gMSkge1xuICAgICAgdGhpcy56b25lcysrO1xuICAgICAgdGhpcy56ID0gbWludXRlcztcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cblxuICAgIHJldHVybiBmYWxzZTtcbiAgfSxcbiAgdG9EYXRlOiBmdW5jdGlvbiB0b0RhdGUocmVsYXRpdmVUbykge1xuICAgIGlmICh0aGlzLmRhdGVzICYmICF0aGlzLnRpbWVzKSB7XG4gICAgICB0aGlzLmggPSB0aGlzLmkgPSB0aGlzLnMgPSB0aGlzLmYgPSAwO1xuICAgIH1cblxuICAgIC8vIGZpbGwgaG9sZXNcbiAgICBpZiAoaXNOYU4odGhpcy55KSkge1xuICAgICAgdGhpcy55ID0gcmVsYXRpdmVUby5nZXRGdWxsWWVhcigpO1xuICAgIH1cblxuICAgIGlmIChpc05hTih0aGlzLm0pKSB7XG4gICAgICB0aGlzLm0gPSByZWxhdGl2ZVRvLmdldE1vbnRoKCk7XG4gICAgfVxuXG4gICAgaWYgKGlzTmFOKHRoaXMuZCkpIHtcbiAgICAgIHRoaXMuZCA9IHJlbGF0aXZlVG8uZ2V0RGF0ZSgpO1xuICAgIH1cblxuICAgIGlmIChpc05hTih0aGlzLmgpKSB7XG4gICAgICB0aGlzLmggPSByZWxhdGl2ZVRvLmdldEhvdXJzKCk7XG4gICAgfVxuXG4gICAgaWYgKGlzTmFOKHRoaXMuaSkpIHtcbiAgICAgIHRoaXMuaSA9IHJlbGF0aXZlVG8uZ2V0TWludXRlcygpO1xuICAgIH1cblxuICAgIGlmIChpc05hTih0aGlzLnMpKSB7XG4gICAgICB0aGlzLnMgPSByZWxhdGl2ZVRvLmdldFNlY29uZHMoKTtcbiAgICB9XG5cbiAgICBpZiAoaXNOYU4odGhpcy5mKSkge1xuICAgICAgdGhpcy5mID0gcmVsYXRpdmVUby5nZXRNaWxsaXNlY29uZHMoKTtcbiAgICB9XG5cbiAgICAvLyBhZGp1c3Qgc3BlY2lhbCBlYXJseVxuICAgIHN3aXRjaCAodGhpcy5maXJzdE9yTGFzdERheU9mTW9udGgpIHtcbiAgICAgIGNhc2UgMTpcbiAgICAgICAgdGhpcy5kID0gMTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlIC0xOlxuICAgICAgICB0aGlzLmQgPSAwO1xuICAgICAgICB0aGlzLm0gKz0gMTtcbiAgICAgICAgYnJlYWs7XG4gICAgfVxuXG4gICAgaWYgKCFpc05hTih0aGlzLndlZWtkYXkpKSB7XG4gICAgICB2YXIgZGF0ZSA9IG5ldyBEYXRlKHJlbGF0aXZlVG8uZ2V0VGltZSgpKTtcbiAgICAgIGRhdGUuc2V0RnVsbFllYXIodGhpcy55LCB0aGlzLm0sIHRoaXMuZCk7XG4gICAgICBkYXRlLnNldEhvdXJzKHRoaXMuaCwgdGhpcy5pLCB0aGlzLnMsIHRoaXMuZik7XG5cbiAgICAgIHZhciBkb3cgPSBkYXRlLmdldERheSgpO1xuXG4gICAgICBpZiAodGhpcy53ZWVrZGF5QmVoYXZpb3IgPT09IDIpIHtcbiAgICAgICAgLy8gVG8gbWFrZSBcInRoaXMgd2Vla1wiIHdvcmssIHdoZXJlIHRoZSBjdXJyZW50IGRheSBvZiB3ZWVrIGlzIGEgXCJzdW5kYXlcIlxuICAgICAgICBpZiAoZG93ID09PSAwICYmIHRoaXMud2Vla2RheSAhPT0gMCkge1xuICAgICAgICAgIHRoaXMud2Vla2RheSA9IC02O1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gVG8gbWFrZSBcInN1bmRheSB0aGlzIHdlZWtcIiB3b3JrLCB3aGVyZSB0aGUgY3VycmVudCBkYXkgb2Ygd2VlayBpcyBub3QgYSBcInN1bmRheVwiXG4gICAgICAgIGlmICh0aGlzLndlZWtkYXkgPT09IDAgJiYgZG93ICE9PSAwKSB7XG4gICAgICAgICAgdGhpcy53ZWVrZGF5ID0gNztcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuZCAtPSBkb3c7XG4gICAgICAgIHRoaXMuZCArPSB0aGlzLndlZWtkYXk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB2YXIgZGlmZiA9IHRoaXMud2Vla2RheSAtIGRvdztcblxuICAgICAgICAvLyBzb21lIFBIUCBtYWdpY1xuICAgICAgICBpZiAodGhpcy5yZCA8IDAgJiYgZGlmZiA8IDAgfHwgdGhpcy5yZCA+PSAwICYmIGRpZmYgPD0gLXRoaXMud2Vla2RheUJlaGF2aW9yKSB7XG4gICAgICAgICAgZGlmZiArPSA3O1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHRoaXMud2Vla2RheSA+PSAwKSB7XG4gICAgICAgICAgdGhpcy5kICs9IGRpZmY7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgdGhpcy5kIC09IDcgLSAoTWF0aC5hYnModGhpcy53ZWVrZGF5KSAtIGRvdyk7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLndlZWtkYXkgPSBOYU47XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gYWRqdXN0IHJlbGF0aXZlXG4gICAgdGhpcy55ICs9IHRoaXMucnk7XG4gICAgdGhpcy5tICs9IHRoaXMucm07XG4gICAgdGhpcy5kICs9IHRoaXMucmQ7XG5cbiAgICB0aGlzLmggKz0gdGhpcy5yaDtcbiAgICB0aGlzLmkgKz0gdGhpcy5yaTtcbiAgICB0aGlzLnMgKz0gdGhpcy5ycztcbiAgICB0aGlzLmYgKz0gdGhpcy5yZjtcblxuICAgIHRoaXMucnkgPSB0aGlzLnJtID0gdGhpcy5yZCA9IDA7XG4gICAgdGhpcy5yaCA9IHRoaXMucmkgPSB0aGlzLnJzID0gdGhpcy5yZiA9IDA7XG5cbiAgICB2YXIgcmVzdWx0ID0gbmV3IERhdGUocmVsYXRpdmVUby5nZXRUaW1lKCkpO1xuICAgIC8vIHNpbmNlIERhdGUgY29uc3RydWN0b3IgdHJlYXRzIHllYXJzIDw9IDk5IGFzIDE5MDArXG4gICAgLy8gaXQgY2FuJ3QgYmUgdXNlZCwgdGh1cyB0aGlzIHdlaXJkIHdheVxuICAgIHJlc3VsdC5zZXRGdWxsWWVhcih0aGlzLnksIHRoaXMubSwgdGhpcy5kKTtcbiAgICByZXN1bHQuc2V0SG91cnModGhpcy5oLCB0aGlzLmksIHRoaXMucywgdGhpcy5mKTtcblxuICAgIC8vIG5vdGU6IHRoaXMgaXMgZG9uZSB0d2ljZSBpbiBQSFBcbiAgICAvLyBlYXJseSB3aGVuIHByb2Nlc3Npbmcgc3BlY2lhbCByZWxhdGl2ZXNcbiAgICAvLyBhbmQgbGF0ZVxuICAgIC8vIHRvZG86IGNoZWNrIGlmIHRoZSBsb2dpYyBjYW4gYmUgcmVkdWNlZFxuICAgIC8vIHRvIGp1c3Qgb25lIHRpbWUgYWN0aW9uXG4gICAgc3dpdGNoICh0aGlzLmZpcnN0T3JMYXN0RGF5T2ZNb250aCkge1xuICAgICAgY2FzZSAxOlxuICAgICAgICByZXN1bHQuc2V0RGF0ZSgxKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlIC0xOlxuICAgICAgICByZXN1bHQuc2V0TW9udGgocmVzdWx0LmdldE1vbnRoKCkgKyAxLCAwKTtcbiAgICAgICAgYnJlYWs7XG4gICAgfVxuXG4gICAgLy8gYWRqdXN0IHRpbWV6b25lXG4gICAgaWYgKCFpc05hTih0aGlzLnopICYmIHJlc3VsdC5nZXRUaW1lem9uZU9mZnNldCgpICE9PSB0aGlzLnopIHtcbiAgICAgIHJlc3VsdC5zZXRVVENGdWxsWWVhcihyZXN1bHQuZ2V0RnVsbFllYXIoKSwgcmVzdWx0LmdldE1vbnRoKCksIHJlc3VsdC5nZXREYXRlKCkpO1xuXG4gICAgICByZXN1bHQuc2V0VVRDSG91cnMocmVzdWx0LmdldEhvdXJzKCksIHJlc3VsdC5nZXRNaW51dGVzKCksIHJlc3VsdC5nZXRTZWNvbmRzKCkgLSB0aGlzLnosIHJlc3VsdC5nZXRNaWxsaXNlY29uZHMoKSk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxufTtcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiBzdHJ0b3RpbWUoc3RyLCBub3cpIHtcbiAgLy8gICAgICAgZGlzY3VzcyBhdDogaHR0cHM6Ly9sb2N1dHVzLmlvL3BocC9zdHJ0b3RpbWUvXG4gIC8vICAgICAgb3JpZ2luYWwgYnk6IENhaW8gQXJpZWRlIChodHRwczovL2NhaW9hcmllZGUuY29tKVxuICAvLyAgICAgIGltcHJvdmVkIGJ5OiBLZXZpbiB2YW4gWm9ubmV2ZWxkIChodHRwczovL2t2ei5pbylcbiAgLy8gICAgICBpbXByb3ZlZCBieTogQ2FpbyBBcmllZGUgKGh0dHBzOi8vY2Fpb2FyaWVkZS5jb20pXG4gIC8vICAgICAgaW1wcm92ZWQgYnk6IEEuIE1hdMOtYXMgUXVlemFkYSAoaHR0cHM6Ly9hbWF0aWFzcS5jb20pXG4gIC8vICAgICAgaW1wcm92ZWQgYnk6IHByZXV0ZXJcbiAgLy8gICAgICBpbXByb3ZlZCBieTogQnJldHQgWmFtaXIgKGh0dHBzOi8vYnJldHQtemFtaXIubWUpXG4gIC8vICAgICAgaW1wcm92ZWQgYnk6IE1pcmtvIEZhYmVyXG4gIC8vICAgICAgICAgaW5wdXQgYnk6IERhdmlkXG4gIC8vICAgICAgYnVnZml4ZWQgYnk6IFdhZ25lciBCLiBTb2FyZXNcbiAgLy8gICAgICBidWdmaXhlZCBieTogQXJ0dXIgVGNoZXJueWNoZXZcbiAgLy8gICAgICBidWdmaXhlZCBieTogU3RlcGhhbiBCw7ZzY2gtUGxlcGVsaXRzIChodHRwczovL2dpdGh1Yi5jb20vcGxlcGUpXG4gIC8vIHJlaW1wbGVtZW50ZWQgYnk6IFJhZmHFgiBLdWthd3NraVxuICAvLyAgICAgICAgICAgbm90ZSAxOiBFeGFtcGxlcyBhbGwgaGF2ZSBhIGZpeGVkIHRpbWVzdGFtcCB0byBwcmV2ZW50XG4gIC8vICAgICAgICAgICBub3RlIDE6IHRlc3RzIHRvIGZhaWwgYmVjYXVzZSBvZiB2YXJpYWJsZSB0aW1lKHpvbmVzKVxuICAvLyAgICAgICAgZXhhbXBsZSAxOiBzdHJ0b3RpbWUoJysxIGRheScsIDExMjk2MzMyMDApXG4gIC8vICAgICAgICByZXR1cm5zIDE6IDExMjk3MTk2MDBcbiAgLy8gICAgICAgIGV4YW1wbGUgMjogc3RydG90aW1lKCcrMSB3ZWVrIDIgZGF5cyA0IGhvdXJzIDIgc2Vjb25kcycsIDExMjk2MzMyMDApXG4gIC8vICAgICAgICByZXR1cm5zIDI6IDExMzA0MjUyMDJcbiAgLy8gICAgICAgIGV4YW1wbGUgMzogc3RydG90aW1lKCdsYXN0IG1vbnRoJywgMTEyOTYzMzIwMClcbiAgLy8gICAgICAgIHJldHVybnMgMzogMTEyNzA0MTIwMFxuICAvLyAgICAgICAgZXhhbXBsZSA0OiBzdHJ0b3RpbWUoJzIwMDktMDUtMDQgMDg6MzA6MDArMDAnKVxuICAvLyAgICAgICAgcmV0dXJucyA0OiAxMjQxNDI1ODAwXG4gIC8vICAgICAgICBleGFtcGxlIDU6IHN0cnRvdGltZSgnMjAwOS0wNS0wNCAwODozMDowMCswMjowMCcpXG4gIC8vICAgICAgICByZXR1cm5zIDU6IDEyNDE0MTg2MDBcbiAgLy8gICAgICAgIGV4YW1wbGUgNjogc3RydG90aW1lKCcyMDA5LTA1LTA0IDA4OjMwOjAwIFlXVCcpXG4gIC8vICAgICAgICByZXR1cm5zIDY6IDEyNDE0NTQ2MDBcbiAgLy8gICAgICAgIGV4YW1wbGUgNzogc3RydG90aW1lKCcxMC1KVUwtMTcnKVxuICAvLyAgICAgICAgcmV0dXJucyA3OiAxNDk5NjQ0ODAwXG5cbiAgaWYgKG5vdyA9PSBudWxsKSB7XG4gICAgbm93ID0gTWF0aC5mbG9vcihEYXRlLm5vdygpIC8gMTAwMCk7XG4gIH1cblxuICAvLyB0aGUgcnVsZSBvcmRlciBpcyBpbXBvcnRhbnRcbiAgLy8gaWYgbXVsdGlwbGUgcnVsZXMgbWF0Y2gsIHRoZSBsb25nZXN0IG1hdGNoIHdpbnNcbiAgLy8gaWYgbXVsdGlwbGUgcnVsZXMgbWF0Y2ggdGhlIHNhbWUgc3RyaW5nLCB0aGUgZmlyc3QgbWF0Y2ggd2luc1xuICB2YXIgcnVsZXMgPSBbZm9ybWF0cy55ZXN0ZXJkYXksIGZvcm1hdHMubm93LCBmb3JtYXRzLm5vb24sIGZvcm1hdHMubWlkbmlnaHRPclRvZGF5LCBmb3JtYXRzLnRvbW9ycm93LCBmb3JtYXRzLnRpbWVzdGFtcCwgZm9ybWF0cy5maXJzdE9yTGFzdERheSwgZm9ybWF0cy5iYWNrT3JGcm9udE9mLFxuICAvLyBmb3JtYXRzLndlZWtkYXlPZiwgLy8gbm90IHlldCBpbXBsZW1lbnRlZFxuICBmb3JtYXRzLnRpbWVUaW55MTIsIGZvcm1hdHMudGltZVNob3J0MTIsIGZvcm1hdHMudGltZUxvbmcxMiwgZm9ybWF0cy5tc3NxbHRpbWUsIGZvcm1hdHMub3JhY2xlZGF0ZSwgZm9ybWF0cy50aW1lU2hvcnQyNCwgZm9ybWF0cy50aW1lTG9uZzI0LCBmb3JtYXRzLmlzbzg2MDFsb25nLCBmb3JtYXRzLmdudU5vQ29sb24sIGZvcm1hdHMuaXNvODYwMW5vQ29sb24sIGZvcm1hdHMuYW1lcmljYW5TaG9ydCwgZm9ybWF0cy5hbWVyaWNhbiwgZm9ybWF0cy5pc284NjAxZGF0ZTQsIGZvcm1hdHMuaXNvODYwMWRhdGVTbGFzaCwgZm9ybWF0cy5kYXRlU2xhc2gsIGZvcm1hdHMuZ251RGF0ZVNob3J0T3JJc284NjAxZGF0ZTIsIGZvcm1hdHMuZ251RGF0ZVNob3J0ZXIsIGZvcm1hdHMuZGF0ZUZ1bGwsIGZvcm1hdHMucG9pbnRlZERhdGU0LCBmb3JtYXRzLnBvaW50ZWREYXRlMiwgZm9ybWF0cy5kYXRlTm9EYXksIGZvcm1hdHMuZGF0ZU5vRGF5UmV2LCBmb3JtYXRzLmRhdGVUZXh0dWFsLCBmb3JtYXRzLmRhdGVOb1llYXIsIGZvcm1hdHMuZGF0ZU5vWWVhclJldiwgZm9ybWF0cy5kYXRlTm9Db2xvbiwgZm9ybWF0cy54bWxScGMsIGZvcm1hdHMueG1sUnBjTm9Db2xvbiwgZm9ybWF0cy5zb2FwLCBmb3JtYXRzLndkZHgsIGZvcm1hdHMuZXhpZiwgZm9ybWF0cy5wZ3lkb3RkLCBmb3JtYXRzLmlzb1dlZWtEYXksIGZvcm1hdHMucGdUZXh0U2hvcnQsIGZvcm1hdHMucGdUZXh0UmV2ZXJzZSwgZm9ybWF0cy5jbGYsIGZvcm1hdHMueWVhcjQsIGZvcm1hdHMuYWdvLCBmb3JtYXRzLmRheVRleHQsIGZvcm1hdHMucmVsYXRpdmVUZXh0V2VlaywgZm9ybWF0cy5yZWxhdGl2ZVRleHQsIGZvcm1hdHMubW9udGhGdWxsT3JNb250aEFiYnIsIGZvcm1hdHMudHpDb3JyZWN0aW9uLCBmb3JtYXRzLnR6QWJiciwgZm9ybWF0cy5kYXRlU2hvcnRXaXRoVGltZVNob3J0MTIsIGZvcm1hdHMuZGF0ZVNob3J0V2l0aFRpbWVMb25nMTIsIGZvcm1hdHMuZGF0ZVNob3J0V2l0aFRpbWVTaG9ydCwgZm9ybWF0cy5kYXRlU2hvcnRXaXRoVGltZUxvbmcsIGZvcm1hdHMucmVsYXRpdmUsIGZvcm1hdHMud2hpdGVzcGFjZV07XG5cbiAgdmFyIHJlc3VsdCA9IE9iamVjdC5jcmVhdGUocmVzdWx0UHJvdG8pO1xuXG4gIHdoaWxlIChzdHIubGVuZ3RoKSB7XG4gICAgdmFyIGxvbmdlc3RNYXRjaCA9IG51bGw7XG4gICAgdmFyIGZpbmFsUnVsZSA9IG51bGw7XG5cbiAgICBmb3IgKHZhciBpID0gMCwgbCA9IHJ1bGVzLmxlbmd0aDsgaSA8IGw7IGkrKykge1xuICAgICAgdmFyIGZvcm1hdCA9IHJ1bGVzW2ldO1xuXG4gICAgICB2YXIgbWF0Y2ggPSBzdHIubWF0Y2goZm9ybWF0LnJlZ2V4KTtcblxuICAgICAgaWYgKG1hdGNoKSB7XG4gICAgICAgIGlmICghbG9uZ2VzdE1hdGNoIHx8IG1hdGNoWzBdLmxlbmd0aCA+IGxvbmdlc3RNYXRjaFswXS5sZW5ndGgpIHtcbiAgICAgICAgICBsb25nZXN0TWF0Y2ggPSBtYXRjaDtcbiAgICAgICAgICBmaW5hbFJ1bGUgPSBmb3JtYXQ7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoIWZpbmFsUnVsZSB8fCBmaW5hbFJ1bGUuY2FsbGJhY2sgJiYgZmluYWxSdWxlLmNhbGxiYWNrLmFwcGx5KHJlc3VsdCwgbG9uZ2VzdE1hdGNoKSA9PT0gZmFsc2UpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG5cbiAgICBzdHIgPSBzdHIuc3Vic3RyKGxvbmdlc3RNYXRjaFswXS5sZW5ndGgpO1xuICAgIGZpbmFsUnVsZSA9IG51bGw7XG4gICAgbG9uZ2VzdE1hdGNoID0gbnVsbDtcbiAgfVxuXG4gIHJldHVybiBNYXRoLmZsb29yKHJlc3VsdC50b0RhdGUobmV3IERhdGUobm93ICogMTAwMCkpIC8gMTAwMCk7XG59O1xuLy8jIHNvdXJjZU1hcHBpbmdVUkw9c3RydG90aW1lLmpzLm1hcCIsIid1c2Ugc3RyaWN0JztcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiBpbmlfZ2V0KHZhcm5hbWUpIHtcbiAgLy8gIGRpc2N1c3MgYXQ6IGh0dHBzOi8vbG9jdXR1cy5pby9waHAvaW5pX2dldC9cbiAgLy8gb3JpZ2luYWwgYnk6IEJyZXR0IFphbWlyIChodHRwczovL2JyZXR0LXphbWlyLm1lKVxuICAvLyAgICAgIG5vdGUgMTogVGhlIGluaSB2YWx1ZXMgbXVzdCBiZSBzZXQgYnkgaW5pX3NldCBvciBtYW51YWxseSB3aXRoaW4gYW4gaW5pIGZpbGVcbiAgLy8gICBleGFtcGxlIDE6IGluaV9zZXQoJ2RhdGUudGltZXpvbmUnLCAnQXNpYS9Ib25nX0tvbmcnKVxuICAvLyAgIGV4YW1wbGUgMTogaW5pX2dldCgnZGF0ZS50aW1lem9uZScpXG4gIC8vICAgcmV0dXJucyAxOiAnQXNpYS9Ib25nX0tvbmcnXG5cbiAgdmFyICRnbG9iYWwgPSB0eXBlb2Ygd2luZG93ICE9PSAndW5kZWZpbmVkJyA/IHdpbmRvdyA6IGdsb2JhbDtcbiAgJGdsb2JhbC4kbG9jdXR1cyA9ICRnbG9iYWwuJGxvY3V0dXMgfHwge307XG4gIHZhciAkbG9jdXR1cyA9ICRnbG9iYWwuJGxvY3V0dXM7XG4gICRsb2N1dHVzLnBocCA9ICRsb2N1dHVzLnBocCB8fCB7fTtcbiAgJGxvY3V0dXMucGhwLmluaSA9ICRsb2N1dHVzLnBocC5pbmkgfHwge307XG5cbiAgaWYgKCRsb2N1dHVzLnBocC5pbmlbdmFybmFtZV0gJiYgJGxvY3V0dXMucGhwLmluaVt2YXJuYW1lXS5sb2NhbF92YWx1ZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgaWYgKCRsb2N1dHVzLnBocC5pbmlbdmFybmFtZV0ubG9jYWxfdmFsdWUgPT09IG51bGwpIHtcbiAgICAgIHJldHVybiAnJztcbiAgICB9XG4gICAgcmV0dXJuICRsb2N1dHVzLnBocC5pbmlbdmFybmFtZV0ubG9jYWxfdmFsdWU7XG4gIH1cblxuICByZXR1cm4gJyc7XG59O1xuLy8jIHNvdXJjZU1hcHBpbmdVUkw9aW5pX2dldC5qcy5tYXAiLCIndXNlIHN0cmljdCc7XG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gc3RybGVuKHN0cmluZykge1xuICAvLyAgZGlzY3VzcyBhdDogaHR0cHM6Ly9sb2N1dHVzLmlvL3BocC9zdHJsZW4vXG4gIC8vIG9yaWdpbmFsIGJ5OiBLZXZpbiB2YW4gWm9ubmV2ZWxkIChodHRwczovL2t2ei5pbylcbiAgLy8gaW1wcm92ZWQgYnk6IFNha2ltb3JpXG4gIC8vIGltcHJvdmVkIGJ5OiBLZXZpbiB2YW4gWm9ubmV2ZWxkIChodHRwczovL2t2ei5pbylcbiAgLy8gICAgaW5wdXQgYnk6IEtpcmsgU3Ryb2JlY2tcbiAgLy8gYnVnZml4ZWQgYnk6IE9ubm8gTWFyc21hbiAoaHR0cHM6Ly90d2l0dGVyLmNvbS9vbm5vbWFyc21hbilcbiAgLy8gIHJldmlzZWQgYnk6IEJyZXR0IFphbWlyIChodHRwczovL2JyZXR0LXphbWlyLm1lKVxuICAvLyAgICAgIG5vdGUgMTogTWF5IGxvb2sgbGlrZSBvdmVya2lsbCwgYnV0IGluIG9yZGVyIHRvIGJlIHRydWx5IGZhaXRoZnVsIHRvIGhhbmRsaW5nIGFsbCBVbmljb2RlXG4gIC8vICAgICAgbm90ZSAxOiBjaGFyYWN0ZXJzIGFuZCB0byB0aGlzIGZ1bmN0aW9uIGluIFBIUCB3aGljaCBkb2VzIG5vdCBjb3VudCB0aGUgbnVtYmVyIG9mIGJ5dGVzXG4gIC8vICAgICAgbm90ZSAxOiBidXQgY291bnRzIHRoZSBudW1iZXIgb2YgY2hhcmFjdGVycywgc29tZXRoaW5nIGxpa2UgdGhpcyBpcyByZWFsbHkgbmVjZXNzYXJ5LlxuICAvLyAgIGV4YW1wbGUgMTogc3RybGVuKCdLZXZpbiB2YW4gWm9ubmV2ZWxkJylcbiAgLy8gICByZXR1cm5zIDE6IDE5XG4gIC8vICAgZXhhbXBsZSAyOiBpbmlfc2V0KCd1bmljb2RlLnNlbWFudGljcycsICdvbicpXG4gIC8vICAgZXhhbXBsZSAyOiBzdHJsZW4oJ0FcXHVkODdlXFx1ZGMwNFonKVxuICAvLyAgIHJldHVybnMgMjogM1xuXG4gIHZhciBzdHIgPSBzdHJpbmcgKyAnJztcblxuICB2YXIgaW5pVmFsID0gKHR5cGVvZiByZXF1aXJlICE9PSAndW5kZWZpbmVkJyA/IHJlcXVpcmUoJy4uL2luZm8vaW5pX2dldCcpKCd1bmljb2RlLnNlbWFudGljcycpIDogdW5kZWZpbmVkKSB8fCAnb2ZmJztcbiAgaWYgKGluaVZhbCA9PT0gJ29mZicpIHtcbiAgICByZXR1cm4gc3RyLmxlbmd0aDtcbiAgfVxuXG4gIHZhciBpID0gMDtcbiAgdmFyIGxndGggPSAwO1xuXG4gIHZhciBnZXRXaG9sZUNoYXIgPSBmdW5jdGlvbiBnZXRXaG9sZUNoYXIoc3RyLCBpKSB7XG4gICAgdmFyIGNvZGUgPSBzdHIuY2hhckNvZGVBdChpKTtcbiAgICB2YXIgbmV4dCA9ICcnO1xuICAgIHZhciBwcmV2ID0gJyc7XG4gICAgaWYgKGNvZGUgPj0gMHhkODAwICYmIGNvZGUgPD0gMHhkYmZmKSB7XG4gICAgICAvLyBIaWdoIHN1cnJvZ2F0ZSAoY291bGQgY2hhbmdlIGxhc3QgaGV4IHRvIDB4REI3RiB0b1xuICAgICAgLy8gdHJlYXQgaGlnaCBwcml2YXRlIHN1cnJvZ2F0ZXMgYXMgc2luZ2xlIGNoYXJhY3RlcnMpXG4gICAgICBpZiAoc3RyLmxlbmd0aCA8PSBpICsgMSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0hpZ2ggc3Vycm9nYXRlIHdpdGhvdXQgZm9sbG93aW5nIGxvdyBzdXJyb2dhdGUnKTtcbiAgICAgIH1cbiAgICAgIG5leHQgPSBzdHIuY2hhckNvZGVBdChpICsgMSk7XG4gICAgICBpZiAobmV4dCA8IDB4ZGMwMCB8fCBuZXh0ID4gMHhkZmZmKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignSGlnaCBzdXJyb2dhdGUgd2l0aG91dCBmb2xsb3dpbmcgbG93IHN1cnJvZ2F0ZScpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHN0ci5jaGFyQXQoaSkgKyBzdHIuY2hhckF0KGkgKyAxKTtcbiAgICB9IGVsc2UgaWYgKGNvZGUgPj0gMHhkYzAwICYmIGNvZGUgPD0gMHhkZmZmKSB7XG4gICAgICAvLyBMb3cgc3Vycm9nYXRlXG4gICAgICBpZiAoaSA9PT0gMCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0xvdyBzdXJyb2dhdGUgd2l0aG91dCBwcmVjZWRpbmcgaGlnaCBzdXJyb2dhdGUnKTtcbiAgICAgIH1cbiAgICAgIHByZXYgPSBzdHIuY2hhckNvZGVBdChpIC0gMSk7XG4gICAgICBpZiAocHJldiA8IDB4ZDgwMCB8fCBwcmV2ID4gMHhkYmZmKSB7XG4gICAgICAgIC8vIChjb3VsZCBjaGFuZ2UgbGFzdCBoZXggdG8gMHhEQjdGIHRvIHRyZWF0IGhpZ2ggcHJpdmF0ZSBzdXJyb2dhdGVzXG4gICAgICAgIC8vIGFzIHNpbmdsZSBjaGFyYWN0ZXJzKVxuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0xvdyBzdXJyb2dhdGUgd2l0aG91dCBwcmVjZWRpbmcgaGlnaCBzdXJyb2dhdGUnKTtcbiAgICAgIH1cbiAgICAgIC8vIFdlIGNhbiBwYXNzIG92ZXIgbG93IHN1cnJvZ2F0ZXMgbm93IGFzIHRoZSBzZWNvbmRcbiAgICAgIC8vIGNvbXBvbmVudCBpbiBhIHBhaXIgd2hpY2ggd2UgaGF2ZSBhbHJlYWR5IHByb2Nlc3NlZFxuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICByZXR1cm4gc3RyLmNoYXJBdChpKTtcbiAgfTtcblxuICBmb3IgKGkgPSAwLCBsZ3RoID0gMDsgaSA8IHN0ci5sZW5ndGg7IGkrKykge1xuICAgIGlmIChnZXRXaG9sZUNoYXIoc3RyLCBpKSA9PT0gZmFsc2UpIHtcbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cbiAgICAvLyBBZGFwdCB0aGlzIGxpbmUgYXQgdGhlIHRvcCBvZiBhbnkgbG9vcCwgcGFzc2luZyBpbiB0aGUgd2hvbGUgc3RyaW5nIGFuZFxuICAgIC8vIHRoZSBjdXJyZW50IGl0ZXJhdGlvbiBhbmQgcmV0dXJuaW5nIGEgdmFyaWFibGUgdG8gcmVwcmVzZW50IHRoZSBpbmRpdmlkdWFsIGNoYXJhY3RlcjtcbiAgICAvLyBwdXJwb3NlIGlzIHRvIHRyZWF0IHRoZSBmaXJzdCBwYXJ0IG9mIGEgc3Vycm9nYXRlIHBhaXIgYXMgdGhlIHdob2xlIGNoYXJhY3RlciBhbmQgdGhlblxuICAgIC8vIGlnbm9yZSB0aGUgc2Vjb25kIHBhcnRcbiAgICBsZ3RoKys7XG4gIH1cblxuICByZXR1cm4gbGd0aDtcbn07XG4vLyMgc291cmNlTWFwcGluZ1VSTD1zdHJsZW4uanMubWFwIiwiJ3VzZSBzdHJpY3QnO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIGlzX251bWVyaWMobWl4ZWRWYXIpIHtcbiAgLy8gIGRpc2N1c3MgYXQ6IGh0dHBzOi8vbG9jdXR1cy5pby9waHAvaXNfbnVtZXJpYy9cbiAgLy8gb3JpZ2luYWwgYnk6IEtldmluIHZhbiBab25uZXZlbGQgKGh0dHBzOi8va3Z6LmlvKVxuICAvLyBpbXByb3ZlZCBieTogRGF2aWRcbiAgLy8gaW1wcm92ZWQgYnk6IHRhaXRoXG4gIC8vIGJ1Z2ZpeGVkIGJ5OiBUaW0gZGUgS29uaW5nXG4gIC8vIGJ1Z2ZpeGVkIGJ5OiBXZWJEZXZIb2JvIChodHRwczovL3dlYmRldmhvYm8uYmxvZ3Nwb3QuY29tLylcbiAgLy8gYnVnZml4ZWQgYnk6IEJyZXR0IFphbWlyIChodHRwczovL2JyZXR0LXphbWlyLm1lKVxuICAvLyBidWdmaXhlZCBieTogRGVuaXMgQ2hlbnUgKGh0dHBzOi8vc2hub3VsbGUubmV0KVxuICAvLyAgIGV4YW1wbGUgMTogaXNfbnVtZXJpYygxODYuMzEpXG4gIC8vICAgcmV0dXJucyAxOiB0cnVlXG4gIC8vICAgZXhhbXBsZSAyOiBpc19udW1lcmljKCdLZXZpbiB2YW4gWm9ubmV2ZWxkJylcbiAgLy8gICByZXR1cm5zIDI6IGZhbHNlXG4gIC8vICAgZXhhbXBsZSAzOiBpc19udW1lcmljKCcgKzE4Ni4zMWUyJylcbiAgLy8gICByZXR1cm5zIDM6IHRydWVcbiAgLy8gICBleGFtcGxlIDQ6IGlzX251bWVyaWMoJycpXG4gIC8vICAgcmV0dXJucyA0OiBmYWxzZVxuICAvLyAgIGV4YW1wbGUgNTogaXNfbnVtZXJpYyhbXSlcbiAgLy8gICByZXR1cm5zIDU6IGZhbHNlXG4gIC8vICAgZXhhbXBsZSA2OiBpc19udW1lcmljKCcxICcpXG4gIC8vICAgcmV0dXJucyA2OiBmYWxzZVxuXG4gIHZhciB3aGl0ZXNwYWNlID0gWycgJywgJ1xcbicsICdcXHInLCAnXFx0JywgJ1xcZicsICdcXHgwYicsICdcXHhhMCcsICdcXHUyMDAwJywgJ1xcdTIwMDEnLCAnXFx1MjAwMicsICdcXHUyMDAzJywgJ1xcdTIwMDQnLCAnXFx1MjAwNScsICdcXHUyMDA2JywgJ1xcdTIwMDcnLCAnXFx1MjAwOCcsICdcXHUyMDA5JywgJ1xcdTIwMEEnLCAnXFx1MjAwQicsICdcXHUyMDI4JywgJ1xcdTIwMjknLCAnXFx1MzAwMCddLmpvaW4oJycpO1xuXG4gIC8vIEB0b2RvOiBCcmVhayB0aGlzIHVwIHVzaW5nIG1hbnkgc2luZ2xlIGNvbmRpdGlvbnMgd2l0aCBlYXJseSByZXR1cm5zXG4gIHJldHVybiAodHlwZW9mIG1peGVkVmFyID09PSAnbnVtYmVyJyB8fCB0eXBlb2YgbWl4ZWRWYXIgPT09ICdzdHJpbmcnICYmIHdoaXRlc3BhY2UuaW5kZXhPZihtaXhlZFZhci5zbGljZSgtMSkpID09PSAtMSkgJiYgbWl4ZWRWYXIgIT09ICcnICYmICFpc05hTihtaXhlZFZhcik7XG59O1xuLy8jIHNvdXJjZU1hcHBpbmdVUkw9aXNfbnVtZXJpYy5qcy5tYXAiLCIvLyBUaGUgbW9kdWxlIGNhY2hlXG52YXIgX193ZWJwYWNrX21vZHVsZV9jYWNoZV9fID0ge307XG5cbi8vIFRoZSByZXF1aXJlIGZ1bmN0aW9uXG5mdW5jdGlvbiBfX3dlYnBhY2tfcmVxdWlyZV9fKG1vZHVsZUlkKSB7XG5cdC8vIENoZWNrIGlmIG1vZHVsZSBpcyBpbiBjYWNoZVxuXHR2YXIgY2FjaGVkTW9kdWxlID0gX193ZWJwYWNrX21vZHVsZV9jYWNoZV9fW21vZHVsZUlkXTtcblx0aWYgKGNhY2hlZE1vZHVsZSAhPT0gdW5kZWZpbmVkKSB7XG5cdFx0cmV0dXJuIGNhY2hlZE1vZHVsZS5leHBvcnRzO1xuXHR9XG5cdC8vIENyZWF0ZSBhIG5ldyBtb2R1bGUgKGFuZCBwdXQgaXQgaW50byB0aGUgY2FjaGUpXG5cdHZhciBtb2R1bGUgPSBfX3dlYnBhY2tfbW9kdWxlX2NhY2hlX19bbW9kdWxlSWRdID0ge1xuXHRcdC8vIG5vIG1vZHVsZS5pZCBuZWVkZWRcblx0XHQvLyBubyBtb2R1bGUubG9hZGVkIG5lZWRlZFxuXHRcdGV4cG9ydHM6IHt9XG5cdH07XG5cblx0Ly8gRXhlY3V0ZSB0aGUgbW9kdWxlIGZ1bmN0aW9uXG5cdF9fd2VicGFja19tb2R1bGVzX19bbW9kdWxlSWRdKG1vZHVsZSwgbW9kdWxlLmV4cG9ydHMsIF9fd2VicGFja19yZXF1aXJlX18pO1xuXG5cdC8vIFJldHVybiB0aGUgZXhwb3J0cyBvZiB0aGUgbW9kdWxlXG5cdHJldHVybiBtb2R1bGUuZXhwb3J0cztcbn1cblxuIiwiLy8gZ2V0RGVmYXVsdEV4cG9ydCBmdW5jdGlvbiBmb3IgY29tcGF0aWJpbGl0eSB3aXRoIG5vbi1oYXJtb255IG1vZHVsZXNcbl9fd2VicGFja19yZXF1aXJlX18ubiA9IGZ1bmN0aW9uKG1vZHVsZSkge1xuXHR2YXIgZ2V0dGVyID0gbW9kdWxlICYmIG1vZHVsZS5fX2VzTW9kdWxlID9cblx0XHRmdW5jdGlvbigpIHsgcmV0dXJuIG1vZHVsZVsnZGVmYXVsdCddOyB9IDpcblx0XHRmdW5jdGlvbigpIHsgcmV0dXJuIG1vZHVsZTsgfTtcblx0X193ZWJwYWNrX3JlcXVpcmVfXy5kKGdldHRlciwgeyBhOiBnZXR0ZXIgfSk7XG5cdHJldHVybiBnZXR0ZXI7XG59OyIsIi8vIGRlZmluZSBnZXR0ZXIgZnVuY3Rpb25zIGZvciBoYXJtb255IGV4cG9ydHNcbl9fd2VicGFja19yZXF1aXJlX18uZCA9IGZ1bmN0aW9uKGV4cG9ydHMsIGRlZmluaXRpb24pIHtcblx0Zm9yKHZhciBrZXkgaW4gZGVmaW5pdGlvbikge1xuXHRcdGlmKF9fd2VicGFja19yZXF1aXJlX18ubyhkZWZpbml0aW9uLCBrZXkpICYmICFfX3dlYnBhY2tfcmVxdWlyZV9fLm8oZXhwb3J0cywga2V5KSkge1xuXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIGtleSwgeyBlbnVtZXJhYmxlOiB0cnVlLCBnZXQ6IGRlZmluaXRpb25ba2V5XSB9KTtcblx0XHR9XG5cdH1cbn07IiwiX193ZWJwYWNrX3JlcXVpcmVfXy5nID0gKGZ1bmN0aW9uKCkge1xuXHRpZiAodHlwZW9mIGdsb2JhbFRoaXMgPT09ICdvYmplY3QnKSByZXR1cm4gZ2xvYmFsVGhpcztcblx0dHJ5IHtcblx0XHRyZXR1cm4gdGhpcyB8fCBuZXcgRnVuY3Rpb24oJ3JldHVybiB0aGlzJykoKTtcblx0fSBjYXRjaCAoZSkge1xuXHRcdGlmICh0eXBlb2Ygd2luZG93ID09PSAnb2JqZWN0JykgcmV0dXJuIHdpbmRvdztcblx0fVxufSkoKTsiLCJfX3dlYnBhY2tfcmVxdWlyZV9fLm8gPSBmdW5jdGlvbihvYmosIHByb3ApIHsgcmV0dXJuIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChvYmosIHByb3ApOyB9IiwiLy8gZGVmaW5lIF9fZXNNb2R1bGUgb24gZXhwb3J0c1xuX193ZWJwYWNrX3JlcXVpcmVfXy5yID0gZnVuY3Rpb24oZXhwb3J0cykge1xuXHRpZih0eXBlb2YgU3ltYm9sICE9PSAndW5kZWZpbmVkJyAmJiBTeW1ib2wudG9TdHJpbmdUYWcpIHtcblx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgU3ltYm9sLnRvU3RyaW5nVGFnLCB7IHZhbHVlOiAnTW9kdWxlJyB9KTtcblx0fVxuXHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgJ19fZXNNb2R1bGUnLCB7IHZhbHVlOiB0cnVlIH0pO1xufTsiLCIvKiFcbiAqIExhcmF2ZWwgSmF2YXNjcmlwdCBWYWxpZGF0aW9uXG4gKlxuICogaHR0cHM6Ly9naXRodWIuY29tL3Byb2VuZ3NvZnQvbGFyYXZlbC1qc3ZhbGlkYXRpb25cbiAqXG4gKiBIZWxwZXIgZnVuY3Rpb25zIHVzZWQgYnkgdmFsaWRhdG9yc1xuICpcbiAqIENvcHlyaWdodCAoYykgMjAxNyBQcm9lbmdzb2Z0XG4gKiBSZWxlYXNlZCB1bmRlciB0aGUgTUlUIGxpY2Vuc2VcbiAqL1xuXG5pbXBvcnQgc3RybGVuIGZyb20gJ2xvY3V0dXMvcGhwL3N0cmluZ3Mvc3RybGVuJztcbmltcG9ydCBhcnJheV9kaWZmIGZyb20gJ2xvY3V0dXMvcGhwL2FycmF5L2FycmF5X2RpZmYnO1xuaW1wb3J0IHN0cnRvdGltZSBmcm9tICdsb2N1dHVzL3BocC9kYXRldGltZS9zdHJ0b3RpbWUnO1xuaW1wb3J0IGlzX251bWVyaWMgZnJvbSAnbG9jdXR1cy9waHAvdmFyL2lzX251bWVyaWMnO1xuXG4kLmV4dGVuZCh0cnVlLCBsYXJhdmVsVmFsaWRhdGlvbiwge1xuXG4gICAgaGVscGVyczoge1xuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBOdW1lcmljIHJ1bGVzXG4gICAgICAgICAqL1xuICAgICAgICBudW1lcmljUnVsZXM6IFsnSW50ZWdlcicsICdOdW1lcmljJ10sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIEdldHMgdGhlIGZpbGUgaW5mb3JtYXRpb24gZnJvbSBmaWxlIGlucHV0LlxuICAgICAgICAgKlxuICAgICAgICAgKiBAcGFyYW0gZmllbGRPYmpcbiAgICAgICAgICogQHBhcmFtIGluZGV4XG4gICAgICAgICAqIEByZXR1cm5zIHt7ZmlsZTogKiwgZXh0ZW5zaW9uOiBzdHJpbmcsIHNpemU6IG51bWJlcn19XG4gICAgICAgICAqL1xuICAgICAgICBmaWxlaW5mbzogZnVuY3Rpb24gKGZpZWxkT2JqLCBpbmRleCkge1xuICAgICAgICAgICAgdmFyIEZpbGVOYW1lID0gZmllbGRPYmoudmFsdWU7XG4gICAgICAgICAgICBpbmRleCA9IHR5cGVvZiBpbmRleCAhPT0gJ3VuZGVmaW5lZCcgPyBpbmRleCA6IDA7XG4gICAgICAgICAgICBpZiAoIGZpZWxkT2JqLmZpbGVzICE9PSBudWxsICkge1xuICAgICAgICAgICAgICAgIGlmICh0eXBlb2YgZmllbGRPYmouZmlsZXNbaW5kZXhdICE9PSAndW5kZWZpbmVkJykge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICAgICAgICAgICAgZmlsZTogRmlsZU5hbWUsXG4gICAgICAgICAgICAgICAgICAgICAgICBleHRlbnNpb246IEZpbGVOYW1lLnN1YnN0cihGaWxlTmFtZS5sYXN0SW5kZXhPZignLicpICsgMSksXG4gICAgICAgICAgICAgICAgICAgICAgICBzaXplOiBmaWVsZE9iai5maWxlc1tpbmRleF0uc2l6ZSAvIDEwMjQsXG4gICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiBmaWVsZE9iai5maWxlc1tpbmRleF0udHlwZVxuICAgICAgICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgfSxcblxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBHZXRzIHRoZSBzZWxlY3RvcnMgZm9yIHRoIHNwZWNpZmllZCBmaWVsZCBuYW1lcy5cbiAgICAgICAgICpcbiAgICAgICAgICogQHBhcmFtIG5hbWVzXG4gICAgICAgICAqIEByZXR1cm5zIHtzdHJpbmd9XG4gICAgICAgICAqL1xuICAgICAgICBzZWxlY3RvcjogZnVuY3Rpb24gKG5hbWVzKSB7XG4gICAgICAgICAgICB2YXIgc2VsZWN0b3IgPSBbXTtcbiAgICAgICAgICAgIGlmICghIHRoaXMuaXNBcnJheShuYW1lcykpICB7XG4gICAgICAgICAgICAgICAgbmFtZXMgPSBbbmFtZXNdO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBuYW1lcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgIHNlbGVjdG9yLnB1c2goXCJbbmFtZT0nXCIgKyBuYW1lc1tpXSArIFwiJ11cIik7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gc2VsZWN0b3Iuam9pbigpO1xuICAgICAgICB9LFxuXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIENoZWNrIGlmIGVsZW1lbnQgaGFzIG51bWVyaWMgcnVsZXMuXG4gICAgICAgICAqXG4gICAgICAgICAqIEBwYXJhbSBlbGVtZW50XG4gICAgICAgICAqIEByZXR1cm5zIHtib29sZWFufVxuICAgICAgICAgKi9cbiAgICAgICAgaGFzTnVtZXJpY1J1bGVzOiBmdW5jdGlvbiAoZWxlbWVudCkge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMuaGFzUnVsZXMoZWxlbWVudCwgdGhpcy5udW1lcmljUnVsZXMpO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBDaGVjayBpZiBlbGVtZW50IGhhcyBwYXNzZWQgcnVsZXMuXG4gICAgICAgICAqXG4gICAgICAgICAqIEBwYXJhbSBlbGVtZW50XG4gICAgICAgICAqIEBwYXJhbSBydWxlc1xuICAgICAgICAgKiBAcmV0dXJucyB7Ym9vbGVhbn1cbiAgICAgICAgICovXG4gICAgICAgIGhhc1J1bGVzOiBmdW5jdGlvbiAoZWxlbWVudCwgcnVsZXMpIHtcblxuICAgICAgICAgICAgdmFyIGZvdW5kID0gZmFsc2U7XG4gICAgICAgICAgICBpZiAodHlwZW9mIHJ1bGVzID09PSAnc3RyaW5nJykge1xuICAgICAgICAgICAgICAgIHJ1bGVzID0gW3J1bGVzXTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdmFyIHZhbGlkYXRvciA9ICQuZGF0YShlbGVtZW50LmZvcm0sIFwidmFsaWRhdG9yXCIpO1xuICAgICAgICAgICAgdmFyIGxpc3RSdWxlcyA9IFtdO1xuICAgICAgICAgICAgdmFyIGNhY2hlID0gdmFsaWRhdG9yLmFycmF5UnVsZXNDYWNoZTtcbiAgICAgICAgICAgIGlmIChlbGVtZW50Lm5hbWUgaW4gY2FjaGUpIHtcbiAgICAgICAgICAgICAgICAkLmVhY2goY2FjaGVbZWxlbWVudC5uYW1lXSwgZnVuY3Rpb24gKGluZGV4LCBhcnJheVJ1bGUpIHtcbiAgICAgICAgICAgICAgICAgICAgbGlzdFJ1bGVzLnB1c2goYXJyYXlSdWxlKTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmIChlbGVtZW50Lm5hbWUgaW4gdmFsaWRhdG9yLnNldHRpbmdzLnJ1bGVzKSB7XG4gICAgICAgICAgICAgICAgbGlzdFJ1bGVzLnB1c2godmFsaWRhdG9yLnNldHRpbmdzLnJ1bGVzW2VsZW1lbnQubmFtZV0pO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgJC5lYWNoKGxpc3RSdWxlcywgZnVuY3Rpb24oaW5kZXgsb2JqUnVsZXMpe1xuICAgICAgICAgICAgICAgIGlmICgnbGFyYXZlbFZhbGlkYXRpb24nIGluIG9ialJ1bGVzKSB7XG4gICAgICAgICAgICAgICAgICAgIHZhciBfcnVsZXM9b2JqUnVsZXMubGFyYXZlbFZhbGlkYXRpb247XG4gICAgICAgICAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgX3J1bGVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoJC5pbkFycmF5KF9ydWxlc1tpXVswXSxydWxlcykgIT09IC0xKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZm91bmQgPSB0cnVlO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICByZXR1cm4gZm91bmQ7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIFJldHVybiB0aGUgc3RyaW5nIGxlbmd0aCB1c2luZyBQSFAgZnVuY3Rpb24uXG4gICAgICAgICAqIGh0dHA6Ly9waHAubmV0L21hbnVhbC9lbi9mdW5jdGlvbi5zdHJsZW4ucGhwXG4gICAgICAgICAqIGh0dHA6Ly9waHBqcy5vcmcvZnVuY3Rpb25zL3N0cmxlbi9cbiAgICAgICAgICpcbiAgICAgICAgICogQHBhcmFtIHN0cmluZ1xuICAgICAgICAgKi9cbiAgICAgICAgc3RybGVuOiBmdW5jdGlvbiAoc3RyaW5nKSB7XG4gICAgICAgICAgICByZXR1cm4gc3RybGVuKHN0cmluZyk7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIEdldCB0aGUgc2l6ZSBvZiB0aGUgb2JqZWN0IGRlcGVuZGluZyBvZiBoaXMgdHlwZS5cbiAgICAgICAgICpcbiAgICAgICAgICogQHBhcmFtIG9ialxuICAgICAgICAgKiBAcGFyYW0gZWxlbWVudFxuICAgICAgICAgKiBAcGFyYW0gdmFsdWVcbiAgICAgICAgICogQHJldHVybnMgaW50XG4gICAgICAgICAqL1xuICAgICAgICBnZXRTaXplOiBmdW5jdGlvbiBnZXRTaXplKG9iaiwgZWxlbWVudCwgdmFsdWUpIHtcblxuICAgICAgICAgICAgaWYgKHRoaXMuaGFzTnVtZXJpY1J1bGVzKGVsZW1lbnQpICYmIHRoaXMuaXNfbnVtZXJpYyh2YWx1ZSkpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gcGFyc2VGbG9hdCh2YWx1ZSk7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKHRoaXMuaXNBcnJheSh2YWx1ZSkpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gcGFyc2VGbG9hdCh2YWx1ZS5sZW5ndGgpO1xuICAgICAgICAgICAgfSBlbHNlIGlmIChlbGVtZW50LnR5cGUgPT09ICdmaWxlJykge1xuICAgICAgICAgICAgICAgIHJldHVybiBwYXJzZUZsb2F0KE1hdGguZmxvb3IodGhpcy5maWxlaW5mbyhlbGVtZW50KS5zaXplKSk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHJldHVybiBwYXJzZUZsb2F0KHRoaXMuc3RybGVuKHZhbHVlKSk7XG4gICAgICAgIH0sXG5cblxuICAgICAgICAvKipcbiAgICAgICAgICogUmV0dXJuIHNwZWNpZmllZCBydWxlIGZyb20gZWxlbWVudC5cbiAgICAgICAgICpcbiAgICAgICAgICogQHBhcmFtIHJ1bGVcbiAgICAgICAgICogQHBhcmFtIGVsZW1lbnRcbiAgICAgICAgICogQHJldHVybnMgb2JqZWN0XG4gICAgICAgICAqL1xuICAgICAgICBnZXRMYXJhdmVsVmFsaWRhdGlvbjogZnVuY3Rpb24ocnVsZSwgZWxlbWVudCkge1xuXG4gICAgICAgICAgICB2YXIgZm91bmQgPSB1bmRlZmluZWQ7XG4gICAgICAgICAgICAkLmVhY2goJC52YWxpZGF0b3Iuc3RhdGljUnVsZXMoZWxlbWVudCksIGZ1bmN0aW9uKGtleSwgcnVsZXMpIHtcbiAgICAgICAgICAgICAgICBpZiAoa2V5PT09XCJsYXJhdmVsVmFsaWRhdGlvblwiKSB7XG4gICAgICAgICAgICAgICAgICAgICQuZWFjaChydWxlcywgZnVuY3Rpb24gKGksIHZhbHVlKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAodmFsdWVbMF09PT1ydWxlKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZm91bmQ9dmFsdWU7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICByZXR1cm4gZm91bmQ7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIFJldHVybiBoZSB0aW1lc3RhbXAgb2YgdmFsdWUgcGFzc2VkIHVzaW5nIGZvcm1hdCBvciBkZWZhdWx0IGZvcm1hdCBpbiBlbGVtZW50LlxuICAgICAgICAgKlxuICAgICAgICAgKiBAcGFyYW0gdmFsdWVcbiAgICAgICAgICogQHBhcmFtIGZvcm1hdFxuICAgICAgICAgKiBAcmV0dXJucyB7Ym9vbGVhbnxpbnR9XG4gICAgICAgICAqL1xuICAgICAgICBwYXJzZVRpbWU6IGZ1bmN0aW9uICh2YWx1ZSwgZm9ybWF0KSB7XG5cbiAgICAgICAgICAgIHZhciB0aW1lVmFsdWUgPSBmYWxzZTtcbiAgICAgICAgICAgIHZhciBmbXQgPSBuZXcgRGF0ZUZvcm1hdHRlcigpO1xuXG4gICAgICAgICAgICBpZiAodHlwZW9mIHZhbHVlID09PSAnbnVtYmVyJyAmJiB0eXBlb2YgZm9ybWF0ID09PSAndW5kZWZpbmVkJykge1xuICAgICAgICAgICAgICAgIHJldHVybiB2YWx1ZTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKHR5cGVvZiBmb3JtYXQgPT09ICdvYmplY3QnKSB7XG4gICAgICAgICAgICAgICAgdmFyIGRhdGVSdWxlID0gdGhpcy5nZXRMYXJhdmVsVmFsaWRhdGlvbignRGF0ZUZvcm1hdCcsIGZvcm1hdCk7XG4gICAgICAgICAgICAgICAgaWYgKGRhdGVSdWxlICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgICAgICAgZm9ybWF0ID0gZGF0ZVJ1bGVbMV1bMF07XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgZm9ybWF0ID0gbnVsbDtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmIChmb3JtYXQgPT0gbnVsbCkge1xuICAgICAgICAgICAgICAgIHRpbWVWYWx1ZSA9IHRoaXMuc3RydG90aW1lKHZhbHVlKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgdGltZVZhbHVlID0gZm10LnBhcnNlRGF0ZSh2YWx1ZSwgZm9ybWF0KTtcbiAgICAgICAgICAgICAgICBpZiAodGltZVZhbHVlIGluc3RhbmNlb2YgRGF0ZSAmJiBmbXQuZm9ybWF0RGF0ZSh0aW1lVmFsdWUsIGZvcm1hdCkgPT09IHZhbHVlKSB7XG4gICAgICAgICAgICAgICAgICAgIHRpbWVWYWx1ZSA9IE1hdGgucm91bmQoKHRpbWVWYWx1ZS5nZXRUaW1lKCkgLyAxMDAwKSk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgdGltZVZhbHVlID0gZmFsc2U7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICByZXR1cm4gdGltZVZhbHVlO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBDb21wYXJlIGEgZ2l2ZW4gZGF0ZSBhZ2FpbnN0IGFub3RoZXIgdXNpbmcgYW4gb3BlcmF0b3IuXG4gICAgICAgICAqXG4gICAgICAgICAqIEBwYXJhbSB2YWxpZGF0b3JcbiAgICAgICAgICogQHBhcmFtIHZhbHVlXG4gICAgICAgICAqIEBwYXJhbSBlbGVtZW50XG4gICAgICAgICAqIEBwYXJhbSBwYXJhbXNcbiAgICAgICAgICogQHBhcmFtIG9wZXJhdG9yXG4gICAgICAgICAqIEByZXR1cm4ge2Jvb2xlYW59XG4gICAgICAgICAqL1xuICAgICAgICBjb21wYXJlRGF0ZXM6IGZ1bmN0aW9uICh2YWxpZGF0b3IsIHZhbHVlLCBlbGVtZW50LCBwYXJhbXMsIG9wZXJhdG9yKSB7XG5cbiAgICAgICAgICAgIHZhciB0aW1lQ29tcGFyZSA9IHRoaXMucGFyc2VUaW1lKHBhcmFtcyk7XG5cbiAgICAgICAgICAgIGlmICghdGltZUNvbXBhcmUpIHtcbiAgICAgICAgICAgICAgICB2YXIgdGFyZ2V0ID0gdGhpcy5kZXBlbmRlbnRFbGVtZW50KHZhbGlkYXRvciwgZWxlbWVudCwgcGFyYW1zKTtcbiAgICAgICAgICAgICAgICBpZiAodGFyZ2V0ID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB0aW1lQ29tcGFyZSA9IHRoaXMucGFyc2VUaW1lKHZhbGlkYXRvci5lbGVtZW50VmFsdWUodGFyZ2V0KSwgdGFyZ2V0KTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdmFyIHRpbWVWYWx1ZSA9IHRoaXMucGFyc2VUaW1lKHZhbHVlLCBlbGVtZW50KTtcbiAgICAgICAgICAgIGlmICh0aW1lVmFsdWUgPT09IGZhbHNlKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBzd2l0Y2ggKG9wZXJhdG9yKSB7XG4gICAgICAgICAgICAgICAgY2FzZSAnPCc6XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiB0aW1lVmFsdWUgPCB0aW1lQ29tcGFyZTtcblxuICAgICAgICAgICAgICAgIGNhc2UgJzw9JzpcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHRpbWVWYWx1ZSA8PSB0aW1lQ29tcGFyZTtcblxuICAgICAgICAgICAgICAgIGNhc2UgJz09JzpcbiAgICAgICAgICAgICAgICBjYXNlICc9PT0nOlxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gdGltZVZhbHVlID09PSB0aW1lQ29tcGFyZTtcblxuICAgICAgICAgICAgICAgIGNhc2UgJz4nOlxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gdGltZVZhbHVlID4gdGltZUNvbXBhcmU7XG5cbiAgICAgICAgICAgICAgICBjYXNlICc+PSc6XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiB0aW1lVmFsdWUgPj0gdGltZUNvbXBhcmU7XG5cbiAgICAgICAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1Vuc3VwcG9ydGVkIG9wZXJhdG9yLicpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBUaGlzIG1ldGhvZCBhbGxvd3MgeW91IHRvIGludGVsbGlnZW50bHkgZ3Vlc3MgdGhlIGRhdGUgYnkgY2xvc2VseSBtYXRjaGluZyB0aGUgc3BlY2lmaWMgZm9ybWF0LlxuICAgICAgICAgKlxuICAgICAgICAgKiBAcGFyYW0gdmFsdWVcbiAgICAgICAgICogQHBhcmFtIGZvcm1hdFxuICAgICAgICAgKiBAcmV0dXJucyB7RGF0ZX1cbiAgICAgICAgICovXG4gICAgICAgIGd1ZXNzRGF0ZTogZnVuY3Rpb24gKHZhbHVlLCBmb3JtYXQpIHtcbiAgICAgICAgICAgIHZhciBmbXQgPSBuZXcgRGF0ZUZvcm1hdHRlcigpO1xuICAgICAgICAgICAgcmV0dXJuIGZtdC5ndWVzc0RhdGUodmFsdWUsIGZvcm1hdClcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogUmV0dXJucyBVbml4IHRpbWVzdGFtcCBiYXNlZCBvbiBQSFAgZnVuY3Rpb24gc3Ryb3RvdGltZS5cbiAgICAgICAgICogaHR0cDovL3BocC5uZXQvbWFudWFsL2VzL2Z1bmN0aW9uLnN0cnRvdGltZS5waHBcbiAgICAgICAgICogaHR0cDovL3BocGpzLm9yZy9mdW5jdGlvbnMvc3RydG90aW1lL1xuICAgICAgICAgKlxuICAgICAgICAgKiBAcGFyYW0gdGV4dFxuICAgICAgICAgKiBAcGFyYW0gbm93XG4gICAgICAgICAqIEByZXR1cm5zIHsqfVxuICAgICAgICAgKi9cbiAgICAgICAgc3RydG90aW1lOiBmdW5jdGlvbiAodGV4dCwgbm93KSB7XG4gICAgICAgICAgICByZXR1cm4gc3RydG90aW1lKHRleHQsIG5vdylcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogUmV0dXJucyBpZiB2YWx1ZSBpcyBudW1lcmljLlxuICAgICAgICAgKiBodHRwOi8vcGhwLm5ldC9tYW51YWwvZXMvdmFyLmlzX251bWVyaWMucGhwXG4gICAgICAgICAqIGh0dHA6Ly9waHBqcy5vcmcvZnVuY3Rpb25zL2lzX251bWVyaWMvXG4gICAgICAgICAqXG4gICAgICAgICAqIEBwYXJhbSBtaXhlZF92YXJcbiAgICAgICAgICogQHJldHVybnMgeyp9XG4gICAgICAgICAqL1xuICAgICAgICBpc19udW1lcmljOiBmdW5jdGlvbiAobWl4ZWRfdmFyKSB7XG4gICAgICAgICAgICByZXR1cm4gaXNfbnVtZXJpYyhtaXhlZF92YXIpXG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIENoZWNrIHdoZXRoZXIgdGhlIGFyZ3VtZW50IGlzIG9mIHR5cGUgQXJyYXkuXG4gICAgICAgICAqIGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0phdmFTY3JpcHQvUmVmZXJlbmNlL0dsb2JhbF9PYmplY3RzL0FycmF5L2lzQXJyYXkjUG9seWZpbGxcbiAgICAgICAgICpcbiAgICAgICAgICogQHBhcmFtIGFyZ1xuICAgICAgICAgKiBAcmV0dXJucyB7Ym9vbGVhbn1cbiAgICAgICAgICovXG4gICAgICAgIGlzQXJyYXk6IGZ1bmN0aW9uKGFyZykge1xuICAgICAgICAgICAgcmV0dXJuIE9iamVjdC5wcm90b3R5cGUudG9TdHJpbmcuY2FsbChhcmcpID09PSAnW29iamVjdCBBcnJheV0nO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBSZXR1cm5zIEFycmF5IGRpZmYgYmFzZWQgb24gUEhQIGZ1bmN0aW9uIGFycmF5X2RpZmYuXG4gICAgICAgICAqIGh0dHA6Ly9waHAubmV0L21hbnVhbC9lcy9mdW5jdGlvbi5hcnJheV9kaWZmLnBocFxuICAgICAgICAgKiBodHRwOi8vcGhwanMub3JnL2Z1bmN0aW9ucy9hcnJheV9kaWZmL1xuICAgICAgICAgKlxuICAgICAgICAgKiBAcGFyYW0gYXJyMVxuICAgICAgICAgKiBAcGFyYW0gYXJyMlxuICAgICAgICAgKiBAcmV0dXJucyB7Kn1cbiAgICAgICAgICovXG4gICAgICAgIGFycmF5RGlmZjogZnVuY3Rpb24gKGFycjEsIGFycjIpIHtcbiAgICAgICAgICAgIHJldHVybiBhcnJheV9kaWZmKGFycjEsIGFycjIpO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBDaGVjayB3aGV0aGVyIHR3byBhcnJheXMgYXJlIGVxdWFsIHRvIG9uZSBhbm90aGVyLlxuICAgICAgICAgKlxuICAgICAgICAgKiBAcGFyYW0gYXJyMVxuICAgICAgICAgKiBAcGFyYW0gYXJyMlxuICAgICAgICAgKiBAcmV0dXJucyB7Kn1cbiAgICAgICAgICovXG4gICAgICAgIGFycmF5RXF1YWxzOiBmdW5jdGlvbiAoYXJyMSwgYXJyMikge1xuICAgICAgICAgICAgaWYgKCEgdGhpcy5pc0FycmF5KGFycjEpIHx8ICEgdGhpcy5pc0FycmF5KGFycjIpKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgXG4gICAgICAgICAgICBpZiAoYXJyMS5sZW5ndGggIT09IGFycjIubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgXG4gICAgICAgICAgICByZXR1cm4gJC5pc0VtcHR5T2JqZWN0KHRoaXMuYXJyYXlEaWZmKGFycjEsIGFycjIpKTtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogTWFrZXMgZWxlbWVudCBkZXBlbmRhbnQgZnJvbSBvdGhlci5cbiAgICAgICAgICpcbiAgICAgICAgICogQHBhcmFtIHZhbGlkYXRvclxuICAgICAgICAgKiBAcGFyYW0gZWxlbWVudFxuICAgICAgICAgKiBAcGFyYW0gbmFtZVxuICAgICAgICAgKiBAcmV0dXJucyB7Kn1cbiAgICAgICAgICovXG4gICAgICAgIGRlcGVuZGVudEVsZW1lbnQ6IGZ1bmN0aW9uKHZhbGlkYXRvciwgZWxlbWVudCwgbmFtZSkge1xuICAgICAgICAgICAgdmFyIGVsID0gdmFsaWRhdG9yLmZpbmRCeU5hbWUobmFtZSk7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIHZhciB0YXJnZXRFbGVtZW50ID0gZWxbZWwubGVuZ3RoIC0gMV07XG4gICAgXG4gICAgICAgICAgICBpZiAodGFyZ2V0RWxlbWVudCAhPT0gdW5kZWZpbmVkICYmIHZhbGlkYXRvci5zZXR0aW5ncy5vbmZvY3Vzb3V0KSB7XG4gICAgICAgICAgICAgICAgdmFyIGV2ZW50ID0gJ2JsdXInO1xuICAgICAgICAgICAgICAgIGlmIChcbiAgICAgICAgICAgICAgICAgICAgdGFyZ2V0RWxlbWVudC50YWdOYW1lID09PSAnU0VMRUNUJyB8fFxuICAgICAgICAgICAgICAgICAgICB0YXJnZXRFbGVtZW50LnRhZ05hbWUgPT09ICdPUFRJT04nIHx8XG4gICAgICAgICAgICAgICAgICAgIHRhcmdldEVsZW1lbnQudHlwZSA9PT0gJ2NoZWNrYm94JyB8fFxuICAgICAgICAgICAgICAgICAgICB0YXJnZXRFbGVtZW50LnR5cGUgPT09ICdyYWRpbydcbiAgICAgICAgICAgICAgICApIHtcbiAgICAgICAgICAgICAgICAgICAgZXZlbnQgPSAnY2xpY2snO1xuICAgICAgICAgICAgICAgIH1cbiAgICBcbiAgICAgICAgICAgICAgICB2YXIgcnVsZU5hbWUgPSAnLnZhbGlkYXRlLWxhcmF2ZWxWYWxpZGF0aW9uJztcbiAgICAgICAgICAgICAgICAkKHRhcmdldEVsZW1lbnQpXG4gICAgICAgICAgICAgICAgICAgIC5vZmYocnVsZU5hbWUpXG4gICAgICAgICAgICAgICAgICAgIC5vZmYoZXZlbnQgKyBydWxlTmFtZSArICctJyArIGVsZW1lbnQubmFtZSlcbiAgICAgICAgICAgICAgICAgICAgLm9uKGV2ZW50ICsgcnVsZU5hbWUgKyAnLScgKyBlbGVtZW50Lm5hbWUsIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICQoZWxlbWVudCkudmFsaWQoKTtcbiAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9XG4gICAgXG4gICAgICAgICAgICByZXR1cm4gdGFyZ2V0RWxlbWVudDtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogUGFyc2VzIGVycm9yIEFqYXggcmVzcG9uc2UgYW5kIGdldHMgdGhlIG1lc3NhZ2UuXG4gICAgICAgICAqXG4gICAgICAgICAqIEBwYXJhbSByZXNwb25zZVxuICAgICAgICAgKiBAcmV0dXJucyB7c3RyaW5nW119XG4gICAgICAgICAqL1xuICAgICAgICBwYXJzZUVycm9yUmVzcG9uc2U6IGZ1bmN0aW9uIChyZXNwb25zZSkge1xuICAgICAgICAgICAgdmFyIG5ld1Jlc3BvbnNlID0gWydXaG9vcHMsIGxvb2tzIGxpa2Ugc29tZXRoaW5nIHdlbnQgd3JvbmcuJ107XG4gICAgICAgICAgICBpZiAoJ3Jlc3BvbnNlVGV4dCcgaW4gcmVzcG9uc2UpIHtcbiAgICAgICAgICAgICAgICB2YXIgZXJyb3JNc2cgPSByZXNwb25zZS5yZXNwb25zZVRleHQubWF0Y2goLzxoMVxccyo+KC4qKTxcXC9oMVxccyo+L2kpO1xuICAgICAgICAgICAgICAgIGlmICh0aGlzLmlzQXJyYXkoZXJyb3JNc2cpKSB7XG4gICAgICAgICAgICAgICAgICAgIG5ld1Jlc3BvbnNlID0gW2Vycm9yTXNnWzFdXTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gbmV3UmVzcG9uc2U7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIEVzY2FwZSBzdHJpbmcgdG8gdXNlIGFzIFJlZ3VsYXIgRXhwcmVzc2lvbi5cbiAgICAgICAgICpcbiAgICAgICAgICogQHBhcmFtIHN0clxuICAgICAgICAgKiBAcmV0dXJucyBzdHJpbmdcbiAgICAgICAgICovXG4gICAgICAgIGVzY2FwZVJlZ0V4cDogZnVuY3Rpb24gKHN0cikge1xuICAgICAgICAgICAgcmV0dXJuIHN0ci5yZXBsYWNlKC9bXFwtXFxbXFxdXFwvXFx7XFx9XFwoXFwpXFwqXFwrXFw/XFwuXFxcXFxcXlxcJFxcfF0vZywgXCJcXFxcJCZcIik7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIEdlbmVyYXRlIFJlZ0V4cCBmcm9tIHdpbGRjYXJkIGF0dHJpYnV0ZXMuXG4gICAgICAgICAqXG4gICAgICAgICAqIEBwYXJhbSBuYW1lXG4gICAgICAgICAqIEByZXR1cm5zIHtSZWdFeHB9XG4gICAgICAgICAqL1xuICAgICAgICByZWdleEZyb21XaWxkY2FyZDogZnVuY3Rpb24gKG5hbWUpIHtcbiAgICAgICAgICAgIHZhciBuYW1lUGFydHMgPSBuYW1lLnNwbGl0KCdbKl0nKTtcbiAgICAgICAgICAgIGlmIChuYW1lUGFydHMubGVuZ3RoID09PSAxKSBuYW1lUGFydHMucHVzaCgnJyk7XG5cbiAgICAgICAgICAgIHJldHVybiBuZXcgUmVnRXhwKCdeJyArIG5hbWVQYXJ0cy5tYXAoZnVuY3Rpb24oeCkge1xuICAgICAgICAgICAgICAgIHJldHVybiBsYXJhdmVsVmFsaWRhdGlvbi5oZWxwZXJzLmVzY2FwZVJlZ0V4cCh4KVxuICAgICAgICAgICAgfSkuam9pbignXFxcXFtbXlxcXFxdXSpcXFxcXScpICsgJyQnKTtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogTWVyZ2UgYWRkaXRpb25hbCBsYXJhdmVsIHZhbGlkYXRpb24gcnVsZXMgaW50byB0aGUgY3VycmVudCBydWxlIHNldC5cbiAgICAgICAgICpcbiAgICAgICAgICogQHBhcmFtIHtvYmplY3R9IHJ1bGVzXG4gICAgICAgICAqIEBwYXJhbSB7b2JqZWN0fSBuZXdSdWxlc1xuICAgICAgICAgKiBAcmV0dXJucyB7b2JqZWN0fVxuICAgICAgICAgKi9cbiAgICAgICAgbWVyZ2VSdWxlczogZnVuY3Rpb24gKHJ1bGVzLCBuZXdSdWxlcykge1xuICAgICAgICAgICAgdmFyIHJ1bGVzTGlzdCA9IHtcbiAgICAgICAgICAgICAgICAnbGFyYXZlbFZhbGlkYXRpb24nOiBuZXdSdWxlcy5sYXJhdmVsVmFsaWRhdGlvbiB8fCBbXSxcbiAgICAgICAgICAgICAgICAnbGFyYXZlbFZhbGlkYXRpb25SZW1vdGUnOiBuZXdSdWxlcy5sYXJhdmVsVmFsaWRhdGlvblJlbW90ZSB8fCBbXVxuICAgICAgICAgICAgfTtcblxuICAgICAgICAgICAgZm9yICh2YXIga2V5IGluIHJ1bGVzTGlzdCkge1xuICAgICAgICAgICAgICAgIGlmIChydWxlc0xpc3Rba2V5XS5sZW5ndGggPT09IDApIHtcbiAgICAgICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgaWYgKHR5cGVvZiBydWxlc1trZXldID09PSBcInVuZGVmaW5lZFwiKSB7XG4gICAgICAgICAgICAgICAgICAgIHJ1bGVzW2tleV0gPSBbXTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBydWxlc1trZXldID0gcnVsZXNba2V5XS5jb25jYXQocnVsZXNMaXN0W2tleV0pO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICByZXR1cm4gcnVsZXM7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIEhUTUwgZW50aXR5IGVuY29kZSBhIHN0cmluZy5cbiAgICAgICAgICpcbiAgICAgICAgICogQHBhcmFtIHN0cmluZ1xuICAgICAgICAgKiBAcmV0dXJucyB7c3RyaW5nfVxuICAgICAgICAgKi9cbiAgICAgICAgZW5jb2RlOiBmdW5jdGlvbiAoc3RyaW5nKSB7XG4gICAgICAgICAgICByZXR1cm4gJCgnPGRpdi8+JykudGV4dChzdHJpbmcpLmh0bWwoKTtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogTG9va3VwIG5hbWUgaW4gYW4gYXJyYXkuXG4gICAgICAgICAqXG4gICAgICAgICAqIEBwYXJhbSB2YWxpZGF0b3JcbiAgICAgICAgICogQHBhcmFtIHtzdHJpbmd9IG5hbWUgTmFtZSBpbiBkb3Qgbm90YXRpb24gZm9ybWF0LlxuICAgICAgICAgKiBAcmV0dXJucyB7Kn1cbiAgICAgICAgICovXG4gICAgICAgIGZpbmRCeUFycmF5TmFtZTogZnVuY3Rpb24gKHZhbGlkYXRvciwgbmFtZSkge1xuICAgICAgICAgICAgdmFyIHNxTmFtZSA9IG5hbWUucmVwbGFjZSgvXFwuKFteXFwuXSspL2csICdbJDFdJyksXG4gICAgICAgICAgICAgICAgbG9va3VwcyA9IFtcbiAgICAgICAgICAgICAgICAgICAgLy8gQ29udmVydCBkb3QgdG8gc3F1YXJlIGJyYWNrZXRzLiBlLmcuIGZvby5iYXIuMCBiZWNvbWVzIGZvb1tiYXJdWzBdXG4gICAgICAgICAgICAgICAgICAgIHNxTmFtZSxcbiAgICAgICAgICAgICAgICAgICAgLy8gQXBwZW5kIFtdIHRvIHRoZSBuYW1lIGUuZy4gZm9vIGJlY29tZXMgZm9vW10gb3IgZm9vLmJhci4wIGJlY29tZXMgZm9vW2Jhcl1bMF1bXVxuICAgICAgICAgICAgICAgICAgICBzcU5hbWUgKyAnW10nLFxuICAgICAgICAgICAgICAgICAgICAvLyBSZW1vdmUga2V5IGZyb20gbGFzdCBhcnJheSBlLmcuIGZvb1tiYXJdWzBdIGJlY29tZXMgZm9vW2Jhcl1bXVxuICAgICAgICAgICAgICAgICAgICBzcU5hbWUucmVwbGFjZSgvKC4qKVxcWyguKilcXF0kL2csICckMVtdJylcbiAgICAgICAgICAgICAgICBdO1xuXG4gICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGxvb2t1cHMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICB2YXIgZWxlbSA9IHZhbGlkYXRvci5maW5kQnlOYW1lKGxvb2t1cHNbaV0pO1xuICAgICAgICAgICAgICAgIGlmIChlbGVtLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGVsZW07XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICByZXR1cm4gJChudWxsKTtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogQXR0ZW1wdCB0byBmaW5kIGFuIGVsZW1lbnQgaW4gdGhlIERPTSBtYXRjaGluZyB0aGUgZ2l2ZW4gbmFtZS5cbiAgICAgICAgICogRXhhbXBsZSBuYW1lcyBpbmNsdWRlOlxuICAgICAgICAgKiAgICAtIGRvbWFpbi4wIHdoaWNoIG1hdGNoZXMgZG9tYWluW11cbiAgICAgICAgICogICAgLSBjdXN0b21maWVsZC4zIHdoaWNoIG1hdGNoZXMgY3VzdG9tZmllbGRbM11cbiAgICAgICAgICpcbiAgICAgICAgICogQHBhcmFtIHZhbGlkYXRvclxuICAgICAgICAgKiBAcGFyYW0ge3N0cmluZ30gbmFtZVxuICAgICAgICAgKiBAcmV0dXJucyB7Kn1cbiAgICAgICAgICovXG4gICAgICAgIGZpbmRCeU5hbWU6IGZ1bmN0aW9uICh2YWxpZGF0b3IsIG5hbWUpIHtcbiAgICAgICAgICAgIC8vIEV4YWN0IG1hdGNoLlxuICAgICAgICAgICAgdmFyIGVsZW0gPSB2YWxpZGF0b3IuZmluZEJ5TmFtZShuYW1lKTtcbiAgICAgICAgICAgIGlmIChlbGVtLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gZWxlbTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gRmluZCBuYW1lIGluIGRhdGEsIHVzaW5nIGRvdCBub3RhdGlvbi5cbiAgICAgICAgICAgIHZhciBkZWxpbSA9ICcuJyxcbiAgICAgICAgICAgICAgICBwYXJ0cyAgPSBuYW1lLnNwbGl0KGRlbGltKTtcbiAgICAgICAgICAgIGZvciAodmFyIGkgPSBwYXJ0cy5sZW5ndGg7IGkgPiAwOyBpLS0pIHtcbiAgICAgICAgICAgICAgICB2YXIgcmVjb25zdHJ1Y3RlZCA9IFtdO1xuICAgICAgICAgICAgICAgIGZvciAodmFyIGMgPSAwOyBjIDwgaTsgYysrKSB7XG4gICAgICAgICAgICAgICAgICAgIHJlY29uc3RydWN0ZWQucHVzaChwYXJ0c1tjXSk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgZWxlbSA9IHRoaXMuZmluZEJ5QXJyYXlOYW1lKHZhbGlkYXRvciwgcmVjb25zdHJ1Y3RlZC5qb2luKGRlbGltKSk7XG4gICAgICAgICAgICAgICAgaWYgKGVsZW0ubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gZWxlbTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHJldHVybiAkKG51bGwpO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBJZiBpdCdzIGFuIGFycmF5IGVsZW1lbnQsIGdldCBhbGwgdmFsdWVzLlxuICAgICAgICAgKlxuICAgICAgICAgKiBAcGFyYW0gdmFsaWRhdG9yXG4gICAgICAgICAqIEBwYXJhbSBlbGVtZW50XG4gICAgICAgICAqIEByZXR1cm5zIHsqfHN0cmluZ31cbiAgICAgICAgICovXG4gICAgICAgIGFsbEVsZW1lbnRWYWx1ZXM6IGZ1bmN0aW9uICh2YWxpZGF0b3IsIGVsZW1lbnQpIHtcbiAgICAgICAgICAgIGlmIChlbGVtZW50Lm5hbWUuaW5kZXhPZignW10nKSAhPT0gLTEpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gdmFsaWRhdG9yLmZpbmRCeU5hbWUoZWxlbWVudC5uYW1lKS5tYXAoZnVuY3Rpb24gKGksIGUpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHZhbGlkYXRvci5lbGVtZW50VmFsdWUoZSk7XG4gICAgICAgICAgICAgICAgfSkuZ2V0KCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHJldHVybiB2YWxpZGF0b3IuZWxlbWVudFZhbHVlKGVsZW1lbnQpO1xuICAgICAgICB9XG4gICAgfVxufSk7XG4iXSwibmFtZXMiOlsic3RybGVuIiwiYXJyYXlfZGlmZiIsInN0cnRvdGltZSIsImlzX251bWVyaWMiLCIkIiwiZXh0ZW5kIiwibGFyYXZlbFZhbGlkYXRpb24iLCJoZWxwZXJzIiwibnVtZXJpY1J1bGVzIiwiZmlsZWluZm8iLCJmaWVsZE9iaiIsImluZGV4IiwiRmlsZU5hbWUiLCJ2YWx1ZSIsImZpbGVzIiwiZmlsZSIsImV4dGVuc2lvbiIsInN1YnN0ciIsImxhc3RJbmRleE9mIiwic2l6ZSIsInR5cGUiLCJzZWxlY3RvciIsIm5hbWVzIiwiaXNBcnJheSIsImkiLCJsZW5ndGgiLCJwdXNoIiwiam9pbiIsImhhc051bWVyaWNSdWxlcyIsImVsZW1lbnQiLCJoYXNSdWxlcyIsInJ1bGVzIiwiZm91bmQiLCJ2YWxpZGF0b3IiLCJkYXRhIiwiZm9ybSIsImxpc3RSdWxlcyIsImNhY2hlIiwiYXJyYXlSdWxlc0NhY2hlIiwibmFtZSIsImVhY2giLCJhcnJheVJ1bGUiLCJzZXR0aW5ncyIsIm9ialJ1bGVzIiwiX3J1bGVzIiwiaW5BcnJheSIsInN0cmluZyIsImdldFNpemUiLCJvYmoiLCJwYXJzZUZsb2F0IiwiTWF0aCIsImZsb29yIiwiZ2V0TGFyYXZlbFZhbGlkYXRpb24iLCJydWxlIiwidW5kZWZpbmVkIiwic3RhdGljUnVsZXMiLCJrZXkiLCJwYXJzZVRpbWUiLCJmb3JtYXQiLCJ0aW1lVmFsdWUiLCJmbXQiLCJEYXRlRm9ybWF0dGVyIiwiZGF0ZVJ1bGUiLCJwYXJzZURhdGUiLCJEYXRlIiwiZm9ybWF0RGF0ZSIsInJvdW5kIiwiZ2V0VGltZSIsImNvbXBhcmVEYXRlcyIsInBhcmFtcyIsIm9wZXJhdG9yIiwidGltZUNvbXBhcmUiLCJ0YXJnZXQiLCJkZXBlbmRlbnRFbGVtZW50IiwiZWxlbWVudFZhbHVlIiwiRXJyb3IiLCJndWVzc0RhdGUiLCJ0ZXh0Iiwibm93IiwibWl4ZWRfdmFyIiwiYXJnIiwiT2JqZWN0IiwicHJvdG90eXBlIiwidG9TdHJpbmciLCJjYWxsIiwiYXJyYXlEaWZmIiwiYXJyMSIsImFycjIiLCJhcnJheUVxdWFscyIsImlzRW1wdHlPYmplY3QiLCJlbCIsImZpbmRCeU5hbWUiLCJ0YXJnZXRFbGVtZW50Iiwib25mb2N1c291dCIsImV2ZW50IiwidGFnTmFtZSIsInJ1bGVOYW1lIiwib2ZmIiwib24iLCJ2YWxpZCIsInBhcnNlRXJyb3JSZXNwb25zZSIsInJlc3BvbnNlIiwibmV3UmVzcG9uc2UiLCJlcnJvck1zZyIsInJlc3BvbnNlVGV4dCIsIm1hdGNoIiwiZXNjYXBlUmVnRXhwIiwic3RyIiwicmVwbGFjZSIsInJlZ2V4RnJvbVdpbGRjYXJkIiwibmFtZVBhcnRzIiwic3BsaXQiLCJSZWdFeHAiLCJtYXAiLCJ4IiwibWVyZ2VSdWxlcyIsIm5ld1J1bGVzIiwicnVsZXNMaXN0IiwibGFyYXZlbFZhbGlkYXRpb25SZW1vdGUiLCJjb25jYXQiLCJlbmNvZGUiLCJodG1sIiwiZmluZEJ5QXJyYXlOYW1lIiwic3FOYW1lIiwibG9va3VwcyIsImVsZW0iLCJkZWxpbSIsInBhcnRzIiwicmVjb25zdHJ1Y3RlZCIsImMiLCJhbGxFbGVtZW50VmFsdWVzIiwiaW5kZXhPZiIsImUiLCJnZXQiXSwic291cmNlUm9vdCI6IiJ9 +/*! + * Laravel Javascript Validation + * + * https://github.com/proengsoft/laravel-jsvalidation + * + * Timezone Helper functions used by validators + * + * Copyright (c) 2017 Proengsoft + * Released under the MIT license + */ + +$.extend(true, laravelValidation, { + + helpers: { + + /** + * Check if the specified timezone is valid. + * + * @param value + * @returns {boolean} + */ + isTimezone: function (value) { + + var timezones={ + "africa": [ + "abidjan", + "accra", + "addis_ababa", + "algiers", + "asmara", + "bamako", + "bangui", + "banjul", + "bissau", + "blantyre", + "brazzaville", + "bujumbura", + "cairo", + "casablanca", + "ceuta", + "conakry", + "dakar", + "dar_es_salaam", + "djibouti", + "douala", + "el_aaiun", + "freetown", + "gaborone", + "harare", + "johannesburg", + "juba", + "kampala", + "khartoum", + "kigali", + "kinshasa", + "lagos", + "libreville", + "lome", + "luanda", + "lubumbashi", + "lusaka", + "malabo", + "maputo", + "maseru", + "mbabane", + "mogadishu", + "monrovia", + "nairobi", + "ndjamena", + "niamey", + "nouakchott", + "ouagadougou", + "porto-novo", + "sao_tome", + "tripoli", + "tunis", + "windhoek" + ], + "america": [ + "adak", + "anchorage", + "anguilla", + "antigua", + "araguaina", + "argentina\/buenos_aires", + "argentina\/catamarca", + "argentina\/cordoba", + "argentina\/jujuy", + "argentina\/la_rioja", + "argentina\/mendoza", + "argentina\/rio_gallegos", + "argentina\/salta", + "argentina\/san_juan", + "argentina\/san_luis", + "argentina\/tucuman", + "argentina\/ushuaia", + "aruba", + "asuncion", + "atikokan", + "bahia", + "bahia_banderas", + "barbados", + "belem", + "belize", + "blanc-sablon", + "boa_vista", + "bogota", + "boise", + "cambridge_bay", + "campo_grande", + "cancun", + "caracas", + "cayenne", + "cayman", + "chicago", + "chihuahua", + "costa_rica", + "creston", + "cuiaba", + "curacao", + "danmarkshavn", + "dawson", + "dawson_creek", + "denver", + "detroit", + "dominica", + "edmonton", + "eirunepe", + "el_salvador", + "fortaleza", + "glace_bay", + "godthab", + "goose_bay", + "grand_turk", + "grenada", + "guadeloupe", + "guatemala", + "guayaquil", + "guyana", + "halifax", + "havana", + "hermosillo", + "indiana\/indianapolis", + "indiana\/knox", + "indiana\/marengo", + "indiana\/petersburg", + "indiana\/tell_city", + "indiana\/vevay", + "indiana\/vincennes", + "indiana\/winamac", + "inuvik", + "iqaluit", + "jamaica", + "juneau", + "kentucky\/louisville", + "kentucky\/monticello", + "kralendijk", + "la_paz", + "lima", + "los_angeles", + "lower_princes", + "maceio", + "managua", + "manaus", + "marigot", + "martinique", + "matamoros", + "mazatlan", + "menominee", + "merida", + "metlakatla", + "mexico_city", + "miquelon", + "moncton", + "monterrey", + "montevideo", + "montreal", + "montserrat", + "nassau", + "new_york", + "nipigon", + "nome", + "noronha", + "north_dakota\/beulah", + "north_dakota\/center", + "north_dakota\/new_salem", + "ojinaga", + "panama", + "pangnirtung", + "paramaribo", + "phoenix", + "port-au-prince", + "port_of_spain", + "porto_velho", + "puerto_rico", + "rainy_river", + "rankin_inlet", + "recife", + "regina", + "resolute", + "rio_branco", + "santa_isabel", + "santarem", + "santiago", + "santo_domingo", + "sao_paulo", + "scoresbysund", + "shiprock", + "sitka", + "st_barthelemy", + "st_johns", + "st_kitts", + "st_lucia", + "st_thomas", + "st_vincent", + "swift_current", + "tegucigalpa", + "thule", + "thunder_bay", + "tijuana", + "toronto", + "tortola", + "vancouver", + "whitehorse", + "winnipeg", + "yakutat", + "yellowknife" + ], + "antarctica": [ + "casey", + "davis", + "dumontdurville", + "macquarie", + "mawson", + "mcmurdo", + "palmer", + "rothera", + "south_pole", + "syowa", + "vostok" + ], + "arctic": [ + "longyearbyen" + ], + "asia": [ + "aden", + "almaty", + "amman", + "anadyr", + "aqtau", + "aqtobe", + "ashgabat", + "baghdad", + "bahrain", + "baku", + "bangkok", + "beirut", + "bishkek", + "brunei", + "choibalsan", + "chongqing", + "colombo", + "damascus", + "dhaka", + "dili", + "dubai", + "dushanbe", + "gaza", + "harbin", + "hebron", + "ho_chi_minh", + "hong_kong", + "hovd", + "irkutsk", + "jakarta", + "jayapura", + "jerusalem", + "kabul", + "kamchatka", + "karachi", + "kashgar", + "kathmandu", + "khandyga", + "kolkata", + "krasnoyarsk", + "kuala_lumpur", + "kuching", + "kuwait", + "macau", + "magadan", + "makassar", + "manila", + "muscat", + "nicosia", + "novokuznetsk", + "novosibirsk", + "omsk", + "oral", + "phnom_penh", + "pontianak", + "pyongyang", + "qatar", + "qyzylorda", + "rangoon", + "riyadh", + "sakhalin", + "samarkand", + "seoul", + "shanghai", + "singapore", + "taipei", + "tashkent", + "tbilisi", + "tehran", + "thimphu", + "tokyo", + "ulaanbaatar", + "urumqi", + "ust-nera", + "vientiane", + "vladivostok", + "yakutsk", + "yekaterinburg", + "yerevan" + ], + "atlantic": [ + "azores", + "bermuda", + "canary", + "cape_verde", + "faroe", + "madeira", + "reykjavik", + "south_georgia", + "st_helena", + "stanley" + ], + "australia": [ + "adelaide", + "brisbane", + "broken_hill", + "currie", + "darwin", + "eucla", + "hobart", + "lindeman", + "lord_howe", + "melbourne", + "perth", + "sydney" + ], + "europe": [ + "amsterdam", + "andorra", + "athens", + "belgrade", + "berlin", + "bratislava", + "brussels", + "bucharest", + "budapest", + "busingen", + "chisinau", + "copenhagen", + "dublin", + "gibraltar", + "guernsey", + "helsinki", + "isle_of_man", + "istanbul", + "jersey", + "kaliningrad", + "kiev", + "lisbon", + "ljubljana", + "london", + "luxembourg", + "madrid", + "malta", + "mariehamn", + "minsk", + "monaco", + "moscow", + "oslo", + "paris", + "podgorica", + "prague", + "riga", + "rome", + "samara", + "san_marino", + "sarajevo", + "simferopol", + "skopje", + "sofia", + "stockholm", + "tallinn", + "tirane", + "uzhgorod", + "vaduz", + "vatican", + "vienna", + "vilnius", + "volgograd", + "warsaw", + "zagreb", + "zaporozhye", + "zurich" + ], + "indian": [ + "antananarivo", + "chagos", + "christmas", + "cocos", + "comoro", + "kerguelen", + "mahe", + "maldives", + "mauritius", + "mayotte", + "reunion" + ], + "pacific": [ + "apia", + "auckland", + "chatham", + "chuuk", + "easter", + "efate", + "enderbury", + "fakaofo", + "fiji", + "funafuti", + "galapagos", + "gambier", + "guadalcanal", + "guam", + "honolulu", + "johnston", + "kiritimati", + "kosrae", + "kwajalein", + "majuro", + "marquesas", + "midway", + "nauru", + "niue", + "norfolk", + "noumea", + "pago_pago", + "palau", + "pitcairn", + "pohnpei", + "port_moresby", + "rarotonga", + "saipan", + "tahiti", + "tarawa", + "tongatapu", + "wake", + "wallis" + ], + "utc": [ + "" + ] + }; + + var tzparts= value.split('/',2); + var continent=tzparts[0].toLowerCase(); + var city=''; + if (tzparts[1]) { + city=tzparts[1].toLowerCase(); + } + + return (continent in timezones && ( timezones[continent].length===0 || timezones[continent].indexOf(city)!==-1)) + } + } +}); + +/*! + * Laravel Javascript Validation + * + * https://github.com/proengsoft/laravel-jsvalidation + * + * Methods that implement Laravel Validations + * + * Copyright (c) 2017 Proengsoft + * Released under the MIT license + */ + +$.extend(true, laravelValidation, { + + methods:{ + + helpers: laravelValidation.helpers, + + jsRemoteTimer:0, + + /** + * "Validate" optional attributes. + * Always returns true, just lets us put sometimes in rules. + * + * @return {boolean} + */ + Sometimes: function() { + return true; + }, + + /** + * Bail This is the default behaivour os JSValidation. + * Always returns true, just lets us put sometimes in rules. + * + * @return {boolean} + */ + Bail: function() { + return true; + }, + + /** + * "Indicate" validation should pass if value is null. + * Always returns true, just lets us put "nullable" in rules. + * + * @return {boolean} + */ + Nullable: function() { + return true; + }, + + /** + * Validate the given attribute is filled if it is present. + */ + Filled: function(value, element) { + return $.validator.methods.required.call(this, value, element, true); + }, + + + /** + * Validate that a required attribute exists. + */ + Required: function(value, element) { + return $.validator.methods.required.call(this, value, element); + }, + + /** + * Validate that an attribute exists when any other attribute exists. + * + * @return {boolean} + */ + RequiredWith: function(value, element, params) { + var validator=this, + required=false; + var currentObject=this; + + $.each(params,function(i,param) { + var target=laravelValidation.helpers.dependentElement( + currentObject, element, param + ); + required=required || ( + target!==undefined && + $.validator.methods.required.call( + validator, + currentObject.elementValue(target), + target,true + )); + }); + + if (required) { + return $.validator.methods.required.call(this, value, element, true); + } + return true; + }, + + /** + * Validate that an attribute exists when all other attribute exists. + * + * @return {boolean} + */ + RequiredWithAll: function(value, element, params) { + var validator=this, + required=true; + var currentObject=this; + + $.each(params,function(i,param) { + var target=laravelValidation.helpers.dependentElement( + currentObject, element, param + ); + required = required && ( + target!==undefined && + $.validator.methods.required.call( + validator, + currentObject.elementValue(target), + target,true + )); + }); + + if (required) { + return $.validator.methods.required.call(this, value, element, true); + } + return true; + }, + + /** + * Validate that an attribute exists when any other attribute does not exists. + * + * @return {boolean} + */ + RequiredWithout: function(value, element, params) { + var validator=this, + required=false; + var currentObject=this; + + $.each(params,function(i,param) { + var target=laravelValidation.helpers.dependentElement( + currentObject, element, param + ); + required = required || + target===undefined|| + !$.validator.methods.required.call( + validator, + currentObject.elementValue(target), + target,true + ); + }); + + if (required) { + return $.validator.methods.required.call(this, value, element, true); + } + return true; + }, + + /** + * Validate that an attribute exists when all other attribute does not exists. + * + * @return {boolean} + */ + RequiredWithoutAll: function(value, element, params) { + var validator=this, + required=true, + currentObject=this; + + $.each(params,function(i, param) { + var target=laravelValidation.helpers.dependentElement( + currentObject, element, param + ); + required = required && ( + target===undefined || + !$.validator.methods.required.call( + validator, + currentObject.elementValue(target), + target,true + )); + }); + + if (required) { + return $.validator.methods.required.call(this, value, element, true); + } + return true; + }, + + /** + * Validate that an attribute exists when another attribute has a given value. + * + * @return {boolean} + */ + RequiredIf: function(value, element, params) { + + var target=laravelValidation.helpers.dependentElement( + this, element, params[0] + ); + + if (target!==undefined) { + var val=String(this.elementValue(target)); + if (typeof val !== 'undefined') { + var data = params.slice(1); + if ($.inArray(val, data) !== -1) { + return $.validator.methods.required.call( + this, value, element, true + ); + } + } + } + + return true; + }, + + /** + * Validate that an attribute exists when another + * attribute does not have a given value. + * + * @return {boolean} + */ + RequiredUnless: function(value, element, params) { + + var target=laravelValidation.helpers.dependentElement( + this, element, params[0] + ); + + if (target!==undefined) { + var val=String(this.elementValue(target)); + if (typeof val !== 'undefined') { + var data = params.slice(1); + if ($.inArray(val, data) !== -1) { + return true; + } + } + } + + return $.validator.methods.required.call( + this, value, element, true + ); + + }, + + /** + * Validate that an attribute has a matching confirmation. + * + * @return {boolean} + */ + Confirmed: function(value, element, params) { + return laravelValidation.methods.Same.call(this,value, element, params); + }, + + /** + * Validate that two attributes match. + * + * @return {boolean} + */ + Same: function(value, element, params) { + + var target=laravelValidation.helpers.dependentElement( + this, element, params[0] + ); + + if (target!==undefined) { + return String(value) === String(this.elementValue(target)); + } + return false; + }, + + /** + * Validate that the values of an attribute is in another attribute. + * + * @param value + * @param element + * @param params + * @returns {boolean} + * @constructor + */ + InArray: function (value, element, params) { + if (typeof params[0] === 'undefined') { + return false; + } + var elements = this.elements(); + var found = false; + var nameRegExp = laravelValidation.helpers.regexFromWildcard(params[0]); + + for ( var i = 0; i < elements.length ; i++ ) { + var targetName = elements[i].name; + if (targetName.match(nameRegExp)) { + var equals = laravelValidation.methods.Same.call(this,value, element, [targetName]); + found = found || equals; + } + } + + return found; + }, + + /** + * Validate an attribute is unique among other values. + * + * @param value + * @param element + * @param params + * @returns {boolean} + */ + Distinct: function (value, element, params) { + if (typeof params[0] === 'undefined') { + return false; + } + + var elements = this.elements(); + var found = false; + var nameRegExp = laravelValidation.helpers.regexFromWildcard(params[0]); + + for ( var i = 0; i < elements.length ; i++ ) { + var targetName = elements[i].name; + if (targetName !== element.name && targetName.match(nameRegExp)) { + var equals = laravelValidation.methods.Same.call(this,value, element, [targetName]); + found = found || equals; + } + } + + return !found; + }, + + + /** + * Validate that an attribute is different from another attribute. + * + * @return {boolean} + */ + Different: function(value, element, params) { + return ! laravelValidation.methods.Same.call(this,value, element, params); + }, + + /** + * Validate that an attribute was "accepted". + * This validation rule implies the attribute is "required". + * + * @return {boolean} + */ + Accepted: function(value) { + var regex = new RegExp("^(?:(yes|on|1|true))$",'i'); + return regex.test(value); + }, + + /** + * Validate that an attribute is an array. + * + * @param value + * @param element + */ + Array: function(value, element) { + if (element.name.indexOf('[') !== -1 && element.name.indexOf(']') !== -1) { + return true; + } + + return laravelValidation.helpers.isArray(value); + }, + + /** + * Validate that an attribute is a boolean. + * + * @return {boolean} + */ + Boolean: function(value) { + var regex= new RegExp("^(?:(true|false|1|0))$",'i'); + return regex.test(value); + }, + + /** + * Validate that an attribute is an integer. + * + * @return {boolean} + */ + Integer: function(value) { + var regex= new RegExp("^(?:-?\\d+)$",'i'); + return regex.test(value); + }, + + /** + * Validate that an attribute is numeric. + */ + Numeric: function(value, element) { + return $.validator.methods.number.call(this, value, element, true); + }, + + /** + * Validate that an attribute is a string. + * + * @return {boolean} + */ + String: function(value) { + return typeof value === 'string'; + }, + + /** + * The field under validation must be numeric and must have an exact length of value. + */ + Digits: function(value, element, params) { + return ( + $.validator.methods.number.call(this, value, element, true) && + value.length === parseInt(params, 10) + ); + }, + + /** + * The field under validation must have a length between the given min and max. + */ + DigitsBetween: function(value, element, params) { + return ($.validator.methods.number.call(this, value, element, true) + && value.length>=parseFloat(params[0]) && value.length<=parseFloat(params[1])); + }, + + /** + * Validate the size of an attribute. + * + * @return {boolean} + */ + Size: function(value, element, params) { + return laravelValidation.helpers.getSize(this, element,value) === parseFloat(params[0]); + }, + + /** + * Validate the size of an attribute is between a set of values. + * + * @return {boolean} + */ + Between: function(value, element, params) { + return ( laravelValidation.helpers.getSize(this, element,value) >= parseFloat(params[0]) && + laravelValidation.helpers.getSize(this,element,value) <= parseFloat(params[1])); + }, + + /** + * Validate the size of an attribute is greater than a minimum value. + * + * @return {boolean} + */ + Min: function(value, element, params) { + value = laravelValidation.helpers.allElementValues(this, element); + + return laravelValidation.helpers.getSize(this, element, value) >= parseFloat(params[0]); + }, + + /** + * Validate the size of an attribute is less than a maximum value. + * + * @return {boolean} + */ + Max: function(value, element, params) { + value = laravelValidation.helpers.allElementValues(this, element); + + return laravelValidation.helpers.getSize(this, element, value) <= parseFloat(params[0]); + }, + + /** + * Validate an attribute is contained within a list of values. + * + * @return {boolean} + */ + In: function(value, element, params) { + if (laravelValidation.helpers.isArray(value) + && laravelValidation.helpers.hasRules(element, "Array") + ) { + var diff = laravelValidation.helpers.arrayDiff(value, params); + + return Object.keys(diff).length === 0; + } + + return params.indexOf(value.toString()) !== -1; + }, + + /** + * Validate an attribute is not contained within a list of values. + * + * @return {boolean} + */ + NotIn: function(value, element, params) { + return params.indexOf(value.toString()) === -1; + }, + + /** + * Validate that an attribute is a valid IP. + * + * @return {boolean} + */ + Ip: function(value) { + return /^(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)$/i.test(value) || + /^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$/i.test(value); + }, + + /** + * Validate that an attribute is a valid e-mail address. + */ + Email: function(value, element) { + return $.validator.methods.email.call(this, value, element, true); + }, + + /** + * Validate that an attribute is a valid URL. + */ + Url: function(value, element) { + return $.validator.methods.url.call(this, value, element, true); + }, + + /** + * The field under validation must be a successfully uploaded file. + * + * @return {boolean} + */ + File: function(value, element) { + if (!window.File || !window.FileReader || !window.FileList || !window.Blob) { + return true; + } + if ('files' in element ) { + return (element.files.length > 0); + } + return false; + }, + + /** + * Validate the MIME type of a file upload attribute is in a set of MIME types. + * + * @return {boolean} + */ + Mimes: function(value, element, params) { + if (!window.File || !window.FileReader || !window.FileList || !window.Blob) { + return true; + } + var lowerParams = $.map(params, function(item) { + return item.toLowerCase(); + }); + + var fileinfo = laravelValidation.helpers.fileinfo(element); + return (fileinfo !== false && lowerParams.indexOf(fileinfo.extension.toLowerCase())!==-1); + }, + + /** + * The file under validation must match one of the given MIME types. + * + * @return {boolean} + */ + Mimetypes: function(value, element, params) { + if (!window.File || !window.FileReader || !window.FileList || !window.Blob) { + return true; + } + var lowerParams = $.map(params, function(item) { + return item.toLowerCase(); + }); + + var fileinfo = laravelValidation.helpers.fileinfo(element); + + if (fileinfo === false) { + return false; + } + return (lowerParams.indexOf(fileinfo.type.toLowerCase())!==-1); + }, + + /** + * Validate the MIME type of a file upload attribute is in a set of MIME types. + */ + Image: function(value, element) { + return laravelValidation.methods.Mimes.call(this, value, element, [ + 'jpg', 'png', 'gif', 'bmp', 'svg', 'jpeg' + ]); + }, + + /** + * Validate dimensions of Image. + * + * @return {boolean|string} + */ + Dimensions: function(value, element, params, callback) { + if (!window.File || !window.FileReader || !window.FileList || !window.Blob) { + return true; + } + if (element.files === null || typeof element.files[0] === 'undefined') { + return false; + } + + var fr = new FileReader; + fr.onload = function () { + var img = new Image(); + img.onload = function () { + var height = parseFloat(img.naturalHeight); + var width = parseFloat(img.naturalWidth); + var ratio = width / height; + var notValid = ((params['width']) && parseFloat(params['width'] !== width)) || + ((params['min_width']) && parseFloat(params['min_width']) > width) || + ((params['max_width']) && parseFloat(params['max_width']) < width) || + ((params['height']) && parseFloat(params['height']) !== height) || + ((params['min_height']) && parseFloat(params['min_height']) > height) || + ((params['max_height']) && parseFloat(params['max_height']) < height) || + ((params['ratio']) && ratio !== parseFloat(eval(params['ratio'])) + ); + callback(! notValid); + }; + img.onerror = function() { + callback(false); + }; + img.src = fr.result; + }; + fr.readAsDataURL(element.files[0]); + + return 'pending'; + }, + + /** + * Validate that an attribute contains only alphabetic characters. + * + * @return {boolean} + */ + Alpha: function(value) { + if (typeof value !== 'string') { + return false; + } + + var regex = new RegExp("^(?:^[a-z\u00E0-\u00FC]+$)$",'i'); + return regex.test(value); + + }, + + /** + * Validate that an attribute contains only alpha-numeric characters. + * + * @return {boolean} + */ + AlphaNum: function(value) { + if (typeof value !== 'string') { + return false; + } + var regex = new RegExp("^(?:^[a-z0-9\u00E0-\u00FC]+$)$",'i'); + return regex.test(value); + }, + + /** + * Validate that an attribute contains only alphabetic characters. + * + * @return {boolean} + */ + AlphaDash: function(value) { + if (typeof value !== 'string') { + return false; + } + var regex = new RegExp("^(?:^[a-z0-9\u00E0-\u00FC_-]+$)$",'i'); + return regex.test(value); + }, + + /** + * Validate that an attribute passes a regular expression check. + * + * @return {boolean} + */ + Regex: function(value, element, params) { + var invalidModifiers=['x','s','u','X','U','A']; + // Converting php regular expression + var phpReg= new RegExp('^(?:\/)(.*\\\/?[^\/]*|[^\/]*)(?:\/)([gmixXsuUAJ]*)?$'); + var matches=params[0].match(phpReg); + if (matches === null) { + return false; + } + // checking modifiers + var php_modifiers=[]; + if (matches[2]!==undefined) { + php_modifiers=matches[2].split(''); + for (var i=0; i'); + }, + + /** + * Validate the date is equal or after a given date. + * + * @return {boolean} + */ + AfterOrEqual: function(value, element, params) { + return laravelValidation.helpers.compareDates(this, value, element, params[0], '>='); + }, + + + /** + * Validate that an attribute is a valid date. + */ + Timezone: function(value) { + return laravelValidation.helpers.isTimezone(value); + }, + + + /** + * Validate the attribute is a valid JSON string. + * + * @param value + * @return bool + */ + Json: function(value) { + var result = true; + try { + JSON.parse(value); + } catch (e) { + result = false; + } + return result; + }, + + /** + * Noop (always returns true). + * + * @param value + * @returns {boolean} + */ + ProengsoftNoop: function (value) { + return true; + }, + } +}); + +//# sourceMappingURL=jsvalidation.js.map diff --git a/public/vendor/jsvalidation/js/jsvalidation.js.map b/public/vendor/jsvalidation/js/jsvalidation.js.map new file mode 100644 index 00000000..c8cb5965 --- /dev/null +++ b/public/vendor/jsvalidation/js/jsvalidation.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["node_modules/jquery-validation/dist/jquery.validate.js","node_modules/php-date-formatter/js/php-date-formatter.js","resources/assets/js/jsvalidation.js","es-build/helpers.js","resources/assets/js/timezones.js","resources/assets/js/validations.js"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACtqDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AC/oBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AC/XA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AC3iEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AC9dA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"jsvalidation.js","sourcesContent":["/*!\n * jQuery Validation Plugin v1.21.0\n *\n * https://jqueryvalidation.org/\n *\n * Copyright (c) 2024 Jörn Zaefferer\n * Released under the MIT license\n */\n(function( factory ) {\n\tif ( typeof define === \"function\" && define.amd ) {\n\t\tdefine( [\"jquery\"], factory );\n\t} else if (typeof module === \"object\" && module.exports) {\n\t\tmodule.exports = factory( require( \"jquery\" ) );\n\t} else {\n\t\tfactory( jQuery );\n\t}\n}(function( $ ) {\n\n$.extend( $.fn, {\n\n\t// https://jqueryvalidation.org/validate/\n\tvalidate: function( options ) {\n\n\t\t// If nothing is selected, return nothing; can't chain anyway\n\t\tif ( !this.length ) {\n\t\t\tif ( options && options.debug && window.console ) {\n\t\t\t\tconsole.warn( \"Nothing selected, can't validate, returning nothing.\" );\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Check if a validator for this form was already created\n\t\tvar validator = $.data( this[ 0 ], \"validator\" );\n\t\tif ( validator ) {\n\t\t\treturn validator;\n\t\t}\n\n\t\t// Add novalidate tag if HTML5.\n\t\tthis.attr( \"novalidate\", \"novalidate\" );\n\n\t\tvalidator = new $.validator( options, this[ 0 ] );\n\t\t$.data( this[ 0 ], \"validator\", validator );\n\n\t\tif ( validator.settings.onsubmit ) {\n\n\t\t\tthis.on( \"click.validate\", \":submit\", function( event ) {\n\n\t\t\t\t// Track the used submit button to properly handle scripted\n\t\t\t\t// submits later.\n\t\t\t\tvalidator.submitButton = event.currentTarget;\n\n\t\t\t\t// Allow suppressing validation by adding a cancel class to the submit button\n\t\t\t\tif ( $( this ).hasClass( \"cancel\" ) ) {\n\t\t\t\t\tvalidator.cancelSubmit = true;\n\t\t\t\t}\n\n\t\t\t\t// Allow suppressing validation by adding the html5 formnovalidate attribute to the submit button\n\t\t\t\tif ( $( this ).attr( \"formnovalidate\" ) !== undefined ) {\n\t\t\t\t\tvalidator.cancelSubmit = true;\n\t\t\t\t}\n\t\t\t} );\n\n\t\t\t// Validate the form on submit\n\t\t\tthis.on( \"submit.validate\", function( event ) {\n\t\t\t\tif ( validator.settings.debug ) {\n\n\t\t\t\t\t// Prevent form submit to be able to see console output\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t}\n\n\t\t\t\tfunction handle() {\n\t\t\t\t\tvar hidden, result;\n\n\t\t\t\t\t// Insert a hidden input as a replacement for the missing submit button\n\t\t\t\t\t// The hidden input is inserted in two cases:\n\t\t\t\t\t// - A user defined a `submitHandler`\n\t\t\t\t\t// - There was a pending request due to `remote` method and `stopRequest()`\n\t\t\t\t\t// was called to submit the form in case it's valid\n\t\t\t\t\tif ( validator.submitButton && ( validator.settings.submitHandler || validator.formSubmitted ) ) {\n\t\t\t\t\t\thidden = $( \"\" )\n\t\t\t\t\t\t\t.attr( \"name\", validator.submitButton.name )\n\t\t\t\t\t\t\t.val( $( validator.submitButton ).val() )\n\t\t\t\t\t\t\t.appendTo( validator.currentForm );\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( validator.settings.submitHandler && !validator.settings.debug ) {\n\t\t\t\t\t\tresult = validator.settings.submitHandler.call( validator, validator.currentForm, event );\n\t\t\t\t\t\tif ( hidden ) {\n\n\t\t\t\t\t\t\t// And clean up afterwards; thanks to no-block-scope, hidden can be referenced\n\t\t\t\t\t\t\thidden.remove();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif ( result !== undefined ) {\n\t\t\t\t\t\t\treturn result;\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\t// Prevent submit for invalid forms or custom submit handlers\n\t\t\t\tif ( validator.cancelSubmit ) {\n\t\t\t\t\tvalidator.cancelSubmit = false;\n\t\t\t\t\treturn handle();\n\t\t\t\t}\n\t\t\t\tif ( validator.form() ) {\n\t\t\t\t\tif ( validator.pendingRequest ) {\n\t\t\t\t\t\tvalidator.formSubmitted = true;\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\treturn handle();\n\t\t\t\t} else {\n\t\t\t\t\tvalidator.focusInvalid();\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t} );\n\t\t}\n\n\t\treturn validator;\n\t},\n\n\t// https://jqueryvalidation.org/valid/\n\tvalid: function() {\n\t\tvar valid, validator, errorList;\n\n\t\tif ( $( this[ 0 ] ).is( \"form\" ) ) {\n\t\t\tvalid = this.validate().form();\n\t\t} else {\n\t\t\terrorList = [];\n\t\t\tvalid = true;\n\t\t\tvalidator = $( this[ 0 ].form ).validate();\n\t\t\tthis.each( function() {\n\t\t\t\tvalid = validator.element( this ) && valid;\n\t\t\t\tif ( !valid ) {\n\t\t\t\t\terrorList = errorList.concat( validator.errorList );\n\t\t\t\t}\n\t\t\t} );\n\t\t\tvalidator.errorList = errorList;\n\t\t}\n\t\treturn valid;\n\t},\n\n\t// https://jqueryvalidation.org/rules/\n\trules: function( command, argument ) {\n\t\tvar element = this[ 0 ],\n\t\t\tisContentEditable = typeof this.attr( \"contenteditable\" ) !== \"undefined\" && this.attr( \"contenteditable\" ) !== \"false\",\n\t\t\tsettings, staticRules, existingRules, data, param, filtered;\n\n\t\t// If nothing is selected, return empty object; can't chain anyway\n\t\tif ( element == null ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( !element.form && isContentEditable ) {\n\t\t\telement.form = this.closest( \"form\" )[ 0 ];\n\t\t\telement.name = this.attr( \"name\" );\n\t\t}\n\n\t\tif ( element.form == null ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( command ) {\n\t\t\tsettings = $.data( element.form, \"validator\" ).settings;\n\t\t\tstaticRules = settings.rules;\n\t\t\texistingRules = $.validator.staticRules( element );\n\t\t\tswitch ( command ) {\n\t\t\tcase \"add\":\n\t\t\t\t$.extend( existingRules, $.validator.normalizeRule( argument ) );\n\n\t\t\t\t// Remove messages from rules, but allow them to be set separately\n\t\t\t\tdelete existingRules.messages;\n\t\t\t\tstaticRules[ element.name ] = existingRules;\n\t\t\t\tif ( argument.messages ) {\n\t\t\t\t\tsettings.messages[ element.name ] = $.extend( settings.messages[ element.name ], argument.messages );\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"remove\":\n\t\t\t\tif ( !argument ) {\n\t\t\t\t\tdelete staticRules[ element.name ];\n\t\t\t\t\treturn existingRules;\n\t\t\t\t}\n\t\t\t\tfiltered = {};\n\t\t\t\t$.each( argument.split( /\\s/ ), function( index, method ) {\n\t\t\t\t\tfiltered[ method ] = existingRules[ method ];\n\t\t\t\t\tdelete existingRules[ method ];\n\t\t\t\t} );\n\t\t\t\treturn filtered;\n\t\t\t}\n\t\t}\n\n\t\tdata = $.validator.normalizeRules(\n\t\t$.extend(\n\t\t\t{},\n\t\t\t$.validator.classRules( element ),\n\t\t\t$.validator.attributeRules( element ),\n\t\t\t$.validator.dataRules( element ),\n\t\t\t$.validator.staticRules( element )\n\t\t), element );\n\n\t\t// Make sure required is at front\n\t\tif ( data.required ) {\n\t\t\tparam = data.required;\n\t\t\tdelete data.required;\n\t\t\tdata = $.extend( { required: param }, data );\n\t\t}\n\n\t\t// Make sure remote is at back\n\t\tif ( data.remote ) {\n\t\t\tparam = data.remote;\n\t\t\tdelete data.remote;\n\t\t\tdata = $.extend( data, { remote: param } );\n\t\t}\n\n\t\treturn data;\n\t}\n} );\n\n// JQuery trim is deprecated, provide a trim method based on String.prototype.trim\nvar trim = function( str ) {\n\n\t// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trim#Polyfill\n\treturn str.replace( /^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g, \"\" );\n};\n\n// Custom selectors\n$.extend( $.expr.pseudos || $.expr[ \":\" ], {\t\t// '|| $.expr[ \":\" ]' here enables backwards compatibility to jQuery 1.7. Can be removed when dropping jQ 1.7.x support\n\n\t// https://jqueryvalidation.org/blank-selector/\n\tblank: function( a ) {\n\t\treturn !trim( \"\" + $( a ).val() );\n\t},\n\n\t// https://jqueryvalidation.org/filled-selector/\n\tfilled: function( a ) {\n\t\tvar val = $( a ).val();\n\t\treturn val !== null && !!trim( \"\" + val );\n\t},\n\n\t// https://jqueryvalidation.org/unchecked-selector/\n\tunchecked: function( a ) {\n\t\treturn !$( a ).prop( \"checked\" );\n\t}\n} );\n\n// Constructor for validator\n$.validator = function( options, form ) {\n\tthis.settings = $.extend( true, {}, $.validator.defaults, options );\n\tthis.currentForm = form;\n\tthis.init();\n};\n\n// https://jqueryvalidation.org/jQuery.validator.format/\n$.validator.format = function( source, params ) {\n\tif ( arguments.length === 1 ) {\n\t\treturn function() {\n\t\t\tvar args = $.makeArray( arguments );\n\t\t\targs.unshift( source );\n\t\t\treturn $.validator.format.apply( this, args );\n\t\t};\n\t}\n\tif ( params === undefined ) {\n\t\treturn source;\n\t}\n\tif ( arguments.length > 2 && params.constructor !== Array ) {\n\t\tparams = $.makeArray( arguments ).slice( 1 );\n\t}\n\tif ( params.constructor !== Array ) {\n\t\tparams = [ params ];\n\t}\n\t$.each( params, function( i, n ) {\n\t\tsource = source.replace( new RegExp( \"\\\\{\" + i + \"\\\\}\", \"g\" ), function() {\n\t\t\treturn n;\n\t\t} );\n\t} );\n\treturn source;\n};\n\n$.extend( $.validator, {\n\n\tdefaults: {\n\t\tmessages: {},\n\t\tgroups: {},\n\t\trules: {},\n\t\terrorClass: \"error\",\n\t\tpendingClass: \"pending\",\n\t\tvalidClass: \"valid\",\n\t\terrorElement: \"label\",\n\t\tfocusCleanup: false,\n\t\tfocusInvalid: true,\n\t\terrorContainer: $( [] ),\n\t\terrorLabelContainer: $( [] ),\n\t\tonsubmit: true,\n\t\tignore: \":hidden\",\n\t\tignoreTitle: false,\n\t\tcustomElements: [],\n\t\tonfocusin: function( element ) {\n\t\t\tthis.lastActive = element;\n\n\t\t\t// Hide error label and remove error class on focus if enabled\n\t\t\tif ( this.settings.focusCleanup ) {\n\t\t\t\tif ( this.settings.unhighlight ) {\n\t\t\t\t\tthis.settings.unhighlight.call( this, element, this.settings.errorClass, this.settings.validClass );\n\t\t\t\t}\n\t\t\t\tthis.hideThese( this.errorsFor( element ) );\n\t\t\t}\n\t\t},\n\t\tonfocusout: function( element ) {\n\t\t\tif ( !this.checkable( element ) && ( element.name in this.submitted || !this.optional( element ) ) ) {\n\t\t\t\tthis.element( element );\n\t\t\t}\n\t\t},\n\t\tonkeyup: function( element, event ) {\n\n\t\t\t// Avoid revalidate the field when pressing one of the following keys\n\t\t\t// Shift => 16\n\t\t\t// Ctrl => 17\n\t\t\t// Alt => 18\n\t\t\t// Caps lock => 20\n\t\t\t// End => 35\n\t\t\t// Home => 36\n\t\t\t// Left arrow => 37\n\t\t\t// Up arrow => 38\n\t\t\t// Right arrow => 39\n\t\t\t// Down arrow => 40\n\t\t\t// Insert => 45\n\t\t\t// Num lock => 144\n\t\t\t// AltGr key => 225\n\t\t\tvar excludedKeys = [\n\t\t\t\t16, 17, 18, 20, 35, 36, 37,\n\t\t\t\t38, 39, 40, 45, 144, 225\n\t\t\t];\n\n\t\t\tif ( event.which === 9 && this.elementValue( element ) === \"\" || $.inArray( event.keyCode, excludedKeys ) !== -1 ) {\n\t\t\t\treturn;\n\t\t\t} else if ( element.name in this.submitted || element.name in this.invalid ) {\n\t\t\t\tthis.element( element );\n\t\t\t}\n\t\t},\n\t\tonclick: function( element ) {\n\n\t\t\t// Click on selects, radiobuttons and checkboxes\n\t\t\tif ( element.name in this.submitted ) {\n\t\t\t\tthis.element( element );\n\n\t\t\t// Or option elements, check parent select in that case\n\t\t\t} else if ( element.parentNode.name in this.submitted ) {\n\t\t\t\tthis.element( element.parentNode );\n\t\t\t}\n\t\t},\n\t\thighlight: function( element, errorClass, validClass ) {\n\t\t\tif ( element.type === \"radio\" ) {\n\t\t\t\tthis.findByName( element.name ).addClass( errorClass ).removeClass( validClass );\n\t\t\t} else {\n\t\t\t\t$( element ).addClass( errorClass ).removeClass( validClass );\n\t\t\t}\n\t\t},\n\t\tunhighlight: function( element, errorClass, validClass ) {\n\t\t\tif ( element.type === \"radio\" ) {\n\t\t\t\tthis.findByName( element.name ).removeClass( errorClass ).addClass( validClass );\n\t\t\t} else {\n\t\t\t\t$( element ).removeClass( errorClass ).addClass( validClass );\n\t\t\t}\n\t\t}\n\t},\n\n\t// https://jqueryvalidation.org/jQuery.validator.setDefaults/\n\tsetDefaults: function( settings ) {\n\t\t$.extend( $.validator.defaults, settings );\n\t},\n\n\tmessages: {\n\t\trequired: \"This field is required.\",\n\t\tremote: \"Please fix this field.\",\n\t\temail: \"Please enter a valid email address.\",\n\t\turl: \"Please enter a valid URL.\",\n\t\tdate: \"Please enter a valid date.\",\n\t\tdateISO: \"Please enter a valid date (ISO).\",\n\t\tnumber: \"Please enter a valid number.\",\n\t\tdigits: \"Please enter only digits.\",\n\t\tequalTo: \"Please enter the same value again.\",\n\t\tmaxlength: $.validator.format( \"Please enter no more than {0} characters.\" ),\n\t\tminlength: $.validator.format( \"Please enter at least {0} characters.\" ),\n\t\trangelength: $.validator.format( \"Please enter a value between {0} and {1} characters long.\" ),\n\t\trange: $.validator.format( \"Please enter a value between {0} and {1}.\" ),\n\t\tmax: $.validator.format( \"Please enter a value less than or equal to {0}.\" ),\n\t\tmin: $.validator.format( \"Please enter a value greater than or equal to {0}.\" ),\n\t\tstep: $.validator.format( \"Please enter a multiple of {0}.\" )\n\t},\n\n\tautoCreateRanges: false,\n\n\tprototype: {\n\n\t\tinit: function() {\n\t\t\tthis.labelContainer = $( this.settings.errorLabelContainer );\n\t\t\tthis.errorContext = this.labelContainer.length && this.labelContainer || $( this.currentForm );\n\t\t\tthis.containers = $( this.settings.errorContainer ).add( this.settings.errorLabelContainer );\n\t\t\tthis.submitted = {};\n\t\t\tthis.valueCache = {};\n\t\t\tthis.pendingRequest = 0;\n\t\t\tthis.pending = {};\n\t\t\tthis.invalid = {};\n\t\t\tthis.reset();\n\n\t\t\tvar currentForm = this.currentForm,\n\t\t\t\tgroups = ( this.groups = {} ),\n\t\t\t\trules;\n\t\t\t$.each( this.settings.groups, function( key, value ) {\n\t\t\t\tif ( typeof value === \"string\" ) {\n\t\t\t\t\tvalue = value.split( /\\s/ );\n\t\t\t\t}\n\t\t\t\t$.each( value, function( index, name ) {\n\t\t\t\t\tgroups[ name ] = key;\n\t\t\t\t} );\n\t\t\t} );\n\t\t\trules = this.settings.rules;\n\t\t\t$.each( rules, function( key, value ) {\n\t\t\t\trules[ key ] = $.validator.normalizeRule( value );\n\t\t\t} );\n\n\t\t\tfunction delegate( event ) {\n\t\t\t\tvar isContentEditable = typeof $( this ).attr( \"contenteditable\" ) !== \"undefined\" && $( this ).attr( \"contenteditable\" ) !== \"false\";\n\n\t\t\t\t// Set form expando on contenteditable\n\t\t\t\tif ( !this.form && isContentEditable ) {\n\t\t\t\t\tthis.form = $( this ).closest( \"form\" )[ 0 ];\n\t\t\t\t\tthis.name = $( this ).attr( \"name\" );\n\t\t\t\t}\n\n\t\t\t\t// Ignore the element if it belongs to another form. This will happen mainly\n\t\t\t\t// when setting the `form` attribute of an input to the id of another form.\n\t\t\t\tif ( currentForm !== this.form ) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tvar validator = $.data( this.form, \"validator\" ),\n\t\t\t\t\teventType = \"on\" + event.type.replace( /^validate/, \"\" ),\n\t\t\t\t\tsettings = validator.settings;\n\t\t\t\tif ( settings[ eventType ] && !$( this ).is( settings.ignore ) ) {\n\t\t\t\t\tsettings[ eventType ].call( validator, this, event );\n\t\t\t\t}\n\t\t\t}\n\t\t\tvar focusListeners = [ \":text\", \"[type='password']\", \"[type='file']\", \"select\", \"textarea\", \"[type='number']\", \"[type='search']\",\n\t\t\t\t\t\t\t\t\"[type='tel']\", \"[type='url']\", \"[type='email']\", \"[type='datetime']\", \"[type='date']\", \"[type='month']\",\n\t\t\t\t\t\t\t\t\"[type='week']\", \"[type='time']\", \"[type='datetime-local']\", \"[type='range']\", \"[type='color']\",\n\t\t\t\t\t\t\t\t\"[type='radio']\", \"[type='checkbox']\", \"[contenteditable]\", \"[type='button']\" ];\n\t\t\tvar clickListeners = [ \"select\", \"option\", \"[type='radio']\", \"[type='checkbox']\" ];\n\t\t\t$( this.currentForm )\n\t\t\t\t.on( \"focusin.validate focusout.validate keyup.validate\", focusListeners.concat( this.settings.customElements ).join( \", \" ), delegate )\n\n\t\t\t\t// Support: Chrome, oldIE\n\t\t\t\t// \"select\" is provided as event.target when clicking a option\n\t\t\t\t.on( \"click.validate\", clickListeners.concat( this.settings.customElements ).join( \", \" ), delegate );\n\n\t\t\tif ( this.settings.invalidHandler ) {\n\t\t\t\t$( this.currentForm ).on( \"invalid-form.validate\", this.settings.invalidHandler );\n\t\t\t}\n\t\t},\n\n\t\t// https://jqueryvalidation.org/Validator.form/\n\t\tform: function() {\n\t\t\tthis.checkForm();\n\t\t\t$.extend( this.submitted, this.errorMap );\n\t\t\tthis.invalid = $.extend( {}, this.errorMap );\n\t\t\tif ( !this.valid() ) {\n\t\t\t\t$( this.currentForm ).triggerHandler( \"invalid-form\", [ this ] );\n\t\t\t}\n\t\t\tthis.showErrors();\n\t\t\treturn this.valid();\n\t\t},\n\n\t\tcheckForm: function() {\n\t\t\tthis.prepareForm();\n\t\t\tfor ( var i = 0, elements = ( this.currentElements = this.elements() ); elements[ i ]; i++ ) {\n\t\t\t\tthis.check( elements[ i ] );\n\t\t\t}\n\t\t\treturn this.valid();\n\t\t},\n\n\t\t// https://jqueryvalidation.org/Validator.element/\n\t\telement: function( element ) {\n\t\t\tvar cleanElement = this.clean( element ),\n\t\t\t\tcheckElement = this.validationTargetFor( cleanElement ),\n\t\t\t\tv = this,\n\t\t\t\tresult = true,\n\t\t\t\trs, group;\n\n\t\t\tif ( checkElement === undefined ) {\n\t\t\t\tdelete this.invalid[ cleanElement.name ];\n\t\t\t} else {\n\t\t\t\tthis.prepareElement( checkElement );\n\t\t\t\tthis.currentElements = $( checkElement );\n\n\t\t\t\t// If this element is grouped, then validate all group elements already\n\t\t\t\t// containing a value\n\t\t\t\tgroup = this.groups[ checkElement.name ];\n\t\t\t\tif ( group ) {\n\t\t\t\t\t$.each( this.groups, function( name, testgroup ) {\n\t\t\t\t\t\tif ( testgroup === group && name !== checkElement.name ) {\n\t\t\t\t\t\t\tcleanElement = v.validationTargetFor( v.clean( v.findByName( name ) ) );\n\t\t\t\t\t\t\tif ( cleanElement && cleanElement.name in v.invalid ) {\n\t\t\t\t\t\t\t\tv.currentElements.push( cleanElement );\n\t\t\t\t\t\t\t\tresult = v.check( cleanElement ) && result;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} );\n\t\t\t\t}\n\n\t\t\t\trs = this.check( checkElement ) !== false;\n\t\t\t\tresult = result && rs;\n\t\t\t\tif ( rs ) {\n\t\t\t\t\tthis.invalid[ checkElement.name ] = false;\n\t\t\t\t} else {\n\t\t\t\t\tthis.invalid[ checkElement.name ] = true;\n\t\t\t\t}\n\n\t\t\t\tif ( !this.numberOfInvalids() ) {\n\n\t\t\t\t\t// Hide error containers on last error\n\t\t\t\t\tthis.toHide = this.toHide.add( this.containers );\n\t\t\t\t}\n\t\t\t\tthis.showErrors();\n\n\t\t\t\t// Add aria-invalid status for screen readers\n\t\t\t\t$( element ).attr( \"aria-invalid\", !rs );\n\t\t\t}\n\n\t\t\treturn result;\n\t\t},\n\n\t\t// https://jqueryvalidation.org/Validator.showErrors/\n\t\tshowErrors: function( errors ) {\n\t\t\tif ( errors ) {\n\t\t\t\tvar validator = this;\n\n\t\t\t\t// Add items to error list and map\n\t\t\t\t$.extend( this.errorMap, errors );\n\t\t\t\tthis.errorList = $.map( this.errorMap, function( message, name ) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tmessage: message,\n\t\t\t\t\t\telement: validator.findByName( name )[ 0 ]\n\t\t\t\t\t};\n\t\t\t\t} );\n\n\t\t\t\t// Remove items from success list\n\t\t\t\tthis.successList = $.grep( this.successList, function( element ) {\n\t\t\t\t\treturn !( element.name in errors );\n\t\t\t\t} );\n\t\t\t}\n\t\t\tif ( this.settings.showErrors ) {\n\t\t\t\tthis.settings.showErrors.call( this, this.errorMap, this.errorList );\n\t\t\t} else {\n\t\t\t\tthis.defaultShowErrors();\n\t\t\t}\n\t\t},\n\n\t\t// https://jqueryvalidation.org/Validator.resetForm/\n\t\tresetForm: function() {\n\t\t\tif ( $.fn.resetForm ) {\n\t\t\t\t$( this.currentForm ).resetForm();\n\t\t\t}\n\t\t\tthis.invalid = {};\n\t\t\tthis.submitted = {};\n\t\t\tthis.prepareForm();\n\t\t\tthis.hideErrors();\n\t\t\tvar elements = this.elements()\n\t\t\t\t.removeData( \"previousValue\" )\n\t\t\t\t.removeAttr( \"aria-invalid\" );\n\n\t\t\tthis.resetElements( elements );\n\t\t},\n\n\t\tresetElements: function( elements ) {\n\t\t\tvar i;\n\n\t\t\tif ( this.settings.unhighlight ) {\n\t\t\t\tfor ( i = 0; elements[ i ]; i++ ) {\n\t\t\t\t\tthis.settings.unhighlight.call( this, elements[ i ],\n\t\t\t\t\t\tthis.settings.errorClass, \"\" );\n\t\t\t\t\tthis.findByName( elements[ i ].name ).removeClass( this.settings.validClass );\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\telements\n\t\t\t\t\t.removeClass( this.settings.errorClass )\n\t\t\t\t\t.removeClass( this.settings.validClass );\n\t\t\t}\n\t\t},\n\n\t\tnumberOfInvalids: function() {\n\t\t\treturn this.objectLength( this.invalid );\n\t\t},\n\n\t\tobjectLength: function( obj ) {\n\t\t\t/* jshint unused: false */\n\t\t\tvar count = 0,\n\t\t\t\ti;\n\t\t\tfor ( i in obj ) {\n\n\t\t\t\t// This check allows counting elements with empty error\n\t\t\t\t// message as invalid elements\n\t\t\t\tif ( obj[ i ] !== undefined && obj[ i ] !== null && obj[ i ] !== false ) {\n\t\t\t\t\tcount++;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn count;\n\t\t},\n\n\t\thideErrors: function() {\n\t\t\tthis.hideThese( this.toHide );\n\t\t},\n\n\t\thideThese: function( errors ) {\n\t\t\terrors.not( this.containers ).text( \"\" );\n\t\t\tthis.addWrapper( errors ).hide();\n\t\t},\n\n\t\tvalid: function() {\n\t\t\treturn this.size() === 0;\n\t\t},\n\n\t\tsize: function() {\n\t\t\treturn this.errorList.length;\n\t\t},\n\n\t\tfocusInvalid: function() {\n\t\t\tif ( this.settings.focusInvalid ) {\n\t\t\t\ttry {\n\t\t\t\t\t$( this.findLastActive() || this.errorList.length && this.errorList[ 0 ].element || [] )\n\t\t\t\t\t.filter( \":visible\" )\n\t\t\t\t\t.trigger( \"focus\" )\n\n\t\t\t\t\t// Manually trigger focusin event; without it, focusin handler isn't called, findLastActive won't have anything to find\n\t\t\t\t\t.trigger( \"focusin\" );\n\t\t\t\t} catch ( e ) {\n\n\t\t\t\t\t// Ignore IE throwing errors when focusing hidden elements\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\tfindLastActive: function() {\n\t\t\tvar lastActive = this.lastActive;\n\t\t\treturn lastActive && $.grep( this.errorList, function( n ) {\n\t\t\t\treturn n.element.name === lastActive.name;\n\t\t\t} ).length === 1 && lastActive;\n\t\t},\n\n\t\telements: function() {\n\t\t\tvar validator = this,\n\t\t\t\trulesCache = {},\n\t\t\t\tselectors = [ \"input\", \"select\", \"textarea\", \"[contenteditable]\" ];\n\n\t\t\t// Select all valid inputs inside the form (no submit or reset buttons)\n\t\t\treturn $( this.currentForm )\n\t\t\t.find( selectors.concat( this.settings.customElements ).join( \", \" ) )\n\t\t\t.not( \":submit, :reset, :image, :disabled\" )\n\t\t\t.not( this.settings.ignore )\n\t\t\t.filter( function() {\n\t\t\t\tvar name = this.name || $( this ).attr( \"name\" ); // For contenteditable\n\t\t\t\tvar isContentEditable = typeof $( this ).attr( \"contenteditable\" ) !== \"undefined\" && $( this ).attr( \"contenteditable\" ) !== \"false\";\n\n\t\t\t\tif ( !name && validator.settings.debug && window.console ) {\n\t\t\t\t\tconsole.error( \"%o has no name assigned\", this );\n\t\t\t\t}\n\n\t\t\t\t// Set form expando on contenteditable\n\t\t\t\tif ( isContentEditable ) {\n\t\t\t\t\tthis.form = $( this ).closest( \"form\" )[ 0 ];\n\t\t\t\t\tthis.name = name;\n\t\t\t\t}\n\n\t\t\t\t// Ignore elements that belong to other/nested forms\n\t\t\t\tif ( this.form !== validator.currentForm ) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\t// Select only the first element for each name, and only those with rules specified\n\t\t\t\tif ( name in rulesCache || !validator.objectLength( $( this ).rules() ) ) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\trulesCache[ name ] = true;\n\t\t\t\treturn true;\n\t\t\t} );\n\t\t},\n\n\t\tclean: function( selector ) {\n\t\t\treturn $( selector )[ 0 ];\n\t\t},\n\n\t\terrors: function() {\n\t\t\tvar errorClass = this.settings.errorClass.split( \" \" ).join( \".\" );\n\t\t\treturn $( this.settings.errorElement + \".\" + errorClass, this.errorContext );\n\t\t},\n\n\t\tresetInternals: function() {\n\t\t\tthis.successList = [];\n\t\t\tthis.errorList = [];\n\t\t\tthis.errorMap = {};\n\t\t\tthis.toShow = $( [] );\n\t\t\tthis.toHide = $( [] );\n\t\t},\n\n\t\treset: function() {\n\t\t\tthis.resetInternals();\n\t\t\tthis.currentElements = $( [] );\n\t\t},\n\n\t\tprepareForm: function() {\n\t\t\tthis.reset();\n\t\t\tthis.toHide = this.errors().add( this.containers );\n\t\t},\n\n\t\tprepareElement: function( element ) {\n\t\t\tthis.reset();\n\t\t\tthis.toHide = this.errorsFor( element );\n\t\t},\n\n\t\telementValue: function( element ) {\n\t\t\tvar $element = $( element ),\n\t\t\t\ttype = element.type,\n\t\t\t\tisContentEditable = typeof $element.attr( \"contenteditable\" ) !== \"undefined\" && $element.attr( \"contenteditable\" ) !== \"false\",\n\t\t\t\tval, idx;\n\n\t\t\tif ( type === \"radio\" || type === \"checkbox\" ) {\n\t\t\t\treturn this.findByName( element.name ).filter( \":checked\" ).val();\n\t\t\t} else if ( type === \"number\" && typeof element.validity !== \"undefined\" ) {\n\t\t\t\treturn element.validity.badInput ? \"NaN\" : $element.val();\n\t\t\t}\n\n\t\t\tif ( isContentEditable ) {\n\t\t\t\tval = $element.text();\n\t\t\t} else {\n\t\t\t\tval = $element.val();\n\t\t\t}\n\n\t\t\tif ( type === \"file\" ) {\n\n\t\t\t\t// Modern browser (chrome & safari)\n\t\t\t\tif ( val.substr( 0, 12 ) === \"C:\\\\fakepath\\\\\" ) {\n\t\t\t\t\treturn val.substr( 12 );\n\t\t\t\t}\n\n\t\t\t\t// Legacy browsers\n\t\t\t\t// Unix-based path\n\t\t\t\tidx = val.lastIndexOf( \"/\" );\n\t\t\t\tif ( idx >= 0 ) {\n\t\t\t\t\treturn val.substr( idx + 1 );\n\t\t\t\t}\n\n\t\t\t\t// Windows-based path\n\t\t\t\tidx = val.lastIndexOf( \"\\\\\" );\n\t\t\t\tif ( idx >= 0 ) {\n\t\t\t\t\treturn val.substr( idx + 1 );\n\t\t\t\t}\n\n\t\t\t\t// Just the file name\n\t\t\t\treturn val;\n\t\t\t}\n\n\t\t\tif ( typeof val === \"string\" ) {\n\t\t\t\treturn val.replace( /\\r/g, \"\" );\n\t\t\t}\n\t\t\treturn val;\n\t\t},\n\n\t\tcheck: function( element ) {\n\t\t\telement = this.validationTargetFor( this.clean( element ) );\n\n\t\t\tvar rules = $( element ).rules(),\n\t\t\t\trulesCount = $.map( rules, function( n, i ) {\n\t\t\t\t\treturn i;\n\t\t\t\t} ).length,\n\t\t\t\tdependencyMismatch = false,\n\t\t\t\tval = this.elementValue( element ),\n\t\t\t\tresult, method, rule, normalizer;\n\n\t\t\t// Abort any pending Ajax request from a previous call to this method.\n\t\t\tthis.abortRequest( element );\n\n\t\t\t// Prioritize the local normalizer defined for this element over the global one\n\t\t\t// if the former exists, otherwise user the global one in case it exists.\n\t\t\tif ( typeof rules.normalizer === \"function\" ) {\n\t\t\t\tnormalizer = rules.normalizer;\n\t\t\t} else if (\ttypeof this.settings.normalizer === \"function\" ) {\n\t\t\t\tnormalizer = this.settings.normalizer;\n\t\t\t}\n\n\t\t\t// If normalizer is defined, then call it to retreive the changed value instead\n\t\t\t// of using the real one.\n\t\t\t// Note that `this` in the normalizer is `element`.\n\t\t\tif ( normalizer ) {\n\t\t\t\tval = normalizer.call( element, val );\n\n\t\t\t\t// Delete the normalizer from rules to avoid treating it as a pre-defined method.\n\t\t\t\tdelete rules.normalizer;\n\t\t\t}\n\n\t\t\tfor ( method in rules ) {\n\t\t\t\trule = { method: method, parameters: rules[ method ] };\n\t\t\t\ttry {\n\t\t\t\t\tresult = $.validator.methods[ method ].call( this, val, element, rule.parameters );\n\n\t\t\t\t\t// If a method indicates that the field is optional and therefore valid,\n\t\t\t\t\t// don't mark it as valid when there are no other rules\n\t\t\t\t\tif ( result === \"dependency-mismatch\" && rulesCount === 1 ) {\n\t\t\t\t\t\tdependencyMismatch = true;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tdependencyMismatch = false;\n\n\t\t\t\t\tif ( result === \"pending\" ) {\n\t\t\t\t\t\tthis.toHide = this.toHide.not( this.errorsFor( element ) );\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( !result ) {\n\t\t\t\t\t\tthis.formatAndAdd( element, rule );\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t} catch ( e ) {\n\t\t\t\t\tif ( this.settings.debug && window.console ) {\n\t\t\t\t\t\tconsole.log( \"Exception occurred when checking element \" + element.id + \", check the '\" + rule.method + \"' method.\", e );\n\t\t\t\t\t}\n\t\t\t\t\tif ( e instanceof TypeError ) {\n\t\t\t\t\t\te.message += \". Exception occurred when checking element \" + element.id + \", check the '\" + rule.method + \"' method.\";\n\t\t\t\t\t}\n\n\t\t\t\t\tthrow e;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif ( dependencyMismatch ) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif ( this.objectLength( rules ) ) {\n\t\t\t\tthis.successList.push( element );\n\t\t\t}\n\t\t\treturn true;\n\t\t},\n\n\t\t// Return the custom message for the given element and validation method\n\t\t// specified in the element's HTML5 data attribute\n\t\t// return the generic message if present and no method specific message is present\n\t\tcustomDataMessage: function( element, method ) {\n\t\t\treturn $( element ).data( \"msg\" + method.charAt( 0 ).toUpperCase() +\n\t\t\t\tmethod.substring( 1 ).toLowerCase() ) || $( element ).data( \"msg\" );\n\t\t},\n\n\t\t// Return the custom message for the given element name and validation method\n\t\tcustomMessage: function( name, method ) {\n\t\t\tvar m = this.settings.messages[ name ];\n\t\t\treturn m && ( m.constructor === String ? m : m[ method ] );\n\t\t},\n\n\t\t// Return the first defined argument, allowing empty strings\n\t\tfindDefined: function() {\n\t\t\tfor ( var i = 0; i < arguments.length; i++ ) {\n\t\t\t\tif ( arguments[ i ] !== undefined ) {\n\t\t\t\t\treturn arguments[ i ];\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn undefined;\n\t\t},\n\n\t\t// The second parameter 'rule' used to be a string, and extended to an object literal\n\t\t// of the following form:\n\t\t// rule = {\n\t\t// method: \"method name\",\n\t\t// parameters: \"the given method parameters\"\n\t\t// }\n\t\t//\n\t\t// The old behavior still supported, kept to maintain backward compatibility with\n\t\t// old code, and will be removed in the next major release.\n\t\tdefaultMessage: function( element, rule ) {\n\t\t\tif ( typeof rule === \"string\" ) {\n\t\t\t\trule = { method: rule };\n\t\t\t}\n\n\t\t\tvar message = this.findDefined(\n\t\t\t\t\tthis.customMessage( element.name, rule.method ),\n\t\t\t\t\tthis.customDataMessage( element, rule.method ),\n\n\t\t\t\t\t// 'title' is never undefined, so handle empty string as undefined\n\t\t\t\t\t!this.settings.ignoreTitle && element.title || undefined,\n\t\t\t\t\t$.validator.messages[ rule.method ],\n\t\t\t\t\t\"Warning: No message defined for \" + element.name + \"\"\n\t\t\t\t),\n\t\t\t\ttheregex = /\\$?\\{(\\d+)\\}/g;\n\t\t\tif ( typeof message === \"function\" ) {\n\t\t\t\tmessage = message.call( this, rule.parameters, element );\n\t\t\t} else if ( theregex.test( message ) ) {\n\t\t\t\tmessage = $.validator.format( message.replace( theregex, \"{$1}\" ), rule.parameters );\n\t\t\t}\n\n\t\t\treturn message;\n\t\t},\n\n\t\tformatAndAdd: function( element, rule ) {\n\t\t\tvar message = this.defaultMessage( element, rule );\n\n\t\t\tthis.errorList.push( {\n\t\t\t\tmessage: message,\n\t\t\t\telement: element,\n\t\t\t\tmethod: rule.method\n\t\t\t} );\n\n\t\t\tthis.errorMap[ element.name ] = message;\n\t\t\tthis.submitted[ element.name ] = message;\n\t\t},\n\n\t\taddWrapper: function( toToggle ) {\n\t\t\tif ( this.settings.wrapper ) {\n\t\t\t\ttoToggle = toToggle.add( toToggle.parent( this.settings.wrapper ) );\n\t\t\t}\n\t\t\treturn toToggle;\n\t\t},\n\n\t\tdefaultShowErrors: function() {\n\t\t\tvar i, elements, error;\n\t\t\tfor ( i = 0; this.errorList[ i ]; i++ ) {\n\t\t\t\terror = this.errorList[ i ];\n\t\t\t\tif ( this.settings.highlight ) {\n\t\t\t\t\tthis.settings.highlight.call( this, error.element, this.settings.errorClass, this.settings.validClass );\n\t\t\t\t}\n\t\t\t\tthis.showLabel( error.element, error.message );\n\t\t\t}\n\t\t\tif ( this.errorList.length ) {\n\t\t\t\tthis.toShow = this.toShow.add( this.containers );\n\t\t\t}\n\t\t\tif ( this.settings.success ) {\n\t\t\t\tfor ( i = 0; this.successList[ i ]; i++ ) {\n\t\t\t\t\tthis.showLabel( this.successList[ i ] );\n\t\t\t\t}\n\t\t\t}\n\t\t\tif ( this.settings.unhighlight ) {\n\t\t\t\tfor ( i = 0, elements = this.validElements(); elements[ i ]; i++ ) {\n\t\t\t\t\tthis.settings.unhighlight.call( this, elements[ i ], this.settings.errorClass, this.settings.validClass );\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.toHide = this.toHide.not( this.toShow );\n\t\t\tthis.hideErrors();\n\t\t\tthis.addWrapper( this.toShow ).show();\n\t\t},\n\n\t\tvalidElements: function() {\n\t\t\treturn this.currentElements.not( this.invalidElements() );\n\t\t},\n\n\t\tinvalidElements: function() {\n\t\t\treturn $( this.errorList ).map( function() {\n\t\t\t\treturn this.element;\n\t\t\t} );\n\t\t},\n\n\t\tshowLabel: function( element, message ) {\n\t\t\tvar place, group, errorID, v,\n\t\t\t\terror = this.errorsFor( element ),\n\t\t\t\telementID = this.idOrName( element ),\n\t\t\t\tdescribedBy = $( element ).attr( \"aria-describedby\" );\n\n\t\t\tif ( error.length ) {\n\n\t\t\t\t// Refresh error/success class\n\t\t\t\terror.removeClass( this.settings.validClass ).addClass( this.settings.errorClass );\n\n\t\t\t\t// Replace message on existing label\n\t\t\t\tif ( this.settings && this.settings.escapeHtml ) {\n\t\t\t\t\terror.text( message || \"\" );\n\t\t\t\t} else {\n\t\t\t\t\terror.html( message || \"\" );\n\t\t\t\t}\n\t\t\t} else {\n\n\t\t\t\t// Create error element\n\t\t\t\terror = $( \"<\" + this.settings.errorElement + \">\" )\n\t\t\t\t\t.attr( \"id\", elementID + \"-error\" )\n\t\t\t\t\t.addClass( this.settings.errorClass );\n\n\t\t\t\tif ( this.settings && this.settings.escapeHtml ) {\n\t\t\t\t\terror.text( message || \"\" );\n\t\t\t\t} else {\n\t\t\t\t\terror.html( message || \"\" );\n\t\t\t\t}\n\n\t\t\t\t// Maintain reference to the element to be placed into the DOM\n\t\t\t\tplace = error;\n\t\t\t\tif ( this.settings.wrapper ) {\n\n\t\t\t\t\t// Make sure the element is visible, even in IE\n\t\t\t\t\t// actually showing the wrapped element is handled elsewhere\n\t\t\t\t\tplace = error.hide().show().wrap( \"<\" + this.settings.wrapper + \"/>\" ).parent();\n\t\t\t\t}\n\t\t\t\tif ( this.labelContainer.length ) {\n\t\t\t\t\tthis.labelContainer.append( place );\n\t\t\t\t} else if ( this.settings.errorPlacement ) {\n\t\t\t\t\tthis.settings.errorPlacement.call( this, place, $( element ) );\n\t\t\t\t} else {\n\t\t\t\t\tplace.insertAfter( element );\n\t\t\t\t}\n\n\t\t\t\t// Link error back to the element\n\t\t\t\tif ( error.is( \"label\" ) ) {\n\n\t\t\t\t\t// If the error is a label, then associate using 'for'\n\t\t\t\t\terror.attr( \"for\", elementID );\n\n\t\t\t\t\t// If the element is not a child of an associated label, then it's necessary\n\t\t\t\t\t// to explicitly apply aria-describedby\n\t\t\t\t} else if ( error.parents( \"label[for='\" + this.escapeCssMeta( elementID ) + \"']\" ).length === 0 ) {\n\t\t\t\t\terrorID = error.attr( \"id\" );\n\n\t\t\t\t\t// Respect existing non-error aria-describedby\n\t\t\t\t\tif ( !describedBy ) {\n\t\t\t\t\t\tdescribedBy = errorID;\n\t\t\t\t\t} else if ( !describedBy.match( new RegExp( \"\\\\b\" + this.escapeCssMeta( errorID ) + \"\\\\b\" ) ) ) {\n\n\t\t\t\t\t\t// Add to end of list if not already present\n\t\t\t\t\t\tdescribedBy += \" \" + errorID;\n\t\t\t\t\t}\n\t\t\t\t\t$( element ).attr( \"aria-describedby\", describedBy );\n\n\t\t\t\t\t// If this element is grouped, then assign to all elements in the same group\n\t\t\t\t\tgroup = this.groups[ element.name ];\n\t\t\t\t\tif ( group ) {\n\t\t\t\t\t\tv = this;\n\t\t\t\t\t\t$.each( v.groups, function( name, testgroup ) {\n\t\t\t\t\t\t\tif ( testgroup === group ) {\n\t\t\t\t\t\t\t\t$( \"[name='\" + v.escapeCssMeta( name ) + \"']\", v.currentForm )\n\t\t\t\t\t\t\t\t\t.attr( \"aria-describedby\", error.attr( \"id\" ) );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif ( !message && this.settings.success ) {\n\t\t\t\terror.text( \"\" );\n\t\t\t\tif ( typeof this.settings.success === \"string\" ) {\n\t\t\t\t\terror.addClass( this.settings.success );\n\t\t\t\t} else {\n\t\t\t\t\tthis.settings.success( error, element );\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.toShow = this.toShow.add( error );\n\t\t},\n\n\t\terrorsFor: function( element ) {\n\t\t\tvar name = this.escapeCssMeta( this.idOrName( element ) ),\n\t\t\t\tdescriber = $( element ).attr( \"aria-describedby\" ),\n\t\t\t\tselector = \"label[for='\" + name + \"'], label[for='\" + name + \"'] *\";\n\n\t\t\t// 'aria-describedby' should directly reference the error element\n\t\t\tif ( describer ) {\n\t\t\t\tselector = selector + \", #\" + this.escapeCssMeta( describer )\n\t\t\t\t\t.replace( /\\s+/g, \", #\" );\n\t\t\t}\n\n\t\t\treturn this\n\t\t\t\t.errors()\n\t\t\t\t.filter( selector );\n\t\t},\n\n\t\t// See https://api.jquery.com/category/selectors/, for CSS\n\t\t// meta-characters that should be escaped in order to be used with JQuery\n\t\t// as a literal part of a name/id or any selector.\n\t\tescapeCssMeta: function( string ) {\n\t\t\tif ( string === undefined ) {\n\t\t\t\treturn \"\";\n\t\t\t}\n\n\t\t\treturn string.replace( /([\\\\!\"#$%&'()*+,./:;<=>?@\\[\\]^`{|}~])/g, \"\\\\$1\" );\n\t\t},\n\n\t\tidOrName: function( element ) {\n\t\t\treturn this.groups[ element.name ] || ( this.checkable( element ) ? element.name : element.id || element.name );\n\t\t},\n\n\t\tvalidationTargetFor: function( element ) {\n\n\t\t\t// If radio/checkbox, validate first element in group instead\n\t\t\tif ( this.checkable( element ) ) {\n\t\t\t\telement = this.findByName( element.name );\n\t\t\t}\n\n\t\t\t// Always apply ignore filter\n\t\t\treturn $( element ).not( this.settings.ignore )[ 0 ];\n\t\t},\n\n\t\tcheckable: function( element ) {\n\t\t\treturn ( /radio|checkbox/i ).test( element.type );\n\t\t},\n\n\t\tfindByName: function( name ) {\n\t\t\treturn $( this.currentForm ).find( \"[name='\" + this.escapeCssMeta( name ) + \"']\" );\n\t\t},\n\n\t\tgetLength: function( value, element ) {\n\t\t\tswitch ( element.nodeName.toLowerCase() ) {\n\t\t\tcase \"select\":\n\t\t\t\treturn $( \"option:selected\", element ).length;\n\t\t\tcase \"input\":\n\t\t\t\tif ( this.checkable( element ) ) {\n\t\t\t\t\treturn this.findByName( element.name ).filter( \":checked\" ).length;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn value.length;\n\t\t},\n\n\t\tdepend: function( param, element ) {\n\t\t\treturn this.dependTypes[ typeof param ] ? this.dependTypes[ typeof param ]( param, element ) : true;\n\t\t},\n\n\t\tdependTypes: {\n\t\t\t\"boolean\": function( param ) {\n\t\t\t\treturn param;\n\t\t\t},\n\t\t\t\"string\": function( param, element ) {\n\t\t\t\treturn !!$( param, element.form ).length;\n\t\t\t},\n\t\t\t\"function\": function( param, element ) {\n\t\t\t\treturn param( element );\n\t\t\t}\n\t\t},\n\n\t\toptional: function( element ) {\n\t\t\tvar val = this.elementValue( element );\n\t\t\treturn !$.validator.methods.required.call( this, val, element ) && \"dependency-mismatch\";\n\t\t},\n\n\t\telementAjaxPort: function( element ) {\n\t\t\treturn \"validate\" + element.name;\n\t\t},\n\n\t\tstartRequest: function( element ) {\n\t\t\tif ( !this.pending[ element.name ] ) {\n\t\t\t\tthis.pendingRequest++;\n\t\t\t\t$( element ).addClass( this.settings.pendingClass );\n\t\t\t\tthis.pending[ element.name ] = true;\n\t\t\t}\n\t\t},\n\n\t\tstopRequest: function( element, valid ) {\n\t\t\tthis.pendingRequest--;\n\n\t\t\t// Sometimes synchronization fails, make sure pendingRequest is never < 0\n\t\t\tif ( this.pendingRequest < 0 ) {\n\t\t\t\tthis.pendingRequest = 0;\n\t\t\t}\n\t\t\tdelete this.pending[ element.name ];\n\t\t\t$( element ).removeClass( this.settings.pendingClass );\n\t\t\tif ( valid && this.pendingRequest === 0 && this.formSubmitted && this.form() && this.pendingRequest === 0 ) {\n\t\t\t\t$( this.currentForm ).trigger( \"submit\" );\n\n\t\t\t\t// Remove the hidden input that was used as a replacement for the\n\t\t\t\t// missing submit button. The hidden input is added by `handle()`\n\t\t\t\t// to ensure that the value of the used submit button is passed on\n\t\t\t\t// for scripted submits triggered by this method\n\t\t\t\tif ( this.submitButton ) {\n\t\t\t\t\t$( \"input:hidden[name='\" + this.submitButton.name + \"']\", this.currentForm ).remove();\n\t\t\t\t}\n\n\t\t\t\tthis.formSubmitted = false;\n\t\t\t} else if ( !valid && this.pendingRequest === 0 && this.formSubmitted ) {\n\t\t\t\t$( this.currentForm ).triggerHandler( \"invalid-form\", [ this ] );\n\t\t\t\tthis.formSubmitted = false;\n\t\t\t}\n\t\t},\n\n\t\tabortRequest: function( element ) {\n\t\t\tvar port;\n\n\t\t\tif ( this.pending[ element.name ] ) {\n\t\t\t\tport = this.elementAjaxPort( element );\n\t\t\t\t$.ajaxAbort( port );\n\n\t\t\t\tthis.pendingRequest--;\n\n\t\t\t\t// Sometimes synchronization fails, make sure pendingRequest is never < 0\n\t\t\t\tif ( this.pendingRequest < 0 ) {\n\t\t\t\t\tthis.pendingRequest = 0;\n\t\t\t\t}\n\n\t\t\t\tdelete this.pending[ element.name ];\n\t\t\t\t$( element ).removeClass( this.settings.pendingClass );\n\t\t\t}\n\t\t},\n\n\t\tpreviousValue: function( element, method ) {\n\t\t\tmethod = typeof method === \"string\" && method || \"remote\";\n\n\t\t\treturn $.data( element, \"previousValue\" ) || $.data( element, \"previousValue\", {\n\t\t\t\told: null,\n\t\t\t\tvalid: true,\n\t\t\t\tmessage: this.defaultMessage( element, { method: method } )\n\t\t\t} );\n\t\t},\n\n\t\t// Cleans up all forms and elements, removes validator-specific events\n\t\tdestroy: function() {\n\t\t\tthis.resetForm();\n\n\t\t\t$( this.currentForm )\n\t\t\t\t.off( \".validate\" )\n\t\t\t\t.removeData( \"validator\" )\n\t\t\t\t.find( \".validate-equalTo-blur\" )\n\t\t\t\t\t.off( \".validate-equalTo\" )\n\t\t\t\t\t.removeClass( \"validate-equalTo-blur\" )\n\t\t\t\t.find( \".validate-lessThan-blur\" )\n\t\t\t\t\t.off( \".validate-lessThan\" )\n\t\t\t\t\t.removeClass( \"validate-lessThan-blur\" )\n\t\t\t\t.find( \".validate-lessThanEqual-blur\" )\n\t\t\t\t\t.off( \".validate-lessThanEqual\" )\n\t\t\t\t\t.removeClass( \"validate-lessThanEqual-blur\" )\n\t\t\t\t.find( \".validate-greaterThanEqual-blur\" )\n\t\t\t\t\t.off( \".validate-greaterThanEqual\" )\n\t\t\t\t\t.removeClass( \"validate-greaterThanEqual-blur\" )\n\t\t\t\t.find( \".validate-greaterThan-blur\" )\n\t\t\t\t\t.off( \".validate-greaterThan\" )\n\t\t\t\t\t.removeClass( \"validate-greaterThan-blur\" );\n\t\t}\n\n\t},\n\n\tclassRuleSettings: {\n\t\trequired: { required: true },\n\t\temail: { email: true },\n\t\turl: { url: true },\n\t\tdate: { date: true },\n\t\tdateISO: { dateISO: true },\n\t\tnumber: { number: true },\n\t\tdigits: { digits: true },\n\t\tcreditcard: { creditcard: true }\n\t},\n\n\taddClassRules: function( className, rules ) {\n\t\tif ( className.constructor === String ) {\n\t\t\tthis.classRuleSettings[ className ] = rules;\n\t\t} else {\n\t\t\t$.extend( this.classRuleSettings, className );\n\t\t}\n\t},\n\n\tclassRules: function( element ) {\n\t\tvar rules = {},\n\t\t\tclasses = $( element ).attr( \"class\" );\n\n\t\tif ( classes ) {\n\t\t\t$.each( classes.split( \" \" ), function() {\n\t\t\t\tif ( this in $.validator.classRuleSettings ) {\n\t\t\t\t\t$.extend( rules, $.validator.classRuleSettings[ this ] );\n\t\t\t\t}\n\t\t\t} );\n\t\t}\n\t\treturn rules;\n\t},\n\n\tnormalizeAttributeRule: function( rules, type, method, value ) {\n\n\t\t// Convert the value to a number for number inputs, and for text for backwards compability\n\t\t// allows type=\"date\" and others to be compared as strings\n\t\tif ( /min|max|step/.test( method ) && ( type === null || /number|range|text/.test( type ) ) ) {\n\t\t\tvalue = Number( value );\n\n\t\t\t// Support Opera Mini, which returns NaN for undefined minlength\n\t\t\tif ( isNaN( value ) ) {\n\t\t\t\tvalue = undefined;\n\t\t\t}\n\t\t}\n\n\t\tif ( value || value === 0 ) {\n\t\t\trules[ method ] = value;\n\t\t} else if ( type === method && type !== \"range\" ) {\n\n\t\t\t// Exception: the jquery validate 'range' method\n\t\t\t// does not test for the html5 'range' type\n\t\t\trules[ type === \"date\" ? \"dateISO\" : method ] = true;\n\t\t}\n\t},\n\n\tattributeRules: function( element ) {\n\t\tvar rules = {},\n\t\t\t$element = $( element ),\n\t\t\ttype = element.getAttribute( \"type\" ),\n\t\t\tmethod, value;\n\n\t\tfor ( method in $.validator.methods ) {\n\n\t\t\t// Support for in both html5 and older browsers\n\t\t\tif ( method === \"required\" ) {\n\t\t\t\tvalue = element.getAttribute( method );\n\n\t\t\t\t// Some browsers return an empty string for the required attribute\n\t\t\t\t// and non-HTML5 browsers might have required=\"\" markup\n\t\t\t\tif ( value === \"\" ) {\n\t\t\t\t\tvalue = true;\n\t\t\t\t}\n\n\t\t\t\t// Force non-HTML5 browsers to return bool\n\t\t\t\tvalue = !!value;\n\t\t\t} else {\n\t\t\t\tvalue = $element.attr( method );\n\t\t\t}\n\n\t\t\tthis.normalizeAttributeRule( rules, type, method, value );\n\t\t}\n\n\t\t// 'maxlength' may be returned as -1, 2147483647 ( IE ) and 524288 ( safari ) for text inputs\n\t\tif ( rules.maxlength && /-1|2147483647|524288/.test( rules.maxlength ) ) {\n\t\t\tdelete rules.maxlength;\n\t\t}\n\n\t\treturn rules;\n\t},\n\n\tdataRules: function( element ) {\n\t\tvar rules = {},\n\t\t\t$element = $( element ),\n\t\t\ttype = element.getAttribute( \"type\" ),\n\t\t\tmethod, value;\n\n\t\tfor ( method in $.validator.methods ) {\n\t\t\tvalue = $element.data( \"rule\" + method.charAt( 0 ).toUpperCase() + method.substring( 1 ).toLowerCase() );\n\n\t\t\t// Cast empty attributes like `data-rule-required` to `true`\n\t\t\tif ( value === \"\" ) {\n\t\t\t\tvalue = true;\n\t\t\t}\n\n\t\t\tthis.normalizeAttributeRule( rules, type, method, value );\n\t\t}\n\t\treturn rules;\n\t},\n\n\tstaticRules: function( element ) {\n\t\tvar rules = {},\n\t\t\tvalidator = $.data( element.form, \"validator\" );\n\n\t\tif ( validator.settings.rules ) {\n\t\t\trules = $.validator.normalizeRule( validator.settings.rules[ element.name ] ) || {};\n\t\t}\n\t\treturn rules;\n\t},\n\n\tnormalizeRules: function( rules, element ) {\n\n\t\t// Handle dependency check\n\t\t$.each( rules, function( prop, val ) {\n\n\t\t\t// Ignore rule when param is explicitly false, eg. required:false\n\t\t\tif ( val === false ) {\n\t\t\t\tdelete rules[ prop ];\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif ( val.param || val.depends ) {\n\t\t\t\tvar keepRule = true;\n\t\t\t\tswitch ( typeof val.depends ) {\n\t\t\t\tcase \"string\":\n\t\t\t\t\tkeepRule = !!$( val.depends, element.form ).length;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"function\":\n\t\t\t\t\tkeepRule = val.depends.call( element, element );\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif ( keepRule ) {\n\t\t\t\t\trules[ prop ] = val.param !== undefined ? val.param : true;\n\t\t\t\t} else {\n\t\t\t\t\t$.data( element.form, \"validator\" ).resetElements( $( element ) );\n\t\t\t\t\tdelete rules[ prop ];\n\t\t\t\t}\n\t\t\t}\n\t\t} );\n\n\t\t// Evaluate parameters\n\t\t$.each( rules, function( rule, parameter ) {\n\t\t\trules[ rule ] = typeof parameter === \"function\" && rule !== \"normalizer\" ? parameter( element ) : parameter;\n\t\t} );\n\n\t\t// Clean number parameters\n\t\t$.each( [ \"minlength\", \"maxlength\" ], function() {\n\t\t\tif ( rules[ this ] ) {\n\t\t\t\trules[ this ] = Number( rules[ this ] );\n\t\t\t}\n\t\t} );\n\t\t$.each( [ \"rangelength\", \"range\" ], function() {\n\t\t\tvar parts;\n\t\t\tif ( rules[ this ] ) {\n\t\t\t\tif ( Array.isArray( rules[ this ] ) ) {\n\t\t\t\t\trules[ this ] = [ Number( rules[ this ][ 0 ] ), Number( rules[ this ][ 1 ] ) ];\n\t\t\t\t} else if ( typeof rules[ this ] === \"string\" ) {\n\t\t\t\t\tparts = rules[ this ].replace( /[\\[\\]]/g, \"\" ).split( /[\\s,]+/ );\n\t\t\t\t\trules[ this ] = [ Number( parts[ 0 ] ), Number( parts[ 1 ] ) ];\n\t\t\t\t}\n\t\t\t}\n\t\t} );\n\n\t\tif ( $.validator.autoCreateRanges ) {\n\n\t\t\t// Auto-create ranges\n\t\t\tif ( rules.min != null && rules.max != null ) {\n\t\t\t\trules.range = [ rules.min, rules.max ];\n\t\t\t\tdelete rules.min;\n\t\t\t\tdelete rules.max;\n\t\t\t}\n\t\t\tif ( rules.minlength != null && rules.maxlength != null ) {\n\t\t\t\trules.rangelength = [ rules.minlength, rules.maxlength ];\n\t\t\t\tdelete rules.minlength;\n\t\t\t\tdelete rules.maxlength;\n\t\t\t}\n\t\t}\n\n\t\treturn rules;\n\t},\n\n\t// Converts a simple string to a {string: true} rule, e.g., \"required\" to {required:true}\n\tnormalizeRule: function( data ) {\n\t\tif ( typeof data === \"string\" ) {\n\t\t\tvar transformed = {};\n\t\t\t$.each( data.split( /\\s/ ), function() {\n\t\t\t\ttransformed[ this ] = true;\n\t\t\t} );\n\t\t\tdata = transformed;\n\t\t}\n\t\treturn data;\n\t},\n\n\t// https://jqueryvalidation.org/jQuery.validator.addMethod/\n\taddMethod: function( name, method, message ) {\n\t\t$.validator.methods[ name ] = method;\n\t\t$.validator.messages[ name ] = message !== undefined ? message : $.validator.messages[ name ];\n\t\tif ( method.length < 3 ) {\n\t\t\t$.validator.addClassRules( name, $.validator.normalizeRule( name ) );\n\t\t}\n\t},\n\n\t// https://jqueryvalidation.org/jQuery.validator.methods/\n\tmethods: {\n\n\t\t// https://jqueryvalidation.org/required-method/\n\t\trequired: function( value, element, param ) {\n\n\t\t\t// Check if dependency is met\n\t\t\tif ( !this.depend( param, element ) ) {\n\t\t\t\treturn \"dependency-mismatch\";\n\t\t\t}\n\t\t\tif ( element.nodeName.toLowerCase() === \"select\" ) {\n\n\t\t\t\t// Could be an array for select-multiple or a string, both are fine this way\n\t\t\t\tvar val = $( element ).val();\n\t\t\t\treturn val && val.length > 0;\n\t\t\t}\n\t\t\tif ( this.checkable( element ) ) {\n\t\t\t\treturn this.getLength( value, element ) > 0;\n\t\t\t}\n\t\t\treturn value !== undefined && value !== null && value.length > 0;\n\t\t},\n\n\t\t// https://jqueryvalidation.org/email-method/\n\t\temail: function( value, element ) {\n\n\t\t\t// From https://html.spec.whatwg.org/multipage/forms.html#valid-e-mail-address\n\t\t\t// Retrieved 2014-01-14\n\t\t\t// If you have a problem with this implementation, report a bug against the above spec\n\t\t\t// Or use custom methods to implement your own email validation\n\t\t\treturn this.optional( element ) || /^[a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test( value );\n\t\t},\n\n\t\t// https://jqueryvalidation.org/url-method/\n\t\turl: function( value, element ) {\n\n\t\t\t// Copyright (c) 2010-2013 Diego Perini, MIT licensed\n\t\t\t// https://gist.github.com/dperini/729294\n\t\t\t// see also https://mathiasbynens.be/demo/url-regex\n\t\t\t// modified to allow protocol-relative URLs\n\t\t\treturn this.optional( element ) || /^(?:(?:(?:https?|ftp):)?\\/\\/)(?:(?:[^\\]\\[?\\/<~#`!@$^&*()+=}|:\";',>{ ]|%[0-9A-Fa-f]{2})+(?::(?:[^\\]\\[?\\/<~#`!@$^&*()+=}|:\";',>{ ]|%[0-9A-Fa-f]{2})*)?@)?(?:(?!(?:10|127)(?:\\.\\d{1,3}){3})(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z0-9\\u00a1-\\uffff][a-z0-9\\u00a1-\\uffff_-]{0,62})?[a-z0-9\\u00a1-\\uffff]\\.)+(?:[a-z\\u00a1-\\uffff]{2,}\\.?))(?::\\d{2,5})?(?:[/?#]\\S*)?$/i.test( value );\n\t\t},\n\n\t\t// https://jqueryvalidation.org/date-method/\n\t\tdate: ( function() {\n\t\t\tvar called = false;\n\n\t\t\treturn function( value, element ) {\n\t\t\t\tif ( !called ) {\n\t\t\t\t\tcalled = true;\n\t\t\t\t\tif ( this.settings.debug && window.console ) {\n\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t\"The `date` method is deprecated and will be removed in version '2.0.0'.\\n\" +\n\t\t\t\t\t\t\t\"Please don't use it, since it relies on the Date constructor, which\\n\" +\n\t\t\t\t\t\t\t\"behaves very differently across browsers and locales. Use `dateISO`\\n\" +\n\t\t\t\t\t\t\t\"instead or one of the locale specific methods in `localizations/`\\n\" +\n\t\t\t\t\t\t\t\"and `additional-methods.js`.\"\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn this.optional( element ) || !/Invalid|NaN/.test( new Date( value ).toString() );\n\t\t\t};\n\t\t}() ),\n\n\t\t// https://jqueryvalidation.org/dateISO-method/\n\t\tdateISO: function( value, element ) {\n\t\t\treturn this.optional( element ) || /^\\d{4}[\\/\\-](0?[1-9]|1[012])[\\/\\-](0?[1-9]|[12][0-9]|3[01])$/.test( value );\n\t\t},\n\n\t\t// https://jqueryvalidation.org/number-method/\n\t\tnumber: function( value, element ) {\n\t\t\treturn this.optional( element ) || /^(?:-?\\d+|-?\\d{1,3}(?:,\\d{3})+)?(?:-?\\.\\d+)?$/.test( value );\n\t\t},\n\n\t\t// https://jqueryvalidation.org/digits-method/\n\t\tdigits: function( value, element ) {\n\t\t\treturn this.optional( element ) || /^\\d+$/.test( value );\n\t\t},\n\n\t\t// https://jqueryvalidation.org/minlength-method/\n\t\tminlength: function( value, element, param ) {\n\t\t\tvar length = Array.isArray( value ) ? value.length : this.getLength( value, element );\n\t\t\treturn this.optional( element ) || length >= param;\n\t\t},\n\n\t\t// https://jqueryvalidation.org/maxlength-method/\n\t\tmaxlength: function( value, element, param ) {\n\t\t\tvar length = Array.isArray( value ) ? value.length : this.getLength( value, element );\n\t\t\treturn this.optional( element ) || length <= param;\n\t\t},\n\n\t\t// https://jqueryvalidation.org/rangelength-method/\n\t\trangelength: function( value, element, param ) {\n\t\t\tvar length = Array.isArray( value ) ? value.length : this.getLength( value, element );\n\t\t\treturn this.optional( element ) || ( length >= param[ 0 ] && length <= param[ 1 ] );\n\t\t},\n\n\t\t// https://jqueryvalidation.org/min-method/\n\t\tmin: function( value, element, param ) {\n\t\t\treturn this.optional( element ) || value >= param;\n\t\t},\n\n\t\t// https://jqueryvalidation.org/max-method/\n\t\tmax: function( value, element, param ) {\n\t\t\treturn this.optional( element ) || value <= param;\n\t\t},\n\n\t\t// https://jqueryvalidation.org/range-method/\n\t\trange: function( value, element, param ) {\n\t\t\treturn this.optional( element ) || ( value >= param[ 0 ] && value <= param[ 1 ] );\n\t\t},\n\n\t\t// https://jqueryvalidation.org/step-method/\n\t\tstep: function( value, element, param ) {\n\t\t\tvar type = $( element ).attr( \"type\" ),\n\t\t\t\terrorMessage = \"Step attribute on input type \" + type + \" is not supported.\",\n\t\t\t\tsupportedTypes = [ \"text\", \"number\", \"range\" ],\n\t\t\t\tre = new RegExp( \"\\\\b\" + type + \"\\\\b\" ),\n\t\t\t\tnotSupported = type && !re.test( supportedTypes.join() ),\n\t\t\t\tdecimalPlaces = function( num ) {\n\t\t\t\t\tvar match = ( \"\" + num ).match( /(?:\\.(\\d+))?$/ );\n\t\t\t\t\tif ( !match ) {\n\t\t\t\t\t\treturn 0;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Number of digits right of decimal point.\n\t\t\t\t\treturn match[ 1 ] ? match[ 1 ].length : 0;\n\t\t\t\t},\n\t\t\t\ttoInt = function( num ) {\n\t\t\t\t\treturn Math.round( num * Math.pow( 10, decimals ) );\n\t\t\t\t},\n\t\t\t\tvalid = true,\n\t\t\t\tdecimals;\n\n\t\t\t// Works only for text, number and range input types\n\t\t\t// TODO find a way to support input types date, datetime, datetime-local, month, time and week\n\t\t\tif ( notSupported ) {\n\t\t\t\tthrow new Error( errorMessage );\n\t\t\t}\n\n\t\t\tdecimals = decimalPlaces( param );\n\n\t\t\t// Value can't have too many decimals\n\t\t\tif ( decimalPlaces( value ) > decimals || toInt( value ) % toInt( param ) !== 0 ) {\n\t\t\t\tvalid = false;\n\t\t\t}\n\n\t\t\treturn this.optional( element ) || valid;\n\t\t},\n\n\t\t// https://jqueryvalidation.org/equalTo-method/\n\t\tequalTo: function( value, element, param ) {\n\n\t\t\t// Bind to the blur event of the target in order to revalidate whenever the target field is updated\n\t\t\tvar target = $( param );\n\t\t\tif ( this.settings.onfocusout && target.not( \".validate-equalTo-blur\" ).length ) {\n\t\t\t\ttarget.addClass( \"validate-equalTo-blur\" ).on( \"blur.validate-equalTo\", function() {\n\t\t\t\t\t$( element ).valid();\n\t\t\t\t} );\n\t\t\t}\n\t\t\treturn value === target.val();\n\t\t},\n\n\t\t// https://jqueryvalidation.org/remote-method/\n\t\tremote: function( value, element, param, method ) {\n\t\t\tif ( this.optional( element ) ) {\n\t\t\t\treturn \"dependency-mismatch\";\n\t\t\t}\n\n\t\t\tmethod = typeof method === \"string\" && method || \"remote\";\n\n\t\t\tvar previous = this.previousValue( element, method ),\n\t\t\t\tvalidator, data, optionDataString;\n\n\t\t\tif ( !this.settings.messages[ element.name ] ) {\n\t\t\t\tthis.settings.messages[ element.name ] = {};\n\t\t\t}\n\t\t\tprevious.originalMessage = previous.originalMessage || this.settings.messages[ element.name ][ method ];\n\t\t\tthis.settings.messages[ element.name ][ method ] = previous.message;\n\n\t\t\tparam = typeof param === \"string\" && { url: param } || param;\n\t\t\toptionDataString = $.param( $.extend( { data: value }, param.data ) );\n\t\t\tif ( previous.valid !== null && previous.old === optionDataString ) {\n\t\t\t\treturn previous.valid;\n\t\t\t}\n\n\t\t\tprevious.old = optionDataString;\n\t\t\tprevious.valid = null;\n\t\t\tvalidator = this;\n\t\t\tthis.startRequest( element );\n\t\t\tdata = {};\n\t\t\tdata[ element.name ] = value;\n\t\t\t$.ajax( $.extend( true, {\n\t\t\t\tmode: \"abort\",\n\t\t\t\tport: this.elementAjaxPort( element ),\n\t\t\t\tdataType: \"json\",\n\t\t\t\tdata: data,\n\t\t\t\tcontext: validator.currentForm,\n\t\t\t\tsuccess: function( response ) {\n\t\t\t\t\tvar valid = response === true || response === \"true\",\n\t\t\t\t\t\terrors, message, submitted;\n\n\t\t\t\t\tvalidator.settings.messages[ element.name ][ method ] = previous.originalMessage;\n\t\t\t\t\tif ( valid ) {\n\t\t\t\t\t\tsubmitted = validator.formSubmitted;\n\t\t\t\t\t\tvalidator.toHide = validator.errorsFor( element );\n\t\t\t\t\t\tvalidator.formSubmitted = submitted;\n\t\t\t\t\t\tvalidator.successList.push( element );\n\t\t\t\t\t\tvalidator.invalid[ element.name ] = false;\n\t\t\t\t\t\tvalidator.showErrors();\n\t\t\t\t\t} else {\n\t\t\t\t\t\terrors = {};\n\t\t\t\t\t\tmessage = response || validator.defaultMessage( element, { method: method, parameters: value } );\n\t\t\t\t\t\terrors[ element.name ] = previous.message = message;\n\t\t\t\t\t\tvalidator.invalid[ element.name ] = true;\n\t\t\t\t\t\tvalidator.showErrors( errors );\n\t\t\t\t\t}\n\t\t\t\t\tprevious.valid = valid;\n\t\t\t\t\tvalidator.stopRequest( element, valid );\n\t\t\t\t}\n\t\t\t}, param ) );\n\t\t\treturn \"pending\";\n\t\t}\n\t}\n\n} );\n\n// Ajax mode: abort\n// usage: $.ajax({ mode: \"abort\"[, port: \"uniqueport\"]});\n// $.ajaxAbort( port );\n// if mode:\"abort\" is used, the previous request on that port (port can be undefined) is aborted via XMLHttpRequest.abort()\n\nvar pendingRequests = {},\n\tajax;\n\n// Use a prefilter if available (1.5+)\nif ( $.ajaxPrefilter ) {\n\t$.ajaxPrefilter( function( settings, _, xhr ) {\n\t\tvar port = settings.port;\n\t\tif ( settings.mode === \"abort\" ) {\n\t\t\t$.ajaxAbort( port );\n\t\t\tpendingRequests[ port ] = xhr;\n\t\t}\n\t} );\n} else {\n\n\t// Proxy ajax\n\tajax = $.ajax;\n\t$.ajax = function( settings ) {\n\t\tvar mode = ( \"mode\" in settings ? settings : $.ajaxSettings ).mode,\n\t\t\tport = ( \"port\" in settings ? settings : $.ajaxSettings ).port;\n\t\tif ( mode === \"abort\" ) {\n\t\t\t$.ajaxAbort( port );\n\t\t\tpendingRequests[ port ] = ajax.apply( this, arguments );\n\t\t\treturn pendingRequests[ port ];\n\t\t}\n\t\treturn ajax.apply( this, arguments );\n\t};\n}\n\n// Abort the previous request without sending a new one\n$.ajaxAbort = function( port ) {\n\tif ( pendingRequests[ port ] ) {\n\t\tpendingRequests[ port ].abort();\n\t\tdelete pendingRequests[ port ];\n\t}\n};\nreturn $;\n}));","/*!\r\n * @copyright Copyright © Kartik Visweswaran, Krajee.com, 2014 - 2025\r\n * @version 1.3.6\r\n *\r\n * Date formatter utility library that allows formatting date/time variables or Date objects using PHP DateTime format.\r\n * This library is a standalone javascript library and does not depend on other libraries or plugins like jQuery. The\r\n * library also adds support for Universal Module Definition (UMD).\r\n *\r\n * @see http://php.net/manual/en/function.date.php\r\n *\r\n * For more JQuery plugins visit http://plugins.krajee.com\r\n * For more Yii related demos visit http://demos.krajee.com\r\n */\r\n(function (root, factory) {\r\n // noinspection JSUnresolvedVariable\r\n if (typeof define === 'function' && define.amd) { // AMD\r\n // noinspection JSUnresolvedFunction\r\n define([], factory);\r\n } else {\r\n // noinspection JSUnresolvedVariable\r\n if (typeof module === 'object' && module.exports) { // Node\r\n // noinspection JSUnresolvedVariable\r\n module.exports = factory();\r\n } else { // Browser globals\r\n root.DateFormatter = factory();\r\n }\r\n }\r\n}(typeof self !== 'undefined' ? self : this, function () {\r\n var DateFormatter, $h;\r\n /**\r\n * Global helper object\r\n */\r\n $h = {\r\n DAY: 1000 * 60 * 60 * 24,\r\n HOUR: 3600,\r\n defaults: {\r\n dateSettings: {\r\n days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],\r\n daysShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],\r\n months: [\r\n 'January', 'February', 'March', 'April', 'May', 'June', 'July',\r\n 'August', 'September', 'October', 'November', 'December'\r\n ],\r\n monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],\r\n meridiem: ['AM', 'PM'],\r\n ordinal: function (number) {\r\n var n = number % 10, suffixes = {1: 'st', 2: 'nd', 3: 'rd'};\r\n return Math.floor(number % 100 / 10) === 1 || !suffixes[n] ? 'th' : suffixes[n];\r\n }\r\n },\r\n separators: /[ \\-+\\/.:@]/g,\r\n validParts: /[dDjlNSwzWFmMntLoYyaABgGhHisueTIOPZcrU]/g,\r\n intParts: /[djwNzmnyYhHgGis]/g,\r\n tzParts: /\\b(?:[PMCEA][SDP]T|(?:Australian|Pacific|Mountain|Central|Eastern|Atlantic) (?:Eastern) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\\d{4})?)\\b/g,\r\n tzClip: /[^-+\\dA-Z]/g\r\n },\r\n getInt: function (str, radix) {\r\n return parseInt(str, (radix ? radix : 10));\r\n },\r\n compare: function (str1, str2) {\r\n return typeof (str1) === 'string' && typeof (str2) === 'string' && str1.toLowerCase() === str2.toLowerCase();\r\n },\r\n lpad: function (value, length, chr) {\r\n var val = value.toString();\r\n chr = chr || '0';\r\n return val.length < length ? $h.lpad(chr + val, length) : val;\r\n },\r\n merge: function (out) {\r\n var i, obj;\r\n out = out || {};\r\n for (i = 1; i < arguments.length; i++) {\r\n obj = arguments[i];\r\n if (!obj) {\r\n continue;\r\n }\r\n for (var key in obj) {\r\n if (obj.hasOwnProperty(key)) {\r\n if (typeof obj[key] === 'object') {\r\n $h.merge(out[key], obj[key]);\r\n } else {\r\n out[key] = obj[key];\r\n }\r\n }\r\n }\r\n }\r\n return out;\r\n },\r\n getIndex: function (val, arr) {\r\n for (var i = 0; i < arr.length; i++) {\r\n if (arr[i].toLowerCase() === val.toLowerCase()) {\r\n return i;\r\n }\r\n }\r\n return -1;\r\n }\r\n };\r\n\r\n /**\r\n * Date Formatter Library Constructor\r\n * @param options\r\n * @constructor\r\n */\r\n DateFormatter = function (options) {\r\n var self = this, config = $h.merge($h.defaults, options);\r\n self.dateSettings = config.dateSettings;\r\n self.separators = config.separators;\r\n self.validParts = config.validParts;\r\n self.intParts = config.intParts;\r\n self.tzParts = config.tzParts;\r\n self.tzClip = config.tzClip;\r\n };\r\n\r\n /**\r\n * DateFormatter Library Prototype\r\n */\r\n DateFormatter.prototype = {\r\n constructor: DateFormatter,\r\n getMonth: function (val) {\r\n var self = this, i;\r\n i = $h.getIndex(val, self.dateSettings.monthsShort) + 1;\r\n if (i === 0) {\r\n i = $h.getIndex(val, self.dateSettings.months) + 1;\r\n }\r\n return i;\r\n },\r\n parseDate: function (vDate, vFormat) {\r\n var self = this, vFormatParts, vDateParts, i, vDateFlag = false, vTimeFlag = false, vDatePart, iDatePart,\r\n vSettings = self.dateSettings, vMonth, vMeriIndex, vMeriOffset, len, mer,\r\n out = {date: null, year: null, month: null, day: null, hour: 0, min: 0, sec: 0};\r\n if (!vDate) {\r\n return null;\r\n }\r\n if (vDate instanceof Date) {\r\n return vDate;\r\n }\r\n if (vFormat === 'U') {\r\n i = $h.getInt(vDate);\r\n return i ? new Date(i * 1000) : vDate;\r\n }\r\n switch (typeof vDate) {\r\n case 'number':\r\n return new Date(vDate);\r\n case 'string':\r\n break;\r\n default:\r\n return null;\r\n }\r\n vFormatParts = vFormat.match(self.validParts);\r\n if (!vFormatParts || vFormatParts.length === 0) {\r\n throw new Error('Invalid date format definition.');\r\n }\r\n for (i = vFormatParts.length - 1; i >= 0; i--) {\r\n if (vFormatParts[i] === 'S') {\r\n vFormatParts.splice(i, 1);\r\n }\r\n }\r\n vDateParts = vDate.replace(self.separators, '\\0').split('\\0');\r\n for (i = 0; i < vDateParts.length; i++) {\r\n vDatePart = vDateParts[i];\r\n iDatePart = $h.getInt(vDatePart);\r\n switch (vFormatParts[i]) {\r\n case 'y':\r\n case 'Y':\r\n if (iDatePart) {\r\n len = vDatePart.length;\r\n out.year = len === 2 ? $h.getInt((iDatePart < 70 ? '20' : '19') + vDatePart) : iDatePart;\r\n } else {\r\n return null;\r\n }\r\n vDateFlag = true;\r\n break;\r\n case 'm':\r\n case 'n':\r\n case 'M':\r\n case 'F':\r\n if (isNaN(iDatePart)) {\r\n vMonth = self.getMonth(vDatePart);\r\n if (vMonth > 0) {\r\n out.month = vMonth;\r\n } else {\r\n return null;\r\n }\r\n } else {\r\n if (iDatePart >= 1 && iDatePart <= 12) {\r\n out.month = iDatePart;\r\n } else {\r\n return null;\r\n }\r\n }\r\n vDateFlag = true;\r\n break;\r\n case 'd':\r\n case 'j':\r\n if (iDatePart >= 1 && iDatePart <= 31) {\r\n out.day = iDatePart;\r\n } else {\r\n return null;\r\n }\r\n vDateFlag = true;\r\n break;\r\n case 'g':\r\n case 'h':\r\n vMeriIndex = (vFormatParts.indexOf('a') > -1) ? vFormatParts.indexOf('a') :\r\n ((vFormatParts.indexOf('A') > -1) ? vFormatParts.indexOf('A') : -1);\r\n mer = vDateParts[vMeriIndex];\r\n if (vMeriIndex !== -1) {\r\n vMeriOffset = $h.compare(mer, vSettings.meridiem[0]) ? 0 :\r\n ($h.compare(mer, vSettings.meridiem[1]) ? 12 : -1);\r\n if (iDatePart >= 1 && iDatePart <= 12 && vMeriOffset !== -1) {\r\n out.hour = iDatePart % 12 === 0 ? vMeriOffset : iDatePart + vMeriOffset;\r\n } else {\r\n if (iDatePart >= 0 && iDatePart <= 23) {\r\n out.hour = iDatePart;\r\n }\r\n }\r\n } else {\r\n if (iDatePart >= 0 && iDatePart <= 23) {\r\n out.hour = iDatePart;\r\n } else {\r\n return null;\r\n }\r\n }\r\n vTimeFlag = true;\r\n break;\r\n case 'G':\r\n case 'H':\r\n if (iDatePart >= 0 && iDatePart <= 23) {\r\n out.hour = iDatePart;\r\n } else {\r\n return null;\r\n }\r\n vTimeFlag = true;\r\n break;\r\n case 'i':\r\n if (iDatePart >= 0 && iDatePart <= 59) {\r\n out.min = iDatePart;\r\n } else {\r\n return null;\r\n }\r\n vTimeFlag = true;\r\n break;\r\n case 's':\r\n if (iDatePart >= 0 && iDatePart <= 59) {\r\n out.sec = iDatePart;\r\n } else {\r\n return null;\r\n }\r\n vTimeFlag = true;\r\n break;\r\n }\r\n }\r\n if (vDateFlag === true) {\r\n var varY = out.year || 0, varM = out.month ? out.month - 1 : 0, varD = out.day || 1;\r\n out.date = new Date(varY, varM, varD, out.hour, out.min, out.sec, 0);\r\n } else {\r\n if (vTimeFlag !== true) {\r\n return null;\r\n }\r\n out.date = new Date(0, 0, 0, out.hour, out.min, out.sec, 0);\r\n }\r\n return out.date;\r\n },\r\n guessDate: function (vDateStr, vFormat) {\r\n if (typeof vDateStr !== 'string') {\r\n return vDateStr;\r\n }\r\n var self = this, vParts = vDateStr.replace(self.separators, '\\0').split('\\0'), vPattern = /^[djmn]/g, len,\r\n vFormatParts = vFormat.match(self.validParts), vDate = new Date(), vDigit = 0, vYear, i, n, iPart, iSec;\r\n\r\n if (!vPattern.test(vFormatParts[0])) {\r\n return vDateStr;\r\n }\r\n\r\n for (i = 0; i < vParts.length; i++) {\r\n vDigit = 2;\r\n iPart = vParts[i];\r\n iSec = $h.getInt(iPart.substr(0, 2));\r\n if (isNaN(iSec)) {\r\n return null;\r\n }\r\n switch (i) {\r\n case 0:\r\n if (vFormatParts[0] === 'm' || vFormatParts[0] === 'n') {\r\n vDate.setMonth(iSec - 1);\r\n } else {\r\n vDate.setDate(iSec);\r\n }\r\n break;\r\n case 1:\r\n if (vFormatParts[0] === 'm' || vFormatParts[0] === 'n') {\r\n vDate.setDate(iSec);\r\n } else {\r\n vDate.setMonth(iSec - 1);\r\n }\r\n break;\r\n case 2:\r\n vYear = vDate.getFullYear();\r\n len = iPart.length;\r\n vDigit = len < 4 ? len : 4;\r\n vYear = $h.getInt(len < 4 ? vYear.toString().substr(0, 4 - len) + iPart : iPart.substr(0, 4));\r\n if (!vYear) {\r\n return null;\r\n }\r\n vDate.setFullYear(vYear);\r\n break;\r\n case 3:\r\n vDate.setHours(iSec);\r\n break;\r\n case 4:\r\n vDate.setMinutes(iSec);\r\n break;\r\n case 5:\r\n vDate.setSeconds(iSec);\r\n break;\r\n }\r\n n = iPart.substr(vDigit);\r\n if (n.length > 0) {\r\n vParts.splice(i + 1, 0, n);\r\n }\r\n }\r\n return vDate;\r\n },\r\n parseFormat: function (vChar, vDate) {\r\n var self = this, vSettings = self.dateSettings, fmt, backslash = /\\\\?(.?)/gi, doFormat = function (t, s) {\r\n return fmt[t] ? fmt[t]() : s;\r\n };\r\n fmt = {\r\n /////////\r\n // DAY //\r\n /////////\r\n /**\r\n * Day of month with leading 0: `01..31`\r\n * @return {string}\r\n */\r\n d: function () {\r\n return $h.lpad(fmt.j(), 2);\r\n },\r\n /**\r\n * Shorthand day name: `Mon...Sun`\r\n * @return {string}\r\n */\r\n D: function () {\r\n return vSettings.daysShort[fmt.w()];\r\n },\r\n /**\r\n * Day of month: `1..31`\r\n * @return {number}\r\n */\r\n j: function () {\r\n return vDate.getDate();\r\n },\r\n /**\r\n * Full day name: `Monday...Sunday`\r\n * @return {string}\r\n */\r\n l: function () {\r\n return vSettings.days[fmt.w()];\r\n },\r\n /**\r\n * ISO-8601 day of week: `1[Mon]..7[Sun]`\r\n * @return {number}\r\n */\r\n N: function () {\r\n return fmt.w() || 7;\r\n },\r\n /**\r\n * Day of week: `0[Sun]..6[Sat]`\r\n * @return {number}\r\n */\r\n w: function () {\r\n return vDate.getDay();\r\n },\r\n /**\r\n * Day of year: `0..365`\r\n * @return {number}\r\n */\r\n z: function () {\r\n var a = new Date(fmt.Y(), fmt.n() - 1, fmt.j()), b = new Date(fmt.Y(), 0, 1);\r\n return Math.round((a - b) / $h.DAY);\r\n },\r\n\r\n //////////\r\n // WEEK //\r\n //////////\r\n /**\r\n * ISO-8601 week number\r\n * @return {number}\r\n */\r\n W: function () {\r\n var a = new Date(fmt.Y(), fmt.n() - 1, fmt.j() - fmt.N() + 3), b = new Date(a.getFullYear(), 0, 4);\r\n return $h.lpad(1 + Math.round((a - b) / $h.DAY / 7), 2);\r\n },\r\n\r\n ///////////\r\n // MONTH //\r\n ///////////\r\n /**\r\n * Full month name: `January...December`\r\n * @return {string}\r\n */\r\n F: function () {\r\n return vSettings.months[vDate.getMonth()];\r\n },\r\n /**\r\n * Month w/leading 0: `01..12`\r\n * @return {string}\r\n */\r\n m: function () {\r\n return $h.lpad(fmt.n(), 2);\r\n },\r\n /**\r\n * Shorthand month name; `Jan...Dec`\r\n * @return {string}\r\n */\r\n M: function () {\r\n return vSettings.monthsShort[vDate.getMonth()];\r\n },\r\n /**\r\n * Month: `1...12`\r\n * @return {number}\r\n */\r\n n: function () {\r\n return vDate.getMonth() + 1;\r\n },\r\n /**\r\n * Days in month: `28...31`\r\n * @return {number}\r\n */\r\n t: function () {\r\n return (new Date(fmt.Y(), fmt.n(), 0)).getDate();\r\n },\r\n\r\n //////////\r\n // YEAR //\r\n //////////\r\n /**\r\n * Is leap year? `0 or 1`\r\n * @return {number}\r\n */\r\n L: function () {\r\n var Y = fmt.Y();\r\n return (Y % 4 === 0 && Y % 100 !== 0 || Y % 400 === 0) ? 1 : 0;\r\n },\r\n /**\r\n * ISO-8601 year\r\n * @return {number}\r\n */\r\n o: function () {\r\n var n = fmt.n(), W = fmt.W(), Y = fmt.Y();\r\n return Y + (n === 12 && W < 9 ? 1 : n === 1 && W > 9 ? -1 : 0);\r\n },\r\n /**\r\n * Full year: `e.g. 1980...2010`\r\n * @return {number}\r\n */\r\n Y: function () {\r\n return vDate.getFullYear();\r\n },\r\n /**\r\n * Last two digits of year: `00...99`\r\n * @return {string}\r\n */\r\n y: function () {\r\n return fmt.Y().toString().slice(-2);\r\n },\r\n\r\n //////////\r\n // TIME //\r\n //////////\r\n /**\r\n * Meridian lower: `am or pm`\r\n * @return {string}\r\n */\r\n a: function () {\r\n return fmt.A().toLowerCase();\r\n },\r\n /**\r\n * Meridian upper: `AM or PM`\r\n * @return {string}\r\n */\r\n A: function () {\r\n var n = fmt.G() < 12 ? 0 : 1;\r\n return vSettings.meridiem[n];\r\n },\r\n /**\r\n * Swatch Internet time: `000..999`\r\n * @return {string}\r\n */\r\n B: function () {\r\n var H = vDate.getUTCHours() * $h.HOUR, i = vDate.getUTCMinutes() * 60, s = vDate.getUTCSeconds();\r\n return $h.lpad(Math.floor((H + i + s + $h.HOUR) / 86.4) % 1000, 3);\r\n },\r\n /**\r\n * 12-Hours: `1..12`\r\n * @return {number}\r\n */\r\n g: function () {\r\n return fmt.G() % 12 || 12;\r\n },\r\n /**\r\n * 24-Hours: `0..23`\r\n * @return {number}\r\n */\r\n G: function () {\r\n return vDate.getHours();\r\n },\r\n /**\r\n * 12-Hours with leading 0: `01..12`\r\n * @return {string}\r\n */\r\n h: function () {\r\n return $h.lpad(fmt.g(), 2);\r\n },\r\n /**\r\n * 24-Hours w/leading 0: `00..23`\r\n * @return {string}\r\n */\r\n H: function () {\r\n return $h.lpad(fmt.G(), 2);\r\n },\r\n /**\r\n * Minutes w/leading 0: `00..59`\r\n * @return {string}\r\n */\r\n i: function () {\r\n return $h.lpad(vDate.getMinutes(), 2);\r\n },\r\n /**\r\n * Seconds w/leading 0: `00..59`\r\n * @return {string}\r\n */\r\n s: function () {\r\n return $h.lpad(vDate.getSeconds(), 2);\r\n },\r\n /**\r\n * Microseconds: `000000-999000`\r\n * @return {string}\r\n */\r\n u: function () {\r\n return $h.lpad(vDate.getMilliseconds() * 1000, 6);\r\n },\r\n\r\n //////////////\r\n // TIMEZONE //\r\n //////////////\r\n /**\r\n * Timezone identifier: `e.g. Atlantic/Azores, ...`\r\n * @return {string}\r\n */\r\n e: function () {\r\n var str = /\\((.*)\\)/.exec(String(vDate))[1];\r\n return str || 'Coordinated Universal Time';\r\n },\r\n /**\r\n * DST observed? `0 or 1`\r\n * @return {number}\r\n */\r\n I: function () {\r\n var a = new Date(fmt.Y(), 0), c = Date.UTC(fmt.Y(), 0),\r\n b = new Date(fmt.Y(), 6), d = Date.UTC(fmt.Y(), 6);\r\n return ((a - c) !== (b - d)) ? 1 : 0;\r\n },\r\n /**\r\n * Difference to GMT in hour format: `e.g. +0200`\r\n * @return {string}\r\n */\r\n O: function () {\r\n var tzo = vDate.getTimezoneOffset(), a = Math.abs(tzo);\r\n return (tzo > 0 ? '-' : '+') + $h.lpad(Math.floor(a / 60) * 100 + a % 60, 4);\r\n },\r\n /**\r\n * Difference to GMT with colon: `e.g. +02:00`\r\n * @return {string}\r\n */\r\n P: function () {\r\n var O = fmt.O();\r\n return (O.substr(0, 3) + ':' + O.substr(3, 2));\r\n },\r\n /**\r\n * Timezone abbreviation: `e.g. EST, MDT, ...`\r\n * @return {string}\r\n */\r\n T: function () {\r\n var str = (String(vDate).match(self.tzParts) || ['']).pop().replace(self.tzClip, '');\r\n return str || 'UTC';\r\n },\r\n /**\r\n * Timezone offset in seconds: `-43200...50400`\r\n * @return {number}\r\n */\r\n Z: function () {\r\n return -vDate.getTimezoneOffset() * 60;\r\n },\r\n\r\n ////////////////////\r\n // FULL DATE TIME //\r\n ////////////////////\r\n /**\r\n * ISO-8601 date\r\n * @return {string}\r\n */\r\n c: function () {\r\n return 'Y-m-d\\\\TH:i:sP'.replace(backslash, doFormat);\r\n },\r\n /**\r\n * RFC 2822 date\r\n * @return {string}\r\n */\r\n r: function () {\r\n return 'D, d M Y H:i:s O'.replace(backslash, doFormat);\r\n },\r\n /**\r\n * Seconds since UNIX epoch\r\n * @return {number}\r\n */\r\n U: function () {\r\n return vDate.getTime() / 1000 || 0;\r\n }\r\n };\r\n return doFormat(vChar, vChar);\r\n },\r\n formatDate: function (vDate, vFormat) {\r\n var self = this, i, n, len, str, vChar, vDateStr = '', BACKSLASH = '\\\\';\r\n if (typeof vDate === 'string') {\r\n vDate = self.parseDate(vDate, vFormat);\r\n if (!vDate) {\r\n return null;\r\n }\r\n }\r\n if (vDate instanceof Date) {\r\n len = vFormat.length;\r\n for (i = 0; i < len; i++) {\r\n vChar = vFormat.charAt(i);\r\n if (vChar === 'S' || vChar === BACKSLASH) {\r\n continue;\r\n }\r\n if (i > 0 && vFormat.charAt(i - 1) === BACKSLASH) {\r\n vDateStr += vChar;\r\n continue;\r\n }\r\n str = self.parseFormat(vChar, vDate);\r\n if (i !== (len - 1) && self.intParts.test(vChar) && vFormat.charAt(i + 1) === 'S') {\r\n n = $h.getInt(str) || 0;\r\n str += self.dateSettings.ordinal(n);\r\n }\r\n vDateStr += str;\r\n }\r\n return vDateStr;\r\n }\r\n return '';\r\n }\r\n };\r\n\tObject.freeze(DateFormatter);\r\n return DateFormatter;\r\n}));\r\n","/*!\n * Laravel Javascript Validation\n *\n * https://github.com/proengsoft/laravel-jsvalidation\n *\n * Copyright (c) 2017 Proengsoft\n * Released under the MIT license\n */\n\nvar laravelValidation;\nlaravelValidation = {\n\n implicitRules: ['Required','Confirmed'],\n\n /**\n * Initialize laravel validations.\n */\n init: function () {\n\n // jquery-validation requires the field under validation to be present. We're adding a dummy hidden\n // field so that any errors are not visible.\n var constructor = $.fn.validate;\n $.fn.validate = function( options ) {\n var name = 'proengsoft_jsvalidation'; // must match the name defined in JsValidatorFactory.newFormRequestValidator\n var $elm = $(this).find('input[name=\"' + name + '\"]');\n if ($elm.length === 0) {\n $('').attr({type: 'hidden', name: name}).appendTo(this);\n }\n\n return constructor.apply(this, [options]);\n };\n\n // Disable class rules and attribute rules\n $.validator.classRuleSettings = {};\n $.validator.attributeRules = function () {};\n\n $.validator.dataRules = this.arrayRules;\n $.validator.prototype.arrayRulesCache = {};\n\n // Register validations methods\n this.setupValidations();\n },\n\n arrayRules: function(element) {\n\n var rules = {},\n validator = $.data( element.form, \"validator\"),\n cache = validator.arrayRulesCache;\n\n // Is not an Array\n if (element.name.indexOf('[') === -1) {\n return rules;\n }\n\n if (! (element.name in cache)) {\n cache[element.name] = {};\n }\n\n $.each(validator.settings.rules, function(name, tmpRules) {\n if (name in cache[element.name]) {\n rules = laravelValidation.helpers.mergeRules(rules, cache[element.name][name]);\n } else {\n cache[element.name][name] = {};\n\n var nameRegExp = laravelValidation.helpers.regexFromWildcard(name);\n if (element.name.match(nameRegExp)) {\n var newRules = $.validator.normalizeRule(tmpRules) || {};\n cache[element.name][name] = newRules;\n\n rules = laravelValidation.helpers.mergeRules(rules, newRules);\n }\n }\n });\n\n return rules;\n },\n\n setupValidations: function () {\n\n /**\n * Get CSRF token.\n *\n * @param params\n * @returns {string}\n */\n var getCsrfToken = function (params) {\n return params[0][1][1];\n };\n\n /**\n * Whether to validate all attributes.\n *\n * @param params\n * @returns {boolean}\n */\n var isValidateAll = function (params) {\n return params[0][1][2];\n };\n\n /**\n * Determine whether the rule is implicit.\n *\n * @param params\n * @returns {boolean}\n */\n var isImplicit = function (params) {\n var implicit = false;\n $.each(params, function (i, parameters) {\n implicit = implicit || parameters[3];\n });\n\n return implicit;\n };\n\n /**\n * Get form method from a validator instance.\n *\n * @param validator\n * @returns {string}\n */\n var formMethod = function (validator) {\n var formMethod = $(validator.currentForm).attr('method');\n if ($(validator.currentForm).find('input[name=\"_method\"]').length) {\n formMethod = $(validator.currentForm).find('input[name=\"_method\"]').val();\n }\n\n return formMethod;\n };\n\n /**\n * Get AJAX parameters for remote requests.\n *\n * @param validator\n * @param element\n * @param params\n * @param data\n * @returns {object}\n */\n var ajaxOpts = function (validator, element, params, data) {\n return {\n mode: 'abort',\n port: 'validate' + element.name,\n dataType: 'json',\n data: data,\n context: validator.currentForm,\n url: $(validator.currentForm).attr('action'),\n type: formMethod(validator),\n beforeSend: function (xhr) {\n var token = getCsrfToken(params);\n if (formMethod(validator) !== 'get' && token) {\n return xhr.setRequestHeader('X-XSRF-TOKEN', token);\n }\n },\n };\n };\n\n /**\n * Validate a set of local JS based rules against an element.\n *\n * @param validator\n * @param values\n * @param element\n * @param rules\n * @returns {boolean}\n */\n var validateLocalRules = function (validator, values, element, rules) {\n var validated = true,\n previous = validator.previousValue(element);\n\n $.each(rules, function (i, param) {\n var implicit = param[3] || laravelValidation.implicitRules.indexOf(param[0]) !== -1;\n var rule = param[0];\n var message = param[2];\n\n if (! implicit && validator.optional(element)) {\n validated = \"dependency-mismatch\";\n return false;\n }\n\n if (laravelValidation.methods[rule] !== undefined) {\n $.each(values, function(index, value) {\n validated = laravelValidation.methods[rule].call(validator, value, element, param[1], function(valid) {\n validator.settings.messages[element.name].laravelValidationRemote = previous.originalMessage;\n if (valid) {\n var submitted = validator.formSubmitted;\n validator.prepareElement(element);\n validator.formSubmitted = submitted;\n validator.successList.push(element);\n delete validator.invalid[element.name];\n validator.showErrors();\n } else {\n var errors = {};\n errors[ element.name ]\n = previous.message\n = typeof message === \"function\" ? message( value ) : message;\n validator.invalid[element.name] = true;\n validator.showErrors(errors);\n }\n validator.showErrors(validator.errorMap);\n previous.valid = valid;\n });\n\n // Break loop.\n if (validated === false) {\n return false;\n }\n });\n } else {\n validated = false;\n }\n\n if (validated !== true) {\n if (!validator.settings.messages[element.name] ) {\n validator.settings.messages[element.name] = {};\n }\n\n validator.settings.messages[element.name].laravelValidation= message;\n\n return false;\n }\n\n });\n\n return validated;\n };\n\n /**\n * Create JQueryValidation check to validate Laravel rules.\n */\n\n $.validator.addMethod(\"laravelValidation\", function (value, element, params) {\n var rules = [],\n arrayRules = [];\n $.each(params, function (i, param) {\n // put Implicit rules in front\n var isArrayRule = param[4].indexOf('[') !== -1;\n if (param[3] || laravelValidation.implicitRules.indexOf(param[0]) !== -1) {\n isArrayRule ? arrayRules.unshift(param) : rules.unshift(param);\n } else {\n isArrayRule ? arrayRules.push(param) : rules.push(param);\n }\n });\n\n // Validate normal rules.\n var localRulesResult = validateLocalRules(this, [value], element, rules);\n\n // Validate items of the array using array rules.\n var arrayValue = ! Array.isArray(value) ? [value] : value;\n var arrayRulesResult = validateLocalRules(this, arrayValue, element, arrayRules);\n\n return localRulesResult && arrayRulesResult;\n }, '');\n\n\n /**\n * Create JQueryValidation check to validate Remote Laravel rules.\n */\n $.validator.addMethod(\"laravelValidationRemote\", function (value, element, params) {\n\n if (! isImplicit(params) && this.optional( element )) {\n return \"dependency-mismatch\";\n }\n\n var previous = this.previousValue( element ),\n validator, data;\n\n if (! this.settings.messages[ element.name ]) {\n this.settings.messages[ element.name ] = {};\n }\n previous.originalMessage = this.settings.messages[ element.name ].laravelValidationRemote;\n this.settings.messages[ element.name ].laravelValidationRemote = previous.message;\n\n if (laravelValidation.helpers.arrayEquals(previous.old, value) || previous.old === value) {\n return previous.valid;\n }\n\n previous.old = value;\n validator = this;\n this.startRequest( element );\n\n data = $(validator.currentForm).serializeArray();\n data.push({'name': '_jsvalidation', 'value': element.name});\n data.push({'name': '_jsvalidation_validate_all', 'value': isValidateAll(params)});\n\n $.ajax( ajaxOpts(validator, element, params, data) )\n .always(function( response, textStatus ) {\n var errors, message, submitted, valid;\n\n if (textStatus === 'error') {\n valid = false;\n response = laravelValidation.helpers.parseErrorResponse(response);\n } else if (textStatus === 'success') {\n valid = response === true || response === \"true\";\n } else {\n return;\n }\n\n validator.settings.messages[ element.name ].laravelValidationRemote = previous.originalMessage;\n\n if ( valid ) {\n submitted = validator.formSubmitted;\n validator.prepareElement( element );\n validator.formSubmitted = submitted;\n validator.successList.push( element );\n delete validator.invalid[ element.name ];\n validator.showErrors();\n } else {\n errors = {};\n message = response || validator.defaultMessage( element, \"remote\" );\n errors[ element.name ]\n = previous.message\n = typeof message === \"function\" ? message( value ) : message[0];\n validator.invalid[ element.name ] = true;\n validator.showErrors( errors );\n }\n validator.showErrors(validator.errorMap);\n previous.valid = valid;\n validator.stopRequest( element, valid );\n }\n );\n return \"pending\";\n }, '');\n\n /**\n * Create JQueryValidation check to form requests.\n */\n $.validator.addMethod(\"laravelValidationFormRequest\", function (value, element, params) {\n\n var validator = this,\n previous = validator.previousValue(element);\n\n var data = $(validator.currentForm).serializeArray();\n data.push({name: '__proengsoft_form_request', value: 1}); // must match FormRequest.JS_VALIDATION_FIELD\n\n // Skip AJAX if the value remains the same as a prior request.\n if (JSON.stringify(previous.old) === JSON.stringify(data)) {\n if (! previous.valid) {\n validator.showErrors(previous.errors || {});\n }\n\n return previous.valid;\n }\n\n previous.old = data;\n this.startRequest( element );\n\n $.ajax(ajaxOpts(validator, element, params, data))\n .always(function( response, textStatus ) {\n var errors = {},\n valid = textStatus === 'success' && (response === true || response === 'true');\n\n if (valid) {\n validator.resetInternals();\n validator.toHide = validator.errorsFor( element );\n } else {\n $.each( response, function( fieldName, errorMessages ) {\n var errorElement = laravelValidation.helpers.findByName(validator, fieldName)[0];\n if (errorElement) {\n errors[errorElement.name] = laravelValidation.helpers.encode(errorMessages[0] || '');\n }\n });\n\n // Failed to find the error fields so mark the form as valid otherwise user\n // will be left in limbo with no visible error messages.\n if ($.isEmptyObject(errors)) {\n valid = true;\n }\n }\n\n previous.valid = valid;\n previous.errors = errors;\n validator.showErrors(errors);\n validator.stopRequest(element, valid);\n });\n\n return 'pending';\n }, '');\n }\n};\n\n$(function() {\n laravelValidation.init();\n});\n","/******/ (function() { // webpackBootstrap\n/******/ \t\"use strict\";\n/******/ \tvar __webpack_modules__ = ({\n\n/***/ \"./node_modules/locutus/php/array/array_diff.js\":\n/*!******************************************************!*\\\n !*** ./node_modules/locutus/php/array/array_diff.js ***!\n \\******************************************************/\n/***/ (function(module) {\n\n\n\nmodule.exports = function array_diff(arr1) {\n // discuss at: https://locutus.io/php/array_diff/\n // original by: Kevin van Zonneveld (https://kvz.io)\n // improved by: Sanjoy Roy\n // revised by: Brett Zamir (https://brett-zamir.me)\n // example 1: array_diff(['Kevin', 'van', 'Zonneveld'], ['van', 'Zonneveld'])\n // returns 1: {0:'Kevin'}\n\n var retArr = {};\n var argl = arguments.length;\n var k1 = '';\n var i = 1;\n var k = '';\n var arr = {};\n\n arr1keys: for (k1 in arr1) {\n for (i = 1; i < argl; i++) {\n arr = arguments[i];\n for (k in arr) {\n if (arr[k] === arr1[k1]) {\n // If it reaches here, it was found in at least one array, so try next value\n continue arr1keys; // eslint-disable-line no-labels\n }\n }\n retArr[k1] = arr1[k1];\n }\n }\n\n return retArr;\n};\n//# sourceMappingURL=array_diff.js.map\n\n/***/ }),\n\n/***/ \"./node_modules/locutus/php/datetime/strtotime.js\":\n/*!********************************************************!*\\\n !*** ./node_modules/locutus/php/datetime/strtotime.js ***!\n \\********************************************************/\n/***/ (function(module) {\n\n\n\nvar reSpace = '[ \\\\t]+';\nvar reSpaceOpt = '[ \\\\t]*';\nvar reMeridian = '(?:([ap])\\\\.?m\\\\.?([\\\\t ]|$))';\nvar reHour24 = '(2[0-4]|[01]?[0-9])';\nvar reHour24lz = '([01][0-9]|2[0-4])';\nvar reHour12 = '(0?[1-9]|1[0-2])';\nvar reMinute = '([0-5]?[0-9])';\nvar reMinutelz = '([0-5][0-9])';\nvar reSecond = '(60|[0-5]?[0-9])';\nvar reSecondlz = '(60|[0-5][0-9])';\nvar reFrac = '(?:\\\\.([0-9]+))';\n\nvar reDayfull = 'sunday|monday|tuesday|wednesday|thursday|friday|saturday';\nvar reDayabbr = 'sun|mon|tue|wed|thu|fri|sat';\nvar reDaytext = reDayfull + '|' + reDayabbr + '|weekdays?';\n\nvar reReltextnumber = 'first|second|third|fourth|fifth|sixth|seventh|eighth?|ninth|tenth|eleventh|twelfth';\nvar reReltexttext = 'next|last|previous|this';\nvar reReltextunit = '(?:second|sec|minute|min|hour|day|fortnight|forthnight|month|year)s?|weeks|' + reDaytext;\n\nvar reYear = '([0-9]{1,4})';\nvar reYear2 = '([0-9]{2})';\nvar reYear4 = '([0-9]{4})';\nvar reYear4withSign = '([+-]?[0-9]{4})';\nvar reMonth = '(1[0-2]|0?[0-9])';\nvar reMonthlz = '(0[0-9]|1[0-2])';\nvar reDay = '(?:(3[01]|[0-2]?[0-9])(?:st|nd|rd|th)?)';\nvar reDaylz = '(0[0-9]|[1-2][0-9]|3[01])';\n\nvar reMonthFull = 'january|february|march|april|may|june|july|august|september|october|november|december';\nvar reMonthAbbr = 'jan|feb|mar|apr|may|jun|jul|aug|sept?|oct|nov|dec';\nvar reMonthroman = 'i[vx]|vi{0,3}|xi{0,2}|i{1,3}';\nvar reMonthText = '(' + reMonthFull + '|' + reMonthAbbr + '|' + reMonthroman + ')';\n\nvar reTzCorrection = '((?:GMT)?([+-])' + reHour24 + ':?' + reMinute + '?)';\nvar reTzAbbr = '\\\\(?([a-zA-Z]{1,6})\\\\)?';\nvar reDayOfYear = '(00[1-9]|0[1-9][0-9]|[12][0-9][0-9]|3[0-5][0-9]|36[0-6])';\nvar reWeekOfYear = '(0[1-9]|[1-4][0-9]|5[0-3])';\n\nvar reDateNoYear = reMonthText + '[ .\\\\t-]*' + reDay + '[,.stndrh\\\\t ]*';\n\nfunction processMeridian(hour, meridian) {\n meridian = meridian && meridian.toLowerCase();\n\n switch (meridian) {\n case 'a':\n hour += hour === 12 ? -12 : 0;\n break;\n case 'p':\n hour += hour !== 12 ? 12 : 0;\n break;\n }\n\n return hour;\n}\n\nfunction processYear(yearStr) {\n var year = +yearStr;\n\n if (yearStr.length < 4 && year < 100) {\n year += year < 70 ? 2000 : 1900;\n }\n\n return year;\n}\n\nfunction lookupMonth(monthStr) {\n return {\n jan: 0,\n january: 0,\n i: 0,\n feb: 1,\n february: 1,\n ii: 1,\n mar: 2,\n march: 2,\n iii: 2,\n apr: 3,\n april: 3,\n iv: 3,\n may: 4,\n v: 4,\n jun: 5,\n june: 5,\n vi: 5,\n jul: 6,\n july: 6,\n vii: 6,\n aug: 7,\n august: 7,\n viii: 7,\n sep: 8,\n sept: 8,\n september: 8,\n ix: 8,\n oct: 9,\n october: 9,\n x: 9,\n nov: 10,\n november: 10,\n xi: 10,\n dec: 11,\n december: 11,\n xii: 11\n }[monthStr.toLowerCase()];\n}\n\nfunction lookupWeekday(dayStr) {\n var desiredSundayNumber = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;\n\n var dayNumbers = {\n mon: 1,\n monday: 1,\n tue: 2,\n tuesday: 2,\n wed: 3,\n wednesday: 3,\n thu: 4,\n thursday: 4,\n fri: 5,\n friday: 5,\n sat: 6,\n saturday: 6,\n sun: 0,\n sunday: 0\n };\n\n return dayNumbers[dayStr.toLowerCase()] || desiredSundayNumber;\n}\n\nfunction lookupRelative(relText) {\n var relativeNumbers = {\n last: -1,\n previous: -1,\n this: 0,\n first: 1,\n next: 1,\n second: 2,\n third: 3,\n fourth: 4,\n fifth: 5,\n sixth: 6,\n seventh: 7,\n eight: 8,\n eighth: 8,\n ninth: 9,\n tenth: 10,\n eleventh: 11,\n twelfth: 12\n };\n\n var relativeBehavior = {\n this: 1\n };\n\n var relTextLower = relText.toLowerCase();\n\n return {\n amount: relativeNumbers[relTextLower],\n behavior: relativeBehavior[relTextLower] || 0\n };\n}\n\nfunction processTzCorrection(tzOffset, oldValue) {\n var reTzCorrectionLoose = /(?:GMT)?([+-])(\\d+)(:?)(\\d{0,2})/i;\n tzOffset = tzOffset && tzOffset.match(reTzCorrectionLoose);\n\n if (!tzOffset) {\n return oldValue;\n }\n\n var sign = tzOffset[1] === '-' ? -1 : 1;\n var hours = +tzOffset[2];\n var minutes = +tzOffset[4];\n\n if (!tzOffset[4] && !tzOffset[3]) {\n minutes = Math.floor(hours % 100);\n hours = Math.floor(hours / 100);\n }\n\n // timezone offset in seconds\n return sign * (hours * 60 + minutes) * 60;\n}\n\n// tz abbrevation : tz offset in seconds\nvar tzAbbrOffsets = {\n acdt: 37800,\n acst: 34200,\n addt: -7200,\n adt: -10800,\n aedt: 39600,\n aest: 36000,\n ahdt: -32400,\n ahst: -36000,\n akdt: -28800,\n akst: -32400,\n amt: -13840,\n apt: -10800,\n ast: -14400,\n awdt: 32400,\n awst: 28800,\n awt: -10800,\n bdst: 7200,\n bdt: -36000,\n bmt: -14309,\n bst: 3600,\n cast: 34200,\n cat: 7200,\n cddt: -14400,\n cdt: -18000,\n cemt: 10800,\n cest: 7200,\n cet: 3600,\n cmt: -15408,\n cpt: -18000,\n cst: -21600,\n cwt: -18000,\n chst: 36000,\n dmt: -1521,\n eat: 10800,\n eddt: -10800,\n edt: -14400,\n eest: 10800,\n eet: 7200,\n emt: -26248,\n ept: -14400,\n est: -18000,\n ewt: -14400,\n ffmt: -14660,\n fmt: -4056,\n gdt: 39600,\n gmt: 0,\n gst: 36000,\n hdt: -34200,\n hkst: 32400,\n hkt: 28800,\n hmt: -19776,\n hpt: -34200,\n hst: -36000,\n hwt: -34200,\n iddt: 14400,\n idt: 10800,\n imt: 25025,\n ist: 7200,\n jdt: 36000,\n jmt: 8440,\n jst: 32400,\n kdt: 36000,\n kmt: 5736,\n kst: 30600,\n lst: 9394,\n mddt: -18000,\n mdst: 16279,\n mdt: -21600,\n mest: 7200,\n met: 3600,\n mmt: 9017,\n mpt: -21600,\n msd: 14400,\n msk: 10800,\n mst: -25200,\n mwt: -21600,\n nddt: -5400,\n ndt: -9052,\n npt: -9000,\n nst: -12600,\n nwt: -9000,\n nzdt: 46800,\n nzmt: 41400,\n nzst: 43200,\n pddt: -21600,\n pdt: -25200,\n pkst: 21600,\n pkt: 18000,\n plmt: 25590,\n pmt: -13236,\n ppmt: -17340,\n ppt: -25200,\n pst: -28800,\n pwt: -25200,\n qmt: -18840,\n rmt: 5794,\n sast: 7200,\n sdmt: -16800,\n sjmt: -20173,\n smt: -13884,\n sst: -39600,\n tbmt: 10751,\n tmt: 12344,\n uct: 0,\n utc: 0,\n wast: 7200,\n wat: 3600,\n wemt: 7200,\n west: 3600,\n wet: 0,\n wib: 25200,\n wita: 28800,\n wit: 32400,\n wmt: 5040,\n yddt: -25200,\n ydt: -28800,\n ypt: -28800,\n yst: -32400,\n ywt: -28800,\n a: 3600,\n b: 7200,\n c: 10800,\n d: 14400,\n e: 18000,\n f: 21600,\n g: 25200,\n h: 28800,\n i: 32400,\n k: 36000,\n l: 39600,\n m: 43200,\n n: -3600,\n o: -7200,\n p: -10800,\n q: -14400,\n r: -18000,\n s: -21600,\n t: -25200,\n u: -28800,\n v: -32400,\n w: -36000,\n x: -39600,\n y: -43200,\n z: 0\n};\n\nvar formats = {\n yesterday: {\n regex: /^yesterday/i,\n name: 'yesterday',\n callback: function callback() {\n this.rd -= 1;\n return this.resetTime();\n }\n },\n\n now: {\n regex: /^now/i,\n name: 'now'\n // do nothing\n },\n\n noon: {\n regex: /^noon/i,\n name: 'noon',\n callback: function callback() {\n return this.resetTime() && this.time(12, 0, 0, 0);\n }\n },\n\n midnightOrToday: {\n regex: /^(midnight|today)/i,\n name: 'midnight | today',\n callback: function callback() {\n return this.resetTime();\n }\n },\n\n tomorrow: {\n regex: /^tomorrow/i,\n name: 'tomorrow',\n callback: function callback() {\n this.rd += 1;\n return this.resetTime();\n }\n },\n\n timestamp: {\n regex: /^@(-?\\d+)/i,\n name: 'timestamp',\n callback: function callback(match, timestamp) {\n this.rs += +timestamp;\n this.y = 1970;\n this.m = 0;\n this.d = 1;\n this.dates = 0;\n\n return this.resetTime() && this.zone(0);\n }\n },\n\n firstOrLastDay: {\n regex: /^(first|last) day of/i,\n name: 'firstdayof | lastdayof',\n callback: function callback(match, day) {\n if (day.toLowerCase() === 'first') {\n this.firstOrLastDayOfMonth = 1;\n } else {\n this.firstOrLastDayOfMonth = -1;\n }\n }\n },\n\n backOrFrontOf: {\n regex: RegExp('^(back|front) of ' + reHour24 + reSpaceOpt + reMeridian + '?', 'i'),\n name: 'backof | frontof',\n callback: function callback(match, side, hours, meridian) {\n var back = side.toLowerCase() === 'back';\n var hour = +hours;\n var minute = 15;\n\n if (!back) {\n hour -= 1;\n minute = 45;\n }\n\n hour = processMeridian(hour, meridian);\n\n return this.resetTime() && this.time(hour, minute, 0, 0);\n }\n },\n\n weekdayOf: {\n regex: RegExp('^(' + reReltextnumber + '|' + reReltexttext + ')' + reSpace + '(' + reDayfull + '|' + reDayabbr + ')' + reSpace + 'of', 'i'),\n name: 'weekdayof'\n // todo\n },\n\n mssqltime: {\n regex: RegExp('^' + reHour12 + ':' + reMinutelz + ':' + reSecondlz + '[:.]([0-9]+)' + reMeridian, 'i'),\n name: 'mssqltime',\n callback: function callback(match, hour, minute, second, frac, meridian) {\n return this.time(processMeridian(+hour, meridian), +minute, +second, +frac.substr(0, 3));\n }\n },\n\n oracledate: {\n regex: /^(\\d{2})-([A-Z]{3})-(\\d{2})$/i,\n name: 'd-M-y',\n callback: function callback(match, day, monthText, year) {\n var month = {\n JAN: 0,\n FEB: 1,\n MAR: 2,\n APR: 3,\n MAY: 4,\n JUN: 5,\n JUL: 6,\n AUG: 7,\n SEP: 8,\n OCT: 9,\n NOV: 10,\n DEC: 11\n }[monthText.toUpperCase()];\n return this.ymd(2000 + parseInt(year, 10), month, parseInt(day, 10));\n }\n },\n\n timeLong12: {\n regex: RegExp('^' + reHour12 + '[:.]' + reMinute + '[:.]' + reSecondlz + reSpaceOpt + reMeridian, 'i'),\n name: 'timelong12',\n callback: function callback(match, hour, minute, second, meridian) {\n return this.time(processMeridian(+hour, meridian), +minute, +second, 0);\n }\n },\n\n timeShort12: {\n regex: RegExp('^' + reHour12 + '[:.]' + reMinutelz + reSpaceOpt + reMeridian, 'i'),\n name: 'timeshort12',\n callback: function callback(match, hour, minute, meridian) {\n return this.time(processMeridian(+hour, meridian), +minute, 0, 0);\n }\n },\n\n timeTiny12: {\n regex: RegExp('^' + reHour12 + reSpaceOpt + reMeridian, 'i'),\n name: 'timetiny12',\n callback: function callback(match, hour, meridian) {\n return this.time(processMeridian(+hour, meridian), 0, 0, 0);\n }\n },\n\n soap: {\n regex: RegExp('^' + reYear4 + '-' + reMonthlz + '-' + reDaylz + 'T' + reHour24lz + ':' + reMinutelz + ':' + reSecondlz + reFrac + reTzCorrection + '?', 'i'),\n name: 'soap',\n callback: function callback(match, year, month, day, hour, minute, second, frac, tzCorrection) {\n return this.ymd(+year, month - 1, +day) && this.time(+hour, +minute, +second, +frac.substr(0, 3)) && this.zone(processTzCorrection(tzCorrection));\n }\n },\n\n wddx: {\n regex: RegExp('^' + reYear4 + '-' + reMonth + '-' + reDay + 'T' + reHour24 + ':' + reMinute + ':' + reSecond),\n name: 'wddx',\n callback: function callback(match, year, month, day, hour, minute, second) {\n return this.ymd(+year, month - 1, +day) && this.time(+hour, +minute, +second, 0);\n }\n },\n\n exif: {\n regex: RegExp('^' + reYear4 + ':' + reMonthlz + ':' + reDaylz + ' ' + reHour24lz + ':' + reMinutelz + ':' + reSecondlz, 'i'),\n name: 'exif',\n callback: function callback(match, year, month, day, hour, minute, second) {\n return this.ymd(+year, month - 1, +day) && this.time(+hour, +minute, +second, 0);\n }\n },\n\n xmlRpc: {\n regex: RegExp('^' + reYear4 + reMonthlz + reDaylz + 'T' + reHour24 + ':' + reMinutelz + ':' + reSecondlz),\n name: 'xmlrpc',\n callback: function callback(match, year, month, day, hour, minute, second) {\n return this.ymd(+year, month - 1, +day) && this.time(+hour, +minute, +second, 0);\n }\n },\n\n xmlRpcNoColon: {\n regex: RegExp('^' + reYear4 + reMonthlz + reDaylz + '[Tt]' + reHour24 + reMinutelz + reSecondlz),\n name: 'xmlrpcnocolon',\n callback: function callback(match, year, month, day, hour, minute, second) {\n return this.ymd(+year, month - 1, +day) && this.time(+hour, +minute, +second, 0);\n }\n },\n\n clf: {\n regex: RegExp('^' + reDay + '/(' + reMonthAbbr + ')/' + reYear4 + ':' + reHour24lz + ':' + reMinutelz + ':' + reSecondlz + reSpace + reTzCorrection, 'i'),\n name: 'clf',\n callback: function callback(match, day, month, year, hour, minute, second, tzCorrection) {\n return this.ymd(+year, lookupMonth(month), +day) && this.time(+hour, +minute, +second, 0) && this.zone(processTzCorrection(tzCorrection));\n }\n },\n\n iso8601long: {\n regex: RegExp('^t?' + reHour24 + '[:.]' + reMinute + '[:.]' + reSecond + reFrac, 'i'),\n name: 'iso8601long',\n callback: function callback(match, hour, minute, second, frac) {\n return this.time(+hour, +minute, +second, +frac.substr(0, 3));\n }\n },\n\n dateTextual: {\n regex: RegExp('^' + reMonthText + '[ .\\\\t-]*' + reDay + '[,.stndrh\\\\t ]+' + reYear, 'i'),\n name: 'datetextual',\n callback: function callback(match, month, day, year) {\n return this.ymd(processYear(year), lookupMonth(month), +day);\n }\n },\n\n pointedDate4: {\n regex: RegExp('^' + reDay + '[.\\\\t-]' + reMonth + '[.-]' + reYear4),\n name: 'pointeddate4',\n callback: function callback(match, day, month, year) {\n return this.ymd(+year, month - 1, +day);\n }\n },\n\n pointedDate2: {\n regex: RegExp('^' + reDay + '[.\\\\t]' + reMonth + '\\\\.' + reYear2),\n name: 'pointeddate2',\n callback: function callback(match, day, month, year) {\n return this.ymd(processYear(year), month - 1, +day);\n }\n },\n\n timeLong24: {\n regex: RegExp('^t?' + reHour24 + '[:.]' + reMinute + '[:.]' + reSecond),\n name: 'timelong24',\n callback: function callback(match, hour, minute, second) {\n return this.time(+hour, +minute, +second, 0);\n }\n },\n\n dateNoColon: {\n regex: RegExp('^' + reYear4 + reMonthlz + reDaylz),\n name: 'datenocolon',\n callback: function callback(match, year, month, day) {\n return this.ymd(+year, month - 1, +day);\n }\n },\n\n pgydotd: {\n regex: RegExp('^' + reYear4 + '\\\\.?' + reDayOfYear),\n name: 'pgydotd',\n callback: function callback(match, year, day) {\n return this.ymd(+year, 0, +day);\n }\n },\n\n timeShort24: {\n regex: RegExp('^t?' + reHour24 + '[:.]' + reMinute, 'i'),\n name: 'timeshort24',\n callback: function callback(match, hour, minute) {\n return this.time(+hour, +minute, 0, 0);\n }\n },\n\n iso8601noColon: {\n regex: RegExp('^t?' + reHour24lz + reMinutelz + reSecondlz, 'i'),\n name: 'iso8601nocolon',\n callback: function callback(match, hour, minute, second) {\n return this.time(+hour, +minute, +second, 0);\n }\n },\n\n iso8601dateSlash: {\n // eventhough the trailing slash is optional in PHP\n // here it's mandatory and inputs without the slash\n // are handled by dateslash\n regex: RegExp('^' + reYear4 + '/' + reMonthlz + '/' + reDaylz + '/'),\n name: 'iso8601dateslash',\n callback: function callback(match, year, month, day) {\n return this.ymd(+year, month - 1, +day);\n }\n },\n\n dateSlash: {\n regex: RegExp('^' + reYear4 + '/' + reMonth + '/' + reDay),\n name: 'dateslash',\n callback: function callback(match, year, month, day) {\n return this.ymd(+year, month - 1, +day);\n }\n },\n\n american: {\n regex: RegExp('^' + reMonth + '/' + reDay + '/' + reYear),\n name: 'american',\n callback: function callback(match, month, day, year) {\n return this.ymd(processYear(year), month - 1, +day);\n }\n },\n\n americanShort: {\n regex: RegExp('^' + reMonth + '/' + reDay),\n name: 'americanshort',\n callback: function callback(match, month, day) {\n return this.ymd(this.y, month - 1, +day);\n }\n },\n\n gnuDateShortOrIso8601date2: {\n // iso8601date2 is complete subset of gnudateshort\n regex: RegExp('^' + reYear + '-' + reMonth + '-' + reDay),\n name: 'gnudateshort | iso8601date2',\n callback: function callback(match, year, month, day) {\n return this.ymd(processYear(year), month - 1, +day);\n }\n },\n\n iso8601date4: {\n regex: RegExp('^' + reYear4withSign + '-' + reMonthlz + '-' + reDaylz),\n name: 'iso8601date4',\n callback: function callback(match, year, month, day) {\n return this.ymd(+year, month - 1, +day);\n }\n },\n\n gnuNoColon: {\n regex: RegExp('^t?' + reHour24lz + reMinutelz, 'i'),\n name: 'gnunocolon',\n callback: function callback(match, hour, minute) {\n // this rule is a special case\n // if time was already set once by any preceding rule, it sets the captured value as year\n switch (this.times) {\n case 0:\n return this.time(+hour, +minute, 0, this.f);\n case 1:\n this.y = hour * 100 + +minute;\n this.times++;\n\n return true;\n default:\n return false;\n }\n }\n },\n\n gnuDateShorter: {\n regex: RegExp('^' + reYear4 + '-' + reMonth),\n name: 'gnudateshorter',\n callback: function callback(match, year, month) {\n return this.ymd(+year, month - 1, 1);\n }\n },\n\n pgTextReverse: {\n // note: allowed years are from 32-9999\n // years below 32 should be treated as days in datefull\n regex: RegExp('^' + '(\\\\d{3,4}|[4-9]\\\\d|3[2-9])-(' + reMonthAbbr + ')-' + reDaylz, 'i'),\n name: 'pgtextreverse',\n callback: function callback(match, year, month, day) {\n return this.ymd(processYear(year), lookupMonth(month), +day);\n }\n },\n\n dateFull: {\n regex: RegExp('^' + reDay + '[ \\\\t.-]*' + reMonthText + '[ \\\\t.-]*' + reYear, 'i'),\n name: 'datefull',\n callback: function callback(match, day, month, year) {\n return this.ymd(processYear(year), lookupMonth(month), +day);\n }\n },\n\n dateNoDay: {\n regex: RegExp('^' + reMonthText + '[ .\\\\t-]*' + reYear4, 'i'),\n name: 'datenoday',\n callback: function callback(match, month, year) {\n return this.ymd(+year, lookupMonth(month), 1);\n }\n },\n\n dateNoDayRev: {\n regex: RegExp('^' + reYear4 + '[ .\\\\t-]*' + reMonthText, 'i'),\n name: 'datenodayrev',\n callback: function callback(match, year, month) {\n return this.ymd(+year, lookupMonth(month), 1);\n }\n },\n\n pgTextShort: {\n regex: RegExp('^(' + reMonthAbbr + ')-' + reDaylz + '-' + reYear, 'i'),\n name: 'pgtextshort',\n callback: function callback(match, month, day, year) {\n return this.ymd(processYear(year), lookupMonth(month), +day);\n }\n },\n\n dateNoYear: {\n regex: RegExp('^' + reDateNoYear, 'i'),\n name: 'datenoyear',\n callback: function callback(match, month, day) {\n return this.ymd(this.y, lookupMonth(month), +day);\n }\n },\n\n dateNoYearRev: {\n regex: RegExp('^' + reDay + '[ .\\\\t-]*' + reMonthText, 'i'),\n name: 'datenoyearrev',\n callback: function callback(match, day, month) {\n return this.ymd(this.y, lookupMonth(month), +day);\n }\n },\n\n isoWeekDay: {\n regex: RegExp('^' + reYear4 + '-?W' + reWeekOfYear + '(?:-?([0-7]))?'),\n name: 'isoweekday | isoweek',\n callback: function callback(match, year, week, day) {\n day = day ? +day : 1;\n\n if (!this.ymd(+year, 0, 1)) {\n return false;\n }\n\n // get day of week for Jan 1st\n var dayOfWeek = new Date(this.y, this.m, this.d).getDay();\n\n // and use the day to figure out the offset for day 1 of week 1\n dayOfWeek = 0 - (dayOfWeek > 4 ? dayOfWeek - 7 : dayOfWeek);\n\n this.rd += dayOfWeek + (week - 1) * 7 + day;\n }\n },\n\n relativeText: {\n regex: RegExp('^(' + reReltextnumber + '|' + reReltexttext + ')' + reSpace + '(' + reReltextunit + ')', 'i'),\n name: 'relativetext',\n callback: function callback(match, relValue, relUnit) {\n // todo: implement handling of 'this time-unit'\n // eslint-disable-next-line no-unused-vars\n var _lookupRelative = lookupRelative(relValue),\n amount = _lookupRelative.amount,\n behavior = _lookupRelative.behavior;\n\n switch (relUnit.toLowerCase()) {\n case 'sec':\n case 'secs':\n case 'second':\n case 'seconds':\n this.rs += amount;\n break;\n case 'min':\n case 'mins':\n case 'minute':\n case 'minutes':\n this.ri += amount;\n break;\n case 'hour':\n case 'hours':\n this.rh += amount;\n break;\n case 'day':\n case 'days':\n this.rd += amount;\n break;\n case 'fortnight':\n case 'fortnights':\n case 'forthnight':\n case 'forthnights':\n this.rd += amount * 14;\n break;\n case 'week':\n case 'weeks':\n this.rd += amount * 7;\n break;\n case 'month':\n case 'months':\n this.rm += amount;\n break;\n case 'year':\n case 'years':\n this.ry += amount;\n break;\n case 'mon':\n case 'monday':\n case 'tue':\n case 'tuesday':\n case 'wed':\n case 'wednesday':\n case 'thu':\n case 'thursday':\n case 'fri':\n case 'friday':\n case 'sat':\n case 'saturday':\n case 'sun':\n case 'sunday':\n this.resetTime();\n this.weekday = lookupWeekday(relUnit, 7);\n this.weekdayBehavior = 1;\n this.rd += (amount > 0 ? amount - 1 : amount) * 7;\n break;\n case 'weekday':\n case 'weekdays':\n // todo\n break;\n }\n }\n },\n\n relative: {\n regex: RegExp('^([+-]*)[ \\\\t]*(\\\\d+)' + reSpaceOpt + '(' + reReltextunit + '|week)', 'i'),\n name: 'relative',\n callback: function callback(match, signs, relValue, relUnit) {\n var minuses = signs.replace(/[^-]/g, '').length;\n\n var amount = +relValue * Math.pow(-1, minuses);\n\n switch (relUnit.toLowerCase()) {\n case 'sec':\n case 'secs':\n case 'second':\n case 'seconds':\n this.rs += amount;\n break;\n case 'min':\n case 'mins':\n case 'minute':\n case 'minutes':\n this.ri += amount;\n break;\n case 'hour':\n case 'hours':\n this.rh += amount;\n break;\n case 'day':\n case 'days':\n this.rd += amount;\n break;\n case 'fortnight':\n case 'fortnights':\n case 'forthnight':\n case 'forthnights':\n this.rd += amount * 14;\n break;\n case 'week':\n case 'weeks':\n this.rd += amount * 7;\n break;\n case 'month':\n case 'months':\n this.rm += amount;\n break;\n case 'year':\n case 'years':\n this.ry += amount;\n break;\n case 'mon':\n case 'monday':\n case 'tue':\n case 'tuesday':\n case 'wed':\n case 'wednesday':\n case 'thu':\n case 'thursday':\n case 'fri':\n case 'friday':\n case 'sat':\n case 'saturday':\n case 'sun':\n case 'sunday':\n this.resetTime();\n this.weekday = lookupWeekday(relUnit, 7);\n this.weekdayBehavior = 1;\n this.rd += (amount > 0 ? amount - 1 : amount) * 7;\n break;\n case 'weekday':\n case 'weekdays':\n // todo\n break;\n }\n }\n },\n\n dayText: {\n regex: RegExp('^(' + reDaytext + ')', 'i'),\n name: 'daytext',\n callback: function callback(match, dayText) {\n this.resetTime();\n this.weekday = lookupWeekday(dayText, 0);\n\n if (this.weekdayBehavior !== 2) {\n this.weekdayBehavior = 1;\n }\n }\n },\n\n relativeTextWeek: {\n regex: RegExp('^(' + reReltexttext + ')' + reSpace + 'week', 'i'),\n name: 'relativetextweek',\n callback: function callback(match, relText) {\n this.weekdayBehavior = 2;\n\n switch (relText.toLowerCase()) {\n case 'this':\n this.rd += 0;\n break;\n case 'next':\n this.rd += 7;\n break;\n case 'last':\n case 'previous':\n this.rd -= 7;\n break;\n }\n\n if (isNaN(this.weekday)) {\n this.weekday = 1;\n }\n }\n },\n\n monthFullOrMonthAbbr: {\n regex: RegExp('^(' + reMonthFull + '|' + reMonthAbbr + ')', 'i'),\n name: 'monthfull | monthabbr',\n callback: function callback(match, month) {\n return this.ymd(this.y, lookupMonth(month), this.d);\n }\n },\n\n tzCorrection: {\n regex: RegExp('^' + reTzCorrection, 'i'),\n name: 'tzcorrection',\n callback: function callback(tzCorrection) {\n return this.zone(processTzCorrection(tzCorrection));\n }\n },\n\n tzAbbr: {\n regex: RegExp('^' + reTzAbbr),\n name: 'tzabbr',\n callback: function callback(match, abbr) {\n var offset = tzAbbrOffsets[abbr.toLowerCase()];\n\n if (isNaN(offset)) {\n return false;\n }\n\n return this.zone(offset);\n }\n },\n\n ago: {\n regex: /^ago/i,\n name: 'ago',\n callback: function callback() {\n this.ry = -this.ry;\n this.rm = -this.rm;\n this.rd = -this.rd;\n this.rh = -this.rh;\n this.ri = -this.ri;\n this.rs = -this.rs;\n this.rf = -this.rf;\n }\n },\n\n year4: {\n regex: RegExp('^' + reYear4),\n name: 'year4',\n callback: function callback(match, year) {\n this.y = +year;\n return true;\n }\n },\n\n whitespace: {\n regex: /^[ .,\\t]+/,\n name: 'whitespace'\n // do nothing\n },\n\n dateShortWithTimeLong: {\n regex: RegExp('^' + reDateNoYear + 't?' + reHour24 + '[:.]' + reMinute + '[:.]' + reSecond, 'i'),\n name: 'dateshortwithtimelong',\n callback: function callback(match, month, day, hour, minute, second) {\n return this.ymd(this.y, lookupMonth(month), +day) && this.time(+hour, +minute, +second, 0);\n }\n },\n\n dateShortWithTimeLong12: {\n regex: RegExp('^' + reDateNoYear + reHour12 + '[:.]' + reMinute + '[:.]' + reSecondlz + reSpaceOpt + reMeridian, 'i'),\n name: 'dateshortwithtimelong12',\n callback: function callback(match, month, day, hour, minute, second, meridian) {\n return this.ymd(this.y, lookupMonth(month), +day) && this.time(processMeridian(+hour, meridian), +minute, +second, 0);\n }\n },\n\n dateShortWithTimeShort: {\n regex: RegExp('^' + reDateNoYear + 't?' + reHour24 + '[:.]' + reMinute, 'i'),\n name: 'dateshortwithtimeshort',\n callback: function callback(match, month, day, hour, minute) {\n return this.ymd(this.y, lookupMonth(month), +day) && this.time(+hour, +minute, 0, 0);\n }\n },\n\n dateShortWithTimeShort12: {\n regex: RegExp('^' + reDateNoYear + reHour12 + '[:.]' + reMinutelz + reSpaceOpt + reMeridian, 'i'),\n name: 'dateshortwithtimeshort12',\n callback: function callback(match, month, day, hour, minute, meridian) {\n return this.ymd(this.y, lookupMonth(month), +day) && this.time(processMeridian(+hour, meridian), +minute, 0, 0);\n }\n }\n};\n\nvar resultProto = {\n // date\n y: NaN,\n m: NaN,\n d: NaN,\n // time\n h: NaN,\n i: NaN,\n s: NaN,\n f: NaN,\n\n // relative shifts\n ry: 0,\n rm: 0,\n rd: 0,\n rh: 0,\n ri: 0,\n rs: 0,\n rf: 0,\n\n // weekday related shifts\n weekday: NaN,\n weekdayBehavior: 0,\n\n // first or last day of month\n // 0 none, 1 first, -1 last\n firstOrLastDayOfMonth: 0,\n\n // timezone correction in minutes\n z: NaN,\n\n // counters\n dates: 0,\n times: 0,\n zones: 0,\n\n // helper functions\n ymd: function ymd(y, m, d) {\n if (this.dates > 0) {\n return false;\n }\n\n this.dates++;\n this.y = y;\n this.m = m;\n this.d = d;\n return true;\n },\n time: function time(h, i, s, f) {\n if (this.times > 0) {\n return false;\n }\n\n this.times++;\n this.h = h;\n this.i = i;\n this.s = s;\n this.f = f;\n\n return true;\n },\n resetTime: function resetTime() {\n this.h = 0;\n this.i = 0;\n this.s = 0;\n this.f = 0;\n this.times = 0;\n\n return true;\n },\n zone: function zone(minutes) {\n if (this.zones <= 1) {\n this.zones++;\n this.z = minutes;\n return true;\n }\n\n return false;\n },\n toDate: function toDate(relativeTo) {\n if (this.dates && !this.times) {\n this.h = this.i = this.s = this.f = 0;\n }\n\n // fill holes\n if (isNaN(this.y)) {\n this.y = relativeTo.getFullYear();\n }\n\n if (isNaN(this.m)) {\n this.m = relativeTo.getMonth();\n }\n\n if (isNaN(this.d)) {\n this.d = relativeTo.getDate();\n }\n\n if (isNaN(this.h)) {\n this.h = relativeTo.getHours();\n }\n\n if (isNaN(this.i)) {\n this.i = relativeTo.getMinutes();\n }\n\n if (isNaN(this.s)) {\n this.s = relativeTo.getSeconds();\n }\n\n if (isNaN(this.f)) {\n this.f = relativeTo.getMilliseconds();\n }\n\n // adjust special early\n switch (this.firstOrLastDayOfMonth) {\n case 1:\n this.d = 1;\n break;\n case -1:\n this.d = 0;\n this.m += 1;\n break;\n }\n\n if (!isNaN(this.weekday)) {\n var date = new Date(relativeTo.getTime());\n date.setFullYear(this.y, this.m, this.d);\n date.setHours(this.h, this.i, this.s, this.f);\n\n var dow = date.getDay();\n\n if (this.weekdayBehavior === 2) {\n // To make \"this week\" work, where the current day of week is a \"sunday\"\n if (dow === 0 && this.weekday !== 0) {\n this.weekday = -6;\n }\n\n // To make \"sunday this week\" work, where the current day of week is not a \"sunday\"\n if (this.weekday === 0 && dow !== 0) {\n this.weekday = 7;\n }\n\n this.d -= dow;\n this.d += this.weekday;\n } else {\n var diff = this.weekday - dow;\n\n // some PHP magic\n if (this.rd < 0 && diff < 0 || this.rd >= 0 && diff <= -this.weekdayBehavior) {\n diff += 7;\n }\n\n if (this.weekday >= 0) {\n this.d += diff;\n } else {\n this.d -= 7 - (Math.abs(this.weekday) - dow);\n }\n\n this.weekday = NaN;\n }\n }\n\n // adjust relative\n this.y += this.ry;\n this.m += this.rm;\n this.d += this.rd;\n\n this.h += this.rh;\n this.i += this.ri;\n this.s += this.rs;\n this.f += this.rf;\n\n this.ry = this.rm = this.rd = 0;\n this.rh = this.ri = this.rs = this.rf = 0;\n\n var result = new Date(relativeTo.getTime());\n // since Date constructor treats years <= 99 as 1900+\n // it can't be used, thus this weird way\n result.setFullYear(this.y, this.m, this.d);\n result.setHours(this.h, this.i, this.s, this.f);\n\n // note: this is done twice in PHP\n // early when processing special relatives\n // and late\n // todo: check if the logic can be reduced\n // to just one time action\n switch (this.firstOrLastDayOfMonth) {\n case 1:\n result.setDate(1);\n break;\n case -1:\n result.setMonth(result.getMonth() + 1, 0);\n break;\n }\n\n // adjust timezone\n if (!isNaN(this.z) && result.getTimezoneOffset() !== this.z) {\n result.setUTCFullYear(result.getFullYear(), result.getMonth(), result.getDate());\n\n result.setUTCHours(result.getHours(), result.getMinutes(), result.getSeconds() - this.z, result.getMilliseconds());\n }\n\n return result;\n }\n};\n\nmodule.exports = function strtotime(str, now) {\n // discuss at: https://locutus.io/php/strtotime/\n // original by: Caio Ariede (https://caioariede.com)\n // improved by: Kevin van Zonneveld (https://kvz.io)\n // improved by: Caio Ariede (https://caioariede.com)\n // improved by: A. Matías Quezada (https://amatiasq.com)\n // improved by: preuter\n // improved by: Brett Zamir (https://brett-zamir.me)\n // improved by: Mirko Faber\n // input by: David\n // bugfixed by: Wagner B. Soares\n // bugfixed by: Artur Tchernychev\n // bugfixed by: Stephan Bösch-Plepelits (https://github.com/plepe)\n // reimplemented by: Rafał Kukawski\n // note 1: Examples all have a fixed timestamp to prevent\n // note 1: tests to fail because of variable time(zones)\n // example 1: strtotime('+1 day', 1129633200)\n // returns 1: 1129719600\n // example 2: strtotime('+1 week 2 days 4 hours 2 seconds', 1129633200)\n // returns 2: 1130425202\n // example 3: strtotime('last month', 1129633200)\n // returns 3: 1127041200\n // example 4: strtotime('2009-05-04 08:30:00+00')\n // returns 4: 1241425800\n // example 5: strtotime('2009-05-04 08:30:00+02:00')\n // returns 5: 1241418600\n // example 6: strtotime('2009-05-04 08:30:00 YWT')\n // returns 6: 1241454600\n // example 7: strtotime('10-JUL-17')\n // returns 7: 1499644800\n\n if (now == null) {\n now = Math.floor(Date.now() / 1000);\n }\n\n // the rule order is important\n // if multiple rules match, the longest match wins\n // if multiple rules match the same string, the first match wins\n var rules = [formats.yesterday, formats.now, formats.noon, formats.midnightOrToday, formats.tomorrow, formats.timestamp, formats.firstOrLastDay, formats.backOrFrontOf,\n // formats.weekdayOf, // not yet implemented\n formats.timeTiny12, formats.timeShort12, formats.timeLong12, formats.mssqltime, formats.oracledate, formats.timeShort24, formats.timeLong24, formats.iso8601long, formats.gnuNoColon, formats.iso8601noColon, formats.americanShort, formats.american, formats.iso8601date4, formats.iso8601dateSlash, formats.dateSlash, formats.gnuDateShortOrIso8601date2, formats.gnuDateShorter, formats.dateFull, formats.pointedDate4, formats.pointedDate2, formats.dateNoDay, formats.dateNoDayRev, formats.dateTextual, formats.dateNoYear, formats.dateNoYearRev, formats.dateNoColon, formats.xmlRpc, formats.xmlRpcNoColon, formats.soap, formats.wddx, formats.exif, formats.pgydotd, formats.isoWeekDay, formats.pgTextShort, formats.pgTextReverse, formats.clf, formats.year4, formats.ago, formats.dayText, formats.relativeTextWeek, formats.relativeText, formats.monthFullOrMonthAbbr, formats.tzCorrection, formats.tzAbbr, formats.dateShortWithTimeShort12, formats.dateShortWithTimeLong12, formats.dateShortWithTimeShort, formats.dateShortWithTimeLong, formats.relative, formats.whitespace];\n\n var result = Object.create(resultProto);\n\n while (str.length) {\n var longestMatch = null;\n var finalRule = null;\n\n for (var i = 0, l = rules.length; i < l; i++) {\n var format = rules[i];\n\n var match = str.match(format.regex);\n\n if (match) {\n if (!longestMatch || match[0].length > longestMatch[0].length) {\n longestMatch = match;\n finalRule = format;\n }\n }\n }\n\n if (!finalRule || finalRule.callback && finalRule.callback.apply(result, longestMatch) === false) {\n return false;\n }\n\n str = str.substr(longestMatch[0].length);\n finalRule = null;\n longestMatch = null;\n }\n\n return Math.floor(result.toDate(new Date(now * 1000)) / 1000);\n};\n//# sourceMappingURL=strtotime.js.map\n\n/***/ }),\n\n/***/ \"./node_modules/locutus/php/info/ini_get.js\":\n/*!**************************************************!*\\\n !*** ./node_modules/locutus/php/info/ini_get.js ***!\n \\**************************************************/\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\n\n\nmodule.exports = function ini_get(varname) {\n // discuss at: https://locutus.io/php/ini_get/\n // original by: Brett Zamir (https://brett-zamir.me)\n // note 1: The ini values must be set by ini_set or manually within an ini file\n // example 1: ini_set('date.timezone', 'Asia/Hong_Kong')\n // example 1: ini_get('date.timezone')\n // returns 1: 'Asia/Hong_Kong'\n\n var $global = typeof window !== 'undefined' ? window : __webpack_require__.g;\n $global.$locutus = $global.$locutus || {};\n var $locutus = $global.$locutus;\n $locutus.php = $locutus.php || {};\n $locutus.php.ini = $locutus.php.ini || {};\n\n if ($locutus.php.ini[varname] && $locutus.php.ini[varname].local_value !== undefined) {\n if ($locutus.php.ini[varname].local_value === null) {\n return '';\n }\n return $locutus.php.ini[varname].local_value;\n }\n\n return '';\n};\n//# sourceMappingURL=ini_get.js.map\n\n/***/ }),\n\n/***/ \"./node_modules/locutus/php/strings/strlen.js\":\n/*!****************************************************!*\\\n !*** ./node_modules/locutus/php/strings/strlen.js ***!\n \\****************************************************/\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\n\n\nmodule.exports = function strlen(string) {\n // discuss at: https://locutus.io/php/strlen/\n // original by: Kevin van Zonneveld (https://kvz.io)\n // improved by: Sakimori\n // improved by: Kevin van Zonneveld (https://kvz.io)\n // input by: Kirk Strobeck\n // bugfixed by: Onno Marsman (https://twitter.com/onnomarsman)\n // revised by: Brett Zamir (https://brett-zamir.me)\n // note 1: May look like overkill, but in order to be truly faithful to handling all Unicode\n // note 1: characters and to this function in PHP which does not count the number of bytes\n // note 1: but counts the number of characters, something like this is really necessary.\n // example 1: strlen('Kevin van Zonneveld')\n // returns 1: 19\n // example 2: ini_set('unicode.semantics', 'on')\n // example 2: strlen('A\\ud87e\\udc04Z')\n // returns 2: 3\n\n var str = string + '';\n\n var iniVal = ( true ? __webpack_require__(/*! ../info/ini_get */ \"./node_modules/locutus/php/info/ini_get.js\")('unicode.semantics') : 0) || 'off';\n if (iniVal === 'off') {\n return str.length;\n }\n\n var i = 0;\n var lgth = 0;\n\n var getWholeChar = function getWholeChar(str, i) {\n var code = str.charCodeAt(i);\n var next = '';\n var prev = '';\n if (code >= 0xd800 && code <= 0xdbff) {\n // High surrogate (could change last hex to 0xDB7F to\n // treat high private surrogates as single characters)\n if (str.length <= i + 1) {\n throw new Error('High surrogate without following low surrogate');\n }\n next = str.charCodeAt(i + 1);\n if (next < 0xdc00 || next > 0xdfff) {\n throw new Error('High surrogate without following low surrogate');\n }\n return str.charAt(i) + str.charAt(i + 1);\n } else if (code >= 0xdc00 && code <= 0xdfff) {\n // Low surrogate\n if (i === 0) {\n throw new Error('Low surrogate without preceding high surrogate');\n }\n prev = str.charCodeAt(i - 1);\n if (prev < 0xd800 || prev > 0xdbff) {\n // (could change last hex to 0xDB7F to treat high private surrogates\n // as single characters)\n throw new Error('Low surrogate without preceding high surrogate');\n }\n // We can pass over low surrogates now as the second\n // component in a pair which we have already processed\n return false;\n }\n return str.charAt(i);\n };\n\n for (i = 0, lgth = 0; i < str.length; i++) {\n if (getWholeChar(str, i) === false) {\n continue;\n }\n // Adapt this line at the top of any loop, passing in the whole string and\n // the current iteration and returning a variable to represent the individual character;\n // purpose is to treat the first part of a surrogate pair as the whole character and then\n // ignore the second part\n lgth++;\n }\n\n return lgth;\n};\n//# sourceMappingURL=strlen.js.map\n\n/***/ }),\n\n/***/ \"./node_modules/locutus/php/var/is_numeric.js\":\n/*!****************************************************!*\\\n !*** ./node_modules/locutus/php/var/is_numeric.js ***!\n \\****************************************************/\n/***/ (function(module) {\n\n\n\nmodule.exports = function is_numeric(mixedVar) {\n // discuss at: https://locutus.io/php/is_numeric/\n // original by: Kevin van Zonneveld (https://kvz.io)\n // improved by: David\n // improved by: taith\n // bugfixed by: Tim de Koning\n // bugfixed by: WebDevHobo (https://webdevhobo.blogspot.com/)\n // bugfixed by: Brett Zamir (https://brett-zamir.me)\n // bugfixed by: Denis Chenu (https://shnoulle.net)\n // example 1: is_numeric(186.31)\n // returns 1: true\n // example 2: is_numeric('Kevin van Zonneveld')\n // returns 2: false\n // example 3: is_numeric(' +186.31e2')\n // returns 3: true\n // example 4: is_numeric('')\n // returns 4: false\n // example 5: is_numeric([])\n // returns 5: false\n // example 6: is_numeric('1 ')\n // returns 6: false\n\n var whitespace = [' ', '\\n', '\\r', '\\t', '\\f', '\\x0b', '\\xa0', '\\u2000', '\\u2001', '\\u2002', '\\u2003', '\\u2004', '\\u2005', '\\u2006', '\\u2007', '\\u2008', '\\u2009', '\\u200A', '\\u200B', '\\u2028', '\\u2029', '\\u3000'].join('');\n\n // @todo: Break this up using many single conditions with early returns\n return (typeof mixedVar === 'number' || typeof mixedVar === 'string' && whitespace.indexOf(mixedVar.slice(-1)) === -1) && mixedVar !== '' && !isNaN(mixedVar);\n};\n//# sourceMappingURL=is_numeric.js.map\n\n/***/ })\n\n/******/ \t});\n/************************************************************************/\n/******/ \t// The module cache\n/******/ \tvar __webpack_module_cache__ = {};\n/******/ \t\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/ \t\t// Check if module is in cache\n/******/ \t\tvar cachedModule = __webpack_module_cache__[moduleId];\n/******/ \t\tif (cachedModule !== undefined) {\n/******/ \t\t\treturn cachedModule.exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = __webpack_module_cache__[moduleId] = {\n/******/ \t\t\t// no module.id needed\n/******/ \t\t\t// no module.loaded needed\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/ \t\n/******/ \t\t// Execute the module function\n/******/ \t\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n/******/ \t\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/ \t\n/************************************************************************/\n/******/ \t/* webpack/runtime/compat get default export */\n/******/ \t!function() {\n/******/ \t\t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t\t__webpack_require__.n = function(module) {\n/******/ \t\t\tvar getter = module && module.__esModule ?\n/******/ \t\t\t\tfunction() { return module['default']; } :\n/******/ \t\t\t\tfunction() { return module; };\n/******/ \t\t\t__webpack_require__.d(getter, { a: getter });\n/******/ \t\t\treturn getter;\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/define property getters */\n/******/ \t!function() {\n/******/ \t\t// define getter functions for harmony exports\n/******/ \t\t__webpack_require__.d = function(exports, definition) {\n/******/ \t\t\tfor(var key in definition) {\n/******/ \t\t\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n/******/ \t\t\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n/******/ \t\t\t\t}\n/******/ \t\t\t}\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/global */\n/******/ \t!function() {\n/******/ \t\t__webpack_require__.g = (function() {\n/******/ \t\t\tif (typeof globalThis === 'object') return globalThis;\n/******/ \t\t\ttry {\n/******/ \t\t\t\treturn this || new Function('return this')();\n/******/ \t\t\t} catch (e) {\n/******/ \t\t\t\tif (typeof window === 'object') return window;\n/******/ \t\t\t}\n/******/ \t\t})();\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/hasOwnProperty shorthand */\n/******/ \t!function() {\n/******/ \t\t__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/make namespace object */\n/******/ \t!function() {\n/******/ \t\t// define __esModule on exports\n/******/ \t\t__webpack_require__.r = function(exports) {\n/******/ \t\t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n/******/ \t\t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n/******/ \t\t\t}\n/******/ \t\t\tObject.defineProperty(exports, '__esModule', { value: true });\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/************************************************************************/\nvar __webpack_exports__ = {};\n// This entry needs to be wrapped in an IIFE because it needs to be isolated against other modules in the chunk.\n!function() {\n/*!****************************************!*\\\n !*** ./resources/assets/js/helpers.js ***!\n \\****************************************/\n__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var locutus_php_strings_strlen__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! locutus/php/strings/strlen */ \"./node_modules/locutus/php/strings/strlen.js\");\n/* harmony import */ var locutus_php_strings_strlen__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(locutus_php_strings_strlen__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var locutus_php_array_array_diff__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! locutus/php/array/array_diff */ \"./node_modules/locutus/php/array/array_diff.js\");\n/* harmony import */ var locutus_php_array_array_diff__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(locutus_php_array_array_diff__WEBPACK_IMPORTED_MODULE_1__);\n/* harmony import */ var locutus_php_datetime_strtotime__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! locutus/php/datetime/strtotime */ \"./node_modules/locutus/php/datetime/strtotime.js\");\n/* harmony import */ var locutus_php_datetime_strtotime__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(locutus_php_datetime_strtotime__WEBPACK_IMPORTED_MODULE_2__);\n/* harmony import */ var locutus_php_var_is_numeric__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! locutus/php/var/is_numeric */ \"./node_modules/locutus/php/var/is_numeric.js\");\n/* harmony import */ var locutus_php_var_is_numeric__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(locutus_php_var_is_numeric__WEBPACK_IMPORTED_MODULE_3__);\n/*!\n * Laravel Javascript Validation\n *\n * https://github.com/proengsoft/laravel-jsvalidation\n *\n * Helper functions used by validators\n *\n * Copyright (c) 2017 Proengsoft\n * Released under the MIT license\n */\n\n\n\n\n\n$.extend(true, laravelValidation, {\n helpers: {\n /**\n * Numeric rules\n */\n numericRules: ['Integer', 'Numeric'],\n /**\n * Gets the file information from file input.\n *\n * @param fieldObj\n * @param index\n * @returns {{file: *, extension: string, size: number}}\n */\n fileinfo: function (fieldObj, index) {\n var FileName = fieldObj.value;\n index = typeof index !== 'undefined' ? index : 0;\n if (fieldObj.files !== null) {\n if (typeof fieldObj.files[index] !== 'undefined') {\n return {\n file: FileName,\n extension: FileName.substr(FileName.lastIndexOf('.') + 1),\n size: fieldObj.files[index].size / 1024,\n type: fieldObj.files[index].type\n };\n }\n }\n return false;\n },\n /**\n * Gets the selectors for th specified field names.\n *\n * @param names\n * @returns {string}\n */\n selector: function (names) {\n var selector = [];\n if (!this.isArray(names)) {\n names = [names];\n }\n for (var i = 0; i < names.length; i++) {\n selector.push(\"[name='\" + names[i] + \"']\");\n }\n return selector.join();\n },\n /**\n * Check if element has numeric rules.\n *\n * @param element\n * @returns {boolean}\n */\n hasNumericRules: function (element) {\n return this.hasRules(element, this.numericRules);\n },\n /**\n * Check if element has passed rules.\n *\n * @param element\n * @param rules\n * @returns {boolean}\n */\n hasRules: function (element, rules) {\n var found = false;\n if (typeof rules === 'string') {\n rules = [rules];\n }\n var validator = $.data(element.form, \"validator\");\n var listRules = [];\n var cache = validator.arrayRulesCache;\n if (element.name in cache) {\n $.each(cache[element.name], function (index, arrayRule) {\n listRules.push(arrayRule);\n });\n }\n if (element.name in validator.settings.rules) {\n listRules.push(validator.settings.rules[element.name]);\n }\n $.each(listRules, function (index, objRules) {\n if ('laravelValidation' in objRules) {\n var _rules = objRules.laravelValidation;\n for (var i = 0; i < _rules.length; i++) {\n if ($.inArray(_rules[i][0], rules) !== -1) {\n found = true;\n return false;\n }\n }\n }\n });\n return found;\n },\n /**\n * Return the string length using PHP function.\n * http://php.net/manual/en/function.strlen.php\n * http://phpjs.org/functions/strlen/\n *\n * @param string\n */\n strlen: function (string) {\n return locutus_php_strings_strlen__WEBPACK_IMPORTED_MODULE_0___default()(string);\n },\n /**\n * Get the size of the object depending of his type.\n *\n * @param obj\n * @param element\n * @param value\n * @returns int\n */\n getSize: function getSize(obj, element, value) {\n if (this.hasNumericRules(element) && this.is_numeric(value)) {\n return parseFloat(value);\n } else if (this.isArray(value)) {\n return parseFloat(value.length);\n } else if (element.type === 'file') {\n return parseFloat(Math.floor(this.fileinfo(element).size));\n }\n return parseFloat(this.strlen(value));\n },\n /**\n * Return specified rule from element.\n *\n * @param rule\n * @param element\n * @returns object\n */\n getLaravelValidation: function (rule, element) {\n var found = undefined;\n $.each($.validator.staticRules(element), function (key, rules) {\n if (key === \"laravelValidation\") {\n $.each(rules, function (i, value) {\n if (value[0] === rule) {\n found = value;\n }\n });\n }\n });\n return found;\n },\n /**\n * Return he timestamp of value passed using format or default format in element.\n *\n * @param value\n * @param format\n * @returns {boolean|int}\n */\n parseTime: function (value, format) {\n var timeValue = false;\n var fmt = new DateFormatter();\n if (typeof value === 'number' && typeof format === 'undefined') {\n return value;\n }\n if (typeof format === 'object') {\n var dateRule = this.getLaravelValidation('DateFormat', format);\n if (dateRule !== undefined) {\n format = dateRule[1][0];\n } else {\n format = null;\n }\n }\n if (format == null) {\n timeValue = this.strtotime(value);\n } else {\n timeValue = fmt.parseDate(value, format);\n if (timeValue instanceof Date && fmt.formatDate(timeValue, format) === value) {\n timeValue = Math.round(timeValue.getTime() / 1000);\n } else {\n timeValue = false;\n }\n }\n return timeValue;\n },\n /**\n * Compare a given date against another using an operator.\n *\n * @param validator\n * @param value\n * @param element\n * @param params\n * @param operator\n * @return {boolean}\n */\n compareDates: function (validator, value, element, params, operator) {\n var timeCompare = this.parseTime(params);\n if (!timeCompare) {\n var target = this.dependentElement(validator, element, params);\n if (target === undefined) {\n return false;\n }\n timeCompare = this.parseTime(validator.elementValue(target), target);\n }\n var timeValue = this.parseTime(value, element);\n if (timeValue === false) {\n return false;\n }\n switch (operator) {\n case '<':\n return timeValue < timeCompare;\n case '<=':\n return timeValue <= timeCompare;\n case '==':\n case '===':\n return timeValue === timeCompare;\n case '>':\n return timeValue > timeCompare;\n case '>=':\n return timeValue >= timeCompare;\n default:\n throw new Error('Unsupported operator.');\n }\n },\n /**\n * This method allows you to intelligently guess the date by closely matching the specific format.\n *\n * @param value\n * @param format\n * @returns {Date}\n */\n guessDate: function (value, format) {\n var fmt = new DateFormatter();\n return fmt.guessDate(value, format);\n },\n /**\n * Returns Unix timestamp based on PHP function strototime.\n * http://php.net/manual/es/function.strtotime.php\n * http://phpjs.org/functions/strtotime/\n *\n * @param text\n * @param now\n * @returns {*}\n */\n strtotime: function (text, now) {\n return locutus_php_datetime_strtotime__WEBPACK_IMPORTED_MODULE_2___default()(text, now);\n },\n /**\n * Returns if value is numeric.\n * http://php.net/manual/es/var.is_numeric.php\n * http://phpjs.org/functions/is_numeric/\n *\n * @param mixed_var\n * @returns {*}\n */\n is_numeric: function (mixed_var) {\n return locutus_php_var_is_numeric__WEBPACK_IMPORTED_MODULE_3___default()(mixed_var);\n },\n /**\n * Check whether the argument is of type Array.\n * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray#Polyfill\n *\n * @param arg\n * @returns {boolean}\n */\n isArray: function (arg) {\n return Object.prototype.toString.call(arg) === '[object Array]';\n },\n /**\n * Returns Array diff based on PHP function array_diff.\n * http://php.net/manual/es/function.array_diff.php\n * http://phpjs.org/functions/array_diff/\n *\n * @param arr1\n * @param arr2\n * @returns {*}\n */\n arrayDiff: function (arr1, arr2) {\n return locutus_php_array_array_diff__WEBPACK_IMPORTED_MODULE_1___default()(arr1, arr2);\n },\n /**\n * Check whether two arrays are equal to one another.\n *\n * @param arr1\n * @param arr2\n * @returns {*}\n */\n arrayEquals: function (arr1, arr2) {\n if (!this.isArray(arr1) || !this.isArray(arr2)) {\n return false;\n }\n if (arr1.length !== arr2.length) {\n return false;\n }\n return $.isEmptyObject(this.arrayDiff(arr1, arr2));\n },\n /**\n * Makes element dependant from other.\n *\n * @param validator\n * @param element\n * @param name\n * @returns {*}\n */\n dependentElement: function (validator, element, name) {\n var el = validator.findByName(name);\n var targetElement = el[el.length - 1];\n if (targetElement !== undefined && validator.settings.onfocusout) {\n var event = 'blur';\n if (targetElement.tagName === 'SELECT' || targetElement.tagName === 'OPTION' || targetElement.type === 'checkbox' || targetElement.type === 'radio') {\n event = 'click';\n }\n var ruleName = '.validate-laravelValidation';\n $(targetElement).off(ruleName).off(event + ruleName + '-' + element.name).on(event + ruleName + '-' + element.name, function () {\n $(element).valid();\n });\n }\n return targetElement;\n },\n /**\n * Parses error Ajax response and gets the message.\n *\n * @param response\n * @returns {string[]}\n */\n parseErrorResponse: function (response) {\n var newResponse = ['Whoops, looks like something went wrong.'];\n if ('responseText' in response) {\n var errorMsg = response.responseText.match(/(.*)<\\/h1\\s*>/i);\n if (this.isArray(errorMsg)) {\n newResponse = [errorMsg[1]];\n }\n }\n return newResponse;\n },\n /**\n * Escape string to use as Regular Expression.\n *\n * @param str\n * @returns string\n */\n escapeRegExp: function (str) {\n return str.replace(/[\\-\\[\\]\\/\\{\\}\\(\\)\\*\\+\\?\\.\\\\\\^\\$\\|]/g, \"\\\\$&\");\n },\n /**\n * Generate RegExp from wildcard attributes.\n *\n * @param name\n * @returns {RegExp}\n */\n regexFromWildcard: function (name) {\n var nameParts = name.split('[*]');\n if (nameParts.length === 1) nameParts.push('');\n return new RegExp('^' + nameParts.map(function (x) {\n return laravelValidation.helpers.escapeRegExp(x);\n }).join('\\\\[[^\\\\]]*\\\\]') + '$');\n },\n /**\n * Merge additional laravel validation rules into the current rule set.\n *\n * @param {object} rules\n * @param {object} newRules\n * @returns {object}\n */\n mergeRules: function (rules, newRules) {\n var rulesList = {\n 'laravelValidation': newRules.laravelValidation || [],\n 'laravelValidationRemote': newRules.laravelValidationRemote || []\n };\n for (var key in rulesList) {\n if (rulesList[key].length === 0) {\n continue;\n }\n if (typeof rules[key] === \"undefined\") {\n rules[key] = [];\n }\n rules[key] = rules[key].concat(rulesList[key]);\n }\n return rules;\n },\n /**\n * HTML entity encode a string.\n *\n * @param string\n * @returns {string}\n */\n encode: function (string) {\n return $('
').text(string).html();\n },\n /**\n * Lookup name in an array.\n *\n * @param validator\n * @param {string} name Name in dot notation format.\n * @returns {*}\n */\n findByArrayName: function (validator, name) {\n var sqName = name.replace(/\\.([^\\.]+)/g, '[$1]'),\n lookups = [\n // Convert dot to square brackets. e.g. foo.bar.0 becomes foo[bar][0]\n sqName,\n // Append [] to the name e.g. foo becomes foo[] or foo.bar.0 becomes foo[bar][0][]\n sqName + '[]',\n // Remove key from last array e.g. foo[bar][0] becomes foo[bar][]\n sqName.replace(/(.*)\\[(.*)\\]$/g, '$1[]')];\n for (var i = 0; i < lookups.length; i++) {\n var elem = validator.findByName(lookups[i]);\n if (elem.length > 0) {\n return elem;\n }\n }\n return $(null);\n },\n /**\n * Attempt to find an element in the DOM matching the given name.\n * Example names include:\n * - domain.0 which matches domain[]\n * - customfield.3 which matches customfield[3]\n *\n * @param validator\n * @param {string} name\n * @returns {*}\n */\n findByName: function (validator, name) {\n // Exact match.\n var elem = validator.findByName(name);\n if (elem.length > 0) {\n return elem;\n }\n\n // Find name in data, using dot notation.\n var delim = '.',\n parts = name.split(delim);\n for (var i = parts.length; i > 0; i--) {\n var reconstructed = [];\n for (var c = 0; c < i; c++) {\n reconstructed.push(parts[c]);\n }\n elem = this.findByArrayName(validator, reconstructed.join(delim));\n if (elem.length > 0) {\n return elem;\n }\n }\n return $(null);\n },\n /**\n * If it's an array element, get all values.\n *\n * @param validator\n * @param element\n * @returns {*|string}\n */\n allElementValues: function (validator, element) {\n if (element.name.indexOf('[]') !== -1) {\n return validator.findByName(element.name).map(function (i, e) {\n return validator.elementValue(e);\n }).get();\n }\n return validator.elementValue(element);\n }\n }\n});\n}();\n/******/ })()\n;\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGVscGVycy5qcyIsIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7O0FBQWE7O0FBRWI7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsbUJBQW1COztBQUVuQjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSxnQkFBZ0IsVUFBVTtBQUMxQjtBQUNBO0FBQ0E7QUFDQTtBQUNBLDZCQUE2QjtBQUM3QjtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7Ozs7Ozs7OztBQ2hDYTs7QUFFYjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUEscUJBQXFCLElBQUk7QUFDekIsc0JBQXNCLEVBQUU7QUFDeEIsc0JBQXNCLEVBQUU7QUFDeEIsbUNBQW1DLEVBQUU7QUFDckM7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLDZCQUE2QixJQUFJLElBQUksSUFBSSxHQUFHLElBQUk7QUFDaEQ7O0FBRUE7QUFDQSw4QkFBOEIsSUFBSTtBQUNsQztBQUNBOztBQUVBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLHdEQUF3RCxJQUFJO0FBQzVEOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQSxpQkFBaUIsRUFBRSxTQUFTLEVBQUUsTUFBTSxFQUFFO0FBQ3RDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU87QUFDUDtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBLDhCQUE4QixJQUFJO0FBQ2xDO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSxHQUFHO0FBQ0g7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLEdBQUc7QUFDSDtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSxRQUFRO0FBQ1I7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLFVBQVU7QUFDVjtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNB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QWUsQ0FBQ3pGLFNBQVMsRUFBRStGLGFBQWEsQ0FBQ3JHLElBQUksQ0FBQ21HLEtBQUssQ0FBQyxDQUFDO1FBQ2pFLElBQUlELElBQUksQ0FBQ3BHLE1BQU0sR0FBRyxDQUFDLEVBQUU7VUFDakIsT0FBT29HLElBQUk7UUFDZjtNQUNKO01BRUEsT0FBT3pILENBQUMsQ0FBQyxJQUFJLENBQUM7SUFDbEIsQ0FBQztJQUVEO0FBQ1I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0lBQ1E4SCxnQkFBZ0IsRUFBRSxTQUFBQSxDQUFVakcsU0FBUyxFQUFFSixPQUFPLEVBQUU7TUFDNUMsSUFBSUEsT0FBTyxDQUFDVSxJQUFJLENBQUM0RixPQUFPLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUU7UUFDbkMsT0FBT2xHLFNBQVMsQ0FBQzBELFVBQVUsQ0FBQzlELE9BQU8sQ0FBQ1UsSUFBSSxDQUFDLENBQUMwRSxHQUFHLENBQUMsVUFBVXpGLENBQUMsRUFBRTRHLENBQUMsRUFBRTtVQUMxRCxPQUFPbkcsU0FBUyxDQUFDeUMsWUFBWSxDQUFDMEQsQ0FBQyxDQUFDO1FBQ3BDLENBQUMsQ0FBQyxDQUFDQyxHQUFHLENBQUMsQ0FBQztNQUNaO01BRUEsT0FBT3BHLFNBQVMsQ0FBQ3lDLFlBQVksQ0FBQzdDLE9BQU8sQ0FBQztJQUMxQztFQUNKO0FBQ0osQ0FBQyxDQUFDLEMiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vLi9ub2RlX21vZHVsZXMvbG9jdXR1cy9waHAvYXJyYXkvYXJyYXlfZGlmZi5qcyIsIndlYnBhY2s6Ly8vLi9ub2RlX21vZHVsZXMvbG9jdXR1cy9waHAvZGF0ZXRpbWUvc3RydG90aW1lLmpzIiwid2VicGFjazovLy8uL25vZGVfbW9kdWxlcy9sb2N1dHVzL3BocC9pbmZvL2luaV9nZXQuanMiLCJ3ZWJwYWNrOi8vLy4vbm9kZV9tb2R1bGVzL2xvY3V0dXMvcGhwL3N0cmluZ3Mvc3RybGVuLmpzIiwid2VicGFjazovLy8uL25vZGVfbW9kdWxlcy9sb2N1dHVzL3BocC92YXIvaXNfbnVtZXJpYy5qcyIsIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vL3dlYnBhY2svcnVudGltZS9jb21wYXQgZ2V0IGRlZmF1bHQgZXhwb3J0Iiwid2VicGFjazovLy93ZWJwYWNrL3J1bnRpbWUvZGVmaW5lIHByb3BlcnR5IGdldHRlcnMiLCJ3ZWJwYWNrOi8vL3dlYnBhY2svcnVudGltZS9nbG9iYWwiLCJ3ZWJwYWNrOi8vL3dlYnBhY2svcnVudGltZS9oYXNPd25Qcm9wZXJ0eSBzaG9ydGhhbmQiLCJ3ZWJwYWNrOi8vL3dlYnBhY2svcnVudGltZS9tYWtlIG5hbWVzcGFjZSBvYmplY3QiLCJ3ZWJwYWNrOi8vLy4vcmVzb3VyY2VzL2Fzc2V0cy9qcy9oZWxwZXJzLmpzIl0sInNvdXJjZXNDb250ZW50IjpbIid1c2Ugc3RyaWN0JztcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiBhcnJheV9kaWZmKGFycjEpIHtcbiAgLy8gIGRpc2N1c3MgYXQ6IGh0dHBzOi8vbG9jdXR1cy5pby9waHAvYXJyYXlfZGlmZi9cbiAgLy8gb3JpZ2luYWwgYnk6IEtldmluIHZhbiBab25uZXZlbGQgKGh0dHBzOi8va3Z6LmlvKVxuICAvLyBpbXByb3ZlZCBieTogU2Fuam95IFJveVxuICAvLyAgcmV2aXNlZCBieTogQnJldHQgWmFtaXIgKGh0dHBzOi8vYnJldHQtemFtaXIubWUpXG4gIC8vICAgZXhhbXBsZSAxOiBhcnJheV9kaWZmKFsnS2V2aW4nLCAndmFuJywgJ1pvbm5ldmVsZCddLCBbJ3ZhbicsICdab25uZXZlbGQnXSlcbiAgLy8gICByZXR1cm5zIDE6IHswOidLZXZpbid9XG5cbiAgdmFyIHJldEFyciA9IHt9O1xuICB2YXIgYXJnbCA9IGFyZ3VtZW50cy5sZW5ndGg7XG4gIHZhciBrMSA9ICcnO1xuICB2YXIgaSA9IDE7XG4gIHZhciBrID0gJyc7XG4gIHZhciBhcnIgPSB7fTtcblxuICBhcnIxa2V5czogZm9yIChrMSBpbiBhcnIxKSB7XG4gICAgZm9yIChpID0gMTsgaSA8IGFyZ2w7IGkrKykge1xuICAgICAgYXJyID0gYXJndW1lbnRzW2ldO1xuICAgICAgZm9yIChrIGluIGFycikge1xuICAgICAgICBpZiAoYXJyW2tdID09PSBhcnIxW2sxXSkge1xuICAgICAgICAgIC8vIElmIGl0IHJlYWNoZXMgaGVyZSwgaXQgd2FzIGZvdW5kIGluIGF0IGxlYXN0IG9uZSBhcnJheSwgc28gdHJ5IG5leHQgdmFsdWVcbiAgICAgICAgICBjb250aW51ZSBhcnIxa2V5czsgLy8gZXNsaW50LWRpc2FibGUtbGluZSBuby1sYWJlbHNcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgcmV0QXJyW2sxXSA9IGFycjFbazFdO1xuICAgIH1cbiAgfVxuXG4gIHJldHVybiByZXRBcnI7XG59O1xuLy8jIHNvdXJjZU1hcHBpbmdVUkw9YXJyYXlfZGlmZi5qcy5tYXAiLCIndXNlIHN0cmljdCc7XG5cbnZhciByZVNwYWNlID0gJ1sgXFxcXHRdKyc7XG52YXIgcmVTcGFjZU9wdCA9ICdbIFxcXFx0XSonO1xudmFyIHJlTWVyaWRpYW4gPSAnKD86KFthcF0pXFxcXC4/bVxcXFwuPyhbXFxcXHQgXXwkKSknO1xudmFyIHJlSG91cjI0ID0gJygyWzAtNF18WzAxXT9bMC05XSknO1xudmFyIHJlSG91cjI0bHogPSAnKFswMV1bMC05XXwyWzAtNF0pJztcbnZhciByZUhvdXIxMiA9ICcoMD9bMS05XXwxWzAtMl0pJztcbnZhciByZU1pbnV0ZSA9ICcoWzAtNV0/WzAtOV0pJztcbnZhciByZU1pbnV0ZWx6ID0gJyhbMC01XVswLTldKSc7XG52YXIgcmVTZWNvbmQgPSAnKDYwfFswLTVdP1swLTldKSc7XG52YXIgcmVTZWNvbmRseiA9ICcoNjB8WzAtNV1bMC05XSknO1xudmFyIHJlRnJhYyA9ICcoPzpcXFxcLihbMC05XSspKSc7XG5cbnZhciByZURheWZ1bGwgPSAnc3VuZGF5fG1vbmRheXx0dWVzZGF5fHdlZG5lc2RheXx0aHVyc2RheXxmcmlkYXl8c2F0dXJkYXknO1xudmFyIHJlRGF5YWJiciA9ICdzdW58bW9ufHR1ZXx3ZWR8dGh1fGZyaXxzYXQnO1xudmFyIHJlRGF5dGV4dCA9IHJlRGF5ZnVsbCArICd8JyArIHJlRGF5YWJiciArICd8d2Vla2RheXM/JztcblxudmFyIHJlUmVsdGV4dG51bWJlciA9ICdmaXJzdHxzZWNvbmR8dGhpcmR8Zm91cnRofGZpZnRofHNpeHRofHNldmVudGh8ZWlnaHRoP3xuaW50aHx0ZW50aHxlbGV2ZW50aHx0d2VsZnRoJztcbnZhciByZVJlbHRleHR0ZXh0ID0gJ25leHR8bGFzdHxwcmV2aW91c3x0aGlzJztcbnZhciByZVJlbHRleHR1bml0ID0gJyg/OnNlY29uZHxzZWN8bWludXRlfG1pbnxob3VyfGRheXxmb3J0bmlnaHR8Zm9ydGhuaWdodHxtb250aHx5ZWFyKXM/fHdlZWtzfCcgKyByZURheXRleHQ7XG5cbnZhciByZVllYXIgPSAnKFswLTldezEsNH0pJztcbnZhciByZVllYXIyID0gJyhbMC05XXsyfSknO1xudmFyIHJlWWVhcjQgPSAnKFswLTldezR9KSc7XG52YXIgcmVZZWFyNHdpdGhTaWduID0gJyhbKy1dP1swLTldezR9KSc7XG52YXIgcmVNb250aCA9ICcoMVswLTJdfDA/WzAtOV0pJztcbnZhciByZU1vbnRobHogPSAnKDBbMC05XXwxWzAtMl0pJztcbnZhciByZURheSA9ICcoPzooM1swMV18WzAtMl0/WzAtOV0pKD86c3R8bmR8cmR8dGgpPyknO1xudmFyIHJlRGF5bHogPSAnKDBbMC05XXxbMS0yXVswLTldfDNbMDFdKSc7XG5cbnZhciByZU1vbnRoRnVsbCA9ICdqYW51YXJ5fGZlYnJ1YXJ5fG1hcmNofGFwcmlsfG1heXxqdW5lfGp1bHl8YXVndXN0fHNlcHRlbWJlcnxvY3RvYmVyfG5vdmVtYmVyfGRlY2VtYmVyJztcbnZhciByZU1vbnRoQWJiciA9ICdqYW58ZmVifG1hcnxhcHJ8bWF5fGp1bnxqdWx8YXVnfHNlcHQ/fG9jdHxub3Z8ZGVjJztcbnZhciByZU1vbnRocm9tYW4gPSAnaVt2eF18dml7MCwzfXx4aXswLDJ9fGl7MSwzfSc7XG52YXIgcmVNb250aFRleHQgPSAnKCcgKyByZU1vbnRoRnVsbCArICd8JyArIHJlTW9udGhBYmJyICsgJ3wnICsgcmVNb250aHJvbWFuICsgJyknO1xuXG52YXIgcmVUekNvcnJlY3Rpb24gPSAnKCg/OkdNVCk/KFsrLV0pJyArIHJlSG91cjI0ICsgJzo/JyArIHJlTWludXRlICsgJz8pJztcbnZhciByZVR6QWJiciA9ICdcXFxcKD8oW2EtekEtWl17MSw2fSlcXFxcKT8nO1xudmFyIHJlRGF5T2ZZZWFyID0gJygwMFsxLTldfDBbMS05XVswLTldfFsxMl1bMC05XVswLTldfDNbMC01XVswLTldfDM2WzAtNl0pJztcbnZhciByZVdlZWtPZlllYXIgPSAnKDBbMS05XXxbMS00XVswLTldfDVbMC0zXSknO1xuXG52YXIgcmVEYXRlTm9ZZWFyID0gcmVNb250aFRleHQgKyAnWyAuXFxcXHQtXSonICsgcmVEYXkgKyAnWywuc3RuZHJoXFxcXHQgXSonO1xuXG5mdW5jdGlvbiBwcm9jZXNzTWVyaWRpYW4oaG91ciwgbWVyaWRpYW4pIHtcbiAgbWVyaWRpYW4gPSBtZXJpZGlhbiAmJiBtZXJpZGlhbi50b0xvd2VyQ2FzZSgpO1xuXG4gIHN3aXRjaCAobWVyaWRpYW4pIHtcbiAgICBjYXNlICdhJzpcbiAgICAgIGhvdXIgKz0gaG91ciA9PT0gMTIgPyAtMTIgOiAwO1xuICAgICAgYnJlYWs7XG4gICAgY2FzZSAncCc6XG4gICAgICBob3VyICs9IGhvdXIgIT09IDEyID8gMTIgOiAwO1xuICAgICAgYnJlYWs7XG4gIH1cblxuICByZXR1cm4gaG91cjtcbn1cblxuZnVuY3Rpb24gcHJvY2Vzc1llYXIoeWVhclN0cikge1xuICB2YXIgeWVhciA9ICt5ZWFyU3RyO1xuXG4gIGlmICh5ZWFyU3RyLmxlbmd0aCA8IDQgJiYgeWVhciA8IDEwMCkge1xuICAgIHllYXIgKz0geWVhciA8IDcwID8gMjAwMCA6IDE5MDA7XG4gIH1cblxuICByZXR1cm4geWVhcjtcbn1cblxuZnVuY3Rpb24gbG9va3VwTW9udGgobW9udGhTdHIpIHtcbiAgcmV0dXJuIHtcbiAgICBqYW46IDAsXG4gICAgamFudWFyeTogMCxcbiAgICBpOiAwLFxuICAgIGZlYjogMSxcbiAgICBmZWJydWFyeTogMSxcbiAgICBpaTogMSxcbiAgICBtYXI6IDIsXG4gICAgbWFyY2g6IDIsXG4gICAgaWlpOiAyLFxuICAgIGFwcjogMyxcbiAgICBhcHJpbDogMyxcbiAgICBpdjogMyxcbiAgICBtYXk6IDQsXG4gICAgdjogNCxcbiAgICBqdW46IDUsXG4gICAganVuZTogNSxcbiAgICB2aTogNSxcbiAgICBqdWw6IDYsXG4gICAganVseTogNixcbiAgICB2aWk6IDYsXG4gICAgYXVnOiA3LFxuICAgIGF1Z3VzdDogNyxcbiAgICB2aWlpOiA3LFxuICAgIHNlcDogOCxcbiAgICBzZXB0OiA4LFxuICAgIHNlcHRlbWJlcjogOCxcbiAgICBpeDogOCxcbiAgICBvY3Q6IDksXG4gICAgb2N0b2JlcjogOSxcbiAgICB4OiA5LFxuICAgIG5vdjogMTAsXG4gICAgbm92ZW1iZXI6IDEwLFxuICAgIHhpOiAxMCxcbiAgICBkZWM6IDExLFxuICAgIGRlY2VtYmVyOiAxMSxcbiAgICB4aWk6IDExXG4gIH1bbW9udGhTdHIudG9Mb3dlckNhc2UoKV07XG59XG5cbmZ1bmN0aW9uIGxvb2t1cFdlZWtkYXkoZGF5U3RyKSB7XG4gIHZhciBkZXNpcmVkU3VuZGF5TnVtYmVyID0gYXJndW1lbnRzLmxlbmd0aCA+IDEgJiYgYXJndW1lbnRzWzFdICE9PSB1bmRlZmluZWQgPyBhcmd1bWVudHNbMV0gOiAwO1xuXG4gIHZhciBkYXlOdW1iZXJzID0ge1xuICAgIG1vbjogMSxcbiAgICBtb25kYXk6IDEsXG4gICAgdHVlOiAyLFxuICAgIHR1ZXNkYXk6IDIsXG4gICAgd2VkOiAzLFxuICAgIHdlZG5lc2RheTogMyxcbiAgICB0aHU6IDQsXG4gICAgdGh1cnNkYXk6IDQsXG4gICAgZnJpOiA1LFxuICAgIGZyaWRheTogNSxcbiAgICBzYXQ6IDYsXG4gICAgc2F0dXJkYXk6IDYsXG4gICAgc3VuOiAwLFxuICAgIHN1bmRheTogMFxuICB9O1xuXG4gIHJldHVybiBkYXlOdW1iZXJzW2RheVN0ci50b0xvd2VyQ2FzZSgpXSB8fCBkZXNpcmVkU3VuZGF5TnVtYmVyO1xufVxuXG5mdW5jdGlvbiBsb29rdXBSZWxhdGl2ZShyZWxUZXh0KSB7XG4gIHZhciByZWxhdGl2ZU51bWJlcnMgPSB7XG4gICAgbGFzdDogLTEsXG4gICAgcHJldmlvdXM6IC0xLFxuICAgIHRoaXM6IDAsXG4gICAgZmlyc3Q6IDEsXG4gICAgbmV4dDogMSxcbiAgICBzZWNvbmQ6IDIsXG4gICAgdGhpcmQ6IDMsXG4gICAgZm91cnRoOiA0LFxuICAgIGZpZnRoOiA1LFxuICAgIHNpeHRoOiA2LFxuICAgIHNldmVudGg6IDcsXG4gICAgZWlnaHQ6IDgsXG4gICAgZWlnaHRoOiA4LFxuICAgIG5pbnRoOiA5LFxuICAgIHRlbnRoOiAxMCxcbiAgICBlbGV2ZW50aDogMTEsXG4gICAgdHdlbGZ0aDogMTJcbiAgfTtcblxuICB2YXIgcmVsYXRpdmVCZWhhdmlvciA9IHtcbiAgICB0aGlzOiAxXG4gIH07XG5cbiAgdmFyIHJlbFRleHRMb3dlciA9IHJlbFRleHQudG9Mb3dlckNhc2UoKTtcblxuICByZXR1cm4ge1xuICAgIGFtb3VudDogcmVsYXRpdmVOdW1iZXJzW3JlbFRleHRMb3dlcl0sXG4gICAgYmVoYXZpb3I6IHJlbGF0aXZlQmVoYXZpb3JbcmVsVGV4dExvd2VyXSB8fCAwXG4gIH07XG59XG5cbmZ1bmN0aW9uIHByb2Nlc3NUekNvcnJlY3Rpb24odHpPZmZzZXQsIG9sZFZhbHVlKSB7XG4gIHZhciByZVR6Q29ycmVjdGlvbkxvb3NlID0gLyg/OkdNVCk/KFsrLV0pKFxcZCspKDo/KShcXGR7MCwyfSkvaTtcbiAgdHpPZmZzZXQgPSB0ek9mZnNldCAmJiB0ek9mZnNldC5tYXRjaChyZVR6Q29ycmVjdGlvbkxvb3NlKTtcblxuICBpZiAoIXR6T2Zmc2V0KSB7XG4gICAgcmV0dXJuIG9sZFZhbHVlO1xuICB9XG5cbiAgdmFyIHNpZ24gPSB0ek9mZnNldFsxXSA9PT0gJy0nID8gLTEgOiAxO1xuICB2YXIgaG91cnMgPSArdHpPZmZzZXRbMl07XG4gIHZhciBtaW51dGVzID0gK3R6T2Zmc2V0WzRdO1xuXG4gIGlmICghdHpPZmZzZXRbNF0gJiYgIXR6T2Zmc2V0WzNdKSB7XG4gICAgbWludXRlcyA9IE1hdGguZmxvb3IoaG91cnMgJSAxMDApO1xuICAgIGhvdXJzID0gTWF0aC5mbG9vcihob3VycyAvIDEwMCk7XG4gIH1cblxuICAvLyB0aW1lem9uZSBvZmZzZXQgaW4gc2Vjb25kc1xuICByZXR1cm4gc2lnbiAqIChob3VycyAqIDYwICsgbWludXRlcykgKiA2MDtcbn1cblxuLy8gdHogYWJicmV2YXRpb24gOiB0eiBvZmZzZXQgaW4gc2Vjb25kc1xudmFyIHR6QWJick9mZnNldHMgPSB7XG4gIGFjZHQ6IDM3ODAwLFxuICBhY3N0OiAzNDIwMCxcbiAgYWRkdDogLTcyMDAsXG4gIGFkdDogLTEwODAwLFxuICBhZWR0OiAzOTYwMCxcbiAgYWVzdDogMzYwMDAsXG4gIGFoZHQ6IC0zMjQwMCxcbiAgYWhzdDogLTM2MDAwLFxuICBha2R0OiAtMjg4MDAsXG4gIGFrc3Q6IC0zMjQwMCxcbiAgYW10OiAtMTM4NDAsXG4gIGFwdDogLTEwODAwLFxuICBhc3Q6IC0xNDQwMCxcbiAgYXdkdDogMzI0MDAsXG4gIGF3c3Q6IDI4ODAwLFxuICBhd3Q6IC0xMDgwMCxcbiAgYmRzdDogNzIwMCxcbiAgYmR0OiAtMzYwMDAsXG4gIGJtdDogLTE0MzA5LFxuICBic3Q6IDM2MDAsXG4gIGNhc3Q6IDM0MjAwLFxuICBjYXQ6IDcyMDAsXG4gIGNkZHQ6IC0xNDQwMCxcbiAgY2R0OiAtMTgwMDAsXG4gIGNlbXQ6IDEwODAwLFxuICBjZXN0OiA3MjAwLFxuICBjZXQ6IDM2MDAsXG4gIGNtdDogLTE1NDA4LFxuICBjcHQ6IC0xODAwMCxcbiAgY3N0OiAtMjE2MDAsXG4gIGN3dDogLTE4MDAwLFxuICBjaHN0OiAzNjAwMCxcbiAgZG10OiAtMTUyMSxcbiAgZWF0OiAxMDgwMCxcbiAgZWRkdDogLTEwODAwLFxuICBlZHQ6IC0xNDQwMCxcbiAgZWVzdDogMTA4MDAsXG4gIGVldDogNzIwMCxcbiAgZW10OiAtMjYyNDgsXG4gIGVwdDogLTE0NDAwLFxuICBlc3Q6IC0xODAwMCxcbiAgZXd0OiAtMTQ0MDAsXG4gIGZmbXQ6IC0xNDY2MCxcbiAgZm10OiAtNDA1NixcbiAgZ2R0OiAzOTYwMCxcbiAgZ210OiAwLFxuICBnc3Q6IDM2MDAwLFxuICBoZHQ6IC0zNDIwMCxcbiAgaGtzdDogMzI0MDAsXG4gIGhrdDogMjg4MDAsXG4gIGhtdDogLTE5Nzc2LFxuICBocHQ6IC0zNDIwMCxcbiAgaHN0OiAtMzYwMDAsXG4gIGh3dDogLTM0MjAwLFxuICBpZGR0OiAxNDQwMCxcbiAgaWR0OiAxMDgwMCxcbiAgaW10OiAyNTAyNSxcbiAgaXN0OiA3MjAwLFxuICBqZHQ6IDM2MDAwLFxuICBqbXQ6IDg0NDAsXG4gIGpzdDogMzI0MDAsXG4gIGtkdDogMzYwMDAsXG4gIGttdDogNTczNixcbiAga3N0OiAzMDYwMCxcbiAgbHN0OiA5Mzk0LFxuICBtZGR0OiAtMTgwMDAsXG4gIG1kc3Q6IDE2Mjc5LFxuICBtZHQ6IC0yMTYwMCxcbiAgbWVzdDogNzIwMCxcbiAgbWV0OiAzNjAwLFxuICBtbXQ6IDkwMTcsXG4gIG1wdDogLTIxNjAwLFxuICBtc2Q6IDE0NDAwLFxuICBtc2s6IDEwODAwLFxuICBtc3Q6IC0yNTIwMCxcbiAgbXd0OiAtMjE2MDAsXG4gIG5kZHQ6IC01NDAwLFxuICBuZHQ6IC05MDUyLFxuICBucHQ6IC05MDAwLFxuICBuc3Q6IC0xMjYwMCxcbiAgbnd0OiAtOTAwMCxcbiAgbnpkdDogNDY4MDAsXG4gIG56bXQ6IDQxNDAwLFxuICBuenN0OiA0MzIwMCxcbiAgcGRkdDogLTIxNjAwLFxuICBwZHQ6IC0yNTIwMCxcbiAgcGtzdDogMjE2MDAsXG4gIHBrdDogMTgwMDAsXG4gIHBsbXQ6IDI1NTkwLFxuICBwbXQ6IC0xMzIzNixcbiAgcHBtdDogLTE3MzQwLFxuICBwcHQ6IC0yNTIwMCxcbiAgcHN0OiAtMjg4MDAsXG4gIHB3dDogLTI1MjAwLFxuICBxbXQ6IC0xODg0MCxcbiAgcm10OiA1Nzk0LFxuICBzYXN0OiA3MjAwLFxuICBzZG10OiAtMTY4MDAsXG4gIHNqbXQ6IC0yMDE3MyxcbiAgc210OiAtMTM4ODQsXG4gIHNzdDogLTM5NjAwLFxuICB0Ym10OiAxMDc1MSxcbiAgdG10OiAxMjM0NCxcbiAgdWN0OiAwLFxuICB1dGM6IDAsXG4gIHdhc3Q6IDcyMDAsXG4gIHdhdDogMzYwMCxcbiAgd2VtdDogNzIwMCxcbiAgd2VzdDogMzYwMCxcbiAgd2V0OiAwLFxuICB3aWI6IDI1MjAwLFxuICB3aXRhOiAyODgwMCxcbiAgd2l0OiAzMjQwMCxcbiAgd210OiA1MDQwLFxuICB5ZGR0OiAtMjUyMDAsXG4gIHlkdDogLTI4ODAwLFxuICB5cHQ6IC0yODgwMCxcbiAgeXN0OiAtMzI0MDAsXG4gIHl3dDogLTI4ODAwLFxuICBhOiAzNjAwLFxuICBiOiA3MjAwLFxuICBjOiAxMDgwMCxcbiAgZDogMTQ0MDAsXG4gIGU6IDE4MDAwLFxuICBmOiAyMTYwMCxcbiAgZzogMjUyMDAsXG4gIGg6IDI4ODAwLFxuICBpOiAzMjQwMCxcbiAgazogMzYwMDAsXG4gIGw6IDM5NjAwLFxuICBtOiA0MzIwMCxcbiAgbjogLTM2MDAsXG4gIG86IC03MjAwLFxuICBwOiAtMTA4MDAsXG4gIHE6IC0xNDQwMCxcbiAgcjogLTE4MDAwLFxuICBzOiAtMjE2MDAsXG4gIHQ6IC0yNTIwMCxcbiAgdTogLTI4ODAwLFxuICB2OiAtMzI0MDAsXG4gIHc6IC0zNjAwMCxcbiAgeDogLTM5NjAwLFxuICB5OiAtNDMyMDAsXG4gIHo6IDBcbn07XG5cbnZhciBmb3JtYXRzID0ge1xuICB5ZXN0ZXJkYXk6IHtcbiAgICByZWdleDogL155ZXN0ZXJkYXkvaSxcbiAgICBuYW1lOiAneWVzdGVyZGF5JyxcbiAgICBjYWxsYmFjazogZnVuY3Rpb24gY2FsbGJhY2soKSB7XG4gICAgICB0aGlzLnJkIC09IDE7XG4gICAgICByZXR1cm4gdGhpcy5yZXNldFRpbWUoKTtcbiAgICB9XG4gIH0sXG5cbiAgbm93OiB7XG4gICAgcmVnZXg6IC9ebm93L2ksXG4gICAgbmFtZTogJ25vdydcbiAgICAvLyBkbyBub3RoaW5nXG4gIH0sXG5cbiAgbm9vbjoge1xuICAgIHJlZ2V4OiAvXm5vb24vaSxcbiAgICBuYW1lOiAnbm9vbicsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKCkge1xuICAgICAgcmV0dXJuIHRoaXMucmVzZXRUaW1lKCkgJiYgdGhpcy50aW1lKDEyLCAwLCAwLCAwKTtcbiAgICB9XG4gIH0sXG5cbiAgbWlkbmlnaHRPclRvZGF5OiB7XG4gICAgcmVnZXg6IC9eKG1pZG5pZ2h0fHRvZGF5KS9pLFxuICAgIG5hbWU6ICdtaWRuaWdodCB8IHRvZGF5JyxcbiAgICBjYWxsYmFjazogZnVuY3Rpb24gY2FsbGJhY2soKSB7XG4gICAgICByZXR1cm4gdGhpcy5yZXNldFRpbWUoKTtcbiAgICB9XG4gIH0sXG5cbiAgdG9tb3Jyb3c6IHtcbiAgICByZWdleDogL150b21vcnJvdy9pLFxuICAgIG5hbWU6ICd0b21vcnJvdycsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKCkge1xuICAgICAgdGhpcy5yZCArPSAxO1xuICAgICAgcmV0dXJuIHRoaXMucmVzZXRUaW1lKCk7XG4gICAgfVxuICB9LFxuXG4gIHRpbWVzdGFtcDoge1xuICAgIHJlZ2V4OiAvXkAoLT9cXGQrKS9pLFxuICAgIG5hbWU6ICd0aW1lc3RhbXAnLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgdGltZXN0YW1wKSB7XG4gICAgICB0aGlzLnJzICs9ICt0aW1lc3RhbXA7XG4gICAgICB0aGlzLnkgPSAxOTcwO1xuICAgICAgdGhpcy5tID0gMDtcbiAgICAgIHRoaXMuZCA9IDE7XG4gICAgICB0aGlzLmRhdGVzID0gMDtcblxuICAgICAgcmV0dXJuIHRoaXMucmVzZXRUaW1lKCkgJiYgdGhpcy56b25lKDApO1xuICAgIH1cbiAgfSxcblxuICBmaXJzdE9yTGFzdERheToge1xuICAgIHJlZ2V4OiAvXihmaXJzdHxsYXN0KSBkYXkgb2YvaSxcbiAgICBuYW1lOiAnZmlyc3RkYXlvZiB8IGxhc3RkYXlvZicsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCBkYXkpIHtcbiAgICAgIGlmIChkYXkudG9Mb3dlckNhc2UoKSA9PT0gJ2ZpcnN0Jykge1xuICAgICAgICB0aGlzLmZpcnN0T3JMYXN0RGF5T2ZNb250aCA9IDE7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLmZpcnN0T3JMYXN0RGF5T2ZNb250aCA9IC0xO1xuICAgICAgfVxuICAgIH1cbiAgfSxcblxuICBiYWNrT3JGcm9udE9mOiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXihiYWNrfGZyb250KSBvZiAnICsgcmVIb3VyMjQgKyByZVNwYWNlT3B0ICsgcmVNZXJpZGlhbiArICc/JywgJ2knKSxcbiAgICBuYW1lOiAnYmFja29mIHwgZnJvbnRvZicsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCBzaWRlLCBob3VycywgbWVyaWRpYW4pIHtcbiAgICAgIHZhciBiYWNrID0gc2lkZS50b0xvd2VyQ2FzZSgpID09PSAnYmFjayc7XG4gICAgICB2YXIgaG91ciA9ICtob3VycztcbiAgICAgIHZhciBtaW51dGUgPSAxNTtcblxuICAgICAgaWYgKCFiYWNrKSB7XG4gICAgICAgIGhvdXIgLT0gMTtcbiAgICAgICAgbWludXRlID0gNDU7XG4gICAgICB9XG5cbiAgICAgIGhvdXIgPSBwcm9jZXNzTWVyaWRpYW4oaG91ciwgbWVyaWRpYW4pO1xuXG4gICAgICByZXR1cm4gdGhpcy5yZXNldFRpbWUoKSAmJiB0aGlzLnRpbWUoaG91ciwgbWludXRlLCAwLCAwKTtcbiAgICB9XG4gIH0sXG5cbiAgd2Vla2RheU9mOiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXignICsgcmVSZWx0ZXh0bnVtYmVyICsgJ3wnICsgcmVSZWx0ZXh0dGV4dCArICcpJyArIHJlU3BhY2UgKyAnKCcgKyByZURheWZ1bGwgKyAnfCcgKyByZURheWFiYnIgKyAnKScgKyByZVNwYWNlICsgJ29mJywgJ2knKSxcbiAgICBuYW1lOiAnd2Vla2RheW9mJ1xuICAgIC8vIHRvZG9cbiAgfSxcblxuICBtc3NxbHRpbWU6IHtcbiAgICByZWdleDogUmVnRXhwKCdeJyArIHJlSG91cjEyICsgJzonICsgcmVNaW51dGVseiArICc6JyArIHJlU2Vjb25kbHogKyAnWzouXShbMC05XSspJyArIHJlTWVyaWRpYW4sICdpJyksXG4gICAgbmFtZTogJ21zc3FsdGltZScsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCBob3VyLCBtaW51dGUsIHNlY29uZCwgZnJhYywgbWVyaWRpYW4pIHtcbiAgICAgIHJldHVybiB0aGlzLnRpbWUocHJvY2Vzc01lcmlkaWFuKCtob3VyLCBtZXJpZGlhbiksICttaW51dGUsICtzZWNvbmQsICtmcmFjLnN1YnN0cigwLCAzKSk7XG4gICAgfVxuICB9LFxuXG4gIG9yYWNsZWRhdGU6IHtcbiAgICByZWdleDogL14oXFxkezJ9KS0oW0EtWl17M30pLShcXGR7Mn0pJC9pLFxuICAgIG5hbWU6ICdkLU0teScsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCBkYXksIG1vbnRoVGV4dCwgeWVhcikge1xuICAgICAgdmFyIG1vbnRoID0ge1xuICAgICAgICBKQU46IDAsXG4gICAgICAgIEZFQjogMSxcbiAgICAgICAgTUFSOiAyLFxuICAgICAgICBBUFI6IDMsXG4gICAgICAgIE1BWTogNCxcbiAgICAgICAgSlVOOiA1LFxuICAgICAgICBKVUw6IDYsXG4gICAgICAgIEFVRzogNyxcbiAgICAgICAgU0VQOiA4LFxuICAgICAgICBPQ1Q6IDksXG4gICAgICAgIE5PVjogMTAsXG4gICAgICAgIERFQzogMTFcbiAgICAgIH1bbW9udGhUZXh0LnRvVXBwZXJDYXNlKCldO1xuICAgICAgcmV0dXJuIHRoaXMueW1kKDIwMDAgKyBwYXJzZUludCh5ZWFyLCAxMCksIG1vbnRoLCBwYXJzZUludChkYXksIDEwKSk7XG4gICAgfVxuICB9LFxuXG4gIHRpbWVMb25nMTI6IHtcbiAgICByZWdleDogUmVnRXhwKCdeJyArIHJlSG91cjEyICsgJ1s6Ll0nICsgcmVNaW51dGUgKyAnWzouXScgKyByZVNlY29uZGx6ICsgcmVTcGFjZU9wdCArIHJlTWVyaWRpYW4sICdpJyksXG4gICAgbmFtZTogJ3RpbWVsb25nMTInLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgaG91ciwgbWludXRlLCBzZWNvbmQsIG1lcmlkaWFuKSB7XG4gICAgICByZXR1cm4gdGhpcy50aW1lKHByb2Nlc3NNZXJpZGlhbigraG91ciwgbWVyaWRpYW4pLCArbWludXRlLCArc2Vjb25kLCAwKTtcbiAgICB9XG4gIH0sXG5cbiAgdGltZVNob3J0MTI6IHtcbiAgICByZWdleDogUmVnRXhwKCdeJyArIHJlSG91cjEyICsgJ1s6Ll0nICsgcmVNaW51dGVseiArIHJlU3BhY2VPcHQgKyByZU1lcmlkaWFuLCAnaScpLFxuICAgIG5hbWU6ICd0aW1lc2hvcnQxMicsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCBob3VyLCBtaW51dGUsIG1lcmlkaWFuKSB7XG4gICAgICByZXR1cm4gdGhpcy50aW1lKHByb2Nlc3NNZXJpZGlhbigraG91ciwgbWVyaWRpYW4pLCArbWludXRlLCAwLCAwKTtcbiAgICB9XG4gIH0sXG5cbiAgdGltZVRpbnkxMjoge1xuICAgIHJlZ2V4OiBSZWdFeHAoJ14nICsgcmVIb3VyMTIgKyByZVNwYWNlT3B0ICsgcmVNZXJpZGlhbiwgJ2knKSxcbiAgICBuYW1lOiAndGltZXRpbnkxMicsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCBob3VyLCBtZXJpZGlhbikge1xuICAgICAgcmV0dXJuIHRoaXMudGltZShwcm9jZXNzTWVyaWRpYW4oK2hvdXIsIG1lcmlkaWFuKSwgMCwgMCwgMCk7XG4gICAgfVxuICB9LFxuXG4gIHNvYXA6IHtcbiAgICByZWdleDogUmVnRXhwKCdeJyArIHJlWWVhcjQgKyAnLScgKyByZU1vbnRobHogKyAnLScgKyByZURheWx6ICsgJ1QnICsgcmVIb3VyMjRseiArICc6JyArIHJlTWludXRlbHogKyAnOicgKyByZVNlY29uZGx6ICsgcmVGcmFjICsgcmVUekNvcnJlY3Rpb24gKyAnPycsICdpJyksXG4gICAgbmFtZTogJ3NvYXAnLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgeWVhciwgbW9udGgsIGRheSwgaG91ciwgbWludXRlLCBzZWNvbmQsIGZyYWMsIHR6Q29ycmVjdGlvbikge1xuICAgICAgcmV0dXJuIHRoaXMueW1kKCt5ZWFyLCBtb250aCAtIDEsICtkYXkpICYmIHRoaXMudGltZSgraG91ciwgK21pbnV0ZSwgK3NlY29uZCwgK2ZyYWMuc3Vic3RyKDAsIDMpKSAmJiB0aGlzLnpvbmUocHJvY2Vzc1R6Q29ycmVjdGlvbih0ekNvcnJlY3Rpb24pKTtcbiAgICB9XG4gIH0sXG5cbiAgd2RkeDoge1xuICAgIHJlZ2V4OiBSZWdFeHAoJ14nICsgcmVZZWFyNCArICctJyArIHJlTW9udGggKyAnLScgKyByZURheSArICdUJyArIHJlSG91cjI0ICsgJzonICsgcmVNaW51dGUgKyAnOicgKyByZVNlY29uZCksXG4gICAgbmFtZTogJ3dkZHgnLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgeWVhciwgbW9udGgsIGRheSwgaG91ciwgbWludXRlLCBzZWNvbmQpIHtcbiAgICAgIHJldHVybiB0aGlzLnltZCgreWVhciwgbW9udGggLSAxLCArZGF5KSAmJiB0aGlzLnRpbWUoK2hvdXIsICttaW51dGUsICtzZWNvbmQsIDApO1xuICAgIH1cbiAgfSxcblxuICBleGlmOiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyByZVllYXI0ICsgJzonICsgcmVNb250aGx6ICsgJzonICsgcmVEYXlseiArICcgJyArIHJlSG91cjI0bHogKyAnOicgKyByZU1pbnV0ZWx6ICsgJzonICsgcmVTZWNvbmRseiwgJ2knKSxcbiAgICBuYW1lOiAnZXhpZicsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCB5ZWFyLCBtb250aCwgZGF5LCBob3VyLCBtaW51dGUsIHNlY29uZCkge1xuICAgICAgcmV0dXJuIHRoaXMueW1kKCt5ZWFyLCBtb250aCAtIDEsICtkYXkpICYmIHRoaXMudGltZSgraG91ciwgK21pbnV0ZSwgK3NlY29uZCwgMCk7XG4gICAgfVxuICB9LFxuXG4gIHhtbFJwYzoge1xuICAgIHJlZ2V4OiBSZWdFeHAoJ14nICsgcmVZZWFyNCArIHJlTW9udGhseiArIHJlRGF5bHogKyAnVCcgKyByZUhvdXIyNCArICc6JyArIHJlTWludXRlbHogKyAnOicgKyByZVNlY29uZGx6KSxcbiAgICBuYW1lOiAneG1scnBjJyxcbiAgICBjYWxsYmFjazogZnVuY3Rpb24gY2FsbGJhY2sobWF0Y2gsIHllYXIsIG1vbnRoLCBkYXksIGhvdXIsIG1pbnV0ZSwgc2Vjb25kKSB7XG4gICAgICByZXR1cm4gdGhpcy55bWQoK3llYXIsIG1vbnRoIC0gMSwgK2RheSkgJiYgdGhpcy50aW1lKCtob3VyLCArbWludXRlLCArc2Vjb25kLCAwKTtcbiAgICB9XG4gIH0sXG5cbiAgeG1sUnBjTm9Db2xvbjoge1xuICAgIHJlZ2V4OiBSZWdFeHAoJ14nICsgcmVZZWFyNCArIHJlTW9udGhseiArIHJlRGF5bHogKyAnW1R0XScgKyByZUhvdXIyNCArIHJlTWludXRlbHogKyByZVNlY29uZGx6KSxcbiAgICBuYW1lOiAneG1scnBjbm9jb2xvbicsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCB5ZWFyLCBtb250aCwgZGF5LCBob3VyLCBtaW51dGUsIHNlY29uZCkge1xuICAgICAgcmV0dXJuIHRoaXMueW1kKCt5ZWFyLCBtb250aCAtIDEsICtkYXkpICYmIHRoaXMudGltZSgraG91ciwgK21pbnV0ZSwgK3NlY29uZCwgMCk7XG4gICAgfVxuICB9LFxuXG4gIGNsZjoge1xuICAgIHJlZ2V4OiBSZWdFeHAoJ14nICsgcmVEYXkgKyAnLygnICsgcmVNb250aEFiYnIgKyAnKS8nICsgcmVZZWFyNCArICc6JyArIHJlSG91cjI0bHogKyAnOicgKyByZU1pbnV0ZWx6ICsgJzonICsgcmVTZWNvbmRseiArIHJlU3BhY2UgKyByZVR6Q29ycmVjdGlvbiwgJ2knKSxcbiAgICBuYW1lOiAnY2xmJyxcbiAgICBjYWxsYmFjazogZnVuY3Rpb24gY2FsbGJhY2sobWF0Y2gsIGRheSwgbW9udGgsIHllYXIsIGhvdXIsIG1pbnV0ZSwgc2Vjb25kLCB0ekNvcnJlY3Rpb24pIHtcbiAgICAgIHJldHVybiB0aGlzLnltZCgreWVhciwgbG9va3VwTW9udGgobW9udGgpLCArZGF5KSAmJiB0aGlzLnRpbWUoK2hvdXIsICttaW51dGUsICtzZWNvbmQsIDApICYmIHRoaXMuem9uZShwcm9jZXNzVHpDb3JyZWN0aW9uKHR6Q29ycmVjdGlvbikpO1xuICAgIH1cbiAgfSxcblxuICBpc284NjAxbG9uZzoge1xuICAgIHJlZ2V4OiBSZWdFeHAoJ150PycgKyByZUhvdXIyNCArICdbOi5dJyArIHJlTWludXRlICsgJ1s6Ll0nICsgcmVTZWNvbmQgKyByZUZyYWMsICdpJyksXG4gICAgbmFtZTogJ2lzbzg2MDFsb25nJyxcbiAgICBjYWxsYmFjazogZnVuY3Rpb24gY2FsbGJhY2sobWF0Y2gsIGhvdXIsIG1pbnV0ZSwgc2Vjb25kLCBmcmFjKSB7XG4gICAgICByZXR1cm4gdGhpcy50aW1lKCtob3VyLCArbWludXRlLCArc2Vjb25kLCArZnJhYy5zdWJzdHIoMCwgMykpO1xuICAgIH1cbiAgfSxcblxuICBkYXRlVGV4dHVhbDoge1xuICAgIHJlZ2V4OiBSZWdFeHAoJ14nICsgcmVNb250aFRleHQgKyAnWyAuXFxcXHQtXSonICsgcmVEYXkgKyAnWywuc3RuZHJoXFxcXHQgXSsnICsgcmVZZWFyLCAnaScpLFxuICAgIG5hbWU6ICdkYXRldGV4dHVhbCcsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCBtb250aCwgZGF5LCB5ZWFyKSB7XG4gICAgICByZXR1cm4gdGhpcy55bWQocHJvY2Vzc1llYXIoeWVhciksIGxvb2t1cE1vbnRoKG1vbnRoKSwgK2RheSk7XG4gICAgfVxuICB9LFxuXG4gIHBvaW50ZWREYXRlNDoge1xuICAgIHJlZ2V4OiBSZWdFeHAoJ14nICsgcmVEYXkgKyAnWy5cXFxcdC1dJyArIHJlTW9udGggKyAnWy4tXScgKyByZVllYXI0KSxcbiAgICBuYW1lOiAncG9pbnRlZGRhdGU0JyxcbiAgICBjYWxsYmFjazogZnVuY3Rpb24gY2FsbGJhY2sobWF0Y2gsIGRheSwgbW9udGgsIHllYXIpIHtcbiAgICAgIHJldHVybiB0aGlzLnltZCgreWVhciwgbW9udGggLSAxLCArZGF5KTtcbiAgICB9XG4gIH0sXG5cbiAgcG9pbnRlZERhdGUyOiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyByZURheSArICdbLlxcXFx0XScgKyByZU1vbnRoICsgJ1xcXFwuJyArIHJlWWVhcjIpLFxuICAgIG5hbWU6ICdwb2ludGVkZGF0ZTInLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgZGF5LCBtb250aCwgeWVhcikge1xuICAgICAgcmV0dXJuIHRoaXMueW1kKHByb2Nlc3NZZWFyKHllYXIpLCBtb250aCAtIDEsICtkYXkpO1xuICAgIH1cbiAgfSxcblxuICB0aW1lTG9uZzI0OiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXnQ/JyArIHJlSG91cjI0ICsgJ1s6Ll0nICsgcmVNaW51dGUgKyAnWzouXScgKyByZVNlY29uZCksXG4gICAgbmFtZTogJ3RpbWVsb25nMjQnLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgaG91ciwgbWludXRlLCBzZWNvbmQpIHtcbiAgICAgIHJldHVybiB0aGlzLnRpbWUoK2hvdXIsICttaW51dGUsICtzZWNvbmQsIDApO1xuICAgIH1cbiAgfSxcblxuICBkYXRlTm9Db2xvbjoge1xuICAgIHJlZ2V4OiBSZWdFeHAoJ14nICsgcmVZZWFyNCArIHJlTW9udGhseiArIHJlRGF5bHopLFxuICAgIG5hbWU6ICdkYXRlbm9jb2xvbicsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCB5ZWFyLCBtb250aCwgZGF5KSB7XG4gICAgICByZXR1cm4gdGhpcy55bWQoK3llYXIsIG1vbnRoIC0gMSwgK2RheSk7XG4gICAgfVxuICB9LFxuXG4gIHBneWRvdGQ6IHtcbiAgICByZWdleDogUmVnRXhwKCdeJyArIHJlWWVhcjQgKyAnXFxcXC4/JyArIHJlRGF5T2ZZZWFyKSxcbiAgICBuYW1lOiAncGd5ZG90ZCcsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCB5ZWFyLCBkYXkpIHtcbiAgICAgIHJldHVybiB0aGlzLnltZCgreWVhciwgMCwgK2RheSk7XG4gICAgfVxuICB9LFxuXG4gIHRpbWVTaG9ydDI0OiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXnQ/JyArIHJlSG91cjI0ICsgJ1s6Ll0nICsgcmVNaW51dGUsICdpJyksXG4gICAgbmFtZTogJ3RpbWVzaG9ydDI0JyxcbiAgICBjYWxsYmFjazogZnVuY3Rpb24gY2FsbGJhY2sobWF0Y2gsIGhvdXIsIG1pbnV0ZSkge1xuICAgICAgcmV0dXJuIHRoaXMudGltZSgraG91ciwgK21pbnV0ZSwgMCwgMCk7XG4gICAgfVxuICB9LFxuXG4gIGlzbzg2MDFub0NvbG9uOiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXnQ/JyArIHJlSG91cjI0bHogKyByZU1pbnV0ZWx6ICsgcmVTZWNvbmRseiwgJ2knKSxcbiAgICBuYW1lOiAnaXNvODYwMW5vY29sb24nLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgaG91ciwgbWludXRlLCBzZWNvbmQpIHtcbiAgICAgIHJldHVybiB0aGlzLnRpbWUoK2hvdXIsICttaW51dGUsICtzZWNvbmQsIDApO1xuICAgIH1cbiAgfSxcblxuICBpc284NjAxZGF0ZVNsYXNoOiB7XG4gICAgLy8gZXZlbnRob3VnaCB0aGUgdHJhaWxpbmcgc2xhc2ggaXMgb3B0aW9uYWwgaW4gUEhQXG4gICAgLy8gaGVyZSBpdCdzIG1hbmRhdG9yeSBhbmQgaW5wdXRzIHdpdGhvdXQgdGhlIHNsYXNoXG4gICAgLy8gYXJlIGhhbmRsZWQgYnkgZGF0ZXNsYXNoXG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyByZVllYXI0ICsgJy8nICsgcmVNb250aGx6ICsgJy8nICsgcmVEYXlseiArICcvJyksXG4gICAgbmFtZTogJ2lzbzg2MDFkYXRlc2xhc2gnLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgeWVhciwgbW9udGgsIGRheSkge1xuICAgICAgcmV0dXJuIHRoaXMueW1kKCt5ZWFyLCBtb250aCAtIDEsICtkYXkpO1xuICAgIH1cbiAgfSxcblxuICBkYXRlU2xhc2g6IHtcbiAgICByZWdleDogUmVnRXhwKCdeJyArIHJlWWVhcjQgKyAnLycgKyByZU1vbnRoICsgJy8nICsgcmVEYXkpLFxuICAgIG5hbWU6ICdkYXRlc2xhc2gnLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgeWVhciwgbW9udGgsIGRheSkge1xuICAgICAgcmV0dXJuIHRoaXMueW1kKCt5ZWFyLCBtb250aCAtIDEsICtkYXkpO1xuICAgIH1cbiAgfSxcblxuICBhbWVyaWNhbjoge1xuICAgIHJlZ2V4OiBSZWdFeHAoJ14nICsgcmVNb250aCArICcvJyArIHJlRGF5ICsgJy8nICsgcmVZZWFyKSxcbiAgICBuYW1lOiAnYW1lcmljYW4nLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgbW9udGgsIGRheSwgeWVhcikge1xuICAgICAgcmV0dXJuIHRoaXMueW1kKHByb2Nlc3NZZWFyKHllYXIpLCBtb250aCAtIDEsICtkYXkpO1xuICAgIH1cbiAgfSxcblxuICBhbWVyaWNhblNob3J0OiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyByZU1vbnRoICsgJy8nICsgcmVEYXkpLFxuICAgIG5hbWU6ICdhbWVyaWNhbnNob3J0JyxcbiAgICBjYWxsYmFjazogZnVuY3Rpb24gY2FsbGJhY2sobWF0Y2gsIG1vbnRoLCBkYXkpIHtcbiAgICAgIHJldHVybiB0aGlzLnltZCh0aGlzLnksIG1vbnRoIC0gMSwgK2RheSk7XG4gICAgfVxuICB9LFxuXG4gIGdudURhdGVTaG9ydE9ySXNvODYwMWRhdGUyOiB7XG4gICAgLy8gaXNvODYwMWRhdGUyIGlzIGNvbXBsZXRlIHN1YnNldCBvZiBnbnVkYXRlc2hvcnRcbiAgICByZWdleDogUmVnRXhwKCdeJyArIHJlWWVhciArICctJyArIHJlTW9udGggKyAnLScgKyByZURheSksXG4gICAgbmFtZTogJ2dudWRhdGVzaG9ydCB8IGlzbzg2MDFkYXRlMicsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCB5ZWFyLCBtb250aCwgZGF5KSB7XG4gICAgICByZXR1cm4gdGhpcy55bWQocHJvY2Vzc1llYXIoeWVhciksIG1vbnRoIC0gMSwgK2RheSk7XG4gICAgfVxuICB9LFxuXG4gIGlzbzg2MDFkYXRlNDoge1xuICAgIHJlZ2V4OiBSZWdFeHAoJ14nICsgcmVZZWFyNHdpdGhTaWduICsgJy0nICsgcmVNb250aGx6ICsgJy0nICsgcmVEYXlseiksXG4gICAgbmFtZTogJ2lzbzg2MDFkYXRlNCcsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCB5ZWFyLCBtb250aCwgZGF5KSB7XG4gICAgICByZXR1cm4gdGhpcy55bWQoK3llYXIsIG1vbnRoIC0gMSwgK2RheSk7XG4gICAgfVxuICB9LFxuXG4gIGdudU5vQ29sb246IHtcbiAgICByZWdleDogUmVnRXhwKCdedD8nICsgcmVIb3VyMjRseiArIHJlTWludXRlbHosICdpJyksXG4gICAgbmFtZTogJ2dudW5vY29sb24nLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgaG91ciwgbWludXRlKSB7XG4gICAgICAvLyB0aGlzIHJ1bGUgaXMgYSBzcGVjaWFsIGNhc2VcbiAgICAgIC8vIGlmIHRpbWUgd2FzIGFscmVhZHkgc2V0IG9uY2UgYnkgYW55IHByZWNlZGluZyBydWxlLCBpdCBzZXRzIHRoZSBjYXB0dXJlZCB2YWx1ZSBhcyB5ZWFyXG4gICAgICBzd2l0Y2ggKHRoaXMudGltZXMpIHtcbiAgICAgICAgY2FzZSAwOlxuICAgICAgICAgIHJldHVybiB0aGlzLnRpbWUoK2hvdXIsICttaW51dGUsIDAsIHRoaXMuZik7XG4gICAgICAgIGNhc2UgMTpcbiAgICAgICAgICB0aGlzLnkgPSBob3VyICogMTAwICsgK21pbnV0ZTtcbiAgICAgICAgICB0aGlzLnRpbWVzKys7XG5cbiAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICB9XG4gICAgfVxuICB9LFxuXG4gIGdudURhdGVTaG9ydGVyOiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyByZVllYXI0ICsgJy0nICsgcmVNb250aCksXG4gICAgbmFtZTogJ2dudWRhdGVzaG9ydGVyJyxcbiAgICBjYWxsYmFjazogZnVuY3Rpb24gY2FsbGJhY2sobWF0Y2gsIHllYXIsIG1vbnRoKSB7XG4gICAgICByZXR1cm4gdGhpcy55bWQoK3llYXIsIG1vbnRoIC0gMSwgMSk7XG4gICAgfVxuICB9LFxuXG4gIHBnVGV4dFJldmVyc2U6IHtcbiAgICAvLyBub3RlOiBhbGxvd2VkIHllYXJzIGFyZSBmcm9tIDMyLTk5OTlcbiAgICAvLyB5ZWFycyBiZWxvdyAzMiBzaG91bGQgYmUgdHJlYXRlZCBhcyBkYXlzIGluIGRhdGVmdWxsXG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyAnKFxcXFxkezMsNH18WzQtOV1cXFxcZHwzWzItOV0pLSgnICsgcmVNb250aEFiYnIgKyAnKS0nICsgcmVEYXlseiwgJ2knKSxcbiAgICBuYW1lOiAncGd0ZXh0cmV2ZXJzZScsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCB5ZWFyLCBtb250aCwgZGF5KSB7XG4gICAgICByZXR1cm4gdGhpcy55bWQocHJvY2Vzc1llYXIoeWVhciksIGxvb2t1cE1vbnRoKG1vbnRoKSwgK2RheSk7XG4gICAgfVxuICB9LFxuXG4gIGRhdGVGdWxsOiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyByZURheSArICdbIFxcXFx0Li1dKicgKyByZU1vbnRoVGV4dCArICdbIFxcXFx0Li1dKicgKyByZVllYXIsICdpJyksXG4gICAgbmFtZTogJ2RhdGVmdWxsJyxcbiAgICBjYWxsYmFjazogZnVuY3Rpb24gY2FsbGJhY2sobWF0Y2gsIGRheSwgbW9udGgsIHllYXIpIHtcbiAgICAgIHJldHVybiB0aGlzLnltZChwcm9jZXNzWWVhcih5ZWFyKSwgbG9va3VwTW9udGgobW9udGgpLCArZGF5KTtcbiAgICB9XG4gIH0sXG5cbiAgZGF0ZU5vRGF5OiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyByZU1vbnRoVGV4dCArICdbIC5cXFxcdC1dKicgKyByZVllYXI0LCAnaScpLFxuICAgIG5hbWU6ICdkYXRlbm9kYXknLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgbW9udGgsIHllYXIpIHtcbiAgICAgIHJldHVybiB0aGlzLnltZCgreWVhciwgbG9va3VwTW9udGgobW9udGgpLCAxKTtcbiAgICB9XG4gIH0sXG5cbiAgZGF0ZU5vRGF5UmV2OiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyByZVllYXI0ICsgJ1sgLlxcXFx0LV0qJyArIHJlTW9udGhUZXh0LCAnaScpLFxuICAgIG5hbWU6ICdkYXRlbm9kYXlyZXYnLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgeWVhciwgbW9udGgpIHtcbiAgICAgIHJldHVybiB0aGlzLnltZCgreWVhciwgbG9va3VwTW9udGgobW9udGgpLCAxKTtcbiAgICB9XG4gIH0sXG5cbiAgcGdUZXh0U2hvcnQ6IHtcbiAgICByZWdleDogUmVnRXhwKCdeKCcgKyByZU1vbnRoQWJiciArICcpLScgKyByZURheWx6ICsgJy0nICsgcmVZZWFyLCAnaScpLFxuICAgIG5hbWU6ICdwZ3RleHRzaG9ydCcsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCBtb250aCwgZGF5LCB5ZWFyKSB7XG4gICAgICByZXR1cm4gdGhpcy55bWQocHJvY2Vzc1llYXIoeWVhciksIGxvb2t1cE1vbnRoKG1vbnRoKSwgK2RheSk7XG4gICAgfVxuICB9LFxuXG4gIGRhdGVOb1llYXI6IHtcbiAgICByZWdleDogUmVnRXhwKCdeJyArIHJlRGF0ZU5vWWVhciwgJ2knKSxcbiAgICBuYW1lOiAnZGF0ZW5veWVhcicsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCBtb250aCwgZGF5KSB7XG4gICAgICByZXR1cm4gdGhpcy55bWQodGhpcy55LCBsb29rdXBNb250aChtb250aCksICtkYXkpO1xuICAgIH1cbiAgfSxcblxuICBkYXRlTm9ZZWFyUmV2OiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyByZURheSArICdbIC5cXFxcdC1dKicgKyByZU1vbnRoVGV4dCwgJ2knKSxcbiAgICBuYW1lOiAnZGF0ZW5veWVhcnJldicsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCBkYXksIG1vbnRoKSB7XG4gICAgICByZXR1cm4gdGhpcy55bWQodGhpcy55LCBsb29rdXBNb250aChtb250aCksICtkYXkpO1xuICAgIH1cbiAgfSxcblxuICBpc29XZWVrRGF5OiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyByZVllYXI0ICsgJy0/VycgKyByZVdlZWtPZlllYXIgKyAnKD86LT8oWzAtN10pKT8nKSxcbiAgICBuYW1lOiAnaXNvd2Vla2RheSB8IGlzb3dlZWsnLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgeWVhciwgd2VlaywgZGF5KSB7XG4gICAgICBkYXkgPSBkYXkgPyArZGF5IDogMTtcblxuICAgICAgaWYgKCF0aGlzLnltZCgreWVhciwgMCwgMSkpIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgfVxuXG4gICAgICAvLyBnZXQgZGF5IG9mIHdlZWsgZm9yIEphbiAxc3RcbiAgICAgIHZhciBkYXlPZldlZWsgPSBuZXcgRGF0ZSh0aGlzLnksIHRoaXMubSwgdGhpcy5kKS5nZXREYXkoKTtcblxuICAgICAgLy8gYW5kIHVzZSB0aGUgZGF5IHRvIGZpZ3VyZSBvdXQgdGhlIG9mZnNldCBmb3IgZGF5IDEgb2Ygd2VlayAxXG4gICAgICBkYXlPZldlZWsgPSAwIC0gKGRheU9mV2VlayA+IDQgPyBkYXlPZldlZWsgLSA3IDogZGF5T2ZXZWVrKTtcblxuICAgICAgdGhpcy5yZCArPSBkYXlPZldlZWsgKyAod2VlayAtIDEpICogNyArIGRheTtcbiAgICB9XG4gIH0sXG5cbiAgcmVsYXRpdmVUZXh0OiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXignICsgcmVSZWx0ZXh0bnVtYmVyICsgJ3wnICsgcmVSZWx0ZXh0dGV4dCArICcpJyArIHJlU3BhY2UgKyAnKCcgKyByZVJlbHRleHR1bml0ICsgJyknLCAnaScpLFxuICAgIG5hbWU6ICdyZWxhdGl2ZXRleHQnLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgcmVsVmFsdWUsIHJlbFVuaXQpIHtcbiAgICAgIC8vIHRvZG86IGltcGxlbWVudCBoYW5kbGluZyBvZiAndGhpcyB0aW1lLXVuaXQnXG4gICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW51c2VkLXZhcnNcbiAgICAgIHZhciBfbG9va3VwUmVsYXRpdmUgPSBsb29rdXBSZWxhdGl2ZShyZWxWYWx1ZSksXG4gICAgICAgICAgYW1vdW50ID0gX2xvb2t1cFJlbGF0aXZlLmFtb3VudCxcbiAgICAgICAgICBiZWhhdmlvciA9IF9sb29rdXBSZWxhdGl2ZS5iZWhhdmlvcjtcblxuICAgICAgc3dpdGNoIChyZWxVbml0LnRvTG93ZXJDYXNlKCkpIHtcbiAgICAgICAgY2FzZSAnc2VjJzpcbiAgICAgICAgY2FzZSAnc2Vjcyc6XG4gICAgICAgIGNhc2UgJ3NlY29uZCc6XG4gICAgICAgIGNhc2UgJ3NlY29uZHMnOlxuICAgICAgICAgIHRoaXMucnMgKz0gYW1vdW50O1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICdtaW4nOlxuICAgICAgICBjYXNlICdtaW5zJzpcbiAgICAgICAgY2FzZSAnbWludXRlJzpcbiAgICAgICAgY2FzZSAnbWludXRlcyc6XG4gICAgICAgICAgdGhpcy5yaSArPSBhbW91bnQ7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgJ2hvdXInOlxuICAgICAgICBjYXNlICdob3Vycyc6XG4gICAgICAgICAgdGhpcy5yaCArPSBhbW91bnQ7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgJ2RheSc6XG4gICAgICAgIGNhc2UgJ2RheXMnOlxuICAgICAgICAgIHRoaXMucmQgKz0gYW1vdW50O1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICdmb3J0bmlnaHQnOlxuICAgICAgICBjYXNlICdmb3J0bmlnaHRzJzpcbiAgICAgICAgY2FzZSAnZm9ydGhuaWdodCc6XG4gICAgICAgIGNhc2UgJ2ZvcnRobmlnaHRzJzpcbiAgICAgICAgICB0aGlzLnJkICs9IGFtb3VudCAqIDE0O1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICd3ZWVrJzpcbiAgICAgICAgY2FzZSAnd2Vla3MnOlxuICAgICAgICAgIHRoaXMucmQgKz0gYW1vdW50ICogNztcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAnbW9udGgnOlxuICAgICAgICBjYXNlICdtb250aHMnOlxuICAgICAgICAgIHRoaXMucm0gKz0gYW1vdW50O1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICd5ZWFyJzpcbiAgICAgICAgY2FzZSAneWVhcnMnOlxuICAgICAgICAgIHRoaXMucnkgKz0gYW1vdW50O1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICdtb24nOlxuICAgICAgICBjYXNlICdtb25kYXknOlxuICAgICAgICBjYXNlICd0dWUnOlxuICAgICAgICBjYXNlICd0dWVzZGF5JzpcbiAgICAgICAgY2FzZSAnd2VkJzpcbiAgICAgICAgY2FzZSAnd2VkbmVzZGF5JzpcbiAgICAgICAgY2FzZSAndGh1JzpcbiAgICAgICAgY2FzZSAndGh1cnNkYXknOlxuICAgICAgICBjYXNlICdmcmknOlxuICAgICAgICBjYXNlICdmcmlkYXknOlxuICAgICAgICBjYXNlICdzYXQnOlxuICAgICAgICBjYXNlICdzYXR1cmRheSc6XG4gICAgICAgIGNhc2UgJ3N1bic6XG4gICAgICAgIGNhc2UgJ3N1bmRheSc6XG4gICAgICAgICAgdGhpcy5yZXNldFRpbWUoKTtcbiAgICAgICAgICB0aGlzLndlZWtkYXkgPSBsb29rdXBXZWVrZGF5KHJlbFVuaXQsIDcpO1xuICAgICAgICAgIHRoaXMud2Vla2RheUJlaGF2aW9yID0gMTtcbiAgICAgICAgICB0aGlzLnJkICs9IChhbW91bnQgPiAwID8gYW1vdW50IC0gMSA6IGFtb3VudCkgKiA3O1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICd3ZWVrZGF5JzpcbiAgICAgICAgY2FzZSAnd2Vla2RheXMnOlxuICAgICAgICAgIC8vIHRvZG9cbiAgICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICB9XG4gIH0sXG5cbiAgcmVsYXRpdmU6IHtcbiAgICByZWdleDogUmVnRXhwKCdeKFsrLV0qKVsgXFxcXHRdKihcXFxcZCspJyArIHJlU3BhY2VPcHQgKyAnKCcgKyByZVJlbHRleHR1bml0ICsgJ3x3ZWVrKScsICdpJyksXG4gICAgbmFtZTogJ3JlbGF0aXZlJyxcbiAgICBjYWxsYmFjazogZnVuY3Rpb24gY2FsbGJhY2sobWF0Y2gsIHNpZ25zLCByZWxWYWx1ZSwgcmVsVW5pdCkge1xuICAgICAgdmFyIG1pbnVzZXMgPSBzaWducy5yZXBsYWNlKC9bXi1dL2csICcnKS5sZW5ndGg7XG5cbiAgICAgIHZhciBhbW91bnQgPSArcmVsVmFsdWUgKiBNYXRoLnBvdygtMSwgbWludXNlcyk7XG5cbiAgICAgIHN3aXRjaCAocmVsVW5pdC50b0xvd2VyQ2FzZSgpKSB7XG4gICAgICAgIGNhc2UgJ3NlYyc6XG4gICAgICAgIGNhc2UgJ3NlY3MnOlxuICAgICAgICBjYXNlICdzZWNvbmQnOlxuICAgICAgICBjYXNlICdzZWNvbmRzJzpcbiAgICAgICAgICB0aGlzLnJzICs9IGFtb3VudDtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAnbWluJzpcbiAgICAgICAgY2FzZSAnbWlucyc6XG4gICAgICAgIGNhc2UgJ21pbnV0ZSc6XG4gICAgICAgIGNhc2UgJ21pbnV0ZXMnOlxuICAgICAgICAgIHRoaXMucmkgKz0gYW1vdW50O1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICdob3VyJzpcbiAgICAgICAgY2FzZSAnaG91cnMnOlxuICAgICAgICAgIHRoaXMucmggKz0gYW1vdW50O1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICdkYXknOlxuICAgICAgICBjYXNlICdkYXlzJzpcbiAgICAgICAgICB0aGlzLnJkICs9IGFtb3VudDtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAnZm9ydG5pZ2h0JzpcbiAgICAgICAgY2FzZSAnZm9ydG5pZ2h0cyc6XG4gICAgICAgIGNhc2UgJ2ZvcnRobmlnaHQnOlxuICAgICAgICBjYXNlICdmb3J0aG5pZ2h0cyc6XG4gICAgICAgICAgdGhpcy5yZCArPSBhbW91bnQgKiAxNDtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAnd2Vlayc6XG4gICAgICAgIGNhc2UgJ3dlZWtzJzpcbiAgICAgICAgICB0aGlzLnJkICs9IGFtb3VudCAqIDc7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgJ21vbnRoJzpcbiAgICAgICAgY2FzZSAnbW9udGhzJzpcbiAgICAgICAgICB0aGlzLnJtICs9IGFtb3VudDtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAneWVhcic6XG4gICAgICAgIGNhc2UgJ3llYXJzJzpcbiAgICAgICAgICB0aGlzLnJ5ICs9IGFtb3VudDtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAnbW9uJzpcbiAgICAgICAgY2FzZSAnbW9uZGF5JzpcbiAgICAgICAgY2FzZSAndHVlJzpcbiAgICAgICAgY2FzZSAndHVlc2RheSc6XG4gICAgICAgIGNhc2UgJ3dlZCc6XG4gICAgICAgIGNhc2UgJ3dlZG5lc2RheSc6XG4gICAgICAgIGNhc2UgJ3RodSc6XG4gICAgICAgIGNhc2UgJ3RodXJzZGF5JzpcbiAgICAgICAgY2FzZSAnZnJpJzpcbiAgICAgICAgY2FzZSAnZnJpZGF5JzpcbiAgICAgICAgY2FzZSAnc2F0JzpcbiAgICAgICAgY2FzZSAnc2F0dXJkYXknOlxuICAgICAgICBjYXNlICdzdW4nOlxuICAgICAgICBjYXNlICdzdW5kYXknOlxuICAgICAgICAgIHRoaXMucmVzZXRUaW1lKCk7XG4gICAgICAgICAgdGhpcy53ZWVrZGF5ID0gbG9va3VwV2Vla2RheShyZWxVbml0LCA3KTtcbiAgICAgICAgICB0aGlzLndlZWtkYXlCZWhhdmlvciA9IDE7XG4gICAgICAgICAgdGhpcy5yZCArPSAoYW1vdW50ID4gMCA/IGFtb3VudCAtIDEgOiBhbW91bnQpICogNztcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAnd2Vla2RheSc6XG4gICAgICAgIGNhc2UgJ3dlZWtkYXlzJzpcbiAgICAgICAgICAvLyB0b2RvXG4gICAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgfVxuICB9LFxuXG4gIGRheVRleHQ6IHtcbiAgICByZWdleDogUmVnRXhwKCdeKCcgKyByZURheXRleHQgKyAnKScsICdpJyksXG4gICAgbmFtZTogJ2RheXRleHQnLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgZGF5VGV4dCkge1xuICAgICAgdGhpcy5yZXNldFRpbWUoKTtcbiAgICAgIHRoaXMud2Vla2RheSA9IGxvb2t1cFdlZWtkYXkoZGF5VGV4dCwgMCk7XG5cbiAgICAgIGlmICh0aGlzLndlZWtkYXlCZWhhdmlvciAhPT0gMikge1xuICAgICAgICB0aGlzLndlZWtkYXlCZWhhdmlvciA9IDE7XG4gICAgICB9XG4gICAgfVxuICB9LFxuXG4gIHJlbGF0aXZlVGV4dFdlZWs6IHtcbiAgICByZWdleDogUmVnRXhwKCdeKCcgKyByZVJlbHRleHR0ZXh0ICsgJyknICsgcmVTcGFjZSArICd3ZWVrJywgJ2knKSxcbiAgICBuYW1lOiAncmVsYXRpdmV0ZXh0d2VlaycsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCByZWxUZXh0KSB7XG4gICAgICB0aGlzLndlZWtkYXlCZWhhdmlvciA9IDI7XG5cbiAgICAgIHN3aXRjaCAocmVsVGV4dC50b0xvd2VyQ2FzZSgpKSB7XG4gICAgICAgIGNhc2UgJ3RoaXMnOlxuICAgICAgICAgIHRoaXMucmQgKz0gMDtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAnbmV4dCc6XG4gICAgICAgICAgdGhpcy5yZCArPSA3O1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICdsYXN0JzpcbiAgICAgICAgY2FzZSAncHJldmlvdXMnOlxuICAgICAgICAgIHRoaXMucmQgLT0gNztcbiAgICAgICAgICBicmVhaztcbiAgICAgIH1cblxuICAgICAgaWYgKGlzTmFOKHRoaXMud2Vla2RheSkpIHtcbiAgICAgICAgdGhpcy53ZWVrZGF5ID0gMTtcbiAgICAgIH1cbiAgICB9XG4gIH0sXG5cbiAgbW9udGhGdWxsT3JNb250aEFiYnI6IHtcbiAgICByZWdleDogUmVnRXhwKCdeKCcgKyByZU1vbnRoRnVsbCArICd8JyArIHJlTW9udGhBYmJyICsgJyknLCAnaScpLFxuICAgIG5hbWU6ICdtb250aGZ1bGwgfCBtb250aGFiYnInLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgbW9udGgpIHtcbiAgICAgIHJldHVybiB0aGlzLnltZCh0aGlzLnksIGxvb2t1cE1vbnRoKG1vbnRoKSwgdGhpcy5kKTtcbiAgICB9XG4gIH0sXG5cbiAgdHpDb3JyZWN0aW9uOiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyByZVR6Q29ycmVjdGlvbiwgJ2knKSxcbiAgICBuYW1lOiAndHpjb3JyZWN0aW9uJyxcbiAgICBjYWxsYmFjazogZnVuY3Rpb24gY2FsbGJhY2sodHpDb3JyZWN0aW9uKSB7XG4gICAgICByZXR1cm4gdGhpcy56b25lKHByb2Nlc3NUekNvcnJlY3Rpb24odHpDb3JyZWN0aW9uKSk7XG4gICAgfVxuICB9LFxuXG4gIHR6QWJicjoge1xuICAgIHJlZ2V4OiBSZWdFeHAoJ14nICsgcmVUekFiYnIpLFxuICAgIG5hbWU6ICd0emFiYnInLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgYWJicikge1xuICAgICAgdmFyIG9mZnNldCA9IHR6QWJick9mZnNldHNbYWJici50b0xvd2VyQ2FzZSgpXTtcblxuICAgICAgaWYgKGlzTmFOKG9mZnNldCkpIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgfVxuXG4gICAgICByZXR1cm4gdGhpcy56b25lKG9mZnNldCk7XG4gICAgfVxuICB9LFxuXG4gIGFnbzoge1xuICAgIHJlZ2V4OiAvXmFnby9pLFxuICAgIG5hbWU6ICdhZ28nLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjaygpIHtcbiAgICAgIHRoaXMucnkgPSAtdGhpcy5yeTtcbiAgICAgIHRoaXMucm0gPSAtdGhpcy5ybTtcbiAgICAgIHRoaXMucmQgPSAtdGhpcy5yZDtcbiAgICAgIHRoaXMucmggPSAtdGhpcy5yaDtcbiAgICAgIHRoaXMucmkgPSAtdGhpcy5yaTtcbiAgICAgIHRoaXMucnMgPSAtdGhpcy5ycztcbiAgICAgIHRoaXMucmYgPSAtdGhpcy5yZjtcbiAgICB9XG4gIH0sXG5cbiAgeWVhcjQ6IHtcbiAgICByZWdleDogUmVnRXhwKCdeJyArIHJlWWVhcjQpLFxuICAgIG5hbWU6ICd5ZWFyNCcsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCB5ZWFyKSB7XG4gICAgICB0aGlzLnkgPSAreWVhcjtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cbiAgfSxcblxuICB3aGl0ZXNwYWNlOiB7XG4gICAgcmVnZXg6IC9eWyAuLFxcdF0rLyxcbiAgICBuYW1lOiAnd2hpdGVzcGFjZSdcbiAgICAvLyBkbyBub3RoaW5nXG4gIH0sXG5cbiAgZGF0ZVNob3J0V2l0aFRpbWVMb25nOiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyByZURhdGVOb1llYXIgKyAndD8nICsgcmVIb3VyMjQgKyAnWzouXScgKyByZU1pbnV0ZSArICdbOi5dJyArIHJlU2Vjb25kLCAnaScpLFxuICAgIG5hbWU6ICdkYXRlc2hvcnR3aXRodGltZWxvbmcnLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgbW9udGgsIGRheSwgaG91ciwgbWludXRlLCBzZWNvbmQpIHtcbiAgICAgIHJldHVybiB0aGlzLnltZCh0aGlzLnksIGxvb2t1cE1vbnRoKG1vbnRoKSwgK2RheSkgJiYgdGhpcy50aW1lKCtob3VyLCArbWludXRlLCArc2Vjb25kLCAwKTtcbiAgICB9XG4gIH0sXG5cbiAgZGF0ZVNob3J0V2l0aFRpbWVMb25nMTI6IHtcbiAgICByZWdleDogUmVnRXhwKCdeJyArIHJlRGF0ZU5vWWVhciArIHJlSG91cjEyICsgJ1s6Ll0nICsgcmVNaW51dGUgKyAnWzouXScgKyByZVNlY29uZGx6ICsgcmVTcGFjZU9wdCArIHJlTWVyaWRpYW4sICdpJyksXG4gICAgbmFtZTogJ2RhdGVzaG9ydHdpdGh0aW1lbG9uZzEyJyxcbiAgICBjYWxsYmFjazogZnVuY3Rpb24gY2FsbGJhY2sobWF0Y2gsIG1vbnRoLCBkYXksIGhvdXIsIG1pbnV0ZSwgc2Vjb25kLCBtZXJpZGlhbikge1xuICAgICAgcmV0dXJuIHRoaXMueW1kKHRoaXMueSwgbG9va3VwTW9udGgobW9udGgpLCArZGF5KSAmJiB0aGlzLnRpbWUocHJvY2Vzc01lcmlkaWFuKCtob3VyLCBtZXJpZGlhbiksICttaW51dGUsICtzZWNvbmQsIDApO1xuICAgIH1cbiAgfSxcblxuICBkYXRlU2hvcnRXaXRoVGltZVNob3J0OiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyByZURhdGVOb1llYXIgKyAndD8nICsgcmVIb3VyMjQgKyAnWzouXScgKyByZU1pbnV0ZSwgJ2knKSxcbiAgICBuYW1lOiAnZGF0ZXNob3J0d2l0aHRpbWVzaG9ydCcsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCBtb250aCwgZGF5LCBob3VyLCBtaW51dGUpIHtcbiAgICAgIHJldHVybiB0aGlzLnltZCh0aGlzLnksIGxvb2t1cE1vbnRoKG1vbnRoKSwgK2RheSkgJiYgdGhpcy50aW1lKCtob3VyLCArbWludXRlLCAwLCAwKTtcbiAgICB9XG4gIH0sXG5cbiAgZGF0ZVNob3J0V2l0aFRpbWVTaG9ydDEyOiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyByZURhdGVOb1llYXIgKyByZUhvdXIxMiArICdbOi5dJyArIHJlTWludXRlbHogKyByZVNwYWNlT3B0ICsgcmVNZXJpZGlhbiwgJ2knKSxcbiAgICBuYW1lOiAnZGF0ZXNob3J0d2l0aHRpbWVzaG9ydDEyJyxcbiAgICBjYWxsYmFjazogZnVuY3Rpb24gY2FsbGJhY2sobWF0Y2gsIG1vbnRoLCBkYXksIGhvdXIsIG1pbnV0ZSwgbWVyaWRpYW4pIHtcbiAgICAgIHJldHVybiB0aGlzLnltZCh0aGlzLnksIGxvb2t1cE1vbnRoKG1vbnRoKSwgK2RheSkgJiYgdGhpcy50aW1lKHByb2Nlc3NNZXJpZGlhbigraG91ciwgbWVyaWRpYW4pLCArbWludXRlLCAwLCAwKTtcbiAgICB9XG4gIH1cbn07XG5cbnZhciByZXN1bHRQcm90byA9IHtcbiAgLy8gZGF0ZVxuICB5OiBOYU4sXG4gIG06IE5hTixcbiAgZDogTmFOLFxuICAvLyB0aW1lXG4gIGg6IE5hTixcbiAgaTogTmFOLFxuICBzOiBOYU4sXG4gIGY6IE5hTixcblxuICAvLyByZWxhdGl2ZSBzaGlmdHNcbiAgcnk6IDAsXG4gIHJtOiAwLFxuICByZDogMCxcbiAgcmg6IDAsXG4gIHJpOiAwLFxuICByczogMCxcbiAgcmY6IDAsXG5cbiAgLy8gd2Vla2RheSByZWxhdGVkIHNoaWZ0c1xuICB3ZWVrZGF5OiBOYU4sXG4gIHdlZWtkYXlCZWhhdmlvcjogMCxcblxuICAvLyBmaXJzdCBvciBsYXN0IGRheSBvZiBtb250aFxuICAvLyAwIG5vbmUsIDEgZmlyc3QsIC0xIGxhc3RcbiAgZmlyc3RPckxhc3REYXlPZk1vbnRoOiAwLFxuXG4gIC8vIHRpbWV6b25lIGNvcnJlY3Rpb24gaW4gbWludXRlc1xuICB6OiBOYU4sXG5cbiAgLy8gY291bnRlcnNcbiAgZGF0ZXM6IDAsXG4gIHRpbWVzOiAwLFxuICB6b25lczogMCxcblxuICAvLyBoZWxwZXIgZnVuY3Rpb25zXG4gIHltZDogZnVuY3Rpb24geW1kKHksIG0sIGQpIHtcbiAgICBpZiAodGhpcy5kYXRlcyA+IDApIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG5cbiAgICB0aGlzLmRhdGVzKys7XG4gICAgdGhpcy55ID0geTtcbiAgICB0aGlzLm0gPSBtO1xuICAgIHRoaXMuZCA9IGQ7XG4gICAgcmV0dXJuIHRydWU7XG4gIH0sXG4gIHRpbWU6IGZ1bmN0aW9uIHRpbWUoaCwgaSwgcywgZikge1xuICAgIGlmICh0aGlzLnRpbWVzID4gMCkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cblxuICAgIHRoaXMudGltZXMrKztcbiAgICB0aGlzLmggPSBoO1xuICAgIHRoaXMuaSA9IGk7XG4gICAgdGhpcy5zID0gcztcbiAgICB0aGlzLmYgPSBmO1xuXG4gICAgcmV0dXJuIHRydWU7XG4gIH0sXG4gIHJlc2V0VGltZTogZnVuY3Rpb24gcmVzZXRUaW1lKCkge1xuICAgIHRoaXMuaCA9IDA7XG4gICAgdGhpcy5pID0gMDtcbiAgICB0aGlzLnMgPSAwO1xuICAgIHRoaXMuZiA9IDA7XG4gICAgdGhpcy50aW1lcyA9IDA7XG5cbiAgICByZXR1cm4gdHJ1ZTtcbiAgfSxcbiAgem9uZTogZnVuY3Rpb24gem9uZShtaW51dGVzKSB7XG4gICAgaWYgKHRoaXMuem9uZXMgPD0gMSkge1xuICAgICAgdGhpcy56b25lcysrO1xuICAgICAgdGhpcy56ID0gbWludXRlcztcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cblxuICAgIHJldHVybiBmYWxzZTtcbiAgfSxcbiAgdG9EYXRlOiBmdW5jdGlvbiB0b0RhdGUocmVsYXRpdmVUbykge1xuICAgIGlmICh0aGlzLmRhdGVzICYmICF0aGlzLnRpbWVzKSB7XG4gICAgICB0aGlzLmggPSB0aGlzLmkgPSB0aGlzLnMgPSB0aGlzLmYgPSAwO1xuICAgIH1cblxuICAgIC8vIGZpbGwgaG9sZXNcbiAgICBpZiAoaXNOYU4odGhpcy55KSkge1xuICAgICAgdGhpcy55ID0gcmVsYXRpdmVUby5nZXRGdWxsWWVhcigpO1xuICAgIH1cblxuICAgIGlmIChpc05hTih0aGlzLm0pKSB7XG4gICAgICB0aGlzLm0gPSByZWxhdGl2ZVRvLmdldE1vbnRoKCk7XG4gICAgfVxuXG4gICAgaWYgKGlzTmFOKHRoaXMuZCkpIHtcbiAgICAgIHRoaXMuZCA9IHJlbGF0aXZlVG8uZ2V0RGF0ZSgpO1xuICAgIH1cblxuICAgIGlmIChpc05hTih0aGlzLmgpKSB7XG4gICAgICB0aGlzLmggPSByZWxhdGl2ZVRvLmdldEhvdXJzKCk7XG4gICAgfVxuXG4gICAgaWYgKGlzTmFOKHRoaXMuaSkpIHtcbiAgICAgIHRoaXMuaSA9IHJlbGF0aXZlVG8uZ2V0TWludXRlcygpO1xuICAgIH1cblxuICAgIGlmIChpc05hTih0aGlzLnMpKSB7XG4gICAgICB0aGlzLnMgPSByZWxhdGl2ZVRvLmdldFNlY29uZHMoKTtcbiAgICB9XG5cbiAgICBpZiAoaXNOYU4odGhpcy5mKSkge1xuICAgICAgdGhpcy5mID0gcmVsYXRpdmVUby5nZXRNaWxsaXNlY29uZHMoKTtcbiAgICB9XG5cbiAgICAvLyBhZGp1c3Qgc3BlY2lhbCBlYXJseVxuICAgIHN3aXRjaCAodGhpcy5maXJzdE9yTGFzdERheU9mTW9udGgpIHtcbiAgICAgIGNhc2UgMTpcbiAgICAgICAgdGhpcy5kID0gMTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlIC0xOlxuICAgICAgICB0aGlzLmQgPSAwO1xuICAgICAgICB0aGlzLm0gKz0gMTtcbiAgICAgICAgYnJlYWs7XG4gICAgfVxuXG4gICAgaWYgKCFpc05hTih0aGlzLndlZWtkYXkpKSB7XG4gICAgICB2YXIgZGF0ZSA9IG5ldyBEYXRlKHJlbGF0aXZlVG8uZ2V0VGltZSgpKTtcbiAgICAgIGRhdGUuc2V0RnVsbFllYXIodGhpcy55LCB0aGlzLm0sIHRoaXMuZCk7XG4gICAgICBkYXRlLnNldEhvdXJzKHRoaXMuaCwgdGhpcy5pLCB0aGlzLnMsIHRoaXMuZik7XG5cbiAgICAgIHZhciBkb3cgPSBkYXRlLmdldERheSgpO1xuXG4gICAgICBpZiAodGhpcy53ZWVrZGF5QmVoYXZpb3IgPT09IDIpIHtcbiAgICAgICAgLy8gVG8gbWFrZSBcInRoaXMgd2Vla1wiIHdvcmssIHdoZXJlIHRoZSBjdXJyZW50IGRheSBvZiB3ZWVrIGlzIGEgXCJzdW5kYXlcIlxuICAgICAgICBpZiAoZG93ID09PSAwICYmIHRoaXMud2Vla2RheSAhPT0gMCkge1xuICAgICAgICAgIHRoaXMud2Vla2RheSA9IC02O1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gVG8gbWFrZSBcInN1bmRheSB0aGlzIHdlZWtcIiB3b3JrLCB3aGVyZSB0aGUgY3VycmVudCBkYXkgb2Ygd2VlayBpcyBub3QgYSBcInN1bmRheVwiXG4gICAgICAgIGlmICh0aGlzLndlZWtkYXkgPT09IDAgJiYgZG93ICE9PSAwKSB7XG4gICAgICAgICAgdGhpcy53ZWVrZGF5ID0gNztcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuZCAtPSBkb3c7XG4gICAgICAgIHRoaXMuZCArPSB0aGlzLndlZWtkYXk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB2YXIgZGlmZiA9IHRoaXMud2Vla2RheSAtIGRvdztcblxuICAgICAgICAvLyBzb21lIFBIUCBtYWdpY1xuICAgICAgICBpZiAodGhpcy5yZCA8IDAgJiYgZGlmZiA8IDAgfHwgdGhpcy5yZCA+PSAwICYmIGRpZmYgPD0gLXRoaXMud2Vla2RheUJlaGF2aW9yKSB7XG4gICAgICAgICAgZGlmZiArPSA3O1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHRoaXMud2Vla2RheSA+PSAwKSB7XG4gICAgICAgICAgdGhpcy5kICs9IGRpZmY7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgdGhpcy5kIC09IDcgLSAoTWF0aC5hYnModGhpcy53ZWVrZGF5KSAtIGRvdyk7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLndlZWtkYXkgPSBOYU47XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gYWRqdXN0IHJlbGF0aXZlXG4gICAgdGhpcy55ICs9IHRoaXMucnk7XG4gICAgdGhpcy5tICs9IHRoaXMucm07XG4gICAgdGhpcy5kICs9IHRoaXMucmQ7XG5cbiAgICB0aGlzLmggKz0gdGhpcy5yaDtcbiAgICB0aGlzLmkgKz0gdGhpcy5yaTtcbiAgICB0aGlzLnMgKz0gdGhpcy5ycztcbiAgICB0aGlzLmYgKz0gdGhpcy5yZjtcblxuICAgIHRoaXMucnkgPSB0aGlzLnJtID0gdGhpcy5yZCA9IDA7XG4gICAgdGhpcy5yaCA9IHRoaXMucmkgPSB0aGlzLnJzID0gdGhpcy5yZiA9IDA7XG5cbiAgICB2YXIgcmVzdWx0ID0gbmV3IERhdGUocmVsYXRpdmVUby5nZXRUaW1lKCkpO1xuICAgIC8vIHNpbmNlIERhdGUgY29uc3RydWN0b3IgdHJlYXRzIHllYXJzIDw9IDk5IGFzIDE5MDArXG4gICAgLy8gaXQgY2FuJ3QgYmUgdXNlZCwgdGh1cyB0aGlzIHdlaXJkIHdheVxuICAgIHJlc3VsdC5zZXRGdWxsWWVhcih0aGlzLnksIHRoaXMubSwgdGhpcy5kKTtcbiAgICByZXN1bHQuc2V0SG91cnModGhpcy5oLCB0aGlzLmksIHRoaXMucywgdGhpcy5mKTtcblxuICAgIC8vIG5vdGU6IHRoaXMgaXMgZG9uZSB0d2ljZSBpbiBQSFBcbiAgICAvLyBlYXJseSB3aGVuIHByb2Nlc3Npbmcgc3BlY2lhbCByZWxhdGl2ZXNcbiAgICAvLyBhbmQgbGF0ZVxuICAgIC8vIHRvZG86IGNoZWNrIGlmIHRoZSBsb2dpYyBjYW4gYmUgcmVkdWNlZFxuICAgIC8vIHRvIGp1c3Qgb25lIHRpbWUgYWN0aW9uXG4gICAgc3dpdGNoICh0aGlzLmZpcnN0T3JMYXN0RGF5T2ZNb250aCkge1xuICAgICAgY2FzZSAxOlxuICAgICAgICByZXN1bHQuc2V0RGF0ZSgxKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlIC0xOlxuICAgICAgICByZXN1bHQuc2V0TW9udGgocmVzdWx0LmdldE1vbnRoKCkgKyAxLCAwKTtcbiAgICAgICAgYnJlYWs7XG4gICAgfVxuXG4gICAgLy8gYWRqdXN0IHRpbWV6b25lXG4gICAgaWYgKCFpc05hTih0aGlzLnopICYmIHJlc3VsdC5nZXRUaW1lem9uZU9mZnNldCgpICE9PSB0aGlzLnopIHtcbiAgICAgIHJlc3VsdC5zZXRVVENGdWxsWWVhcihyZXN1bHQuZ2V0RnVsbFllYXIoKSwgcmVzdWx0LmdldE1vbnRoKCksIHJlc3VsdC5nZXREYXRlKCkpO1xuXG4gICAgICByZXN1bHQuc2V0VVRDSG91cnMocmVzdWx0LmdldEhvdXJzKCksIHJlc3VsdC5nZXRNaW51dGVzKCksIHJlc3VsdC5nZXRTZWNvbmRzKCkgLSB0aGlzLnosIHJlc3VsdC5nZXRNaWxsaXNlY29uZHMoKSk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxufTtcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiBzdHJ0b3RpbWUoc3RyLCBub3cpIHtcbiAgLy8gICAgICAgZGlzY3VzcyBhdDogaHR0cHM6Ly9sb2N1dHVzLmlvL3BocC9zdHJ0b3RpbWUvXG4gIC8vICAgICAgb3JpZ2luYWwgYnk6IENhaW8gQXJpZWRlIChodHRwczovL2NhaW9hcmllZGUuY29tKVxuICAvLyAgICAgIGltcHJvdmVkIGJ5OiBLZXZpbiB2YW4gWm9ubmV2ZWxkIChodHRwczovL2t2ei5pbylcbiAgLy8gICAgICBpbXByb3ZlZCBieTogQ2FpbyBBcmllZGUgKGh0dHBzOi8vY2Fpb2FyaWVkZS5jb20pXG4gIC8vICAgICAgaW1wcm92ZWQgYnk6IEEuIE1hdMOtYXMgUXVlemFkYSAoaHR0cHM6Ly9hbWF0aWFzcS5jb20pXG4gIC8vICAgICAgaW1wcm92ZWQgYnk6IHByZXV0ZXJcbiAgLy8gICAgICBpbXByb3ZlZCBieTogQnJldHQgWmFtaXIgKGh0dHBzOi8vYnJldHQtemFtaXIubWUpXG4gIC8vICAgICAgaW1wcm92ZWQgYnk6IE1pcmtvIEZhYmVyXG4gIC8vICAgICAgICAgaW5wdXQgYnk6IERhdmlkXG4gIC8vICAgICAgYnVnZml4ZWQgYnk6IFdhZ25lciBCLiBTb2FyZXNcbiAgLy8gICAgICBidWdmaXhlZCBieTogQXJ0dXIgVGNoZXJueWNoZXZcbiAgLy8gICAgICBidWdmaXhlZCBieTogU3RlcGhhbiBCw7ZzY2gtUGxlcGVsaXRzIChodHRwczovL2dpdGh1Yi5jb20vcGxlcGUpXG4gIC8vIHJlaW1wbGVtZW50ZWQgYnk6IFJhZmHFgiBLdWthd3NraVxuICAvLyAgICAgICAgICAgbm90ZSAxOiBFeGFtcGxlcyBhbGwgaGF2ZSBhIGZpeGVkIHRpbWVzdGFtcCB0byBwcmV2ZW50XG4gIC8vICAgICAgICAgICBub3RlIDE6IHRlc3RzIHRvIGZhaWwgYmVjYXVzZSBvZiB2YXJpYWJsZSB0aW1lKHpvbmVzKVxuICAvLyAgICAgICAgZXhhbXBsZSAxOiBzdHJ0b3RpbWUoJysxIGRheScsIDExMjk2MzMyMDApXG4gIC8vICAgICAgICByZXR1cm5zIDE6IDExMjk3MTk2MDBcbiAgLy8gICAgICAgIGV4YW1wbGUgMjogc3RydG90aW1lKCcrMSB3ZWVrIDIgZGF5cyA0IGhvdXJzIDIgc2Vjb25kcycsIDExMjk2MzMyMDApXG4gIC8vICAgICAgICByZXR1cm5zIDI6IDExMzA0MjUyMDJcbiAgLy8gICAgICAgIGV4YW1wbGUgMzogc3RydG90aW1lKCdsYXN0IG1vbnRoJywgMTEyOTYzMzIwMClcbiAgLy8gICAgICAgIHJldHVybnMgMzogMTEyNzA0MTIwMFxuICAvLyAgICAgICAgZXhhbXBsZSA0OiBzdHJ0b3RpbWUoJzIwMDktMDUtMDQgMDg6MzA6MDArMDAnKVxuICAvLyAgICAgICAgcmV0dXJucyA0OiAxMjQxNDI1ODAwXG4gIC8vICAgICAgICBleGFtcGxlIDU6IHN0cnRvdGltZSgnMjAwOS0wNS0wNCAwODozMDowMCswMjowMCcpXG4gIC8vICAgICAgICByZXR1cm5zIDU6IDEyNDE0MTg2MDBcbiAgLy8gICAgICAgIGV4YW1wbGUgNjogc3RydG90aW1lKCcyMDA5LTA1LTA0IDA4OjMwOjAwIFlXVCcpXG4gIC8vICAgICAgICByZXR1cm5zIDY6IDEyNDE0NTQ2MDBcbiAgLy8gICAgICAgIGV4YW1wbGUgNzogc3RydG90aW1lKCcxMC1KVUwtMTcnKVxuICAvLyAgICAgICAgcmV0dXJucyA3OiAxNDk5NjQ0ODAwXG5cbiAgaWYgKG5vdyA9PSBudWxsKSB7XG4gICAgbm93ID0gTWF0aC5mbG9vcihEYXRlLm5vdygpIC8gMTAwMCk7XG4gIH1cblxuICAvLyB0aGUgcnVsZSBvcmRlciBpcyBpbXBvcnRhbnRcbiAgLy8gaWYgbXVsdGlwbGUgcnVsZXMgbWF0Y2gsIHRoZSBsb25nZXN0IG1hdGNoIHdpbnNcbiAgLy8gaWYgbXVsdGlwbGUgcnVsZXMgbWF0Y2ggdGhlIHNhbWUgc3RyaW5nLCB0aGUgZmlyc3QgbWF0Y2ggd2luc1xuICB2YXIgcnVsZXMgPSBbZm9ybWF0cy55ZXN0ZXJkYXksIGZvcm1hdHMubm93LCBmb3JtYXRzLm5vb24sIGZvcm1hdHMubWlkbmlnaHRPclRvZGF5LCBmb3JtYXRzLnRvbW9ycm93LCBmb3JtYXRzLnRpbWVzdGFtcCwgZm9ybWF0cy5maXJzdE9yTGFzdERheSwgZm9ybWF0cy5iYWNrT3JGcm9udE9mLFxuICAvLyBmb3JtYXRzLndlZWtkYXlPZiwgLy8gbm90IHlldCBpbXBsZW1lbnRlZFxuICBmb3JtYXRzLnRpbWVUaW55MTIsIGZvcm1hdHMudGltZVNob3J0MTIsIGZvcm1hdHMudGltZUxvbmcxMiwgZm9ybWF0cy5tc3NxbHRpbWUsIGZvcm1hdHMub3JhY2xlZGF0ZSwgZm9ybWF0cy50aW1lU2hvcnQyNCwgZm9ybWF0cy50aW1lTG9uZzI0LCBmb3JtYXRzLmlzbzg2MDFsb25nLCBmb3JtYXRzLmdudU5vQ29sb24sIGZvcm1hdHMuaXNvODYwMW5vQ29sb24sIGZvcm1hdHMuYW1lcmljYW5TaG9ydCwgZm9ybWF0cy5hbWVyaWNhbiwgZm9ybWF0cy5pc284NjAxZGF0ZTQsIGZvcm1hdHMuaXNvODYwMWRhdGVTbGFzaCwgZm9ybWF0cy5kYXRlU2xhc2gsIGZvcm1hdHMuZ251RGF0ZVNob3J0T3JJc284NjAxZGF0ZTIsIGZvcm1hdHMuZ251RGF0ZVNob3J0ZXIsIGZvcm1hdHMuZGF0ZUZ1bGwsIGZvcm1hdHMucG9pbnRlZERhdGU0LCBmb3JtYXRzLnBvaW50ZWREYXRlMiwgZm9ybWF0cy5kYXRlTm9EYXksIGZvcm1hdHMuZGF0ZU5vRGF5UmV2LCBmb3JtYXRzLmRhdGVUZXh0dWFsLCBmb3JtYXRzLmRhdGVOb1llYXIsIGZvcm1hdHMuZGF0ZU5vWWVhclJldiwgZm9ybWF0cy5kYXRlTm9Db2xvbiwgZm9ybWF0cy54bWxScGMsIGZvcm1hdHMueG1sUnBjTm9Db2xvbiwgZm9ybWF0cy5zb2FwLCBmb3JtYXRzLndkZHgsIGZvcm1hdHMuZXhpZiwgZm9ybWF0cy5wZ3lkb3RkLCBmb3JtYXRzLmlzb1dlZWtEYXksIGZvcm1hdHMucGdUZXh0U2hvcnQsIGZvcm1hdHMucGdUZXh0UmV2ZXJzZSwgZm9ybWF0cy5jbGYsIGZvcm1hdHMueWVhcjQsIGZvcm1hdHMuYWdvLCBmb3JtYXRzLmRheVRleHQsIGZvcm1hdHMucmVsYXRpdmVUZXh0V2VlaywgZm9ybWF0cy5yZWxhdGl2ZVRleHQsIGZvcm1hdHMubW9udGhGdWxsT3JNb250aEFiYnIsIGZvcm1hdHMudHpDb3JyZWN0aW9uLCBmb3JtYXRzLnR6QWJiciwgZm9ybWF0cy5kYXRlU2hvcnRXaXRoVGltZVNob3J0MTIsIGZvcm1hdHMuZGF0ZVNob3J0V2l0aFRpbWVMb25nMTIsIGZvcm1hdHMuZGF0ZVNob3J0V2l0aFRpbWVTaG9ydCwgZm9ybWF0cy5kYXRlU2hvcnRXaXRoVGltZUxvbmcsIGZvcm1hdHMucmVsYXRpdmUsIGZvcm1hdHMud2hpdGVzcGFjZV07XG5cbiAgdmFyIHJlc3VsdCA9IE9iamVjdC5jcmVhdGUocmVzdWx0UHJvdG8pO1xuXG4gIHdoaWxlIChzdHIubGVuZ3RoKSB7XG4gICAgdmFyIGxvbmdlc3RNYXRjaCA9IG51bGw7XG4gICAgdmFyIGZpbmFsUnVsZSA9IG51bGw7XG5cbiAgICBmb3IgKHZhciBpID0gMCwgbCA9IHJ1bGVzLmxlbmd0aDsgaSA8IGw7IGkrKykge1xuICAgICAgdmFyIGZvcm1hdCA9IHJ1bGVzW2ldO1xuXG4gICAgICB2YXIgbWF0Y2ggPSBzdHIubWF0Y2goZm9ybWF0LnJlZ2V4KTtcblxuICAgICAgaWYgKG1hdGNoKSB7XG4gICAgICAgIGlmICghbG9uZ2VzdE1hdGNoIHx8IG1hdGNoWzBdLmxlbmd0aCA+IGxvbmdlc3RNYXRjaFswXS5sZW5ndGgpIHtcbiAgICAgICAgICBsb25nZXN0TWF0Y2ggPSBtYXRjaDtcbiAgICAgICAgICBmaW5hbFJ1bGUgPSBmb3JtYXQ7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoIWZpbmFsUnVsZSB8fCBmaW5hbFJ1bGUuY2FsbGJhY2sgJiYgZmluYWxSdWxlLmNhbGxiYWNrLmFwcGx5KHJlc3VsdCwgbG9uZ2VzdE1hdGNoKSA9PT0gZmFsc2UpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG5cbiAgICBzdHIgPSBzdHIuc3Vic3RyKGxvbmdlc3RNYXRjaFswXS5sZW5ndGgpO1xuICAgIGZpbmFsUnVsZSA9IG51bGw7XG4gICAgbG9uZ2VzdE1hdGNoID0gbnVsbDtcbiAgfVxuXG4gIHJldHVybiBNYXRoLmZsb29yKHJlc3VsdC50b0RhdGUobmV3IERhdGUobm93ICogMTAwMCkpIC8gMTAwMCk7XG59O1xuLy8jIHNvdXJjZU1hcHBpbmdVUkw9c3RydG90aW1lLmpzLm1hcCIsIid1c2Ugc3RyaWN0JztcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiBpbmlfZ2V0KHZhcm5hbWUpIHtcbiAgLy8gIGRpc2N1c3MgYXQ6IGh0dHBzOi8vbG9jdXR1cy5pby9waHAvaW5pX2dldC9cbiAgLy8gb3JpZ2luYWwgYnk6IEJyZXR0IFphbWlyIChodHRwczovL2JyZXR0LXphbWlyLm1lKVxuICAvLyAgICAgIG5vdGUgMTogVGhlIGluaSB2YWx1ZXMgbXVzdCBiZSBzZXQgYnkgaW5pX3NldCBvciBtYW51YWxseSB3aXRoaW4gYW4gaW5pIGZpbGVcbiAgLy8gICBleGFtcGxlIDE6IGluaV9zZXQoJ2RhdGUudGltZXpvbmUnLCAnQXNpYS9Ib25nX0tvbmcnKVxuICAvLyAgIGV4YW1wbGUgMTogaW5pX2dldCgnZGF0ZS50aW1lem9uZScpXG4gIC8vICAgcmV0dXJucyAxOiAnQXNpYS9Ib25nX0tvbmcnXG5cbiAgdmFyICRnbG9iYWwgPSB0eXBlb2Ygd2luZG93ICE9PSAndW5kZWZpbmVkJyA/IHdpbmRvdyA6IGdsb2JhbDtcbiAgJGdsb2JhbC4kbG9jdXR1cyA9ICRnbG9iYWwuJGxvY3V0dXMgfHwge307XG4gIHZhciAkbG9jdXR1cyA9ICRnbG9iYWwuJGxvY3V0dXM7XG4gICRsb2N1dHVzLnBocCA9ICRsb2N1dHVzLnBocCB8fCB7fTtcbiAgJGxvY3V0dXMucGhwLmluaSA9ICRsb2N1dHVzLnBocC5pbmkgfHwge307XG5cbiAgaWYgKCRsb2N1dHVzLnBocC5pbmlbdmFybmFtZV0gJiYgJGxvY3V0dXMucGhwLmluaVt2YXJuYW1lXS5sb2NhbF92YWx1ZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgaWYgKCRsb2N1dHVzLnBocC5pbmlbdmFybmFtZV0ubG9jYWxfdmFsdWUgPT09IG51bGwpIHtcbiAgICAgIHJldHVybiAnJztcbiAgICB9XG4gICAgcmV0dXJuICRsb2N1dHVzLnBocC5pbmlbdmFybmFtZV0ubG9jYWxfdmFsdWU7XG4gIH1cblxuICByZXR1cm4gJyc7XG59O1xuLy8jIHNvdXJjZU1hcHBpbmdVUkw9aW5pX2dldC5qcy5tYXAiLCIndXNlIHN0cmljdCc7XG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gc3RybGVuKHN0cmluZykge1xuICAvLyAgZGlzY3VzcyBhdDogaHR0cHM6Ly9sb2N1dHVzLmlvL3BocC9zdHJsZW4vXG4gIC8vIG9yaWdpbmFsIGJ5OiBLZXZpbiB2YW4gWm9ubmV2ZWxkIChodHRwczovL2t2ei5pbylcbiAgLy8gaW1wcm92ZWQgYnk6IFNha2ltb3JpXG4gIC8vIGltcHJvdmVkIGJ5OiBLZXZpbiB2YW4gWm9ubmV2ZWxkIChodHRwczovL2t2ei5pbylcbiAgLy8gICAgaW5wdXQgYnk6IEtpcmsgU3Ryb2JlY2tcbiAgLy8gYnVnZml4ZWQgYnk6IE9ubm8gTWFyc21hbiAoaHR0cHM6Ly90d2l0dGVyLmNvbS9vbm5vbWFyc21hbilcbiAgLy8gIHJldmlzZWQgYnk6IEJyZXR0IFphbWlyIChodHRwczovL2JyZXR0LXphbWlyLm1lKVxuICAvLyAgICAgIG5vdGUgMTogTWF5IGxvb2sgbGlrZSBvdmVya2lsbCwgYnV0IGluIG9yZGVyIHRvIGJlIHRydWx5IGZhaXRoZnVsIHRvIGhhbmRsaW5nIGFsbCBVbmljb2RlXG4gIC8vICAgICAgbm90ZSAxOiBjaGFyYWN0ZXJzIGFuZCB0byB0aGlzIGZ1bmN0aW9uIGluIFBIUCB3aGljaCBkb2VzIG5vdCBjb3VudCB0aGUgbnVtYmVyIG9mIGJ5dGVzXG4gIC8vICAgICAgbm90ZSAxOiBidXQgY291bnRzIHRoZSBudW1iZXIgb2YgY2hhcmFjdGVycywgc29tZXRoaW5nIGxpa2UgdGhpcyBpcyByZWFsbHkgbmVjZXNzYXJ5LlxuICAvLyAgIGV4YW1wbGUgMTogc3RybGVuKCdLZXZpbiB2YW4gWm9ubmV2ZWxkJylcbiAgLy8gICByZXR1cm5zIDE6IDE5XG4gIC8vICAgZXhhbXBsZSAyOiBpbmlfc2V0KCd1bmljb2RlLnNlbWFudGljcycsICdvbicpXG4gIC8vICAgZXhhbXBsZSAyOiBzdHJsZW4oJ0FcXHVkODdlXFx1ZGMwNFonKVxuICAvLyAgIHJldHVybnMgMjogM1xuXG4gIHZhciBzdHIgPSBzdHJpbmcgKyAnJztcblxuICB2YXIgaW5pVmFsID0gKHR5cGVvZiByZXF1aXJlICE9PSAndW5kZWZpbmVkJyA/IHJlcXVpcmUoJy4uL2luZm8vaW5pX2dldCcpKCd1bmljb2RlLnNlbWFudGljcycpIDogdW5kZWZpbmVkKSB8fCAnb2ZmJztcbiAgaWYgKGluaVZhbCA9PT0gJ29mZicpIHtcbiAgICByZXR1cm4gc3RyLmxlbmd0aDtcbiAgfVxuXG4gIHZhciBpID0gMDtcbiAgdmFyIGxndGggPSAwO1xuXG4gIHZhciBnZXRXaG9sZUNoYXIgPSBmdW5jdGlvbiBnZXRXaG9sZUNoYXIoc3RyLCBpKSB7XG4gICAgdmFyIGNvZGUgPSBzdHIuY2hhckNvZGVBdChpKTtcbiAgICB2YXIgbmV4dCA9ICcnO1xuICAgIHZhciBwcmV2ID0gJyc7XG4gICAgaWYgKGNvZGUgPj0gMHhkODAwICYmIGNvZGUgPD0gMHhkYmZmKSB7XG4gICAgICAvLyBIaWdoIHN1cnJvZ2F0ZSAoY291bGQgY2hhbmdlIGxhc3QgaGV4IHRvIDB4REI3RiB0b1xuICAgICAgLy8gdHJlYXQgaGlnaCBwcml2YXRlIHN1cnJvZ2F0ZXMgYXMgc2luZ2xlIGNoYXJhY3RlcnMpXG4gICAgICBpZiAoc3RyLmxlbmd0aCA8PSBpICsgMSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0hpZ2ggc3Vycm9nYXRlIHdpdGhvdXQgZm9sbG93aW5nIGxvdyBzdXJyb2dhdGUnKTtcbiAgICAgIH1cbiAgICAgIG5leHQgPSBzdHIuY2hhckNvZGVBdChpICsgMSk7XG4gICAgICBpZiAobmV4dCA8IDB4ZGMwMCB8fCBuZXh0ID4gMHhkZmZmKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignSGlnaCBzdXJyb2dhdGUgd2l0aG91dCBmb2xsb3dpbmcgbG93IHN1cnJvZ2F0ZScpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHN0ci5jaGFyQXQoaSkgKyBzdHIuY2hhckF0KGkgKyAxKTtcbiAgICB9IGVsc2UgaWYgKGNvZGUgPj0gMHhkYzAwICYmIGNvZGUgPD0gMHhkZmZmKSB7XG4gICAgICAvLyBMb3cgc3Vycm9nYXRlXG4gICAgICBpZiAoaSA9PT0gMCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0xvdyBzdXJyb2dhdGUgd2l0aG91dCBwcmVjZWRpbmcgaGlnaCBzdXJyb2dhdGUnKTtcbiAgICAgIH1cbiAgICAgIHByZXYgPSBzdHIuY2hhckNvZGVBdChpIC0gMSk7XG4gICAgICBpZiAocHJldiA8IDB4ZDgwMCB8fCBwcmV2ID4gMHhkYmZmKSB7XG4gICAgICAgIC8vIChjb3VsZCBjaGFuZ2UgbGFzdCBoZXggdG8gMHhEQjdGIHRvIHRyZWF0IGhpZ2ggcHJpdmF0ZSBzdXJyb2dhdGVzXG4gICAgICAgIC8vIGFzIHNpbmdsZSBjaGFyYWN0ZXJzKVxuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0xvdyBzdXJyb2dhdGUgd2l0aG91dCBwcmVjZWRpbmcgaGlnaCBzdXJyb2dhdGUnKTtcbiAgICAgIH1cbiAgICAgIC8vIFdlIGNhbiBwYXNzIG92ZXIgbG93IHN1cnJvZ2F0ZXMgbm93IGFzIHRoZSBzZWNvbmRcbiAgICAgIC8vIGNvbXBvbmVudCBpbiBhIHBhaXIgd2hpY2ggd2UgaGF2ZSBhbHJlYWR5IHByb2Nlc3NlZFxuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICByZXR1cm4gc3RyLmNoYXJBdChpKTtcbiAgfTtcblxuICBmb3IgKGkgPSAwLCBsZ3RoID0gMDsgaSA8IHN0ci5sZW5ndGg7IGkrKykge1xuICAgIGlmIChnZXRXaG9sZUNoYXIoc3RyLCBpKSA9PT0gZmFsc2UpIHtcbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cbiAgICAvLyBBZGFwdCB0aGlzIGxpbmUgYXQgdGhlIHRvcCBvZiBhbnkgbG9vcCwgcGFzc2luZyBpbiB0aGUgd2hvbGUgc3RyaW5nIGFuZFxuICAgIC8vIHRoZSBjdXJyZW50IGl0ZXJhdGlvbiBhbmQgcmV0dXJuaW5nIGEgdmFyaWFibGUgdG8gcmVwcmVzZW50IHRoZSBpbmRpdmlkdWFsIGNoYXJhY3RlcjtcbiAgICAvLyBwdXJwb3NlIGlzIHRvIHRyZWF0IHRoZSBmaXJzdCBwYXJ0IG9mIGEgc3Vycm9nYXRlIHBhaXIgYXMgdGhlIHdob2xlIGNoYXJhY3RlciBhbmQgdGhlblxuICAgIC8vIGlnbm9yZSB0aGUgc2Vjb25kIHBhcnRcbiAgICBsZ3RoKys7XG4gIH1cblxuICByZXR1cm4gbGd0aDtcbn07XG4vLyMgc291cmNlTWFwcGluZ1VSTD1zdHJsZW4uanMubWFwIiwiJ3VzZSBzdHJpY3QnO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIGlzX251bWVyaWMobWl4ZWRWYXIpIHtcbiAgLy8gIGRpc2N1c3MgYXQ6IGh0dHBzOi8vbG9jdXR1cy5pby9waHAvaXNfbnVtZXJpYy9cbiAgLy8gb3JpZ2luYWwgYnk6IEtldmluIHZhbiBab25uZXZlbGQgKGh0dHBzOi8va3Z6LmlvKVxuICAvLyBpbXByb3ZlZCBieTogRGF2aWRcbiAgLy8gaW1wcm92ZWQgYnk6IHRhaXRoXG4gIC8vIGJ1Z2ZpeGVkIGJ5OiBUaW0gZGUgS29uaW5nXG4gIC8vIGJ1Z2ZpeGVkIGJ5OiBXZWJEZXZIb2JvIChodHRwczovL3dlYmRldmhvYm8uYmxvZ3Nwb3QuY29tLylcbiAgLy8gYnVnZml4ZWQgYnk6IEJyZXR0IFphbWlyIChodHRwczovL2JyZXR0LXphbWlyLm1lKVxuICAvLyBidWdmaXhlZCBieTogRGVuaXMgQ2hlbnUgKGh0dHBzOi8vc2hub3VsbGUubmV0KVxuICAvLyAgIGV4YW1wbGUgMTogaXNfbnVtZXJpYygxODYuMzEpXG4gIC8vICAgcmV0dXJucyAxOiB0cnVlXG4gIC8vICAgZXhhbXBsZSAyOiBpc19udW1lcmljKCdLZXZpbiB2YW4gWm9ubmV2ZWxkJylcbiAgLy8gICByZXR1cm5zIDI6IGZhbHNlXG4gIC8vICAgZXhhbXBsZSAzOiBpc19udW1lcmljKCcgKzE4Ni4zMWUyJylcbiAgLy8gICByZXR1cm5zIDM6IHRydWVcbiAgLy8gICBleGFtcGxlIDQ6IGlzX251bWVyaWMoJycpXG4gIC8vICAgcmV0dXJucyA0OiBmYWxzZVxuICAvLyAgIGV4YW1wbGUgNTogaXNfbnVtZXJpYyhbXSlcbiAgLy8gICByZXR1cm5zIDU6IGZhbHNlXG4gIC8vICAgZXhhbXBsZSA2OiBpc19udW1lcmljKCcxICcpXG4gIC8vICAgcmV0dXJucyA2OiBmYWxzZVxuXG4gIHZhciB3aGl0ZXNwYWNlID0gWycgJywgJ1xcbicsICdcXHInLCAnXFx0JywgJ1xcZicsICdcXHgwYicsICdcXHhhMCcsICdcXHUyMDAwJywgJ1xcdTIwMDEnLCAnXFx1MjAwMicsICdcXHUyMDAzJywgJ1xcdTIwMDQnLCAnXFx1MjAwNScsICdcXHUyMDA2JywgJ1xcdTIwMDcnLCAnXFx1MjAwOCcsICdcXHUyMDA5JywgJ1xcdTIwMEEnLCAnXFx1MjAwQicsICdcXHUyMDI4JywgJ1xcdTIwMjknLCAnXFx1MzAwMCddLmpvaW4oJycpO1xuXG4gIC8vIEB0b2RvOiBCcmVhayB0aGlzIHVwIHVzaW5nIG1hbnkgc2luZ2xlIGNvbmRpdGlvbnMgd2l0aCBlYXJseSByZXR1cm5zXG4gIHJldHVybiAodHlwZW9mIG1peGVkVmFyID09PSAnbnVtYmVyJyB8fCB0eXBlb2YgbWl4ZWRWYXIgPT09ICdzdHJpbmcnICYmIHdoaXRlc3BhY2UuaW5kZXhPZihtaXhlZFZhci5zbGljZSgtMSkpID09PSAtMSkgJiYgbWl4ZWRWYXIgIT09ICcnICYmICFpc05hTihtaXhlZFZhcik7XG59O1xuLy8jIHNvdXJjZU1hcHBpbmdVUkw9aXNfbnVtZXJpYy5qcy5tYXAiLCIvLyBUaGUgbW9kdWxlIGNhY2hlXG52YXIgX193ZWJwYWNrX21vZHVsZV9jYWNoZV9fID0ge307XG5cbi8vIFRoZSByZXF1aXJlIGZ1bmN0aW9uXG5mdW5jdGlvbiBfX3dlYnBhY2tfcmVxdWlyZV9fKG1vZHVsZUlkKSB7XG5cdC8vIENoZWNrIGlmIG1vZHVsZSBpcyBpbiBjYWNoZVxuXHR2YXIgY2FjaGVkTW9kdWxlID0gX193ZWJwYWNrX21vZHVsZV9jYWNoZV9fW21vZHVsZUlkXTtcblx0aWYgKGNhY2hlZE1vZHVsZSAhPT0gdW5kZWZpbmVkKSB7XG5cdFx0cmV0dXJuIGNhY2hlZE1vZHVsZS5leHBvcnRzO1xuXHR9XG5cdC8vIENyZWF0ZSBhIG5ldyBtb2R1bGUgKGFuZCBwdXQgaXQgaW50byB0aGUgY2FjaGUpXG5cdHZhciBtb2R1bGUgPSBfX3dlYnBhY2tfbW9kdWxlX2NhY2hlX19bbW9kdWxlSWRdID0ge1xuXHRcdC8vIG5vIG1vZHVsZS5pZCBuZWVkZWRcblx0XHQvLyBubyBtb2R1bGUubG9hZGVkIG5lZWRlZFxuXHRcdGV4cG9ydHM6IHt9XG5cdH07XG5cblx0Ly8gRXhlY3V0ZSB0aGUgbW9kdWxlIGZ1bmN0aW9uXG5cdF9fd2VicGFja19tb2R1bGVzX19bbW9kdWxlSWRdKG1vZHVsZSwgbW9kdWxlLmV4cG9ydHMsIF9fd2VicGFja19yZXF1aXJlX18pO1xuXG5cdC8vIFJldHVybiB0aGUgZXhwb3J0cyBvZiB0aGUgbW9kdWxlXG5cdHJldHVybiBtb2R1bGUuZXhwb3J0cztcbn1cblxuIiwiLy8gZ2V0RGVmYXVsdEV4cG9ydCBmdW5jdGlvbiBmb3IgY29tcGF0aWJpbGl0eSB3aXRoIG5vbi1oYXJtb255IG1vZHVsZXNcbl9fd2VicGFja19yZXF1aXJlX18ubiA9IGZ1bmN0aW9uKG1vZHVsZSkge1xuXHR2YXIgZ2V0dGVyID0gbW9kdWxlICYmIG1vZHVsZS5fX2VzTW9kdWxlID9cblx0XHRmdW5jdGlvbigpIHsgcmV0dXJuIG1vZHVsZVsnZGVmYXVsdCddOyB9IDpcblx0XHRmdW5jdGlvbigpIHsgcmV0dXJuIG1vZHVsZTsgfTtcblx0X193ZWJwYWNrX3JlcXVpcmVfXy5kKGdldHRlciwgeyBhOiBnZXR0ZXIgfSk7XG5cdHJldHVybiBnZXR0ZXI7XG59OyIsIi8vIGRlZmluZSBnZXR0ZXIgZnVuY3Rpb25zIGZvciBoYXJtb255IGV4cG9ydHNcbl9fd2VicGFja19yZXF1aXJlX18uZCA9IGZ1bmN0aW9uKGV4cG9ydHMsIGRlZmluaXRpb24pIHtcblx0Zm9yKHZhciBrZXkgaW4gZGVmaW5pdGlvbikge1xuXHRcdGlmKF9fd2VicGFja19yZXF1aXJlX18ubyhkZWZpbml0aW9uLCBrZXkpICYmICFfX3dlYnBhY2tfcmVxdWlyZV9fLm8oZXhwb3J0cywga2V5KSkge1xuXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIGtleSwgeyBlbnVtZXJhYmxlOiB0cnVlLCBnZXQ6IGRlZmluaXRpb25ba2V5XSB9KTtcblx0XHR9XG5cdH1cbn07IiwiX193ZWJwYWNrX3JlcXVpcmVfXy5nID0gKGZ1bmN0aW9uKCkge1xuXHRpZiAodHlwZW9mIGdsb2JhbFRoaXMgPT09ICdvYmplY3QnKSByZXR1cm4gZ2xvYmFsVGhpcztcblx0dHJ5IHtcblx0XHRyZXR1cm4gdGhpcyB8fCBuZXcgRnVuY3Rpb24oJ3JldHVybiB0aGlzJykoKTtcblx0fSBjYXRjaCAoZSkge1xuXHRcdGlmICh0eXBlb2Ygd2luZG93ID09PSAnb2JqZWN0JykgcmV0dXJuIHdpbmRvdztcblx0fVxufSkoKTsiLCJfX3dlYnBhY2tfcmVxdWlyZV9fLm8gPSBmdW5jdGlvbihvYmosIHByb3ApIHsgcmV0dXJuIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChvYmosIHByb3ApOyB9IiwiLy8gZGVmaW5lIF9fZXNNb2R1bGUgb24gZXhwb3J0c1xuX193ZWJwYWNrX3JlcXVpcmVfXy5yID0gZnVuY3Rpb24oZXhwb3J0cykge1xuXHRpZih0eXBlb2YgU3ltYm9sICE9PSAndW5kZWZpbmVkJyAmJiBTeW1ib2wudG9TdHJpbmdUYWcpIHtcblx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgU3ltYm9sLnRvU3RyaW5nVGFnLCB7IHZhbHVlOiAnTW9kdWxlJyB9KTtcblx0fVxuXHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgJ19fZXNNb2R1bGUnLCB7IHZhbHVlOiB0cnVlIH0pO1xufTsiLCIvKiFcbiAqIExhcmF2ZWwgSmF2YXNjcmlwdCBWYWxpZGF0aW9uXG4gKlxuICogaHR0cHM6Ly9naXRodWIuY29tL3Byb2VuZ3NvZnQvbGFyYXZlbC1qc3ZhbGlkYXRpb25cbiAqXG4gKiBIZWxwZXIgZnVuY3Rpb25zIHVzZWQgYnkgdmFsaWRhdG9yc1xuICpcbiAqIENvcHlyaWdodCAoYykgMjAxNyBQcm9lbmdzb2Z0XG4gKiBSZWxlYXNlZCB1bmRlciB0aGUgTUlUIGxpY2Vuc2VcbiAqL1xuXG5pbXBvcnQgc3RybGVuIGZyb20gJ2xvY3V0dXMvcGhwL3N0cmluZ3Mvc3RybGVuJztcbmltcG9ydCBhcnJheV9kaWZmIGZyb20gJ2xvY3V0dXMvcGhwL2FycmF5L2FycmF5X2RpZmYnO1xuaW1wb3J0IHN0cnRvdGltZSBmcm9tICdsb2N1dHVzL3BocC9kYXRldGltZS9zdHJ0b3RpbWUnO1xuaW1wb3J0IGlzX251bWVyaWMgZnJvbSAnbG9jdXR1cy9waHAvdmFyL2lzX251bWVyaWMnO1xuXG4kLmV4dGVuZCh0cnVlLCBsYXJhdmVsVmFsaWRhdGlvbiwge1xuXG4gICAgaGVscGVyczoge1xuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBOdW1lcmljIHJ1bGVzXG4gICAgICAgICAqL1xuICAgICAgICBudW1lcmljUnVsZXM6IFsnSW50ZWdlcicsICdOdW1lcmljJ10sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIEdldHMgdGhlIGZpbGUgaW5mb3JtYXRpb24gZnJvbSBmaWxlIGlucHV0LlxuICAgICAgICAgKlxuICAgICAgICAgKiBAcGFyYW0gZmllbGRPYmpcbiAgICAgICAgICogQHBhcmFtIGluZGV4XG4gICAgICAgICAqIEByZXR1cm5zIHt7ZmlsZTogKiwgZXh0ZW5zaW9uOiBzdHJpbmcsIHNpemU6IG51bWJlcn19XG4gICAgICAgICAqL1xuICAgICAgICBmaWxlaW5mbzogZnVuY3Rpb24gKGZpZWxkT2JqLCBpbmRleCkge1xuICAgICAgICAgICAgdmFyIEZpbGVOYW1lID0gZmllbGRPYmoudmFsdWU7XG4gICAgICAgICAgICBpbmRleCA9IHR5cGVvZiBpbmRleCAhPT0gJ3VuZGVmaW5lZCcgPyBpbmRleCA6IDA7XG4gICAgICAgICAgICBpZiAoIGZpZWxkT2JqLmZpbGVzICE9PSBudWxsICkge1xuICAgICAgICAgICAgICAgIGlmICh0eXBlb2YgZmllbGRPYmouZmlsZXNbaW5kZXhdICE9PSAndW5kZWZpbmVkJykge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICAgICAgICAgICAgZmlsZTogRmlsZU5hbWUsXG4gICAgICAgICAgICAgICAgICAgICAgICBleHRlbnNpb246IEZpbGVOYW1lLnN1YnN0cihGaWxlTmFtZS5sYXN0SW5kZXhPZignLicpICsgMSksXG4gICAgICAgICAgICAgICAgICAgICAgICBzaXplOiBmaWVsZE9iai5maWxlc1tpbmRleF0uc2l6ZSAvIDEwMjQsXG4gICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiBmaWVsZE9iai5maWxlc1tpbmRleF0udHlwZVxuICAgICAgICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgfSxcblxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBHZXRzIHRoZSBzZWxlY3RvcnMgZm9yIHRoIHNwZWNpZmllZCBmaWVsZCBuYW1lcy5cbiAgICAgICAgICpcbiAgICAgICAgICogQHBhcmFtIG5hbWVzXG4gICAgICAgICAqIEByZXR1cm5zIHtzdHJpbmd9XG4gICAgICAgICAqL1xuICAgICAgICBzZWxlY3RvcjogZnVuY3Rpb24gKG5hbWVzKSB7XG4gICAgICAgICAgICB2YXIgc2VsZWN0b3IgPSBbXTtcbiAgICAgICAgICAgIGlmICghIHRoaXMuaXNBcnJheShuYW1lcykpICB7XG4gICAgICAgICAgICAgICAgbmFtZXMgPSBbbmFtZXNdO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBuYW1lcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgIHNlbGVjdG9yLnB1c2goXCJbbmFtZT0nXCIgKyBuYW1lc1tpXSArIFwiJ11cIik7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gc2VsZWN0b3Iuam9pbigpO1xuICAgICAgICB9LFxuXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIENoZWNrIGlmIGVsZW1lbnQgaGFzIG51bWVyaWMgcnVsZXMuXG4gICAgICAgICAqXG4gICAgICAgICAqIEBwYXJhbSBlbGVtZW50XG4gICAgICAgICAqIEByZXR1cm5zIHtib29sZWFufVxuICAgICAgICAgKi9cbiAgICAgICAgaGFzTnVtZXJpY1J1bGVzOiBmdW5jdGlvbiAoZWxlbWVudCkge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMuaGFzUnVsZXMoZWxlbWVudCwgdGhpcy5udW1lcmljUnVsZXMpO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBDaGVjayBpZiBlbGVtZW50IGhhcyBwYXNzZWQgcnVsZXMuXG4gICAgICAgICAqXG4gICAgICAgICAqIEBwYXJhbSBlbGVtZW50XG4gICAgICAgICAqIEBwYXJhbSBydWxlc1xuICAgICAgICAgKiBAcmV0dXJucyB7Ym9vbGVhbn1cbiAgICAgICAgICovXG4gICAgICAgIGhhc1J1bGVzOiBmdW5jdGlvbiAoZWxlbWVudCwgcnVsZXMpIHtcblxuICAgICAgICAgICAgdmFyIGZvdW5kID0gZmFsc2U7XG4gICAgICAgICAgICBpZiAodHlwZW9mIHJ1bGVzID09PSAnc3RyaW5nJykge1xuICAgICAgICAgICAgICAgIHJ1bGVzID0gW3J1bGVzXTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdmFyIHZhbGlkYXRvciA9ICQuZGF0YShlbGVtZW50LmZvcm0sIFwidmFsaWRhdG9yXCIpO1xuICAgICAgICAgICAgdmFyIGxpc3RSdWxlcyA9IFtdO1xuICAgICAgICAgICAgdmFyIGNhY2hlID0gdmFsaWRhdG9yLmFycmF5UnVsZXNDYWNoZTtcbiAgICAgICAgICAgIGlmIChlbGVtZW50Lm5hbWUgaW4gY2FjaGUpIHtcbiAgICAgICAgICAgICAgICAkLmVhY2goY2FjaGVbZWxlbWVudC5uYW1lXSwgZnVuY3Rpb24gKGluZGV4LCBhcnJheVJ1bGUpIHtcbiAgICAgICAgICAgICAgICAgICAgbGlzdFJ1bGVzLnB1c2goYXJyYXlSdWxlKTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmIChlbGVtZW50Lm5hbWUgaW4gdmFsaWRhdG9yLnNldHRpbmdzLnJ1bGVzKSB7XG4gICAgICAgICAgICAgICAgbGlzdFJ1bGVzLnB1c2godmFsaWRhdG9yLnNldHRpbmdzLnJ1bGVzW2VsZW1lbnQubmFtZV0pO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgJC5lYWNoKGxpc3RSdWxlcywgZnVuY3Rpb24oaW5kZXgsb2JqUnVsZXMpe1xuICAgICAgICAgICAgICAgIGlmICgnbGFyYXZlbFZhbGlkYXRpb24nIGluIG9ialJ1bGVzKSB7XG4gICAgICAgICAgICAgICAgICAgIHZhciBfcnVsZXM9b2JqUnVsZXMubGFyYXZlbFZhbGlkYXRpb247XG4gICAgICAgICAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgX3J1bGVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoJC5pbkFycmF5KF9ydWxlc1tpXVswXSxydWxlcykgIT09IC0xKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZm91bmQgPSB0cnVlO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICByZXR1cm4gZm91bmQ7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIFJldHVybiB0aGUgc3RyaW5nIGxlbmd0aCB1c2luZyBQSFAgZnVuY3Rpb24uXG4gICAgICAgICAqIGh0dHA6Ly9waHAubmV0L21hbnVhbC9lbi9mdW5jdGlvbi5zdHJsZW4ucGhwXG4gICAgICAgICAqIGh0dHA6Ly9waHBqcy5vcmcvZnVuY3Rpb25zL3N0cmxlbi9cbiAgICAgICAgICpcbiAgICAgICAgICogQHBhcmFtIHN0cmluZ1xuICAgICAgICAgKi9cbiAgICAgICAgc3RybGVuOiBmdW5jdGlvbiAoc3RyaW5nKSB7XG4gICAgICAgICAgICByZXR1cm4gc3RybGVuKHN0cmluZyk7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIEdldCB0aGUgc2l6ZSBvZiB0aGUgb2JqZWN0IGRlcGVuZGluZyBvZiBoaXMgdHlwZS5cbiAgICAgICAgICpcbiAgICAgICAgICogQHBhcmFtIG9ialxuICAgICAgICAgKiBAcGFyYW0gZWxlbWVudFxuICAgICAgICAgKiBAcGFyYW0gdmFsdWVcbiAgICAgICAgICogQHJldHVybnMgaW50XG4gICAgICAgICAqL1xuICAgICAgICBnZXRTaXplOiBmdW5jdGlvbiBnZXRTaXplKG9iaiwgZWxlbWVudCwgdmFsdWUpIHtcblxuICAgICAgICAgICAgaWYgKHRoaXMuaGFzTnVtZXJpY1J1bGVzKGVsZW1lbnQpICYmIHRoaXMuaXNfbnVtZXJpYyh2YWx1ZSkpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gcGFyc2VGbG9hdCh2YWx1ZSk7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKHRoaXMuaXNBcnJheSh2YWx1ZSkpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gcGFyc2VGbG9hdCh2YWx1ZS5sZW5ndGgpO1xuICAgICAgICAgICAgfSBlbHNlIGlmIChlbGVtZW50LnR5cGUgPT09ICdmaWxlJykge1xuICAgICAgICAgICAgICAgIHJldHVybiBwYXJzZUZsb2F0KE1hdGguZmxvb3IodGhpcy5maWxlaW5mbyhlbGVtZW50KS5zaXplKSk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHJldHVybiBwYXJzZUZsb2F0KHRoaXMuc3RybGVuKHZhbHVlKSk7XG4gICAgICAgIH0sXG5cblxuICAgICAgICAvKipcbiAgICAgICAgICogUmV0dXJuIHNwZWNpZmllZCBydWxlIGZyb20gZWxlbWVudC5cbiAgICAgICAgICpcbiAgICAgICAgICogQHBhcmFtIHJ1bGVcbiAgICAgICAgICogQHBhcmFtIGVsZW1lbnRcbiAgICAgICAgICogQHJldHVybnMgb2JqZWN0XG4gICAgICAgICAqL1xuICAgICAgICBnZXRMYXJhdmVsVmFsaWRhdGlvbjogZnVuY3Rpb24ocnVsZSwgZWxlbWVudCkge1xuXG4gICAgICAgICAgICB2YXIgZm91bmQgPSB1bmRlZmluZWQ7XG4gICAgICAgICAgICAkLmVhY2goJC52YWxpZGF0b3Iuc3RhdGljUnVsZXMoZWxlbWVudCksIGZ1bmN0aW9uKGtleSwgcnVsZXMpIHtcbiAgICAgICAgICAgICAgICBpZiAoa2V5PT09XCJsYXJhdmVsVmFsaWRhdGlvblwiKSB7XG4gICAgICAgICAgICAgICAgICAgICQuZWFjaChydWxlcywgZnVuY3Rpb24gKGksIHZhbHVlKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAodmFsdWVbMF09PT1ydWxlKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZm91bmQ9dmFsdWU7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICByZXR1cm4gZm91bmQ7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIFJldHVybiBoZSB0aW1lc3RhbXAgb2YgdmFsdWUgcGFzc2VkIHVzaW5nIGZvcm1hdCBvciBkZWZhdWx0IGZvcm1hdCBpbiBlbGVtZW50LlxuICAgICAgICAgKlxuICAgICAgICAgKiBAcGFyYW0gdmFsdWVcbiAgICAgICAgICogQHBhcmFtIGZvcm1hdFxuICAgICAgICAgKiBAcmV0dXJucyB7Ym9vbGVhbnxpbnR9XG4gICAgICAgICAqL1xuICAgICAgICBwYXJzZVRpbWU6IGZ1bmN0aW9uICh2YWx1ZSwgZm9ybWF0KSB7XG5cbiAgICAgICAgICAgIHZhciB0aW1lVmFsdWUgPSBmYWxzZTtcbiAgICAgICAgICAgIHZhciBmbXQgPSBuZXcgRGF0ZUZvcm1hdHRlcigpO1xuXG4gICAgICAgICAgICBpZiAodHlwZW9mIHZhbHVlID09PSAnbnVtYmVyJyAmJiB0eXBlb2YgZm9ybWF0ID09PSAndW5kZWZpbmVkJykge1xuICAgICAgICAgICAgICAgIHJldHVybiB2YWx1ZTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKHR5cGVvZiBmb3JtYXQgPT09ICdvYmplY3QnKSB7XG4gICAgICAgICAgICAgICAgdmFyIGRhdGVSdWxlID0gdGhpcy5nZXRMYXJhdmVsVmFsaWRhdGlvbignRGF0ZUZvcm1hdCcsIGZvcm1hdCk7XG4gICAgICAgICAgICAgICAgaWYgKGRhdGVSdWxlICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgICAgICAgZm9ybWF0ID0gZGF0ZVJ1bGVbMV1bMF07XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgZm9ybWF0ID0gbnVsbDtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmIChmb3JtYXQgPT0gbnVsbCkge1xuICAgICAgICAgICAgICAgIHRpbWVWYWx1ZSA9IHRoaXMuc3RydG90aW1lKHZhbHVlKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgdGltZVZhbHVlID0gZm10LnBhcnNlRGF0ZSh2YWx1ZSwgZm9ybWF0KTtcbiAgICAgICAgICAgICAgICBpZiAodGltZVZhbHVlIGluc3RhbmNlb2YgRGF0ZSAmJiBmbXQuZm9ybWF0RGF0ZSh0aW1lVmFsdWUsIGZvcm1hdCkgPT09IHZhbHVlKSB7XG4gICAgICAgICAgICAgICAgICAgIHRpbWVWYWx1ZSA9IE1hdGgucm91bmQoKHRpbWVWYWx1ZS5nZXRUaW1lKCkgLyAxMDAwKSk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgdGltZVZhbHVlID0gZmFsc2U7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICByZXR1cm4gdGltZVZhbHVlO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBDb21wYXJlIGEgZ2l2ZW4gZGF0ZSBhZ2FpbnN0IGFub3RoZXIgdXNpbmcgYW4gb3BlcmF0b3IuXG4gICAgICAgICAqXG4gICAgICAgICAqIEBwYXJhbSB2YWxpZGF0b3JcbiAgICAgICAgICogQHBhcmFtIHZhbHVlXG4gICAgICAgICAqIEBwYXJhbSBlbGVtZW50XG4gICAgICAgICAqIEBwYXJhbSBwYXJhbXNcbiAgICAgICAgICogQHBhcmFtIG9wZXJhdG9yXG4gICAgICAgICAqIEByZXR1cm4ge2Jvb2xlYW59XG4gICAgICAgICAqL1xuICAgICAgICBjb21wYXJlRGF0ZXM6IGZ1bmN0aW9uICh2YWxpZGF0b3IsIHZhbHVlLCBlbGVtZW50LCBwYXJhbXMsIG9wZXJhdG9yKSB7XG5cbiAgICAgICAgICAgIHZhciB0aW1lQ29tcGFyZSA9IHRoaXMucGFyc2VUaW1lKHBhcmFtcyk7XG5cbiAgICAgICAgICAgIGlmICghdGltZUNvbXBhcmUpIHtcbiAgICAgICAgICAgICAgICB2YXIgdGFyZ2V0ID0gdGhpcy5kZXBlbmRlbnRFbGVtZW50KHZhbGlkYXRvciwgZWxlbWVudCwgcGFyYW1zKTtcbiAgICAgICAgICAgICAgICBpZiAodGFyZ2V0ID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB0aW1lQ29tcGFyZSA9IHRoaXMucGFyc2VUaW1lKHZhbGlkYXRvci5lbGVtZW50VmFsdWUodGFyZ2V0KSwgdGFyZ2V0KTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdmFyIHRpbWVWYWx1ZSA9IHRoaXMucGFyc2VUaW1lKHZhbHVlLCBlbGVtZW50KTtcbiAgICAgICAgICAgIGlmICh0aW1lVmFsdWUgPT09IGZhbHNlKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBzd2l0Y2ggKG9wZXJhdG9yKSB7XG4gICAgICAgICAgICAgICAgY2FzZSAnPCc6XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiB0aW1lVmFsdWUgPCB0aW1lQ29tcGFyZTtcblxuICAgICAgICAgICAgICAgIGNhc2UgJzw9JzpcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHRpbWVWYWx1ZSA8PSB0aW1lQ29tcGFyZTtcblxuICAgICAgICAgICAgICAgIGNhc2UgJz09JzpcbiAgICAgICAgICAgICAgICBjYXNlICc9PT0nOlxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gdGltZVZhbHVlID09PSB0aW1lQ29tcGFyZTtcblxuICAgICAgICAgICAgICAgIGNhc2UgJz4nOlxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gdGltZVZhbHVlID4gdGltZUNvbXBhcmU7XG5cbiAgICAgICAgICAgICAgICBjYXNlICc+PSc6XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiB0aW1lVmFsdWUgPj0gdGltZUNvbXBhcmU7XG5cbiAgICAgICAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1Vuc3VwcG9ydGVkIG9wZXJhdG9yLicpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBUaGlzIG1ldGhvZCBhbGxvd3MgeW91IHRvIGludGVsbGlnZW50bHkgZ3Vlc3MgdGhlIGRhdGUgYnkgY2xvc2VseSBtYXRjaGluZyB0aGUgc3BlY2lmaWMgZm9ybWF0LlxuICAgICAgICAgKlxuICAgICAgICAgKiBAcGFyYW0gdmFsdWVcbiAgICAgICAgICogQHBhcmFtIGZvcm1hdFxuICAgICAgICAgKiBAcmV0dXJucyB7RGF0ZX1cbiAgICAgICAgICovXG4gICAgICAgIGd1ZXNzRGF0ZTogZnVuY3Rpb24gKHZhbHVlLCBmb3JtYXQpIHtcbiAgICAgICAgICAgIHZhciBmbXQgPSBuZXcgRGF0ZUZvcm1hdHRlcigpO1xuICAgICAgICAgICAgcmV0dXJuIGZtdC5ndWVzc0RhdGUodmFsdWUsIGZvcm1hdClcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogUmV0dXJucyBVbml4IHRpbWVzdGFtcCBiYXNlZCBvbiBQSFAgZnVuY3Rpb24gc3Ryb3RvdGltZS5cbiAgICAgICAgICogaHR0cDovL3BocC5uZXQvbWFudWFsL2VzL2Z1bmN0aW9uLnN0cnRvdGltZS5waHBcbiAgICAgICAgICogaHR0cDovL3BocGpzLm9yZy9mdW5jdGlvbnMvc3RydG90aW1lL1xuICAgICAgICAgKlxuICAgICAgICAgKiBAcGFyYW0gdGV4dFxuICAgICAgICAgKiBAcGFyYW0gbm93XG4gICAgICAgICAqIEByZXR1cm5zIHsqfVxuICAgICAgICAgKi9cbiAgICAgICAgc3RydG90aW1lOiBmdW5jdGlvbiAodGV4dCwgbm93KSB7XG4gICAgICAgICAgICByZXR1cm4gc3RydG90aW1lKHRleHQsIG5vdylcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogUmV0dXJucyBpZiB2YWx1ZSBpcyBudW1lcmljLlxuICAgICAgICAgKiBodHRwOi8vcGhwLm5ldC9tYW51YWwvZXMvdmFyLmlzX251bWVyaWMucGhwXG4gICAgICAgICAqIGh0dHA6Ly9waHBqcy5vcmcvZnVuY3Rpb25zL2lzX251bWVyaWMvXG4gICAgICAgICAqXG4gICAgICAgICAqIEBwYXJhbSBtaXhlZF92YXJcbiAgICAgICAgICogQHJldHVybnMgeyp9XG4gICAgICAgICAqL1xuICAgICAgICBpc19udW1lcmljOiBmdW5jdGlvbiAobWl4ZWRfdmFyKSB7XG4gICAgICAgICAgICByZXR1cm4gaXNfbnVtZXJpYyhtaXhlZF92YXIpXG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIENoZWNrIHdoZXRoZXIgdGhlIGFyZ3VtZW50IGlzIG9mIHR5cGUgQXJyYXkuXG4gICAgICAgICAqIGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0phdmFTY3JpcHQvUmVmZXJlbmNlL0dsb2JhbF9PYmplY3RzL0FycmF5L2lzQXJyYXkjUG9seWZpbGxcbiAgICAgICAgICpcbiAgICAgICAgICogQHBhcmFtIGFyZ1xuICAgICAgICAgKiBAcmV0dXJucyB7Ym9vbGVhbn1cbiAgICAgICAgICovXG4gICAgICAgIGlzQXJyYXk6IGZ1bmN0aW9uKGFyZykge1xuICAgICAgICAgICAgcmV0dXJuIE9iamVjdC5wcm90b3R5cGUudG9TdHJpbmcuY2FsbChhcmcpID09PSAnW29iamVjdCBBcnJheV0nO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBSZXR1cm5zIEFycmF5IGRpZmYgYmFzZWQgb24gUEhQIGZ1bmN0aW9uIGFycmF5X2RpZmYuXG4gICAgICAgICAqIGh0dHA6Ly9waHAubmV0L21hbnVhbC9lcy9mdW5jdGlvbi5hcnJheV9kaWZmLnBocFxuICAgICAgICAgKiBodHRwOi8vcGhwanMub3JnL2Z1bmN0aW9ucy9hcnJheV9kaWZmL1xuICAgICAgICAgKlxuICAgICAgICAgKiBAcGFyYW0gYXJyMVxuICAgICAgICAgKiBAcGFyYW0gYXJyMlxuICAgICAgICAgKiBAcmV0dXJucyB7Kn1cbiAgICAgICAgICovXG4gICAgICAgIGFycmF5RGlmZjogZnVuY3Rpb24gKGFycjEsIGFycjIpIHtcbiAgICAgICAgICAgIHJldHVybiBhcnJheV9kaWZmKGFycjEsIGFycjIpO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBDaGVjayB3aGV0aGVyIHR3byBhcnJheXMgYXJlIGVxdWFsIHRvIG9uZSBhbm90aGVyLlxuICAgICAgICAgKlxuICAgICAgICAgKiBAcGFyYW0gYXJyMVxuICAgICAgICAgKiBAcGFyYW0gYXJyMlxuICAgICAgICAgKiBAcmV0dXJucyB7Kn1cbiAgICAgICAgICovXG4gICAgICAgIGFycmF5RXF1YWxzOiBmdW5jdGlvbiAoYXJyMSwgYXJyMikge1xuICAgICAgICAgICAgaWYgKCEgdGhpcy5pc0FycmF5KGFycjEpIHx8ICEgdGhpcy5pc0FycmF5KGFycjIpKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgXG4gICAgICAgICAgICBpZiAoYXJyMS5sZW5ndGggIT09IGFycjIubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgXG4gICAgICAgICAgICByZXR1cm4gJC5pc0VtcHR5T2JqZWN0KHRoaXMuYXJyYXlEaWZmKGFycjEsIGFycjIpKTtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogTWFrZXMgZWxlbWVudCBkZXBlbmRhbnQgZnJvbSBvdGhlci5cbiAgICAgICAgICpcbiAgICAgICAgICogQHBhcmFtIHZhbGlkYXRvclxuICAgICAgICAgKiBAcGFyYW0gZWxlbWVudFxuICAgICAgICAgKiBAcGFyYW0gbmFtZVxuICAgICAgICAgKiBAcmV0dXJucyB7Kn1cbiAgICAgICAgICovXG4gICAgICAgIGRlcGVuZGVudEVsZW1lbnQ6IGZ1bmN0aW9uKHZhbGlkYXRvciwgZWxlbWVudCwgbmFtZSkge1xuICAgICAgICAgICAgdmFyIGVsID0gdmFsaWRhdG9yLmZpbmRCeU5hbWUobmFtZSk7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIHZhciB0YXJnZXRFbGVtZW50ID0gZWxbZWwubGVuZ3RoIC0gMV07XG4gICAgXG4gICAgICAgICAgICBpZiAodGFyZ2V0RWxlbWVudCAhPT0gdW5kZWZpbmVkICYmIHZhbGlkYXRvci5zZXR0aW5ncy5vbmZvY3Vzb3V0KSB7XG4gICAgICAgICAgICAgICAgdmFyIGV2ZW50ID0gJ2JsdXInO1xuICAgICAgICAgICAgICAgIGlmIChcbiAgICAgICAgICAgICAgICAgICAgdGFyZ2V0RWxlbWVudC50YWdOYW1lID09PSAnU0VMRUNUJyB8fFxuICAgICAgICAgICAgICAgICAgICB0YXJnZXRFbGVtZW50LnRhZ05hbWUgPT09ICdPUFRJT04nIHx8XG4gICAgICAgICAgICAgICAgICAgIHRhcmdldEVsZW1lbnQudHlwZSA9PT0gJ2NoZWNrYm94JyB8fFxuICAgICAgICAgICAgICAgICAgICB0YXJnZXRFbGVtZW50LnR5cGUgPT09ICdyYWRpbydcbiAgICAgICAgICAgICAgICApIHtcbiAgICAgICAgICAgICAgICAgICAgZXZlbnQgPSAnY2xpY2snO1xuICAgICAgICAgICAgICAgIH1cbiAgICBcbiAgICAgICAgICAgICAgICB2YXIgcnVsZU5hbWUgPSAnLnZhbGlkYXRlLWxhcmF2ZWxWYWxpZGF0aW9uJztcbiAgICAgICAgICAgICAgICAkKHRhcmdldEVsZW1lbnQpXG4gICAgICAgICAgICAgICAgICAgIC5vZmYocnVsZU5hbWUpXG4gICAgICAgICAgICAgICAgICAgIC5vZmYoZXZlbnQgKyBydWxlTmFtZSArICctJyArIGVsZW1lbnQubmFtZSlcbiAgICAgICAgICAgICAgICAgICAgLm9uKGV2ZW50ICsgcnVsZU5hbWUgKyAnLScgKyBlbGVtZW50Lm5hbWUsIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICQoZWxlbWVudCkudmFsaWQoKTtcbiAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9XG4gICAgXG4gICAgICAgICAgICByZXR1cm4gdGFyZ2V0RWxlbWVudDtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogUGFyc2VzIGVycm9yIEFqYXggcmVzcG9uc2UgYW5kIGdldHMgdGhlIG1lc3NhZ2UuXG4gICAgICAgICAqXG4gICAgICAgICAqIEBwYXJhbSByZXNwb25zZVxuICAgICAgICAgKiBAcmV0dXJucyB7c3RyaW5nW119XG4gICAgICAgICAqL1xuICAgICAgICBwYXJzZUVycm9yUmVzcG9uc2U6IGZ1bmN0aW9uIChyZXNwb25zZSkge1xuICAgICAgICAgICAgdmFyIG5ld1Jlc3BvbnNlID0gWydXaG9vcHMsIGxvb2tzIGxpa2Ugc29tZXRoaW5nIHdlbnQgd3JvbmcuJ107XG4gICAgICAgICAgICBpZiAoJ3Jlc3BvbnNlVGV4dCcgaW4gcmVzcG9uc2UpIHtcbiAgICAgICAgICAgICAgICB2YXIgZXJyb3JNc2cgPSByZXNwb25zZS5yZXNwb25zZVRleHQubWF0Y2goLzxoMVxccyo+KC4qKTxcXC9oMVxccyo+L2kpO1xuICAgICAgICAgICAgICAgIGlmICh0aGlzLmlzQXJyYXkoZXJyb3JNc2cpKSB7XG4gICAgICAgICAgICAgICAgICAgIG5ld1Jlc3BvbnNlID0gW2Vycm9yTXNnWzFdXTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gbmV3UmVzcG9uc2U7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIEVzY2FwZSBzdHJpbmcgdG8gdXNlIGFzIFJlZ3VsYXIgRXhwcmVzc2lvbi5cbiAgICAgICAgICpcbiAgICAgICAgICogQHBhcmFtIHN0clxuICAgICAgICAgKiBAcmV0dXJucyBzdHJpbmdcbiAgICAgICAgICovXG4gICAgICAgIGVzY2FwZVJlZ0V4cDogZnVuY3Rpb24gKHN0cikge1xuICAgICAgICAgICAgcmV0dXJuIHN0ci5yZXBsYWNlKC9bXFwtXFxbXFxdXFwvXFx7XFx9XFwoXFwpXFwqXFwrXFw/XFwuXFxcXFxcXlxcJFxcfF0vZywgXCJcXFxcJCZcIik7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIEdlbmVyYXRlIFJlZ0V4cCBmcm9tIHdpbGRjYXJkIGF0dHJpYnV0ZXMuXG4gICAgICAgICAqXG4gICAgICAgICAqIEBwYXJhbSBuYW1lXG4gICAgICAgICAqIEByZXR1cm5zIHtSZWdFeHB9XG4gICAgICAgICAqL1xuICAgICAgICByZWdleEZyb21XaWxkY2FyZDogZnVuY3Rpb24gKG5hbWUpIHtcbiAgICAgICAgICAgIHZhciBuYW1lUGFydHMgPSBuYW1lLnNwbGl0KCdbKl0nKTtcbiAgICAgICAgICAgIGlmIChuYW1lUGFydHMubGVuZ3RoID09PSAxKSBuYW1lUGFydHMucHVzaCgnJyk7XG5cbiAgICAgICAgICAgIHJldHVybiBuZXcgUmVnRXhwKCdeJyArIG5hbWVQYXJ0cy5tYXAoZnVuY3Rpb24oeCkge1xuICAgICAgICAgICAgICAgIHJldHVybiBsYXJhdmVsVmFsaWRhdGlvbi5oZWxwZXJzLmVzY2FwZVJlZ0V4cCh4KVxuICAgICAgICAgICAgfSkuam9pbignXFxcXFtbXlxcXFxdXSpcXFxcXScpICsgJyQnKTtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogTWVyZ2UgYWRkaXRpb25hbCBsYXJhdmVsIHZhbGlkYXRpb24gcnVsZXMgaW50byB0aGUgY3VycmVudCBydWxlIHNldC5cbiAgICAgICAgICpcbiAgICAgICAgICogQHBhcmFtIHtvYmplY3R9IHJ1bGVzXG4gICAgICAgICAqIEBwYXJhbSB7b2JqZWN0fSBuZXdSdWxlc1xuICAgICAgICAgKiBAcmV0dXJucyB7b2JqZWN0fVxuICAgICAgICAgKi9cbiAgICAgICAgbWVyZ2VSdWxlczogZnVuY3Rpb24gKHJ1bGVzLCBuZXdSdWxlcykge1xuICAgICAgICAgICAgdmFyIHJ1bGVzTGlzdCA9IHtcbiAgICAgICAgICAgICAgICAnbGFyYXZlbFZhbGlkYXRpb24nOiBuZXdSdWxlcy5sYXJhdmVsVmFsaWRhdGlvbiB8fCBbXSxcbiAgICAgICAgICAgICAgICAnbGFyYXZlbFZhbGlkYXRpb25SZW1vdGUnOiBuZXdSdWxlcy5sYXJhdmVsVmFsaWRhdGlvblJlbW90ZSB8fCBbXVxuICAgICAgICAgICAgfTtcblxuICAgICAgICAgICAgZm9yICh2YXIga2V5IGluIHJ1bGVzTGlzdCkge1xuICAgICAgICAgICAgICAgIGlmIChydWxlc0xpc3Rba2V5XS5sZW5ndGggPT09IDApIHtcbiAgICAgICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgaWYgKHR5cGVvZiBydWxlc1trZXldID09PSBcInVuZGVmaW5lZFwiKSB7XG4gICAgICAgICAgICAgICAgICAgIHJ1bGVzW2tleV0gPSBbXTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBydWxlc1trZXldID0gcnVsZXNba2V5XS5jb25jYXQocnVsZXNMaXN0W2tleV0pO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICByZXR1cm4gcnVsZXM7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIEhUTUwgZW50aXR5IGVuY29kZSBhIHN0cmluZy5cbiAgICAgICAgICpcbiAgICAgICAgICogQHBhcmFtIHN0cmluZ1xuICAgICAgICAgKiBAcmV0dXJucyB7c3RyaW5nfVxuICAgICAgICAgKi9cbiAgICAgICAgZW5jb2RlOiBmdW5jdGlvbiAoc3RyaW5nKSB7XG4gICAgICAgICAgICByZXR1cm4gJCgnPGRpdi8+JykudGV4dChzdHJpbmcpLmh0bWwoKTtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogTG9va3VwIG5hbWUgaW4gYW4gYXJyYXkuXG4gICAgICAgICAqXG4gICAgICAgICAqIEBwYXJhbSB2YWxpZGF0b3JcbiAgICAgICAgICogQHBhcmFtIHtzdHJpbmd9IG5hbWUgTmFtZSBpbiBkb3Qgbm90YXRpb24gZm9ybWF0LlxuICAgICAgICAgKiBAcmV0dXJucyB7Kn1cbiAgICAgICAgICovXG4gICAgICAgIGZpbmRCeUFycmF5TmFtZTogZnVuY3Rpb24gKHZhbGlkYXRvciwgbmFtZSkge1xuICAgICAgICAgICAgdmFyIHNxTmFtZSA9IG5hbWUucmVwbGFjZSgvXFwuKFteXFwuXSspL2csICdbJDFdJyksXG4gICAgICAgICAgICAgICAgbG9va3VwcyA9IFtcbiAgICAgICAgICAgICAgICAgICAgLy8gQ29udmVydCBkb3QgdG8gc3F1YXJlIGJyYWNrZXRzLiBlLmcuIGZvby5iYXIuMCBiZWNvbWVzIGZvb1tiYXJdWzBdXG4gICAgICAgICAgICAgICAgICAgIHNxTmFtZSxcbiAgICAgICAgICAgICAgICAgICAgLy8gQXBwZW5kIFtdIHRvIHRoZSBuYW1lIGUuZy4gZm9vIGJlY29tZXMgZm9vW10gb3IgZm9vLmJhci4wIGJlY29tZXMgZm9vW2Jhcl1bMF1bXVxuICAgICAgICAgICAgICAgICAgICBzcU5hbWUgKyAnW10nLFxuICAgICAgICAgICAgICAgICAgICAvLyBSZW1vdmUga2V5IGZyb20gbGFzdCBhcnJheSBlLmcuIGZvb1tiYXJdWzBdIGJlY29tZXMgZm9vW2Jhcl1bXVxuICAgICAgICAgICAgICAgICAgICBzcU5hbWUucmVwbGFjZSgvKC4qKVxcWyguKilcXF0kL2csICckMVtdJylcbiAgICAgICAgICAgICAgICBdO1xuXG4gICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGxvb2t1cHMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICB2YXIgZWxlbSA9IHZhbGlkYXRvci5maW5kQnlOYW1lKGxvb2t1cHNbaV0pO1xuICAgICAgICAgICAgICAgIGlmIChlbGVtLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGVsZW07XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICByZXR1cm4gJChudWxsKTtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogQXR0ZW1wdCB0byBmaW5kIGFuIGVsZW1lbnQgaW4gdGhlIERPTSBtYXRjaGluZyB0aGUgZ2l2ZW4gbmFtZS5cbiAgICAgICAgICogRXhhbXBsZSBuYW1lcyBpbmNsdWRlOlxuICAgICAgICAgKiAgICAtIGRvbWFpbi4wIHdoaWNoIG1hdGNoZXMgZG9tYWluW11cbiAgICAgICAgICogICAgLSBjdXN0b21maWVsZC4zIHdoaWNoIG1hdGNoZXMgY3VzdG9tZmllbGRbM11cbiAgICAgICAgICpcbiAgICAgICAgICogQHBhcmFtIHZhbGlkYXRvclxuICAgICAgICAgKiBAcGFyYW0ge3N0cmluZ30gbmFtZVxuICAgICAgICAgKiBAcmV0dXJucyB7Kn1cbiAgICAgICAgICovXG4gICAgICAgIGZpbmRCeU5hbWU6IGZ1bmN0aW9uICh2YWxpZGF0b3IsIG5hbWUpIHtcbiAgICAgICAgICAgIC8vIEV4YWN0IG1hdGNoLlxuICAgICAgICAgICAgdmFyIGVsZW0gPSB2YWxpZGF0b3IuZmluZEJ5TmFtZShuYW1lKTtcbiAgICAgICAgICAgIGlmIChlbGVtLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gZWxlbTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gRmluZCBuYW1lIGluIGRhdGEsIHVzaW5nIGRvdCBub3RhdGlvbi5cbiAgICAgICAgICAgIHZhciBkZWxpbSA9ICcuJyxcbiAgICAgICAgICAgICAgICBwYXJ0cyAgPSBuYW1lLnNwbGl0KGRlbGltKTtcbiAgICAgICAgICAgIGZvciAodmFyIGkgPSBwYXJ0cy5sZW5ndGg7IGkgPiAwOyBpLS0pIHtcbiAgICAgICAgICAgICAgICB2YXIgcmVjb25zdHJ1Y3RlZCA9IFtdO1xuICAgICAgICAgICAgICAgIGZvciAodmFyIGMgPSAwOyBjIDwgaTsgYysrKSB7XG4gICAgICAgICAgICAgICAgICAgIHJlY29uc3RydWN0ZWQucHVzaChwYXJ0c1tjXSk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgZWxlbSA9IHRoaXMuZmluZEJ5QXJyYXlOYW1lKHZhbGlkYXRvciwgcmVjb25zdHJ1Y3RlZC5qb2luKGRlbGltKSk7XG4gICAgICAgICAgICAgICAgaWYgKGVsZW0ubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gZWxlbTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHJldHVybiAkKG51bGwpO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBJZiBpdCdzIGFuIGFycmF5IGVsZW1lbnQsIGdldCBhbGwgdmFsdWVzLlxuICAgICAgICAgKlxuICAgICAgICAgKiBAcGFyYW0gdmFsaWRhdG9yXG4gICAgICAgICAqIEBwYXJhbSBlbGVtZW50XG4gICAgICAgICAqIEByZXR1cm5zIHsqfHN0cmluZ31cbiAgICAgICAgICovXG4gICAgICAgIGFsbEVsZW1lbnRWYWx1ZXM6IGZ1bmN0aW9uICh2YWxpZGF0b3IsIGVsZW1lbnQpIHtcbiAgICAgICAgICAgIGlmIChlbGVtZW50Lm5hbWUuaW5kZXhPZignW10nKSAhPT0gLTEpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gdmFsaWRhdG9yLmZpbmRCeU5hbWUoZWxlbWVudC5uYW1lKS5tYXAoZnVuY3Rpb24gKGksIGUpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHZhbGlkYXRvci5lbGVtZW50VmFsdWUoZSk7XG4gICAgICAgICAgICAgICAgfSkuZ2V0KCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHJldHVybiB2YWxpZGF0b3IuZWxlbWVudFZhbHVlKGVsZW1lbnQpO1xuICAgICAgICB9XG4gICAgfVxufSk7XG4iXSwibmFtZXMiOlsic3RybGVuIiwiYXJyYXlfZGlmZiIsInN0cnRvdGltZSIsImlzX251bWVyaWMiLCIkIiwiZXh0ZW5kIiwibGFyYXZlbFZhbGlkYXRpb24iLCJoZWxwZXJzIiwibnVtZXJpY1J1bGVzIiwiZmlsZWluZm8iLCJmaWVsZE9iaiIsImluZGV4IiwiRmlsZU5hbWUiLCJ2YWx1ZSIsImZpbGVzIiwiZmlsZSIsImV4dGVuc2lvbiIsInN1YnN0ciIsImxhc3RJbmRleE9mIiwic2l6ZSIsInR5cGUiLCJzZWxlY3RvciIsIm5hbWVzIiwiaXNBcnJheSIsImkiLCJsZW5ndGgiLCJwdXNoIiwiam9pbiIsImhhc051bWVyaWNSdWxlcyIsImVsZW1lbnQiLCJoYXNSdWxlcyIsInJ1bGVzIiwiZm91bmQiLCJ2YWxpZGF0b3IiLCJkYXRhIiwiZm9ybSIsImxpc3RSdWxlcyIsImNhY2hlIiwiYXJyYXlSdWxlc0NhY2hlIiwibmFtZSIsImVhY2giLCJhcnJheVJ1bGUiLCJzZXR0aW5ncyIsIm9ialJ1bGVzIiwiX3J1bGVzIiwiaW5BcnJheSIsInN0cmluZyIsImdldFNpemUiLCJvYmoiLCJwYXJzZUZsb2F0IiwiTWF0aCIsImZsb29yIiwiZ2V0TGFyYXZlbFZhbGlkYXRpb24iLCJydWxlIiwidW5kZWZpbmVkIiwic3RhdGljUnVsZXMiLCJrZXkiLCJwYXJzZVRpbWUiLCJmb3JtYXQiLCJ0aW1lVmFsdWUiLCJmbXQiLCJEYXRlRm9ybWF0dGVyIiwiZGF0ZVJ1bGUiLCJwYXJzZURhdGUiLCJEYXRlIiwiZm9ybWF0RGF0ZSIsInJvdW5kIiwiZ2V0VGltZSIsImNvbXBhcmVEYXRlcyIsInBhcmFtcyIsIm9wZXJhdG9yIiwidGltZUNvbXBhcmUiLCJ0YXJnZXQiLCJkZXBlbmRlbnRFbGVtZW50IiwiZWxlbWVudFZhbHVlIiwiRXJyb3IiLCJndWVzc0RhdGUiLCJ0ZXh0Iiwibm93IiwibWl4ZWRfdmFyIiwiYXJnIiwiT2JqZWN0IiwicHJvdG90eXBlIiwidG9TdHJpbmciLCJjYWxsIiwiYXJyYXlEaWZmIiwiYXJyMSIsImFycjIiLCJhcnJheUVxdWFscyIsImlzRW1wdHlPYmplY3QiLCJlbCIsImZpbmRCeU5hbWUiLCJ0YXJnZXRFbGVtZW50Iiwib25mb2N1c291dCIsImV2ZW50IiwidGFnTmFtZSIsInJ1bGVOYW1lIiwib2ZmIiwib24iLCJ2YWxpZCIsInBhcnNlRXJyb3JSZXNwb25zZSIsInJlc3BvbnNlIiwibmV3UmVzcG9uc2UiLCJlcnJvck1zZyIsInJlc3BvbnNlVGV4dCIsIm1hdGNoIiwiZXNjYXBlUmVnRXhwIiwic3RyIiwicmVwbGFjZSIsInJlZ2V4RnJvbVdpbGRjYXJkIiwibmFtZVBhcnRzIiwic3BsaXQiLCJSZWdFeHAiLCJtYXAiLCJ4IiwibWVyZ2VSdWxlcyIsIm5ld1J1bGVzIiwicnVsZXNMaXN0IiwibGFyYXZlbFZhbGlkYXRpb25SZW1vdGUiLCJjb25jYXQiLCJlbmNvZGUiLCJodG1sIiwiZmluZEJ5QXJyYXlOYW1lIiwic3FOYW1lIiwibG9va3VwcyIsImVsZW0iLCJkZWxpbSIsInBhcnRzIiwicmVjb25zdHJ1Y3RlZCIsImMiLCJhbGxFbGVtZW50VmFsdWVzIiwiaW5kZXhPZiIsImUiLCJnZXQiXSwic291cmNlUm9vdCI6IiJ9","/*!\n * Laravel Javascript Validation\n *\n * https://github.com/proengsoft/laravel-jsvalidation\n *\n * Timezone Helper functions used by validators\n *\n * Copyright (c) 2017 Proengsoft\n * Released under the MIT license\n */\n\n$.extend(true, laravelValidation, {\n\n helpers: {\n\n /**\n * Check if the specified timezone is valid.\n *\n * @param value\n * @returns {boolean}\n */\n isTimezone: function (value) {\n\n var timezones={\n \"africa\": [\n \"abidjan\",\n \"accra\",\n \"addis_ababa\",\n \"algiers\",\n \"asmara\",\n \"bamako\",\n \"bangui\",\n \"banjul\",\n \"bissau\",\n \"blantyre\",\n \"brazzaville\",\n \"bujumbura\",\n \"cairo\",\n \"casablanca\",\n \"ceuta\",\n \"conakry\",\n \"dakar\",\n \"dar_es_salaam\",\n \"djibouti\",\n \"douala\",\n \"el_aaiun\",\n \"freetown\",\n \"gaborone\",\n \"harare\",\n \"johannesburg\",\n \"juba\",\n \"kampala\",\n \"khartoum\",\n \"kigali\",\n \"kinshasa\",\n \"lagos\",\n \"libreville\",\n \"lome\",\n \"luanda\",\n \"lubumbashi\",\n \"lusaka\",\n \"malabo\",\n \"maputo\",\n \"maseru\",\n \"mbabane\",\n \"mogadishu\",\n \"monrovia\",\n \"nairobi\",\n \"ndjamena\",\n \"niamey\",\n \"nouakchott\",\n \"ouagadougou\",\n \"porto-novo\",\n \"sao_tome\",\n \"tripoli\",\n \"tunis\",\n \"windhoek\"\n ],\n \"america\": [\n \"adak\",\n \"anchorage\",\n \"anguilla\",\n \"antigua\",\n \"araguaina\",\n \"argentina\\/buenos_aires\",\n \"argentina\\/catamarca\",\n \"argentina\\/cordoba\",\n \"argentina\\/jujuy\",\n \"argentina\\/la_rioja\",\n \"argentina\\/mendoza\",\n \"argentina\\/rio_gallegos\",\n \"argentina\\/salta\",\n \"argentina\\/san_juan\",\n \"argentina\\/san_luis\",\n \"argentina\\/tucuman\",\n \"argentina\\/ushuaia\",\n \"aruba\",\n \"asuncion\",\n \"atikokan\",\n \"bahia\",\n \"bahia_banderas\",\n \"barbados\",\n \"belem\",\n \"belize\",\n \"blanc-sablon\",\n \"boa_vista\",\n \"bogota\",\n \"boise\",\n \"cambridge_bay\",\n \"campo_grande\",\n \"cancun\",\n \"caracas\",\n \"cayenne\",\n \"cayman\",\n \"chicago\",\n \"chihuahua\",\n \"costa_rica\",\n \"creston\",\n \"cuiaba\",\n \"curacao\",\n \"danmarkshavn\",\n \"dawson\",\n \"dawson_creek\",\n \"denver\",\n \"detroit\",\n \"dominica\",\n \"edmonton\",\n \"eirunepe\",\n \"el_salvador\",\n \"fortaleza\",\n \"glace_bay\",\n \"godthab\",\n \"goose_bay\",\n \"grand_turk\",\n \"grenada\",\n \"guadeloupe\",\n \"guatemala\",\n \"guayaquil\",\n \"guyana\",\n \"halifax\",\n \"havana\",\n \"hermosillo\",\n \"indiana\\/indianapolis\",\n \"indiana\\/knox\",\n \"indiana\\/marengo\",\n \"indiana\\/petersburg\",\n \"indiana\\/tell_city\",\n \"indiana\\/vevay\",\n \"indiana\\/vincennes\",\n \"indiana\\/winamac\",\n \"inuvik\",\n \"iqaluit\",\n \"jamaica\",\n \"juneau\",\n \"kentucky\\/louisville\",\n \"kentucky\\/monticello\",\n \"kralendijk\",\n \"la_paz\",\n \"lima\",\n \"los_angeles\",\n \"lower_princes\",\n \"maceio\",\n \"managua\",\n \"manaus\",\n \"marigot\",\n \"martinique\",\n \"matamoros\",\n \"mazatlan\",\n \"menominee\",\n \"merida\",\n \"metlakatla\",\n \"mexico_city\",\n \"miquelon\",\n \"moncton\",\n \"monterrey\",\n \"montevideo\",\n \"montreal\",\n \"montserrat\",\n \"nassau\",\n \"new_york\",\n \"nipigon\",\n \"nome\",\n \"noronha\",\n \"north_dakota\\/beulah\",\n \"north_dakota\\/center\",\n \"north_dakota\\/new_salem\",\n \"ojinaga\",\n \"panama\",\n \"pangnirtung\",\n \"paramaribo\",\n \"phoenix\",\n \"port-au-prince\",\n \"port_of_spain\",\n \"porto_velho\",\n \"puerto_rico\",\n \"rainy_river\",\n \"rankin_inlet\",\n \"recife\",\n \"regina\",\n \"resolute\",\n \"rio_branco\",\n \"santa_isabel\",\n \"santarem\",\n \"santiago\",\n \"santo_domingo\",\n \"sao_paulo\",\n \"scoresbysund\",\n \"shiprock\",\n \"sitka\",\n \"st_barthelemy\",\n \"st_johns\",\n \"st_kitts\",\n \"st_lucia\",\n \"st_thomas\",\n \"st_vincent\",\n \"swift_current\",\n \"tegucigalpa\",\n \"thule\",\n \"thunder_bay\",\n \"tijuana\",\n \"toronto\",\n \"tortola\",\n \"vancouver\",\n \"whitehorse\",\n \"winnipeg\",\n \"yakutat\",\n \"yellowknife\"\n ],\n \"antarctica\": [\n \"casey\",\n \"davis\",\n \"dumontdurville\",\n \"macquarie\",\n \"mawson\",\n \"mcmurdo\",\n \"palmer\",\n \"rothera\",\n \"south_pole\",\n \"syowa\",\n \"vostok\"\n ],\n \"arctic\": [\n \"longyearbyen\"\n ],\n \"asia\": [\n \"aden\",\n \"almaty\",\n \"amman\",\n \"anadyr\",\n \"aqtau\",\n \"aqtobe\",\n \"ashgabat\",\n \"baghdad\",\n \"bahrain\",\n \"baku\",\n \"bangkok\",\n \"beirut\",\n \"bishkek\",\n \"brunei\",\n \"choibalsan\",\n \"chongqing\",\n \"colombo\",\n \"damascus\",\n \"dhaka\",\n \"dili\",\n \"dubai\",\n \"dushanbe\",\n \"gaza\",\n \"harbin\",\n \"hebron\",\n \"ho_chi_minh\",\n \"hong_kong\",\n \"hovd\",\n \"irkutsk\",\n \"jakarta\",\n \"jayapura\",\n \"jerusalem\",\n \"kabul\",\n \"kamchatka\",\n \"karachi\",\n \"kashgar\",\n \"kathmandu\",\n \"khandyga\",\n \"kolkata\",\n \"krasnoyarsk\",\n \"kuala_lumpur\",\n \"kuching\",\n \"kuwait\",\n \"macau\",\n \"magadan\",\n \"makassar\",\n \"manila\",\n \"muscat\",\n \"nicosia\",\n \"novokuznetsk\",\n \"novosibirsk\",\n \"omsk\",\n \"oral\",\n \"phnom_penh\",\n \"pontianak\",\n \"pyongyang\",\n \"qatar\",\n \"qyzylorda\",\n \"rangoon\",\n \"riyadh\",\n \"sakhalin\",\n \"samarkand\",\n \"seoul\",\n \"shanghai\",\n \"singapore\",\n \"taipei\",\n \"tashkent\",\n \"tbilisi\",\n \"tehran\",\n \"thimphu\",\n \"tokyo\",\n \"ulaanbaatar\",\n \"urumqi\",\n \"ust-nera\",\n \"vientiane\",\n \"vladivostok\",\n \"yakutsk\",\n \"yekaterinburg\",\n \"yerevan\"\n ],\n \"atlantic\": [\n \"azores\",\n \"bermuda\",\n \"canary\",\n \"cape_verde\",\n \"faroe\",\n \"madeira\",\n \"reykjavik\",\n \"south_georgia\",\n \"st_helena\",\n \"stanley\"\n ],\n \"australia\": [\n \"adelaide\",\n \"brisbane\",\n \"broken_hill\",\n \"currie\",\n \"darwin\",\n \"eucla\",\n \"hobart\",\n \"lindeman\",\n \"lord_howe\",\n \"melbourne\",\n \"perth\",\n \"sydney\"\n ],\n \"europe\": [\n \"amsterdam\",\n \"andorra\",\n \"athens\",\n \"belgrade\",\n \"berlin\",\n \"bratislava\",\n \"brussels\",\n \"bucharest\",\n \"budapest\",\n \"busingen\",\n \"chisinau\",\n \"copenhagen\",\n \"dublin\",\n \"gibraltar\",\n \"guernsey\",\n \"helsinki\",\n \"isle_of_man\",\n \"istanbul\",\n \"jersey\",\n \"kaliningrad\",\n \"kiev\",\n \"lisbon\",\n \"ljubljana\",\n \"london\",\n \"luxembourg\",\n \"madrid\",\n \"malta\",\n \"mariehamn\",\n \"minsk\",\n \"monaco\",\n \"moscow\",\n \"oslo\",\n \"paris\",\n \"podgorica\",\n \"prague\",\n \"riga\",\n \"rome\",\n \"samara\",\n \"san_marino\",\n \"sarajevo\",\n \"simferopol\",\n \"skopje\",\n \"sofia\",\n \"stockholm\",\n \"tallinn\",\n \"tirane\",\n \"uzhgorod\",\n \"vaduz\",\n \"vatican\",\n \"vienna\",\n \"vilnius\",\n \"volgograd\",\n \"warsaw\",\n \"zagreb\",\n \"zaporozhye\",\n \"zurich\"\n ],\n \"indian\": [\n \"antananarivo\",\n \"chagos\",\n \"christmas\",\n \"cocos\",\n \"comoro\",\n \"kerguelen\",\n \"mahe\",\n \"maldives\",\n \"mauritius\",\n \"mayotte\",\n \"reunion\"\n ],\n \"pacific\": [\n \"apia\",\n \"auckland\",\n \"chatham\",\n \"chuuk\",\n \"easter\",\n \"efate\",\n \"enderbury\",\n \"fakaofo\",\n \"fiji\",\n \"funafuti\",\n \"galapagos\",\n \"gambier\",\n \"guadalcanal\",\n \"guam\",\n \"honolulu\",\n \"johnston\",\n \"kiritimati\",\n \"kosrae\",\n \"kwajalein\",\n \"majuro\",\n \"marquesas\",\n \"midway\",\n \"nauru\",\n \"niue\",\n \"norfolk\",\n \"noumea\",\n \"pago_pago\",\n \"palau\",\n \"pitcairn\",\n \"pohnpei\",\n \"port_moresby\",\n \"rarotonga\",\n \"saipan\",\n \"tahiti\",\n \"tarawa\",\n \"tongatapu\",\n \"wake\",\n \"wallis\"\n ],\n \"utc\": [\n \"\"\n ]\n };\n\n var tzparts= value.split('/',2);\n var continent=tzparts[0].toLowerCase();\n var city='';\n if (tzparts[1]) {\n city=tzparts[1].toLowerCase();\n }\n\n return (continent in timezones && ( timezones[continent].length===0 || timezones[continent].indexOf(city)!==-1))\n }\n }\n});\n","/*!\n * Laravel Javascript Validation\n *\n * https://github.com/proengsoft/laravel-jsvalidation\n *\n * Methods that implement Laravel Validations\n *\n * Copyright (c) 2017 Proengsoft\n * Released under the MIT license\n */\n\n$.extend(true, laravelValidation, {\n\n methods:{\n\n helpers: laravelValidation.helpers,\n\n jsRemoteTimer:0,\n\n /**\n * \"Validate\" optional attributes.\n * Always returns true, just lets us put sometimes in rules.\n *\n * @return {boolean}\n */\n Sometimes: function() {\n return true;\n },\n\n /**\n * Bail This is the default behaivour os JSValidation.\n * Always returns true, just lets us put sometimes in rules.\n *\n * @return {boolean}\n */\n Bail: function() {\n return true;\n },\n\n /**\n * \"Indicate\" validation should pass if value is null.\n * Always returns true, just lets us put \"nullable\" in rules.\n *\n * @return {boolean}\n */\n Nullable: function() {\n return true;\n },\n\n /**\n * Validate the given attribute is filled if it is present.\n */\n Filled: function(value, element) {\n return $.validator.methods.required.call(this, value, element, true);\n },\n\n\n /**\n * Validate that a required attribute exists.\n */\n Required: function(value, element) {\n return $.validator.methods.required.call(this, value, element);\n },\n\n /**\n * Validate that an attribute exists when any other attribute exists.\n *\n * @return {boolean}\n */\n RequiredWith: function(value, element, params) {\n var validator=this,\n required=false;\n var currentObject=this;\n\n $.each(params,function(i,param) {\n var target=laravelValidation.helpers.dependentElement(\n currentObject, element, param\n );\n required=required || (\n target!==undefined &&\n $.validator.methods.required.call(\n validator,\n currentObject.elementValue(target),\n target,true\n ));\n });\n\n if (required) {\n return $.validator.methods.required.call(this, value, element, true);\n }\n return true;\n },\n\n /**\n * Validate that an attribute exists when all other attribute exists.\n *\n * @return {boolean}\n */\n RequiredWithAll: function(value, element, params) {\n var validator=this,\n required=true;\n var currentObject=this;\n\n $.each(params,function(i,param) {\n var target=laravelValidation.helpers.dependentElement(\n currentObject, element, param\n );\n required = required && (\n target!==undefined &&\n $.validator.methods.required.call(\n validator,\n currentObject.elementValue(target),\n target,true\n ));\n });\n\n if (required) {\n return $.validator.methods.required.call(this, value, element, true);\n }\n return true;\n },\n\n /**\n * Validate that an attribute exists when any other attribute does not exists.\n *\n * @return {boolean}\n */\n RequiredWithout: function(value, element, params) {\n var validator=this,\n required=false;\n var currentObject=this;\n\n $.each(params,function(i,param) {\n var target=laravelValidation.helpers.dependentElement(\n currentObject, element, param\n );\n required = required ||\n target===undefined||\n !$.validator.methods.required.call(\n validator,\n currentObject.elementValue(target),\n target,true\n );\n });\n\n if (required) {\n return $.validator.methods.required.call(this, value, element, true);\n }\n return true;\n },\n\n /**\n * Validate that an attribute exists when all other attribute does not exists.\n *\n * @return {boolean}\n */\n RequiredWithoutAll: function(value, element, params) {\n var validator=this,\n required=true,\n currentObject=this;\n\n $.each(params,function(i, param) {\n var target=laravelValidation.helpers.dependentElement(\n currentObject, element, param\n );\n required = required && (\n target===undefined ||\n !$.validator.methods.required.call(\n validator,\n currentObject.elementValue(target),\n target,true\n ));\n });\n\n if (required) {\n return $.validator.methods.required.call(this, value, element, true);\n }\n return true;\n },\n\n /**\n * Validate that an attribute exists when another attribute has a given value.\n *\n * @return {boolean}\n */\n RequiredIf: function(value, element, params) {\n\n var target=laravelValidation.helpers.dependentElement(\n this, element, params[0]\n );\n\n if (target!==undefined) {\n var val=String(this.elementValue(target));\n if (typeof val !== 'undefined') {\n var data = params.slice(1);\n if ($.inArray(val, data) !== -1) {\n return $.validator.methods.required.call(\n this, value, element, true\n );\n }\n }\n }\n\n return true;\n },\n\n /**\n * Validate that an attribute exists when another\n * attribute does not have a given value.\n *\n * @return {boolean}\n */\n RequiredUnless: function(value, element, params) {\n\n var target=laravelValidation.helpers.dependentElement(\n this, element, params[0]\n );\n\n if (target!==undefined) {\n var val=String(this.elementValue(target));\n if (typeof val !== 'undefined') {\n var data = params.slice(1);\n if ($.inArray(val, data) !== -1) {\n return true;\n }\n }\n }\n\n return $.validator.methods.required.call(\n this, value, element, true\n );\n\n },\n\n /**\n * Validate that an attribute has a matching confirmation.\n *\n * @return {boolean}\n */\n Confirmed: function(value, element, params) {\n return laravelValidation.methods.Same.call(this,value, element, params);\n },\n\n /**\n * Validate that two attributes match.\n *\n * @return {boolean}\n */\n Same: function(value, element, params) {\n\n var target=laravelValidation.helpers.dependentElement(\n this, element, params[0]\n );\n\n if (target!==undefined) {\n return String(value) === String(this.elementValue(target));\n }\n return false;\n },\n\n /**\n * Validate that the values of an attribute is in another attribute.\n *\n * @param value\n * @param element\n * @param params\n * @returns {boolean}\n * @constructor\n */\n InArray: function (value, element, params) {\n if (typeof params[0] === 'undefined') {\n return false;\n }\n var elements = this.elements();\n var found = false;\n var nameRegExp = laravelValidation.helpers.regexFromWildcard(params[0]);\n\n for ( var i = 0; i < elements.length ; i++ ) {\n var targetName = elements[i].name;\n if (targetName.match(nameRegExp)) {\n var equals = laravelValidation.methods.Same.call(this,value, element, [targetName]);\n found = found || equals;\n }\n }\n\n return found;\n },\n\n /**\n * Validate an attribute is unique among other values.\n *\n * @param value\n * @param element\n * @param params\n * @returns {boolean}\n */\n Distinct: function (value, element, params) {\n if (typeof params[0] === 'undefined') {\n return false;\n }\n\n var elements = this.elements();\n var found = false;\n var nameRegExp = laravelValidation.helpers.regexFromWildcard(params[0]);\n\n for ( var i = 0; i < elements.length ; i++ ) {\n var targetName = elements[i].name;\n if (targetName !== element.name && targetName.match(nameRegExp)) {\n var equals = laravelValidation.methods.Same.call(this,value, element, [targetName]);\n found = found || equals;\n }\n }\n\n return !found;\n },\n\n\n /**\n * Validate that an attribute is different from another attribute.\n *\n * @return {boolean}\n */\n Different: function(value, element, params) {\n return ! laravelValidation.methods.Same.call(this,value, element, params);\n },\n\n /**\n * Validate that an attribute was \"accepted\".\n * This validation rule implies the attribute is \"required\".\n *\n * @return {boolean}\n */\n Accepted: function(value) {\n var regex = new RegExp(\"^(?:(yes|on|1|true))$\",'i');\n return regex.test(value);\n },\n\n /**\n * Validate that an attribute is an array.\n *\n * @param value\n * @param element\n */\n Array: function(value, element) {\n if (element.name.indexOf('[') !== -1 && element.name.indexOf(']') !== -1) {\n return true;\n }\n\n return laravelValidation.helpers.isArray(value);\n },\n\n /**\n * Validate that an attribute is a boolean.\n *\n * @return {boolean}\n */\n Boolean: function(value) {\n var regex= new RegExp(\"^(?:(true|false|1|0))$\",'i');\n return regex.test(value);\n },\n\n /**\n * Validate that an attribute is an integer.\n *\n * @return {boolean}\n */\n Integer: function(value) {\n var regex= new RegExp(\"^(?:-?\\\\d+)$\",'i');\n return regex.test(value);\n },\n\n /**\n * Validate that an attribute is numeric.\n */\n Numeric: function(value, element) {\n return $.validator.methods.number.call(this, value, element, true);\n },\n\n /**\n * Validate that an attribute is a string.\n *\n * @return {boolean}\n */\n String: function(value) {\n return typeof value === 'string';\n },\n\n /**\n * The field under validation must be numeric and must have an exact length of value.\n */\n Digits: function(value, element, params) {\n return (\n $.validator.methods.number.call(this, value, element, true) &&\n value.length === parseInt(params, 10)\n );\n },\n\n /**\n * The field under validation must have a length between the given min and max.\n */\n DigitsBetween: function(value, element, params) {\n return ($.validator.methods.number.call(this, value, element, true)\n && value.length>=parseFloat(params[0]) && value.length<=parseFloat(params[1]));\n },\n\n /**\n * Validate the size of an attribute.\n *\n * @return {boolean}\n */\n Size: function(value, element, params) {\n return laravelValidation.helpers.getSize(this, element,value) === parseFloat(params[0]);\n },\n\n /**\n * Validate the size of an attribute is between a set of values.\n *\n * @return {boolean}\n */\n Between: function(value, element, params) {\n return ( laravelValidation.helpers.getSize(this, element,value) >= parseFloat(params[0]) &&\n laravelValidation.helpers.getSize(this,element,value) <= parseFloat(params[1]));\n },\n\n /**\n * Validate the size of an attribute is greater than a minimum value.\n *\n * @return {boolean}\n */\n Min: function(value, element, params) {\n value = laravelValidation.helpers.allElementValues(this, element);\n\n return laravelValidation.helpers.getSize(this, element, value) >= parseFloat(params[0]);\n },\n\n /**\n * Validate the size of an attribute is less than a maximum value.\n *\n * @return {boolean}\n */\n Max: function(value, element, params) {\n value = laravelValidation.helpers.allElementValues(this, element);\n\n return laravelValidation.helpers.getSize(this, element, value) <= parseFloat(params[0]);\n },\n\n /**\n * Validate an attribute is contained within a list of values.\n *\n * @return {boolean}\n */\n In: function(value, element, params) {\n if (laravelValidation.helpers.isArray(value)\n && laravelValidation.helpers.hasRules(element, \"Array\")\n ) {\n var diff = laravelValidation.helpers.arrayDiff(value, params);\n\n return Object.keys(diff).length === 0;\n }\n\n return params.indexOf(value.toString()) !== -1;\n },\n\n /**\n * Validate an attribute is not contained within a list of values.\n *\n * @return {boolean}\n */\n NotIn: function(value, element, params) {\n return params.indexOf(value.toString()) === -1;\n },\n\n /**\n * Validate that an attribute is a valid IP.\n *\n * @return {boolean}\n */\n Ip: function(value) {\n return /^(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$/i.test(value) ||\n /^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\\b((25[0-5])|(1\\d{2})|(2[0-4]\\d)|(\\d{1,2}))\\b)\\.){3}(\\b((25[0-5])|(1\\d{2})|(2[0-4]\\d)|(\\d{1,2}))\\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\\b((25[0-5])|(1\\d{2})|(2[0-4]\\d)|(\\d{1,2}))\\b)\\.){3}(\\b((25[0-5])|(1\\d{2})|(2[0-4]\\d)|(\\d{1,2}))\\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\\b((25[0-5])|(1\\d{2})|(2[0-4]\\d)|(\\d{1,2}))\\b)\\.){3}(\\b((25[0-5])|(1\\d{2})|(2[0-4]\\d)|(\\d{1,2}))\\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$/i.test(value);\n },\n\n /**\n * Validate that an attribute is a valid e-mail address.\n */\n Email: function(value, element) {\n return $.validator.methods.email.call(this, value, element, true);\n },\n\n /**\n * Validate that an attribute is a valid URL.\n */\n Url: function(value, element) {\n return $.validator.methods.url.call(this, value, element, true);\n },\n\n /**\n * The field under validation must be a successfully uploaded file.\n *\n * @return {boolean}\n */\n File: function(value, element) {\n if (!window.File || !window.FileReader || !window.FileList || !window.Blob) {\n return true;\n }\n if ('files' in element ) {\n return (element.files.length > 0);\n }\n return false;\n },\n\n /**\n * Validate the MIME type of a file upload attribute is in a set of MIME types.\n *\n * @return {boolean}\n */\n Mimes: function(value, element, params) {\n if (!window.File || !window.FileReader || !window.FileList || !window.Blob) {\n return true;\n }\n var lowerParams = $.map(params, function(item) {\n return item.toLowerCase();\n });\n\n var fileinfo = laravelValidation.helpers.fileinfo(element);\n return (fileinfo !== false && lowerParams.indexOf(fileinfo.extension.toLowerCase())!==-1);\n },\n\n /**\n * The file under validation must match one of the given MIME types.\n *\n * @return {boolean}\n */\n Mimetypes: function(value, element, params) {\n if (!window.File || !window.FileReader || !window.FileList || !window.Blob) {\n return true;\n }\n var lowerParams = $.map(params, function(item) {\n return item.toLowerCase();\n });\n\n var fileinfo = laravelValidation.helpers.fileinfo(element);\n\n if (fileinfo === false) {\n return false;\n }\n return (lowerParams.indexOf(fileinfo.type.toLowerCase())!==-1);\n },\n\n /**\n * Validate the MIME type of a file upload attribute is in a set of MIME types.\n */\n Image: function(value, element) {\n return laravelValidation.methods.Mimes.call(this, value, element, [\n 'jpg', 'png', 'gif', 'bmp', 'svg', 'jpeg'\n ]);\n },\n\n /**\n * Validate dimensions of Image.\n *\n * @return {boolean|string}\n */\n Dimensions: function(value, element, params, callback) {\n if (!window.File || !window.FileReader || !window.FileList || !window.Blob) {\n return true;\n }\n if (element.files === null || typeof element.files[0] === 'undefined') {\n return false;\n }\n\n var fr = new FileReader;\n fr.onload = function () {\n var img = new Image();\n img.onload = function () {\n var height = parseFloat(img.naturalHeight);\n var width = parseFloat(img.naturalWidth);\n var ratio = width / height;\n var notValid = ((params['width']) && parseFloat(params['width'] !== width)) ||\n ((params['min_width']) && parseFloat(params['min_width']) > width) ||\n ((params['max_width']) && parseFloat(params['max_width']) < width) ||\n ((params['height']) && parseFloat(params['height']) !== height) ||\n ((params['min_height']) && parseFloat(params['min_height']) > height) ||\n ((params['max_height']) && parseFloat(params['max_height']) < height) ||\n ((params['ratio']) && ratio !== parseFloat(eval(params['ratio']))\n );\n callback(! notValid);\n };\n img.onerror = function() {\n callback(false);\n };\n img.src = fr.result;\n };\n fr.readAsDataURL(element.files[0]);\n\n return 'pending';\n },\n\n /**\n * Validate that an attribute contains only alphabetic characters.\n *\n * @return {boolean}\n */\n Alpha: function(value) {\n if (typeof value !== 'string') {\n return false;\n }\n\n var regex = new RegExp(\"^(?:^[a-z\\u00E0-\\u00FC]+$)$\",'i');\n return regex.test(value);\n\n },\n\n /**\n * Validate that an attribute contains only alpha-numeric characters.\n *\n * @return {boolean}\n */\n AlphaNum: function(value) {\n if (typeof value !== 'string') {\n return false;\n }\n var regex = new RegExp(\"^(?:^[a-z0-9\\u00E0-\\u00FC]+$)$\",'i');\n return regex.test(value);\n },\n\n /**\n * Validate that an attribute contains only alphabetic characters.\n *\n * @return {boolean}\n */\n AlphaDash: function(value) {\n if (typeof value !== 'string') {\n return false;\n }\n var regex = new RegExp(\"^(?:^[a-z0-9\\u00E0-\\u00FC_-]+$)$\",'i');\n return regex.test(value);\n },\n\n /**\n * Validate that an attribute passes a regular expression check.\n *\n * @return {boolean}\n */\n Regex: function(value, element, params) {\n var invalidModifiers=['x','s','u','X','U','A'];\n // Converting php regular expression\n var phpReg= new RegExp('^(?:\\/)(.*\\\\\\/?[^\\/]*|[^\\/]*)(?:\\/)([gmixXsuUAJ]*)?$');\n var matches=params[0].match(phpReg);\n if (matches === null) {\n return false;\n }\n // checking modifiers\n var php_modifiers=[];\n if (matches[2]!==undefined) {\n php_modifiers=matches[2].split('');\n for (var i=0; i');\n },\n\n /**\n * Validate the date is equal or after a given date.\n *\n * @return {boolean}\n */\n AfterOrEqual: function(value, element, params) {\n return laravelValidation.helpers.compareDates(this, value, element, params[0], '>=');\n },\n\n\n /**\n * Validate that an attribute is a valid date.\n */\n Timezone: function(value) {\n return laravelValidation.helpers.isTimezone(value);\n },\n\n\n /**\n * Validate the attribute is a valid JSON string.\n *\n * @param value\n * @return bool\n */\n Json: function(value) {\n var result = true;\n try {\n JSON.parse(value);\n } catch (e) {\n result = false;\n }\n return result;\n },\n\n /**\n * Noop (always returns true).\n *\n * @param value\n * @returns {boolean}\n */\n ProengsoftNoop: function (value) {\n return true;\n },\n }\n});\n"]} \ No newline at end of file diff --git a/public/vendor/jsvalidation/js/jsvalidation.min.js b/public/vendor/jsvalidation/js/jsvalidation.min.js new file mode 100644 index 00000000..6ebaf36e --- /dev/null +++ b/public/vendor/jsvalidation/js/jsvalidation.min.js @@ -0,0 +1,2 @@ +var laravelValidation;!function(e){"function"==typeof define&&define.amd?define(["jquery"],e):"object"==typeof module&&module.exports?module.exports=e(require("jquery")):e(jQuery)}(function(h){h.extend(h.fn,{validate:function(e){if(this.length){var i=h.data(this[0],"validator");return i?i:(this.attr("novalidate","novalidate"),i=new h.validator(e,this[0]),h.data(this[0],"validator",i),i.settings.onsubmit&&(this.on("click.validate",":submit",function(e){i.submitButton=e.currentTarget,h(this).hasClass("cancel")&&(i.cancelSubmit=!0),void 0!==h(this).attr("formnovalidate")&&(i.cancelSubmit=!0)}),this.on("submit.validate",function(a){function e(){var e,t;return i.submitButton&&(i.settings.submitHandler||i.formSubmitted)&&(e=h("").attr("name",i.submitButton.name).val(h(i.submitButton).val()).appendTo(i.currentForm)),!(i.settings.submitHandler&&!i.settings.debug)||(t=i.settings.submitHandler.call(i,i.currentForm,a),e&&e.remove(),void 0!==t&&t)}return i.settings.debug&&a.preventDefault(),i.cancelSubmit?(i.cancelSubmit=!1,e()):i.form()?i.pendingRequest?!(i.formSubmitted=!0):e():(i.focusInvalid(),!1)})),i)}e&&e.debug&&window.console&&console.warn("Nothing selected, can't validate, returning nothing.")},valid:function(){var e,t,a;return h(this[0]).is("form")?e=this.validate().form():(a=[],e=!0,t=h(this[0].form).validate(),this.each(function(){(e=t.element(this)&&e)||(a=a.concat(t.errorList))}),t.errorList=a),e},rules:function(e,t){var a,i,n,r,s,o,l=this[0],u=void 0!==this.attr("contenteditable")&&"false"!==this.attr("contenteditable");if(null!=l&&(!l.form&&u&&(l.form=this.closest("form")[0],l.name=this.attr("name")),null!=l.form)){if(e)switch(i=(a=h.data(l.form,"validator").settings).rules,n=h.validator.staticRules(l),e){case"add":h.extend(n,h.validator.normalizeRule(t)),delete n.messages,i[l.name]=n,t.messages&&(a.messages[l.name]=h.extend(a.messages[l.name],t.messages));break;case"remove":return t?(o={},h.each(t.split(/\s/),function(e,t){o[t]=n[t],delete n[t]}),o):(delete i[l.name],n)}return(r=h.validator.normalizeRules(h.extend({},h.validator.classRules(l),h.validator.attributeRules(l),h.validator.dataRules(l),h.validator.staticRules(l)),l)).required&&(s=r.required,delete r.required,r=h.extend({required:s},r)),r.remote&&(s=r.remote,delete r.remote,r=h.extend(r,{remote:s})),r}}});function a(e){return e.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"")}var i;h.extend(h.expr.pseudos||h.expr[":"],{blank:function(e){return!a(""+h(e).val())},filled:function(e){var t=h(e).val();return null!==t&&!!a(""+t)},unchecked:function(e){return!h(e).prop("checked")}}),h.validator=function(e,t){this.settings=h.extend(!0,{},h.validator.defaults,e),this.currentForm=t,this.init()},h.validator.format=function(a,e){return 1===arguments.length?function(){var e=h.makeArray(arguments);return e.unshift(a),h.validator.format.apply(this,e)}:(void 0===e||(2Warning: No message defined for "+e.name+""),i=/\$?\{(\d+)\}/g;return"function"==typeof a?a=a.call(this,t.parameters,e):i.test(a)&&(a=h.validator.format(a.replace(i,"{$1}"),t.parameters)),a},formatAndAdd:function(e,t){var a=this.defaultMessage(e,t);this.errorList.push({message:a,element:e,method:t.method}),this.errorMap[e.name]=a,this.submitted[e.name]=a},addWrapper:function(e){return this.settings.wrapper&&(e=e.add(e.parent(this.settings.wrapper))),e},defaultShowErrors:function(){for(var e,t,a=0;this.errorList[a];a++)t=this.errorList[a],this.settings.highlight&&this.settings.highlight.call(this,t.element,this.settings.errorClass,this.settings.validClass),this.showLabel(t.element,t.message);if(this.errorList.length&&(this.toShow=this.toShow.add(this.containers)),this.settings.success)for(a=0;this.successList[a];a++)this.showLabel(this.successList[a]);if(this.settings.unhighlight)for(a=0,e=this.validElements();e[a];a++)this.settings.unhighlight.call(this,e[a],this.settings.errorClass,this.settings.validClass);this.toHide=this.toHide.not(this.toShow),this.hideErrors(),this.addWrapper(this.toShow).show()},validElements:function(){return this.currentElements.not(this.invalidElements())},invalidElements:function(){return h(this.errorList).map(function(){return this.element})},showLabel:function(e,t){var a,i,n,r,s=this.errorsFor(e),o=this.idOrName(e),l=h(e).attr("aria-describedby");s.length?(s.removeClass(this.settings.validClass).addClass(this.settings.errorClass),this.settings&&this.settings.escapeHtml?s.text(t||""):s.html(t||"")):(s=h("<"+this.settings.errorElement+">").attr("id",o+"-error").addClass(this.settings.errorClass),this.settings&&this.settings.escapeHtml?s.text(t||""):s.html(t||""),a=s,this.settings.wrapper&&(a=s.hide().show().wrap("<"+this.settings.wrapper+"/>").parent()),this.labelContainer.length?this.labelContainer.append(a):this.settings.errorPlacement?this.settings.errorPlacement.call(this,a,h(e)):a.insertAfter(e),s.is("label")?s.attr("for",o):0===s.parents("label[for='"+this.escapeCssMeta(o)+"']").length&&(n=s.attr("id"),l?l.match(new RegExp("\\b"+this.escapeCssMeta(n)+"\\b"))||(l+=" "+n):l=n,h(e).attr("aria-describedby",l),(i=this.groups[e.name])&&(r=this,h.each(r.groups,function(e,t){t===i&&h("[name='"+r.escapeCssMeta(e)+"']",r.currentForm).attr("aria-describedby",s.attr("id"))})))),!t&&this.settings.success&&(s.text(""),"string"==typeof this.settings.success?s.addClass(this.settings.success):this.settings.success(s,e)),this.toShow=this.toShow.add(s)},errorsFor:function(e){var t=this.escapeCssMeta(this.idOrName(e)),a=h(e).attr("aria-describedby"),i="label[for='"+t+"'], label[for='"+t+"'] *";return a&&(i=i+", #"+this.escapeCssMeta(a).replace(/\s+/g,", #")),this.errors().filter(i)},escapeCssMeta:function(e){return void 0===e?"":e.replace(/([\\!"#$%&'()*+,./:;<=>?@\[\]^`{|}~])/g,"\\$1")},idOrName:function(e){return this.groups[e.name]||!this.checkable(e)&&e.id||e.name},validationTargetFor:function(e){return this.checkable(e)&&(e=this.findByName(e.name)),h(e).not(this.settings.ignore)[0]},checkable:function(e){return/radio|checkbox/i.test(e.type)},findByName:function(e){return h(this.currentForm).find("[name='"+this.escapeCssMeta(e)+"']")},getLength:function(e,t){switch(t.nodeName.toLowerCase()){case"select":return h("option:selected",t).length;case"input":if(this.checkable(t))return this.findByName(t.name).filter(":checked").length}return e.length},depend:function(e,t){return!this.dependTypes[typeof e]||this.dependTypes[typeof e](e,t)},dependTypes:{boolean:function(e){return e},string:function(e,t){return!!h(e,t.form).length},function:function(e,t){return e(t)}},optional:function(e){var t=this.elementValue(e);return!h.validator.methods.required.call(this,t,e)&&"dependency-mismatch"},elementAjaxPort:function(e){return"validate"+e.name},startRequest:function(e){this.pending[e.name]||(this.pendingRequest++,h(e).addClass(this.settings.pendingClass),this.pending[e.name]=!0)},stopRequest:function(e,t){this.pendingRequest--,this.pendingRequest<0&&(this.pendingRequest=0),delete this.pending[e.name],h(e).removeClass(this.settings.pendingClass),t&&0===this.pendingRequest&&this.formSubmitted&&this.form()&&0===this.pendingRequest?(h(this.currentForm).trigger("submit"),this.submitButton&&h("input:hidden[name='"+this.submitButton.name+"']",this.currentForm).remove(),this.formSubmitted=!1):!t&&0===this.pendingRequest&&this.formSubmitted&&(h(this.currentForm).triggerHandler("invalid-form",[this]),this.formSubmitted=!1)},abortRequest:function(e){var t;this.pending[e.name]&&(t=this.elementAjaxPort(e),h.ajaxAbort(t),this.pendingRequest--,this.pendingRequest<0&&(this.pendingRequest=0),delete this.pending[e.name],h(e).removeClass(this.settings.pendingClass))},previousValue:function(e,t){return t="string"==typeof t&&t||"remote",h.data(e,"previousValue")||h.data(e,"previousValue",{old:null,valid:!0,message:this.defaultMessage(e,{method:t})})},destroy:function(){this.resetForm(),h(this.currentForm).off(".validate").removeData("validator").find(".validate-equalTo-blur").off(".validate-equalTo").removeClass("validate-equalTo-blur").find(".validate-lessThan-blur").off(".validate-lessThan").removeClass("validate-lessThan-blur").find(".validate-lessThanEqual-blur").off(".validate-lessThanEqual").removeClass("validate-lessThanEqual-blur").find(".validate-greaterThanEqual-blur").off(".validate-greaterThanEqual").removeClass("validate-greaterThanEqual-blur").find(".validate-greaterThan-blur").off(".validate-greaterThan").removeClass("validate-greaterThan-blur")}},classRuleSettings:{required:{required:!0},email:{email:!0},url:{url:!0},date:{date:!0},dateISO:{dateISO:!0},number:{number:!0},digits:{digits:!0},creditcard:{creditcard:!0}},addClassRules:function(e,t){e.constructor===String?this.classRuleSettings[e]=t:h.extend(this.classRuleSettings,e)},classRules:function(e){var t={},a=h(e).attr("class");return a&&h.each(a.split(" "),function(){this in h.validator.classRuleSettings&&h.extend(t,h.validator.classRuleSettings[this])}),t},normalizeAttributeRule:function(e,t,a,i){/min|max|step/.test(a)&&(null===t||/number|range|text/.test(t))&&(i=Number(i),isNaN(i)&&(i=void 0)),i||0===i?e[a]=i:t===a&&"range"!==t&&(e["date"===t?"dateISO":a]=!0)},attributeRules:function(e){var t,a,i={},n=h(e),r=e.getAttribute("type");for(t in h.validator.methods)a="required"===t?(""===(a=e.getAttribute(t))&&(a=!0),!!a):n.attr(t),this.normalizeAttributeRule(i,r,t,a);return i.maxlength&&/-1|2147483647|524288/.test(i.maxlength)&&delete i.maxlength,i},dataRules:function(e){var t,a,i={},n=h(e),r=e.getAttribute("type");for(t in h.validator.methods)""===(a=n.data("rule"+t.charAt(0).toUpperCase()+t.substring(1).toLowerCase()))&&(a=!0),this.normalizeAttributeRule(i,r,t,a);return i},staticRules:function(e){var t={},a=h.data(e.form,"validator");return a.settings.rules&&(t=h.validator.normalizeRule(a.settings.rules[e.name])||{}),t},normalizeRules:function(i,n){return h.each(i,function(e,t){if(!1!==t){if(t.param||t.depends){var a=!0;switch(typeof t.depends){case"string":a=!!h(t.depends,n.form).length;break;case"function":a=t.depends.call(n,n)}a?i[e]=void 0===t.param||t.param:(h.data(n.form,"validator").resetElements(h(n)),delete i[e])}}else delete i[e]}),h.each(i,function(e,t){i[e]="function"==typeof t&&"normalizer"!==e?t(n):t}),h.each(["minlength","maxlength"],function(){i[this]&&(i[this]=Number(i[this]))}),h.each(["rangelength","range"],function(){var e;i[this]&&(Array.isArray(i[this])?i[this]=[Number(i[this][0]),Number(i[this][1])]:"string"==typeof i[this]&&(e=i[this].replace(/[\[\]]/g,"").split(/[\s,]+/),i[this]=[Number(e[0]),Number(e[1])]))}),h.validator.autoCreateRanges&&(null!=i.min&&null!=i.max&&(i.range=[i.min,i.max],delete i.min,delete i.max),null!=i.minlength&&null!=i.maxlength&&(i.rangelength=[i.minlength,i.maxlength],delete i.minlength,delete i.maxlength)),i},normalizeRule:function(e){var t;return"string"==typeof e&&(t={},h.each(e.split(/\s/),function(){t[this]=!0}),e=t),e},addMethod:function(e,t,a){h.validator.methods[e]=t,h.validator.messages[e]=void 0!==a?a:h.validator.messages[e],t.length<3&&h.validator.addClassRules(e,h.validator.normalizeRule(e))},methods:{required:function(e,t,a){if(!this.depend(a,t))return"dependency-mismatch";if("select"!==t.nodeName.toLowerCase())return this.checkable(t)?0{ ]|%[0-9A-Fa-f]{2})+(?::(?:[^\]\[?\/<~#`!@$^&*()+=}|:";',>{ ]|%[0-9A-Fa-f]{2})*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\u00a1-\uffff][a-z0-9\u00a1-\uffff_-]{0,62})?[a-z0-9\u00a1-\uffff]\.)+(?:[a-z\u00a1-\uffff]{2,}\.?))(?::\d{2,5})?(?:[/?#]\S*)?$/i.test(e)},date:(i=!1,function(e,t){return i||(i=!0,this.settings.debug&&window.console&&console.warn("The `date` method is deprecated and will be removed in version '2.0.0'.\nPlease don't use it, since it relies on the Date constructor, which\nbehaves very differently across browsers and locales. Use `dateISO`\ninstead or one of the locale specific methods in `localizations/`\nand `additional-methods.js`.")),this.optional(t)||!/Invalid|NaN/.test(new Date(e).toString())}),dateISO:function(e,t){return this.optional(t)||/^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/.test(e)},number:function(e,t){return this.optional(t)||/^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:-?\.\d+)?$/.test(e)},digits:function(e,t){return this.optional(t)||/^\d+$/.test(e)},minlength:function(e,t,a){var i=Array.isArray(e)?e.length:this.getLength(e,t);return this.optional(t)||a<=i},maxlength:function(e,t,a){var i=Array.isArray(e)?e.length:this.getLength(e,t);return this.optional(t)||i<=a},rangelength:function(e,t,a){var i=Array.isArray(e)?e.length:this.getLength(e,t);return this.optional(t)||i>=a[0]&&i<=a[1]},min:function(e,t,a){return this.optional(t)||a<=e},max:function(e,t,a){return this.optional(t)||e<=a},range:function(e,t,a){return this.optional(t)||e>=a[0]&&e<=a[1]},step:function(e,t,a){function i(e){var t=(""+e).match(/(?:\.(\d+))?$/);return t&&t[1]?t[1].length:0}function n(e){return Math.round(e*Math.pow(10,r))}var r,s=h(t).attr("type"),o="Step attribute on input type "+s+" is not supported.",l=new RegExp("\\b"+s+"\\b"),u=!0;if(s&&!l.test(["text","number","range"].join()))throw new Error(o);return r=i(a),(i(e)>r||n(e)%n(a)!=0)&&(u=!1),this.optional(t)||u},equalTo:function(e,t,a){var i=h(a);return this.settings.onfocusout&&i.not(".validate-equalTo-blur").length&&i.addClass("validate-equalTo-blur").on("blur.validate-equalTo",function(){h(t).valid()}),e===i.val()},remote:function(r,s,e,o){if(this.optional(s))return"dependency-mismatch";o="string"==typeof o&&o||"remote";var l,t,a,u=this.previousValue(s,o);return this.settings.messages[s.name]||(this.settings.messages[s.name]={}),u.originalMessage=u.originalMessage||this.settings.messages[s.name][o],this.settings.messages[s.name][o]=u.message,e="string"==typeof e?{url:e}:e,a=h.param(h.extend({data:r},e.data)),null!==u.valid&&u.old===a?u.valid:(u.old=a,u.valid=null,(l=this).startRequest(s),(t={})[s.name]=r,h.ajax(h.extend(!0,{mode:"abort",port:this.elementAjaxPort(s),dataType:"json",data:t,context:l.currentForm,success:function(e){var t,a,i,n=!0===e||"true"===e;l.settings.messages[s.name][o]=u.originalMessage,n?(i=l.formSubmitted,l.toHide=l.errorsFor(s),l.formSubmitted=i,l.successList.push(s),l.invalid[s.name]=!1,l.showErrors()):(t={},a=e||l.defaultMessage(s,{method:o,parameters:r}),t[s.name]=u.message=a,l.invalid[s.name]=!0,l.showErrors(t)),u.valid=n,l.stopRequest(s,n)}},e)),"pending")}}});var n,r={};return h.ajaxPrefilter?h.ajaxPrefilter(function(e,t,a){var i=e.port;"abort"===e.mode&&(h.ajaxAbort(i),r[i]=a)}):(n=h.ajax,h.ajax=function(e){var t=("mode"in e?e:h.ajaxSettings).mode,a=("port"in e?e:h.ajaxSettings).port;return"abort"===t?(h.ajaxAbort(a),r[a]=n.apply(this,arguments),r[a]):n.apply(this,arguments)}),h.ajaxAbort=function(e){r[e]&&(r[e].abort(),delete r[e])},h}),function(e,t){"function"==typeof define&&define.amd?define([],t):"object"==typeof module&&module.exports?module.exports=t():e.DateFormatter=t()}("undefined"!=typeof self?self:this,function(){var b={DAY:864e5,HOUR:3600,defaults:{dateSettings:{days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],daysShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],monthsShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],meridiem:["AM","PM"],ordinal:function(e){var t=e%10,a={1:"st",2:"nd",3:"rd"};return 1!==Math.floor(e%100/10)&&a[t]?a[t]:"th"}},separators:/[ \-+\/.:@]/g,validParts:/[dDjlNSwzWFmMntLoYyaABgGhHisueTIOPZcrU]/g,intParts:/[djwNzmnyYhHgGis]/g,tzParts:/\b(?:[PMCEA][SDP]T|(?:Australian|Pacific|Mountain|Central|Eastern|Atlantic) (?:Eastern) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g,tzClip:/[^-+\dA-Z]/g},getInt:function(e,t){return parseInt(e,t||10)},compare:function(e,t){return"string"==typeof e&&"string"==typeof t&&e.toLowerCase()===t.toLowerCase()},lpad:function(e,t,a){var i=e.toString();return a=a||"0",i.length").attr({type:"hidden",name:t}).appendTo(this),a.apply(this,[e])},$.validator.classRuleSettings={},$.validator.attributeRules=function(){},$.validator.dataRules=this.arrayRules,$.validator.prototype.arrayRulesCache={},this.setupValidations()},arrayRules:function(n){var r={},e=$.data(n.form,"validator"),s=e.arrayRulesCache;return-1===n.name.indexOf("[")||(n.name in s||(s[n.name]={}),$.each(e.settings.rules,function(e,t){var a,i;e in s[n.name]?r=laravelValidation.helpers.mergeRules(r,s[n.name][e]):(s[n.name][e]={},a=laravelValidation.helpers.regexFromWildcard(e),n.name.match(a)&&(i=$.validator.normalizeRule(t)||{},s[n.name][e]=i,r=laravelValidation.helpers.mergeRules(r,i)))})),r},setupValidations:function(){function n(e){var t=$(e.currentForm).attr("method");return $(e.currentForm).find('input[name="_method"]').length&&(t=$(e.currentForm).find('input[name="_method"]').val()),t}function h(a,e,i,t){return{mode:"abort",port:"validate"+e.name,dataType:"json",data:t,context:a.currentForm,url:$(a.currentForm).attr("action"),type:n(a),beforeSend:function(e){var t=i[0][1][1];if("get"!==n(a)&&t)return e.setRequestHeader("X-XSRF-TOKEN",t)}}}function l(s,i,o,e){var l=!0,u=s.previousValue(o);return $.each(e,function(e,t){var a=t[3]||-1!==laravelValidation.implicitRules.indexOf(t[0]),n=t[0],r=t[2];return!a&&s.optional(o)?!(l="dependency-mismatch"):(void 0!==laravelValidation.methods[n]?$.each(i,function(e,i){if(!1===(l=laravelValidation.methods[n].call(s,i,o,t[1],function(e){var t,a;s.settings.messages[o.name].laravelValidationRemote=u.originalMessage,e?(t=s.formSubmitted,s.prepareElement(o),s.formSubmitted=t,s.successList.push(o),delete s.invalid[o.name],s.showErrors()):((a={})[o.name]=u.message="function"==typeof r?r(i):r,s.invalid[o.name]=!0,s.showErrors(a)),s.showErrors(s.errorMap),u.valid=e})))return!1}):l=!1,!0!==l?(s.settings.messages[o.name]||(s.settings.messages[o.name]={}),s.settings.messages[o.name].laravelValidation=r,!1):void 0)}),l}$.validator.addMethod("laravelValidation",function(e,t,a){var i=[],n=[];$.each(a,function(e,t){var a=-1!==t[4].indexOf("[");t[3]||-1!==laravelValidation.implicitRules.indexOf(t[0])?a?n.unshift(t):i.unshift(t):a?n.push(t):i.push(t)});var r=l(this,[e],t,i),s=Array.isArray(e)?e:[e],o=l(this,s,t,n);return r&&o},""),$.validator.addMethod("laravelValidationRemote",function(s,o,e){if(t=e,a=!1,$.each(t,function(e,t){a=a||t[3]}),!a&&this.optional(o))return"dependency-mismatch";var t,a,l,i,u=this.previousValue(o);return this.settings.messages[o.name]||(this.settings.messages[o.name]={}),u.originalMessage=this.settings.messages[o.name].laravelValidationRemote,this.settings.messages[o.name].laravelValidationRemote=u.message,laravelValidation.helpers.arrayEquals(u.old,s)||u.old===s?u.valid:(u.old=s,(l=this).startRequest(o),(i=$(l.currentForm).serializeArray()).push({name:"_jsvalidation",value:o.name}),i.push({name:"_jsvalidation_validate_all",value:e[0][1][2]}),$.ajax(h(l,o,e,i)).always(function(e,t){var a,i,n,r;if("error"===t)r=!1,e=laravelValidation.helpers.parseErrorResponse(e);else{if("success"!==t)return;r=!0===e||"true"===e}l.settings.messages[o.name].laravelValidationRemote=u.originalMessage,r?(n=l.formSubmitted,l.prepareElement(o),l.formSubmitted=n,l.successList.push(o),delete l.invalid[o.name],l.showErrors()):(a={},i=e||l.defaultMessage(o,"remote"),a[o.name]=u.message="function"==typeof i?i(s):i[0],l.invalid[o.name]=!0,l.showErrors(a)),l.showErrors(l.errorMap),u.valid=r,l.stopRequest(o,r)}),"pending")},""),$.validator.addMethod("laravelValidationFormRequest",function(e,n,t){var r=this,s=r.previousValue(n),a=$(r.currentForm).serializeArray();return a.push({name:"__proengsoft_form_request",value:1}),JSON.stringify(s.old)===JSON.stringify(a)?(s.valid||r.showErrors(s.errors||{}),s.valid):(s.old=a,this.startRequest(n),$.ajax(h(r,n,t,a)).always(function(e,t){var i={},a="success"===t&&(!0===e||"true"===e);a?(r.resetInternals(),r.toHide=r.errorsFor(n)):($.each(e,function(e,t){var a=laravelValidation.helpers.findByName(r,e)[0];a&&(i[a.name]=laravelValidation.helpers.encode(t[0]||""))}),$.isEmptyObject(i)&&(a=!0)),s.valid=a,s.errors=i,r.showErrors(i),r.stopRequest(n,a)}),"pending")},"")}},$(function(){laravelValidation.init()}),function(){"use strict";var i={"./node_modules/locutus/php/array/array_diff.js":function(e){e.exports=function(e){var t,a={},i=arguments.length,n="",r=1,s="";e:for(n in e)for(r=1;rn[0].length)&&(n=u,r=l)}if(!r||r.callback&&!1===r.callback.apply(i,n))return!1;e=e.substr(n[0].length),n=r=null}return Math.floor(i.toDate(new Date(1e3*t))/1e3)}},"./node_modules/locutus/php/info/ini_get.js":function(e,t,i){e.exports=function(e){var t="undefined"!=typeof window?window:i.g;t.$locutus=t.$locutus||{};var a=t.$locutus;return a.php=a.php||{},a.php.ini=a.php.ini||{},!a.php.ini[e]||void 0===a.php.ini[e].local_value||null===a.php.ini[e].local_value?"":a.php.ini[e].local_value}},"./node_modules/locutus/php/strings/strlen.js":function(e,t,n){e.exports=function(e){var t=e+"";if("off"===(n("./node_modules/locutus/php/info/ini_get.js")("unicode.semantics")||"off"))return t.length;for(var a=0,i=0,a=0,i=0;a":return r=":return r<=o;default:throw new Error("Unsupported operator.")}},guessDate:function(e,t){return(new DateFormatter).guessDate(e,t)},strtotime:function(e,t){return r()(e,t)},is_numeric:function(e){return o()(e)},isArray:function(e){return"[object Array]"===Object.prototype.toString.call(e)},arrayDiff:function(e,t){return i()(e,t)},arrayEquals:function(e,t){return!(!this.isArray(e)||!this.isArray(t))&&(e.length===t.length&&$.isEmptyObject(this.arrayDiff(e,t)))},dependentElement:function(e,t,a){var i,n,r=e.findByName(a),s=r[r.length-1];return void 0!==s&&e.settings.onfocusout&&(i="blur","SELECT"!==s.tagName&&"OPTION"!==s.tagName&&"checkbox"!==s.type&&"radio"!==s.type||(i="click"),n=".validate-laravelValidation",$(s).off(n).off(i+n+"-"+t.name).on(i+n+"-"+t.name,function(){$(t).valid()})),s},parseErrorResponse:function(e){var t,a=["Whoops, looks like something went wrong."];return"responseText"in e&&(t=e.responseText.match(/(.*)<\/h1\s*>/i),this.isArray(t)&&(a=[t[1]])),a},escapeRegExp:function(e){return e.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")},regexFromWildcard:function(e){var t=e.split("[*]");return 1===t.length&&t.push(""),new RegExp("^"+t.map(function(e){return laravelValidation.helpers.escapeRegExp(e)}).join("\\[[^\\]]*\\]")+"$")},mergeRules:function(e,t){var a={laravelValidation:t.laravelValidation||[],laravelValidationRemote:t.laravelValidationRemote||[]};for(var i in a)0!==a[i].length&&(void 0===e[i]&&(e[i]=[]),e[i]=e[i].concat(a[i]));return e},encode:function(e){return $("
").text(e).html()},findByArrayName:function(e,t){for(var a=t.replace(/\.([^\.]+)/g,"[$1]"),i=[a,a+"[]",a.replace(/(.*)\[(.*)\]$/g,"$1[]")],n=0;n=parseFloat(a[0])&&e.length<=parseFloat(a[1])},Size:function(e,t,a){return laravelValidation.helpers.getSize(this,t,e)===parseFloat(a[0])},Between:function(e,t,a){return laravelValidation.helpers.getSize(this,t,e)>=parseFloat(a[0])&&laravelValidation.helpers.getSize(this,t,e)<=parseFloat(a[1])},Min:function(e,t,a){return e=laravelValidation.helpers.allElementValues(this,t),laravelValidation.helpers.getSize(this,t,e)>=parseFloat(a[0])},Max:function(e,t,a){return e=laravelValidation.helpers.allElementValues(this,t),laravelValidation.helpers.getSize(this,t,e)<=parseFloat(a[0])},In:function(e,t,a){if(laravelValidation.helpers.isArray(e)&&laravelValidation.helpers.hasRules(t,"Array")){var i=laravelValidation.helpers.arrayDiff(e,a);return 0===Object.keys(i).length}return-1!==a.indexOf(e.toString())},NotIn:function(e,t,a){return-1===a.indexOf(e.toString())},Ip:function(e){return/^(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)$/i.test(e)||/^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$/i.test(e)},Email:function(e,t){return $.validator.methods.email.call(this,e,t,!0)},Url:function(e,t){return $.validator.methods.url.call(this,e,t,!0)},File:function(e,t){return!(window.File&&window.FileReader&&window.FileList&&window.Blob)||"files"in t&&0width||params.max_width&&parseFloat(params.max_width)height||params.max_height&&parseFloat(params.max_height)")},AfterOrEqual:function(e,t,a){return laravelValidation.helpers.compareDates(this,e,t,a[0],">=")},Timezone:function(e){return laravelValidation.helpers.isTimezone(e)},Json:function(e){var t=!0;try{JSON.parse(e)}catch(e){t=!1}return t},ProengsoftNoop:function(e){return!0}}}); +//# sourceMappingURL=jsvalidation.min.js.map diff --git a/public/vendor/jsvalidation/js/jsvalidation.min.js.map b/public/vendor/jsvalidation/js/jsvalidation.min.js.map new file mode 100644 index 00000000..8d1bbe04 --- /dev/null +++ b/public/vendor/jsvalidation/js/jsvalidation.min.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["resources/assets/js/jsvalidation.js","node_modules/jquery-validation/dist/jquery.validate.js","node_modules/php-date-formatter/js/php-date-formatter.js","es-build/helpers.js","resources/assets/js/timezones.js","resources/assets/js/validations.js"],"names":["laravelValidation","factory","define","amd","module","exports","require","jQuery","$","extend","fn","validate","options","this","length","validator","data","attr","settings","onsubmit","on","event","submitButton","currentTarget","hasClass","cancelSubmit","undefined","handle","hidden","result","submitHandler","formSubmitted","name","val","appendTo","currentForm","debug","call","remove","preventDefault","form","pendingRequest","focusInvalid","window","console","warn","valid","errorList","is","each","element","concat","rules","command","argument","staticRules","existingRules","param","filtered","isContentEditable","closest","normalizeRule","messages","split","index","method","normalizeRules","classRules","attributeRules","dataRules","required","remote","trim","str","replace","called","expr","pseudos","blank","a","filled","unchecked","prop","defaults","init","format","source","params","arguments","args","makeArray","unshift","apply","constructor","Array","slice","i","n","RegExp","groups","errorClass","pendingClass","validClass","errorElement","focusCleanup","errorContainer","errorLabelContainer","ignore","ignoreTitle","customElements","onfocusin","lastActive","unhighlight","hideThese","errorsFor","onfocusout","checkable","submitted","optional","onkeyup","which","elementValue","inArray","keyCode","invalid","onclick","parentNode","highlight","type","findByName","addClass","removeClass","setDefaults","email","url","date","dateISO","number","digits","equalTo","maxlength","minlength","rangelength","range","max","min","step","autoCreateRanges","prototype","labelContainer","errorContext","containers","add","valueCache","pending","reset","delegate","eventType","key","value","join","invalidHandler","checkForm","errorMap","triggerHandler","showErrors","prepareForm","elements","currentElements","check","rs","group","cleanElement","clean","checkElement","validationTargetFor","v","prepareElement","testgroup","push","numberOfInvalids","toHide","errors","map","message","successList","grep","defaultShowErrors","resetForm","hideErrors","removeData","removeAttr","resetElements","objectLength","obj","count","not","text","addWrapper","hide","size","findLastActive","filter","trigger","e","rulesCache","find","error","selector","resetInternals","toShow","idx","$element","validity","badInput","substr","lastIndexOf","rule","normalizer","rulesCount","dependencyMismatch","abortRequest","parameters","methods","formatAndAdd","log","id","TypeError","customDataMessage","charAt","toUpperCase","substring","toLowerCase","customMessage","m","String","findDefined","defaultMessage","title","theregex","test","toToggle","wrapper","parent","showLabel","success","validElements","show","invalidElements","place","errorID","elementID","idOrName","describedBy","escapeHtml","html","wrap","append","errorPlacement","insertAfter","parents","escapeCssMeta","match","describer","string","getLength","nodeName","depend","dependTypes","boolean","function","elementAjaxPort","startRequest","stopRequest","port","ajaxAbort","previousValue","old","destroy","off","classRuleSettings","creditcard","addClassRules","className","classes","normalizeAttributeRule","Number","isNaN","getAttribute","depends","keepRule","parameter","parts","isArray","transformed","addMethod","Date","toString","decimalPlaces","num","toInt","Math","round","pow","decimals","errorMessage","re","Error","target","optionDataString","previous","originalMessage","ajax","mode","dataType","context","response","pendingRequests","ajaxPrefilter","_","xhr","ajaxSettings","abort","root","DateFormatter","self","$h","DAY","HOUR","dateSettings","days","daysShort","months","monthsShort","meridiem","ordinal","suffixes","1","2","3","floor","separators","validParts","intParts","tzParts","tzClip","getInt","radix","parseInt","compare","str1","str2","lpad","chr","merge","out","hasOwnProperty","getIndex","arr","config","getMonth","parseDate","vDate","vFormat","vFormatParts","vDateParts","vDatePart","iDatePart","vMonth","vMeriIndex","vMeriOffset","len","mer","vDateFlag","vTimeFlag","vSettings","year","month","day","hour","sec","splice","indexOf","varY","varM","varD","guessDate","vDateStr","vYear","iPart","iSec","vParts","vDigit","setMonth","setDate","getFullYear","setFullYear","setHours","setMinutes","setSeconds","parseFormat","vChar","doFormat","t","s","fmt","backslash","d","j","D","w","getDate","l","N","getDay","z","Y","b","W","F","M","L","o","y","A","G","B","H","getUTCHours","getUTCMinutes","getUTCSeconds","g","getHours","h","getMinutes","getSeconds","u","getMilliseconds","exec","I","UTC","O","tzo","getTimezoneOffset","abs","P","T","pop","Z","c","r","U","getTime","formatDate","Object","freeze","implicitRules","arrayRules","arrayRulesCache","setupValidations","cache","tmpRules","nameRegExp","newRules","helpers","mergeRules","regexFromWildcard","formMethod","ajaxOpts","beforeSend","token","setRequestHeader","validateLocalRules","values","validated","implicit","laravelValidationRemote","isArrayRule","localRulesResult","arrayValue","arrayRulesResult","arrayEquals","serializeArray","always","textStatus","parseErrorResponse","JSON","stringify","fieldName","errorMessages","encode","isEmptyObject","__webpack_modules__","./node_modules/locutus/php/array/array_diff.js","arr1","retArr","argl","k1","k","arr1keys","./node_modules/locutus/php/datetime/strtotime.js","reSpace","reSpaceOpt","reMeridian","reHour24","reHour24lz","reHour12","reMinute","reMinutelz","reSecond","reSecondlz","reFrac","reDayfull","reDayabbr","reDaytext","reReltextnumber","reReltexttext","reReltextunit","reYear","reYear4","reMonth","reMonthlz","reDay","reDaylz","reMonthFull","reMonthAbbr","reMonthText","reTzCorrection","reDateNoYear","processMeridian","meridian","processYear","yearStr","lookupMonth","monthStr","jan","january","feb","february","ii","mar","march","iii","apr","april","iv","may","jun","june","vi","jul","july","vii","aug","august","viii","sep","sept","september","ix","oct","october","x","nov","november","xi","dec","december","xii","lookupWeekday","dayStr","desiredSundayNumber","mon","monday","tue","tuesday","wed","wednesday","thu","thursday","fri","friday","sat","saturday","sun","sunday","processTzCorrection","tzOffset","oldValue","sign","hours","minutes","tzAbbrOffsets","acdt","acst","addt","adt","aedt","aest","ahdt","ahst","akdt","akst","amt","apt","ast","awdt","awst","awt","bdst","bdt","bmt","bst","cast","cat","cddt","cdt","cemt","cest","cet","cmt","cpt","cst","cwt","chst","dmt","eat","eddt","edt","eest","eet","emt","ept","est","ewt","ffmt","gdt","gmt","gst","hdt","hkst","hkt","hmt","hpt","hst","hwt","iddt","idt","imt","ist","jdt","jmt","jst","kdt","kmt","kst","lst","mddt","mdst","mdt","mest","met","mmt","mpt","msd","msk","mst","mwt","nddt","ndt","npt","nst","nwt","nzdt","nzmt","nzst","pddt","pdt","pkst","pkt","plmt","pmt","ppmt","ppt","pst","pwt","qmt","rmt","sast","sdmt","sjmt","smt","sst","tbmt","tmt","uct","utc","wast","wat","wemt","west","wet","wib","wita","wit","wmt","yddt","ydt","ypt","yst","ywt","f","p","q","formats","yesterday","regex","callback","rd","resetTime","now","noon","time","midnightOrToday","tomorrow","timestamp","dates","zone","firstOrLastDay","firstOrLastDayOfMonth","backOrFrontOf","side","minute","weekdayOf","mssqltime","second","frac","oracledate","monthText","JAN","FEB","MAR","APR","MAY","JUN","JUL","AUG","SEP","OCT","NOV","DEC","ymd","timeLong12","timeShort12","timeTiny12","soap","tzCorrection","wddx","exif","xmlRpc","xmlRpcNoColon","clf","iso8601long","dateTextual","pointedDate4","pointedDate2","timeLong24","dateNoColon","pgydotd","timeShort24","iso8601noColon","iso8601dateSlash","dateSlash","american","americanShort","gnuDateShortOrIso8601date2","iso8601date4","gnuNoColon","times","gnuDateShorter","pgTextReverse","dateFull","dateNoDay","dateNoDayRev","pgTextShort","dateNoYear","dateNoYearRev","isoWeekDay","week","dayOfWeek","relativeText","relValue","relUnit","relTextLower","_lookupRelative","amount","last","first","next","third","fourth","fifth","sixth","seventh","eight","eighth","ninth","tenth","eleventh","twelfth","behavior","ri","rh","rm","ry","weekday","weekdayBehavior","relative","signs","minuses","dayText","relativeTextWeek","relText","monthFullOrMonthAbbr","tzAbbr","abbr","offset","ago","rf","year4","whitespace","dateShortWithTimeLong","dateShortWithTimeLong12","dateShortWithTimeShort","dateShortWithTimeShort12","resultProto","NaN","zones","toDate","relativeTo","dow","diff","setUTCFullYear","setUTCHours","create","longestMatch","finalRule","./node_modules/locutus/php/info/ini_get.js","__unused_webpack_exports","__webpack_require__","varname","$global","$locutus","php","ini","local_value","./node_modules/locutus/php/strings/strlen.js","lgth","prev","code","charCodeAt","getWholeChar","./node_modules/locutus/php/var/is_numeric.js","mixedVar","__webpack_module_cache__","moduleId","cachedModule","getter","__esModule","definition","defineProperty","enumerable","get","globalThis","Function","Symbol","toStringTag","__webpack_exports__","locutus_php_strings_strlen__WEBPACK_IMPORTED_MODULE_0__","locutus_php_strings_strlen__WEBPACK_IMPORTED_MODULE_0___default","locutus_php_array_array_diff__WEBPACK_IMPORTED_MODULE_1__","locutus_php_array_array_diff__WEBPACK_IMPORTED_MODULE_1___default","locutus_php_datetime_strtotime__WEBPACK_IMPORTED_MODULE_2__","locutus_php_datetime_strtotime__WEBPACK_IMPORTED_MODULE_2___default","locutus_php_var_is_numeric__WEBPACK_IMPORTED_MODULE_3__","locutus_php_var_is_numeric__WEBPACK_IMPORTED_MODULE_3___default","numericRules","fileinfo","fieldObj","FileName","files","file","extension","names","hasNumericRules","hasRules","found","listRules","arrayRule","objRules","_rules","strlen","getSize","is_numeric","parseFloat","getLaravelValidation","parseTime","dateRule","timeValue","strtotime","compareDates","operator","timeCompare","dependentElement","mixed_var","arg","arrayDiff","arr2","ruleName","el","targetElement","tagName","errorMsg","newResponse","responseText","escapeRegExp","nameParts","rulesList","findByArrayName","sqName","lookups","elem","reconstructed","allElementValues","isTimezone","timezones","africa","america","antarctica","arctic","asia","atlantic","australia","europe","indian","pacific","tzparts","continent","city","jsRemoteTimer","Sometimes","Bail","Nullable","Filled","Required","RequiredWith","currentObject","RequiredWithAll","RequiredWithout","RequiredWithoutAll","RequiredIf","RequiredUnless","Confirmed","Same","InArray","equals","targetName","Distinct","Different","Accepted","Boolean","Integer","Numeric","Digits","DigitsBetween","Size","Between","Min","Max","In","keys","NotIn","Ip","Email","Url","File","FileReader","FileList","Blob","Mimes","lowerParams","item","Mimetypes","Image","Dimensions","fr","onload","img","height","naturalHeight","width","naturalWidth","ratio","notValid","eval","onerror","src","readAsDataURL","Alpha","AlphaNum","AlphaDash","Regex","invalidModifiers","phpReg","matches","php_modifiers","DateFormat","Before","BeforeOrEqual","After","AfterOrEqual","Timezone","Json","parse","ProengsoftNoop"],"mappings":"AASA,IAAAA,mBCDA,SAAAC,GACA,mBAAAC,QAAAA,OAAAC,IACAD,OAAA,CAAA,UAAAD,GACA,iBAAAG,QAAAA,OAAAC,QACAD,OAAAC,QAAAJ,EAAAK,QAAA,WAEAL,EAAAM,QANA,CAQA,SAAAC,GAEAA,EAAAC,OAAAD,EAAAE,GAAA,CAGAC,SAAA,SAAAC,GAGA,GAAAC,KAAAC,OAAA,CAQA,IAAAC,EAAAP,EAAAQ,KAAAH,KAAA,GAAA,aACA,OAAAE,EACAA,GAIAF,KAAAI,KAAA,aAAA,cAEAF,EAAA,IAAAP,EAAAO,UAAAH,EAAAC,KAAA,IACAL,EAAAQ,KAAAH,KAAA,GAAA,YAAAE,GAEAA,EAAAG,SAAAC,WAEAN,KAAAO,GAAA,iBAAA,UAAA,SAAAC,GAIAN,EAAAO,aAAAD,EAAAE,cAGAf,EAAAK,MAAAW,SAAA,YACAT,EAAAU,cAAA,QAIAC,IAAAlB,EAAAK,MAAAI,KAAA,oBACAF,EAAAU,cAAA,KAKAZ,KAAAO,GAAA,kBAAA,SAAAC,GAOA,SAAAM,IACA,IAAAC,EAAAC,EAcA,OAPAd,EAAAO,eAAAP,EAAAG,SAAAY,eAAAf,EAAAgB,iBACAH,EAAApB,EAAA,0BACAS,KAAA,OAAAF,EAAAO,aAAAU,MACAC,IAAAzB,EAAAO,EAAAO,cAAAW,OACAC,SAAAnB,EAAAoB,gBAGApB,EAAAG,SAAAY,gBAAAf,EAAAG,SAAAkB,SACAP,EAAAd,EAAAG,SAAAY,cAAAO,KAAAtB,EAAAA,EAAAoB,YAAAd,GACAO,GAGAA,EAAAU,cAEAZ,IAAAG,GACAA,GAQA,OArCAd,EAAAG,SAAAkB,OAGAf,EAAAkB,iBAkCAxB,EAAAU,cACAV,EAAAU,cAAA,EACAE,KAEAZ,EAAAyB,OACAzB,EAAA0B,iBACA1B,EAAAgB,eAAA,GAGAJ,KAEAZ,EAAA2B,gBACA,MAKA3B,GA7FAH,GAAAA,EAAAwB,OAAAO,OAAAC,SACAA,QAAAC,KAAA,yDAgGAC,MAAA,WACA,IAAAA,EAAA/B,EAAAgC,EAgBA,OAdAvC,EAAAK,KAAA,IAAAmC,GAAA,QACAF,EAAAjC,KAAAF,WAAA6B,QAEAO,EAAA,GACAD,GAAA,EACA/B,EAAAP,EAAAK,KAAA,GAAA2B,MAAA7B,WACAE,KAAAoC,KAAA,YACAH,EAAA/B,EAAAmC,QAAArC,OAAAiC,KAEAC,EAAAA,EAAAI,OAAApC,EAAAgC,cAGAhC,EAAAgC,UAAAA,GAEAD,GAIAM,MAAA,SAAAC,EAAAC,GACA,IAEApC,EAAAqC,EAAAC,EAAAxC,EAAAyC,EAAAC,EAFAR,EAAArC,KAAA,GACA8C,OAAA,IAAA9C,KAAAI,KAAA,oBAAA,UAAAJ,KAAAI,KAAA,mBAIA,GAAA,MAAAiC,KAIAA,EAAAV,MAAAmB,IACAT,EAAAV,KAAA3B,KAAA+C,QAAA,QAAA,GACAV,EAAAlB,KAAAnB,KAAAI,KAAA,SAGA,MAAAiC,EAAAV,MAAA,CAIA,GAAAa,EAIA,OAFAE,GADArC,EAAAV,EAAAQ,KAAAkC,EAAAV,KAAA,aAAAtB,UACAkC,MACAI,EAAAhD,EAAAO,UAAAwC,YAAAL,GACAG,GACA,IAAA,MACA7C,EAAAC,OAAA+C,EAAAhD,EAAAO,UAAA8C,cAAAP,WAGAE,EAAAM,SACAP,EAAAL,EAAAlB,MAAAwB,EACAF,EAAAQ,WACA5C,EAAA4C,SAAAZ,EAAAlB,MAAAxB,EAAAC,OAAAS,EAAA4C,SAAAZ,EAAAlB,MAAAsB,EAAAQ,WAEA,MACA,IAAA,SACA,OAAAR,GAIAI,EAAA,GACAlD,EAAAyC,KAAAK,EAAAS,MAAA,MAAA,SAAAC,EAAAC,GACAP,EAAAO,GAAAT,EAAAS,UACAT,EAAAS,KAEAP,WARAH,EAAAL,EAAAlB,MACAwB,GAkCA,OAvBAxC,EAAAR,EAAAO,UAAAmD,eACA1D,EAAAC,OACA,GACAD,EAAAO,UAAAoD,WAAAjB,GACA1C,EAAAO,UAAAqD,eAAAlB,GACA1C,EAAAO,UAAAsD,UAAAnB,GACA1C,EAAAO,UAAAwC,YAAAL,IACAA,IAGAoB,WACAb,EAAAzC,EAAAsD,gBACAtD,EAAAsD,SACAtD,EAAAR,EAAAC,OAAA,CAAA6D,SAAAb,GAAAzC,IAIAA,EAAAuD,SACAd,EAAAzC,EAAAuD,cACAvD,EAAAuD,OACAvD,EAAAR,EAAAC,OAAAO,EAAA,CAAAuD,OAAAd,KAGAzC,MAKA,SAAAwD,EAAAC,GAGA,OAAAA,EAAAC,QAAA,qCAAA,IAHA,IA2uCAC,EApuCAnE,EAAAC,OAAAD,EAAAoE,KAAAC,SAAArE,EAAAoE,KAAA,KAAA,CAGAE,MAAA,SAAAC,GACA,OAAAP,EAAA,GAAAhE,EAAAuE,GAAA9C,QAIA+C,OAAA,SAAAD,GACA,IAAA9C,EAAAzB,EAAAuE,GAAA9C,MACA,OAAA,OAAAA,KAAAuC,EAAA,GAAAvC,IAIAgD,UAAA,SAAAF,GACA,OAAAvE,EAAAuE,GAAAG,KAAA,cAKA1E,EAAAO,UAAA,SAAAH,EAAA4B,GACA3B,KAAAK,SAAAV,EAAAC,QAAA,EAAA,GAAAD,EAAAO,UAAAoE,SAAAvE,GACAC,KAAAsB,YAAAK,EACA3B,KAAAuE,QAIA5E,EAAAO,UAAAsE,OAAA,SAAAC,EAAAC,GACA,OAAA,IAAAC,UAAA1E,OACA,WACA,IAAA2E,EAAAjF,EAAAkF,UAAAF,WAEA,OADAC,EAAAE,QAAAL,GACA9E,EAAAO,UAAAsE,OAAAO,MAAA/E,KAAA4E,UAGA/D,IAAA6D,IAGA,EAAAC,UAAA1E,QAAAyE,EAAAM,cAAAC,QACAP,EAAA/E,EAAAkF,UAAAF,WAAAO,MAAA,IAEAR,EAAAM,cAAAC,QACAP,EAAA,CAAAA,IAEA/E,EAAAyC,KAAAsC,EAAA,SAAAS,EAAAC,GACAX,EAAAA,EAAAZ,QAAA,IAAAwB,OAAA,MAAAF,EAAA,MAAA,KAAA,WACA,OAAAC,OAVAX,IAgBA9E,EAAAC,OAAAD,EAAAO,UAAA,CAEAoE,SAAA,CACArB,SAAA,GACAqC,OAAA,GACA/C,MAAA,GACAgD,WAAA,QACAC,aAAA,UACAC,WAAA,QACAC,aAAA,QACAC,cAAA,EACA9D,cAAA,EACA+D,eAAAjG,EAAA,IACAkG,oBAAAlG,EAAA,IACAW,UAAA,EACAwF,OAAA,UACAC,aAAA,EACAC,eAAA,GACAC,UAAA,SAAA5D,GACArC,KAAAkG,WAAA7D,EAGArC,KAAAK,SAAAsF,eACA3F,KAAAK,SAAA8F,aACAnG,KAAAK,SAAA8F,YAAA3E,KAAAxB,KAAAqC,EAAArC,KAAAK,SAAAkF,WAAAvF,KAAAK,SAAAoF,YAEAzF,KAAAoG,UAAApG,KAAAqG,UAAAhE,MAGAiE,WAAA,SAAAjE,GACArC,KAAAuG,UAAAlE,MAAAA,EAAAlB,QAAAnB,KAAAwG,YAAAxG,KAAAyG,SAAApE,IACArC,KAAAqC,QAAAA,IAGAqE,QAAA,SAAArE,EAAA7B,GAqBA,IAAAA,EAAAmG,OAAA,KAAA3G,KAAA4G,aAAAvE,KAAA,IAAA1C,EAAAkH,QAAArG,EAAAsG,QALA,CACA,GAAA,GAAA,GAAA,GAAA,GAAA,GAAA,GACA,GAAA,GAAA,GAAA,GAAA,IAAA,QAKAzE,EAAAlB,QAAAnB,KAAAwG,WAAAnE,EAAAlB,QAAAnB,KAAA+G,UACA/G,KAAAqC,QAAAA,IAGA2E,QAAA,SAAA3E,GAGAA,EAAAlB,QAAAnB,KAAAwG,UACAxG,KAAAqC,QAAAA,GAGAA,EAAA4E,WAAA9F,QAAAnB,KAAAwG,WACAxG,KAAAqC,QAAAA,EAAA4E,aAGAC,UAAA,SAAA7E,EAAAkD,EAAAE,GACA,UAAApD,EAAA8E,KACAnH,KAAAoH,WAAA/E,EAAAlB,MAAAkG,SAAA9B,GAAA+B,YAAA7B,GAEA9F,EAAA0C,GAAAgF,SAAA9B,GAAA+B,YAAA7B,IAGAU,YAAA,SAAA9D,EAAAkD,EAAAE,GACA,UAAApD,EAAA8E,KACAnH,KAAAoH,WAAA/E,EAAAlB,MAAAmG,YAAA/B,GAAA8B,SAAA5B,GAEA9F,EAAA0C,GAAAiF,YAAA/B,GAAA8B,SAAA5B,KAMA8B,YAAA,SAAAlH,GACAV,EAAAC,OAAAD,EAAAO,UAAAoE,SAAAjE,IAGA4C,SAAA,CACAQ,SAAA,0BACAC,OAAA,yBACA8D,MAAA,sCACAC,IAAA,4BACAC,KAAA,6BACAC,QAAA,mCACAC,OAAA,+BACAC,OAAA,4BACAC,QAAA,qCACAC,UAAApI,EAAAO,UAAAsE,OAAA,6CACAwD,UAAArI,EAAAO,UAAAsE,OAAA,yCACAyD,YAAAtI,EAAAO,UAAAsE,OAAA,6DACA0D,MAAAvI,EAAAO,UAAAsE,OAAA,6CACA2D,IAAAxI,EAAAO,UAAAsE,OAAA,mDACA4D,IAAAzI,EAAAO,UAAAsE,OAAA,sDACA6D,KAAA1I,EAAAO,UAAAsE,OAAA,oCAGA8D,kBAAA,EAEAC,UAAA,CAEAhE,KAAA,WACAvE,KAAAwI,eAAA7I,EAAAK,KAAAK,SAAAwF,qBACA7F,KAAAyI,aAAAzI,KAAAwI,eAAAvI,QAAAD,KAAAwI,gBAAA7I,EAAAK,KAAAsB,aACAtB,KAAA0I,WAAA/I,EAAAK,KAAAK,SAAAuF,gBAAA+C,IAAA3I,KAAAK,SAAAwF,qBACA7F,KAAAwG,UAAA,GACAxG,KAAA4I,WAAA,GACA5I,KAAA4B,eAAA,EACA5B,KAAA6I,QAAA,GACA7I,KAAA+G,QAAA,GACA/G,KAAA8I,QAEA,IAEAvG,EAFAjB,EAAAtB,KAAAsB,YACAgE,EAAAtF,KAAAsF,OAAA,GAeA,SAAAyD,EAAAvI,GACA,IAcAN,EACA8I,EACA3I,EAhBAyC,OAAA,IAAAnD,EAAAK,MAAAI,KAAA,oBAAA,UAAAT,EAAAK,MAAAI,KAAA,oBAGAJ,KAAA2B,MAAAmB,IACA9C,KAAA2B,KAAAhC,EAAAK,MAAA+C,QAAA,QAAA,GACA/C,KAAAmB,KAAAxB,EAAAK,MAAAI,KAAA,SAKAkB,IAAAtB,KAAA2B,OAIAzB,EAAAP,EAAAQ,KAAAH,KAAA2B,KAAA,aACAqH,EAAA,KAAAxI,EAAA2G,KAAAtD,QAAA,YAAA,KACAxD,EAAAH,EAAAG,UACA2I,KAAArJ,EAAAK,MAAAmC,GAAA9B,EAAAyF,SACAzF,EAAA2I,GAAAxH,KAAAtB,EAAAF,KAAAQ,IAhCAb,EAAAyC,KAAApC,KAAAK,SAAAiF,OAAA,SAAA2D,EAAAC,GACA,iBAAAA,IACAA,EAAAA,EAAAhG,MAAA,OAEAvD,EAAAyC,KAAA8G,EAAA,SAAA/F,EAAAhC,GACAmE,EAAAnE,GAAA8H,MAGA1G,EAAAvC,KAAAK,SAAAkC,MACA5C,EAAAyC,KAAAG,EAAA,SAAA0G,EAAAC,GACA3G,EAAA0G,GAAAtJ,EAAAO,UAAA8C,cAAAkG,KA8BAvJ,EAAAK,KAAAsB,aACAf,GAAA,oDANA,CAAA,QAAA,oBAAA,gBAAA,SAAA,WAAA,kBAAA,kBACA,eAAA,eAAA,iBAAA,oBAAA,gBAAA,iBACA,gBAAA,gBAAA,0BAAA,iBAAA,iBACA,iBAAA,oBAAA,oBAAA,mBAGA+B,OAAAtC,KAAAK,SAAA2F,gBAAAmD,KAAA,MAAAJ,GAIAxI,GAAA,iBANA,CAAA,SAAA,SAAA,iBAAA,qBAMA+B,OAAAtC,KAAAK,SAAA2F,gBAAAmD,KAAA,MAAAJ,GAEA/I,KAAAK,SAAA+I,gBACAzJ,EAAAK,KAAAsB,aAAAf,GAAA,wBAAAP,KAAAK,SAAA+I,iBAKAzH,KAAA,WAQA,OAPA3B,KAAAqJ,YACA1J,EAAAC,OAAAI,KAAAwG,UAAAxG,KAAAsJ,UACAtJ,KAAA+G,QAAApH,EAAAC,OAAA,GAAAI,KAAAsJ,UACAtJ,KAAAiC,SACAtC,EAAAK,KAAAsB,aAAAiI,eAAA,eAAA,CAAAvJ,OAEAA,KAAAwJ,aACAxJ,KAAAiC,SAGAoH,UAAA,WACArJ,KAAAyJ,cACA,IAAA,IAAAtE,EAAA,EAAAuE,EAAA1J,KAAA2J,gBAAA3J,KAAA0J,WAAAA,EAAAvE,GAAAA,IACAnF,KAAA4J,MAAAF,EAAAvE,IAEA,OAAAnF,KAAAiC,SAIAI,QAAA,SAAAA,GACA,IAIAwH,EAAAC,EAJAC,EAAA/J,KAAAgK,MAAA3H,GACA4H,EAAAjK,KAAAkK,oBAAAH,GACAI,EAAAnK,KACAgB,GAAA,EA2CA,YAxCAH,IAAAoJ,SACAjK,KAAA+G,QAAAgD,EAAA5I,OAEAnB,KAAAoK,eAAAH,GACAjK,KAAA2J,gBAAAhK,EAAAsK,IAIAH,EAAA9J,KAAAsF,OAAA2E,EAAA9I,QAEAxB,EAAAyC,KAAApC,KAAAsF,OAAA,SAAAnE,EAAAkJ,GACAA,IAAAP,GAAA3I,IAAA8I,EAAA9I,OACA4I,EAAAI,EAAAD,oBAAAC,EAAAH,MAAAG,EAAA/C,WAAAjG,OACA4I,EAAA5I,QAAAgJ,EAAApD,UACAoD,EAAAR,gBAAAW,KAAAP,GACA/I,EAAAmJ,EAAAP,MAAAG,IAAA/I,KAMA6I,GAAA,IAAA7J,KAAA4J,MAAAK,GACAjJ,EAAAA,GAAA6I,EAEA7J,KAAA+G,QAAAkD,EAAA9I,OADA0I,EAMA7J,KAAAuK,qBAGAvK,KAAAwK,OAAAxK,KAAAwK,OAAA7B,IAAA3I,KAAA0I,aAEA1I,KAAAwJ,aAGA7J,EAAA0C,GAAAjC,KAAA,gBAAAyJ,IAGA7I,GAIAwI,WAAA,SAAAiB,GACA,IACAvK,EADAuK,IACAvK,EAAAF,KAGAL,EAAAC,OAAAI,KAAAsJ,SAAAmB,GACAzK,KAAAkC,UAAAvC,EAAA+K,IAAA1K,KAAAsJ,SAAA,SAAAqB,EAAAxJ,GACA,MAAA,CACAwJ,QAAAA,EACAtI,QAAAnC,EAAAkH,WAAAjG,GAAA,MAKAnB,KAAA4K,YAAAjL,EAAAkL,KAAA7K,KAAA4K,YAAA,SAAAvI,GACA,QAAAA,EAAAlB,QAAAsJ,MAGAzK,KAAAK,SAAAmJ,WACAxJ,KAAAK,SAAAmJ,WAAAhI,KAAAxB,KAAAA,KAAAsJ,SAAAtJ,KAAAkC,WAEAlC,KAAA8K,qBAKAC,UAAA,WACApL,EAAAE,GAAAkL,WACApL,EAAAK,KAAAsB,aAAAyJ,YAEA/K,KAAA+G,QAAA,GACA/G,KAAAwG,UAAA,GACAxG,KAAAyJ,cACAzJ,KAAAgL,aACA,IAAAtB,EAAA1J,KAAA0J,WACAuB,WAAA,iBACAC,WAAA,gBAEAlL,KAAAmL,cAAAzB,IAGAyB,cAAA,SAAAzB,GACA,IAAAvE,EAEA,GAAAnF,KAAAK,SAAA8F,YACA,IAAAhB,EAAA,EAAAuE,EAAAvE,GAAAA,IACAnF,KAAAK,SAAA8F,YAAA3E,KAAAxB,KAAA0J,EAAAvE,GACAnF,KAAAK,SAAAkF,WAAA,IACAvF,KAAAoH,WAAAsC,EAAAvE,GAAAhE,MAAAmG,YAAAtH,KAAAK,SAAAoF,iBAGAiE,EACApC,YAAAtH,KAAAK,SAAAkF,YACA+B,YAAAtH,KAAAK,SAAAoF,aAIA8E,iBAAA,WACA,OAAAvK,KAAAoL,aAAApL,KAAA+G,UAGAqE,aAAA,SAAAC,GAEA,IACAlG,EADAmG,EAAA,EAEA,IAAAnG,KAAAkG,OAIAxK,IAAAwK,EAAAlG,IAAA,OAAAkG,EAAAlG,KAAA,IAAAkG,EAAAlG,IACAmG,IAGA,OAAAA,GAGAN,WAAA,WACAhL,KAAAoG,UAAApG,KAAAwK,SAGApE,UAAA,SAAAqE,GACAA,EAAAc,IAAAvL,KAAA0I,YAAA8C,KAAA,IACAxL,KAAAyL,WAAAhB,GAAAiB,QAGAzJ,MAAA,WACA,OAAA,IAAAjC,KAAA2L,QAGAA,KAAA,WACA,OAAA3L,KAAAkC,UAAAjC,QAGA4B,aAAA,WACA,GAAA7B,KAAAK,SAAAwB,aACA,IACAlC,EAAAK,KAAA4L,kBAAA5L,KAAAkC,UAAAjC,QAAAD,KAAAkC,UAAA,GAAAG,SAAA,IACAwJ,OAAA,YACAC,QAAA,SAGAA,QAAA,WACA,MAAAC,MAOAH,eAAA,WACA,IAAA1F,EAAAlG,KAAAkG,WACA,OAAAA,GAEA,IAFAvG,EAAAkL,KAAA7K,KAAAkC,UAAA,SAAAkD,GACA,OAAAA,EAAA/C,QAAAlB,OAAA+E,EAAA/E,OACAlB,QAAAiG,GAGAwD,SAAA,WACA,IAAAxJ,EAAAF,KACAgM,EAAA,GAIA,OAAArM,EAAAK,KAAAsB,aACA2K,KAJA,CAAA,QAAA,SAAA,WAAA,qBAIA3J,OAAAtC,KAAAK,SAAA2F,gBAAAmD,KAAA,OACAoC,IAAA,sCACAA,IAAAvL,KAAAK,SAAAyF,QACA+F,OAAA,WACA,IAAA1K,EAAAnB,KAAAmB,MAAAxB,EAAAK,MAAAI,KAAA,QACA0C,OAAA,IAAAnD,EAAAK,MAAAI,KAAA,oBAAA,UAAAT,EAAAK,MAAAI,KAAA,mBAaA,OAXAe,GAAAjB,EAAAG,SAAAkB,OAAAO,OAAAC,SACAA,QAAAmK,MAAA,0BAAAlM,MAIA8C,IACA9C,KAAA2B,KAAAhC,EAAAK,MAAA+C,QAAA,QAAA,GACA/C,KAAAmB,KAAAA,GAIAnB,KAAA2B,OAAAzB,EAAAoB,gBAKAH,KAAA6K,IAAA9L,EAAAkL,aAAAzL,EAAAK,MAAAuC,YAIAyJ,EAAA7K,IAAA,OAKA6I,MAAA,SAAAmC,GACA,OAAAxM,EAAAwM,GAAA,IAGA1B,OAAA,WACA,IAAAlF,EAAAvF,KAAAK,SAAAkF,WAAArC,MAAA,KAAAiG,KAAA,KACA,OAAAxJ,EAAAK,KAAAK,SAAAqF,aAAA,IAAAH,EAAAvF,KAAAyI,eAGA2D,eAAA,WACApM,KAAA4K,YAAA,GACA5K,KAAAkC,UAAA,GACAlC,KAAAsJ,SAAA,GACAtJ,KAAAqM,OAAA1M,EAAA,IACAK,KAAAwK,OAAA7K,EAAA,KAGAmJ,MAAA,WACA9I,KAAAoM,iBACApM,KAAA2J,gBAAAhK,EAAA,KAGA8J,YAAA,WACAzJ,KAAA8I,QACA9I,KAAAwK,OAAAxK,KAAAyK,SAAA9B,IAAA3I,KAAA0I,aAGA0B,eAAA,SAAA/H,GACArC,KAAA8I,QACA9I,KAAAwK,OAAAxK,KAAAqG,UAAAhE,IAGAuE,aAAA,SAAAvE,GACA,IAGAjB,EAAAkL,EAHAC,EAAA5M,EAAA0C,GACA8E,EAAA9E,EAAA8E,KACArE,OAAA,IAAAyJ,EAAAnM,KAAA,oBAAA,UAAAmM,EAAAnM,KAAA,mBAGA,MAAA,UAAA+G,GAAA,aAAAA,EACAnH,KAAAoH,WAAA/E,EAAAlB,MAAA0K,OAAA,YAAAzK,MACA,WAAA+F,QAAA,IAAA9E,EAAAmK,SACAnK,EAAAmK,SAAAC,SAAA,MAAAF,EAAAnL,OAIAA,EADA0B,EACAyJ,EAAAf,OAEAe,EAAAnL,MAGA,SAAA+F,EAGA,mBAAA/F,EAAAsL,OAAA,EAAA,IACAtL,EAAAsL,OAAA,IAMA,IADAJ,EAAAlL,EAAAuL,YAAA,OAOA,IADAL,EAAAlL,EAAAuL,YAAA,OAJAvL,EAAAsL,OAAAJ,EAAA,GAUAlL,EAGA,iBAAAA,EACAA,EAAAyC,QAAA,MAAA,IAEAzC,IAGAwI,MAAA,SAAAvH,GACAA,EAAArC,KAAAkK,oBAAAlK,KAAAgK,MAAA3H,IAEA,IAMArB,EAAAoC,EAAAwJ,EAAAC,EANAtK,EAAA5C,EAAA0C,GAAAE,QACAuK,EAAAnN,EAAA+K,IAAAnI,EAAA,SAAA6C,EAAAD,GACA,OAAAA,IACAlF,OACA8M,GAAA,EACA3L,EAAApB,KAAA4G,aAAAvE,GAwBA,IAAAe,KApBApD,KAAAgN,aAAA3K,GAIA,mBAAAE,EAAAsK,WACAA,EAAAtK,EAAAsK,WACA,mBAAA7M,KAAAK,SAAAwM,aACAA,EAAA7M,KAAAK,SAAAwM,YAMAA,IACAzL,EAAAyL,EAAArL,KAAAa,EAAAjB,UAGAmB,EAAAsK,YAGAtK,EAAA,CACAqK,EAAA,CAAAxJ,OAAAA,EAAA6J,WAAA1K,EAAAa,IACA,IAKA,GAAA,yBAJApC,EAAArB,EAAAO,UAAAgN,QAAA9J,GAAA5B,KAAAxB,KAAAoB,EAAAiB,EAAAuK,EAAAK,cAIA,IAAAH,EAAA,CACAC,GAAA,EACA,SAIA,GAFAA,GAAA,EAEA,YAAA/L,EAEA,YADAhB,KAAAwK,OAAAxK,KAAAwK,OAAAe,IAAAvL,KAAAqG,UAAAhE,KAIA,IAAArB,EAEA,OADAhB,KAAAmN,aAAA9K,EAAAuK,IACA,EAEA,MAAAb,GAQA,MAPA/L,KAAAK,SAAAkB,OAAAO,OAAAC,SACAA,QAAAqL,IAAA,4CAAA/K,EAAAgL,GAAA,gBAAAT,EAAAxJ,OAAA,YAAA2I,GAEAA,aAAAuB,YACAvB,EAAApB,SAAA,+CAAAtI,EAAAgL,GAAA,gBAAAT,EAAAxJ,OAAA,aAGA2I,GAGA,IAAAgB,EAMA,OAHA/M,KAAAoL,aAAA7I,IACAvC,KAAA4K,YAAAN,KAAAjI,IAEA,GAMAkL,kBAAA,SAAAlL,EAAAe,GACA,OAAAzD,EAAA0C,GAAAlC,KAAA,MAAAiD,EAAAoK,OAAA,GAAAC,cACArK,EAAAsK,UAAA,GAAAC,gBAAAhO,EAAA0C,GAAAlC,KAAA,QAIAyN,cAAA,SAAAzM,EAAAiC,GACA,IAAAyK,EAAA7N,KAAAK,SAAA4C,SAAA9B,GACA,OAAA0M,IAAAA,EAAA7I,cAAA8I,OAAAD,EAAAA,EAAAzK,KAIA2K,YAAA,WACA,IAAA,IAAA5I,EAAA,EAAAA,EAAAR,UAAA1E,OAAAkF,IACA,QAAAtE,IAAA8D,UAAAQ,GACA,OAAAR,UAAAQ,IAeA6I,eAAA,SAAA3L,EAAAuK,GACA,iBAAAA,IACAA,EAAA,CAAAxJ,OAAAwJ,IAGA,IAAAjC,EAAA3K,KAAA+N,YACA/N,KAAA4N,cAAAvL,EAAAlB,KAAAyL,EAAAxJ,QACApD,KAAAuN,kBAAAlL,EAAAuK,EAAAxJ,SAGApD,KAAAK,SAAA0F,aAAA1D,EAAA4L,YAAApN,EACAlB,EAAAO,UAAA+C,SAAA2J,EAAAxJ,QACA,2CAAAf,EAAAlB,KAAA,aAEA+M,EAAA,gBAOA,MANA,mBAAAvD,EACAA,EAAAA,EAAAnJ,KAAAxB,KAAA4M,EAAAK,WAAA5K,GACA6L,EAAAC,KAAAxD,KACAA,EAAAhL,EAAAO,UAAAsE,OAAAmG,EAAA9G,QAAAqK,EAAA,QAAAtB,EAAAK,aAGAtC,GAGAwC,aAAA,SAAA9K,EAAAuK,GACA,IAAAjC,EAAA3K,KAAAgO,eAAA3L,EAAAuK,GAEA5M,KAAAkC,UAAAoI,KAAA,CACAK,QAAAA,EACAtI,QAAAA,EACAe,OAAAwJ,EAAAxJ,SAGApD,KAAAsJ,SAAAjH,EAAAlB,MAAAwJ,EACA3K,KAAAwG,UAAAnE,EAAAlB,MAAAwJ,GAGAc,WAAA,SAAA2C,GAIA,OAHApO,KAAAK,SAAAgO,UACAD,EAAAA,EAAAzF,IAAAyF,EAAAE,OAAAtO,KAAAK,SAAAgO,WAEAD,GAGAtD,kBAAA,WAEA,IADA,IAAApB,EAAAwC,EACA/G,EAAA,EAAAnF,KAAAkC,UAAAiD,GAAAA,IACA+G,EAAAlM,KAAAkC,UAAAiD,GACAnF,KAAAK,SAAA6G,WACAlH,KAAAK,SAAA6G,UAAA1F,KAAAxB,KAAAkM,EAAA7J,QAAArC,KAAAK,SAAAkF,WAAAvF,KAAAK,SAAAoF,YAEAzF,KAAAuO,UAAArC,EAAA7J,QAAA6J,EAAAvB,SAKA,GAHA3K,KAAAkC,UAAAjC,SACAD,KAAAqM,OAAArM,KAAAqM,OAAA1D,IAAA3I,KAAA0I,aAEA1I,KAAAK,SAAAmO,QACA,IAAArJ,EAAA,EAAAnF,KAAA4K,YAAAzF,GAAAA,IACAnF,KAAAuO,UAAAvO,KAAA4K,YAAAzF,IAGA,GAAAnF,KAAAK,SAAA8F,YACA,IAAAhB,EAAA,EAAAuE,EAAA1J,KAAAyO,gBAAA/E,EAAAvE,GAAAA,IACAnF,KAAAK,SAAA8F,YAAA3E,KAAAxB,KAAA0J,EAAAvE,GAAAnF,KAAAK,SAAAkF,WAAAvF,KAAAK,SAAAoF,YAGAzF,KAAAwK,OAAAxK,KAAAwK,OAAAe,IAAAvL,KAAAqM,QACArM,KAAAgL,aACAhL,KAAAyL,WAAAzL,KAAAqM,QAAAqC,QAGAD,cAAA,WACA,OAAAzO,KAAA2J,gBAAA4B,IAAAvL,KAAA2O,oBAGAA,gBAAA,WACA,OAAAhP,EAAAK,KAAAkC,WAAAwI,IAAA,WACA,OAAA1K,KAAAqC,WAIAkM,UAAA,SAAAlM,EAAAsI,GACA,IAAAiE,EAAA9E,EAAA+E,EAAA1E,EACA+B,EAAAlM,KAAAqG,UAAAhE,GACAyM,EAAA9O,KAAA+O,SAAA1M,GACA2M,EAAArP,EAAA0C,GAAAjC,KAAA,oBAEA8L,EAAAjM,QAGAiM,EAAA5E,YAAAtH,KAAAK,SAAAoF,YAAA4B,SAAArH,KAAAK,SAAAkF,YAGAvF,KAAAK,UAAAL,KAAAK,SAAA4O,WACA/C,EAAAV,KAAAb,GAAA,IAEAuB,EAAAgD,KAAAvE,GAAA,MAKAuB,EAAAvM,EAAA,IAAAK,KAAAK,SAAAqF,aAAA,KACAtF,KAAA,KAAA0O,EAAA,UACAzH,SAAArH,KAAAK,SAAAkF,YAEAvF,KAAAK,UAAAL,KAAAK,SAAA4O,WACA/C,EAAAV,KAAAb,GAAA,IAEAuB,EAAAgD,KAAAvE,GAAA,IAIAiE,EAAA1C,EACAlM,KAAAK,SAAAgO,UAIAO,EAAA1C,EAAAR,OAAAgD,OAAAS,KAAA,IAAAnP,KAAAK,SAAAgO,QAAA,MAAAC,UAEAtO,KAAAwI,eAAAvI,OACAD,KAAAwI,eAAA4G,OAAAR,GACA5O,KAAAK,SAAAgP,eACArP,KAAAK,SAAAgP,eAAA7N,KAAAxB,KAAA4O,EAAAjP,EAAA0C,IAEAuM,EAAAU,YAAAjN,GAIA6J,EAAA/J,GAAA,SAGA+J,EAAA9L,KAAA,MAAA0O,GAIA,IAAA5C,EAAAqD,QAAA,cAAAvP,KAAAwP,cAAAV,GAAA,MAAA7O,SACA4O,EAAA3C,EAAA9L,KAAA,MAGA4O,EAEAA,EAAAS,MAAA,IAAApK,OAAA,MAAArF,KAAAwP,cAAAX,GAAA,UAGAG,GAAA,IAAAH,GAJAG,EAAAH,EAMAlP,EAAA0C,GAAAjC,KAAA,mBAAA4O,IAGAlF,EAAA9J,KAAAsF,OAAAjD,EAAAlB,SAEAgJ,EAAAnK,KACAL,EAAAyC,KAAA+H,EAAA7E,OAAA,SAAAnE,EAAAkJ,GACAA,IAAAP,GACAnK,EAAA,UAAAwK,EAAAqF,cAAArO,GAAA,KAAAgJ,EAAA7I,aACAlB,KAAA,mBAAA8L,EAAA9L,KAAA,aAMAuK,GAAA3K,KAAAK,SAAAmO,UACAtC,EAAAV,KAAA,IACA,iBAAAxL,KAAAK,SAAAmO,QACAtC,EAAA7E,SAAArH,KAAAK,SAAAmO,SAEAxO,KAAAK,SAAAmO,QAAAtC,EAAA7J,IAGArC,KAAAqM,OAAArM,KAAAqM,OAAA1D,IAAAuD,IAGA7F,UAAA,SAAAhE,GACA,IAAAlB,EAAAnB,KAAAwP,cAAAxP,KAAA+O,SAAA1M,IACAqN,EAAA/P,EAAA0C,GAAAjC,KAAA,oBACA+L,EAAA,cAAAhL,EAAA,kBAAAA,EAAA,OAQA,OALAuO,IACAvD,EAAAA,EAAA,MAAAnM,KAAAwP,cAAAE,GACA7L,QAAA,OAAA,QAGA7D,KACAyK,SACAoB,OAAAM,IAMAqD,cAAA,SAAAG,GACA,YAAA9O,IAAA8O,EACA,GAGAA,EAAA9L,QAAA,yCAAA,SAGAkL,SAAA,SAAA1M,GACA,OAAArC,KAAAsF,OAAAjD,EAAAlB,QAAAnB,KAAAuG,UAAAlE,IAAAA,EAAAgL,IAAAhL,EAAAlB,MAGA+I,oBAAA,SAAA7H,GAQA,OALArC,KAAAuG,UAAAlE,KACAA,EAAArC,KAAAoH,WAAA/E,EAAAlB,OAIAxB,EAAA0C,GAAAkJ,IAAAvL,KAAAK,SAAAyF,QAAA,IAGAS,UAAA,SAAAlE,GACA,MAAA,kBAAA8L,KAAA9L,EAAA8E,OAGAC,WAAA,SAAAjG,GACA,OAAAxB,EAAAK,KAAAsB,aAAA2K,KAAA,UAAAjM,KAAAwP,cAAArO,GAAA,OAGAyO,UAAA,SAAA1G,EAAA7G,GACA,OAAAA,EAAAwN,SAAAlC,eACA,IAAA,SACA,OAAAhO,EAAA,kBAAA0C,GAAApC,OACA,IAAA,QACA,GAAAD,KAAAuG,UAAAlE,GACA,OAAArC,KAAAoH,WAAA/E,EAAAlB,MAAA0K,OAAA,YAAA5L,OAGA,OAAAiJ,EAAAjJ,QAGA6P,OAAA,SAAAlN,EAAAP,GACA,OAAArC,KAAA+P,mBAAAnN,IAAA5C,KAAA+P,mBAAAnN,GAAAA,EAAAP,IAGA0N,YAAA,CACAC,QAAA,SAAApN,GACA,OAAAA,GAEA+M,OAAA,SAAA/M,EAAAP,GACA,QAAA1C,EAAAiD,EAAAP,EAAAV,MAAA1B,QAEAgQ,SAAA,SAAArN,EAAAP,GACA,OAAAO,EAAAP,KAIAoE,SAAA,SAAApE,GACA,IAAAjB,EAAApB,KAAA4G,aAAAvE,GACA,OAAA1C,EAAAO,UAAAgN,QAAAzJ,SAAAjC,KAAAxB,KAAAoB,EAAAiB,IAAA,uBAGA6N,gBAAA,SAAA7N,GACA,MAAA,WAAAA,EAAAlB,MAGAgP,aAAA,SAAA9N,GACArC,KAAA6I,QAAAxG,EAAAlB,QACAnB,KAAA4B,iBACAjC,EAAA0C,GAAAgF,SAAArH,KAAAK,SAAAmF,cACAxF,KAAA6I,QAAAxG,EAAAlB,OAAA,IAIAiP,YAAA,SAAA/N,EAAAJ,GACAjC,KAAA4B,iBAGA5B,KAAA4B,eAAA,IACA5B,KAAA4B,eAAA,UAEA5B,KAAA6I,QAAAxG,EAAAlB,MACAxB,EAAA0C,GAAAiF,YAAAtH,KAAAK,SAAAmF,cACAvD,GAAA,IAAAjC,KAAA4B,gBAAA5B,KAAAkB,eAAAlB,KAAA2B,QAAA,IAAA3B,KAAA4B,gBACAjC,EAAAK,KAAAsB,aAAAwK,QAAA,UAMA9L,KAAAS,cACAd,EAAA,sBAAAK,KAAAS,aAAAU,KAAA,KAAAnB,KAAAsB,aAAAG,SAGAzB,KAAAkB,eAAA,IACAe,GAAA,IAAAjC,KAAA4B,gBAAA5B,KAAAkB,gBACAvB,EAAAK,KAAAsB,aAAAiI,eAAA,eAAA,CAAAvJ,OACAA,KAAAkB,eAAA,IAIA8L,aAAA,SAAA3K,GACA,IAAAgO,EAEArQ,KAAA6I,QAAAxG,EAAAlB,QACAkP,EAAArQ,KAAAkQ,gBAAA7N,GACA1C,EAAA2Q,UAAAD,GAEArQ,KAAA4B,iBAGA5B,KAAA4B,eAAA,IACA5B,KAAA4B,eAAA,UAGA5B,KAAA6I,QAAAxG,EAAAlB,MACAxB,EAAA0C,GAAAiF,YAAAtH,KAAAK,SAAAmF,gBAIA+K,cAAA,SAAAlO,EAAAe,GAGA,OAFAA,EAAA,iBAAAA,GAAAA,GAAA,SAEAzD,EAAAQ,KAAAkC,EAAA,kBAAA1C,EAAAQ,KAAAkC,EAAA,gBAAA,CACAmO,IAAA,KACAvO,OAAA,EACA0I,QAAA3K,KAAAgO,eAAA3L,EAAA,CAAAe,OAAAA,OAKAqN,QAAA,WACAzQ,KAAA+K,YAEApL,EAAAK,KAAAsB,aACAoP,IAAA,aACAzF,WAAA,aACAgB,KAAA,0BACAyE,IAAA,qBACApJ,YAAA,yBACA2E,KAAA,2BACAyE,IAAA,sBACApJ,YAAA,0BACA2E,KAAA,gCACAyE,IAAA,2BACApJ,YAAA,+BACA2E,KAAA,mCACAyE,IAAA,8BACApJ,YAAA,kCACA2E,KAAA,8BACAyE,IAAA,yBACApJ,YAAA,+BAKAqJ,kBAAA,CACAlN,SAAA,CAAAA,UAAA,GACA+D,MAAA,CAAAA,OAAA,GACAC,IAAA,CAAAA,KAAA,GACAC,KAAA,CAAAA,MAAA,GACAC,QAAA,CAAAA,SAAA,GACAC,OAAA,CAAAA,QAAA,GACAC,OAAA,CAAAA,QAAA,GACA+I,WAAA,CAAAA,YAAA,IAGAC,cAAA,SAAAC,EAAAvO,GACAuO,EAAA9L,cAAA8I,OACA9N,KAAA2Q,kBAAAG,GAAAvO,EAEA5C,EAAAC,OAAAI,KAAA2Q,kBAAAG,IAIAxN,WAAA,SAAAjB,GACA,IAAAE,EAAA,GACAwO,EAAApR,EAAA0C,GAAAjC,KAAA,SASA,OAPA2Q,GACApR,EAAAyC,KAAA2O,EAAA7N,MAAA,KAAA,WACAlD,QAAAL,EAAAO,UAAAyQ,mBACAhR,EAAAC,OAAA2C,EAAA5C,EAAAO,UAAAyQ,kBAAA3Q,SAIAuC,GAGAyO,uBAAA,SAAAzO,EAAA4E,EAAA/D,EAAA8F,GAIA,eAAAiF,KAAA/K,KAAA,OAAA+D,GAAA,oBAAAgH,KAAAhH,MACA+B,EAAA+H,OAAA/H,GAGAgI,MAAAhI,KACAA,OAAArI,IAIAqI,GAAA,IAAAA,EACA3G,EAAAa,GAAA8F,EACA/B,IAAA/D,GAAA,UAAA+D,IAIA5E,EAAA,SAAA4E,EAAA,UAAA/D,IAAA,IAIAG,eAAA,SAAAlB,GACA,IAGAe,EAAA8F,EAHA3G,EAAA,GACAgK,EAAA5M,EAAA0C,GACA8E,EAAA9E,EAAA8O,aAAA,QAGA,IAAA/N,KAAAzD,EAAAO,UAAAgN,QAaAhE,EAVA,aAAA9F,GAKA,MAJA8F,EAAA7G,EAAA8O,aAAA/N,MAKA8F,GAAA,KAIAA,GAEAqD,EAAAnM,KAAAgD,GAGApD,KAAAgR,uBAAAzO,EAAA4E,EAAA/D,EAAA8F,GAQA,OAJA3G,EAAAwF,WAAA,uBAAAoG,KAAA5L,EAAAwF,mBACAxF,EAAAwF,UAGAxF,GAGAiB,UAAA,SAAAnB,GACA,IAGAe,EAAA8F,EAHA3G,EAAA,GACAgK,EAAA5M,EAAA0C,GACA8E,EAAA9E,EAAA8O,aAAA,QAGA,IAAA/N,KAAAzD,EAAAO,UAAAgN,QAIA,MAHAhE,EAAAqD,EAAApM,KAAA,OAAAiD,EAAAoK,OAAA,GAAAC,cAAArK,EAAAsK,UAAA,GAAAC,kBAIAzE,GAAA,GAGAlJ,KAAAgR,uBAAAzO,EAAA4E,EAAA/D,EAAA8F,GAEA,OAAA3G,GAGAG,YAAA,SAAAL,GACA,IAAAE,EAAA,GACArC,EAAAP,EAAAQ,KAAAkC,EAAAV,KAAA,aAKA,OAHAzB,EAAAG,SAAAkC,QACAA,EAAA5C,EAAAO,UAAA8C,cAAA9C,EAAAG,SAAAkC,MAAAF,EAAAlB,QAAA,IAEAoB,GAGAc,eAAA,SAAAd,EAAAF,GAmEA,OAhEA1C,EAAAyC,KAAAG,EAAA,SAAA8B,EAAAjD,GAGA,IAAA,IAAAA,GAIA,GAAAA,EAAAwB,OAAAxB,EAAAgQ,QAAA,CACA,IAAAC,GAAA,EACA,cAAAjQ,EAAAgQ,SACA,IAAA,SACAC,IAAA1R,EAAAyB,EAAAgQ,QAAA/O,EAAAV,MAAA1B,OACA,MACA,IAAA,WACAoR,EAAAjQ,EAAAgQ,QAAA5P,KAAAa,EAAAA,GAGAgP,EACA9O,EAAA8B,QAAAxD,IAAAO,EAAAwB,OAAAxB,EAAAwB,OAEAjD,EAAAQ,KAAAkC,EAAAV,KAAA,aAAAwJ,cAAAxL,EAAA0C,WACAE,EAAA8B,iBAjBA9B,EAAA8B,KAuBA1E,EAAAyC,KAAAG,EAAA,SAAAqK,EAAA0E,GACA/O,EAAAqK,GAAA,mBAAA0E,GAAA,eAAA1E,EAAA0E,EAAAjP,GAAAiP,IAIA3R,EAAAyC,KAAA,CAAA,YAAA,aAAA,WACAG,EAAAvC,QACAuC,EAAAvC,MAAAiR,OAAA1O,EAAAvC,UAGAL,EAAAyC,KAAA,CAAA,cAAA,SAAA,WACA,IAAAmP,EACAhP,EAAAvC,QACAiF,MAAAuM,QAAAjP,EAAAvC,OACAuC,EAAAvC,MAAA,CAAAiR,OAAA1O,EAAAvC,MAAA,IAAAiR,OAAA1O,EAAAvC,MAAA,KACA,iBAAAuC,EAAAvC,QACAuR,EAAAhP,EAAAvC,MAAA6D,QAAA,UAAA,IAAAX,MAAA,UACAX,EAAAvC,MAAA,CAAAiR,OAAAM,EAAA,IAAAN,OAAAM,EAAA,SAKA5R,EAAAO,UAAAoI,mBAGA,MAAA/F,EAAA6F,KAAA,MAAA7F,EAAA4F,MACA5F,EAAA2F,MAAA,CAAA3F,EAAA6F,IAAA7F,EAAA4F,YACA5F,EAAA6F,WACA7F,EAAA4F,KAEA,MAAA5F,EAAAyF,WAAA,MAAAzF,EAAAwF,YACAxF,EAAA0F,YAAA,CAAA1F,EAAAyF,UAAAzF,EAAAwF,kBACAxF,EAAAyF,iBACAzF,EAAAwF,YAIAxF,GAIAS,cAAA,SAAA7C,GACA,IACAsR,EAMA,MAPA,iBAAAtR,IACAsR,EAAA,GACA9R,EAAAyC,KAAAjC,EAAA+C,MAAA,MAAA,WACAuO,EAAAzR,OAAA,IAEAG,EAAAsR,GAEAtR,GAIAuR,UAAA,SAAAvQ,EAAAiC,EAAAuH,GACAhL,EAAAO,UAAAgN,QAAA/L,GAAAiC,EACAzD,EAAAO,UAAA+C,SAAA9B,QAAAN,IAAA8J,EAAAA,EAAAhL,EAAAO,UAAA+C,SAAA9B,GACAiC,EAAAnD,OAAA,GACAN,EAAAO,UAAA2Q,cAAA1P,EAAAxB,EAAAO,UAAA8C,cAAA7B,KAKA+L,QAAA,CAGAzJ,SAAA,SAAAyF,EAAA7G,EAAAO,GAGA,IAAA5C,KAAA8P,OAAAlN,EAAAP,GACA,MAAA,sBAEA,GAAA,WAAAA,EAAAwN,SAAAlC,cAMA,OAAA3N,KAAAuG,UAAAlE,GACA,EAAArC,KAAA4P,UAAA1G,EAAA7G,GAEA6G,MAAAA,GAAA,EAAAA,EAAAjJ,OANA,IAAAmB,EAAAzB,EAAA0C,GAAAjB,MACA,OAAAA,GAAA,EAAAA,EAAAnB,QASAuH,MAAA,SAAA0B,EAAA7G,GAMA,OAAArC,KAAAyG,SAAApE,IAAA,wIAAA8L,KAAAjF,IAIAzB,IAAA,SAAAyB,EAAA7G,GAMA,OAAArC,KAAAyG,SAAApE,IAAA,khBAAA8L,KAAAjF,IAIAxB,MACA5D,GAAA,EAEA,SAAAoF,EAAA7G,GAcA,OAbAyB,IACAA,GAAA,EACA9D,KAAAK,SAAAkB,OAAAO,OAAAC,SACAA,QAAAC,KACA,uTASAhC,KAAAyG,SAAApE,KAAA,cAAA8L,KAAA,IAAAwD,KAAAzI,GAAA0I,cAKAjK,QAAA,SAAAuB,EAAA7G,GACA,OAAArC,KAAAyG,SAAApE,IAAA,+DAAA8L,KAAAjF,IAIAtB,OAAA,SAAAsB,EAAA7G,GACA,OAAArC,KAAAyG,SAAApE,IAAA,gDAAA8L,KAAAjF,IAIArB,OAAA,SAAAqB,EAAA7G,GACA,OAAArC,KAAAyG,SAAApE,IAAA,QAAA8L,KAAAjF,IAIAlB,UAAA,SAAAkB,EAAA7G,EAAAO,GACA,IAAA3C,EAAAgF,MAAAuM,QAAAtI,GAAAA,EAAAjJ,OAAAD,KAAA4P,UAAA1G,EAAA7G,GACA,OAAArC,KAAAyG,SAAApE,IAAAO,GAAA3C,GAIA8H,UAAA,SAAAmB,EAAA7G,EAAAO,GACA,IAAA3C,EAAAgF,MAAAuM,QAAAtI,GAAAA,EAAAjJ,OAAAD,KAAA4P,UAAA1G,EAAA7G,GACA,OAAArC,KAAAyG,SAAApE,IAAApC,GAAA2C,GAIAqF,YAAA,SAAAiB,EAAA7G,EAAAO,GACA,IAAA3C,EAAAgF,MAAAuM,QAAAtI,GAAAA,EAAAjJ,OAAAD,KAAA4P,UAAA1G,EAAA7G,GACA,OAAArC,KAAAyG,SAAApE,IAAApC,GAAA2C,EAAA,IAAA3C,GAAA2C,EAAA,IAIAwF,IAAA,SAAAc,EAAA7G,EAAAO,GACA,OAAA5C,KAAAyG,SAAApE,IAAAO,GAAAsG,GAIAf,IAAA,SAAAe,EAAA7G,EAAAO,GACA,OAAA5C,KAAAyG,SAAApE,IAAA6G,GAAAtG,GAIAsF,MAAA,SAAAgB,EAAA7G,EAAAO,GACA,OAAA5C,KAAAyG,SAAApE,IAAA6G,GAAAtG,EAAA,IAAAsG,GAAAtG,EAAA,IAIAyF,KAAA,SAAAa,EAAA7G,EAAAO,GAMA,SAAAiP,EAAAC,GACA,IAAArC,GAAA,GAAAqC,GAAArC,MAAA,iBACA,OAAAA,GAKAA,EAAA,GAAAA,EAAA,GAAAxP,OAJA,EAMA,SAAA8R,EAAAD,GACA,OAAAE,KAAAC,MAAAH,EAAAE,KAAAE,IAAA,GAAAC,IAfA,IAkBAA,EAlBAhL,EAAAxH,EAAA0C,GAAAjC,KAAA,QACAgS,EAAA,gCAAAjL,EAAA,qBAEAkL,EAAA,IAAAhN,OAAA,MAAA8B,EAAA,OAcAlF,GAAA,EAKA,GAlBAkF,IAAAkL,EAAAlE,KAFA,CAAA,OAAA,SAAA,SAEAhF,QAmBA,MAAA,IAAAmJ,MAAAF,GAUA,OAPAD,EAAAN,EAAAjP,IAGAiP,EAAA3I,GAAAiJ,GAAAJ,EAAA7I,GAAA6I,EAAAnP,IAAA,KACAX,GAAA,GAGAjC,KAAAyG,SAAApE,IAAAJ,GAIA6F,QAAA,SAAAoB,EAAA7G,EAAAO,GAGA,IAAA2P,EAAA5S,EAAAiD,GAMA,OALA5C,KAAAK,SAAAiG,YAAAiM,EAAAhH,IAAA,0BAAAtL,QACAsS,EAAAlL,SAAA,yBAAA9G,GAAA,wBAAA,WACAZ,EAAA0C,GAAAJ,UAGAiH,IAAAqJ,EAAAnR,OAIAsC,OAAA,SAAAwF,EAAA7G,EAAAO,EAAAQ,GACA,GAAApD,KAAAyG,SAAApE,GACA,MAAA,sBAGAe,EAAA,iBAAAA,GAAAA,GAAA,SAEA,IACAlD,EAAAC,EAAAqS,EADAC,EAAAzS,KAAAuQ,cAAAlO,EAAAe,GAWA,OARApD,KAAAK,SAAA4C,SAAAZ,EAAAlB,QACAnB,KAAAK,SAAA4C,SAAAZ,EAAAlB,MAAA,IAEAsR,EAAAC,gBAAAD,EAAAC,iBAAA1S,KAAAK,SAAA4C,SAAAZ,EAAAlB,MAAAiC,GACApD,KAAAK,SAAA4C,SAAAZ,EAAAlB,MAAAiC,GAAAqP,EAAA9H,QAEA/H,EAAA,iBAAAA,EAAA,CAAA6E,IAAA7E,GAAAA,EACA4P,EAAA7S,EAAAiD,MAAAjD,EAAAC,OAAA,CAAAO,KAAA+I,GAAAtG,EAAAzC,OACA,OAAAsS,EAAAxQ,OAAAwQ,EAAAjC,MAAAgC,EACAC,EAAAxQ,OAGAwQ,EAAAjC,IAAAgC,EACAC,EAAAxQ,MAAA,MACA/B,EAAAF,MACAmQ,aAAA9N,IACAlC,EAAA,IACAkC,EAAAlB,MAAA+H,EACAvJ,EAAAgT,KAAAhT,EAAAC,QAAA,EAAA,CACAgT,KAAA,QACAvC,KAAArQ,KAAAkQ,gBAAA7N,GACAwQ,SAAA,OACA1S,KAAAA,EACA2S,QAAA5S,EAAAoB,YACAkN,QAAA,SAAAuE,GACA,IACAtI,EAAAE,EAAAnE,EADAvE,GAAA,IAAA8Q,GAAA,SAAAA,EAGA7S,EAAAG,SAAA4C,SAAAZ,EAAAlB,MAAAiC,GAAAqP,EAAAC,gBACAzQ,GACAuE,EAAAtG,EAAAgB,cACAhB,EAAAsK,OAAAtK,EAAAmG,UAAAhE,GACAnC,EAAAgB,cAAAsF,EACAtG,EAAA0K,YAAAN,KAAAjI,GACAnC,EAAA6G,QAAA1E,EAAAlB,OAAA,EACAjB,EAAAsJ,eAEAiB,EAAA,GACAE,EAAAoI,GAAA7S,EAAA8N,eAAA3L,EAAA,CAAAe,OAAAA,EAAA6J,WAAA/D,IACAuB,EAAApI,EAAAlB,MAAAsR,EAAA9H,QAAAA,EACAzK,EAAA6G,QAAA1E,EAAAlB,OAAA,EACAjB,EAAAsJ,WAAAiB,IAEAgI,EAAAxQ,MAAAA,EACA/B,EAAAkQ,YAAA/N,EAAAJ,KAEAW,IACA,eAWA,IACA+P,EADAK,EAAA,GAmCA,OA/BArT,EAAAsT,cACAtT,EAAAsT,cAAA,SAAA5S,EAAA6S,EAAAC,GACA,IAAA9C,EAAAhQ,EAAAgQ,KACA,UAAAhQ,EAAAuS,OACAjT,EAAA2Q,UAAAD,GACA2C,EAAA3C,GAAA8C,MAMAR,EAAAhT,EAAAgT,KACAhT,EAAAgT,KAAA,SAAAtS,GACA,IAAAuS,GAAA,SAAAvS,EAAAA,EAAAV,EAAAyT,cAAAR,KACAvC,GAAA,SAAAhQ,EAAAA,EAAAV,EAAAyT,cAAA/C,KACA,MAAA,UAAAuC,GACAjT,EAAA2Q,UAAAD,GACA2C,EAAA3C,GAAAsC,EAAA5N,MAAA/E,KAAA2E,WACAqO,EAAA3C,IAEAsC,EAAA5N,MAAA/E,KAAA2E,aAKAhF,EAAA2Q,UAAA,SAAAD,GACA2C,EAAA3C,KACA2C,EAAA3C,GAAAgD,eACAL,EAAA3C,KAGA1Q,ICxpDA,SAAA2T,EAAAlU,GAEA,mBAAAC,QAAAA,OAAAC,IAEAD,OAAA,GAAAD,GAGA,iBAAAG,QAAAA,OAAAC,QAEAD,OAAAC,QAAAJ,IAEAkU,EAAAC,cAAAnU,IAXA,CAcA,oBAAAoU,KAAAA,KAAAxT,KAAA,WACA,IAIAyT,EAAA,CACAC,IAAA,MACAC,KAAA,KACArP,SAAA,CACAsP,aAAA,CACAC,KAAA,CAAA,SAAA,SAAA,UAAA,YAAA,WAAA,SAAA,YACAC,UAAA,CAAA,MAAA,MAAA,MAAA,MAAA,MAAA,MAAA,OACAC,OAAA,CACA,UAAA,WAAA,QAAA,QAAA,MAAA,OAAA,OACA,SAAA,YAAA,UAAA,WAAA,YAEAC,YAAA,CAAA,MAAA,MAAA,MAAA,MAAA,MAAA,MAAA,MAAA,MAAA,MAAA,MAAA,MAAA,OACAC,SAAA,CAAA,KAAA,MACAC,QAAA,SAAAtM,GACA,IAAAxC,EAAAwC,EAAA,GAAAuM,EAAA,CAAAC,EAAA,KAAAC,EAAA,KAAAC,EAAA,MACA,OAAA,IAAAtC,KAAAuC,MAAA3M,EAAA,IAAA,KAAAuM,EAAA/O,GAAA+O,EAAA/O,GAAA,OAGAoP,WAAA,eACAC,WAAA,2CACAC,SAAA,qBACAC,QAAA,8JACAC,OAAA,eAEAC,OAAA,SAAAjR,EAAAkR,GACA,OAAAC,SAAAnR,EAAAkR,GAAA,KAEAE,QAAA,SAAAC,EAAAC,GACA,MAAA,iBAAA,GAAA,iBAAA,GAAAD,EAAAtH,gBAAAuH,EAAAvH,eAEAwH,KAAA,SAAAjM,EAAAjJ,EAAAmV,GACA,IAAAhU,EAAA8H,EAAA0I,WAEA,OADAwD,EAAAA,GAAA,IACAhU,EAAAnB,OAAAA,EAAAwT,EAAA0B,KAAAC,EAAAhU,EAAAnB,GAAAmB,GAEAiU,MAAA,SAAAC,GACA,IAAAnQ,EAAAkG,EAEA,IADAiK,EAAAA,GAAA,GACAnQ,EAAA,EAAAA,EAAAR,UAAA1E,OAAAkF,IAEA,GADAkG,EAAA1G,UAAAQ,GAIA,IAAA,IAAA8D,KAAAoC,EACAA,EAAAkK,eAAAtM,KACA,iBAAAoC,EAAApC,GACAwK,EAAA4B,MAAAC,EAAArM,GAAAoC,EAAApC,IAEAqM,EAAArM,GAAAoC,EAAApC,IAKA,OAAAqM,GAEAE,SAAA,SAAApU,EAAAqU,GACA,IAAA,IAAAtQ,EAAA,EAAAA,EAAAsQ,EAAAxV,OAAAkF,IACA,GAAAsQ,EAAAtQ,GAAAwI,gBAAAvM,EAAAuM,cACA,OAAAxI,EAGA,OAAA,IASAoO,EAAA,SAAAxT,GACA,IAAAyT,EAAAxT,KAAA0V,EAAAjC,EAAA4B,MAAA5B,EAAAnP,SAAAvE,GACAyT,EAAAI,aAAA8B,EAAA9B,aACAJ,EAAAgB,WAAAkB,EAAAlB,WACAhB,EAAAiB,WAAAiB,EAAAjB,WACAjB,EAAAkB,SAAAgB,EAAAhB,SACAlB,EAAAmB,QAAAe,EAAAf,QACAnB,EAAAoB,OAAAc,EAAAd,QAgiBA,OA1hBArB,EAAAhL,UAAA,CACAvD,YAAAuO,EACAoC,SAAA,SAAAvU,GACA,IACA+D,EAAAsO,EAAA+B,SAAApU,EADApB,KACA4T,aAAAI,aAAA,EAIA,OAHA,IAAA7O,IACAA,EAAAsO,EAAA+B,SAAApU,EAHApB,KAGA4T,aAAAG,QAAA,GAEA5O,GAEAyQ,UAAA,SAAAC,EAAAC,GACA,IAAAC,EAAAC,EAAA7Q,EAAA8Q,EAAAC,EACAC,EAAAC,EAAAC,EAAAC,EAAAC,EADAC,GAAA,EAAAC,GAAA,EACAC,EADA1W,KACA4T,aACA0B,EAAA,CAAA5N,KAAA,KAAAiP,KAAA,KAAAC,MAAA,KAAAC,IAAA,KAAAC,KAAA,EAAA1O,IAAA,EAAA2O,IAAA,GACA,IAAAlB,EACA,OAAA,KAEA,GAAAA,aAAAlE,KACA,OAAAkE,EAEA,GAAA,MAAAC,EAEA,OADA3Q,EAAAsO,EAAAoB,OAAAgB,IACA,IAAAlE,KAAA,IAAAxM,GAAA0Q,EAEA,cAAAA,GACA,IAAA,SACA,OAAA,IAAAlE,KAAAkE,GACA,IAAA,SACA,MACA,QACA,OAAA,KAGA,KADAE,EAAAD,EAAArG,MArBAzP,KAqBAyU,cACA,IAAAsB,EAAA9V,OACA,MAAA,IAAAqS,MAAA,mCAEA,IAAAnN,EAAA4Q,EAAA9V,OAAA,EAAA,GAAAkF,EAAAA,IACA,MAAA4Q,EAAA5Q,IACA4Q,EAAAiB,OAAA7R,EAAA,GAIA,IADA6Q,EAAAH,EAAAhS,QA9BA7D,KA8BAwU,WAAA,MAAAtR,MAAA,MACAiC,EAAA,EAAAA,EAAA6Q,EAAA/V,OAAAkF,IAGA,OAFA8Q,EAAAD,EAAA7Q,GACA+Q,EAAAzC,EAAAoB,OAAAoB,GACAF,EAAA5Q,IACA,IAAA,IACA,IAAA,IACA,IAAA+Q,EAIA,OAAA,KAHAI,EAAAL,EAAAhW,OACAqV,EAAAqB,KAAA,IAAAL,EAAA7C,EAAAoB,QAAAqB,EAAA,GAAA,KAAA,MAAAD,GAAAC,EAIAM,GAAA,EACA,MACA,IAAA,IACA,IAAA,IACA,IAAA,IACA,IAAA,IACA,GAAAtF,MAAAgF,GAAA,CAEA,KAAA,GADAC,EAlDAnW,KAkDA2V,SAAAM,KAIA,OAAA,KAFAX,EAAAsB,MAAAT,MAIA,CACA,KAAA,GAAAD,GAAAA,GAAA,IAGA,OAAA,KAFAZ,EAAAsB,MAAAV,EAKAM,GAAA,EACA,MACA,IAAA,IACA,IAAA,IACA,KAAA,GAAAN,GAAAA,GAAA,IAGA,OAAA,KAFAZ,EAAAuB,IAAAX,EAIAM,GAAA,EACA,MACA,IAAA,IACA,IAAA,IAIA,GADAD,EAAAP,EAFAI,GAAA,EAAAL,EAAAkB,QAAA,KAAAlB,EAAAkB,QAAA,MACA,EAAAlB,EAAAkB,QAAA,KAAAlB,EAAAkB,QAAA,MAAA,IAEA,IAAAb,EACAC,EAAA5C,EAAAuB,QAAAuB,EAAAG,EAAAzC,SAAA,IAAA,EACAR,EAAAuB,QAAAuB,EAAAG,EAAAzC,SAAA,IAAA,IAAA,EACA,GAAAiC,GAAAA,GAAA,KAAA,GAAAG,EACAf,EAAAwB,KAAAZ,EAAA,IAAA,EAAAG,EAAAH,EAAAG,EAEA,GAAAH,GAAAA,GAAA,KACAZ,EAAAwB,KAAAZ,OAGA,CACA,KAAA,GAAAA,GAAAA,GAAA,IAGA,OAAA,KAFAZ,EAAAwB,KAAAZ,EAKAO,GAAA,EACA,MACA,IAAA,IACA,IAAA,IACA,KAAA,GAAAP,GAAAA,GAAA,IAGA,OAAA,KAFAZ,EAAAwB,KAAAZ,EAIAO,GAAA,EACA,MACA,IAAA,IACA,KAAA,GAAAP,GAAAA,GAAA,IAGA,OAAA,KAFAZ,EAAAlN,IAAA8N,EAIAO,GAAA,EACA,MACA,IAAA,IACA,KAAA,GAAAP,GAAAA,GAAA,IAGA,OAAA,KAFAZ,EAAAyB,IAAAb,EAIAO,GAAA,EAIA,IAAA,IAAAD,EAAA,CACA,IAAAU,EAAA5B,EAAAqB,MAAA,EAAAQ,EAAA7B,EAAAsB,MAAAtB,EAAAsB,MAAA,EAAA,EAAAQ,EAAA9B,EAAAuB,KAAA,EACAvB,EAAA5N,KAAA,IAAAiK,KAAAuF,EAAAC,EAAAC,EAAA9B,EAAAwB,KAAAxB,EAAAlN,IAAAkN,EAAAyB,IAAA,OACA,CACA,IAAA,IAAAN,EACA,OAAA,KAEAnB,EAAA5N,KAAA,IAAAiK,KAAA,EAAA,EAAA,EAAA2D,EAAAwB,KAAAxB,EAAAlN,IAAAkN,EAAAyB,IAAA,GAEA,OAAAzB,EAAA5N,MAEA2P,UAAA,SAAAC,EAAAxB,GACA,GAAA,iBAAAwB,EACA,OAAAA,EAEA,IAAAhB,EACAiB,EAAApS,EAAAC,EAAAoS,EAAAC,EADAC,EAAAJ,EAAAzT,QAAA7D,KAAAwU,WAAA,MAAAtR,MAAA,MACA6S,EAAAD,EAAArG,MADAzP,KACAyU,YAAAoB,EAAA,IAAAlE,KAAAgG,EAAA,EAEA,IAHA,WAGAxJ,KAAA4H,EAAA,IACA,OAAAuB,EAGA,IAAAnS,EAAA,EAAAA,EAAAuS,EAAAzX,OAAAkF,IAAA,CAIA,GAHAwS,EAAA,EACAH,EAAAE,EAAAvS,GACAsS,EAAAhE,EAAAoB,OAAA2C,EAAA9K,OAAA,EAAA,IACAwE,MAAAuG,GACA,OAAA,KAEA,OAAAtS,GACA,KAAA,EACA,MAAA4Q,EAAA,IAAA,MAAAA,EAAA,GACAF,EAAA+B,SAAAH,EAAA,GAEA5B,EAAAgC,QAAAJ,GAEA,MACA,KAAA,EACA,MAAA1B,EAAA,IAAA,MAAAA,EAAA,GACAF,EAAAgC,QAAAJ,GAEA5B,EAAA+B,SAAAH,EAAA,GAEA,MACA,KAAA,EAKA,GAJAF,EAAA1B,EAAAiC,cAEAH,GADArB,EAAAkB,EAAAvX,QACA,EAAAqW,EAAA,IACAiB,EAAA9D,EAAAoB,OAAAyB,EAAA,EAAAiB,EAAA3F,WAAAlF,OAAA,EAAA,EAAA4J,GAAAkB,EAAAA,EAAA9K,OAAA,EAAA,KAEA,OAAA,KAEAmJ,EAAAkC,YAAAR,GACA,MACA,KAAA,EACA1B,EAAAmC,SAAAP,GACA,MACA,KAAA,EACA5B,EAAAoC,WAAAR,GACA,MACA,KAAA,EACA5B,EAAAqC,WAAAT,GAIA,GADArS,EAAAoS,EAAA9K,OAAAiL,IACA1X,QACAyX,EAAAV,OAAA7R,EAAA,EAAA,EAAAC,GAGA,OAAAyQ,GAEAsC,YAAA,SAAAC,EAAAvC,GACA,SAAAwC,EAAAC,EAAAC,GACA,OAAAC,EAAAF,GAAAE,EAAAF,KAAAC,EADA,IAAA/E,EAAAxT,KAAA0W,EAAAlD,EAAAI,aAAA6E,EAAA,YAGAD,EAAA,CAQAE,EAAA,WACA,OAAAjF,EAAA0B,KAAAqD,EAAAG,IAAA,IAMAC,EAAA,WACA,OAAAlC,EAAA5C,UAAA0E,EAAAK,MAMAF,EAAA,WACA,OAAA9C,EAAAiD,WAMAC,EAAA,WACA,OAAArC,EAAA7C,KAAA2E,EAAAK,MAMAG,EAAA,WACA,OAAAR,EAAAK,KAAA,GAMAA,EAAA,WACA,OAAAhD,EAAAoD,UAMAC,EAAA,WACA,IAAAhV,EAAA,IAAAyN,KAAA6G,EAAAW,IAAAX,EAAApT,IAAA,EAAAoT,EAAAG,KAAAS,EAAA,IAAAzH,KAAA6G,EAAAW,IAAA,EAAA,GACA,OAAAnH,KAAAC,OAAA/N,EAAAkV,GAAA3F,EAAAC,MAUA2F,EAAA,WACA,IAAAnV,EAAA,IAAAyN,KAAA6G,EAAAW,IAAAX,EAAApT,IAAA,EAAAoT,EAAAG,IAAAH,EAAAQ,IAAA,GAAAI,EAAA,IAAAzH,KAAAzN,EAAA4T,cAAA,EAAA,GACA,OAAArE,EAAA0B,KAAA,EAAAnD,KAAAC,OAAA/N,EAAAkV,GAAA3F,EAAAC,IAAA,GAAA,IAUA4F,EAAA,WACA,OAAA5C,EAAA3C,OAAA8B,EAAAF,aAMA9H,EAAA,WACA,OAAA4F,EAAA0B,KAAAqD,EAAApT,IAAA,IAMAmU,EAAA,WACA,OAAA7C,EAAA1C,YAAA6B,EAAAF,aAMAvQ,EAAA,WACA,OAAAyQ,EAAAF,WAAA,GAMA2C,EAAA,WACA,OAAA,IAAA3G,KAAA6G,EAAAW,IAAAX,EAAApT,IAAA,GAAA0T,WAUAU,EAAA,WACA,IAAAL,EAAAX,EAAAW,IACA,OAAAA,EAAA,GAAA,GAAAA,EAAA,KAAA,GAAAA,EAAA,KAAA,EAAA,EAAA,GAMAM,EAAA,WACA,IAAArU,EAAAoT,EAAApT,IAAAiU,EAAAb,EAAAa,IACA,OADAb,EAAAW,KACA,KAAA/T,GAAAiU,EAAA,EAAA,EAAA,IAAAjU,GAAA,EAAAiU,GAAA,EAAA,IAMAF,EAAA,WACA,OAAAtD,EAAAiC,eAMA4B,EAAA,WACA,OAAAlB,EAAAW,IAAAvH,WAAA1M,OAAA,IAUAhB,EAAA,WACA,OAAAsU,EAAAmB,IAAAhM,eAMAgM,EAAA,WACA,IAAAvU,EAAAoT,EAAAoB,IAAA,GAAA,EAAA,EACA,OAAAlD,EAAAzC,SAAA7O,IAMAyU,EAAA,WACA,IAAAC,EAAAjE,EAAAkE,cAAAtG,EAAAE,KAAAxO,EAAA,GAAA0Q,EAAAmE,gBAAAzB,EAAA1C,EAAAoE,gBACA,OAAAxG,EAAA0B,KAAAnD,KAAAuC,OAAAuF,EAAA3U,EAAAoT,EAAA9E,EAAAE,MAAA,MAAA,IAAA,IAMAuG,EAAA,WACA,OAAA1B,EAAAoB,IAAA,IAAA,IAMAA,EAAA,WACA,OAAA/D,EAAAsE,YAMAC,EAAA,WACA,OAAA3G,EAAA0B,KAAAqD,EAAA0B,IAAA,IAMAJ,EAAA,WACA,OAAArG,EAAA0B,KAAAqD,EAAAoB,IAAA,IAMAzU,EAAA,WACA,OAAAsO,EAAA0B,KAAAU,EAAAwE,aAAA,IAMA9B,EAAA,WACA,OAAA9E,EAAA0B,KAAAU,EAAAyE,aAAA,IAMAC,EAAA,WACA,OAAA9G,EAAA0B,KAAA,IAAAU,EAAA2E,kBAAA,IAUAzO,EAAA,WAEA,MADA,WAAA0O,KAAA3M,OAAA+H,IAAA,IACA,8BAMA6E,EAAA,WAGA,OAFA,IAAA/I,KAAA6G,EAAAW,IAAA,GAAAxH,KAAAgJ,IAAAnC,EAAAW,IAAA,IACA,IAAAxH,KAAA6G,EAAAW,IAAA,GAAAxH,KAAAgJ,IAAAnC,EAAAW,IAAA,GACA,EAAA,GAMAyB,EAAA,WACA,IAAAC,EAAAhF,EAAAiF,oBAAA5W,EAAA8N,KAAA+I,IAAAF,GACA,OAAA,EAAAA,EAAA,IAAA,KAAApH,EAAA0B,KAAA,IAAAnD,KAAAuC,MAAArQ,EAAA,IAAAA,EAAA,GAAA,IAMA8W,EAAA,WACA,IAAAJ,EAAApC,EAAAoC,IACA,OAAAA,EAAAlO,OAAA,EAAA,GAAA,IAAAkO,EAAAlO,OAAA,EAAA,IAMAuO,EAAA,WAEA,OADAnN,OAAA+H,GAAApG,MAAA+D,EAAAmB,UAAA,CAAA,KAAAuG,MAAArX,QAAA2P,EAAAoB,OAAA,KACA,OAMAuG,EAAA,WACA,OAAA,IAAAtF,EAAAiF,qBAUAM,EAAA,WACA,MAAA,iBAAAvX,QAAA4U,EAAAJ,IAMAgD,EAAA,WACA,MAAA,mBAAAxX,QAAA4U,EAAAJ,IAMAiD,EAAA,WACA,OAAAzF,EAAA0F,UAAA,KAAA,IAGA,OAAAlD,EAAAD,EAAAA,IAEAoD,WAAA,SAAA3F,EAAAC,GACA,IAAA3Q,EAAAC,EAAAkR,EAAA1S,EAAAwU,EAAAd,EAAA,GACA,GAAA,iBAAAzB,KACAA,EAFA7V,KAEA4V,UAAAC,EAAAC,IAEA,OAAA,KAGA,GAAAD,aAAAlE,KAAA,CAEA,IADA2E,EAAAR,EAAA7V,OACAkF,EAAA,EAAAA,EAAAmR,EAAAnR,IAEA,OADAiT,EAAAtC,EAAAtI,OAAArI,KAVA,OAWAiT,IAGA,EAAAjT,GAdA,OAcA2Q,EAAAtI,OAAArI,EAAA,GACAmS,GAAAc,GAGAxU,EAlBA5D,KAkBAmY,YAAAC,EAAAvC,GACA1Q,IAAAmR,EAAA,GAnBAtW,KAmBA0U,SAAAvG,KAAAiK,IAAA,MAAAtC,EAAAtI,OAAArI,EAAA,KACAC,EAAAqO,EAAAoB,OAAAjR,IAAA,EACAA,GArBA5D,KAqBA4T,aAAAM,QAAA9O,IAEAkS,GAAA1T,IAEA,OAAA0T,EAEA,MAAA,KAGAmE,OAAAC,OAAAnI,GACAA,IFnoBApU,kBAAA,CAEAwc,cAAA,CAAA,WAAA,aAKApX,KAAA,WAIA,IAAAS,EAAArF,EAAAE,GAAAC,SACAH,EAAAE,GAAAC,SAAA,SAAAC,GACA,IAAAoB,EAAA,0BAMA,OAJA,IADAxB,EAAAK,MAAAiM,KAAA,eAAA9K,EAAA,MACAlB,QACAN,EAAA,WAAAS,KAAA,CAAA+G,KAAA,SAAAhG,KAAAA,IAAAE,SAAArB,MAGAgF,EAAAD,MAAA/E,KAAA,CAAAD,KAIAJ,EAAAO,UAAAyQ,kBAAA,GACAhR,EAAAO,UAAAqD,eAAA,aAEA5D,EAAAO,UAAAsD,UAAAxD,KAAA4b,WACAjc,EAAAO,UAAAqI,UAAAsT,gBAAA,GAGA7b,KAAA8b,oBAGAF,WAAA,SAAAvZ,GAEA,IAAAE,EAAA,GACArC,EAAAP,EAAAQ,KAAAkC,EAAAV,KAAA,aACAoa,EAAA7b,EAAA2b,gBAGA,OAAA,IAAAxZ,EAAAlB,KAAA8V,QAAA,OAIA5U,EAAAlB,QAAA4a,IACAA,EAAA1Z,EAAAlB,MAAA,IAGAxB,EAAAyC,KAAAlC,EAAAG,SAAAkC,MAAA,SAAApB,EAAA6a,GACA,IAKAC,EAEAC,EAPA/a,KAAA4a,EAAA1Z,EAAAlB,MACAoB,EAAApD,kBAAAgd,QAAAC,WAAA7Z,EAAAwZ,EAAA1Z,EAAAlB,MAAAA,KAEA4a,EAAA1Z,EAAAlB,MAAAA,GAAA,GAEA8a,EAAA9c,kBAAAgd,QAAAE,kBAAAlb,GACAkB,EAAAlB,KAAAsO,MAAAwM,KACAC,EAAAvc,EAAAO,UAAA8C,cAAAgZ,IAAA,GACAD,EAAA1Z,EAAAlB,MAAAA,GAAA+a,EAEA3Z,EAAApD,kBAAAgd,QAAAC,WAAA7Z,EAAA2Z,QAlBA3Z,GA0BAuZ,iBAAA,WA2CA,SAAAQ,EAAApc,GACA,IAAAoc,EAAA3c,EAAAO,EAAAoB,aAAAlB,KAAA,UAKA,OAJAT,EAAAO,EAAAoB,aAAA2K,KAAA,yBAAAhM,SACAqc,EAAA3c,EAAAO,EAAAoB,aAAA2K,KAAA,yBAAA7K,OAGAkb,EAYA,SAAAC,EAAArc,EAAAmC,EAAAqC,EAAAvE,GACA,MAAA,CACAyS,KAAA,QACAvC,KAAA,WAAAhO,EAAAlB,KACA0R,SAAA,OACA1S,KAAAA,EACA2S,QAAA5S,EAAAoB,YACAmG,IAAA9H,EAAAO,EAAAoB,aAAAlB,KAAA,UACA+G,KAAAmV,EAAApc,GACAsc,WAAA,SAAArJ,GACA,IAAAsJ,EAAA/X,EA9DA,GAAA,GAAA,GA+DA,GAAA,QAAA4X,EAAApc,IAAAuc,EACA,OAAAtJ,EAAAuJ,iBAAA,eAAAD,KAeA,SAAAE,EAAAzc,EAAA0c,EAAAva,EAAAE,GACA,IAAAsa,GAAA,EACApK,EAAAvS,EAAAqQ,cAAAlO,GAwDA,OAtDA1C,EAAAyC,KAAAG,EAAA,SAAA4C,EAAAvC,GACA,IAAAka,EAAAla,EAAA,KAAA,IAAAzD,kBAAAwc,cAAA1E,QAAArU,EAAA,IACAgK,EAAAhK,EAAA,GACA+H,EAAA/H,EAAA,GAEA,OAAAka,GAAA5c,EAAAuG,SAAApE,KACAwa,EAAA,6BAIAhc,IAAA1B,kBAAA+N,QAAAN,GACAjN,EAAAyC,KAAAwa,EAAA,SAAAzZ,EAAA+F,GAuBA,IAAA,KAtBA2T,EAAA1d,kBAAA+N,QAAAN,GAAApL,KAAAtB,EAAAgJ,EAAA7G,EAAAO,EAAA,GAAA,SAAAX,GAEA,IACAuE,EAOAiE,EATAvK,EAAAG,SAAA4C,SAAAZ,EAAAlB,MAAA4b,wBAAAtK,EAAAC,gBACAzQ,GACAuE,EAAAtG,EAAAgB,cACAhB,EAAAkK,eAAA/H,GACAnC,EAAAgB,cAAAsF,EACAtG,EAAA0K,YAAAN,KAAAjI,UACAnC,EAAA6G,QAAA1E,EAAAlB,MACAjB,EAAAsJ,gBAEAiB,EAAA,IACApI,EAAAlB,MACAsR,EAAA9H,QACA,mBAAAA,EAAAA,EAAAzB,GAAAyB,EACAzK,EAAA6G,QAAA1E,EAAAlB,OAAA,EACAjB,EAAAsJ,WAAAiB,IAEAvK,EAAAsJ,WAAAtJ,EAAAoJ,UACAmJ,EAAAxQ,MAAAA,KAKA,OAAA,IAIA4a,GAAA,GAGA,IAAAA,GACA3c,EAAAG,SAAA4C,SAAAZ,EAAAlB,QACAjB,EAAAG,SAAA4C,SAAAZ,EAAAlB,MAAA,IAGAjB,EAAAG,SAAA4C,SAAAZ,EAAAlB,MAAAhC,kBAAAwL,GAEA,QAPA,KAYAkS,EAOAld,EAAAO,UAAAwR,UAAA,oBAAA,SAAAxI,EAAA7G,EAAAqC,GACA,IAAAnC,EAAA,GACAqZ,EAAA,GACAjc,EAAAyC,KAAAsC,EAAA,SAAAS,EAAAvC,GAEA,IAAAoa,GAAA,IAAApa,EAAA,GAAAqU,QAAA,KACArU,EAAA,KAAA,IAAAzD,kBAAAwc,cAAA1E,QAAArU,EAAA,IACAoa,EAAApB,EAAA9W,QAAAlC,GAAAL,EAAAuC,QAAAlC,GAEAoa,EAAApB,EAAAtR,KAAA1H,GAAAL,EAAA+H,KAAA1H,KAKA,IAAAqa,EAAAN,EAAA3c,KAAA,CAAAkJ,GAAA7G,EAAAE,GAGA2a,EAAAjY,MAAAuM,QAAAtI,GAAAA,EAAA,CAAAA,GACAiU,EAAAR,EAAA3c,KAAAkd,EAAA7a,EAAAuZ,GAEA,OAAAqB,GAAAE,GACA,IAMAxd,EAAAO,UAAAwR,UAAA,0BAAA,SAAAxI,EAAA7G,EAAAqC,GAEA,GA1JAA,EA0JAA,EAzJAoY,GAAA,EACAnd,EAAAyC,KAAAsC,EAAA,SAAAS,EAAA8H,GACA6P,EAAAA,GAAA7P,EAAA,MAGA6P,GAoJA9c,KAAAyG,SAAApE,GACA,MAAA,sBA3JA,IAAAqC,EACAoY,EA8JA5c,EAAAC,EADAsS,EAAAzS,KAAAuQ,cAAAlO,GASA,OANArC,KAAAK,SAAA4C,SAAAZ,EAAAlB,QACAnB,KAAAK,SAAA4C,SAAAZ,EAAAlB,MAAA,IAEAsR,EAAAC,gBAAA1S,KAAAK,SAAA4C,SAAAZ,EAAAlB,MAAA4b,wBACA/c,KAAAK,SAAA4C,SAAAZ,EAAAlB,MAAA4b,wBAAAtK,EAAA9H,QAEAxL,kBAAAgd,QAAAiB,YAAA3K,EAAAjC,IAAAtH,IAAAuJ,EAAAjC,MAAAtH,EACAuJ,EAAAxQ,OAGAwQ,EAAAjC,IAAAtH,GACAhJ,EAAAF,MACAmQ,aAAA9N,IAEAlC,EAAAR,EAAAO,EAAAoB,aAAA+b,kBACA/S,KAAA,CAAAnJ,KAAA,gBAAA+H,MAAA7G,EAAAlB,OACAhB,EAAAmK,KAAA,CAAAnJ,KAAA,6BAAA+H,MAAAxE,EA1LA,GAAA,GAAA,KA4LA/E,EAAAgT,KAAA4J,EAAArc,EAAAmC,EAAAqC,EAAAvE,IACAmd,OAAA,SAAAvK,EAAAwK,GACA,IAAA9S,EAAAE,EAAAnE,EAAAvE,EAEA,GAAA,UAAAsb,EACAtb,GAAA,EACA8Q,EAAA5T,kBAAAgd,QAAAqB,mBAAAzK,OACA,CAAA,GAAA,YAAAwK,EAGA,OAFAtb,GAAA,IAAA8Q,GAAA,SAAAA,EAKA7S,EAAAG,SAAA4C,SAAAZ,EAAAlB,MAAA4b,wBAAAtK,EAAAC,gBAEAzQ,GACAuE,EAAAtG,EAAAgB,cACAhB,EAAAkK,eAAA/H,GACAnC,EAAAgB,cAAAsF,EACAtG,EAAA0K,YAAAN,KAAAjI,UACAnC,EAAA6G,QAAA1E,EAAAlB,MACAjB,EAAAsJ,eAEAiB,EAAA,GACAE,EAAAoI,GAAA7S,EAAA8N,eAAA3L,EAAA,UACAoI,EAAApI,EAAAlB,MACAsR,EAAA9H,QACA,mBAAAA,EAAAA,EAAAzB,GAAAyB,EAAA,GACAzK,EAAA6G,QAAA1E,EAAAlB,OAAA,EACAjB,EAAAsJ,WAAAiB,IAEAvK,EAAAsJ,WAAAtJ,EAAAoJ,UACAmJ,EAAAxQ,MAAAA,EACA/B,EAAAkQ,YAAA/N,EAAAJ,KAGA,YACA,IAKAtC,EAAAO,UAAAwR,UAAA,+BAAA,SAAAxI,EAAA7G,EAAAqC,GAEA,IAAAxE,EAAAF,KACAyS,EAAAvS,EAAAqQ,cAAAlO,GAEAlC,EAAAR,EAAAO,EAAAoB,aAAA+b,iBAIA,OAHAld,EAAAmK,KAAA,CAAAnJ,KAAA,4BAAA+H,MAAA,IAGAuU,KAAAC,UAAAjL,EAAAjC,OAAAiN,KAAAC,UAAAvd,IACAsS,EAAAxQ,OACA/B,EAAAsJ,WAAAiJ,EAAAhI,QAAA,IAGAgI,EAAAxQ,QAGAwQ,EAAAjC,IAAArQ,EACAH,KAAAmQ,aAAA9N,GAEA1C,EAAAgT,KAAA4J,EAAArc,EAAAmC,EAAAqC,EAAAvE,IACAmd,OAAA,SAAAvK,EAAAwK,GACA,IAAA9S,EAAA,GACAxI,EAAA,YAAAsb,KAAA,IAAAxK,GAAA,SAAAA,GAEA9Q,GACA/B,EAAAkM,iBACAlM,EAAAsK,OAAAtK,EAAAmG,UAAAhE,KAEA1C,EAAAyC,KAAA2Q,EAAA,SAAA4K,EAAAC,GACA,IAAAlY,EAAAvG,kBAAAgd,QAAA/U,WAAAlH,EAAAyd,GAAA,GACAjY,IACA+E,EAAA/E,EAAAvE,MAAAhC,kBAAAgd,QAAA0B,OAAAD,EAAA,IAAA,OAMAje,EAAAme,cAAArT,KACAxI,GAAA,IAIAwQ,EAAAxQ,MAAAA,EACAwQ,EAAAhI,OAAAA,EACAvK,EAAAsJ,WAAAiB,GACAvK,EAAAkQ,YAAA/N,EAAAJ,KAGA,YACA,MAIAtC,EAAA,WACAR,kBAAAoF,SG7XA,wBAEA,IAAAwZ,EAAA,CAEAC,iDAIA,SAAAze,GAIAA,EAAAC,QAAA,SAAAye,GAQA,IAKAxI,EALAyI,EAAA,GACAC,EAAAxZ,UAAA1E,OACAme,EAAA,GACAjZ,EAAA,EACAkZ,EAAA,GAGAC,EAAA,IAAAF,KAAAH,EACA,IAAA9Y,EAAA,EAAAA,EAAAgZ,EAAAhZ,IAAA,CAEA,IAAAkZ,KADA5I,EAAA9Q,UAAAQ,GAEA,GAAAsQ,EAAA4I,KAAAJ,EAAAG,GAEA,SAAAE,EAGAJ,EAAAE,GAAAH,EAAAG,GAIA,OAAAF,IAMAK,mDAIA,SAAAhf,GAIA,IAAAif,EAAA,UACAC,EAAA,UACAC,EAAA,gCACAC,EAAA,sBACAC,EAAA,qBACAC,EAAA,mBACAC,EAAA,gBACAC,EAAA,eACAC,EAAA,mBACAC,EAAA,kBACAC,EAAA,kBAEAC,EAAA,2DACAC,EAAA,8BACAC,EAAAF,EAAA,IAAAC,EAAA,aAEAE,EAAA,qFACAC,EAAA,0BACAC,EAAA,8EAAAH,EAEAI,EAAA,eAEAC,EAAA,aAEAC,EAAA,mBACAC,EAAA,kBACAC,EAAA,0CACAC,EAAA,4BAEAC,EAAA,wFACAC,EAAA,oDAEAC,EAAA,IAAAF,EAAA,IAAAC,EAAA,iCAEAE,EAAA,kBAAAvB,EAAA,KAAAG,EAAA,KAKAqB,EAAAF,EAAA,YAAAJ,EAAA,kBAEA,SAAAO,EAAAtJ,EAAAuJ,GAGA,OAFAA,EAAAA,GAAAA,EAAA1S,eAGA,IAAA,IACAmJ,GAAA,KAAAA,GAAA,GAAA,EACA,MACA,IAAA,IACAA,GAAA,KAAAA,EAAA,GAAA,EAIA,OAAAA,EAGA,SAAAwJ,EAAAC,GACA,IAAA5J,GAAA4J,EAMA,OAJAA,EAAAtgB,OAAA,GAAA0W,EAAA,MACAA,GAAAA,EAAA,GAAA,IAAA,MAGAA,EAGA,SAAA6J,EAAAC,GACA,MAAA,CACAC,IAAA,EACAC,QAAA,EACAxb,EAAA,EACAyb,IAAA,EACAC,SAAA,EACAC,GAAA,EACAC,IAAA,EACAC,MAAA,EACAC,IAAA,EACAC,IAAA,EACAC,MAAA,EACAC,GAAA,EACAC,IAAA,EACAlX,EAAA,EACAmX,IAAA,EACAC,KAAA,EACAC,GAAA,EACAC,IAAA,EACAC,KAAA,EACAC,IAAA,EACAC,IAAA,EACAC,OAAA,EACAC,KAAA,EACAC,IAAA,EACAC,KAAA,EACAC,UAAA,EACAC,GAAA,EACAC,IAAA,EACAC,QAAA,EACAC,EAAA,EACAC,IAAA,GACAC,SAAA,GACAC,GAAA,GACAC,IAAA,GACAC,SAAA,GACAC,IAAA,IACAlC,EAAA9S,eAGA,SAAAiV,EAAAC,EAAA,GACA,IAAAC,EAAA,EAAAne,UAAA1E,aAAAY,IADA,EAAA,EACA,EAmBA,MAjBA,CACAkiB,IAAA,EACAC,OAAA,EACAC,IAAA,EACAC,QAAA,EACAC,IAAA,EACAC,UAAA,EACAC,IAAA,EACAC,SAAA,EACAC,IAAA,EACAC,OAAA,EACAC,IAAA,EACAC,SAAA,EACAC,IAAA,EACAC,OAAA,GAGAf,EAAAlV,gBAAAmV,EAoCA,SAAAe,EAAAC,EAAAC,GAIA,KAFAD,EAAAA,GAAAA,EAAArU,MADA,sCAIA,OAAAsU,EAGA,IAAAC,EAAA,MAAAF,EAAA,IAAA,EAAA,EACAG,GAAAH,EAAA,GACAI,GAAAJ,EAAA,GAQA,OANAA,EAAA,IAAAA,EAAA,KACAI,EAAAlS,KAAAuC,MAAA0P,EAAA,KACAA,EAAAjS,KAAAuC,MAAA0P,EAAA,MAIAD,GAAA,GAAAC,EAAAC,GAAA,GAIA,IAAAC,EAAA,CACAC,KAAA,MACAC,KAAA,MACAC,MAAA,KACAC,KAAA,MACAC,KAAA,MACAC,KAAA,KACAC,MAAA,MACAC,MAAA,KACAC,MAAA,MACAC,MAAA,MACAC,KAAA,MACAC,KAAA,MACAC,KAAA,MACAC,KAAA,MACAC,KAAA,MACAC,KAAA,MACAC,KAAA,KACAC,KAAA,KACAC,KAAA,MACAC,IAAA,KACAC,KAAA,MACAC,IAAA,KACAC,MAAA,MACAC,KAAA,KACAC,KAAA,MACAC,KAAA,KACAC,IAAA,KACAC,KAAA,MACAC,KAAA,KACAC,KAAA,MACAC,KAAA,KACAC,KAAA,KACAC,KAAA,KACAC,IAAA,MACAC,MAAA,MACAC,KAAA,MACAC,KAAA,MACAC,IAAA,KACAC,KAAA,MACAC,KAAA,MACAC,KAAA,KACAC,KAAA,MACAC,MAAA,MACAtO,KAAA,KACAuO,IAAA,MACAC,IAAA,EACAC,IAAA,KACAC,KAAA,MACAC,KAAA,MACAC,IAAA,MACAC,KAAA,MACAC,KAAA,MACAC,KAAA,KACAC,KAAA,MACAC,KAAA,MACAC,IAAA,MACAC,IAAA,MACAC,IAAA,KACAC,IAAA,KACAC,IAAA,KACAC,IAAA,MACAC,IAAA,KACAC,IAAA,KACAC,IAAA,MACAC,IAAA,KACAC,MAAA,KACAC,KAAA,MACAC,KAAA,MACAC,KAAA,KACAC,IAAA,KACAC,IAAA,KACAC,KAAA,MACAC,IAAA,MACAC,IAAA,MACAC,KAAA,MACAC,KAAA,MACAC,MAAA,KACAC,KAAA,KACAC,KAAA,IACAC,KAAA,MACAC,KAAA,IACAC,KAAA,MACAC,KAAA,MACAC,KAAA,MACAC,MAAA,MACAC,KAAA,MACAC,KAAA,MACAC,IAAA,KACAC,KAAA,MACAC,KAAA,MACAC,MAAA,MACAC,KAAA,MACAC,KAAA,MACAC,KAAA,MACAC,KAAA,MACAC,IAAA,KACAC,KAAA,KACAC,MAAA,MACAC,MAAA,MACAC,KAAA,MACAC,KAAA,MACAC,KAAA,MACAC,IAAA,MACAC,IAAA,EACAC,IAAA,EACAC,KAAA,KACAC,IAAA,KACAC,KAAA,KACAC,KAAA,KACAC,IAAA,EACAC,IAAA,MACAC,KAAA,MACAC,IAAA,MACAC,IAAA,KACAC,MAAA,MACAC,KAAA,MACAC,KAAA,MACAC,KAAA,MACAC,KAAA,MACAvnB,EAAA,KACAkV,EAAA,KACAgC,EAAA,MACA1C,EAAA,MACA3M,EAAA,KACA2f,EAAA,MACAxR,EAAA,MACAE,EAAA,MACAjV,EAAA,MACAkZ,EAAA,KACAtF,EAAA,MACAlL,EAAA,MACAzI,GAAA,KACAqU,GAAA,KACAkS,GAAA,MACAC,GAAA,MACAvQ,GAAA,KACA9C,GAAA,MACAD,GAAA,MACAiC,GAAA,MACApQ,GAAA,MACA0O,GAAA,KACAwJ,GAAA,MACA3I,GAAA,MACAR,EAAA,GAGA2S,EAAA,CACAC,UAAA,CACAC,MAAA,cACA5qB,KAAA,YACA6qB,SAAA,WAEA,QADAhsB,KAAAisB,GACAjsB,KAAAksB,cAIAC,IAAA,CACAJ,MAAA,QACA5qB,KAAA,OAIAirB,KAAA,CACAL,MAAA,SACA5qB,KAAA,OACA6qB,SAAA,WACA,OAAAhsB,KAAAksB,aAAAlsB,KAAAqsB,KAAA,GAAA,EAAA,EAAA,KAIAC,gBAAA,CACAP,MAAA,qBACA5qB,KAAA,mBACA6qB,SAAA,WACA,OAAAhsB,KAAAksB,cAIAK,SAAA,CACAR,MAAA,aACA5qB,KAAA,WACA6qB,SAAA,WAEA,OADAhsB,KAAAisB,IAAA,EACAjsB,KAAAksB,cAIAM,UAAA,CACAT,MAAA,aACA5qB,KAAA,YACA6qB,SAAA,SAAAvc,EAAA+c,GAOA,OANAxsB,KAAA6J,KAAA2iB,EACAxsB,KAAA0Z,EAAA,KACA1Z,KAAA6N,EAAA,EACA7N,KAAA0Y,EAAA,EACA1Y,KAAAysB,MAAA,EAEAzsB,KAAAksB,aAAAlsB,KAAA0sB,KAAA,KAIAC,eAAA,CACAZ,MAAA,wBACA5qB,KAAA,yBACA6qB,SAAA,SAAAvc,EAAAoH,GACA,UAAAA,EAAAlJ,cACA3N,KAAA4sB,sBAAA,EAEA5sB,KAAA4sB,uBAAA,IAKAC,cAAA,CACAd,MAAA1mB,OAAA,oBAAAsZ,EAAAF,EAAAC,EAAA,IAAA,KACAvd,KAAA,mBACA6qB,SAAA,SAAAvc,EAAAqd,EAAA7I,EAAA5D,GACA,IACAvJ,GAAAmN,EACA8I,EAAA,GASA,MAXA,SAAAD,EAAAnf,kBAKAmJ,EACAiW,EAAA,IAGAjW,EAAAsJ,EAAAtJ,EAAAuJ,GAEArgB,KAAAksB,aAAAlsB,KAAAqsB,KAAAvV,EAAAiW,EAAA,EAAA,KAIAC,UAAA,CACAjB,MAAA1mB,OAAA,KAAAia,EAAA,IAAAC,EAAA,IAAAf,EAAA,IAAAW,EAAA,IAAAC,EAAA,IAAAZ,EAAA,KAAA,KACArd,KAAA,aAIA8rB,UAAA,CACAlB,MAAA1mB,OAAA,IAAAwZ,EAAA,IAAAE,EAAA,IAAAE,EAAA,eAAAP,EAAA,KACAvd,KAAA,YACA6qB,SAAA,SAAAvc,EAAAqH,EAAAiW,EAAAG,EAAAC,EAAA9M,GACA,OAAArgB,KAAAqsB,KAAAjM,GAAAtJ,EAAAuJ,IAAA0M,GAAAG,GAAAC,EAAAzgB,OAAA,EAAA,MAIA0gB,WAAA,CACArB,MAAA,gCACA5qB,KAAA,QACA6qB,SAAA,SAAAvc,EAAAoH,EAAAwW,EAAA1W,GACA,IAAAC,EAAA,CACA0W,IAAA,EACAC,IAAA,EACAC,IAAA,EACAC,IAAA,EACAC,IAAA,EACAC,IAAA,EACAC,IAAA,EACAC,IAAA,EACAC,IAAA,EACAC,IAAA,EACAC,IAAA,GACAC,IAAA,IACAZ,EAAA5f,eACA,OAAAzN,KAAAkuB,IAAA,IAAAnZ,SAAA4B,EAAA,IAAAC,EAAA7B,SAAA8B,EAAA,OAIAsX,WAAA,CACApC,MAAA1mB,OAAA,IAAAwZ,EAAA,OAAAC,EAAA,OAAAG,EAAAR,EAAAC,EAAA,KACAvd,KAAA,aACA6qB,SAAA,SAAAvc,EAAAqH,EAAAiW,EAAAG,EAAA7M,GACA,OAAArgB,KAAAqsB,KAAAjM,GAAAtJ,EAAAuJ,IAAA0M,GAAAG,EAAA,KAIAkB,YAAA,CACArC,MAAA1mB,OAAA,IAAAwZ,EAAA,OAAAE,EAAAN,EAAAC,EAAA,KACAvd,KAAA,cACA6qB,SAAA,SAAAvc,EAAAqH,EAAAiW,EAAA1M,GACA,OAAArgB,KAAAqsB,KAAAjM,GAAAtJ,EAAAuJ,IAAA0M,EAAA,EAAA,KAIAsB,WAAA,CACAtC,MAAA1mB,OAAA,IAAAwZ,EAAAJ,EAAAC,EAAA,KACAvd,KAAA,aACA6qB,SAAA,SAAAvc,EAAAqH,EAAAuJ,GACA,OAAArgB,KAAAqsB,KAAAjM,GAAAtJ,EAAAuJ,GAAA,EAAA,EAAA,KAIAiO,KAAA,CACAvC,MAAA1mB,OAAA,IAAAqa,EAAA,IAAAE,EAAA,IAAAE,EAAA,IAAAlB,EAAA,IAAAG,EAAA,IAAAE,EAAAC,EAAAgB,EAAA,IAAA,KACA/e,KAAA,OACA6qB,SAAA,SAAAvc,EAAAkH,EAAAC,EAAAC,EAAAC,EAAAiW,EAAAG,EAAAC,EAAAoB,GACA,OAAAvuB,KAAAkuB,KAAAvX,EAAAC,EAAA,GAAAC,IAAA7W,KAAAqsB,MAAAvV,GAAAiW,GAAAG,GAAAC,EAAAzgB,OAAA,EAAA,KAAA1M,KAAA0sB,KAAA7I,EAAA0K,MAIAC,KAAA,CACAzC,MAAA1mB,OAAA,IAAAqa,EAAA,IAAAC,EAAA,IAAAE,EAAA,IAAAlB,EAAA,IAAAG,EAAA,IAAAE,GACA7d,KAAA,OACA6qB,SAAA,SAAAvc,EAAAkH,EAAAC,EAAAC,EAAAC,EAAAiW,EAAAG,GACA,OAAAltB,KAAAkuB,KAAAvX,EAAAC,EAAA,GAAAC,IAAA7W,KAAAqsB,MAAAvV,GAAAiW,GAAAG,EAAA,KAIAuB,KAAA,CACA1C,MAAA1mB,OAAA,IAAAqa,EAAA,IAAAE,EAAA,IAAAE,EAAA,IAAAlB,EAAA,IAAAG,EAAA,IAAAE,EAAA,KACA9d,KAAA,OACA6qB,SAAA,SAAAvc,EAAAkH,EAAAC,EAAAC,EAAAC,EAAAiW,EAAAG,GACA,OAAAltB,KAAAkuB,KAAAvX,EAAAC,EAAA,GAAAC,IAAA7W,KAAAqsB,MAAAvV,GAAAiW,GAAAG,EAAA,KAIAwB,OAAA,CACA3C,MAAA1mB,OAAA,IAAAqa,EAAAE,EAAAE,EAAA,IAAAnB,EAAA,IAAAI,EAAA,IAAAE,GACA9d,KAAA,SACA6qB,SAAA,SAAAvc,EAAAkH,EAAAC,EAAAC,EAAAC,EAAAiW,EAAAG,GACA,OAAAltB,KAAAkuB,KAAAvX,EAAAC,EAAA,GAAAC,IAAA7W,KAAAqsB,MAAAvV,GAAAiW,GAAAG,EAAA,KAIAyB,cAAA,CACA5C,MAAA1mB,OAAA,IAAAqa,EAAAE,EAAAE,EAAA,OAAAnB,EAAAI,EAAAE,GACA9d,KAAA,gBACA6qB,SAAA,SAAAvc,EAAAkH,EAAAC,EAAAC,EAAAC,EAAAiW,EAAAG,GACA,OAAAltB,KAAAkuB,KAAAvX,EAAAC,EAAA,GAAAC,IAAA7W,KAAAqsB,MAAAvV,GAAAiW,GAAAG,EAAA,KAIA0B,IAAA,CACA7C,MAAA1mB,OAAA,IAAAwa,EAAA,KAAAG,EAAA,KAAAN,EAAA,IAAAd,EAAA,IAAAG,EAAA,IAAAE,EAAAT,EAAA0B,EAAA,KACA/e,KAAA,MACA6qB,SAAA,SAAAvc,EAAAoH,EAAAD,EAAAD,EAAAG,EAAAiW,EAAAG,EAAAqB,GACA,OAAAvuB,KAAAkuB,KAAAvX,EAAA6J,EAAA5J,IAAAC,IAAA7W,KAAAqsB,MAAAvV,GAAAiW,GAAAG,EAAA,IAAAltB,KAAA0sB,KAAA7I,EAAA0K,MAIAM,YAAA,CACA9C,MAAA1mB,OAAA,MAAAsZ,EAAA,OAAAG,EAAA,OAAAE,EAAAE,EAAA,KACA/d,KAAA,cACA6qB,SAAA,SAAAvc,EAAAqH,EAAAiW,EAAAG,EAAAC,GACA,OAAAntB,KAAAqsB,MAAAvV,GAAAiW,GAAAG,GAAAC,EAAAzgB,OAAA,EAAA,MAIAoiB,YAAA,CACA/C,MAAA1mB,OAAA,IAAA4a,EAAA,YAAAJ,EAAA,kBAAAJ,EAAA,KACAte,KAAA,cACA6qB,SAAA,SAAAvc,EAAAmH,EAAAC,EAAAF,GACA,OAAA3W,KAAAkuB,IAAA5N,EAAA3J,GAAA6J,EAAA5J,IAAAC,KAIAkY,aAAA,CACAhD,MAAA1mB,OAAA,IAAAwa,EAAA,UAAAF,EAAA,OAAAD,GACAve,KAAA,eACA6qB,SAAA,SAAAvc,EAAAoH,EAAAD,EAAAD,GACA,OAAA3W,KAAAkuB,KAAAvX,EAAAC,EAAA,GAAAC,KAIAmY,aAAA,CACAjD,MAAA1mB,OAAA,IAAAwa,EAAA,SAAAF,EAAA,iBACAxe,KAAA,eACA6qB,SAAA,SAAAvc,EAAAoH,EAAAD,EAAAD,GACA,OAAA3W,KAAAkuB,IAAA5N,EAAA3J,GAAAC,EAAA,GAAAC,KAIAoY,WAAA,CACAlD,MAAA1mB,OAAA,MAAAsZ,EAAA,OAAAG,EAAA,OAAAE,GACA7d,KAAA,aACA6qB,SAAA,SAAAvc,EAAAqH,EAAAiW,EAAAG,GACA,OAAAltB,KAAAqsB,MAAAvV,GAAAiW,GAAAG,EAAA,KAIAgC,YAAA,CACAnD,MAAA1mB,OAAA,IAAAqa,EAAAE,EAAAE,GACA3e,KAAA,cACA6qB,SAAA,SAAAvc,EAAAkH,EAAAC,EAAAC,GACA,OAAA7W,KAAAkuB,KAAAvX,EAAAC,EAAA,GAAAC,KAIAsY,QAAA,CACApD,MAAA1mB,OAAA,IAAAqa,EAAA,gEACAve,KAAA,UACA6qB,SAAA,SAAAvc,EAAAkH,EAAAE,GACA,OAAA7W,KAAAkuB,KAAAvX,EAAA,GAAAE,KAIAuY,YAAA,CACArD,MAAA1mB,OAAA,MAAAsZ,EAAA,OAAAG,EAAA,KACA3d,KAAA,cACA6qB,SAAA,SAAAvc,EAAAqH,EAAAiW,GACA,OAAA/sB,KAAAqsB,MAAAvV,GAAAiW,EAAA,EAAA,KAIAsC,eAAA,CACAtD,MAAA1mB,OAAA,MAAAuZ,EAAAG,EAAAE,EAAA,KACA9d,KAAA,iBACA6qB,SAAA,SAAAvc,EAAAqH,EAAAiW,EAAAG,GACA,OAAAltB,KAAAqsB,MAAAvV,GAAAiW,GAAAG,EAAA,KAIAoC,iBAAA,CAIAvD,MAAA1mB,OAAA,IAAAqa,EAAA,IAAAE,EAAA,IAAAE,EAAA,KACA3e,KAAA,mBACA6qB,SAAA,SAAAvc,EAAAkH,EAAAC,EAAAC,GACA,OAAA7W,KAAAkuB,KAAAvX,EAAAC,EAAA,GAAAC,KAIA0Y,UAAA,CACAxD,MAAA1mB,OAAA,IAAAqa,EAAA,IAAAC,EAAA,IAAAE,GACA1e,KAAA,YACA6qB,SAAA,SAAAvc,EAAAkH,EAAAC,EAAAC,GACA,OAAA7W,KAAAkuB,KAAAvX,EAAAC,EAAA,GAAAC,KAIA2Y,SAAA,CACAzD,MAAA1mB,OAAA,IAAAsa,EAAA,IAAAE,EAAA,IAAAJ,GACAte,KAAA,WACA6qB,SAAA,SAAAvc,EAAAmH,EAAAC,EAAAF,GACA,OAAA3W,KAAAkuB,IAAA5N,EAAA3J,GAAAC,EAAA,GAAAC,KAIA4Y,cAAA,CACA1D,MAAA1mB,OAAA,IAAAsa,EAAA,IAAAE,GACA1e,KAAA,gBACA6qB,SAAA,SAAAvc,EAAAmH,EAAAC,GACA,OAAA7W,KAAAkuB,IAAAluB,KAAA0Z,EAAA9C,EAAA,GAAAC,KAIA6Y,2BAAA,CAEA3D,MAAA1mB,OAAA,IAAAoa,EAAA,IAAAE,EAAA,IAAAE,GACA1e,KAAA,8BACA6qB,SAAA,SAAAvc,EAAAkH,EAAAC,EAAAC,GACA,OAAA7W,KAAAkuB,IAAA5N,EAAA3J,GAAAC,EAAA,GAAAC,KAIA8Y,aAAA,CACA5D,MAAA1mB,OAAA,oBAAAua,EAAA,IAAAE,GACA3e,KAAA,eACA6qB,SAAA,SAAAvc,EAAAkH,EAAAC,EAAAC,GACA,OAAA7W,KAAAkuB,KAAAvX,EAAAC,EAAA,GAAAC,KAIA+Y,WAAA,CACA7D,MAAA1mB,OAAA,MAAAuZ,EAAAG,EAAA,KACA5d,KAAA,aACA6qB,SAAA,SAAAvc,EAAAqH,EAAAiW,GAGA,OAAA/sB,KAAA6vB,OACA,KAAA,EACA,OAAA7vB,KAAAqsB,MAAAvV,GAAAiW,EAAA,EAAA/sB,KAAA0rB,GACA,KAAA,EAIA,OAHA1rB,KAAA0Z,EAAA,IAAA5C,IAAAiW,EACA/sB,KAAA6vB,SAEA,EACA,QACA,OAAA,KAKAC,eAAA,CACA/D,MAAA1mB,OAAA,IAAAqa,EAAA,IAAAC,GACAxe,KAAA,iBACA6qB,SAAA,SAAAvc,EAAAkH,EAAAC,GACA,OAAA5W,KAAAkuB,KAAAvX,EAAAC,EAAA,EAAA,KAIAmZ,cAAA,CAGAhE,MAAA1mB,OAAA,gCAAA2a,EAAA,KAAAF,EAAA,KACA3e,KAAA,gBACA6qB,SAAA,SAAAvc,EAAAkH,EAAAC,EAAAC,GACA,OAAA7W,KAAAkuB,IAAA5N,EAAA3J,GAAA6J,EAAA5J,IAAAC,KAIAmZ,SAAA,CACAjE,MAAA1mB,OAAA,IAAAwa,EAAA,YAAAI,EAAA,YAAAR,EAAA,KACAte,KAAA,WACA6qB,SAAA,SAAAvc,EAAAoH,EAAAD,EAAAD,GACA,OAAA3W,KAAAkuB,IAAA5N,EAAA3J,GAAA6J,EAAA5J,IAAAC,KAIAoZ,UAAA,CACAlE,MAAA1mB,OAAA,IAAA4a,EAAA,YAAAP,EAAA,KACAve,KAAA,YACA6qB,SAAA,SAAAvc,EAAAmH,EAAAD,GACA,OAAA3W,KAAAkuB,KAAAvX,EAAA6J,EAAA5J,GAAA,KAIAsZ,aAAA,CACAnE,MAAA1mB,OAAA,IAAAqa,EAAA,YAAAO,EAAA,KACA9e,KAAA,eACA6qB,SAAA,SAAAvc,EAAAkH,EAAAC,GACA,OAAA5W,KAAAkuB,KAAAvX,EAAA6J,EAAA5J,GAAA,KAIAuZ,YAAA,CACApE,MAAA1mB,OAAA,KAAA2a,EAAA,KAAAF,EAAA,IAAAL,EAAA,KACAte,KAAA,cACA6qB,SAAA,SAAAvc,EAAAmH,EAAAC,EAAAF,GACA,OAAA3W,KAAAkuB,IAAA5N,EAAA3J,GAAA6J,EAAA5J,IAAAC,KAIAuZ,WAAA,CACArE,MAAA1mB,OAAA,IAAA8a,EAAA,KACAhf,KAAA,aACA6qB,SAAA,SAAAvc,EAAAmH,EAAAC,GACA,OAAA7W,KAAAkuB,IAAAluB,KAAA0Z,EAAA8G,EAAA5J,IAAAC,KAIAwZ,cAAA,CACAtE,MAAA1mB,OAAA,IAAAwa,EAAA,YAAAI,EAAA,KACA9e,KAAA,gBACA6qB,SAAA,SAAAvc,EAAAoH,EAAAD,GACA,OAAA5W,KAAAkuB,IAAAluB,KAAA0Z,EAAA8G,EAAA5J,IAAAC,KAIAyZ,WAAA,CACAvE,MAAA1mB,OAAA,IAAAqa,EAAA,+CACAve,KAAA,uBACA6qB,SAAA,SAAAvc,EAAAkH,EAAA4Z,EAAA1Z,GAGA,GAFAA,EAAAA,GAAAA,EAAA,GAEA7W,KAAAkuB,KAAAvX,EAAA,EAAA,GACA,OAAA,EAIA,IAGA6Z,EAAA,GAAA,GAHAA,EAAA,IAAA7e,KAAA3R,KAAA0Z,EAAA1Z,KAAA6N,EAAA7N,KAAA0Y,GAAAO,UAGAuX,EAAA,EAAAA,GAEAxwB,KAAAisB,IAAAuE,EAAA,GAAAD,EAAA,GAAA1Z,IAIA4Z,aAAA,CACA1E,MAAA1mB,OAAA,KAAAia,EAAA,IAAAC,EAAA,IAAAf,EAAA,IAAAgB,EAAA,IAAA,KACAre,KAAA,eACA6qB,SAAA,SAAAvc,EAAAihB,EAAAC,GAGA,IA/lBAC,EA+lBAC,EA7lBA,CACAC,OA3BA,CACAC,MAAA,EACAte,UAAA,EACAzS,KAAA,EACAgxB,MAAA,EACAC,KAAA,EACA/D,OAAA,EACAgE,MAAA,EACAC,OAAA,EACAC,MAAA,EACAC,MAAA,EACAC,QAAA,EACAC,MAAA,EACAC,OAAA,EACAC,MAAA,EACAC,MAAA,GACAC,SAAA,GACAC,QAAA,IAOAhB,EA+lBAF,EA/lBA/iB,eAIAkkB,SARA,CACA7xB,KAAA,GAOA4wB,IAAA,GA4lBAE,EAAAD,EAAAC,OAGA,OAAAH,EAAAhjB,eACA,IAAA,MACA,IAAA,OACA,IAAA,SACA,IAAA,UACA3N,KAAA6J,IAAAinB,EACA,MACA,IAAA,MACA,IAAA,OACA,IAAA,SACA,IAAA,UACA9wB,KAAA8xB,IAAAhB,EACA,MACA,IAAA,OACA,IAAA,QACA9wB,KAAA+xB,IAAAjB,EACA,MACA,IAAA,MACA,IAAA,OACA9wB,KAAAisB,IAAA6E,EACA,MACA,IAAA,YACA,IAAA,aACA,IAAA,aACA,IAAA,cACA9wB,KAAAisB,IAAA,GAAA6E,EACA,MACA,IAAA,OACA,IAAA,QACA9wB,KAAAisB,IAAA,EAAA6E,EACA,MACA,IAAA,QACA,IAAA,SACA9wB,KAAAgyB,IAAAlB,EACA,MACA,IAAA,OACA,IAAA,QACA9wB,KAAAiyB,IAAAnB,EACA,MACA,IAAA,MACA,IAAA,SACA,IAAA,MACA,IAAA,UACA,IAAA,MACA,IAAA,YACA,IAAA,MACA,IAAA,WACA,IAAA,MACA,IAAA,SACA,IAAA,MACA,IAAA,WACA,IAAA,MACA,IAAA,SACA9wB,KAAAksB,YACAlsB,KAAAkyB,QAAAtP,EAAA+N,EAAA,GACA3wB,KAAAmyB,gBAAA,EACAnyB,KAAAisB,IAAA,GAAA,EAAA6E,EAAAA,EAAA,EAAAA,MAUAsB,SAAA,CACArG,MAAA1mB,OAAA,wBAAAoZ,EAAA,IAAAe,EAAA,SAAA,KACAre,KAAA,WACA6qB,SAAA,SAAAvc,EAAA4iB,EAAA3B,EAAAC,GACA,IAAA2B,EAAAD,EAAAxuB,QAAA,QAAA,IAAA5D,OAEA6wB,EAAAJ,EAAA1e,KAAAE,KAAA,EAAAogB,GAEA,OAAA3B,EAAAhjB,eACA,IAAA,MACA,IAAA,OACA,IAAA,SACA,IAAA,UACA3N,KAAA6J,IAAAinB,EACA,MACA,IAAA,MACA,IAAA,OACA,IAAA,SACA,IAAA,UACA9wB,KAAA8xB,IAAAhB,EACA,MACA,IAAA,OACA,IAAA,QACA9wB,KAAA+xB,IAAAjB,EACA,MACA,IAAA,MACA,IAAA,OACA9wB,KAAAisB,IAAA6E,EACA,MACA,IAAA,YACA,IAAA,aACA,IAAA,aACA,IAAA,cACA9wB,KAAAisB,IAAA,GAAA6E,EACA,MACA,IAAA,OACA,IAAA,QACA9wB,KAAAisB,IAAA,EAAA6E,EACA,MACA,IAAA,QACA,IAAA,SACA9wB,KAAAgyB,IAAAlB,EACA,MACA,IAAA,OACA,IAAA,QACA9wB,KAAAiyB,IAAAnB,EACA,MACA,IAAA,MACA,IAAA,SACA,IAAA,MACA,IAAA,UACA,IAAA,MACA,IAAA,YACA,IAAA,MACA,IAAA,WACA,IAAA,MACA,IAAA,SACA,IAAA,MACA,IAAA,WACA,IAAA,MACA,IAAA,SACA9wB,KAAAksB,YACAlsB,KAAAkyB,QAAAtP,EAAA+N,EAAA,GACA3wB,KAAAmyB,gBAAA,EACAnyB,KAAAisB,IAAA,GAAA,EAAA6E,EAAAA,EAAA,EAAAA,MAUAyB,QAAA,CACAxG,MAAA1mB,OAAA,KAAAga,EAAA,IAAA,KACAle,KAAA,UACA6qB,SAAA,SAAAvc,EAAA8iB,GACAvyB,KAAAksB,YACAlsB,KAAAkyB,QAAAtP,EAAA2P,EAAA,GAEA,IAAAvyB,KAAAmyB,kBACAnyB,KAAAmyB,gBAAA,KAKAK,iBAAA,CACAzG,MAAA1mB,OAAA,KAAAka,EAAA,IAAAf,EAAA,OAAA,KACArd,KAAA,mBACA6qB,SAAA,SAAAvc,EAAAgjB,GAGA,OAFAzyB,KAAAmyB,gBAAA,EAEAM,EAAA9kB,eACA,IAAA,OACA3N,KAAAisB,IAAA,EACA,MACA,IAAA,OACAjsB,KAAAisB,IAAA,EACA,MACA,IAAA,OACA,IAAA,WACAjsB,KAAAisB,IAAA,EAIA/a,MAAAlR,KAAAkyB,WACAlyB,KAAAkyB,QAAA,KAKAQ,qBAAA,CACA3G,MAAA1mB,OAAA,KAAA0a,EAAA,IAAAC,EAAA,IAAA,KACA7e,KAAA,wBACA6qB,SAAA,SAAAvc,EAAAmH,GACA,OAAA5W,KAAAkuB,IAAAluB,KAAA0Z,EAAA8G,EAAA5J,GAAA5W,KAAA0Y,KAIA6V,aAAA,CACAxC,MAAA1mB,OAAA,IAAA6a,EAAA,KACA/e,KAAA,eACA6qB,SAAA,SAAAuC,GACA,OAAAvuB,KAAA0sB,KAAA7I,EAAA0K,MAIAoE,OAAA,CACA5G,MAAA1mB,OAAA,4BACAlE,KAAA,SACA6qB,SAAA,SAAAvc,EAAAmjB,GACA,IAAAC,EAAA1O,EAAAyO,EAAAjlB,eAEA,OAAAuD,MAAA2hB,IAIA7yB,KAAA0sB,KAAAmG,KAIAC,IAAA,CACA/G,MAAA,QACA5qB,KAAA,MACA6qB,SAAA,WACAhsB,KAAAiyB,IAAAjyB,KAAAiyB,GACAjyB,KAAAgyB,IAAAhyB,KAAAgyB,GACAhyB,KAAAisB,IAAAjsB,KAAAisB,GACAjsB,KAAA+xB,IAAA/xB,KAAA+xB,GACA/xB,KAAA8xB,IAAA9xB,KAAA8xB,GACA9xB,KAAA6J,IAAA7J,KAAA6J,GACA7J,KAAA+yB,IAAA/yB,KAAA+yB,KAIAC,MAAA,CACAjH,MAAA1mB,OAAA,IAAAqa,GACAve,KAAA,QACA6qB,SAAA,SAAAvc,EAAAkH,GAEA,OADA3W,KAAA0Z,GAAA/C,GACA,IAIAsc,WAAA,CACAlH,MAAA,YACA5qB,KAAA,cAIA+xB,sBAAA,CACAnH,MAAA1mB,OAAA,IAAA8a,EAAA,KAAAxB,EAAA,OAAAG,EAAA,OAAAE,EAAA,KACA7d,KAAA,wBACA6qB,SAAA,SAAAvc,EAAAmH,EAAAC,EAAAC,EAAAiW,EAAAG,GACA,OAAAltB,KAAAkuB,IAAAluB,KAAA0Z,EAAA8G,EAAA5J,IAAAC,IAAA7W,KAAAqsB,MAAAvV,GAAAiW,GAAAG,EAAA,KAIAiG,wBAAA,CACApH,MAAA1mB,OAAA,IAAA8a,EAAAtB,EAAA,OAAAC,EAAA,OAAAG,EAAAR,EAAAC,EAAA,KACAvd,KAAA,0BACA6qB,SAAA,SAAAvc,EAAAmH,EAAAC,EAAAC,EAAAiW,EAAAG,EAAA7M,GACA,OAAArgB,KAAAkuB,IAAAluB,KAAA0Z,EAAA8G,EAAA5J,IAAAC,IAAA7W,KAAAqsB,KAAAjM,GAAAtJ,EAAAuJ,IAAA0M,GAAAG,EAAA,KAIAkG,uBAAA,CACArH,MAAA1mB,OAAA,IAAA8a,EAAA,KAAAxB,EAAA,OAAAG,EAAA,KACA3d,KAAA,yBACA6qB,SAAA,SAAAvc,EAAAmH,EAAAC,EAAAC,EAAAiW,GACA,OAAA/sB,KAAAkuB,IAAAluB,KAAA0Z,EAAA8G,EAAA5J,IAAAC,IAAA7W,KAAAqsB,MAAAvV,GAAAiW,EAAA,EAAA,KAIAsG,yBAAA,CACAtH,MAAA1mB,OAAA,IAAA8a,EAAAtB,EAAA,OAAAE,EAAAN,EAAAC,EAAA,KACAvd,KAAA,2BACA6qB,SAAA,SAAAvc,EAAAmH,EAAAC,EAAAC,EAAAiW,EAAA1M,GACA,OAAArgB,KAAAkuB,IAAAluB,KAAA0Z,EAAA8G,EAAA5J,IAAAC,IAAA7W,KAAAqsB,KAAAjM,GAAAtJ,EAAAuJ,IAAA0M,EAAA,EAAA,MAKAuG,EAAA,CAEA5Z,EAAA6Z,IACA1lB,EAAA0lB,IACA7a,EAAA6a,IAEAnZ,EAAAmZ,IACApuB,EAAAouB,IACAhb,EAAAgb,IACA7H,EAAA6H,IAGAtB,GAAA,EACAD,GAAA,EACA/F,GAAA,EACA8F,GAAA,EACAD,GAAA,EACAjoB,GAAA,EACAkpB,GAAA,EAGAb,QAAAqB,IACApB,gBAAA,EAIAvF,sBAAA,EAGA1T,EAAAqa,IAGA9G,MAAA,EACAoD,MAAA,EACA2D,MAAA,EAGAtF,IAAA,SAAAxU,EAAA7L,EAAA6K,GACA,QAAA,EAAA1Y,KAAAysB,SAIAzsB,KAAAysB,QACAzsB,KAAA0Z,EAAAA,EACA1Z,KAAA6N,EAAAA,EACA7N,KAAA0Y,EAAAA,GACA,IAEA2T,KAAA,SAAAjS,EAAAjV,EAAAoT,EAAAmT,GACA,QAAA,EAAA1rB,KAAA6vB,SAIA7vB,KAAA6vB,QACA7vB,KAAAoa,EAAAA,EACApa,KAAAmF,EAAAA,EACAnF,KAAAuY,EAAAA,EACAvY,KAAA0rB,EAAAA,GAEA,IAEAQ,UAAA,WAOA,OANAlsB,KAAAoa,EAAA,EACApa,KAAAmF,EAAA,EACAnF,KAAAuY,EAAA,EACAvY,KAAA0rB,EAAA,IACA1rB,KAAA6vB,MAAA,IAIAnD,KAAA,SAAAxI,GACA,OAAAlkB,KAAAwzB,OAAA,IACAxzB,KAAAwzB,QACAxzB,KAAAkZ,EAAAgL,GACA,IAKAuP,OAAA,SAAAC,GAmCA,OAlCA1zB,KAAAysB,QAAAzsB,KAAA6vB,QACA7vB,KAAAoa,EAAApa,KAAAmF,EAAAnF,KAAAuY,EAAAvY,KAAA0rB,EAAA,GAIAxa,MAAAlR,KAAA0Z,KACA1Z,KAAA0Z,EAAAga,EAAA5b,eAGA5G,MAAAlR,KAAA6N,KACA7N,KAAA6N,EAAA6lB,EAAA/d,YAGAzE,MAAAlR,KAAA0Y,KACA1Y,KAAA0Y,EAAAgb,EAAA5a,WAGA5H,MAAAlR,KAAAoa,KACApa,KAAAoa,EAAAsZ,EAAAvZ,YAGAjJ,MAAAlR,KAAAmF,KACAnF,KAAAmF,EAAAuuB,EAAArZ,cAGAnJ,MAAAlR,KAAAuY,KACAvY,KAAAuY,EAAAmb,EAAApZ,cAGApJ,MAAAlR,KAAA0rB,KACA1rB,KAAA0rB,EAAAgI,EAAAlZ,mBAIAxa,KAAA4sB,uBACA,KAAA,EACA5sB,KAAA0Y,EAAA,EACA,MACA,KAAA,EACA1Y,KAAA0Y,EAAA,EACA1Y,KAAA6N,GAAA,EAIA,IACAnG,EAIAisB,EAgBAC,EArBA1iB,MAAAlR,KAAAkyB,YACAxqB,EAAA,IAAAiK,KAAA+hB,EAAAnY,YACAxD,YAAA/X,KAAA0Z,EAAA1Z,KAAA6N,EAAA7N,KAAA0Y,GACAhR,EAAAsQ,SAAAhY,KAAAoa,EAAApa,KAAAmF,EAAAnF,KAAAuY,EAAAvY,KAAA0rB,GAEAiI,EAAAjsB,EAAAuR,SAEA,IAAAjZ,KAAAmyB,iBAEA,IAAAwB,GAAA,IAAA3zB,KAAAkyB,UACAlyB,KAAAkyB,SAAA,GAIA,IAAAlyB,KAAAkyB,SAAA,IAAAyB,IACA3zB,KAAAkyB,QAAA,GAGAlyB,KAAA0Y,GAAAib,EACA3zB,KAAA0Y,GAAA1Y,KAAAkyB,UAEA0B,EAAA5zB,KAAAkyB,QAAAyB,GAGA3zB,KAAAisB,GAAA,GAAA2H,EAAA,GAAA,GAAA5zB,KAAAisB,IAAA2H,IAAA5zB,KAAAmyB,mBACAyB,GAAA,GAGA,GAAA5zB,KAAAkyB,QACAlyB,KAAA0Y,GAAAkb,EAEA5zB,KAAA0Y,GAAA,GAAA1G,KAAA+I,IAAA/a,KAAAkyB,SAAAyB,GAGA3zB,KAAAkyB,QAAAqB,MAKAvzB,KAAA0Z,GAAA1Z,KAAAiyB,GACAjyB,KAAA6N,GAAA7N,KAAAgyB,GACAhyB,KAAA0Y,GAAA1Y,KAAAisB,GAEAjsB,KAAAoa,GAAApa,KAAA+xB,GACA/xB,KAAAmF,GAAAnF,KAAA8xB,GACA9xB,KAAAuY,GAAAvY,KAAA6J,GACA7J,KAAA0rB,GAAA1rB,KAAA+yB,GAEA/yB,KAAAiyB,GAAAjyB,KAAAgyB,GAAAhyB,KAAAisB,GAAA,EACAjsB,KAAA+xB,GAAA/xB,KAAA8xB,GAAA9xB,KAAA6J,GAAA7J,KAAA+yB,GAAA,EAEA,IAAA/xB,EAAA,IAAA2Q,KAAA+hB,EAAAnY,WAWA,OARAva,EAAA+W,YAAA/X,KAAA0Z,EAAA1Z,KAAA6N,EAAA7N,KAAA0Y,GACA1X,EAAAgX,SAAAhY,KAAAoa,EAAApa,KAAAmF,EAAAnF,KAAAuY,EAAAvY,KAAA0rB,GAOA1rB,KAAA4sB,uBACA,KAAA,EACA5rB,EAAA6W,QAAA,GACA,MACA,KAAA,EACA7W,EAAA4W,SAAA5W,EAAA2U,WAAA,EAAA,GAWA,OANAzE,MAAAlR,KAAAkZ,IAAAlY,EAAA8Z,sBAAA9a,KAAAkZ,IACAlY,EAAA6yB,eAAA7yB,EAAA8W,cAAA9W,EAAA2U,WAAA3U,EAAA8X,WAEA9X,EAAA8yB,YAAA9yB,EAAAmZ,WAAAnZ,EAAAqZ,aAAArZ,EAAAsZ,aAAAta,KAAAkZ,EAAAlY,EAAAwZ,oBAGAxZ,IAIAzB,EAAAC,QAAA,SAAAoE,EAAAuoB,GA+BA,MAAAA,IACAA,EAAAna,KAAAuC,MAAA5C,KAAAwa,MAAA,MAYA,IANA,IAAA5pB,EAAA,CAAAspB,EAAAC,UAAAD,EAAAM,IAAAN,EAAAO,KAAAP,EAAAS,gBAAAT,EAAAU,SAAAV,EAAAW,UAAAX,EAAAc,eAAAd,EAAAgB,cAEAhB,EAAAwC,WAAAxC,EAAAuC,YAAAvC,EAAAsC,WAAAtC,EAAAoB,UAAApB,EAAAuB,WAAAvB,EAAAuD,YAAAvD,EAAAoD,WAAApD,EAAAgD,YAAAhD,EAAA+D,WAAA/D,EAAAwD,eAAAxD,EAAA4D,cAAA5D,EAAA2D,SAAA3D,EAAA8D,aAAA9D,EAAAyD,iBAAAzD,EAAA0D,UAAA1D,EAAA6D,2BAAA7D,EAAAiE,eAAAjE,EAAAmE,SAAAnE,EAAAkD,aAAAlD,EAAAmD,aAAAnD,EAAAoE,UAAApE,EAAAqE,aAAArE,EAAAiD,YAAAjD,EAAAuE,WAAAvE,EAAAwE,cAAAxE,EAAAqD,YAAArD,EAAA6C,OAAA7C,EAAA8C,cAAA9C,EAAAyC,KAAAzC,EAAA2C,KAAA3C,EAAA4C,KAAA5C,EAAAsD,QAAAtD,EAAAyE,WAAAzE,EAAAsE,YAAAtE,EAAAkE,cAAAlE,EAAA+C,IAAA/C,EAAAmH,MAAAnH,EAAAiH,IAAAjH,EAAA0G,QAAA1G,EAAA2G,iBAAA3G,EAAA4E,aAAA5E,EAAA6G,qBAAA7G,EAAA0C,aAAA1C,EAAA8G,OAAA9G,EAAAwH,yBAAAxH,EAAAsH,wBAAAtH,EAAAuH,uBAAAvH,EAAAqH,sBAAArH,EAAAuG,SAAAvG,EAAAoH,YAEAjyB,EAAAya,OAAAsY,OAAAT,GAEA1vB,EAAA3D,QAAA,CAIA,IAHA,IAAA+zB,EAAA,KACAC,EAAA,KAEA9uB,EAAA,EAAA4T,EAAAxW,EAAAtC,OAAAkF,EAAA4T,EAAA5T,IAAA,CACA,IAAAX,EAAAjC,EAAA4C,GAEAsK,EAAA7L,EAAA6L,MAAAjL,EAAAunB,OAEAtc,KACAukB,GAAAvkB,EAAA,GAAAxP,OAAA+zB,EAAA,GAAA/zB,UACA+zB,EAAAvkB,EACAwkB,EAAAzvB,GAKA,IAAAyvB,GAAAA,EAAAjI,WAAA,IAAAiI,EAAAjI,SAAAjnB,MAAA/D,EAAAgzB,GACA,OAAA,EAGApwB,EAAAA,EAAA8I,OAAAsnB,EAAA,GAAA/zB,QAEA+zB,EADAC,EAAA,KAIA,OAAAjiB,KAAAuC,MAAAvT,EAAAyyB,OAAA,IAAA9hB,KAAA,IAAAwa,IAAA,OAMA+H,6CAIA,SAAA30B,EAAA40B,EAAAC,GAIA70B,EAAAC,QAAA,SAAA60B,GAQA,IAAAC,EAAA,oBAAAxyB,OAAAA,OAAAsyB,EAAAla,EACAoa,EAAAC,SAAAD,EAAAC,UAAA,GACA,IAAAA,EAAAD,EAAAC,SAIA,OAHAA,EAAAC,IAAAD,EAAAC,KAAA,GACAD,EAAAC,IAAAC,IAAAF,EAAAC,IAAAC,KAAA,IAEAF,EAAAC,IAAAC,IAAAJ,SAAAxzB,IAAA0zB,EAAAC,IAAAC,IAAAJ,GAAAK,aACA,OAAAH,EAAAC,IAAAC,IAAAJ,GAAAK,YAMA,GAHAH,EAAAC,IAAAC,IAAAJ,GAAAK,cASAC,+CAIA,SAAAp1B,EAAA40B,EAAAC,GAIA70B,EAAAC,QAAA,SAAAmQ,GAiBA,IAAA/L,EAAA+L,EAAA,GAGA,GAAA,SADAykB,EAAA,6CAAAA,CAAA,sBAAA,OAEA,OAAAxwB,EAAA3D,OAuCA,IApCA,IAAAkF,EAAA,EACAyvB,EAAA,EAmCAzvB,EAAA,EAAAyvB,EAAA,EAAAzvB,EAAAvB,EAAA3D,OAAAkF,KACA,IAlCA,SAAAvB,EAAAuB,GACA,IACA8rB,EACA4D,EAFAC,EAAAlxB,EAAAmxB,WAAA5vB,GAGA,GAAA,OAAA2vB,GAAAA,GAAA,MAAA,CAGA,GAAAlxB,EAAA3D,QAAAkF,EAAA,EACA,MAAA,IAAAmN,MAAA,kDAGA,IADA2e,EAAArtB,EAAAmxB,WAAA5vB,EAAA,IACA,OAAA,MAAA8rB,EACA,MAAA,IAAA3e,MAAA,kDAEA,OAAA1O,EAAA4J,OAAArI,GAAAvB,EAAA4J,OAAArI,EAAA,GACA,GAAA,OAAA2vB,GAAAA,GAAA,MAAA,CAEA,GAAA,IAAA3vB,EACA,MAAA,IAAAmN,MAAA,kDAGA,IADAuiB,EAAAjxB,EAAAmxB,WAAA5vB,EAAA,IACA,OAAA,MAAA0vB,EAGA,MAAA,IAAAviB,MAAA,kDAIA,OAAA,EAEA,OAAA1O,EAAA4J,OAAArI,GAIA6vB,CAAApxB,EAAAuB,IAOAyvB,IAGA,OAAAA,IAMAK,+CAIA,SAAA11B,GAIAA,EAAAC,QAAA,SAAA01B,GAsBA,IAAAjC,EAAA,CAAA,IAAA,KAAA,KAAA,KAAA,KAAA,KAAA,IAAA,IAAA,IAAA,IAAA,IAAA,IAAA,IAAA,IAAA,IAAA,IAAA,IAAA,IAAA,IAAA,SAAA,SAAA,KAAA9pB,KAAA,IAGA,OAAA,iBAAA+rB,GAAA,iBAAAA,IAAA,IAAAjC,EAAAhc,QAAAie,EAAAhwB,OAAA,MAAA,KAAAgwB,IAAAhkB,MAAAgkB,MASAC,EAAA,GAGA,SAAAf,EAAAgB,GAEA,IAAAC,EAAAF,EAAAC,GACA,QAAAv0B,IAAAw0B,EACA,OAAAA,EAAA71B,QAGA,IAAAD,EAAA41B,EAAAC,GAAA,CAGA51B,QAAA,IAOA,OAHAue,EAAAqX,GAAA71B,EAAAA,EAAAC,QAAA40B,GAGA70B,EAAAC,QAOA40B,EAAAhvB,EAAA,SAAA7F,GACA,IAAA+1B,EAAA/1B,GAAAA,EAAAg2B,WACA,WAAA,OAAAh2B,EAAA,SACA,WAAA,OAAAA,GAEA,OADA60B,EAAA1b,EAAA4c,EAAA,CAAApxB,EAAAoxB,IACAA,GAOAlB,EAAA1b,EAAA,SAAAlZ,EAAAg2B,GACA,IAAA,IAAAvsB,KAAAusB,EACApB,EAAA3a,EAAA+b,EAAAvsB,KAAAmrB,EAAA3a,EAAAja,EAAAyJ,IACAwS,OAAAga,eAAAj2B,EAAAyJ,EAAA,CAAAysB,YAAA,EAAAC,IAAAH,EAAAvsB,MAQAmrB,EAAAla,EAAA,WACA,GAAA,iBAAA0b,WAAA,OAAAA,WACA,IACA,OAAA51B,MAAA,IAAA61B,SAAA,cAAA,GACA,MAAA9pB,GACA,GAAA,iBAAAjK,OAAA,OAAAA,QALA,GAYAsyB,EAAA3a,EAAA,SAAApO,EAAAhH,GAAA,OAAAoX,OAAAlT,UAAAgN,eAAA/T,KAAA6J,EAAAhH,IAMA+vB,EAAA/Y,EAAA,SAAA7b,GACA,oBAAAs2B,QAAAA,OAAAC,aACAta,OAAAga,eAAAj2B,EAAAs2B,OAAAC,YAAA,CAAA7sB,MAAA,WAEAuS,OAAAga,eAAAj2B,EAAA,aAAA,CAAA0J,OAAA,KAKA,IAAA8sB,EAAA,IAEA,WAIA5B,EAAA/Y,EAAA2a,GACA,IAAAC,EAAA7B,EAAA,gDACA8B,EAAA9B,EAAAhvB,EAAA6wB,GACAE,EAAA/B,EAAA,kDACAgC,EAAAhC,EAAAhvB,EAAA+wB,GACAE,EAAAjC,EAAA,oDACAkC,EAAAlC,EAAAhvB,EAAAixB,GACAE,EAAAnC,EAAA,gDACAoC,EAAApC,EAAAhvB,EAAAmxB,GAgBA52B,EAAAC,QAAA,EAAAT,kBAAA,CACAgd,QAAA,CAIAsa,aAAA,CAAA,UAAA,WAQAC,SAAA,SAAAC,EAAAxzB,GACA,IAAAyzB,EAAAD,EAAAztB,MAEA,OADA/F,OAAA,IAAAA,EAAAA,EAAA,EACA,OAAAwzB,EAAAE,YACA,IAAAF,EAAAE,MAAA1zB,IACA,CACA2zB,KAAAF,EACAG,UAAAH,EAAAlqB,OAAAkqB,EAAAjqB,YAAA,KAAA,GACAhB,KAAAgrB,EAAAE,MAAA1zB,GAAAwI,KAAA,KACAxE,KAAAwvB,EAAAE,MAAA1zB,GAAAgE,OAYAgF,SAAA,SAAA6qB,GACA,IAAA7qB,EAAA,GACAnM,KAAAwR,QAAAwlB,KACAA,EAAA,CAAAA,IAEA,IAAA,IAAA7xB,EAAA,EAAAA,EAAA6xB,EAAA/2B,OAAAkF,IACAgH,EAAA7B,KAAA,UAAA0sB,EAAA7xB,GAAA,MAEA,OAAAgH,EAAAhD,QAQA8tB,gBAAA,SAAA50B,GACA,OAAArC,KAAAk3B,SAAA70B,EAAArC,KAAAy2B,eASAS,SAAA,SAAA70B,EAAAE,GACA,IAAA40B,GAAA,EACA,iBAAA50B,IACAA,EAAA,CAAAA,IAEA,IAAArC,EAAAP,EAAAQ,KAAAkC,EAAAV,KAAA,aACAy1B,EAAA,GACArb,EAAA7b,EAAA2b,gBAoBA,OAnBAxZ,EAAAlB,QAAA4a,GACApc,EAAAyC,KAAA2Z,EAAA1Z,EAAAlB,MAAA,SAAAgC,EAAAk0B,GACAD,EAAA9sB,KAAA+sB,KAGAh1B,EAAAlB,QAAAjB,EAAAG,SAAAkC,OACA60B,EAAA9sB,KAAApK,EAAAG,SAAAkC,MAAAF,EAAAlB,OAEAxB,EAAAyC,KAAAg1B,EAAA,SAAAj0B,EAAAm0B,GACA,GAAA,sBAAAA,EAEA,IADA,IAAAC,EAAAD,EAAAn4B,kBACAgG,EAAA,EAAAA,EAAAoyB,EAAAt3B,OAAAkF,IACA,IAAA,IAAAxF,EAAAkH,QAAA0wB,EAAApyB,GAAA,GAAA5C,GAEA,QADA40B,GAAA,KAMAA,GASAK,OAAA,SAAA7nB,GACA,OAAAumB,GAAAA,CAAAvmB,IAUA8nB,QAAA,SAAApsB,EAAAhJ,EAAA6G,GACA,OAAAlJ,KAAAi3B,gBAAA50B,IAAArC,KAAA03B,WAAAxuB,GACAyuB,WAAAzuB,GACAlJ,KAAAwR,QAAAtI,GACAyuB,WAAAzuB,EAAAjJ,QACA,SAAAoC,EAAA8E,KACAwwB,WAAA3lB,KAAAuC,MAAAvU,KAAA02B,SAAAr0B,GAAAsJ,OAEAgsB,WAAA33B,KAAAw3B,OAAAtuB,KASA0uB,qBAAA,SAAAhrB,EAAAvK,GACA,IAAA80B,OAAAt2B,EAUA,OATAlB,EAAAyC,KAAAzC,EAAAO,UAAAwC,YAAAL,GAAA,SAAA4G,EAAA1G,GACA,sBAAA0G,GACAtJ,EAAAyC,KAAAG,EAAA,SAAA4C,EAAA+D,GACAA,EAAA,KAAA0D,IACAuqB,EAAAjuB,OAKAiuB,GASAU,UAAA,SAAA3uB,EAAA1E,GACA,IAMAszB,EANAC,GAAA,EACAvf,EAAA,IAAAjF,cACA,MAAA,iBAAArK,QAAA,IAAA1E,EACA0E,GAEA,iBAAA1E,IAGAA,OADA3D,KADAi3B,EAAA93B,KAAA43B,qBAAA,aAAApzB,IAEAszB,EAAA,GAAA,GAEA,MAIAC,EADA,MAAAvzB,EACAxE,KAAAg4B,UAAA9uB,IAEA6uB,EAAAvf,EAAA5C,UAAA1M,EAAA1E,cACAmN,MAAA6G,EAAAgD,WAAAuc,EAAAvzB,KAAA0E,GACA8I,KAAAC,MAAA8lB,EAAAxc,UAAA,OAiBA0c,aAAA,SAAA/3B,EAAAgJ,EAAA7G,EAAAqC,EAAAwzB,GACA,IAAAC,EAAAn4B,KAAA63B,UAAAnzB,GACA,IAAAyzB,EAAA,CACA,IAAA5lB,EAAAvS,KAAAo4B,iBAAAl4B,EAAAmC,EAAAqC,GACA,QAAA7D,IAAA0R,EACA,OAAA,EAEA4lB,EAAAn4B,KAAA63B,UAAA33B,EAAA0G,aAAA2L,GAAAA,GAEA,IAAAwlB,EAAA/3B,KAAA63B,UAAA3uB,EAAA7G,GACA,IAAA,IAAA01B,EACA,OAAA,EAEA,OAAAG,GACA,IAAA,IACA,OAAAH,EAAAI,EACA,IAAA,KACA,OAAAJ,GAAAI,EACA,IAAA,KACA,IAAA,MACA,OAAAJ,IAAAI,EACA,IAAA,IACA,OAAAA,EAAAJ,EACA,IAAA,KACA,OAAAI,GAAAJ,EACA,QACA,MAAA,IAAAzlB,MAAA,2BAUA+E,UAAA,SAAAnO,EAAA1E,GAEA,OADA,IAAA+O,eACA8D,UAAAnO,EAAA1E,IAWAwzB,UAAA,SAAAxsB,EAAA2gB,GACA,OAAAmK,GAAAA,CAAA9qB,EAAA2gB,IAUAuL,WAAA,SAAAW,GACA,OAAA7B,GAAAA,CAAA6B,IASA7mB,QAAA,SAAA8mB,GACA,MAAA,mBAAA7c,OAAAlT,UAAAqJ,SAAApQ,KAAA82B,IAWAC,UAAA,SAAAta,EAAAua,GACA,OAAApC,GAAAA,CAAAnY,EAAAua,IASApb,YAAA,SAAAa,EAAAua,GACA,SAAAx4B,KAAAwR,QAAAyM,KAAAje,KAAAwR,QAAAgnB,MAGAva,EAAAhe,SAAAu4B,EAAAv4B,QAGAN,EAAAme,cAAA9d,KAAAu4B,UAAAta,EAAAua,MAUAJ,iBAAA,SAAAl4B,EAAAmC,EAAAlB,GACA,IAGAX,EAIAi4B,EAPAC,EAAAx4B,EAAAkH,WAAAjG,GACAw3B,EAAAD,EAAAA,EAAAz4B,OAAA,GAWA,YAVAY,IAAA83B,GAAAz4B,EAAAG,SAAAiG,aACA9F,EAAA,OACA,WAAAm4B,EAAAC,SAAA,WAAAD,EAAAC,SAAA,aAAAD,EAAAxxB,MAAA,UAAAwxB,EAAAxxB,OACA3G,EAAA,SAEAi4B,EAAA,8BACA94B,EAAAg5B,GAAAjoB,IAAA+nB,GAAA/nB,IAAAlQ,EAAAi4B,EAAA,IAAAp2B,EAAAlB,MAAAZ,GAAAC,EAAAi4B,EAAA,IAAAp2B,EAAAlB,KAAA,WACAxB,EAAA0C,GAAAJ,WAGA02B,GAQAnb,mBAAA,SAAAzK,GACA,IAEA8lB,EAFAC,EAAA,CAAA,4CAOA,MANA,iBAAA/lB,IACA8lB,EAAA9lB,EAAAgmB,aAAAtpB,MAAA,yBACAzP,KAAAwR,QAAAqnB,KACAC,EAAA,CAAAD,EAAA,MAGAC,GAQAE,aAAA,SAAAp1B,GACA,OAAAA,EAAAC,QAAA,sCAAA,SAQAwY,kBAAA,SAAAlb,GACA,IAAA83B,EAAA93B,EAAA+B,MAAA,OAEA,OADA,IAAA+1B,EAAAh5B,QAAAg5B,EAAA3uB,KAAA,IACA,IAAAjF,OAAA,IAAA4zB,EAAAvuB,IAAA,SAAA2X,GACA,OAAAljB,kBAAAgd,QAAA6c,aAAA3W,KACAlZ,KAAA,iBAAA,MASAiT,WAAA,SAAA7Z,EAAA2Z,GACA,IAAAgd,EAAA,CACA/5B,kBAAA+c,EAAA/c,mBAAA,GACA4d,wBAAAb,EAAAa,yBAAA,IAEA,IAAA,IAAA9T,KAAAiwB,EACA,IAAAA,EAAAjwB,GAAAhJ,cAGA,IAAAsC,EAAA0G,KACA1G,EAAA0G,GAAA,IAEA1G,EAAA0G,GAAA1G,EAAA0G,GAAA3G,OAAA42B,EAAAjwB,KAEA,OAAA1G,GAQAsb,OAAA,SAAAlO,GACA,OAAAhQ,EAAA,UAAA6L,KAAAmE,GAAAT,QASAiqB,gBAAA,SAAAj5B,EAAAiB,GASA,IARA,IAAAi4B,EAAAj4B,EAAA0C,QAAA,cAAA,QACAw1B,EAAA,CAEAD,EAEAA,EAAA,KAEAA,EAAAv1B,QAAA,iBAAA,SACAsB,EAAA,EAAAA,EAAAk0B,EAAAp5B,OAAAkF,IAAA,CACA,IAAAm0B,EAAAp5B,EAAAkH,WAAAiyB,EAAAl0B,IACA,GAAA,EAAAm0B,EAAAr5B,OACA,OAAAq5B,EAGA,OAAA35B,EAAA,OAYAyH,WAAA,SAAAlH,EAAAiB,GAEA,IAAAm4B,EAAAp5B,EAAAkH,WAAAjG,GACA,GAAA,EAAAm4B,EAAAr5B,OACA,OAAAq5B,EAMA,IAFA,IACA/nB,EAAApQ,EAAA+B,MADA,KAEAiC,EAAAoM,EAAAtR,OAAA,EAAAkF,EAAAA,IAAA,CAEA,IADA,IAAAo0B,EAAA,GACAne,EAAA,EAAAA,EAAAjW,EAAAiW,IACAme,EAAAjvB,KAAAiH,EAAA6J,IAGA,GAAA,GADAke,EAAAt5B,KAAAm5B,gBAAAj5B,EAAAq5B,EAAApwB,KAPA,OAQAlJ,OACA,OAAAq5B,EAGA,OAAA35B,EAAA,OASA65B,iBAAA,SAAAt5B,EAAAmC,GACA,OAAA,IAAAA,EAAAlB,KAAA8V,QAAA,MACA/W,EAAAkH,WAAA/E,EAAAlB,MAAAuJ,IAAA,SAAAvF,EAAA4G,GACA,OAAA7L,EAAA0G,aAAAmF,KACA4pB,MAEAz1B,EAAA0G,aAAAvE,OAvdA,GA7kDA,GCWA1C,EAAAC,QAAA,EAAAT,kBAAA,CAEAgd,QAAA,CAQAsd,WAAA,SAAAvwB,GAEA,IAAAwwB,EAAA,CACAC,OAAA,CACA,UACA,QACA,cACA,UACA,SACA,SACA,SACA,SACA,SACA,WACA,cACA,YACA,QACA,aACA,QACA,UACA,QACA,gBACA,WACA,SACA,WACA,WACA,WACA,SACA,eACA,OACA,UACA,WACA,SACA,WACA,QACA,aACA,OACA,SACA,aACA,SACA,SACA,SACA,SACA,UACA,YACA,WACA,UACA,WACA,SACA,aACA,cACA,aACA,WACA,UACA,QACA,YAEAC,QAAA,CACA,OACA,YACA,WACA,UACA,YACA,yBACA,sBACA,oBACA,kBACA,qBACA,oBACA,yBACA,kBACA,qBACA,qBACA,oBACA,oBACA,QACA,WACA,WACA,QACA,iBACA,WACA,QACA,SACA,eACA,YACA,SACA,QACA,gBACA,eACA,SACA,UACA,UACA,SACA,UACA,YACA,aACA,UACA,SACA,UACA,eACA,SACA,eACA,SACA,UACA,WACA,WACA,WACA,cACA,YACA,YACA,UACA,YACA,aACA,UACA,aACA,YACA,YACA,SACA,UACA,SACA,aACA,uBACA,eACA,kBACA,qBACA,oBACA,gBACA,oBACA,kBACA,SACA,UACA,UACA,SACA,sBACA,sBACA,aACA,SACA,OACA,cACA,gBACA,SACA,UACA,SACA,UACA,aACA,YACA,WACA,YACA,SACA,aACA,cACA,WACA,UACA,YACA,aACA,WACA,aACA,SACA,WACA,UACA,OACA,UACA,sBACA,sBACA,yBACA,UACA,SACA,cACA,aACA,UACA,iBACA,gBACA,cACA,cACA,cACA,eACA,SACA,SACA,WACA,aACA,eACA,WACA,WACA,gBACA,YACA,eACA,WACA,QACA,gBACA,WACA,WACA,WACA,YACA,aACA,gBACA,cACA,QACA,cACA,UACA,UACA,UACA,YACA,aACA,WACA,UACA,eAEAC,WAAA,CACA,QACA,QACA,iBACA,YACA,SACA,UACA,SACA,UACA,aACA,QACA,UAEAC,OAAA,CACA,gBAEAC,KAAA,CACA,OACA,SACA,QACA,SACA,QACA,SACA,WACA,UACA,UACA,OACA,UACA,SACA,UACA,SACA,aACA,YACA,UACA,WACA,QACA,OACA,QACA,WACA,OACA,SACA,SACA,cACA,YACA,OACA,UACA,UACA,WACA,YACA,QACA,YACA,UACA,UACA,YACA,WACA,UACA,cACA,eACA,UACA,SACA,QACA,UACA,WACA,SACA,SACA,UACA,eACA,cACA,OACA,OACA,aACA,YACA,YACA,QACA,YACA,UACA,SACA,WACA,YACA,QACA,WACA,YACA,SACA,WACA,UACA,SACA,UACA,QACA,cACA,SACA,WACA,YACA,cACA,UACA,gBACA,WAEAC,SAAA,CACA,SACA,UACA,SACA,aACA,QACA,UACA,YACA,gBACA,YACA,WAEAC,UAAA,CACA,WACA,WACA,cACA,SACA,SACA,QACA,SACA,WACA,YACA,YACA,QACA,UAEAC,OAAA,CACA,YACA,UACA,SACA,WACA,SACA,aACA,WACA,YACA,WACA,WACA,WACA,aACA,SACA,YACA,WACA,WACA,cACA,WACA,SACA,cACA,OACA,SACA,YACA,SACA,aACA,SACA,QACA,YACA,QACA,SACA,SACA,OACA,QACA,YACA,SACA,OACA,OACA,SACA,aACA,WACA,aACA,SACA,QACA,YACA,UACA,SACA,WACA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","file":"jsvalidation.min.js","sourcesContent":["/*!\n * Laravel Javascript Validation\n *\n * https://github.com/proengsoft/laravel-jsvalidation\n *\n * Copyright (c) 2017 Proengsoft\n * Released under the MIT license\n */\n\nvar laravelValidation;\nlaravelValidation = {\n\n implicitRules: ['Required','Confirmed'],\n\n /**\n * Initialize laravel validations.\n */\n init: function () {\n\n // jquery-validation requires the field under validation to be present. We're adding a dummy hidden\n // field so that any errors are not visible.\n var constructor = $.fn.validate;\n $.fn.validate = function( options ) {\n var name = 'proengsoft_jsvalidation'; // must match the name defined in JsValidatorFactory.newFormRequestValidator\n var $elm = $(this).find('input[name=\"' + name + '\"]');\n if ($elm.length === 0) {\n $('').attr({type: 'hidden', name: name}).appendTo(this);\n }\n\n return constructor.apply(this, [options]);\n };\n\n // Disable class rules and attribute rules\n $.validator.classRuleSettings = {};\n $.validator.attributeRules = function () {};\n\n $.validator.dataRules = this.arrayRules;\n $.validator.prototype.arrayRulesCache = {};\n\n // Register validations methods\n this.setupValidations();\n },\n\n arrayRules: function(element) {\n\n var rules = {},\n validator = $.data( element.form, \"validator\"),\n cache = validator.arrayRulesCache;\n\n // Is not an Array\n if (element.name.indexOf('[') === -1) {\n return rules;\n }\n\n if (! (element.name in cache)) {\n cache[element.name] = {};\n }\n\n $.each(validator.settings.rules, function(name, tmpRules) {\n if (name in cache[element.name]) {\n rules = laravelValidation.helpers.mergeRules(rules, cache[element.name][name]);\n } else {\n cache[element.name][name] = {};\n\n var nameRegExp = laravelValidation.helpers.regexFromWildcard(name);\n if (element.name.match(nameRegExp)) {\n var newRules = $.validator.normalizeRule(tmpRules) || {};\n cache[element.name][name] = newRules;\n\n rules = laravelValidation.helpers.mergeRules(rules, newRules);\n }\n }\n });\n\n return rules;\n },\n\n setupValidations: function () {\n\n /**\n * Get CSRF token.\n *\n * @param params\n * @returns {string}\n */\n var getCsrfToken = function (params) {\n return params[0][1][1];\n };\n\n /**\n * Whether to validate all attributes.\n *\n * @param params\n * @returns {boolean}\n */\n var isValidateAll = function (params) {\n return params[0][1][2];\n };\n\n /**\n * Determine whether the rule is implicit.\n *\n * @param params\n * @returns {boolean}\n */\n var isImplicit = function (params) {\n var implicit = false;\n $.each(params, function (i, parameters) {\n implicit = implicit || parameters[3];\n });\n\n return implicit;\n };\n\n /**\n * Get form method from a validator instance.\n *\n * @param validator\n * @returns {string}\n */\n var formMethod = function (validator) {\n var formMethod = $(validator.currentForm).attr('method');\n if ($(validator.currentForm).find('input[name=\"_method\"]').length) {\n formMethod = $(validator.currentForm).find('input[name=\"_method\"]').val();\n }\n\n return formMethod;\n };\n\n /**\n * Get AJAX parameters for remote requests.\n *\n * @param validator\n * @param element\n * @param params\n * @param data\n * @returns {object}\n */\n var ajaxOpts = function (validator, element, params, data) {\n return {\n mode: 'abort',\n port: 'validate' + element.name,\n dataType: 'json',\n data: data,\n context: validator.currentForm,\n url: $(validator.currentForm).attr('action'),\n type: formMethod(validator),\n beforeSend: function (xhr) {\n var token = getCsrfToken(params);\n if (formMethod(validator) !== 'get' && token) {\n return xhr.setRequestHeader('X-XSRF-TOKEN', token);\n }\n },\n };\n };\n\n /**\n * Validate a set of local JS based rules against an element.\n *\n * @param validator\n * @param values\n * @param element\n * @param rules\n * @returns {boolean}\n */\n var validateLocalRules = function (validator, values, element, rules) {\n var validated = true,\n previous = validator.previousValue(element);\n\n $.each(rules, function (i, param) {\n var implicit = param[3] || laravelValidation.implicitRules.indexOf(param[0]) !== -1;\n var rule = param[0];\n var message = param[2];\n\n if (! implicit && validator.optional(element)) {\n validated = \"dependency-mismatch\";\n return false;\n }\n\n if (laravelValidation.methods[rule] !== undefined) {\n $.each(values, function(index, value) {\n validated = laravelValidation.methods[rule].call(validator, value, element, param[1], function(valid) {\n validator.settings.messages[element.name].laravelValidationRemote = previous.originalMessage;\n if (valid) {\n var submitted = validator.formSubmitted;\n validator.prepareElement(element);\n validator.formSubmitted = submitted;\n validator.successList.push(element);\n delete validator.invalid[element.name];\n validator.showErrors();\n } else {\n var errors = {};\n errors[ element.name ]\n = previous.message\n = typeof message === \"function\" ? message( value ) : message;\n validator.invalid[element.name] = true;\n validator.showErrors(errors);\n }\n validator.showErrors(validator.errorMap);\n previous.valid = valid;\n });\n\n // Break loop.\n if (validated === false) {\n return false;\n }\n });\n } else {\n validated = false;\n }\n\n if (validated !== true) {\n if (!validator.settings.messages[element.name] ) {\n validator.settings.messages[element.name] = {};\n }\n\n validator.settings.messages[element.name].laravelValidation= message;\n\n return false;\n }\n\n });\n\n return validated;\n };\n\n /**\n * Create JQueryValidation check to validate Laravel rules.\n */\n\n $.validator.addMethod(\"laravelValidation\", function (value, element, params) {\n var rules = [],\n arrayRules = [];\n $.each(params, function (i, param) {\n // put Implicit rules in front\n var isArrayRule = param[4].indexOf('[') !== -1;\n if (param[3] || laravelValidation.implicitRules.indexOf(param[0]) !== -1) {\n isArrayRule ? arrayRules.unshift(param) : rules.unshift(param);\n } else {\n isArrayRule ? arrayRules.push(param) : rules.push(param);\n }\n });\n\n // Validate normal rules.\n var localRulesResult = validateLocalRules(this, [value], element, rules);\n\n // Validate items of the array using array rules.\n var arrayValue = ! Array.isArray(value) ? [value] : value;\n var arrayRulesResult = validateLocalRules(this, arrayValue, element, arrayRules);\n\n return localRulesResult && arrayRulesResult;\n }, '');\n\n\n /**\n * Create JQueryValidation check to validate Remote Laravel rules.\n */\n $.validator.addMethod(\"laravelValidationRemote\", function (value, element, params) {\n\n if (! isImplicit(params) && this.optional( element )) {\n return \"dependency-mismatch\";\n }\n\n var previous = this.previousValue( element ),\n validator, data;\n\n if (! this.settings.messages[ element.name ]) {\n this.settings.messages[ element.name ] = {};\n }\n previous.originalMessage = this.settings.messages[ element.name ].laravelValidationRemote;\n this.settings.messages[ element.name ].laravelValidationRemote = previous.message;\n\n if (laravelValidation.helpers.arrayEquals(previous.old, value) || previous.old === value) {\n return previous.valid;\n }\n\n previous.old = value;\n validator = this;\n this.startRequest( element );\n\n data = $(validator.currentForm).serializeArray();\n data.push({'name': '_jsvalidation', 'value': element.name});\n data.push({'name': '_jsvalidation_validate_all', 'value': isValidateAll(params)});\n\n $.ajax( ajaxOpts(validator, element, params, data) )\n .always(function( response, textStatus ) {\n var errors, message, submitted, valid;\n\n if (textStatus === 'error') {\n valid = false;\n response = laravelValidation.helpers.parseErrorResponse(response);\n } else if (textStatus === 'success') {\n valid = response === true || response === \"true\";\n } else {\n return;\n }\n\n validator.settings.messages[ element.name ].laravelValidationRemote = previous.originalMessage;\n\n if ( valid ) {\n submitted = validator.formSubmitted;\n validator.prepareElement( element );\n validator.formSubmitted = submitted;\n validator.successList.push( element );\n delete validator.invalid[ element.name ];\n validator.showErrors();\n } else {\n errors = {};\n message = response || validator.defaultMessage( element, \"remote\" );\n errors[ element.name ]\n = previous.message\n = typeof message === \"function\" ? message( value ) : message[0];\n validator.invalid[ element.name ] = true;\n validator.showErrors( errors );\n }\n validator.showErrors(validator.errorMap);\n previous.valid = valid;\n validator.stopRequest( element, valid );\n }\n );\n return \"pending\";\n }, '');\n\n /**\n * Create JQueryValidation check to form requests.\n */\n $.validator.addMethod(\"laravelValidationFormRequest\", function (value, element, params) {\n\n var validator = this,\n previous = validator.previousValue(element);\n\n var data = $(validator.currentForm).serializeArray();\n data.push({name: '__proengsoft_form_request', value: 1}); // must match FormRequest.JS_VALIDATION_FIELD\n\n // Skip AJAX if the value remains the same as a prior request.\n if (JSON.stringify(previous.old) === JSON.stringify(data)) {\n if (! previous.valid) {\n validator.showErrors(previous.errors || {});\n }\n\n return previous.valid;\n }\n\n previous.old = data;\n this.startRequest( element );\n\n $.ajax(ajaxOpts(validator, element, params, data))\n .always(function( response, textStatus ) {\n var errors = {},\n valid = textStatus === 'success' && (response === true || response === 'true');\n\n if (valid) {\n validator.resetInternals();\n validator.toHide = validator.errorsFor( element );\n } else {\n $.each( response, function( fieldName, errorMessages ) {\n var errorElement = laravelValidation.helpers.findByName(validator, fieldName)[0];\n if (errorElement) {\n errors[errorElement.name] = laravelValidation.helpers.encode(errorMessages[0] || '');\n }\n });\n\n // Failed to find the error fields so mark the form as valid otherwise user\n // will be left in limbo with no visible error messages.\n if ($.isEmptyObject(errors)) {\n valid = true;\n }\n }\n\n previous.valid = valid;\n previous.errors = errors;\n validator.showErrors(errors);\n validator.stopRequest(element, valid);\n });\n\n return 'pending';\n }, '');\n }\n};\n\n$(function() {\n laravelValidation.init();\n});\n","/*!\n * jQuery Validation Plugin v1.21.0\n *\n * https://jqueryvalidation.org/\n *\n * Copyright (c) 2024 Jörn Zaefferer\n * Released under the MIT license\n */\n(function( factory ) {\n\tif ( typeof define === \"function\" && define.amd ) {\n\t\tdefine( [\"jquery\"], factory );\n\t} else if (typeof module === \"object\" && module.exports) {\n\t\tmodule.exports = factory( require( \"jquery\" ) );\n\t} else {\n\t\tfactory( jQuery );\n\t}\n}(function( $ ) {\n\n$.extend( $.fn, {\n\n\t// https://jqueryvalidation.org/validate/\n\tvalidate: function( options ) {\n\n\t\t// If nothing is selected, return nothing; can't chain anyway\n\t\tif ( !this.length ) {\n\t\t\tif ( options && options.debug && window.console ) {\n\t\t\t\tconsole.warn( \"Nothing selected, can't validate, returning nothing.\" );\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Check if a validator for this form was already created\n\t\tvar validator = $.data( this[ 0 ], \"validator\" );\n\t\tif ( validator ) {\n\t\t\treturn validator;\n\t\t}\n\n\t\t// Add novalidate tag if HTML5.\n\t\tthis.attr( \"novalidate\", \"novalidate\" );\n\n\t\tvalidator = new $.validator( options, this[ 0 ] );\n\t\t$.data( this[ 0 ], \"validator\", validator );\n\n\t\tif ( validator.settings.onsubmit ) {\n\n\t\t\tthis.on( \"click.validate\", \":submit\", function( event ) {\n\n\t\t\t\t// Track the used submit button to properly handle scripted\n\t\t\t\t// submits later.\n\t\t\t\tvalidator.submitButton = event.currentTarget;\n\n\t\t\t\t// Allow suppressing validation by adding a cancel class to the submit button\n\t\t\t\tif ( $( this ).hasClass( \"cancel\" ) ) {\n\t\t\t\t\tvalidator.cancelSubmit = true;\n\t\t\t\t}\n\n\t\t\t\t// Allow suppressing validation by adding the html5 formnovalidate attribute to the submit button\n\t\t\t\tif ( $( this ).attr( \"formnovalidate\" ) !== undefined ) {\n\t\t\t\t\tvalidator.cancelSubmit = true;\n\t\t\t\t}\n\t\t\t} );\n\n\t\t\t// Validate the form on submit\n\t\t\tthis.on( \"submit.validate\", function( event ) {\n\t\t\t\tif ( validator.settings.debug ) {\n\n\t\t\t\t\t// Prevent form submit to be able to see console output\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t}\n\n\t\t\t\tfunction handle() {\n\t\t\t\t\tvar hidden, result;\n\n\t\t\t\t\t// Insert a hidden input as a replacement for the missing submit button\n\t\t\t\t\t// The hidden input is inserted in two cases:\n\t\t\t\t\t// - A user defined a `submitHandler`\n\t\t\t\t\t// - There was a pending request due to `remote` method and `stopRequest()`\n\t\t\t\t\t// was called to submit the form in case it's valid\n\t\t\t\t\tif ( validator.submitButton && ( validator.settings.submitHandler || validator.formSubmitted ) ) {\n\t\t\t\t\t\thidden = $( \"\" )\n\t\t\t\t\t\t\t.attr( \"name\", validator.submitButton.name )\n\t\t\t\t\t\t\t.val( $( validator.submitButton ).val() )\n\t\t\t\t\t\t\t.appendTo( validator.currentForm );\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( validator.settings.submitHandler && !validator.settings.debug ) {\n\t\t\t\t\t\tresult = validator.settings.submitHandler.call( validator, validator.currentForm, event );\n\t\t\t\t\t\tif ( hidden ) {\n\n\t\t\t\t\t\t\t// And clean up afterwards; thanks to no-block-scope, hidden can be referenced\n\t\t\t\t\t\t\thidden.remove();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif ( result !== undefined ) {\n\t\t\t\t\t\t\treturn result;\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\t// Prevent submit for invalid forms or custom submit handlers\n\t\t\t\tif ( validator.cancelSubmit ) {\n\t\t\t\t\tvalidator.cancelSubmit = false;\n\t\t\t\t\treturn handle();\n\t\t\t\t}\n\t\t\t\tif ( validator.form() ) {\n\t\t\t\t\tif ( validator.pendingRequest ) {\n\t\t\t\t\t\tvalidator.formSubmitted = true;\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\treturn handle();\n\t\t\t\t} else {\n\t\t\t\t\tvalidator.focusInvalid();\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t} );\n\t\t}\n\n\t\treturn validator;\n\t},\n\n\t// https://jqueryvalidation.org/valid/\n\tvalid: function() {\n\t\tvar valid, validator, errorList;\n\n\t\tif ( $( this[ 0 ] ).is( \"form\" ) ) {\n\t\t\tvalid = this.validate().form();\n\t\t} else {\n\t\t\terrorList = [];\n\t\t\tvalid = true;\n\t\t\tvalidator = $( this[ 0 ].form ).validate();\n\t\t\tthis.each( function() {\n\t\t\t\tvalid = validator.element( this ) && valid;\n\t\t\t\tif ( !valid ) {\n\t\t\t\t\terrorList = errorList.concat( validator.errorList );\n\t\t\t\t}\n\t\t\t} );\n\t\t\tvalidator.errorList = errorList;\n\t\t}\n\t\treturn valid;\n\t},\n\n\t// https://jqueryvalidation.org/rules/\n\trules: function( command, argument ) {\n\t\tvar element = this[ 0 ],\n\t\t\tisContentEditable = typeof this.attr( \"contenteditable\" ) !== \"undefined\" && this.attr( \"contenteditable\" ) !== \"false\",\n\t\t\tsettings, staticRules, existingRules, data, param, filtered;\n\n\t\t// If nothing is selected, return empty object; can't chain anyway\n\t\tif ( element == null ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( !element.form && isContentEditable ) {\n\t\t\telement.form = this.closest( \"form\" )[ 0 ];\n\t\t\telement.name = this.attr( \"name\" );\n\t\t}\n\n\t\tif ( element.form == null ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( command ) {\n\t\t\tsettings = $.data( element.form, \"validator\" ).settings;\n\t\t\tstaticRules = settings.rules;\n\t\t\texistingRules = $.validator.staticRules( element );\n\t\t\tswitch ( command ) {\n\t\t\tcase \"add\":\n\t\t\t\t$.extend( existingRules, $.validator.normalizeRule( argument ) );\n\n\t\t\t\t// Remove messages from rules, but allow them to be set separately\n\t\t\t\tdelete existingRules.messages;\n\t\t\t\tstaticRules[ element.name ] = existingRules;\n\t\t\t\tif ( argument.messages ) {\n\t\t\t\t\tsettings.messages[ element.name ] = $.extend( settings.messages[ element.name ], argument.messages );\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"remove\":\n\t\t\t\tif ( !argument ) {\n\t\t\t\t\tdelete staticRules[ element.name ];\n\t\t\t\t\treturn existingRules;\n\t\t\t\t}\n\t\t\t\tfiltered = {};\n\t\t\t\t$.each( argument.split( /\\s/ ), function( index, method ) {\n\t\t\t\t\tfiltered[ method ] = existingRules[ method ];\n\t\t\t\t\tdelete existingRules[ method ];\n\t\t\t\t} );\n\t\t\t\treturn filtered;\n\t\t\t}\n\t\t}\n\n\t\tdata = $.validator.normalizeRules(\n\t\t$.extend(\n\t\t\t{},\n\t\t\t$.validator.classRules( element ),\n\t\t\t$.validator.attributeRules( element ),\n\t\t\t$.validator.dataRules( element ),\n\t\t\t$.validator.staticRules( element )\n\t\t), element );\n\n\t\t// Make sure required is at front\n\t\tif ( data.required ) {\n\t\t\tparam = data.required;\n\t\t\tdelete data.required;\n\t\t\tdata = $.extend( { required: param }, data );\n\t\t}\n\n\t\t// Make sure remote is at back\n\t\tif ( data.remote ) {\n\t\t\tparam = data.remote;\n\t\t\tdelete data.remote;\n\t\t\tdata = $.extend( data, { remote: param } );\n\t\t}\n\n\t\treturn data;\n\t}\n} );\n\n// JQuery trim is deprecated, provide a trim method based on String.prototype.trim\nvar trim = function( str ) {\n\n\t// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trim#Polyfill\n\treturn str.replace( /^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g, \"\" );\n};\n\n// Custom selectors\n$.extend( $.expr.pseudos || $.expr[ \":\" ], {\t\t// '|| $.expr[ \":\" ]' here enables backwards compatibility to jQuery 1.7. Can be removed when dropping jQ 1.7.x support\n\n\t// https://jqueryvalidation.org/blank-selector/\n\tblank: function( a ) {\n\t\treturn !trim( \"\" + $( a ).val() );\n\t},\n\n\t// https://jqueryvalidation.org/filled-selector/\n\tfilled: function( a ) {\n\t\tvar val = $( a ).val();\n\t\treturn val !== null && !!trim( \"\" + val );\n\t},\n\n\t// https://jqueryvalidation.org/unchecked-selector/\n\tunchecked: function( a ) {\n\t\treturn !$( a ).prop( \"checked\" );\n\t}\n} );\n\n// Constructor for validator\n$.validator = function( options, form ) {\n\tthis.settings = $.extend( true, {}, $.validator.defaults, options );\n\tthis.currentForm = form;\n\tthis.init();\n};\n\n// https://jqueryvalidation.org/jQuery.validator.format/\n$.validator.format = function( source, params ) {\n\tif ( arguments.length === 1 ) {\n\t\treturn function() {\n\t\t\tvar args = $.makeArray( arguments );\n\t\t\targs.unshift( source );\n\t\t\treturn $.validator.format.apply( this, args );\n\t\t};\n\t}\n\tif ( params === undefined ) {\n\t\treturn source;\n\t}\n\tif ( arguments.length > 2 && params.constructor !== Array ) {\n\t\tparams = $.makeArray( arguments ).slice( 1 );\n\t}\n\tif ( params.constructor !== Array ) {\n\t\tparams = [ params ];\n\t}\n\t$.each( params, function( i, n ) {\n\t\tsource = source.replace( new RegExp( \"\\\\{\" + i + \"\\\\}\", \"g\" ), function() {\n\t\t\treturn n;\n\t\t} );\n\t} );\n\treturn source;\n};\n\n$.extend( $.validator, {\n\n\tdefaults: {\n\t\tmessages: {},\n\t\tgroups: {},\n\t\trules: {},\n\t\terrorClass: \"error\",\n\t\tpendingClass: \"pending\",\n\t\tvalidClass: \"valid\",\n\t\terrorElement: \"label\",\n\t\tfocusCleanup: false,\n\t\tfocusInvalid: true,\n\t\terrorContainer: $( [] ),\n\t\terrorLabelContainer: $( [] ),\n\t\tonsubmit: true,\n\t\tignore: \":hidden\",\n\t\tignoreTitle: false,\n\t\tcustomElements: [],\n\t\tonfocusin: function( element ) {\n\t\t\tthis.lastActive = element;\n\n\t\t\t// Hide error label and remove error class on focus if enabled\n\t\t\tif ( this.settings.focusCleanup ) {\n\t\t\t\tif ( this.settings.unhighlight ) {\n\t\t\t\t\tthis.settings.unhighlight.call( this, element, this.settings.errorClass, this.settings.validClass );\n\t\t\t\t}\n\t\t\t\tthis.hideThese( this.errorsFor( element ) );\n\t\t\t}\n\t\t},\n\t\tonfocusout: function( element ) {\n\t\t\tif ( !this.checkable( element ) && ( element.name in this.submitted || !this.optional( element ) ) ) {\n\t\t\t\tthis.element( element );\n\t\t\t}\n\t\t},\n\t\tonkeyup: function( element, event ) {\n\n\t\t\t// Avoid revalidate the field when pressing one of the following keys\n\t\t\t// Shift => 16\n\t\t\t// Ctrl => 17\n\t\t\t// Alt => 18\n\t\t\t// Caps lock => 20\n\t\t\t// End => 35\n\t\t\t// Home => 36\n\t\t\t// Left arrow => 37\n\t\t\t// Up arrow => 38\n\t\t\t// Right arrow => 39\n\t\t\t// Down arrow => 40\n\t\t\t// Insert => 45\n\t\t\t// Num lock => 144\n\t\t\t// AltGr key => 225\n\t\t\tvar excludedKeys = [\n\t\t\t\t16, 17, 18, 20, 35, 36, 37,\n\t\t\t\t38, 39, 40, 45, 144, 225\n\t\t\t];\n\n\t\t\tif ( event.which === 9 && this.elementValue( element ) === \"\" || $.inArray( event.keyCode, excludedKeys ) !== -1 ) {\n\t\t\t\treturn;\n\t\t\t} else if ( element.name in this.submitted || element.name in this.invalid ) {\n\t\t\t\tthis.element( element );\n\t\t\t}\n\t\t},\n\t\tonclick: function( element ) {\n\n\t\t\t// Click on selects, radiobuttons and checkboxes\n\t\t\tif ( element.name in this.submitted ) {\n\t\t\t\tthis.element( element );\n\n\t\t\t// Or option elements, check parent select in that case\n\t\t\t} else if ( element.parentNode.name in this.submitted ) {\n\t\t\t\tthis.element( element.parentNode );\n\t\t\t}\n\t\t},\n\t\thighlight: function( element, errorClass, validClass ) {\n\t\t\tif ( element.type === \"radio\" ) {\n\t\t\t\tthis.findByName( element.name ).addClass( errorClass ).removeClass( validClass );\n\t\t\t} else {\n\t\t\t\t$( element ).addClass( errorClass ).removeClass( validClass );\n\t\t\t}\n\t\t},\n\t\tunhighlight: function( element, errorClass, validClass ) {\n\t\t\tif ( element.type === \"radio\" ) {\n\t\t\t\tthis.findByName( element.name ).removeClass( errorClass ).addClass( validClass );\n\t\t\t} else {\n\t\t\t\t$( element ).removeClass( errorClass ).addClass( validClass );\n\t\t\t}\n\t\t}\n\t},\n\n\t// https://jqueryvalidation.org/jQuery.validator.setDefaults/\n\tsetDefaults: function( settings ) {\n\t\t$.extend( $.validator.defaults, settings );\n\t},\n\n\tmessages: {\n\t\trequired: \"This field is required.\",\n\t\tremote: \"Please fix this field.\",\n\t\temail: \"Please enter a valid email address.\",\n\t\turl: \"Please enter a valid URL.\",\n\t\tdate: \"Please enter a valid date.\",\n\t\tdateISO: \"Please enter a valid date (ISO).\",\n\t\tnumber: \"Please enter a valid number.\",\n\t\tdigits: \"Please enter only digits.\",\n\t\tequalTo: \"Please enter the same value again.\",\n\t\tmaxlength: $.validator.format( \"Please enter no more than {0} characters.\" ),\n\t\tminlength: $.validator.format( \"Please enter at least {0} characters.\" ),\n\t\trangelength: $.validator.format( \"Please enter a value between {0} and {1} characters long.\" ),\n\t\trange: $.validator.format( \"Please enter a value between {0} and {1}.\" ),\n\t\tmax: $.validator.format( \"Please enter a value less than or equal to {0}.\" ),\n\t\tmin: $.validator.format( \"Please enter a value greater than or equal to {0}.\" ),\n\t\tstep: $.validator.format( \"Please enter a multiple of {0}.\" )\n\t},\n\n\tautoCreateRanges: false,\n\n\tprototype: {\n\n\t\tinit: function() {\n\t\t\tthis.labelContainer = $( this.settings.errorLabelContainer );\n\t\t\tthis.errorContext = this.labelContainer.length && this.labelContainer || $( this.currentForm );\n\t\t\tthis.containers = $( this.settings.errorContainer ).add( this.settings.errorLabelContainer );\n\t\t\tthis.submitted = {};\n\t\t\tthis.valueCache = {};\n\t\t\tthis.pendingRequest = 0;\n\t\t\tthis.pending = {};\n\t\t\tthis.invalid = {};\n\t\t\tthis.reset();\n\n\t\t\tvar currentForm = this.currentForm,\n\t\t\t\tgroups = ( this.groups = {} ),\n\t\t\t\trules;\n\t\t\t$.each( this.settings.groups, function( key, value ) {\n\t\t\t\tif ( typeof value === \"string\" ) {\n\t\t\t\t\tvalue = value.split( /\\s/ );\n\t\t\t\t}\n\t\t\t\t$.each( value, function( index, name ) {\n\t\t\t\t\tgroups[ name ] = key;\n\t\t\t\t} );\n\t\t\t} );\n\t\t\trules = this.settings.rules;\n\t\t\t$.each( rules, function( key, value ) {\n\t\t\t\trules[ key ] = $.validator.normalizeRule( value );\n\t\t\t} );\n\n\t\t\tfunction delegate( event ) {\n\t\t\t\tvar isContentEditable = typeof $( this ).attr( \"contenteditable\" ) !== \"undefined\" && $( this ).attr( \"contenteditable\" ) !== \"false\";\n\n\t\t\t\t// Set form expando on contenteditable\n\t\t\t\tif ( !this.form && isContentEditable ) {\n\t\t\t\t\tthis.form = $( this ).closest( \"form\" )[ 0 ];\n\t\t\t\t\tthis.name = $( this ).attr( \"name\" );\n\t\t\t\t}\n\n\t\t\t\t// Ignore the element if it belongs to another form. This will happen mainly\n\t\t\t\t// when setting the `form` attribute of an input to the id of another form.\n\t\t\t\tif ( currentForm !== this.form ) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tvar validator = $.data( this.form, \"validator\" ),\n\t\t\t\t\teventType = \"on\" + event.type.replace( /^validate/, \"\" ),\n\t\t\t\t\tsettings = validator.settings;\n\t\t\t\tif ( settings[ eventType ] && !$( this ).is( settings.ignore ) ) {\n\t\t\t\t\tsettings[ eventType ].call( validator, this, event );\n\t\t\t\t}\n\t\t\t}\n\t\t\tvar focusListeners = [ \":text\", \"[type='password']\", \"[type='file']\", \"select\", \"textarea\", \"[type='number']\", \"[type='search']\",\n\t\t\t\t\t\t\t\t\"[type='tel']\", \"[type='url']\", \"[type='email']\", \"[type='datetime']\", \"[type='date']\", \"[type='month']\",\n\t\t\t\t\t\t\t\t\"[type='week']\", \"[type='time']\", \"[type='datetime-local']\", \"[type='range']\", \"[type='color']\",\n\t\t\t\t\t\t\t\t\"[type='radio']\", \"[type='checkbox']\", \"[contenteditable]\", \"[type='button']\" ];\n\t\t\tvar clickListeners = [ \"select\", \"option\", \"[type='radio']\", \"[type='checkbox']\" ];\n\t\t\t$( this.currentForm )\n\t\t\t\t.on( \"focusin.validate focusout.validate keyup.validate\", focusListeners.concat( this.settings.customElements ).join( \", \" ), delegate )\n\n\t\t\t\t// Support: Chrome, oldIE\n\t\t\t\t// \"select\" is provided as event.target when clicking a option\n\t\t\t\t.on( \"click.validate\", clickListeners.concat( this.settings.customElements ).join( \", \" ), delegate );\n\n\t\t\tif ( this.settings.invalidHandler ) {\n\t\t\t\t$( this.currentForm ).on( \"invalid-form.validate\", this.settings.invalidHandler );\n\t\t\t}\n\t\t},\n\n\t\t// https://jqueryvalidation.org/Validator.form/\n\t\tform: function() {\n\t\t\tthis.checkForm();\n\t\t\t$.extend( this.submitted, this.errorMap );\n\t\t\tthis.invalid = $.extend( {}, this.errorMap );\n\t\t\tif ( !this.valid() ) {\n\t\t\t\t$( this.currentForm ).triggerHandler( \"invalid-form\", [ this ] );\n\t\t\t}\n\t\t\tthis.showErrors();\n\t\t\treturn this.valid();\n\t\t},\n\n\t\tcheckForm: function() {\n\t\t\tthis.prepareForm();\n\t\t\tfor ( var i = 0, elements = ( this.currentElements = this.elements() ); elements[ i ]; i++ ) {\n\t\t\t\tthis.check( elements[ i ] );\n\t\t\t}\n\t\t\treturn this.valid();\n\t\t},\n\n\t\t// https://jqueryvalidation.org/Validator.element/\n\t\telement: function( element ) {\n\t\t\tvar cleanElement = this.clean( element ),\n\t\t\t\tcheckElement = this.validationTargetFor( cleanElement ),\n\t\t\t\tv = this,\n\t\t\t\tresult = true,\n\t\t\t\trs, group;\n\n\t\t\tif ( checkElement === undefined ) {\n\t\t\t\tdelete this.invalid[ cleanElement.name ];\n\t\t\t} else {\n\t\t\t\tthis.prepareElement( checkElement );\n\t\t\t\tthis.currentElements = $( checkElement );\n\n\t\t\t\t// If this element is grouped, then validate all group elements already\n\t\t\t\t// containing a value\n\t\t\t\tgroup = this.groups[ checkElement.name ];\n\t\t\t\tif ( group ) {\n\t\t\t\t\t$.each( this.groups, function( name, testgroup ) {\n\t\t\t\t\t\tif ( testgroup === group && name !== checkElement.name ) {\n\t\t\t\t\t\t\tcleanElement = v.validationTargetFor( v.clean( v.findByName( name ) ) );\n\t\t\t\t\t\t\tif ( cleanElement && cleanElement.name in v.invalid ) {\n\t\t\t\t\t\t\t\tv.currentElements.push( cleanElement );\n\t\t\t\t\t\t\t\tresult = v.check( cleanElement ) && result;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} );\n\t\t\t\t}\n\n\t\t\t\trs = this.check( checkElement ) !== false;\n\t\t\t\tresult = result && rs;\n\t\t\t\tif ( rs ) {\n\t\t\t\t\tthis.invalid[ checkElement.name ] = false;\n\t\t\t\t} else {\n\t\t\t\t\tthis.invalid[ checkElement.name ] = true;\n\t\t\t\t}\n\n\t\t\t\tif ( !this.numberOfInvalids() ) {\n\n\t\t\t\t\t// Hide error containers on last error\n\t\t\t\t\tthis.toHide = this.toHide.add( this.containers );\n\t\t\t\t}\n\t\t\t\tthis.showErrors();\n\n\t\t\t\t// Add aria-invalid status for screen readers\n\t\t\t\t$( element ).attr( \"aria-invalid\", !rs );\n\t\t\t}\n\n\t\t\treturn result;\n\t\t},\n\n\t\t// https://jqueryvalidation.org/Validator.showErrors/\n\t\tshowErrors: function( errors ) {\n\t\t\tif ( errors ) {\n\t\t\t\tvar validator = this;\n\n\t\t\t\t// Add items to error list and map\n\t\t\t\t$.extend( this.errorMap, errors );\n\t\t\t\tthis.errorList = $.map( this.errorMap, function( message, name ) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tmessage: message,\n\t\t\t\t\t\telement: validator.findByName( name )[ 0 ]\n\t\t\t\t\t};\n\t\t\t\t} );\n\n\t\t\t\t// Remove items from success list\n\t\t\t\tthis.successList = $.grep( this.successList, function( element ) {\n\t\t\t\t\treturn !( element.name in errors );\n\t\t\t\t} );\n\t\t\t}\n\t\t\tif ( this.settings.showErrors ) {\n\t\t\t\tthis.settings.showErrors.call( this, this.errorMap, this.errorList );\n\t\t\t} else {\n\t\t\t\tthis.defaultShowErrors();\n\t\t\t}\n\t\t},\n\n\t\t// https://jqueryvalidation.org/Validator.resetForm/\n\t\tresetForm: function() {\n\t\t\tif ( $.fn.resetForm ) {\n\t\t\t\t$( this.currentForm ).resetForm();\n\t\t\t}\n\t\t\tthis.invalid = {};\n\t\t\tthis.submitted = {};\n\t\t\tthis.prepareForm();\n\t\t\tthis.hideErrors();\n\t\t\tvar elements = this.elements()\n\t\t\t\t.removeData( \"previousValue\" )\n\t\t\t\t.removeAttr( \"aria-invalid\" );\n\n\t\t\tthis.resetElements( elements );\n\t\t},\n\n\t\tresetElements: function( elements ) {\n\t\t\tvar i;\n\n\t\t\tif ( this.settings.unhighlight ) {\n\t\t\t\tfor ( i = 0; elements[ i ]; i++ ) {\n\t\t\t\t\tthis.settings.unhighlight.call( this, elements[ i ],\n\t\t\t\t\t\tthis.settings.errorClass, \"\" );\n\t\t\t\t\tthis.findByName( elements[ i ].name ).removeClass( this.settings.validClass );\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\telements\n\t\t\t\t\t.removeClass( this.settings.errorClass )\n\t\t\t\t\t.removeClass( this.settings.validClass );\n\t\t\t}\n\t\t},\n\n\t\tnumberOfInvalids: function() {\n\t\t\treturn this.objectLength( this.invalid );\n\t\t},\n\n\t\tobjectLength: function( obj ) {\n\t\t\t/* jshint unused: false */\n\t\t\tvar count = 0,\n\t\t\t\ti;\n\t\t\tfor ( i in obj ) {\n\n\t\t\t\t// This check allows counting elements with empty error\n\t\t\t\t// message as invalid elements\n\t\t\t\tif ( obj[ i ] !== undefined && obj[ i ] !== null && obj[ i ] !== false ) {\n\t\t\t\t\tcount++;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn count;\n\t\t},\n\n\t\thideErrors: function() {\n\t\t\tthis.hideThese( this.toHide );\n\t\t},\n\n\t\thideThese: function( errors ) {\n\t\t\terrors.not( this.containers ).text( \"\" );\n\t\t\tthis.addWrapper( errors ).hide();\n\t\t},\n\n\t\tvalid: function() {\n\t\t\treturn this.size() === 0;\n\t\t},\n\n\t\tsize: function() {\n\t\t\treturn this.errorList.length;\n\t\t},\n\n\t\tfocusInvalid: function() {\n\t\t\tif ( this.settings.focusInvalid ) {\n\t\t\t\ttry {\n\t\t\t\t\t$( this.findLastActive() || this.errorList.length && this.errorList[ 0 ].element || [] )\n\t\t\t\t\t.filter( \":visible\" )\n\t\t\t\t\t.trigger( \"focus\" )\n\n\t\t\t\t\t// Manually trigger focusin event; without it, focusin handler isn't called, findLastActive won't have anything to find\n\t\t\t\t\t.trigger( \"focusin\" );\n\t\t\t\t} catch ( e ) {\n\n\t\t\t\t\t// Ignore IE throwing errors when focusing hidden elements\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\tfindLastActive: function() {\n\t\t\tvar lastActive = this.lastActive;\n\t\t\treturn lastActive && $.grep( this.errorList, function( n ) {\n\t\t\t\treturn n.element.name === lastActive.name;\n\t\t\t} ).length === 1 && lastActive;\n\t\t},\n\n\t\telements: function() {\n\t\t\tvar validator = this,\n\t\t\t\trulesCache = {},\n\t\t\t\tselectors = [ \"input\", \"select\", \"textarea\", \"[contenteditable]\" ];\n\n\t\t\t// Select all valid inputs inside the form (no submit or reset buttons)\n\t\t\treturn $( this.currentForm )\n\t\t\t.find( selectors.concat( this.settings.customElements ).join( \", \" ) )\n\t\t\t.not( \":submit, :reset, :image, :disabled\" )\n\t\t\t.not( this.settings.ignore )\n\t\t\t.filter( function() {\n\t\t\t\tvar name = this.name || $( this ).attr( \"name\" ); // For contenteditable\n\t\t\t\tvar isContentEditable = typeof $( this ).attr( \"contenteditable\" ) !== \"undefined\" && $( this ).attr( \"contenteditable\" ) !== \"false\";\n\n\t\t\t\tif ( !name && validator.settings.debug && window.console ) {\n\t\t\t\t\tconsole.error( \"%o has no name assigned\", this );\n\t\t\t\t}\n\n\t\t\t\t// Set form expando on contenteditable\n\t\t\t\tif ( isContentEditable ) {\n\t\t\t\t\tthis.form = $( this ).closest( \"form\" )[ 0 ];\n\t\t\t\t\tthis.name = name;\n\t\t\t\t}\n\n\t\t\t\t// Ignore elements that belong to other/nested forms\n\t\t\t\tif ( this.form !== validator.currentForm ) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\t// Select only the first element for each name, and only those with rules specified\n\t\t\t\tif ( name in rulesCache || !validator.objectLength( $( this ).rules() ) ) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\trulesCache[ name ] = true;\n\t\t\t\treturn true;\n\t\t\t} );\n\t\t},\n\n\t\tclean: function( selector ) {\n\t\t\treturn $( selector )[ 0 ];\n\t\t},\n\n\t\terrors: function() {\n\t\t\tvar errorClass = this.settings.errorClass.split( \" \" ).join( \".\" );\n\t\t\treturn $( this.settings.errorElement + \".\" + errorClass, this.errorContext );\n\t\t},\n\n\t\tresetInternals: function() {\n\t\t\tthis.successList = [];\n\t\t\tthis.errorList = [];\n\t\t\tthis.errorMap = {};\n\t\t\tthis.toShow = $( [] );\n\t\t\tthis.toHide = $( [] );\n\t\t},\n\n\t\treset: function() {\n\t\t\tthis.resetInternals();\n\t\t\tthis.currentElements = $( [] );\n\t\t},\n\n\t\tprepareForm: function() {\n\t\t\tthis.reset();\n\t\t\tthis.toHide = this.errors().add( this.containers );\n\t\t},\n\n\t\tprepareElement: function( element ) {\n\t\t\tthis.reset();\n\t\t\tthis.toHide = this.errorsFor( element );\n\t\t},\n\n\t\telementValue: function( element ) {\n\t\t\tvar $element = $( element ),\n\t\t\t\ttype = element.type,\n\t\t\t\tisContentEditable = typeof $element.attr( \"contenteditable\" ) !== \"undefined\" && $element.attr( \"contenteditable\" ) !== \"false\",\n\t\t\t\tval, idx;\n\n\t\t\tif ( type === \"radio\" || type === \"checkbox\" ) {\n\t\t\t\treturn this.findByName( element.name ).filter( \":checked\" ).val();\n\t\t\t} else if ( type === \"number\" && typeof element.validity !== \"undefined\" ) {\n\t\t\t\treturn element.validity.badInput ? \"NaN\" : $element.val();\n\t\t\t}\n\n\t\t\tif ( isContentEditable ) {\n\t\t\t\tval = $element.text();\n\t\t\t} else {\n\t\t\t\tval = $element.val();\n\t\t\t}\n\n\t\t\tif ( type === \"file\" ) {\n\n\t\t\t\t// Modern browser (chrome & safari)\n\t\t\t\tif ( val.substr( 0, 12 ) === \"C:\\\\fakepath\\\\\" ) {\n\t\t\t\t\treturn val.substr( 12 );\n\t\t\t\t}\n\n\t\t\t\t// Legacy browsers\n\t\t\t\t// Unix-based path\n\t\t\t\tidx = val.lastIndexOf( \"/\" );\n\t\t\t\tif ( idx >= 0 ) {\n\t\t\t\t\treturn val.substr( idx + 1 );\n\t\t\t\t}\n\n\t\t\t\t// Windows-based path\n\t\t\t\tidx = val.lastIndexOf( \"\\\\\" );\n\t\t\t\tif ( idx >= 0 ) {\n\t\t\t\t\treturn val.substr( idx + 1 );\n\t\t\t\t}\n\n\t\t\t\t// Just the file name\n\t\t\t\treturn val;\n\t\t\t}\n\n\t\t\tif ( typeof val === \"string\" ) {\n\t\t\t\treturn val.replace( /\\r/g, \"\" );\n\t\t\t}\n\t\t\treturn val;\n\t\t},\n\n\t\tcheck: function( element ) {\n\t\t\telement = this.validationTargetFor( this.clean( element ) );\n\n\t\t\tvar rules = $( element ).rules(),\n\t\t\t\trulesCount = $.map( rules, function( n, i ) {\n\t\t\t\t\treturn i;\n\t\t\t\t} ).length,\n\t\t\t\tdependencyMismatch = false,\n\t\t\t\tval = this.elementValue( element ),\n\t\t\t\tresult, method, rule, normalizer;\n\n\t\t\t// Abort any pending Ajax request from a previous call to this method.\n\t\t\tthis.abortRequest( element );\n\n\t\t\t// Prioritize the local normalizer defined for this element over the global one\n\t\t\t// if the former exists, otherwise user the global one in case it exists.\n\t\t\tif ( typeof rules.normalizer === \"function\" ) {\n\t\t\t\tnormalizer = rules.normalizer;\n\t\t\t} else if (\ttypeof this.settings.normalizer === \"function\" ) {\n\t\t\t\tnormalizer = this.settings.normalizer;\n\t\t\t}\n\n\t\t\t// If normalizer is defined, then call it to retreive the changed value instead\n\t\t\t// of using the real one.\n\t\t\t// Note that `this` in the normalizer is `element`.\n\t\t\tif ( normalizer ) {\n\t\t\t\tval = normalizer.call( element, val );\n\n\t\t\t\t// Delete the normalizer from rules to avoid treating it as a pre-defined method.\n\t\t\t\tdelete rules.normalizer;\n\t\t\t}\n\n\t\t\tfor ( method in rules ) {\n\t\t\t\trule = { method: method, parameters: rules[ method ] };\n\t\t\t\ttry {\n\t\t\t\t\tresult = $.validator.methods[ method ].call( this, val, element, rule.parameters );\n\n\t\t\t\t\t// If a method indicates that the field is optional and therefore valid,\n\t\t\t\t\t// don't mark it as valid when there are no other rules\n\t\t\t\t\tif ( result === \"dependency-mismatch\" && rulesCount === 1 ) {\n\t\t\t\t\t\tdependencyMismatch = true;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tdependencyMismatch = false;\n\n\t\t\t\t\tif ( result === \"pending\" ) {\n\t\t\t\t\t\tthis.toHide = this.toHide.not( this.errorsFor( element ) );\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( !result ) {\n\t\t\t\t\t\tthis.formatAndAdd( element, rule );\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t} catch ( e ) {\n\t\t\t\t\tif ( this.settings.debug && window.console ) {\n\t\t\t\t\t\tconsole.log( \"Exception occurred when checking element \" + element.id + \", check the '\" + rule.method + \"' method.\", e );\n\t\t\t\t\t}\n\t\t\t\t\tif ( e instanceof TypeError ) {\n\t\t\t\t\t\te.message += \". Exception occurred when checking element \" + element.id + \", check the '\" + rule.method + \"' method.\";\n\t\t\t\t\t}\n\n\t\t\t\t\tthrow e;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif ( dependencyMismatch ) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif ( this.objectLength( rules ) ) {\n\t\t\t\tthis.successList.push( element );\n\t\t\t}\n\t\t\treturn true;\n\t\t},\n\n\t\t// Return the custom message for the given element and validation method\n\t\t// specified in the element's HTML5 data attribute\n\t\t// return the generic message if present and no method specific message is present\n\t\tcustomDataMessage: function( element, method ) {\n\t\t\treturn $( element ).data( \"msg\" + method.charAt( 0 ).toUpperCase() +\n\t\t\t\tmethod.substring( 1 ).toLowerCase() ) || $( element ).data( \"msg\" );\n\t\t},\n\n\t\t// Return the custom message for the given element name and validation method\n\t\tcustomMessage: function( name, method ) {\n\t\t\tvar m = this.settings.messages[ name ];\n\t\t\treturn m && ( m.constructor === String ? m : m[ method ] );\n\t\t},\n\n\t\t// Return the first defined argument, allowing empty strings\n\t\tfindDefined: function() {\n\t\t\tfor ( var i = 0; i < arguments.length; i++ ) {\n\t\t\t\tif ( arguments[ i ] !== undefined ) {\n\t\t\t\t\treturn arguments[ i ];\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn undefined;\n\t\t},\n\n\t\t// The second parameter 'rule' used to be a string, and extended to an object literal\n\t\t// of the following form:\n\t\t// rule = {\n\t\t// method: \"method name\",\n\t\t// parameters: \"the given method parameters\"\n\t\t// }\n\t\t//\n\t\t// The old behavior still supported, kept to maintain backward compatibility with\n\t\t// old code, and will be removed in the next major release.\n\t\tdefaultMessage: function( element, rule ) {\n\t\t\tif ( typeof rule === \"string\" ) {\n\t\t\t\trule = { method: rule };\n\t\t\t}\n\n\t\t\tvar message = this.findDefined(\n\t\t\t\t\tthis.customMessage( element.name, rule.method ),\n\t\t\t\t\tthis.customDataMessage( element, rule.method ),\n\n\t\t\t\t\t// 'title' is never undefined, so handle empty string as undefined\n\t\t\t\t\t!this.settings.ignoreTitle && element.title || undefined,\n\t\t\t\t\t$.validator.messages[ rule.method ],\n\t\t\t\t\t\"Warning: No message defined for \" + element.name + \"\"\n\t\t\t\t),\n\t\t\t\ttheregex = /\\$?\\{(\\d+)\\}/g;\n\t\t\tif ( typeof message === \"function\" ) {\n\t\t\t\tmessage = message.call( this, rule.parameters, element );\n\t\t\t} else if ( theregex.test( message ) ) {\n\t\t\t\tmessage = $.validator.format( message.replace( theregex, \"{$1}\" ), rule.parameters );\n\t\t\t}\n\n\t\t\treturn message;\n\t\t},\n\n\t\tformatAndAdd: function( element, rule ) {\n\t\t\tvar message = this.defaultMessage( element, rule );\n\n\t\t\tthis.errorList.push( {\n\t\t\t\tmessage: message,\n\t\t\t\telement: element,\n\t\t\t\tmethod: rule.method\n\t\t\t} );\n\n\t\t\tthis.errorMap[ element.name ] = message;\n\t\t\tthis.submitted[ element.name ] = message;\n\t\t},\n\n\t\taddWrapper: function( toToggle ) {\n\t\t\tif ( this.settings.wrapper ) {\n\t\t\t\ttoToggle = toToggle.add( toToggle.parent( this.settings.wrapper ) );\n\t\t\t}\n\t\t\treturn toToggle;\n\t\t},\n\n\t\tdefaultShowErrors: function() {\n\t\t\tvar i, elements, error;\n\t\t\tfor ( i = 0; this.errorList[ i ]; i++ ) {\n\t\t\t\terror = this.errorList[ i ];\n\t\t\t\tif ( this.settings.highlight ) {\n\t\t\t\t\tthis.settings.highlight.call( this, error.element, this.settings.errorClass, this.settings.validClass );\n\t\t\t\t}\n\t\t\t\tthis.showLabel( error.element, error.message );\n\t\t\t}\n\t\t\tif ( this.errorList.length ) {\n\t\t\t\tthis.toShow = this.toShow.add( this.containers );\n\t\t\t}\n\t\t\tif ( this.settings.success ) {\n\t\t\t\tfor ( i = 0; this.successList[ i ]; i++ ) {\n\t\t\t\t\tthis.showLabel( this.successList[ i ] );\n\t\t\t\t}\n\t\t\t}\n\t\t\tif ( this.settings.unhighlight ) {\n\t\t\t\tfor ( i = 0, elements = this.validElements(); elements[ i ]; i++ ) {\n\t\t\t\t\tthis.settings.unhighlight.call( this, elements[ i ], this.settings.errorClass, this.settings.validClass );\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.toHide = this.toHide.not( this.toShow );\n\t\t\tthis.hideErrors();\n\t\t\tthis.addWrapper( this.toShow ).show();\n\t\t},\n\n\t\tvalidElements: function() {\n\t\t\treturn this.currentElements.not( this.invalidElements() );\n\t\t},\n\n\t\tinvalidElements: function() {\n\t\t\treturn $( this.errorList ).map( function() {\n\t\t\t\treturn this.element;\n\t\t\t} );\n\t\t},\n\n\t\tshowLabel: function( element, message ) {\n\t\t\tvar place, group, errorID, v,\n\t\t\t\terror = this.errorsFor( element ),\n\t\t\t\telementID = this.idOrName( element ),\n\t\t\t\tdescribedBy = $( element ).attr( \"aria-describedby\" );\n\n\t\t\tif ( error.length ) {\n\n\t\t\t\t// Refresh error/success class\n\t\t\t\terror.removeClass( this.settings.validClass ).addClass( this.settings.errorClass );\n\n\t\t\t\t// Replace message on existing label\n\t\t\t\tif ( this.settings && this.settings.escapeHtml ) {\n\t\t\t\t\terror.text( message || \"\" );\n\t\t\t\t} else {\n\t\t\t\t\terror.html( message || \"\" );\n\t\t\t\t}\n\t\t\t} else {\n\n\t\t\t\t// Create error element\n\t\t\t\terror = $( \"<\" + this.settings.errorElement + \">\" )\n\t\t\t\t\t.attr( \"id\", elementID + \"-error\" )\n\t\t\t\t\t.addClass( this.settings.errorClass );\n\n\t\t\t\tif ( this.settings && this.settings.escapeHtml ) {\n\t\t\t\t\terror.text( message || \"\" );\n\t\t\t\t} else {\n\t\t\t\t\terror.html( message || \"\" );\n\t\t\t\t}\n\n\t\t\t\t// Maintain reference to the element to be placed into the DOM\n\t\t\t\tplace = error;\n\t\t\t\tif ( this.settings.wrapper ) {\n\n\t\t\t\t\t// Make sure the element is visible, even in IE\n\t\t\t\t\t// actually showing the wrapped element is handled elsewhere\n\t\t\t\t\tplace = error.hide().show().wrap( \"<\" + this.settings.wrapper + \"/>\" ).parent();\n\t\t\t\t}\n\t\t\t\tif ( this.labelContainer.length ) {\n\t\t\t\t\tthis.labelContainer.append( place );\n\t\t\t\t} else if ( this.settings.errorPlacement ) {\n\t\t\t\t\tthis.settings.errorPlacement.call( this, place, $( element ) );\n\t\t\t\t} else {\n\t\t\t\t\tplace.insertAfter( element );\n\t\t\t\t}\n\n\t\t\t\t// Link error back to the element\n\t\t\t\tif ( error.is( \"label\" ) ) {\n\n\t\t\t\t\t// If the error is a label, then associate using 'for'\n\t\t\t\t\terror.attr( \"for\", elementID );\n\n\t\t\t\t\t// If the element is not a child of an associated label, then it's necessary\n\t\t\t\t\t// to explicitly apply aria-describedby\n\t\t\t\t} else if ( error.parents( \"label[for='\" + this.escapeCssMeta( elementID ) + \"']\" ).length === 0 ) {\n\t\t\t\t\terrorID = error.attr( \"id\" );\n\n\t\t\t\t\t// Respect existing non-error aria-describedby\n\t\t\t\t\tif ( !describedBy ) {\n\t\t\t\t\t\tdescribedBy = errorID;\n\t\t\t\t\t} else if ( !describedBy.match( new RegExp( \"\\\\b\" + this.escapeCssMeta( errorID ) + \"\\\\b\" ) ) ) {\n\n\t\t\t\t\t\t// Add to end of list if not already present\n\t\t\t\t\t\tdescribedBy += \" \" + errorID;\n\t\t\t\t\t}\n\t\t\t\t\t$( element ).attr( \"aria-describedby\", describedBy );\n\n\t\t\t\t\t// If this element is grouped, then assign to all elements in the same group\n\t\t\t\t\tgroup = this.groups[ element.name ];\n\t\t\t\t\tif ( group ) {\n\t\t\t\t\t\tv = this;\n\t\t\t\t\t\t$.each( v.groups, function( name, testgroup ) {\n\t\t\t\t\t\t\tif ( testgroup === group ) {\n\t\t\t\t\t\t\t\t$( \"[name='\" + v.escapeCssMeta( name ) + \"']\", v.currentForm )\n\t\t\t\t\t\t\t\t\t.attr( \"aria-describedby\", error.attr( \"id\" ) );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif ( !message && this.settings.success ) {\n\t\t\t\terror.text( \"\" );\n\t\t\t\tif ( typeof this.settings.success === \"string\" ) {\n\t\t\t\t\terror.addClass( this.settings.success );\n\t\t\t\t} else {\n\t\t\t\t\tthis.settings.success( error, element );\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.toShow = this.toShow.add( error );\n\t\t},\n\n\t\terrorsFor: function( element ) {\n\t\t\tvar name = this.escapeCssMeta( this.idOrName( element ) ),\n\t\t\t\tdescriber = $( element ).attr( \"aria-describedby\" ),\n\t\t\t\tselector = \"label[for='\" + name + \"'], label[for='\" + name + \"'] *\";\n\n\t\t\t// 'aria-describedby' should directly reference the error element\n\t\t\tif ( describer ) {\n\t\t\t\tselector = selector + \", #\" + this.escapeCssMeta( describer )\n\t\t\t\t\t.replace( /\\s+/g, \", #\" );\n\t\t\t}\n\n\t\t\treturn this\n\t\t\t\t.errors()\n\t\t\t\t.filter( selector );\n\t\t},\n\n\t\t// See https://api.jquery.com/category/selectors/, for CSS\n\t\t// meta-characters that should be escaped in order to be used with JQuery\n\t\t// as a literal part of a name/id or any selector.\n\t\tescapeCssMeta: function( string ) {\n\t\t\tif ( string === undefined ) {\n\t\t\t\treturn \"\";\n\t\t\t}\n\n\t\t\treturn string.replace( /([\\\\!\"#$%&'()*+,./:;<=>?@\\[\\]^`{|}~])/g, \"\\\\$1\" );\n\t\t},\n\n\t\tidOrName: function( element ) {\n\t\t\treturn this.groups[ element.name ] || ( this.checkable( element ) ? element.name : element.id || element.name );\n\t\t},\n\n\t\tvalidationTargetFor: function( element ) {\n\n\t\t\t// If radio/checkbox, validate first element in group instead\n\t\t\tif ( this.checkable( element ) ) {\n\t\t\t\telement = this.findByName( element.name );\n\t\t\t}\n\n\t\t\t// Always apply ignore filter\n\t\t\treturn $( element ).not( this.settings.ignore )[ 0 ];\n\t\t},\n\n\t\tcheckable: function( element ) {\n\t\t\treturn ( /radio|checkbox/i ).test( element.type );\n\t\t},\n\n\t\tfindByName: function( name ) {\n\t\t\treturn $( this.currentForm ).find( \"[name='\" + this.escapeCssMeta( name ) + \"']\" );\n\t\t},\n\n\t\tgetLength: function( value, element ) {\n\t\t\tswitch ( element.nodeName.toLowerCase() ) {\n\t\t\tcase \"select\":\n\t\t\t\treturn $( \"option:selected\", element ).length;\n\t\t\tcase \"input\":\n\t\t\t\tif ( this.checkable( element ) ) {\n\t\t\t\t\treturn this.findByName( element.name ).filter( \":checked\" ).length;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn value.length;\n\t\t},\n\n\t\tdepend: function( param, element ) {\n\t\t\treturn this.dependTypes[ typeof param ] ? this.dependTypes[ typeof param ]( param, element ) : true;\n\t\t},\n\n\t\tdependTypes: {\n\t\t\t\"boolean\": function( param ) {\n\t\t\t\treturn param;\n\t\t\t},\n\t\t\t\"string\": function( param, element ) {\n\t\t\t\treturn !!$( param, element.form ).length;\n\t\t\t},\n\t\t\t\"function\": function( param, element ) {\n\t\t\t\treturn param( element );\n\t\t\t}\n\t\t},\n\n\t\toptional: function( element ) {\n\t\t\tvar val = this.elementValue( element );\n\t\t\treturn !$.validator.methods.required.call( this, val, element ) && \"dependency-mismatch\";\n\t\t},\n\n\t\telementAjaxPort: function( element ) {\n\t\t\treturn \"validate\" + element.name;\n\t\t},\n\n\t\tstartRequest: function( element ) {\n\t\t\tif ( !this.pending[ element.name ] ) {\n\t\t\t\tthis.pendingRequest++;\n\t\t\t\t$( element ).addClass( this.settings.pendingClass );\n\t\t\t\tthis.pending[ element.name ] = true;\n\t\t\t}\n\t\t},\n\n\t\tstopRequest: function( element, valid ) {\n\t\t\tthis.pendingRequest--;\n\n\t\t\t// Sometimes synchronization fails, make sure pendingRequest is never < 0\n\t\t\tif ( this.pendingRequest < 0 ) {\n\t\t\t\tthis.pendingRequest = 0;\n\t\t\t}\n\t\t\tdelete this.pending[ element.name ];\n\t\t\t$( element ).removeClass( this.settings.pendingClass );\n\t\t\tif ( valid && this.pendingRequest === 0 && this.formSubmitted && this.form() && this.pendingRequest === 0 ) {\n\t\t\t\t$( this.currentForm ).trigger( \"submit\" );\n\n\t\t\t\t// Remove the hidden input that was used as a replacement for the\n\t\t\t\t// missing submit button. The hidden input is added by `handle()`\n\t\t\t\t// to ensure that the value of the used submit button is passed on\n\t\t\t\t// for scripted submits triggered by this method\n\t\t\t\tif ( this.submitButton ) {\n\t\t\t\t\t$( \"input:hidden[name='\" + this.submitButton.name + \"']\", this.currentForm ).remove();\n\t\t\t\t}\n\n\t\t\t\tthis.formSubmitted = false;\n\t\t\t} else if ( !valid && this.pendingRequest === 0 && this.formSubmitted ) {\n\t\t\t\t$( this.currentForm ).triggerHandler( \"invalid-form\", [ this ] );\n\t\t\t\tthis.formSubmitted = false;\n\t\t\t}\n\t\t},\n\n\t\tabortRequest: function( element ) {\n\t\t\tvar port;\n\n\t\t\tif ( this.pending[ element.name ] ) {\n\t\t\t\tport = this.elementAjaxPort( element );\n\t\t\t\t$.ajaxAbort( port );\n\n\t\t\t\tthis.pendingRequest--;\n\n\t\t\t\t// Sometimes synchronization fails, make sure pendingRequest is never < 0\n\t\t\t\tif ( this.pendingRequest < 0 ) {\n\t\t\t\t\tthis.pendingRequest = 0;\n\t\t\t\t}\n\n\t\t\t\tdelete this.pending[ element.name ];\n\t\t\t\t$( element ).removeClass( this.settings.pendingClass );\n\t\t\t}\n\t\t},\n\n\t\tpreviousValue: function( element, method ) {\n\t\t\tmethod = typeof method === \"string\" && method || \"remote\";\n\n\t\t\treturn $.data( element, \"previousValue\" ) || $.data( element, \"previousValue\", {\n\t\t\t\told: null,\n\t\t\t\tvalid: true,\n\t\t\t\tmessage: this.defaultMessage( element, { method: method } )\n\t\t\t} );\n\t\t},\n\n\t\t// Cleans up all forms and elements, removes validator-specific events\n\t\tdestroy: function() {\n\t\t\tthis.resetForm();\n\n\t\t\t$( this.currentForm )\n\t\t\t\t.off( \".validate\" )\n\t\t\t\t.removeData( \"validator\" )\n\t\t\t\t.find( \".validate-equalTo-blur\" )\n\t\t\t\t\t.off( \".validate-equalTo\" )\n\t\t\t\t\t.removeClass( \"validate-equalTo-blur\" )\n\t\t\t\t.find( \".validate-lessThan-blur\" )\n\t\t\t\t\t.off( \".validate-lessThan\" )\n\t\t\t\t\t.removeClass( \"validate-lessThan-blur\" )\n\t\t\t\t.find( \".validate-lessThanEqual-blur\" )\n\t\t\t\t\t.off( \".validate-lessThanEqual\" )\n\t\t\t\t\t.removeClass( \"validate-lessThanEqual-blur\" )\n\t\t\t\t.find( \".validate-greaterThanEqual-blur\" )\n\t\t\t\t\t.off( \".validate-greaterThanEqual\" )\n\t\t\t\t\t.removeClass( \"validate-greaterThanEqual-blur\" )\n\t\t\t\t.find( \".validate-greaterThan-blur\" )\n\t\t\t\t\t.off( \".validate-greaterThan\" )\n\t\t\t\t\t.removeClass( \"validate-greaterThan-blur\" );\n\t\t}\n\n\t},\n\n\tclassRuleSettings: {\n\t\trequired: { required: true },\n\t\temail: { email: true },\n\t\turl: { url: true },\n\t\tdate: { date: true },\n\t\tdateISO: { dateISO: true },\n\t\tnumber: { number: true },\n\t\tdigits: { digits: true },\n\t\tcreditcard: { creditcard: true }\n\t},\n\n\taddClassRules: function( className, rules ) {\n\t\tif ( className.constructor === String ) {\n\t\t\tthis.classRuleSettings[ className ] = rules;\n\t\t} else {\n\t\t\t$.extend( this.classRuleSettings, className );\n\t\t}\n\t},\n\n\tclassRules: function( element ) {\n\t\tvar rules = {},\n\t\t\tclasses = $( element ).attr( \"class\" );\n\n\t\tif ( classes ) {\n\t\t\t$.each( classes.split( \" \" ), function() {\n\t\t\t\tif ( this in $.validator.classRuleSettings ) {\n\t\t\t\t\t$.extend( rules, $.validator.classRuleSettings[ this ] );\n\t\t\t\t}\n\t\t\t} );\n\t\t}\n\t\treturn rules;\n\t},\n\n\tnormalizeAttributeRule: function( rules, type, method, value ) {\n\n\t\t// Convert the value to a number for number inputs, and for text for backwards compability\n\t\t// allows type=\"date\" and others to be compared as strings\n\t\tif ( /min|max|step/.test( method ) && ( type === null || /number|range|text/.test( type ) ) ) {\n\t\t\tvalue = Number( value );\n\n\t\t\t// Support Opera Mini, which returns NaN for undefined minlength\n\t\t\tif ( isNaN( value ) ) {\n\t\t\t\tvalue = undefined;\n\t\t\t}\n\t\t}\n\n\t\tif ( value || value === 0 ) {\n\t\t\trules[ method ] = value;\n\t\t} else if ( type === method && type !== \"range\" ) {\n\n\t\t\t// Exception: the jquery validate 'range' method\n\t\t\t// does not test for the html5 'range' type\n\t\t\trules[ type === \"date\" ? \"dateISO\" : method ] = true;\n\t\t}\n\t},\n\n\tattributeRules: function( element ) {\n\t\tvar rules = {},\n\t\t\t$element = $( element ),\n\t\t\ttype = element.getAttribute( \"type\" ),\n\t\t\tmethod, value;\n\n\t\tfor ( method in $.validator.methods ) {\n\n\t\t\t// Support for in both html5 and older browsers\n\t\t\tif ( method === \"required\" ) {\n\t\t\t\tvalue = element.getAttribute( method );\n\n\t\t\t\t// Some browsers return an empty string for the required attribute\n\t\t\t\t// and non-HTML5 browsers might have required=\"\" markup\n\t\t\t\tif ( value === \"\" ) {\n\t\t\t\t\tvalue = true;\n\t\t\t\t}\n\n\t\t\t\t// Force non-HTML5 browsers to return bool\n\t\t\t\tvalue = !!value;\n\t\t\t} else {\n\t\t\t\tvalue = $element.attr( method );\n\t\t\t}\n\n\t\t\tthis.normalizeAttributeRule( rules, type, method, value );\n\t\t}\n\n\t\t// 'maxlength' may be returned as -1, 2147483647 ( IE ) and 524288 ( safari ) for text inputs\n\t\tif ( rules.maxlength && /-1|2147483647|524288/.test( rules.maxlength ) ) {\n\t\t\tdelete rules.maxlength;\n\t\t}\n\n\t\treturn rules;\n\t},\n\n\tdataRules: function( element ) {\n\t\tvar rules = {},\n\t\t\t$element = $( element ),\n\t\t\ttype = element.getAttribute( \"type\" ),\n\t\t\tmethod, value;\n\n\t\tfor ( method in $.validator.methods ) {\n\t\t\tvalue = $element.data( \"rule\" + method.charAt( 0 ).toUpperCase() + method.substring( 1 ).toLowerCase() );\n\n\t\t\t// Cast empty attributes like `data-rule-required` to `true`\n\t\t\tif ( value === \"\" ) {\n\t\t\t\tvalue = true;\n\t\t\t}\n\n\t\t\tthis.normalizeAttributeRule( rules, type, method, value );\n\t\t}\n\t\treturn rules;\n\t},\n\n\tstaticRules: function( element ) {\n\t\tvar rules = {},\n\t\t\tvalidator = $.data( element.form, \"validator\" );\n\n\t\tif ( validator.settings.rules ) {\n\t\t\trules = $.validator.normalizeRule( validator.settings.rules[ element.name ] ) || {};\n\t\t}\n\t\treturn rules;\n\t},\n\n\tnormalizeRules: function( rules, element ) {\n\n\t\t// Handle dependency check\n\t\t$.each( rules, function( prop, val ) {\n\n\t\t\t// Ignore rule when param is explicitly false, eg. required:false\n\t\t\tif ( val === false ) {\n\t\t\t\tdelete rules[ prop ];\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif ( val.param || val.depends ) {\n\t\t\t\tvar keepRule = true;\n\t\t\t\tswitch ( typeof val.depends ) {\n\t\t\t\tcase \"string\":\n\t\t\t\t\tkeepRule = !!$( val.depends, element.form ).length;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"function\":\n\t\t\t\t\tkeepRule = val.depends.call( element, element );\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif ( keepRule ) {\n\t\t\t\t\trules[ prop ] = val.param !== undefined ? val.param : true;\n\t\t\t\t} else {\n\t\t\t\t\t$.data( element.form, \"validator\" ).resetElements( $( element ) );\n\t\t\t\t\tdelete rules[ prop ];\n\t\t\t\t}\n\t\t\t}\n\t\t} );\n\n\t\t// Evaluate parameters\n\t\t$.each( rules, function( rule, parameter ) {\n\t\t\trules[ rule ] = typeof parameter === \"function\" && rule !== \"normalizer\" ? parameter( element ) : parameter;\n\t\t} );\n\n\t\t// Clean number parameters\n\t\t$.each( [ \"minlength\", \"maxlength\" ], function() {\n\t\t\tif ( rules[ this ] ) {\n\t\t\t\trules[ this ] = Number( rules[ this ] );\n\t\t\t}\n\t\t} );\n\t\t$.each( [ \"rangelength\", \"range\" ], function() {\n\t\t\tvar parts;\n\t\t\tif ( rules[ this ] ) {\n\t\t\t\tif ( Array.isArray( rules[ this ] ) ) {\n\t\t\t\t\trules[ this ] = [ Number( rules[ this ][ 0 ] ), Number( rules[ this ][ 1 ] ) ];\n\t\t\t\t} else if ( typeof rules[ this ] === \"string\" ) {\n\t\t\t\t\tparts = rules[ this ].replace( /[\\[\\]]/g, \"\" ).split( /[\\s,]+/ );\n\t\t\t\t\trules[ this ] = [ Number( parts[ 0 ] ), Number( parts[ 1 ] ) ];\n\t\t\t\t}\n\t\t\t}\n\t\t} );\n\n\t\tif ( $.validator.autoCreateRanges ) {\n\n\t\t\t// Auto-create ranges\n\t\t\tif ( rules.min != null && rules.max != null ) {\n\t\t\t\trules.range = [ rules.min, rules.max ];\n\t\t\t\tdelete rules.min;\n\t\t\t\tdelete rules.max;\n\t\t\t}\n\t\t\tif ( rules.minlength != null && rules.maxlength != null ) {\n\t\t\t\trules.rangelength = [ rules.minlength, rules.maxlength ];\n\t\t\t\tdelete rules.minlength;\n\t\t\t\tdelete rules.maxlength;\n\t\t\t}\n\t\t}\n\n\t\treturn rules;\n\t},\n\n\t// Converts a simple string to a {string: true} rule, e.g., \"required\" to {required:true}\n\tnormalizeRule: function( data ) {\n\t\tif ( typeof data === \"string\" ) {\n\t\t\tvar transformed = {};\n\t\t\t$.each( data.split( /\\s/ ), function() {\n\t\t\t\ttransformed[ this ] = true;\n\t\t\t} );\n\t\t\tdata = transformed;\n\t\t}\n\t\treturn data;\n\t},\n\n\t// https://jqueryvalidation.org/jQuery.validator.addMethod/\n\taddMethod: function( name, method, message ) {\n\t\t$.validator.methods[ name ] = method;\n\t\t$.validator.messages[ name ] = message !== undefined ? message : $.validator.messages[ name ];\n\t\tif ( method.length < 3 ) {\n\t\t\t$.validator.addClassRules( name, $.validator.normalizeRule( name ) );\n\t\t}\n\t},\n\n\t// https://jqueryvalidation.org/jQuery.validator.methods/\n\tmethods: {\n\n\t\t// https://jqueryvalidation.org/required-method/\n\t\trequired: function( value, element, param ) {\n\n\t\t\t// Check if dependency is met\n\t\t\tif ( !this.depend( param, element ) ) {\n\t\t\t\treturn \"dependency-mismatch\";\n\t\t\t}\n\t\t\tif ( element.nodeName.toLowerCase() === \"select\" ) {\n\n\t\t\t\t// Could be an array for select-multiple or a string, both are fine this way\n\t\t\t\tvar val = $( element ).val();\n\t\t\t\treturn val && val.length > 0;\n\t\t\t}\n\t\t\tif ( this.checkable( element ) ) {\n\t\t\t\treturn this.getLength( value, element ) > 0;\n\t\t\t}\n\t\t\treturn value !== undefined && value !== null && value.length > 0;\n\t\t},\n\n\t\t// https://jqueryvalidation.org/email-method/\n\t\temail: function( value, element ) {\n\n\t\t\t// From https://html.spec.whatwg.org/multipage/forms.html#valid-e-mail-address\n\t\t\t// Retrieved 2014-01-14\n\t\t\t// If you have a problem with this implementation, report a bug against the above spec\n\t\t\t// Or use custom methods to implement your own email validation\n\t\t\treturn this.optional( element ) || /^[a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test( value );\n\t\t},\n\n\t\t// https://jqueryvalidation.org/url-method/\n\t\turl: function( value, element ) {\n\n\t\t\t// Copyright (c) 2010-2013 Diego Perini, MIT licensed\n\t\t\t// https://gist.github.com/dperini/729294\n\t\t\t// see also https://mathiasbynens.be/demo/url-regex\n\t\t\t// modified to allow protocol-relative URLs\n\t\t\treturn this.optional( element ) || /^(?:(?:(?:https?|ftp):)?\\/\\/)(?:(?:[^\\]\\[?\\/<~#`!@$^&*()+=}|:\";',>{ ]|%[0-9A-Fa-f]{2})+(?::(?:[^\\]\\[?\\/<~#`!@$^&*()+=}|:\";',>{ ]|%[0-9A-Fa-f]{2})*)?@)?(?:(?!(?:10|127)(?:\\.\\d{1,3}){3})(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z0-9\\u00a1-\\uffff][a-z0-9\\u00a1-\\uffff_-]{0,62})?[a-z0-9\\u00a1-\\uffff]\\.)+(?:[a-z\\u00a1-\\uffff]{2,}\\.?))(?::\\d{2,5})?(?:[/?#]\\S*)?$/i.test( value );\n\t\t},\n\n\t\t// https://jqueryvalidation.org/date-method/\n\t\tdate: ( function() {\n\t\t\tvar called = false;\n\n\t\t\treturn function( value, element ) {\n\t\t\t\tif ( !called ) {\n\t\t\t\t\tcalled = true;\n\t\t\t\t\tif ( this.settings.debug && window.console ) {\n\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t\"The `date` method is deprecated and will be removed in version '2.0.0'.\\n\" +\n\t\t\t\t\t\t\t\"Please don't use it, since it relies on the Date constructor, which\\n\" +\n\t\t\t\t\t\t\t\"behaves very differently across browsers and locales. Use `dateISO`\\n\" +\n\t\t\t\t\t\t\t\"instead or one of the locale specific methods in `localizations/`\\n\" +\n\t\t\t\t\t\t\t\"and `additional-methods.js`.\"\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn this.optional( element ) || !/Invalid|NaN/.test( new Date( value ).toString() );\n\t\t\t};\n\t\t}() ),\n\n\t\t// https://jqueryvalidation.org/dateISO-method/\n\t\tdateISO: function( value, element ) {\n\t\t\treturn this.optional( element ) || /^\\d{4}[\\/\\-](0?[1-9]|1[012])[\\/\\-](0?[1-9]|[12][0-9]|3[01])$/.test( value );\n\t\t},\n\n\t\t// https://jqueryvalidation.org/number-method/\n\t\tnumber: function( value, element ) {\n\t\t\treturn this.optional( element ) || /^(?:-?\\d+|-?\\d{1,3}(?:,\\d{3})+)?(?:-?\\.\\d+)?$/.test( value );\n\t\t},\n\n\t\t// https://jqueryvalidation.org/digits-method/\n\t\tdigits: function( value, element ) {\n\t\t\treturn this.optional( element ) || /^\\d+$/.test( value );\n\t\t},\n\n\t\t// https://jqueryvalidation.org/minlength-method/\n\t\tminlength: function( value, element, param ) {\n\t\t\tvar length = Array.isArray( value ) ? value.length : this.getLength( value, element );\n\t\t\treturn this.optional( element ) || length >= param;\n\t\t},\n\n\t\t// https://jqueryvalidation.org/maxlength-method/\n\t\tmaxlength: function( value, element, param ) {\n\t\t\tvar length = Array.isArray( value ) ? value.length : this.getLength( value, element );\n\t\t\treturn this.optional( element ) || length <= param;\n\t\t},\n\n\t\t// https://jqueryvalidation.org/rangelength-method/\n\t\trangelength: function( value, element, param ) {\n\t\t\tvar length = Array.isArray( value ) ? value.length : this.getLength( value, element );\n\t\t\treturn this.optional( element ) || ( length >= param[ 0 ] && length <= param[ 1 ] );\n\t\t},\n\n\t\t// https://jqueryvalidation.org/min-method/\n\t\tmin: function( value, element, param ) {\n\t\t\treturn this.optional( element ) || value >= param;\n\t\t},\n\n\t\t// https://jqueryvalidation.org/max-method/\n\t\tmax: function( value, element, param ) {\n\t\t\treturn this.optional( element ) || value <= param;\n\t\t},\n\n\t\t// https://jqueryvalidation.org/range-method/\n\t\trange: function( value, element, param ) {\n\t\t\treturn this.optional( element ) || ( value >= param[ 0 ] && value <= param[ 1 ] );\n\t\t},\n\n\t\t// https://jqueryvalidation.org/step-method/\n\t\tstep: function( value, element, param ) {\n\t\t\tvar type = $( element ).attr( \"type\" ),\n\t\t\t\terrorMessage = \"Step attribute on input type \" + type + \" is not supported.\",\n\t\t\t\tsupportedTypes = [ \"text\", \"number\", \"range\" ],\n\t\t\t\tre = new RegExp( \"\\\\b\" + type + \"\\\\b\" ),\n\t\t\t\tnotSupported = type && !re.test( supportedTypes.join() ),\n\t\t\t\tdecimalPlaces = function( num ) {\n\t\t\t\t\tvar match = ( \"\" + num ).match( /(?:\\.(\\d+))?$/ );\n\t\t\t\t\tif ( !match ) {\n\t\t\t\t\t\treturn 0;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Number of digits right of decimal point.\n\t\t\t\t\treturn match[ 1 ] ? match[ 1 ].length : 0;\n\t\t\t\t},\n\t\t\t\ttoInt = function( num ) {\n\t\t\t\t\treturn Math.round( num * Math.pow( 10, decimals ) );\n\t\t\t\t},\n\t\t\t\tvalid = true,\n\t\t\t\tdecimals;\n\n\t\t\t// Works only for text, number and range input types\n\t\t\t// TODO find a way to support input types date, datetime, datetime-local, month, time and week\n\t\t\tif ( notSupported ) {\n\t\t\t\tthrow new Error( errorMessage );\n\t\t\t}\n\n\t\t\tdecimals = decimalPlaces( param );\n\n\t\t\t// Value can't have too many decimals\n\t\t\tif ( decimalPlaces( value ) > decimals || toInt( value ) % toInt( param ) !== 0 ) {\n\t\t\t\tvalid = false;\n\t\t\t}\n\n\t\t\treturn this.optional( element ) || valid;\n\t\t},\n\n\t\t// https://jqueryvalidation.org/equalTo-method/\n\t\tequalTo: function( value, element, param ) {\n\n\t\t\t// Bind to the blur event of the target in order to revalidate whenever the target field is updated\n\t\t\tvar target = $( param );\n\t\t\tif ( this.settings.onfocusout && target.not( \".validate-equalTo-blur\" ).length ) {\n\t\t\t\ttarget.addClass( \"validate-equalTo-blur\" ).on( \"blur.validate-equalTo\", function() {\n\t\t\t\t\t$( element ).valid();\n\t\t\t\t} );\n\t\t\t}\n\t\t\treturn value === target.val();\n\t\t},\n\n\t\t// https://jqueryvalidation.org/remote-method/\n\t\tremote: function( value, element, param, method ) {\n\t\t\tif ( this.optional( element ) ) {\n\t\t\t\treturn \"dependency-mismatch\";\n\t\t\t}\n\n\t\t\tmethod = typeof method === \"string\" && method || \"remote\";\n\n\t\t\tvar previous = this.previousValue( element, method ),\n\t\t\t\tvalidator, data, optionDataString;\n\n\t\t\tif ( !this.settings.messages[ element.name ] ) {\n\t\t\t\tthis.settings.messages[ element.name ] = {};\n\t\t\t}\n\t\t\tprevious.originalMessage = previous.originalMessage || this.settings.messages[ element.name ][ method ];\n\t\t\tthis.settings.messages[ element.name ][ method ] = previous.message;\n\n\t\t\tparam = typeof param === \"string\" && { url: param } || param;\n\t\t\toptionDataString = $.param( $.extend( { data: value }, param.data ) );\n\t\t\tif ( previous.valid !== null && previous.old === optionDataString ) {\n\t\t\t\treturn previous.valid;\n\t\t\t}\n\n\t\t\tprevious.old = optionDataString;\n\t\t\tprevious.valid = null;\n\t\t\tvalidator = this;\n\t\t\tthis.startRequest( element );\n\t\t\tdata = {};\n\t\t\tdata[ element.name ] = value;\n\t\t\t$.ajax( $.extend( true, {\n\t\t\t\tmode: \"abort\",\n\t\t\t\tport: this.elementAjaxPort( element ),\n\t\t\t\tdataType: \"json\",\n\t\t\t\tdata: data,\n\t\t\t\tcontext: validator.currentForm,\n\t\t\t\tsuccess: function( response ) {\n\t\t\t\t\tvar valid = response === true || response === \"true\",\n\t\t\t\t\t\terrors, message, submitted;\n\n\t\t\t\t\tvalidator.settings.messages[ element.name ][ method ] = previous.originalMessage;\n\t\t\t\t\tif ( valid ) {\n\t\t\t\t\t\tsubmitted = validator.formSubmitted;\n\t\t\t\t\t\tvalidator.toHide = validator.errorsFor( element );\n\t\t\t\t\t\tvalidator.formSubmitted = submitted;\n\t\t\t\t\t\tvalidator.successList.push( element );\n\t\t\t\t\t\tvalidator.invalid[ element.name ] = false;\n\t\t\t\t\t\tvalidator.showErrors();\n\t\t\t\t\t} else {\n\t\t\t\t\t\terrors = {};\n\t\t\t\t\t\tmessage = response || validator.defaultMessage( element, { method: method, parameters: value } );\n\t\t\t\t\t\terrors[ element.name ] = previous.message = message;\n\t\t\t\t\t\tvalidator.invalid[ element.name ] = true;\n\t\t\t\t\t\tvalidator.showErrors( errors );\n\t\t\t\t\t}\n\t\t\t\t\tprevious.valid = valid;\n\t\t\t\t\tvalidator.stopRequest( element, valid );\n\t\t\t\t}\n\t\t\t}, param ) );\n\t\t\treturn \"pending\";\n\t\t}\n\t}\n\n} );\n\n// Ajax mode: abort\n// usage: $.ajax({ mode: \"abort\"[, port: \"uniqueport\"]});\n// $.ajaxAbort( port );\n// if mode:\"abort\" is used, the previous request on that port (port can be undefined) is aborted via XMLHttpRequest.abort()\n\nvar pendingRequests = {},\n\tajax;\n\n// Use a prefilter if available (1.5+)\nif ( $.ajaxPrefilter ) {\n\t$.ajaxPrefilter( function( settings, _, xhr ) {\n\t\tvar port = settings.port;\n\t\tif ( settings.mode === \"abort\" ) {\n\t\t\t$.ajaxAbort( port );\n\t\t\tpendingRequests[ port ] = xhr;\n\t\t}\n\t} );\n} else {\n\n\t// Proxy ajax\n\tajax = $.ajax;\n\t$.ajax = function( settings ) {\n\t\tvar mode = ( \"mode\" in settings ? settings : $.ajaxSettings ).mode,\n\t\t\tport = ( \"port\" in settings ? settings : $.ajaxSettings ).port;\n\t\tif ( mode === \"abort\" ) {\n\t\t\t$.ajaxAbort( port );\n\t\t\tpendingRequests[ port ] = ajax.apply( this, arguments );\n\t\t\treturn pendingRequests[ port ];\n\t\t}\n\t\treturn ajax.apply( this, arguments );\n\t};\n}\n\n// Abort the previous request without sending a new one\n$.ajaxAbort = function( port ) {\n\tif ( pendingRequests[ port ] ) {\n\t\tpendingRequests[ port ].abort();\n\t\tdelete pendingRequests[ port ];\n\t}\n};\nreturn $;\n}));","/*!\r\n * @copyright Copyright © Kartik Visweswaran, Krajee.com, 2014 - 2025\r\n * @version 1.3.6\r\n *\r\n * Date formatter utility library that allows formatting date/time variables or Date objects using PHP DateTime format.\r\n * This library is a standalone javascript library and does not depend on other libraries or plugins like jQuery. The\r\n * library also adds support for Universal Module Definition (UMD).\r\n *\r\n * @see http://php.net/manual/en/function.date.php\r\n *\r\n * For more JQuery plugins visit http://plugins.krajee.com\r\n * For more Yii related demos visit http://demos.krajee.com\r\n */\r\n(function (root, factory) {\r\n // noinspection JSUnresolvedVariable\r\n if (typeof define === 'function' && define.amd) { // AMD\r\n // noinspection JSUnresolvedFunction\r\n define([], factory);\r\n } else {\r\n // noinspection JSUnresolvedVariable\r\n if (typeof module === 'object' && module.exports) { // Node\r\n // noinspection JSUnresolvedVariable\r\n module.exports = factory();\r\n } else { // Browser globals\r\n root.DateFormatter = factory();\r\n }\r\n }\r\n}(typeof self !== 'undefined' ? self : this, function () {\r\n var DateFormatter, $h;\r\n /**\r\n * Global helper object\r\n */\r\n $h = {\r\n DAY: 1000 * 60 * 60 * 24,\r\n HOUR: 3600,\r\n defaults: {\r\n dateSettings: {\r\n days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],\r\n daysShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],\r\n months: [\r\n 'January', 'February', 'March', 'April', 'May', 'June', 'July',\r\n 'August', 'September', 'October', 'November', 'December'\r\n ],\r\n monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],\r\n meridiem: ['AM', 'PM'],\r\n ordinal: function (number) {\r\n var n = number % 10, suffixes = {1: 'st', 2: 'nd', 3: 'rd'};\r\n return Math.floor(number % 100 / 10) === 1 || !suffixes[n] ? 'th' : suffixes[n];\r\n }\r\n },\r\n separators: /[ \\-+\\/.:@]/g,\r\n validParts: /[dDjlNSwzWFmMntLoYyaABgGhHisueTIOPZcrU]/g,\r\n intParts: /[djwNzmnyYhHgGis]/g,\r\n tzParts: /\\b(?:[PMCEA][SDP]T|(?:Australian|Pacific|Mountain|Central|Eastern|Atlantic) (?:Eastern) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\\d{4})?)\\b/g,\r\n tzClip: /[^-+\\dA-Z]/g\r\n },\r\n getInt: function (str, radix) {\r\n return parseInt(str, (radix ? radix : 10));\r\n },\r\n compare: function (str1, str2) {\r\n return typeof (str1) === 'string' && typeof (str2) === 'string' && str1.toLowerCase() === str2.toLowerCase();\r\n },\r\n lpad: function (value, length, chr) {\r\n var val = value.toString();\r\n chr = chr || '0';\r\n return val.length < length ? $h.lpad(chr + val, length) : val;\r\n },\r\n merge: function (out) {\r\n var i, obj;\r\n out = out || {};\r\n for (i = 1; i < arguments.length; i++) {\r\n obj = arguments[i];\r\n if (!obj) {\r\n continue;\r\n }\r\n for (var key in obj) {\r\n if (obj.hasOwnProperty(key)) {\r\n if (typeof obj[key] === 'object') {\r\n $h.merge(out[key], obj[key]);\r\n } else {\r\n out[key] = obj[key];\r\n }\r\n }\r\n }\r\n }\r\n return out;\r\n },\r\n getIndex: function (val, arr) {\r\n for (var i = 0; i < arr.length; i++) {\r\n if (arr[i].toLowerCase() === val.toLowerCase()) {\r\n return i;\r\n }\r\n }\r\n return -1;\r\n }\r\n };\r\n\r\n /**\r\n * Date Formatter Library Constructor\r\n * @param options\r\n * @constructor\r\n */\r\n DateFormatter = function (options) {\r\n var self = this, config = $h.merge($h.defaults, options);\r\n self.dateSettings = config.dateSettings;\r\n self.separators = config.separators;\r\n self.validParts = config.validParts;\r\n self.intParts = config.intParts;\r\n self.tzParts = config.tzParts;\r\n self.tzClip = config.tzClip;\r\n };\r\n\r\n /**\r\n * DateFormatter Library Prototype\r\n */\r\n DateFormatter.prototype = {\r\n constructor: DateFormatter,\r\n getMonth: function (val) {\r\n var self = this, i;\r\n i = $h.getIndex(val, self.dateSettings.monthsShort) + 1;\r\n if (i === 0) {\r\n i = $h.getIndex(val, self.dateSettings.months) + 1;\r\n }\r\n return i;\r\n },\r\n parseDate: function (vDate, vFormat) {\r\n var self = this, vFormatParts, vDateParts, i, vDateFlag = false, vTimeFlag = false, vDatePart, iDatePart,\r\n vSettings = self.dateSettings, vMonth, vMeriIndex, vMeriOffset, len, mer,\r\n out = {date: null, year: null, month: null, day: null, hour: 0, min: 0, sec: 0};\r\n if (!vDate) {\r\n return null;\r\n }\r\n if (vDate instanceof Date) {\r\n return vDate;\r\n }\r\n if (vFormat === 'U') {\r\n i = $h.getInt(vDate);\r\n return i ? new Date(i * 1000) : vDate;\r\n }\r\n switch (typeof vDate) {\r\n case 'number':\r\n return new Date(vDate);\r\n case 'string':\r\n break;\r\n default:\r\n return null;\r\n }\r\n vFormatParts = vFormat.match(self.validParts);\r\n if (!vFormatParts || vFormatParts.length === 0) {\r\n throw new Error('Invalid date format definition.');\r\n }\r\n for (i = vFormatParts.length - 1; i >= 0; i--) {\r\n if (vFormatParts[i] === 'S') {\r\n vFormatParts.splice(i, 1);\r\n }\r\n }\r\n vDateParts = vDate.replace(self.separators, '\\0').split('\\0');\r\n for (i = 0; i < vDateParts.length; i++) {\r\n vDatePart = vDateParts[i];\r\n iDatePart = $h.getInt(vDatePart);\r\n switch (vFormatParts[i]) {\r\n case 'y':\r\n case 'Y':\r\n if (iDatePart) {\r\n len = vDatePart.length;\r\n out.year = len === 2 ? $h.getInt((iDatePart < 70 ? '20' : '19') + vDatePart) : iDatePart;\r\n } else {\r\n return null;\r\n }\r\n vDateFlag = true;\r\n break;\r\n case 'm':\r\n case 'n':\r\n case 'M':\r\n case 'F':\r\n if (isNaN(iDatePart)) {\r\n vMonth = self.getMonth(vDatePart);\r\n if (vMonth > 0) {\r\n out.month = vMonth;\r\n } else {\r\n return null;\r\n }\r\n } else {\r\n if (iDatePart >= 1 && iDatePart <= 12) {\r\n out.month = iDatePart;\r\n } else {\r\n return null;\r\n }\r\n }\r\n vDateFlag = true;\r\n break;\r\n case 'd':\r\n case 'j':\r\n if (iDatePart >= 1 && iDatePart <= 31) {\r\n out.day = iDatePart;\r\n } else {\r\n return null;\r\n }\r\n vDateFlag = true;\r\n break;\r\n case 'g':\r\n case 'h':\r\n vMeriIndex = (vFormatParts.indexOf('a') > -1) ? vFormatParts.indexOf('a') :\r\n ((vFormatParts.indexOf('A') > -1) ? vFormatParts.indexOf('A') : -1);\r\n mer = vDateParts[vMeriIndex];\r\n if (vMeriIndex !== -1) {\r\n vMeriOffset = $h.compare(mer, vSettings.meridiem[0]) ? 0 :\r\n ($h.compare(mer, vSettings.meridiem[1]) ? 12 : -1);\r\n if (iDatePart >= 1 && iDatePart <= 12 && vMeriOffset !== -1) {\r\n out.hour = iDatePart % 12 === 0 ? vMeriOffset : iDatePart + vMeriOffset;\r\n } else {\r\n if (iDatePart >= 0 && iDatePart <= 23) {\r\n out.hour = iDatePart;\r\n }\r\n }\r\n } else {\r\n if (iDatePart >= 0 && iDatePart <= 23) {\r\n out.hour = iDatePart;\r\n } else {\r\n return null;\r\n }\r\n }\r\n vTimeFlag = true;\r\n break;\r\n case 'G':\r\n case 'H':\r\n if (iDatePart >= 0 && iDatePart <= 23) {\r\n out.hour = iDatePart;\r\n } else {\r\n return null;\r\n }\r\n vTimeFlag = true;\r\n break;\r\n case 'i':\r\n if (iDatePart >= 0 && iDatePart <= 59) {\r\n out.min = iDatePart;\r\n } else {\r\n return null;\r\n }\r\n vTimeFlag = true;\r\n break;\r\n case 's':\r\n if (iDatePart >= 0 && iDatePart <= 59) {\r\n out.sec = iDatePart;\r\n } else {\r\n return null;\r\n }\r\n vTimeFlag = true;\r\n break;\r\n }\r\n }\r\n if (vDateFlag === true) {\r\n var varY = out.year || 0, varM = out.month ? out.month - 1 : 0, varD = out.day || 1;\r\n out.date = new Date(varY, varM, varD, out.hour, out.min, out.sec, 0);\r\n } else {\r\n if (vTimeFlag !== true) {\r\n return null;\r\n }\r\n out.date = new Date(0, 0, 0, out.hour, out.min, out.sec, 0);\r\n }\r\n return out.date;\r\n },\r\n guessDate: function (vDateStr, vFormat) {\r\n if (typeof vDateStr !== 'string') {\r\n return vDateStr;\r\n }\r\n var self = this, vParts = vDateStr.replace(self.separators, '\\0').split('\\0'), vPattern = /^[djmn]/g, len,\r\n vFormatParts = vFormat.match(self.validParts), vDate = new Date(), vDigit = 0, vYear, i, n, iPart, iSec;\r\n\r\n if (!vPattern.test(vFormatParts[0])) {\r\n return vDateStr;\r\n }\r\n\r\n for (i = 0; i < vParts.length; i++) {\r\n vDigit = 2;\r\n iPart = vParts[i];\r\n iSec = $h.getInt(iPart.substr(0, 2));\r\n if (isNaN(iSec)) {\r\n return null;\r\n }\r\n switch (i) {\r\n case 0:\r\n if (vFormatParts[0] === 'm' || vFormatParts[0] === 'n') {\r\n vDate.setMonth(iSec - 1);\r\n } else {\r\n vDate.setDate(iSec);\r\n }\r\n break;\r\n case 1:\r\n if (vFormatParts[0] === 'm' || vFormatParts[0] === 'n') {\r\n vDate.setDate(iSec);\r\n } else {\r\n vDate.setMonth(iSec - 1);\r\n }\r\n break;\r\n case 2:\r\n vYear = vDate.getFullYear();\r\n len = iPart.length;\r\n vDigit = len < 4 ? len : 4;\r\n vYear = $h.getInt(len < 4 ? vYear.toString().substr(0, 4 - len) + iPart : iPart.substr(0, 4));\r\n if (!vYear) {\r\n return null;\r\n }\r\n vDate.setFullYear(vYear);\r\n break;\r\n case 3:\r\n vDate.setHours(iSec);\r\n break;\r\n case 4:\r\n vDate.setMinutes(iSec);\r\n break;\r\n case 5:\r\n vDate.setSeconds(iSec);\r\n break;\r\n }\r\n n = iPart.substr(vDigit);\r\n if (n.length > 0) {\r\n vParts.splice(i + 1, 0, n);\r\n }\r\n }\r\n return vDate;\r\n },\r\n parseFormat: function (vChar, vDate) {\r\n var self = this, vSettings = self.dateSettings, fmt, backslash = /\\\\?(.?)/gi, doFormat = function (t, s) {\r\n return fmt[t] ? fmt[t]() : s;\r\n };\r\n fmt = {\r\n /////////\r\n // DAY //\r\n /////////\r\n /**\r\n * Day of month with leading 0: `01..31`\r\n * @return {string}\r\n */\r\n d: function () {\r\n return $h.lpad(fmt.j(), 2);\r\n },\r\n /**\r\n * Shorthand day name: `Mon...Sun`\r\n * @return {string}\r\n */\r\n D: function () {\r\n return vSettings.daysShort[fmt.w()];\r\n },\r\n /**\r\n * Day of month: `1..31`\r\n * @return {number}\r\n */\r\n j: function () {\r\n return vDate.getDate();\r\n },\r\n /**\r\n * Full day name: `Monday...Sunday`\r\n * @return {string}\r\n */\r\n l: function () {\r\n return vSettings.days[fmt.w()];\r\n },\r\n /**\r\n * ISO-8601 day of week: `1[Mon]..7[Sun]`\r\n * @return {number}\r\n */\r\n N: function () {\r\n return fmt.w() || 7;\r\n },\r\n /**\r\n * Day of week: `0[Sun]..6[Sat]`\r\n * @return {number}\r\n */\r\n w: function () {\r\n return vDate.getDay();\r\n },\r\n /**\r\n * Day of year: `0..365`\r\n * @return {number}\r\n */\r\n z: function () {\r\n var a = new Date(fmt.Y(), fmt.n() - 1, fmt.j()), b = new Date(fmt.Y(), 0, 1);\r\n return Math.round((a - b) / $h.DAY);\r\n },\r\n\r\n //////////\r\n // WEEK //\r\n //////////\r\n /**\r\n * ISO-8601 week number\r\n * @return {number}\r\n */\r\n W: function () {\r\n var a = new Date(fmt.Y(), fmt.n() - 1, fmt.j() - fmt.N() + 3), b = new Date(a.getFullYear(), 0, 4);\r\n return $h.lpad(1 + Math.round((a - b) / $h.DAY / 7), 2);\r\n },\r\n\r\n ///////////\r\n // MONTH //\r\n ///////////\r\n /**\r\n * Full month name: `January...December`\r\n * @return {string}\r\n */\r\n F: function () {\r\n return vSettings.months[vDate.getMonth()];\r\n },\r\n /**\r\n * Month w/leading 0: `01..12`\r\n * @return {string}\r\n */\r\n m: function () {\r\n return $h.lpad(fmt.n(), 2);\r\n },\r\n /**\r\n * Shorthand month name; `Jan...Dec`\r\n * @return {string}\r\n */\r\n M: function () {\r\n return vSettings.monthsShort[vDate.getMonth()];\r\n },\r\n /**\r\n * Month: `1...12`\r\n * @return {number}\r\n */\r\n n: function () {\r\n return vDate.getMonth() + 1;\r\n },\r\n /**\r\n * Days in month: `28...31`\r\n * @return {number}\r\n */\r\n t: function () {\r\n return (new Date(fmt.Y(), fmt.n(), 0)).getDate();\r\n },\r\n\r\n //////////\r\n // YEAR //\r\n //////////\r\n /**\r\n * Is leap year? `0 or 1`\r\n * @return {number}\r\n */\r\n L: function () {\r\n var Y = fmt.Y();\r\n return (Y % 4 === 0 && Y % 100 !== 0 || Y % 400 === 0) ? 1 : 0;\r\n },\r\n /**\r\n * ISO-8601 year\r\n * @return {number}\r\n */\r\n o: function () {\r\n var n = fmt.n(), W = fmt.W(), Y = fmt.Y();\r\n return Y + (n === 12 && W < 9 ? 1 : n === 1 && W > 9 ? -1 : 0);\r\n },\r\n /**\r\n * Full year: `e.g. 1980...2010`\r\n * @return {number}\r\n */\r\n Y: function () {\r\n return vDate.getFullYear();\r\n },\r\n /**\r\n * Last two digits of year: `00...99`\r\n * @return {string}\r\n */\r\n y: function () {\r\n return fmt.Y().toString().slice(-2);\r\n },\r\n\r\n //////////\r\n // TIME //\r\n //////////\r\n /**\r\n * Meridian lower: `am or pm`\r\n * @return {string}\r\n */\r\n a: function () {\r\n return fmt.A().toLowerCase();\r\n },\r\n /**\r\n * Meridian upper: `AM or PM`\r\n * @return {string}\r\n */\r\n A: function () {\r\n var n = fmt.G() < 12 ? 0 : 1;\r\n return vSettings.meridiem[n];\r\n },\r\n /**\r\n * Swatch Internet time: `000..999`\r\n * @return {string}\r\n */\r\n B: function () {\r\n var H = vDate.getUTCHours() * $h.HOUR, i = vDate.getUTCMinutes() * 60, s = vDate.getUTCSeconds();\r\n return $h.lpad(Math.floor((H + i + s + $h.HOUR) / 86.4) % 1000, 3);\r\n },\r\n /**\r\n * 12-Hours: `1..12`\r\n * @return {number}\r\n */\r\n g: function () {\r\n return fmt.G() % 12 || 12;\r\n },\r\n /**\r\n * 24-Hours: `0..23`\r\n * @return {number}\r\n */\r\n G: function () {\r\n return vDate.getHours();\r\n },\r\n /**\r\n * 12-Hours with leading 0: `01..12`\r\n * @return {string}\r\n */\r\n h: function () {\r\n return $h.lpad(fmt.g(), 2);\r\n },\r\n /**\r\n * 24-Hours w/leading 0: `00..23`\r\n * @return {string}\r\n */\r\n H: function () {\r\n return $h.lpad(fmt.G(), 2);\r\n },\r\n /**\r\n * Minutes w/leading 0: `00..59`\r\n * @return {string}\r\n */\r\n i: function () {\r\n return $h.lpad(vDate.getMinutes(), 2);\r\n },\r\n /**\r\n * Seconds w/leading 0: `00..59`\r\n * @return {string}\r\n */\r\n s: function () {\r\n return $h.lpad(vDate.getSeconds(), 2);\r\n },\r\n /**\r\n * Microseconds: `000000-999000`\r\n * @return {string}\r\n */\r\n u: function () {\r\n return $h.lpad(vDate.getMilliseconds() * 1000, 6);\r\n },\r\n\r\n //////////////\r\n // TIMEZONE //\r\n //////////////\r\n /**\r\n * Timezone identifier: `e.g. Atlantic/Azores, ...`\r\n * @return {string}\r\n */\r\n e: function () {\r\n var str = /\\((.*)\\)/.exec(String(vDate))[1];\r\n return str || 'Coordinated Universal Time';\r\n },\r\n /**\r\n * DST observed? `0 or 1`\r\n * @return {number}\r\n */\r\n I: function () {\r\n var a = new Date(fmt.Y(), 0), c = Date.UTC(fmt.Y(), 0),\r\n b = new Date(fmt.Y(), 6), d = Date.UTC(fmt.Y(), 6);\r\n return ((a - c) !== (b - d)) ? 1 : 0;\r\n },\r\n /**\r\n * Difference to GMT in hour format: `e.g. +0200`\r\n * @return {string}\r\n */\r\n O: function () {\r\n var tzo = vDate.getTimezoneOffset(), a = Math.abs(tzo);\r\n return (tzo > 0 ? '-' : '+') + $h.lpad(Math.floor(a / 60) * 100 + a % 60, 4);\r\n },\r\n /**\r\n * Difference to GMT with colon: `e.g. +02:00`\r\n * @return {string}\r\n */\r\n P: function () {\r\n var O = fmt.O();\r\n return (O.substr(0, 3) + ':' + O.substr(3, 2));\r\n },\r\n /**\r\n * Timezone abbreviation: `e.g. EST, MDT, ...`\r\n * @return {string}\r\n */\r\n T: function () {\r\n var str = (String(vDate).match(self.tzParts) || ['']).pop().replace(self.tzClip, '');\r\n return str || 'UTC';\r\n },\r\n /**\r\n * Timezone offset in seconds: `-43200...50400`\r\n * @return {number}\r\n */\r\n Z: function () {\r\n return -vDate.getTimezoneOffset() * 60;\r\n },\r\n\r\n ////////////////////\r\n // FULL DATE TIME //\r\n ////////////////////\r\n /**\r\n * ISO-8601 date\r\n * @return {string}\r\n */\r\n c: function () {\r\n return 'Y-m-d\\\\TH:i:sP'.replace(backslash, doFormat);\r\n },\r\n /**\r\n * RFC 2822 date\r\n * @return {string}\r\n */\r\n r: function () {\r\n return 'D, d M Y H:i:s O'.replace(backslash, doFormat);\r\n },\r\n /**\r\n * Seconds since UNIX epoch\r\n * @return {number}\r\n */\r\n U: function () {\r\n return vDate.getTime() / 1000 || 0;\r\n }\r\n };\r\n return doFormat(vChar, vChar);\r\n },\r\n formatDate: function (vDate, vFormat) {\r\n var self = this, i, n, len, str, vChar, vDateStr = '', BACKSLASH = '\\\\';\r\n if (typeof vDate === 'string') {\r\n vDate = self.parseDate(vDate, vFormat);\r\n if (!vDate) {\r\n return null;\r\n }\r\n }\r\n if (vDate instanceof Date) {\r\n len = vFormat.length;\r\n for (i = 0; i < len; i++) {\r\n vChar = vFormat.charAt(i);\r\n if (vChar === 'S' || vChar === BACKSLASH) {\r\n continue;\r\n }\r\n if (i > 0 && vFormat.charAt(i - 1) === BACKSLASH) {\r\n vDateStr += vChar;\r\n continue;\r\n }\r\n str = self.parseFormat(vChar, vDate);\r\n if (i !== (len - 1) && self.intParts.test(vChar) && vFormat.charAt(i + 1) === 'S') {\r\n n = $h.getInt(str) || 0;\r\n str += self.dateSettings.ordinal(n);\r\n }\r\n vDateStr += str;\r\n }\r\n return vDateStr;\r\n }\r\n return '';\r\n }\r\n };\r\n\tObject.freeze(DateFormatter);\r\n return DateFormatter;\r\n}));\r\n","/******/ (function() { // webpackBootstrap\n/******/ \t\"use strict\";\n/******/ \tvar __webpack_modules__ = ({\n\n/***/ \"./node_modules/locutus/php/array/array_diff.js\":\n/*!******************************************************!*\\\n !*** ./node_modules/locutus/php/array/array_diff.js ***!\n \\******************************************************/\n/***/ (function(module) {\n\n\n\nmodule.exports = function array_diff(arr1) {\n // discuss at: https://locutus.io/php/array_diff/\n // original by: Kevin van Zonneveld (https://kvz.io)\n // improved by: Sanjoy Roy\n // revised by: Brett Zamir (https://brett-zamir.me)\n // example 1: array_diff(['Kevin', 'van', 'Zonneveld'], ['van', 'Zonneveld'])\n // returns 1: {0:'Kevin'}\n\n var retArr = {};\n var argl = arguments.length;\n var k1 = '';\n var i = 1;\n var k = '';\n var arr = {};\n\n arr1keys: for (k1 in arr1) {\n for (i = 1; i < argl; i++) {\n arr = arguments[i];\n for (k in arr) {\n if (arr[k] === arr1[k1]) {\n // If it reaches here, it was found in at least one array, so try next value\n continue arr1keys; // eslint-disable-line no-labels\n }\n }\n retArr[k1] = arr1[k1];\n }\n }\n\n return retArr;\n};\n//# sourceMappingURL=array_diff.js.map\n\n/***/ }),\n\n/***/ \"./node_modules/locutus/php/datetime/strtotime.js\":\n/*!********************************************************!*\\\n !*** ./node_modules/locutus/php/datetime/strtotime.js ***!\n \\********************************************************/\n/***/ (function(module) {\n\n\n\nvar reSpace = '[ \\\\t]+';\nvar reSpaceOpt = '[ \\\\t]*';\nvar reMeridian = '(?:([ap])\\\\.?m\\\\.?([\\\\t ]|$))';\nvar reHour24 = '(2[0-4]|[01]?[0-9])';\nvar reHour24lz = '([01][0-9]|2[0-4])';\nvar reHour12 = '(0?[1-9]|1[0-2])';\nvar reMinute = '([0-5]?[0-9])';\nvar reMinutelz = '([0-5][0-9])';\nvar reSecond = '(60|[0-5]?[0-9])';\nvar reSecondlz = '(60|[0-5][0-9])';\nvar reFrac = '(?:\\\\.([0-9]+))';\n\nvar reDayfull = 'sunday|monday|tuesday|wednesday|thursday|friday|saturday';\nvar reDayabbr = 'sun|mon|tue|wed|thu|fri|sat';\nvar reDaytext = reDayfull + '|' + reDayabbr + '|weekdays?';\n\nvar reReltextnumber = 'first|second|third|fourth|fifth|sixth|seventh|eighth?|ninth|tenth|eleventh|twelfth';\nvar reReltexttext = 'next|last|previous|this';\nvar reReltextunit = '(?:second|sec|minute|min|hour|day|fortnight|forthnight|month|year)s?|weeks|' + reDaytext;\n\nvar reYear = '([0-9]{1,4})';\nvar reYear2 = '([0-9]{2})';\nvar reYear4 = '([0-9]{4})';\nvar reYear4withSign = '([+-]?[0-9]{4})';\nvar reMonth = '(1[0-2]|0?[0-9])';\nvar reMonthlz = '(0[0-9]|1[0-2])';\nvar reDay = '(?:(3[01]|[0-2]?[0-9])(?:st|nd|rd|th)?)';\nvar reDaylz = '(0[0-9]|[1-2][0-9]|3[01])';\n\nvar reMonthFull = 'january|february|march|april|may|june|july|august|september|october|november|december';\nvar reMonthAbbr = 'jan|feb|mar|apr|may|jun|jul|aug|sept?|oct|nov|dec';\nvar reMonthroman = 'i[vx]|vi{0,3}|xi{0,2}|i{1,3}';\nvar reMonthText = '(' + reMonthFull + '|' + reMonthAbbr + '|' + reMonthroman + ')';\n\nvar reTzCorrection = '((?:GMT)?([+-])' + reHour24 + ':?' + reMinute + '?)';\nvar reTzAbbr = '\\\\(?([a-zA-Z]{1,6})\\\\)?';\nvar reDayOfYear = '(00[1-9]|0[1-9][0-9]|[12][0-9][0-9]|3[0-5][0-9]|36[0-6])';\nvar reWeekOfYear = '(0[1-9]|[1-4][0-9]|5[0-3])';\n\nvar reDateNoYear = reMonthText + '[ .\\\\t-]*' + reDay + '[,.stndrh\\\\t ]*';\n\nfunction processMeridian(hour, meridian) {\n meridian = meridian && meridian.toLowerCase();\n\n switch (meridian) {\n case 'a':\n hour += hour === 12 ? -12 : 0;\n break;\n case 'p':\n hour += hour !== 12 ? 12 : 0;\n break;\n }\n\n return hour;\n}\n\nfunction processYear(yearStr) {\n var year = +yearStr;\n\n if (yearStr.length < 4 && year < 100) {\n year += year < 70 ? 2000 : 1900;\n }\n\n return year;\n}\n\nfunction lookupMonth(monthStr) {\n return {\n jan: 0,\n january: 0,\n i: 0,\n feb: 1,\n february: 1,\n ii: 1,\n mar: 2,\n march: 2,\n iii: 2,\n apr: 3,\n april: 3,\n iv: 3,\n may: 4,\n v: 4,\n jun: 5,\n june: 5,\n vi: 5,\n jul: 6,\n july: 6,\n vii: 6,\n aug: 7,\n august: 7,\n viii: 7,\n sep: 8,\n sept: 8,\n september: 8,\n ix: 8,\n oct: 9,\n october: 9,\n x: 9,\n nov: 10,\n november: 10,\n xi: 10,\n dec: 11,\n december: 11,\n xii: 11\n }[monthStr.toLowerCase()];\n}\n\nfunction lookupWeekday(dayStr) {\n var desiredSundayNumber = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;\n\n var dayNumbers = {\n mon: 1,\n monday: 1,\n tue: 2,\n tuesday: 2,\n wed: 3,\n wednesday: 3,\n thu: 4,\n thursday: 4,\n fri: 5,\n friday: 5,\n sat: 6,\n saturday: 6,\n sun: 0,\n sunday: 0\n };\n\n return dayNumbers[dayStr.toLowerCase()] || desiredSundayNumber;\n}\n\nfunction lookupRelative(relText) {\n var relativeNumbers = {\n last: -1,\n previous: -1,\n this: 0,\n first: 1,\n next: 1,\n second: 2,\n third: 3,\n fourth: 4,\n fifth: 5,\n sixth: 6,\n seventh: 7,\n eight: 8,\n eighth: 8,\n ninth: 9,\n tenth: 10,\n eleventh: 11,\n twelfth: 12\n };\n\n var relativeBehavior = {\n this: 1\n };\n\n var relTextLower = relText.toLowerCase();\n\n return {\n amount: relativeNumbers[relTextLower],\n behavior: relativeBehavior[relTextLower] || 0\n };\n}\n\nfunction processTzCorrection(tzOffset, oldValue) {\n var reTzCorrectionLoose = /(?:GMT)?([+-])(\\d+)(:?)(\\d{0,2})/i;\n tzOffset = tzOffset && tzOffset.match(reTzCorrectionLoose);\n\n if (!tzOffset) {\n return oldValue;\n }\n\n var sign = tzOffset[1] === '-' ? -1 : 1;\n var hours = +tzOffset[2];\n var minutes = +tzOffset[4];\n\n if (!tzOffset[4] && !tzOffset[3]) {\n minutes = Math.floor(hours % 100);\n hours = Math.floor(hours / 100);\n }\n\n // timezone offset in seconds\n return sign * (hours * 60 + minutes) * 60;\n}\n\n// tz abbrevation : tz offset in seconds\nvar tzAbbrOffsets = {\n acdt: 37800,\n acst: 34200,\n addt: -7200,\n adt: -10800,\n aedt: 39600,\n aest: 36000,\n ahdt: -32400,\n ahst: -36000,\n akdt: -28800,\n akst: -32400,\n amt: -13840,\n apt: -10800,\n ast: -14400,\n awdt: 32400,\n awst: 28800,\n awt: -10800,\n bdst: 7200,\n bdt: -36000,\n bmt: -14309,\n bst: 3600,\n cast: 34200,\n cat: 7200,\n cddt: -14400,\n cdt: -18000,\n cemt: 10800,\n cest: 7200,\n cet: 3600,\n cmt: -15408,\n cpt: -18000,\n cst: -21600,\n cwt: -18000,\n chst: 36000,\n dmt: -1521,\n eat: 10800,\n eddt: -10800,\n edt: -14400,\n eest: 10800,\n eet: 7200,\n emt: -26248,\n ept: -14400,\n est: -18000,\n ewt: -14400,\n ffmt: -14660,\n fmt: -4056,\n gdt: 39600,\n gmt: 0,\n gst: 36000,\n hdt: -34200,\n hkst: 32400,\n hkt: 28800,\n hmt: -19776,\n hpt: -34200,\n hst: -36000,\n hwt: -34200,\n iddt: 14400,\n idt: 10800,\n imt: 25025,\n ist: 7200,\n jdt: 36000,\n jmt: 8440,\n jst: 32400,\n kdt: 36000,\n kmt: 5736,\n kst: 30600,\n lst: 9394,\n mddt: -18000,\n mdst: 16279,\n mdt: -21600,\n mest: 7200,\n met: 3600,\n mmt: 9017,\n mpt: -21600,\n msd: 14400,\n msk: 10800,\n mst: -25200,\n mwt: -21600,\n nddt: -5400,\n ndt: -9052,\n npt: -9000,\n nst: -12600,\n nwt: -9000,\n nzdt: 46800,\n nzmt: 41400,\n nzst: 43200,\n pddt: -21600,\n pdt: -25200,\n pkst: 21600,\n pkt: 18000,\n plmt: 25590,\n pmt: -13236,\n ppmt: -17340,\n ppt: -25200,\n pst: -28800,\n pwt: -25200,\n qmt: -18840,\n rmt: 5794,\n sast: 7200,\n sdmt: -16800,\n sjmt: -20173,\n smt: -13884,\n sst: -39600,\n tbmt: 10751,\n tmt: 12344,\n uct: 0,\n utc: 0,\n wast: 7200,\n wat: 3600,\n wemt: 7200,\n west: 3600,\n wet: 0,\n wib: 25200,\n wita: 28800,\n wit: 32400,\n wmt: 5040,\n yddt: -25200,\n ydt: -28800,\n ypt: -28800,\n yst: -32400,\n ywt: -28800,\n a: 3600,\n b: 7200,\n c: 10800,\n d: 14400,\n e: 18000,\n f: 21600,\n g: 25200,\n h: 28800,\n i: 32400,\n k: 36000,\n l: 39600,\n m: 43200,\n n: -3600,\n o: -7200,\n p: -10800,\n q: -14400,\n r: -18000,\n s: -21600,\n t: -25200,\n u: -28800,\n v: -32400,\n w: -36000,\n x: -39600,\n y: -43200,\n z: 0\n};\n\nvar formats = {\n yesterday: {\n regex: /^yesterday/i,\n name: 'yesterday',\n callback: function callback() {\n this.rd -= 1;\n return this.resetTime();\n }\n },\n\n now: {\n regex: /^now/i,\n name: 'now'\n // do nothing\n },\n\n noon: {\n regex: /^noon/i,\n name: 'noon',\n callback: function callback() {\n return this.resetTime() && this.time(12, 0, 0, 0);\n }\n },\n\n midnightOrToday: {\n regex: /^(midnight|today)/i,\n name: 'midnight | today',\n callback: function callback() {\n return this.resetTime();\n }\n },\n\n tomorrow: {\n regex: /^tomorrow/i,\n name: 'tomorrow',\n callback: function callback() {\n this.rd += 1;\n return this.resetTime();\n }\n },\n\n timestamp: {\n regex: /^@(-?\\d+)/i,\n name: 'timestamp',\n callback: function callback(match, timestamp) {\n this.rs += +timestamp;\n this.y = 1970;\n this.m = 0;\n this.d = 1;\n this.dates = 0;\n\n return this.resetTime() && this.zone(0);\n }\n },\n\n firstOrLastDay: {\n regex: /^(first|last) day of/i,\n name: 'firstdayof | lastdayof',\n callback: function callback(match, day) {\n if (day.toLowerCase() === 'first') {\n this.firstOrLastDayOfMonth = 1;\n } else {\n this.firstOrLastDayOfMonth = -1;\n }\n }\n },\n\n backOrFrontOf: {\n regex: RegExp('^(back|front) of ' + reHour24 + reSpaceOpt + reMeridian + '?', 'i'),\n name: 'backof | frontof',\n callback: function callback(match, side, hours, meridian) {\n var back = side.toLowerCase() === 'back';\n var hour = +hours;\n var minute = 15;\n\n if (!back) {\n hour -= 1;\n minute = 45;\n }\n\n hour = processMeridian(hour, meridian);\n\n return this.resetTime() && this.time(hour, minute, 0, 0);\n }\n },\n\n weekdayOf: {\n regex: RegExp('^(' + reReltextnumber + '|' + reReltexttext + ')' + reSpace + '(' + reDayfull + '|' + reDayabbr + ')' + reSpace + 'of', 'i'),\n name: 'weekdayof'\n // todo\n },\n\n mssqltime: {\n regex: RegExp('^' + reHour12 + ':' + reMinutelz + ':' + reSecondlz + '[:.]([0-9]+)' + reMeridian, 'i'),\n name: 'mssqltime',\n callback: function callback(match, hour, minute, second, frac, meridian) {\n return this.time(processMeridian(+hour, meridian), +minute, +second, +frac.substr(0, 3));\n }\n },\n\n oracledate: {\n regex: /^(\\d{2})-([A-Z]{3})-(\\d{2})$/i,\n name: 'd-M-y',\n callback: function callback(match, day, monthText, year) {\n var month = {\n JAN: 0,\n FEB: 1,\n MAR: 2,\n APR: 3,\n MAY: 4,\n JUN: 5,\n JUL: 6,\n AUG: 7,\n SEP: 8,\n OCT: 9,\n NOV: 10,\n DEC: 11\n }[monthText.toUpperCase()];\n return this.ymd(2000 + parseInt(year, 10), month, parseInt(day, 10));\n }\n },\n\n timeLong12: {\n regex: RegExp('^' + reHour12 + '[:.]' + reMinute + '[:.]' + reSecondlz + reSpaceOpt + reMeridian, 'i'),\n name: 'timelong12',\n callback: function callback(match, hour, minute, second, meridian) {\n return this.time(processMeridian(+hour, meridian), +minute, +second, 0);\n }\n },\n\n timeShort12: {\n regex: RegExp('^' + reHour12 + '[:.]' + reMinutelz + reSpaceOpt + reMeridian, 'i'),\n name: 'timeshort12',\n callback: function callback(match, hour, minute, meridian) {\n return this.time(processMeridian(+hour, meridian), +minute, 0, 0);\n }\n },\n\n timeTiny12: {\n regex: RegExp('^' + reHour12 + reSpaceOpt + reMeridian, 'i'),\n name: 'timetiny12',\n callback: function callback(match, hour, meridian) {\n return this.time(processMeridian(+hour, meridian), 0, 0, 0);\n }\n },\n\n soap: {\n regex: RegExp('^' + reYear4 + '-' + reMonthlz + '-' + reDaylz + 'T' + reHour24lz + ':' + reMinutelz + ':' + reSecondlz + reFrac + reTzCorrection + '?', 'i'),\n name: 'soap',\n callback: function callback(match, year, month, day, hour, minute, second, frac, tzCorrection) {\n return this.ymd(+year, month - 1, +day) && this.time(+hour, +minute, +second, +frac.substr(0, 3)) && this.zone(processTzCorrection(tzCorrection));\n }\n },\n\n wddx: {\n regex: RegExp('^' + reYear4 + '-' + reMonth + '-' + reDay + 'T' + reHour24 + ':' + reMinute + ':' + reSecond),\n name: 'wddx',\n callback: function callback(match, year, month, day, hour, minute, second) {\n return this.ymd(+year, month - 1, +day) && this.time(+hour, +minute, +second, 0);\n }\n },\n\n exif: {\n regex: RegExp('^' + reYear4 + ':' + reMonthlz + ':' + reDaylz + ' ' + reHour24lz + ':' + reMinutelz + ':' + reSecondlz, 'i'),\n name: 'exif',\n callback: function callback(match, year, month, day, hour, minute, second) {\n return this.ymd(+year, month - 1, +day) && this.time(+hour, +minute, +second, 0);\n }\n },\n\n xmlRpc: {\n regex: RegExp('^' + reYear4 + reMonthlz + reDaylz + 'T' + reHour24 + ':' + reMinutelz + ':' + reSecondlz),\n name: 'xmlrpc',\n callback: function callback(match, year, month, day, hour, minute, second) {\n return this.ymd(+year, month - 1, +day) && this.time(+hour, +minute, +second, 0);\n }\n },\n\n xmlRpcNoColon: {\n regex: RegExp('^' + reYear4 + reMonthlz + reDaylz + '[Tt]' + reHour24 + reMinutelz + reSecondlz),\n name: 'xmlrpcnocolon',\n callback: function callback(match, year, month, day, hour, minute, second) {\n return this.ymd(+year, month - 1, +day) && this.time(+hour, +minute, +second, 0);\n }\n },\n\n clf: {\n regex: RegExp('^' + reDay + '/(' + reMonthAbbr + ')/' + reYear4 + ':' + reHour24lz + ':' + reMinutelz + ':' + reSecondlz + reSpace + reTzCorrection, 'i'),\n name: 'clf',\n callback: function callback(match, day, month, year, hour, minute, second, tzCorrection) {\n return this.ymd(+year, lookupMonth(month), +day) && this.time(+hour, +minute, +second, 0) && this.zone(processTzCorrection(tzCorrection));\n }\n },\n\n iso8601long: {\n regex: RegExp('^t?' + reHour24 + '[:.]' + reMinute + '[:.]' + reSecond + reFrac, 'i'),\n name: 'iso8601long',\n callback: function callback(match, hour, minute, second, frac) {\n return this.time(+hour, +minute, +second, +frac.substr(0, 3));\n }\n },\n\n dateTextual: {\n regex: RegExp('^' + reMonthText + '[ .\\\\t-]*' + reDay + '[,.stndrh\\\\t ]+' + reYear, 'i'),\n name: 'datetextual',\n callback: function callback(match, month, day, year) {\n return this.ymd(processYear(year), lookupMonth(month), +day);\n }\n },\n\n pointedDate4: {\n regex: RegExp('^' + reDay + '[.\\\\t-]' + reMonth + '[.-]' + reYear4),\n name: 'pointeddate4',\n callback: function callback(match, day, month, year) {\n return this.ymd(+year, month - 1, +day);\n }\n },\n\n pointedDate2: {\n regex: RegExp('^' + reDay + '[.\\\\t]' + reMonth + '\\\\.' + reYear2),\n name: 'pointeddate2',\n callback: function callback(match, day, month, year) {\n return this.ymd(processYear(year), month - 1, +day);\n }\n },\n\n timeLong24: {\n regex: RegExp('^t?' + reHour24 + '[:.]' + reMinute + '[:.]' + reSecond),\n name: 'timelong24',\n callback: function callback(match, hour, minute, second) {\n return this.time(+hour, +minute, +second, 0);\n }\n },\n\n dateNoColon: {\n regex: RegExp('^' + reYear4 + reMonthlz + reDaylz),\n name: 'datenocolon',\n callback: function callback(match, year, month, day) {\n return this.ymd(+year, month - 1, +day);\n }\n },\n\n pgydotd: {\n regex: RegExp('^' + reYear4 + '\\\\.?' + reDayOfYear),\n name: 'pgydotd',\n callback: function callback(match, year, day) {\n return this.ymd(+year, 0, +day);\n }\n },\n\n timeShort24: {\n regex: RegExp('^t?' + reHour24 + '[:.]' + reMinute, 'i'),\n name: 'timeshort24',\n callback: function callback(match, hour, minute) {\n return this.time(+hour, +minute, 0, 0);\n }\n },\n\n iso8601noColon: {\n regex: RegExp('^t?' + reHour24lz + reMinutelz + reSecondlz, 'i'),\n name: 'iso8601nocolon',\n callback: function callback(match, hour, minute, second) {\n return this.time(+hour, +minute, +second, 0);\n }\n },\n\n iso8601dateSlash: {\n // eventhough the trailing slash is optional in PHP\n // here it's mandatory and inputs without the slash\n // are handled by dateslash\n regex: RegExp('^' + reYear4 + '/' + reMonthlz + '/' + reDaylz + '/'),\n name: 'iso8601dateslash',\n callback: function callback(match, year, month, day) {\n return this.ymd(+year, month - 1, +day);\n }\n },\n\n dateSlash: {\n regex: RegExp('^' + reYear4 + '/' + reMonth + '/' + reDay),\n name: 'dateslash',\n callback: function callback(match, year, month, day) {\n return this.ymd(+year, month - 1, +day);\n }\n },\n\n american: {\n regex: RegExp('^' + reMonth + '/' + reDay + '/' + reYear),\n name: 'american',\n callback: function callback(match, month, day, year) {\n return this.ymd(processYear(year), month - 1, +day);\n }\n },\n\n americanShort: {\n regex: RegExp('^' + reMonth + '/' + reDay),\n name: 'americanshort',\n callback: function callback(match, month, day) {\n return this.ymd(this.y, month - 1, +day);\n }\n },\n\n gnuDateShortOrIso8601date2: {\n // iso8601date2 is complete subset of gnudateshort\n regex: RegExp('^' + reYear + '-' + reMonth + '-' + reDay),\n name: 'gnudateshort | iso8601date2',\n callback: function callback(match, year, month, day) {\n return this.ymd(processYear(year), month - 1, +day);\n }\n },\n\n iso8601date4: {\n regex: RegExp('^' + reYear4withSign + '-' + reMonthlz + '-' + reDaylz),\n name: 'iso8601date4',\n callback: function callback(match, year, month, day) {\n return this.ymd(+year, month - 1, +day);\n }\n },\n\n gnuNoColon: {\n regex: RegExp('^t?' + reHour24lz + reMinutelz, 'i'),\n name: 'gnunocolon',\n callback: function callback(match, hour, minute) {\n // this rule is a special case\n // if time was already set once by any preceding rule, it sets the captured value as year\n switch (this.times) {\n case 0:\n return this.time(+hour, +minute, 0, this.f);\n case 1:\n this.y = hour * 100 + +minute;\n this.times++;\n\n return true;\n default:\n return false;\n }\n }\n },\n\n gnuDateShorter: {\n regex: RegExp('^' + reYear4 + '-' + reMonth),\n name: 'gnudateshorter',\n callback: function callback(match, year, month) {\n return this.ymd(+year, month - 1, 1);\n }\n },\n\n pgTextReverse: {\n // note: allowed years are from 32-9999\n // years below 32 should be treated as days in datefull\n regex: RegExp('^' + '(\\\\d{3,4}|[4-9]\\\\d|3[2-9])-(' + reMonthAbbr + ')-' + reDaylz, 'i'),\n name: 'pgtextreverse',\n callback: function callback(match, year, month, day) {\n return this.ymd(processYear(year), lookupMonth(month), +day);\n }\n },\n\n dateFull: {\n regex: RegExp('^' + reDay + '[ \\\\t.-]*' + reMonthText + '[ \\\\t.-]*' + reYear, 'i'),\n name: 'datefull',\n callback: function callback(match, day, month, year) {\n return this.ymd(processYear(year), lookupMonth(month), +day);\n }\n },\n\n dateNoDay: {\n regex: RegExp('^' + reMonthText + '[ .\\\\t-]*' + reYear4, 'i'),\n name: 'datenoday',\n callback: function callback(match, month, year) {\n return this.ymd(+year, lookupMonth(month), 1);\n }\n },\n\n dateNoDayRev: {\n regex: RegExp('^' + reYear4 + '[ .\\\\t-]*' + reMonthText, 'i'),\n name: 'datenodayrev',\n callback: function callback(match, year, month) {\n return this.ymd(+year, lookupMonth(month), 1);\n }\n },\n\n pgTextShort: {\n regex: RegExp('^(' + reMonthAbbr + ')-' + reDaylz + '-' + reYear, 'i'),\n name: 'pgtextshort',\n callback: function callback(match, month, day, year) {\n return this.ymd(processYear(year), lookupMonth(month), +day);\n }\n },\n\n dateNoYear: {\n regex: RegExp('^' + reDateNoYear, 'i'),\n name: 'datenoyear',\n callback: function callback(match, month, day) {\n return this.ymd(this.y, lookupMonth(month), +day);\n }\n },\n\n dateNoYearRev: {\n regex: RegExp('^' + reDay + '[ .\\\\t-]*' + reMonthText, 'i'),\n name: 'datenoyearrev',\n callback: function callback(match, day, month) {\n return this.ymd(this.y, lookupMonth(month), +day);\n }\n },\n\n isoWeekDay: {\n regex: RegExp('^' + reYear4 + '-?W' + reWeekOfYear + '(?:-?([0-7]))?'),\n name: 'isoweekday | isoweek',\n callback: function callback(match, year, week, day) {\n day = day ? +day : 1;\n\n if (!this.ymd(+year, 0, 1)) {\n return false;\n }\n\n // get day of week for Jan 1st\n var dayOfWeek = new Date(this.y, this.m, this.d).getDay();\n\n // and use the day to figure out the offset for day 1 of week 1\n dayOfWeek = 0 - (dayOfWeek > 4 ? dayOfWeek - 7 : dayOfWeek);\n\n this.rd += dayOfWeek + (week - 1) * 7 + day;\n }\n },\n\n relativeText: {\n regex: RegExp('^(' + reReltextnumber + '|' + reReltexttext + ')' + reSpace + '(' + reReltextunit + ')', 'i'),\n name: 'relativetext',\n callback: function callback(match, relValue, relUnit) {\n // todo: implement handling of 'this time-unit'\n // eslint-disable-next-line no-unused-vars\n var _lookupRelative = lookupRelative(relValue),\n amount = _lookupRelative.amount,\n behavior = _lookupRelative.behavior;\n\n switch (relUnit.toLowerCase()) {\n case 'sec':\n case 'secs':\n case 'second':\n case 'seconds':\n this.rs += amount;\n break;\n case 'min':\n case 'mins':\n case 'minute':\n case 'minutes':\n this.ri += amount;\n break;\n case 'hour':\n case 'hours':\n this.rh += amount;\n break;\n case 'day':\n case 'days':\n this.rd += amount;\n break;\n case 'fortnight':\n case 'fortnights':\n case 'forthnight':\n case 'forthnights':\n this.rd += amount * 14;\n break;\n case 'week':\n case 'weeks':\n this.rd += amount * 7;\n break;\n case 'month':\n case 'months':\n this.rm += amount;\n break;\n case 'year':\n case 'years':\n this.ry += amount;\n break;\n case 'mon':\n case 'monday':\n case 'tue':\n case 'tuesday':\n case 'wed':\n case 'wednesday':\n case 'thu':\n case 'thursday':\n case 'fri':\n case 'friday':\n case 'sat':\n case 'saturday':\n case 'sun':\n case 'sunday':\n this.resetTime();\n this.weekday = lookupWeekday(relUnit, 7);\n this.weekdayBehavior = 1;\n this.rd += (amount > 0 ? amount - 1 : amount) * 7;\n break;\n case 'weekday':\n case 'weekdays':\n // todo\n break;\n }\n }\n },\n\n relative: {\n regex: RegExp('^([+-]*)[ \\\\t]*(\\\\d+)' + reSpaceOpt + '(' + reReltextunit + '|week)', 'i'),\n name: 'relative',\n callback: function callback(match, signs, relValue, relUnit) {\n var minuses = signs.replace(/[^-]/g, '').length;\n\n var amount = +relValue * Math.pow(-1, minuses);\n\n switch (relUnit.toLowerCase()) {\n case 'sec':\n case 'secs':\n case 'second':\n case 'seconds':\n this.rs += amount;\n break;\n case 'min':\n case 'mins':\n case 'minute':\n case 'minutes':\n this.ri += amount;\n break;\n case 'hour':\n case 'hours':\n this.rh += amount;\n break;\n case 'day':\n case 'days':\n this.rd += amount;\n break;\n case 'fortnight':\n case 'fortnights':\n case 'forthnight':\n case 'forthnights':\n this.rd += amount * 14;\n break;\n case 'week':\n case 'weeks':\n this.rd += amount * 7;\n break;\n case 'month':\n case 'months':\n this.rm += amount;\n break;\n case 'year':\n case 'years':\n this.ry += amount;\n break;\n case 'mon':\n case 'monday':\n case 'tue':\n case 'tuesday':\n case 'wed':\n case 'wednesday':\n case 'thu':\n case 'thursday':\n case 'fri':\n case 'friday':\n case 'sat':\n case 'saturday':\n case 'sun':\n case 'sunday':\n this.resetTime();\n this.weekday = lookupWeekday(relUnit, 7);\n this.weekdayBehavior = 1;\n this.rd += (amount > 0 ? amount - 1 : amount) * 7;\n break;\n case 'weekday':\n case 'weekdays':\n // todo\n break;\n }\n }\n },\n\n dayText: {\n regex: RegExp('^(' + reDaytext + ')', 'i'),\n name: 'daytext',\n callback: function callback(match, dayText) {\n this.resetTime();\n this.weekday = lookupWeekday(dayText, 0);\n\n if (this.weekdayBehavior !== 2) {\n this.weekdayBehavior = 1;\n }\n }\n },\n\n relativeTextWeek: {\n regex: RegExp('^(' + reReltexttext + ')' + reSpace + 'week', 'i'),\n name: 'relativetextweek',\n callback: function callback(match, relText) {\n this.weekdayBehavior = 2;\n\n switch (relText.toLowerCase()) {\n case 'this':\n this.rd += 0;\n break;\n case 'next':\n this.rd += 7;\n break;\n case 'last':\n case 'previous':\n this.rd -= 7;\n break;\n }\n\n if (isNaN(this.weekday)) {\n this.weekday = 1;\n }\n }\n },\n\n monthFullOrMonthAbbr: {\n regex: RegExp('^(' + reMonthFull + '|' + reMonthAbbr + ')', 'i'),\n name: 'monthfull | monthabbr',\n callback: function callback(match, month) {\n return this.ymd(this.y, lookupMonth(month), this.d);\n }\n },\n\n tzCorrection: {\n regex: RegExp('^' + reTzCorrection, 'i'),\n name: 'tzcorrection',\n callback: function callback(tzCorrection) {\n return this.zone(processTzCorrection(tzCorrection));\n }\n },\n\n tzAbbr: {\n regex: RegExp('^' + reTzAbbr),\n name: 'tzabbr',\n callback: function callback(match, abbr) {\n var offset = tzAbbrOffsets[abbr.toLowerCase()];\n\n if (isNaN(offset)) {\n return false;\n }\n\n return this.zone(offset);\n }\n },\n\n ago: {\n regex: /^ago/i,\n name: 'ago',\n callback: function callback() {\n this.ry = -this.ry;\n this.rm = -this.rm;\n this.rd = -this.rd;\n this.rh = -this.rh;\n this.ri = -this.ri;\n this.rs = -this.rs;\n this.rf = -this.rf;\n }\n },\n\n year4: {\n regex: RegExp('^' + reYear4),\n name: 'year4',\n callback: function callback(match, year) {\n this.y = +year;\n return true;\n }\n },\n\n whitespace: {\n regex: /^[ .,\\t]+/,\n name: 'whitespace'\n // do nothing\n },\n\n dateShortWithTimeLong: {\n regex: RegExp('^' + reDateNoYear + 't?' + reHour24 + '[:.]' + reMinute + '[:.]' + reSecond, 'i'),\n name: 'dateshortwithtimelong',\n callback: function callback(match, month, day, hour, minute, second) {\n return this.ymd(this.y, lookupMonth(month), +day) && this.time(+hour, +minute, +second, 0);\n }\n },\n\n dateShortWithTimeLong12: {\n regex: RegExp('^' + reDateNoYear + reHour12 + '[:.]' + reMinute + '[:.]' + reSecondlz + reSpaceOpt + reMeridian, 'i'),\n name: 'dateshortwithtimelong12',\n callback: function callback(match, month, day, hour, minute, second, meridian) {\n return this.ymd(this.y, lookupMonth(month), +day) && this.time(processMeridian(+hour, meridian), +minute, +second, 0);\n }\n },\n\n dateShortWithTimeShort: {\n regex: RegExp('^' + reDateNoYear + 't?' + reHour24 + '[:.]' + reMinute, 'i'),\n name: 'dateshortwithtimeshort',\n callback: function callback(match, month, day, hour, minute) {\n return this.ymd(this.y, lookupMonth(month), +day) && this.time(+hour, +minute, 0, 0);\n }\n },\n\n dateShortWithTimeShort12: {\n regex: RegExp('^' + reDateNoYear + reHour12 + '[:.]' + reMinutelz + reSpaceOpt + reMeridian, 'i'),\n name: 'dateshortwithtimeshort12',\n callback: function callback(match, month, day, hour, minute, meridian) {\n return this.ymd(this.y, lookupMonth(month), +day) && this.time(processMeridian(+hour, meridian), +minute, 0, 0);\n }\n }\n};\n\nvar resultProto = {\n // date\n y: NaN,\n m: NaN,\n d: NaN,\n // time\n h: NaN,\n i: NaN,\n s: NaN,\n f: NaN,\n\n // relative shifts\n ry: 0,\n rm: 0,\n rd: 0,\n rh: 0,\n ri: 0,\n rs: 0,\n rf: 0,\n\n // weekday related shifts\n weekday: NaN,\n weekdayBehavior: 0,\n\n // first or last day of month\n // 0 none, 1 first, -1 last\n firstOrLastDayOfMonth: 0,\n\n // timezone correction in minutes\n z: NaN,\n\n // counters\n dates: 0,\n times: 0,\n zones: 0,\n\n // helper functions\n ymd: function ymd(y, m, d) {\n if (this.dates > 0) {\n return false;\n }\n\n this.dates++;\n this.y = y;\n this.m = m;\n this.d = d;\n return true;\n },\n time: function time(h, i, s, f) {\n if (this.times > 0) {\n return false;\n }\n\n this.times++;\n this.h = h;\n this.i = i;\n this.s = s;\n this.f = f;\n\n return true;\n },\n resetTime: function resetTime() {\n this.h = 0;\n this.i = 0;\n this.s = 0;\n this.f = 0;\n this.times = 0;\n\n return true;\n },\n zone: function zone(minutes) {\n if (this.zones <= 1) {\n this.zones++;\n this.z = minutes;\n return true;\n }\n\n return false;\n },\n toDate: function toDate(relativeTo) {\n if (this.dates && !this.times) {\n this.h = this.i = this.s = this.f = 0;\n }\n\n // fill holes\n if (isNaN(this.y)) {\n this.y = relativeTo.getFullYear();\n }\n\n if (isNaN(this.m)) {\n this.m = relativeTo.getMonth();\n }\n\n if (isNaN(this.d)) {\n this.d = relativeTo.getDate();\n }\n\n if (isNaN(this.h)) {\n this.h = relativeTo.getHours();\n }\n\n if (isNaN(this.i)) {\n this.i = relativeTo.getMinutes();\n }\n\n if (isNaN(this.s)) {\n this.s = relativeTo.getSeconds();\n }\n\n if (isNaN(this.f)) {\n this.f = relativeTo.getMilliseconds();\n }\n\n // adjust special early\n switch (this.firstOrLastDayOfMonth) {\n case 1:\n this.d = 1;\n break;\n case -1:\n this.d = 0;\n this.m += 1;\n break;\n }\n\n if (!isNaN(this.weekday)) {\n var date = new Date(relativeTo.getTime());\n date.setFullYear(this.y, this.m, this.d);\n date.setHours(this.h, this.i, this.s, this.f);\n\n var dow = date.getDay();\n\n if (this.weekdayBehavior === 2) {\n // To make \"this week\" work, where the current day of week is a \"sunday\"\n if (dow === 0 && this.weekday !== 0) {\n this.weekday = -6;\n }\n\n // To make \"sunday this week\" work, where the current day of week is not a \"sunday\"\n if (this.weekday === 0 && dow !== 0) {\n this.weekday = 7;\n }\n\n this.d -= dow;\n this.d += this.weekday;\n } else {\n var diff = this.weekday - dow;\n\n // some PHP magic\n if (this.rd < 0 && diff < 0 || this.rd >= 0 && diff <= -this.weekdayBehavior) {\n diff += 7;\n }\n\n if (this.weekday >= 0) {\n this.d += diff;\n } else {\n this.d -= 7 - (Math.abs(this.weekday) - dow);\n }\n\n this.weekday = NaN;\n }\n }\n\n // adjust relative\n this.y += this.ry;\n this.m += this.rm;\n this.d += this.rd;\n\n this.h += this.rh;\n this.i += this.ri;\n this.s += this.rs;\n this.f += this.rf;\n\n this.ry = this.rm = this.rd = 0;\n this.rh = this.ri = this.rs = this.rf = 0;\n\n var result = new Date(relativeTo.getTime());\n // since Date constructor treats years <= 99 as 1900+\n // it can't be used, thus this weird way\n result.setFullYear(this.y, this.m, this.d);\n result.setHours(this.h, this.i, this.s, this.f);\n\n // note: this is done twice in PHP\n // early when processing special relatives\n // and late\n // todo: check if the logic can be reduced\n // to just one time action\n switch (this.firstOrLastDayOfMonth) {\n case 1:\n result.setDate(1);\n break;\n case -1:\n result.setMonth(result.getMonth() + 1, 0);\n break;\n }\n\n // adjust timezone\n if (!isNaN(this.z) && result.getTimezoneOffset() !== this.z) {\n result.setUTCFullYear(result.getFullYear(), result.getMonth(), result.getDate());\n\n result.setUTCHours(result.getHours(), result.getMinutes(), result.getSeconds() - this.z, result.getMilliseconds());\n }\n\n return result;\n }\n};\n\nmodule.exports = function strtotime(str, now) {\n // discuss at: https://locutus.io/php/strtotime/\n // original by: Caio Ariede (https://caioariede.com)\n // improved by: Kevin van Zonneveld (https://kvz.io)\n // improved by: Caio Ariede (https://caioariede.com)\n // improved by: A. Matías Quezada (https://amatiasq.com)\n // improved by: preuter\n // improved by: Brett Zamir (https://brett-zamir.me)\n // improved by: Mirko Faber\n // input by: David\n // bugfixed by: Wagner B. Soares\n // bugfixed by: Artur Tchernychev\n // bugfixed by: Stephan Bösch-Plepelits (https://github.com/plepe)\n // reimplemented by: Rafał Kukawski\n // note 1: Examples all have a fixed timestamp to prevent\n // note 1: tests to fail because of variable time(zones)\n // example 1: strtotime('+1 day', 1129633200)\n // returns 1: 1129719600\n // example 2: strtotime('+1 week 2 days 4 hours 2 seconds', 1129633200)\n // returns 2: 1130425202\n // example 3: strtotime('last month', 1129633200)\n // returns 3: 1127041200\n // example 4: strtotime('2009-05-04 08:30:00+00')\n // returns 4: 1241425800\n // example 5: strtotime('2009-05-04 08:30:00+02:00')\n // returns 5: 1241418600\n // example 6: strtotime('2009-05-04 08:30:00 YWT')\n // returns 6: 1241454600\n // example 7: strtotime('10-JUL-17')\n // returns 7: 1499644800\n\n if (now == null) {\n now = Math.floor(Date.now() / 1000);\n }\n\n // the rule order is important\n // if multiple rules match, the longest match wins\n // if multiple rules match the same string, the first match wins\n var rules = [formats.yesterday, formats.now, formats.noon, formats.midnightOrToday, formats.tomorrow, formats.timestamp, formats.firstOrLastDay, formats.backOrFrontOf,\n // formats.weekdayOf, // not yet implemented\n formats.timeTiny12, formats.timeShort12, formats.timeLong12, formats.mssqltime, formats.oracledate, formats.timeShort24, formats.timeLong24, formats.iso8601long, formats.gnuNoColon, formats.iso8601noColon, formats.americanShort, formats.american, formats.iso8601date4, formats.iso8601dateSlash, formats.dateSlash, formats.gnuDateShortOrIso8601date2, formats.gnuDateShorter, formats.dateFull, formats.pointedDate4, formats.pointedDate2, formats.dateNoDay, formats.dateNoDayRev, formats.dateTextual, formats.dateNoYear, formats.dateNoYearRev, formats.dateNoColon, formats.xmlRpc, formats.xmlRpcNoColon, formats.soap, formats.wddx, formats.exif, formats.pgydotd, formats.isoWeekDay, formats.pgTextShort, formats.pgTextReverse, formats.clf, formats.year4, formats.ago, formats.dayText, formats.relativeTextWeek, formats.relativeText, formats.monthFullOrMonthAbbr, formats.tzCorrection, formats.tzAbbr, formats.dateShortWithTimeShort12, formats.dateShortWithTimeLong12, formats.dateShortWithTimeShort, formats.dateShortWithTimeLong, formats.relative, formats.whitespace];\n\n var result = Object.create(resultProto);\n\n while (str.length) {\n var longestMatch = null;\n var finalRule = null;\n\n for (var i = 0, l = rules.length; i < l; i++) {\n var format = rules[i];\n\n var match = str.match(format.regex);\n\n if (match) {\n if (!longestMatch || match[0].length > longestMatch[0].length) {\n longestMatch = match;\n finalRule = format;\n }\n }\n }\n\n if (!finalRule || finalRule.callback && finalRule.callback.apply(result, longestMatch) === false) {\n return false;\n }\n\n str = str.substr(longestMatch[0].length);\n finalRule = null;\n longestMatch = null;\n }\n\n return Math.floor(result.toDate(new Date(now * 1000)) / 1000);\n};\n//# sourceMappingURL=strtotime.js.map\n\n/***/ }),\n\n/***/ \"./node_modules/locutus/php/info/ini_get.js\":\n/*!**************************************************!*\\\n !*** ./node_modules/locutus/php/info/ini_get.js ***!\n \\**************************************************/\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\n\n\nmodule.exports = function ini_get(varname) {\n // discuss at: https://locutus.io/php/ini_get/\n // original by: Brett Zamir (https://brett-zamir.me)\n // note 1: The ini values must be set by ini_set or manually within an ini file\n // example 1: ini_set('date.timezone', 'Asia/Hong_Kong')\n // example 1: ini_get('date.timezone')\n // returns 1: 'Asia/Hong_Kong'\n\n var $global = typeof window !== 'undefined' ? window : __webpack_require__.g;\n $global.$locutus = $global.$locutus || {};\n var $locutus = $global.$locutus;\n $locutus.php = $locutus.php || {};\n $locutus.php.ini = $locutus.php.ini || {};\n\n if ($locutus.php.ini[varname] && $locutus.php.ini[varname].local_value !== undefined) {\n if ($locutus.php.ini[varname].local_value === null) {\n return '';\n }\n return $locutus.php.ini[varname].local_value;\n }\n\n return '';\n};\n//# sourceMappingURL=ini_get.js.map\n\n/***/ }),\n\n/***/ \"./node_modules/locutus/php/strings/strlen.js\":\n/*!****************************************************!*\\\n !*** ./node_modules/locutus/php/strings/strlen.js ***!\n \\****************************************************/\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\n\n\nmodule.exports = function strlen(string) {\n // discuss at: https://locutus.io/php/strlen/\n // original by: Kevin van Zonneveld (https://kvz.io)\n // improved by: Sakimori\n // improved by: Kevin van Zonneveld (https://kvz.io)\n // input by: Kirk Strobeck\n // bugfixed by: Onno Marsman (https://twitter.com/onnomarsman)\n // revised by: Brett Zamir (https://brett-zamir.me)\n // note 1: May look like overkill, but in order to be truly faithful to handling all Unicode\n // note 1: characters and to this function in PHP which does not count the number of bytes\n // note 1: but counts the number of characters, something like this is really necessary.\n // example 1: strlen('Kevin van Zonneveld')\n // returns 1: 19\n // example 2: ini_set('unicode.semantics', 'on')\n // example 2: strlen('A\\ud87e\\udc04Z')\n // returns 2: 3\n\n var str = string + '';\n\n var iniVal = ( true ? __webpack_require__(/*! ../info/ini_get */ \"./node_modules/locutus/php/info/ini_get.js\")('unicode.semantics') : 0) || 'off';\n if (iniVal === 'off') {\n return str.length;\n }\n\n var i = 0;\n var lgth = 0;\n\n var getWholeChar = function getWholeChar(str, i) {\n var code = str.charCodeAt(i);\n var next = '';\n var prev = '';\n if (code >= 0xd800 && code <= 0xdbff) {\n // High surrogate (could change last hex to 0xDB7F to\n // treat high private surrogates as single characters)\n if (str.length <= i + 1) {\n throw new Error('High surrogate without following low surrogate');\n }\n next = str.charCodeAt(i + 1);\n if (next < 0xdc00 || next > 0xdfff) {\n throw new Error('High surrogate without following low surrogate');\n }\n return str.charAt(i) + str.charAt(i + 1);\n } else if (code >= 0xdc00 && code <= 0xdfff) {\n // Low surrogate\n if (i === 0) {\n throw new Error('Low surrogate without preceding high surrogate');\n }\n prev = str.charCodeAt(i - 1);\n if (prev < 0xd800 || prev > 0xdbff) {\n // (could change last hex to 0xDB7F to treat high private surrogates\n // as single characters)\n throw new Error('Low surrogate without preceding high surrogate');\n }\n // We can pass over low surrogates now as the second\n // component in a pair which we have already processed\n return false;\n }\n return str.charAt(i);\n };\n\n for (i = 0, lgth = 0; i < str.length; i++) {\n if (getWholeChar(str, i) === false) {\n continue;\n }\n // Adapt this line at the top of any loop, passing in the whole string and\n // the current iteration and returning a variable to represent the individual character;\n // purpose is to treat the first part of a surrogate pair as the whole character and then\n // ignore the second part\n lgth++;\n }\n\n return lgth;\n};\n//# sourceMappingURL=strlen.js.map\n\n/***/ }),\n\n/***/ \"./node_modules/locutus/php/var/is_numeric.js\":\n/*!****************************************************!*\\\n !*** ./node_modules/locutus/php/var/is_numeric.js ***!\n \\****************************************************/\n/***/ (function(module) {\n\n\n\nmodule.exports = function is_numeric(mixedVar) {\n // discuss at: https://locutus.io/php/is_numeric/\n // original by: Kevin van Zonneveld (https://kvz.io)\n // improved by: David\n // improved by: taith\n // bugfixed by: Tim de Koning\n // bugfixed by: WebDevHobo (https://webdevhobo.blogspot.com/)\n // bugfixed by: Brett Zamir (https://brett-zamir.me)\n // bugfixed by: Denis Chenu (https://shnoulle.net)\n // example 1: is_numeric(186.31)\n // returns 1: true\n // example 2: is_numeric('Kevin van Zonneveld')\n // returns 2: false\n // example 3: is_numeric(' +186.31e2')\n // returns 3: true\n // example 4: is_numeric('')\n // returns 4: false\n // example 5: is_numeric([])\n // returns 5: false\n // example 6: is_numeric('1 ')\n // returns 6: false\n\n var whitespace = [' ', '\\n', '\\r', '\\t', '\\f', '\\x0b', '\\xa0', '\\u2000', '\\u2001', '\\u2002', '\\u2003', '\\u2004', '\\u2005', '\\u2006', '\\u2007', '\\u2008', '\\u2009', '\\u200A', '\\u200B', '\\u2028', '\\u2029', '\\u3000'].join('');\n\n // @todo: Break this up using many single conditions with early returns\n return (typeof mixedVar === 'number' || typeof mixedVar === 'string' && whitespace.indexOf(mixedVar.slice(-1)) === -1) && mixedVar !== '' && !isNaN(mixedVar);\n};\n//# sourceMappingURL=is_numeric.js.map\n\n/***/ })\n\n/******/ \t});\n/************************************************************************/\n/******/ \t// The module cache\n/******/ \tvar __webpack_module_cache__ = {};\n/******/ \t\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/ \t\t// Check if module is in cache\n/******/ \t\tvar cachedModule = __webpack_module_cache__[moduleId];\n/******/ \t\tif (cachedModule !== undefined) {\n/******/ \t\t\treturn cachedModule.exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = __webpack_module_cache__[moduleId] = {\n/******/ \t\t\t// no module.id needed\n/******/ \t\t\t// no module.loaded needed\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/ \t\n/******/ \t\t// Execute the module function\n/******/ \t\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n/******/ \t\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/ \t\n/************************************************************************/\n/******/ \t/* webpack/runtime/compat get default export */\n/******/ \t!function() {\n/******/ \t\t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t\t__webpack_require__.n = function(module) {\n/******/ \t\t\tvar getter = module && module.__esModule ?\n/******/ \t\t\t\tfunction() { return module['default']; } :\n/******/ \t\t\t\tfunction() { return module; };\n/******/ \t\t\t__webpack_require__.d(getter, { a: getter });\n/******/ \t\t\treturn getter;\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/define property getters */\n/******/ \t!function() {\n/******/ \t\t// define getter functions for harmony exports\n/******/ \t\t__webpack_require__.d = function(exports, definition) {\n/******/ \t\t\tfor(var key in definition) {\n/******/ \t\t\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n/******/ \t\t\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n/******/ \t\t\t\t}\n/******/ \t\t\t}\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/global */\n/******/ \t!function() {\n/******/ \t\t__webpack_require__.g = (function() {\n/******/ \t\t\tif (typeof globalThis === 'object') return globalThis;\n/******/ \t\t\ttry {\n/******/ \t\t\t\treturn this || new Function('return this')();\n/******/ \t\t\t} catch (e) {\n/******/ \t\t\t\tif (typeof window === 'object') return window;\n/******/ \t\t\t}\n/******/ \t\t})();\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/hasOwnProperty shorthand */\n/******/ \t!function() {\n/******/ \t\t__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/make namespace object */\n/******/ \t!function() {\n/******/ \t\t// define __esModule on exports\n/******/ \t\t__webpack_require__.r = function(exports) {\n/******/ \t\t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n/******/ \t\t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n/******/ \t\t\t}\n/******/ \t\t\tObject.defineProperty(exports, '__esModule', { value: true });\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/************************************************************************/\nvar __webpack_exports__ = {};\n// This entry needs to be wrapped in an IIFE because it needs to be isolated against other modules in the chunk.\n!function() {\n/*!****************************************!*\\\n !*** ./resources/assets/js/helpers.js ***!\n \\****************************************/\n__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var locutus_php_strings_strlen__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! locutus/php/strings/strlen */ \"./node_modules/locutus/php/strings/strlen.js\");\n/* harmony import */ var locutus_php_strings_strlen__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(locutus_php_strings_strlen__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var locutus_php_array_array_diff__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! locutus/php/array/array_diff */ \"./node_modules/locutus/php/array/array_diff.js\");\n/* harmony import */ var locutus_php_array_array_diff__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(locutus_php_array_array_diff__WEBPACK_IMPORTED_MODULE_1__);\n/* harmony import */ var locutus_php_datetime_strtotime__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! locutus/php/datetime/strtotime */ \"./node_modules/locutus/php/datetime/strtotime.js\");\n/* harmony import */ var locutus_php_datetime_strtotime__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(locutus_php_datetime_strtotime__WEBPACK_IMPORTED_MODULE_2__);\n/* harmony import */ var locutus_php_var_is_numeric__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! locutus/php/var/is_numeric */ \"./node_modules/locutus/php/var/is_numeric.js\");\n/* harmony import */ var locutus_php_var_is_numeric__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(locutus_php_var_is_numeric__WEBPACK_IMPORTED_MODULE_3__);\n/*!\n * Laravel Javascript Validation\n *\n * https://github.com/proengsoft/laravel-jsvalidation\n *\n * Helper functions used by validators\n *\n * Copyright (c) 2017 Proengsoft\n * Released under the MIT license\n */\n\n\n\n\n\n$.extend(true, laravelValidation, {\n helpers: {\n /**\n * Numeric rules\n */\n numericRules: ['Integer', 'Numeric'],\n /**\n * Gets the file information from file input.\n *\n * @param fieldObj\n * @param index\n * @returns {{file: *, extension: string, size: number}}\n */\n fileinfo: function (fieldObj, index) {\n var FileName = fieldObj.value;\n index = typeof index !== 'undefined' ? index : 0;\n if (fieldObj.files !== null) {\n if (typeof fieldObj.files[index] !== 'undefined') {\n return {\n file: FileName,\n extension: FileName.substr(FileName.lastIndexOf('.') + 1),\n size: fieldObj.files[index].size / 1024,\n type: fieldObj.files[index].type\n };\n }\n }\n return false;\n },\n /**\n * Gets the selectors for th specified field names.\n *\n * @param names\n * @returns {string}\n */\n selector: function (names) {\n var selector = [];\n if (!this.isArray(names)) {\n names = [names];\n }\n for (var i = 0; i < names.length; i++) {\n selector.push(\"[name='\" + names[i] + \"']\");\n }\n return selector.join();\n },\n /**\n * Check if element has numeric rules.\n *\n * @param element\n * @returns {boolean}\n */\n hasNumericRules: function (element) {\n return this.hasRules(element, this.numericRules);\n },\n /**\n * Check if element has passed rules.\n *\n * @param element\n * @param rules\n * @returns {boolean}\n */\n hasRules: function (element, rules) {\n var found = false;\n if (typeof rules === 'string') {\n rules = [rules];\n }\n var validator = $.data(element.form, \"validator\");\n var listRules = [];\n var cache = validator.arrayRulesCache;\n if (element.name in cache) {\n $.each(cache[element.name], function (index, arrayRule) {\n listRules.push(arrayRule);\n });\n }\n if (element.name in validator.settings.rules) {\n listRules.push(validator.settings.rules[element.name]);\n }\n $.each(listRules, function (index, objRules) {\n if ('laravelValidation' in objRules) {\n var _rules = objRules.laravelValidation;\n for (var i = 0; i < _rules.length; i++) {\n if ($.inArray(_rules[i][0], rules) !== -1) {\n found = true;\n return false;\n }\n }\n }\n });\n return found;\n },\n /**\n * Return the string length using PHP function.\n * http://php.net/manual/en/function.strlen.php\n * http://phpjs.org/functions/strlen/\n *\n * @param string\n */\n strlen: function (string) {\n return locutus_php_strings_strlen__WEBPACK_IMPORTED_MODULE_0___default()(string);\n },\n /**\n * Get the size of the object depending of his type.\n *\n * @param obj\n * @param element\n * @param value\n * @returns int\n */\n getSize: function getSize(obj, element, value) {\n if (this.hasNumericRules(element) && this.is_numeric(value)) {\n return parseFloat(value);\n } else if (this.isArray(value)) {\n return parseFloat(value.length);\n } else if (element.type === 'file') {\n return parseFloat(Math.floor(this.fileinfo(element).size));\n }\n return parseFloat(this.strlen(value));\n },\n /**\n * Return specified rule from element.\n *\n * @param rule\n * @param element\n * @returns object\n */\n getLaravelValidation: function (rule, element) {\n var found = undefined;\n $.each($.validator.staticRules(element), function (key, rules) {\n if (key === \"laravelValidation\") {\n $.each(rules, function (i, value) {\n if (value[0] === rule) {\n found = value;\n }\n });\n }\n });\n return found;\n },\n /**\n * Return he timestamp of value passed using format or default format in element.\n *\n * @param value\n * @param format\n * @returns {boolean|int}\n */\n parseTime: function (value, format) {\n var timeValue = false;\n var fmt = new DateFormatter();\n if (typeof value === 'number' && typeof format === 'undefined') {\n return value;\n }\n if (typeof format === 'object') {\n var dateRule = this.getLaravelValidation('DateFormat', format);\n if (dateRule !== undefined) {\n format = dateRule[1][0];\n } else {\n format = null;\n }\n }\n if (format == null) {\n timeValue = this.strtotime(value);\n } else {\n timeValue = fmt.parseDate(value, format);\n if (timeValue instanceof Date && fmt.formatDate(timeValue, format) === value) {\n timeValue = Math.round(timeValue.getTime() / 1000);\n } else {\n timeValue = false;\n }\n }\n return timeValue;\n },\n /**\n * Compare a given date against another using an operator.\n *\n * @param validator\n * @param value\n * @param element\n * @param params\n * @param operator\n * @return {boolean}\n */\n compareDates: function (validator, value, element, params, operator) {\n var timeCompare = this.parseTime(params);\n if (!timeCompare) {\n var target = this.dependentElement(validator, element, params);\n if (target === undefined) {\n return false;\n }\n timeCompare = this.parseTime(validator.elementValue(target), target);\n }\n var timeValue = this.parseTime(value, element);\n if (timeValue === false) {\n return false;\n }\n switch (operator) {\n case '<':\n return timeValue < timeCompare;\n case '<=':\n return timeValue <= timeCompare;\n case '==':\n case '===':\n return timeValue === timeCompare;\n case '>':\n return timeValue > timeCompare;\n case '>=':\n return timeValue >= timeCompare;\n default:\n throw new Error('Unsupported operator.');\n }\n },\n /**\n * This method allows you to intelligently guess the date by closely matching the specific format.\n *\n * @param value\n * @param format\n * @returns {Date}\n */\n guessDate: function (value, format) {\n var fmt = new DateFormatter();\n return fmt.guessDate(value, format);\n },\n /**\n * Returns Unix timestamp based on PHP function strototime.\n * http://php.net/manual/es/function.strtotime.php\n * http://phpjs.org/functions/strtotime/\n *\n * @param text\n * @param now\n * @returns {*}\n */\n strtotime: function (text, now) {\n return locutus_php_datetime_strtotime__WEBPACK_IMPORTED_MODULE_2___default()(text, now);\n },\n /**\n * Returns if value is numeric.\n * http://php.net/manual/es/var.is_numeric.php\n * http://phpjs.org/functions/is_numeric/\n *\n * @param mixed_var\n * @returns {*}\n */\n is_numeric: function (mixed_var) {\n return locutus_php_var_is_numeric__WEBPACK_IMPORTED_MODULE_3___default()(mixed_var);\n },\n /**\n * Check whether the argument is of type Array.\n * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray#Polyfill\n *\n * @param arg\n * @returns {boolean}\n */\n isArray: function (arg) {\n return Object.prototype.toString.call(arg) === '[object Array]';\n },\n /**\n * Returns Array diff based on PHP function array_diff.\n * http://php.net/manual/es/function.array_diff.php\n * http://phpjs.org/functions/array_diff/\n *\n * @param arr1\n * @param arr2\n * @returns {*}\n */\n arrayDiff: function (arr1, arr2) {\n return locutus_php_array_array_diff__WEBPACK_IMPORTED_MODULE_1___default()(arr1, arr2);\n },\n /**\n * Check whether two arrays are equal to one another.\n *\n * @param arr1\n * @param arr2\n * @returns {*}\n */\n arrayEquals: function (arr1, arr2) {\n if (!this.isArray(arr1) || !this.isArray(arr2)) {\n return false;\n }\n if (arr1.length !== arr2.length) {\n return false;\n }\n return $.isEmptyObject(this.arrayDiff(arr1, arr2));\n },\n /**\n * Makes element dependant from other.\n *\n * @param validator\n * @param element\n * @param name\n * @returns {*}\n */\n dependentElement: function (validator, element, name) {\n var el = validator.findByName(name);\n var targetElement = el[el.length - 1];\n if (targetElement !== undefined && validator.settings.onfocusout) {\n var event = 'blur';\n if (targetElement.tagName === 'SELECT' || targetElement.tagName === 'OPTION' || targetElement.type === 'checkbox' || targetElement.type === 'radio') {\n event = 'click';\n }\n var ruleName = '.validate-laravelValidation';\n $(targetElement).off(ruleName).off(event + ruleName + '-' + element.name).on(event + ruleName + '-' + element.name, function () {\n $(element).valid();\n });\n }\n return targetElement;\n },\n /**\n * Parses error Ajax response and gets the message.\n *\n * @param response\n * @returns {string[]}\n */\n parseErrorResponse: function (response) {\n var newResponse = ['Whoops, looks like something went wrong.'];\n if ('responseText' in response) {\n var errorMsg = response.responseText.match(/(.*)<\\/h1\\s*>/i);\n if (this.isArray(errorMsg)) {\n newResponse = [errorMsg[1]];\n }\n }\n return newResponse;\n },\n /**\n * Escape string to use as Regular Expression.\n *\n * @param str\n * @returns string\n */\n escapeRegExp: function (str) {\n return str.replace(/[\\-\\[\\]\\/\\{\\}\\(\\)\\*\\+\\?\\.\\\\\\^\\$\\|]/g, \"\\\\$&\");\n },\n /**\n * Generate RegExp from wildcard attributes.\n *\n * @param name\n * @returns {RegExp}\n */\n regexFromWildcard: function (name) {\n var nameParts = name.split('[*]');\n if (nameParts.length === 1) nameParts.push('');\n return new RegExp('^' + nameParts.map(function (x) {\n return laravelValidation.helpers.escapeRegExp(x);\n }).join('\\\\[[^\\\\]]*\\\\]') + '$');\n },\n /**\n * Merge additional laravel validation rules into the current rule set.\n *\n * @param {object} rules\n * @param {object} newRules\n * @returns {object}\n */\n mergeRules: function (rules, newRules) {\n var rulesList = {\n 'laravelValidation': newRules.laravelValidation || [],\n 'laravelValidationRemote': newRules.laravelValidationRemote || []\n };\n for (var key in rulesList) {\n if (rulesList[key].length === 0) {\n continue;\n }\n if (typeof rules[key] === \"undefined\") {\n rules[key] = [];\n }\n rules[key] = rules[key].concat(rulesList[key]);\n }\n return rules;\n },\n /**\n * HTML entity encode a string.\n *\n * @param string\n * @returns {string}\n */\n encode: function (string) {\n return $('
').text(string).html();\n },\n /**\n * Lookup name in an array.\n *\n * @param validator\n * @param {string} name Name in dot notation format.\n * @returns {*}\n */\n findByArrayName: function (validator, name) {\n var sqName = name.replace(/\\.([^\\.]+)/g, '[$1]'),\n lookups = [\n // Convert dot to square brackets. e.g. foo.bar.0 becomes foo[bar][0]\n sqName,\n // Append [] to the name e.g. foo becomes foo[] or foo.bar.0 becomes foo[bar][0][]\n sqName + '[]',\n // Remove key from last array e.g. foo[bar][0] becomes foo[bar][]\n sqName.replace(/(.*)\\[(.*)\\]$/g, '$1[]')];\n for (var i = 0; i < lookups.length; i++) {\n var elem = validator.findByName(lookups[i]);\n if (elem.length > 0) {\n return elem;\n }\n }\n return $(null);\n },\n /**\n * Attempt to find an element in the DOM matching the given name.\n * Example names include:\n * - domain.0 which matches domain[]\n * - customfield.3 which matches customfield[3]\n *\n * @param validator\n * @param {string} name\n * @returns {*}\n */\n findByName: function (validator, name) {\n // Exact match.\n var elem = validator.findByName(name);\n if (elem.length > 0) {\n return elem;\n }\n\n // Find name in data, using dot notation.\n var delim = '.',\n parts = name.split(delim);\n for (var i = parts.length; i > 0; i--) {\n var reconstructed = [];\n for (var c = 0; c < i; c++) {\n reconstructed.push(parts[c]);\n }\n elem = this.findByArrayName(validator, reconstructed.join(delim));\n if (elem.length > 0) {\n return elem;\n }\n }\n return $(null);\n },\n /**\n * If it's an array element, get all values.\n *\n * @param validator\n * @param element\n * @returns {*|string}\n */\n allElementValues: function (validator, element) {\n if (element.name.indexOf('[]') !== -1) {\n return validator.findByName(element.name).map(function (i, e) {\n return validator.elementValue(e);\n }).get();\n }\n return validator.elementValue(element);\n }\n }\n});\n}();\n/******/ })()\n;\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGVscGVycy5qcyIsIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7O0FBQWE7O0FBRWI7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsbUJBQW1COztBQUVuQjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSxnQkFBZ0IsVUFBVTtBQUMxQjtBQUNBO0FBQ0E7QUFDQTtBQUNBLDZCQUE2QjtBQUM3QjtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7Ozs7Ozs7OztBQ2hDYTs7QUFFYjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUEscUJBQXFCLElBQUk7QUFDekIsc0JBQXNCLEVBQUU7QUFDeEIsc0JBQXNCLEVBQUU7QUFDeEIsbUNBQW1DLEVBQUU7QUFDckM7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLDZCQUE2QixJQUFJLElBQUksSUFBSSxHQUFHLElBQUk7QUFDaEQ7O0FBRUE7QUFDQSw4QkFBOEIsSUFBSTtBQUNsQztBQUNBOztBQUVBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLHdEQUF3RCxJQUFJO0FBQzVEOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQSxpQkFBaUIsRUFBRSxTQUFTLEVBQUUsTUFBTSxFQUFFO0FBQ3RDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU87QUFDUDtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBLDhCQUE4QixJQUFJO0FBQ2xDO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSxHQUFHO0FBQ0g7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLEdBQUc7QUFDSDtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSxRQUFRO0FBQ1I7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLFVBQVU7QUFDVjtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNB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QWUsQ0FBQ3pGLFNBQVMsRUFBRStGLGFBQWEsQ0FBQ3JHLElBQUksQ0FBQ21HLEtBQUssQ0FBQyxDQUFDO1FBQ2pFLElBQUlELElBQUksQ0FBQ3BHLE1BQU0sR0FBRyxDQUFDLEVBQUU7VUFDakIsT0FBT29HLElBQUk7UUFDZjtNQUNKO01BRUEsT0FBT3pILENBQUMsQ0FBQyxJQUFJLENBQUM7SUFDbEIsQ0FBQztJQUVEO0FBQ1I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0lBQ1E4SCxnQkFBZ0IsRUFBRSxTQUFBQSxDQUFVakcsU0FBUyxFQUFFSixPQUFPLEVBQUU7TUFDNUMsSUFBSUEsT0FBTyxDQUFDVSxJQUFJLENBQUM0RixPQUFPLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUU7UUFDbkMsT0FBT2xHLFNBQVMsQ0FBQzBELFVBQVUsQ0FBQzlELE9BQU8sQ0FBQ1UsSUFBSSxDQUFDLENBQUMwRSxHQUFHLENBQUMsVUFBVXpGLENBQUMsRUFBRTRHLENBQUMsRUFBRTtVQUMxRCxPQUFPbkcsU0FBUyxDQUFDeUMsWUFBWSxDQUFDMEQsQ0FBQyxDQUFDO1FBQ3BDLENBQUMsQ0FBQyxDQUFDQyxHQUFHLENBQUMsQ0FBQztNQUNaO01BRUEsT0FBT3BHLFNBQVMsQ0FBQ3lDLFlBQVksQ0FBQzdDLE9BQU8sQ0FBQztJQUMxQztFQUNKO0FBQ0osQ0FBQyxDQUFDLEMiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vLi9ub2RlX21vZHVsZXMvbG9jdXR1cy9waHAvYXJyYXkvYXJyYXlfZGlmZi5qcyIsIndlYnBhY2s6Ly8vLi9ub2RlX21vZHVsZXMvbG9jdXR1cy9waHAvZGF0ZXRpbWUvc3RydG90aW1lLmpzIiwid2VicGFjazovLy8uL25vZGVfbW9kdWxlcy9sb2N1dHVzL3BocC9pbmZvL2luaV9nZXQuanMiLCJ3ZWJwYWNrOi8vLy4vbm9kZV9tb2R1bGVzL2xvY3V0dXMvcGhwL3N0cmluZ3Mvc3RybGVuLmpzIiwid2VicGFjazovLy8uL25vZGVfbW9kdWxlcy9sb2N1dHVzL3BocC92YXIvaXNfbnVtZXJpYy5qcyIsIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vL3dlYnBhY2svcnVudGltZS9jb21wYXQgZ2V0IGRlZmF1bHQgZXhwb3J0Iiwid2VicGFjazovLy93ZWJwYWNrL3J1bnRpbWUvZGVmaW5lIHByb3BlcnR5IGdldHRlcnMiLCJ3ZWJwYWNrOi8vL3dlYnBhY2svcnVudGltZS9nbG9iYWwiLCJ3ZWJwYWNrOi8vL3dlYnBhY2svcnVudGltZS9oYXNPd25Qcm9wZXJ0eSBzaG9ydGhhbmQiLCJ3ZWJwYWNrOi8vL3dlYnBhY2svcnVudGltZS9tYWtlIG5hbWVzcGFjZSBvYmplY3QiLCJ3ZWJwYWNrOi8vLy4vcmVzb3VyY2VzL2Fzc2V0cy9qcy9oZWxwZXJzLmpzIl0sInNvdXJjZXNDb250ZW50IjpbIid1c2Ugc3RyaWN0JztcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiBhcnJheV9kaWZmKGFycjEpIHtcbiAgLy8gIGRpc2N1c3MgYXQ6IGh0dHBzOi8vbG9jdXR1cy5pby9waHAvYXJyYXlfZGlmZi9cbiAgLy8gb3JpZ2luYWwgYnk6IEtldmluIHZhbiBab25uZXZlbGQgKGh0dHBzOi8va3Z6LmlvKVxuICAvLyBpbXByb3ZlZCBieTogU2Fuam95IFJveVxuICAvLyAgcmV2aXNlZCBieTogQnJldHQgWmFtaXIgKGh0dHBzOi8vYnJldHQtemFtaXIubWUpXG4gIC8vICAgZXhhbXBsZSAxOiBhcnJheV9kaWZmKFsnS2V2aW4nLCAndmFuJywgJ1pvbm5ldmVsZCddLCBbJ3ZhbicsICdab25uZXZlbGQnXSlcbiAgLy8gICByZXR1cm5zIDE6IHswOidLZXZpbid9XG5cbiAgdmFyIHJldEFyciA9IHt9O1xuICB2YXIgYXJnbCA9IGFyZ3VtZW50cy5sZW5ndGg7XG4gIHZhciBrMSA9ICcnO1xuICB2YXIgaSA9IDE7XG4gIHZhciBrID0gJyc7XG4gIHZhciBhcnIgPSB7fTtcblxuICBhcnIxa2V5czogZm9yIChrMSBpbiBhcnIxKSB7XG4gICAgZm9yIChpID0gMTsgaSA8IGFyZ2w7IGkrKykge1xuICAgICAgYXJyID0gYXJndW1lbnRzW2ldO1xuICAgICAgZm9yIChrIGluIGFycikge1xuICAgICAgICBpZiAoYXJyW2tdID09PSBhcnIxW2sxXSkge1xuICAgICAgICAgIC8vIElmIGl0IHJlYWNoZXMgaGVyZSwgaXQgd2FzIGZvdW5kIGluIGF0IGxlYXN0IG9uZSBhcnJheSwgc28gdHJ5IG5leHQgdmFsdWVcbiAgICAgICAgICBjb250aW51ZSBhcnIxa2V5czsgLy8gZXNsaW50LWRpc2FibGUtbGluZSBuby1sYWJlbHNcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgcmV0QXJyW2sxXSA9IGFycjFbazFdO1xuICAgIH1cbiAgfVxuXG4gIHJldHVybiByZXRBcnI7XG59O1xuLy8jIHNvdXJjZU1hcHBpbmdVUkw9YXJyYXlfZGlmZi5qcy5tYXAiLCIndXNlIHN0cmljdCc7XG5cbnZhciByZVNwYWNlID0gJ1sgXFxcXHRdKyc7XG52YXIgcmVTcGFjZU9wdCA9ICdbIFxcXFx0XSonO1xudmFyIHJlTWVyaWRpYW4gPSAnKD86KFthcF0pXFxcXC4/bVxcXFwuPyhbXFxcXHQgXXwkKSknO1xudmFyIHJlSG91cjI0ID0gJygyWzAtNF18WzAxXT9bMC05XSknO1xudmFyIHJlSG91cjI0bHogPSAnKFswMV1bMC05XXwyWzAtNF0pJztcbnZhciByZUhvdXIxMiA9ICcoMD9bMS05XXwxWzAtMl0pJztcbnZhciByZU1pbnV0ZSA9ICcoWzAtNV0/WzAtOV0pJztcbnZhciByZU1pbnV0ZWx6ID0gJyhbMC01XVswLTldKSc7XG52YXIgcmVTZWNvbmQgPSAnKDYwfFswLTVdP1swLTldKSc7XG52YXIgcmVTZWNvbmRseiA9ICcoNjB8WzAtNV1bMC05XSknO1xudmFyIHJlRnJhYyA9ICcoPzpcXFxcLihbMC05XSspKSc7XG5cbnZhciByZURheWZ1bGwgPSAnc3VuZGF5fG1vbmRheXx0dWVzZGF5fHdlZG5lc2RheXx0aHVyc2RheXxmcmlkYXl8c2F0dXJkYXknO1xudmFyIHJlRGF5YWJiciA9ICdzdW58bW9ufHR1ZXx3ZWR8dGh1fGZyaXxzYXQnO1xudmFyIHJlRGF5dGV4dCA9IHJlRGF5ZnVsbCArICd8JyArIHJlRGF5YWJiciArICd8d2Vla2RheXM/JztcblxudmFyIHJlUmVsdGV4dG51bWJlciA9ICdmaXJzdHxzZWNvbmR8dGhpcmR8Zm91cnRofGZpZnRofHNpeHRofHNldmVudGh8ZWlnaHRoP3xuaW50aHx0ZW50aHxlbGV2ZW50aHx0d2VsZnRoJztcbnZhciByZVJlbHRleHR0ZXh0ID0gJ25leHR8bGFzdHxwcmV2aW91c3x0aGlzJztcbnZhciByZVJlbHRleHR1bml0ID0gJyg/OnNlY29uZHxzZWN8bWludXRlfG1pbnxob3VyfGRheXxmb3J0bmlnaHR8Zm9ydGhuaWdodHxtb250aHx5ZWFyKXM/fHdlZWtzfCcgKyByZURheXRleHQ7XG5cbnZhciByZVllYXIgPSAnKFswLTldezEsNH0pJztcbnZhciByZVllYXIyID0gJyhbMC05XXsyfSknO1xudmFyIHJlWWVhcjQgPSAnKFswLTldezR9KSc7XG52YXIgcmVZZWFyNHdpdGhTaWduID0gJyhbKy1dP1swLTldezR9KSc7XG52YXIgcmVNb250aCA9ICcoMVswLTJdfDA/WzAtOV0pJztcbnZhciByZU1vbnRobHogPSAnKDBbMC05XXwxWzAtMl0pJztcbnZhciByZURheSA9ICcoPzooM1swMV18WzAtMl0/WzAtOV0pKD86c3R8bmR8cmR8dGgpPyknO1xudmFyIHJlRGF5bHogPSAnKDBbMC05XXxbMS0yXVswLTldfDNbMDFdKSc7XG5cbnZhciByZU1vbnRoRnVsbCA9ICdqYW51YXJ5fGZlYnJ1YXJ5fG1hcmNofGFwcmlsfG1heXxqdW5lfGp1bHl8YXVndXN0fHNlcHRlbWJlcnxvY3RvYmVyfG5vdmVtYmVyfGRlY2VtYmVyJztcbnZhciByZU1vbnRoQWJiciA9ICdqYW58ZmVifG1hcnxhcHJ8bWF5fGp1bnxqdWx8YXVnfHNlcHQ/fG9jdHxub3Z8ZGVjJztcbnZhciByZU1vbnRocm9tYW4gPSAnaVt2eF18dml7MCwzfXx4aXswLDJ9fGl7MSwzfSc7XG52YXIgcmVNb250aFRleHQgPSAnKCcgKyByZU1vbnRoRnVsbCArICd8JyArIHJlTW9udGhBYmJyICsgJ3wnICsgcmVNb250aHJvbWFuICsgJyknO1xuXG52YXIgcmVUekNvcnJlY3Rpb24gPSAnKCg/OkdNVCk/KFsrLV0pJyArIHJlSG91cjI0ICsgJzo/JyArIHJlTWludXRlICsgJz8pJztcbnZhciByZVR6QWJiciA9ICdcXFxcKD8oW2EtekEtWl17MSw2fSlcXFxcKT8nO1xudmFyIHJlRGF5T2ZZZWFyID0gJygwMFsxLTldfDBbMS05XVswLTldfFsxMl1bMC05XVswLTldfDNbMC01XVswLTldfDM2WzAtNl0pJztcbnZhciByZVdlZWtPZlllYXIgPSAnKDBbMS05XXxbMS00XVswLTldfDVbMC0zXSknO1xuXG52YXIgcmVEYXRlTm9ZZWFyID0gcmVNb250aFRleHQgKyAnWyAuXFxcXHQtXSonICsgcmVEYXkgKyAnWywuc3RuZHJoXFxcXHQgXSonO1xuXG5mdW5jdGlvbiBwcm9jZXNzTWVyaWRpYW4oaG91ciwgbWVyaWRpYW4pIHtcbiAgbWVyaWRpYW4gPSBtZXJpZGlhbiAmJiBtZXJpZGlhbi50b0xvd2VyQ2FzZSgpO1xuXG4gIHN3aXRjaCAobWVyaWRpYW4pIHtcbiAgICBjYXNlICdhJzpcbiAgICAgIGhvdXIgKz0gaG91ciA9PT0gMTIgPyAtMTIgOiAwO1xuICAgICAgYnJlYWs7XG4gICAgY2FzZSAncCc6XG4gICAgICBob3VyICs9IGhvdXIgIT09IDEyID8gMTIgOiAwO1xuICAgICAgYnJlYWs7XG4gIH1cblxuICByZXR1cm4gaG91cjtcbn1cblxuZnVuY3Rpb24gcHJvY2Vzc1llYXIoeWVhclN0cikge1xuICB2YXIgeWVhciA9ICt5ZWFyU3RyO1xuXG4gIGlmICh5ZWFyU3RyLmxlbmd0aCA8IDQgJiYgeWVhciA8IDEwMCkge1xuICAgIHllYXIgKz0geWVhciA8IDcwID8gMjAwMCA6IDE5MDA7XG4gIH1cblxuICByZXR1cm4geWVhcjtcbn1cblxuZnVuY3Rpb24gbG9va3VwTW9udGgobW9udGhTdHIpIHtcbiAgcmV0dXJuIHtcbiAgICBqYW46IDAsXG4gICAgamFudWFyeTogMCxcbiAgICBpOiAwLFxuICAgIGZlYjogMSxcbiAgICBmZWJydWFyeTogMSxcbiAgICBpaTogMSxcbiAgICBtYXI6IDIsXG4gICAgbWFyY2g6IDIsXG4gICAgaWlpOiAyLFxuICAgIGFwcjogMyxcbiAgICBhcHJpbDogMyxcbiAgICBpdjogMyxcbiAgICBtYXk6IDQsXG4gICAgdjogNCxcbiAgICBqdW46IDUsXG4gICAganVuZTogNSxcbiAgICB2aTogNSxcbiAgICBqdWw6IDYsXG4gICAganVseTogNixcbiAgICB2aWk6IDYsXG4gICAgYXVnOiA3LFxuICAgIGF1Z3VzdDogNyxcbiAgICB2aWlpOiA3LFxuICAgIHNlcDogOCxcbiAgICBzZXB0OiA4LFxuICAgIHNlcHRlbWJlcjogOCxcbiAgICBpeDogOCxcbiAgICBvY3Q6IDksXG4gICAgb2N0b2JlcjogOSxcbiAgICB4OiA5LFxuICAgIG5vdjogMTAsXG4gICAgbm92ZW1iZXI6IDEwLFxuICAgIHhpOiAxMCxcbiAgICBkZWM6IDExLFxuICAgIGRlY2VtYmVyOiAxMSxcbiAgICB4aWk6IDExXG4gIH1bbW9udGhTdHIudG9Mb3dlckNhc2UoKV07XG59XG5cbmZ1bmN0aW9uIGxvb2t1cFdlZWtkYXkoZGF5U3RyKSB7XG4gIHZhciBkZXNpcmVkU3VuZGF5TnVtYmVyID0gYXJndW1lbnRzLmxlbmd0aCA+IDEgJiYgYXJndW1lbnRzWzFdICE9PSB1bmRlZmluZWQgPyBhcmd1bWVudHNbMV0gOiAwO1xuXG4gIHZhciBkYXlOdW1iZXJzID0ge1xuICAgIG1vbjogMSxcbiAgICBtb25kYXk6IDEsXG4gICAgdHVlOiAyLFxuICAgIHR1ZXNkYXk6IDIsXG4gICAgd2VkOiAzLFxuICAgIHdlZG5lc2RheTogMyxcbiAgICB0aHU6IDQsXG4gICAgdGh1cnNkYXk6IDQsXG4gICAgZnJpOiA1LFxuICAgIGZyaWRheTogNSxcbiAgICBzYXQ6IDYsXG4gICAgc2F0dXJkYXk6IDYsXG4gICAgc3VuOiAwLFxuICAgIHN1bmRheTogMFxuICB9O1xuXG4gIHJldHVybiBkYXlOdW1iZXJzW2RheVN0ci50b0xvd2VyQ2FzZSgpXSB8fCBkZXNpcmVkU3VuZGF5TnVtYmVyO1xufVxuXG5mdW5jdGlvbiBsb29rdXBSZWxhdGl2ZShyZWxUZXh0KSB7XG4gIHZhciByZWxhdGl2ZU51bWJlcnMgPSB7XG4gICAgbGFzdDogLTEsXG4gICAgcHJldmlvdXM6IC0xLFxuICAgIHRoaXM6IDAsXG4gICAgZmlyc3Q6IDEsXG4gICAgbmV4dDogMSxcbiAgICBzZWNvbmQ6IDIsXG4gICAgdGhpcmQ6IDMsXG4gICAgZm91cnRoOiA0LFxuICAgIGZpZnRoOiA1LFxuICAgIHNpeHRoOiA2LFxuICAgIHNldmVudGg6IDcsXG4gICAgZWlnaHQ6IDgsXG4gICAgZWlnaHRoOiA4LFxuICAgIG5pbnRoOiA5LFxuICAgIHRlbnRoOiAxMCxcbiAgICBlbGV2ZW50aDogMTEsXG4gICAgdHdlbGZ0aDogMTJcbiAgfTtcblxuICB2YXIgcmVsYXRpdmVCZWhhdmlvciA9IHtcbiAgICB0aGlzOiAxXG4gIH07XG5cbiAgdmFyIHJlbFRleHRMb3dlciA9IHJlbFRleHQudG9Mb3dlckNhc2UoKTtcblxuICByZXR1cm4ge1xuICAgIGFtb3VudDogcmVsYXRpdmVOdW1iZXJzW3JlbFRleHRMb3dlcl0sXG4gICAgYmVoYXZpb3I6IHJlbGF0aXZlQmVoYXZpb3JbcmVsVGV4dExvd2VyXSB8fCAwXG4gIH07XG59XG5cbmZ1bmN0aW9uIHByb2Nlc3NUekNvcnJlY3Rpb24odHpPZmZzZXQsIG9sZFZhbHVlKSB7XG4gIHZhciByZVR6Q29ycmVjdGlvbkxvb3NlID0gLyg/OkdNVCk/KFsrLV0pKFxcZCspKDo/KShcXGR7MCwyfSkvaTtcbiAgdHpPZmZzZXQgPSB0ek9mZnNldCAmJiB0ek9mZnNldC5tYXRjaChyZVR6Q29ycmVjdGlvbkxvb3NlKTtcblxuICBpZiAoIXR6T2Zmc2V0KSB7XG4gICAgcmV0dXJuIG9sZFZhbHVlO1xuICB9XG5cbiAgdmFyIHNpZ24gPSB0ek9mZnNldFsxXSA9PT0gJy0nID8gLTEgOiAxO1xuICB2YXIgaG91cnMgPSArdHpPZmZzZXRbMl07XG4gIHZhciBtaW51dGVzID0gK3R6T2Zmc2V0WzRdO1xuXG4gIGlmICghdHpPZmZzZXRbNF0gJiYgIXR6T2Zmc2V0WzNdKSB7XG4gICAgbWludXRlcyA9IE1hdGguZmxvb3IoaG91cnMgJSAxMDApO1xuICAgIGhvdXJzID0gTWF0aC5mbG9vcihob3VycyAvIDEwMCk7XG4gIH1cblxuICAvLyB0aW1lem9uZSBvZmZzZXQgaW4gc2Vjb25kc1xuICByZXR1cm4gc2lnbiAqIChob3VycyAqIDYwICsgbWludXRlcykgKiA2MDtcbn1cblxuLy8gdHogYWJicmV2YXRpb24gOiB0eiBvZmZzZXQgaW4gc2Vjb25kc1xudmFyIHR6QWJick9mZnNldHMgPSB7XG4gIGFjZHQ6IDM3ODAwLFxuICBhY3N0OiAzNDIwMCxcbiAgYWRkdDogLTcyMDAsXG4gIGFkdDogLTEwODAwLFxuICBhZWR0OiAzOTYwMCxcbiAgYWVzdDogMzYwMDAsXG4gIGFoZHQ6IC0zMjQwMCxcbiAgYWhzdDogLTM2MDAwLFxuICBha2R0OiAtMjg4MDAsXG4gIGFrc3Q6IC0zMjQwMCxcbiAgYW10OiAtMTM4NDAsXG4gIGFwdDogLTEwODAwLFxuICBhc3Q6IC0xNDQwMCxcbiAgYXdkdDogMzI0MDAsXG4gIGF3c3Q6IDI4ODAwLFxuICBhd3Q6IC0xMDgwMCxcbiAgYmRzdDogNzIwMCxcbiAgYmR0OiAtMzYwMDAsXG4gIGJtdDogLTE0MzA5LFxuICBic3Q6IDM2MDAsXG4gIGNhc3Q6IDM0MjAwLFxuICBjYXQ6IDcyMDAsXG4gIGNkZHQ6IC0xNDQwMCxcbiAgY2R0OiAtMTgwMDAsXG4gIGNlbXQ6IDEwODAwLFxuICBjZXN0OiA3MjAwLFxuICBjZXQ6IDM2MDAsXG4gIGNtdDogLTE1NDA4LFxuICBjcHQ6IC0xODAwMCxcbiAgY3N0OiAtMjE2MDAsXG4gIGN3dDogLTE4MDAwLFxuICBjaHN0OiAzNjAwMCxcbiAgZG10OiAtMTUyMSxcbiAgZWF0OiAxMDgwMCxcbiAgZWRkdDogLTEwODAwLFxuICBlZHQ6IC0xNDQwMCxcbiAgZWVzdDogMTA4MDAsXG4gIGVldDogNzIwMCxcbiAgZW10OiAtMjYyNDgsXG4gIGVwdDogLTE0NDAwLFxuICBlc3Q6IC0xODAwMCxcbiAgZXd0OiAtMTQ0MDAsXG4gIGZmbXQ6IC0xNDY2MCxcbiAgZm10OiAtNDA1NixcbiAgZ2R0OiAzOTYwMCxcbiAgZ210OiAwLFxuICBnc3Q6IDM2MDAwLFxuICBoZHQ6IC0zNDIwMCxcbiAgaGtzdDogMzI0MDAsXG4gIGhrdDogMjg4MDAsXG4gIGhtdDogLTE5Nzc2LFxuICBocHQ6IC0zNDIwMCxcbiAgaHN0OiAtMzYwMDAsXG4gIGh3dDogLTM0MjAwLFxuICBpZGR0OiAxNDQwMCxcbiAgaWR0OiAxMDgwMCxcbiAgaW10OiAyNTAyNSxcbiAgaXN0OiA3MjAwLFxuICBqZHQ6IDM2MDAwLFxuICBqbXQ6IDg0NDAsXG4gIGpzdDogMzI0MDAsXG4gIGtkdDogMzYwMDAsXG4gIGttdDogNTczNixcbiAga3N0OiAzMDYwMCxcbiAgbHN0OiA5Mzk0LFxuICBtZGR0OiAtMTgwMDAsXG4gIG1kc3Q6IDE2Mjc5LFxuICBtZHQ6IC0yMTYwMCxcbiAgbWVzdDogNzIwMCxcbiAgbWV0OiAzNjAwLFxuICBtbXQ6IDkwMTcsXG4gIG1wdDogLTIxNjAwLFxuICBtc2Q6IDE0NDAwLFxuICBtc2s6IDEwODAwLFxuICBtc3Q6IC0yNTIwMCxcbiAgbXd0OiAtMjE2MDAsXG4gIG5kZHQ6IC01NDAwLFxuICBuZHQ6IC05MDUyLFxuICBucHQ6IC05MDAwLFxuICBuc3Q6IC0xMjYwMCxcbiAgbnd0OiAtOTAwMCxcbiAgbnpkdDogNDY4MDAsXG4gIG56bXQ6IDQxNDAwLFxuICBuenN0OiA0MzIwMCxcbiAgcGRkdDogLTIxNjAwLFxuICBwZHQ6IC0yNTIwMCxcbiAgcGtzdDogMjE2MDAsXG4gIHBrdDogMTgwMDAsXG4gIHBsbXQ6IDI1NTkwLFxuICBwbXQ6IC0xMzIzNixcbiAgcHBtdDogLTE3MzQwLFxuICBwcHQ6IC0yNTIwMCxcbiAgcHN0OiAtMjg4MDAsXG4gIHB3dDogLTI1MjAwLFxuICBxbXQ6IC0xODg0MCxcbiAgcm10OiA1Nzk0LFxuICBzYXN0OiA3MjAwLFxuICBzZG10OiAtMTY4MDAsXG4gIHNqbXQ6IC0yMDE3MyxcbiAgc210OiAtMTM4ODQsXG4gIHNzdDogLTM5NjAwLFxuICB0Ym10OiAxMDc1MSxcbiAgdG10OiAxMjM0NCxcbiAgdWN0OiAwLFxuICB1dGM6IDAsXG4gIHdhc3Q6IDcyMDAsXG4gIHdhdDogMzYwMCxcbiAgd2VtdDogNzIwMCxcbiAgd2VzdDogMzYwMCxcbiAgd2V0OiAwLFxuICB3aWI6IDI1MjAwLFxuICB3aXRhOiAyODgwMCxcbiAgd2l0OiAzMjQwMCxcbiAgd210OiA1MDQwLFxuICB5ZGR0OiAtMjUyMDAsXG4gIHlkdDogLTI4ODAwLFxuICB5cHQ6IC0yODgwMCxcbiAgeXN0OiAtMzI0MDAsXG4gIHl3dDogLTI4ODAwLFxuICBhOiAzNjAwLFxuICBiOiA3MjAwLFxuICBjOiAxMDgwMCxcbiAgZDogMTQ0MDAsXG4gIGU6IDE4MDAwLFxuICBmOiAyMTYwMCxcbiAgZzogMjUyMDAsXG4gIGg6IDI4ODAwLFxuICBpOiAzMjQwMCxcbiAgazogMzYwMDAsXG4gIGw6IDM5NjAwLFxuICBtOiA0MzIwMCxcbiAgbjogLTM2MDAsXG4gIG86IC03MjAwLFxuICBwOiAtMTA4MDAsXG4gIHE6IC0xNDQwMCxcbiAgcjogLTE4MDAwLFxuICBzOiAtMjE2MDAsXG4gIHQ6IC0yNTIwMCxcbiAgdTogLTI4ODAwLFxuICB2OiAtMzI0MDAsXG4gIHc6IC0zNjAwMCxcbiAgeDogLTM5NjAwLFxuICB5OiAtNDMyMDAsXG4gIHo6IDBcbn07XG5cbnZhciBmb3JtYXRzID0ge1xuICB5ZXN0ZXJkYXk6IHtcbiAgICByZWdleDogL155ZXN0ZXJkYXkvaSxcbiAgICBuYW1lOiAneWVzdGVyZGF5JyxcbiAgICBjYWxsYmFjazogZnVuY3Rpb24gY2FsbGJhY2soKSB7XG4gICAgICB0aGlzLnJkIC09IDE7XG4gICAgICByZXR1cm4gdGhpcy5yZXNldFRpbWUoKTtcbiAgICB9XG4gIH0sXG5cbiAgbm93OiB7XG4gICAgcmVnZXg6IC9ebm93L2ksXG4gICAgbmFtZTogJ25vdydcbiAgICAvLyBkbyBub3RoaW5nXG4gIH0sXG5cbiAgbm9vbjoge1xuICAgIHJlZ2V4OiAvXm5vb24vaSxcbiAgICBuYW1lOiAnbm9vbicsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKCkge1xuICAgICAgcmV0dXJuIHRoaXMucmVzZXRUaW1lKCkgJiYgdGhpcy50aW1lKDEyLCAwLCAwLCAwKTtcbiAgICB9XG4gIH0sXG5cbiAgbWlkbmlnaHRPclRvZGF5OiB7XG4gICAgcmVnZXg6IC9eKG1pZG5pZ2h0fHRvZGF5KS9pLFxuICAgIG5hbWU6ICdtaWRuaWdodCB8IHRvZGF5JyxcbiAgICBjYWxsYmFjazogZnVuY3Rpb24gY2FsbGJhY2soKSB7XG4gICAgICByZXR1cm4gdGhpcy5yZXNldFRpbWUoKTtcbiAgICB9XG4gIH0sXG5cbiAgdG9tb3Jyb3c6IHtcbiAgICByZWdleDogL150b21vcnJvdy9pLFxuICAgIG5hbWU6ICd0b21vcnJvdycsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKCkge1xuICAgICAgdGhpcy5yZCArPSAxO1xuICAgICAgcmV0dXJuIHRoaXMucmVzZXRUaW1lKCk7XG4gICAgfVxuICB9LFxuXG4gIHRpbWVzdGFtcDoge1xuICAgIHJlZ2V4OiAvXkAoLT9cXGQrKS9pLFxuICAgIG5hbWU6ICd0aW1lc3RhbXAnLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgdGltZXN0YW1wKSB7XG4gICAgICB0aGlzLnJzICs9ICt0aW1lc3RhbXA7XG4gICAgICB0aGlzLnkgPSAxOTcwO1xuICAgICAgdGhpcy5tID0gMDtcbiAgICAgIHRoaXMuZCA9IDE7XG4gICAgICB0aGlzLmRhdGVzID0gMDtcblxuICAgICAgcmV0dXJuIHRoaXMucmVzZXRUaW1lKCkgJiYgdGhpcy56b25lKDApO1xuICAgIH1cbiAgfSxcblxuICBmaXJzdE9yTGFzdERheToge1xuICAgIHJlZ2V4OiAvXihmaXJzdHxsYXN0KSBkYXkgb2YvaSxcbiAgICBuYW1lOiAnZmlyc3RkYXlvZiB8IGxhc3RkYXlvZicsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCBkYXkpIHtcbiAgICAgIGlmIChkYXkudG9Mb3dlckNhc2UoKSA9PT0gJ2ZpcnN0Jykge1xuICAgICAgICB0aGlzLmZpcnN0T3JMYXN0RGF5T2ZNb250aCA9IDE7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLmZpcnN0T3JMYXN0RGF5T2ZNb250aCA9IC0xO1xuICAgICAgfVxuICAgIH1cbiAgfSxcblxuICBiYWNrT3JGcm9udE9mOiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXihiYWNrfGZyb250KSBvZiAnICsgcmVIb3VyMjQgKyByZVNwYWNlT3B0ICsgcmVNZXJpZGlhbiArICc/JywgJ2knKSxcbiAgICBuYW1lOiAnYmFja29mIHwgZnJvbnRvZicsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCBzaWRlLCBob3VycywgbWVyaWRpYW4pIHtcbiAgICAgIHZhciBiYWNrID0gc2lkZS50b0xvd2VyQ2FzZSgpID09PSAnYmFjayc7XG4gICAgICB2YXIgaG91ciA9ICtob3VycztcbiAgICAgIHZhciBtaW51dGUgPSAxNTtcblxuICAgICAgaWYgKCFiYWNrKSB7XG4gICAgICAgIGhvdXIgLT0gMTtcbiAgICAgICAgbWludXRlID0gNDU7XG4gICAgICB9XG5cbiAgICAgIGhvdXIgPSBwcm9jZXNzTWVyaWRpYW4oaG91ciwgbWVyaWRpYW4pO1xuXG4gICAgICByZXR1cm4gdGhpcy5yZXNldFRpbWUoKSAmJiB0aGlzLnRpbWUoaG91ciwgbWludXRlLCAwLCAwKTtcbiAgICB9XG4gIH0sXG5cbiAgd2Vla2RheU9mOiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXignICsgcmVSZWx0ZXh0bnVtYmVyICsgJ3wnICsgcmVSZWx0ZXh0dGV4dCArICcpJyArIHJlU3BhY2UgKyAnKCcgKyByZURheWZ1bGwgKyAnfCcgKyByZURheWFiYnIgKyAnKScgKyByZVNwYWNlICsgJ29mJywgJ2knKSxcbiAgICBuYW1lOiAnd2Vla2RheW9mJ1xuICAgIC8vIHRvZG9cbiAgfSxcblxuICBtc3NxbHRpbWU6IHtcbiAgICByZWdleDogUmVnRXhwKCdeJyArIHJlSG91cjEyICsgJzonICsgcmVNaW51dGVseiArICc6JyArIHJlU2Vjb25kbHogKyAnWzouXShbMC05XSspJyArIHJlTWVyaWRpYW4sICdpJyksXG4gICAgbmFtZTogJ21zc3FsdGltZScsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCBob3VyLCBtaW51dGUsIHNlY29uZCwgZnJhYywgbWVyaWRpYW4pIHtcbiAgICAgIHJldHVybiB0aGlzLnRpbWUocHJvY2Vzc01lcmlkaWFuKCtob3VyLCBtZXJpZGlhbiksICttaW51dGUsICtzZWNvbmQsICtmcmFjLnN1YnN0cigwLCAzKSk7XG4gICAgfVxuICB9LFxuXG4gIG9yYWNsZWRhdGU6IHtcbiAgICByZWdleDogL14oXFxkezJ9KS0oW0EtWl17M30pLShcXGR7Mn0pJC9pLFxuICAgIG5hbWU6ICdkLU0teScsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCBkYXksIG1vbnRoVGV4dCwgeWVhcikge1xuICAgICAgdmFyIG1vbnRoID0ge1xuICAgICAgICBKQU46IDAsXG4gICAgICAgIEZFQjogMSxcbiAgICAgICAgTUFSOiAyLFxuICAgICAgICBBUFI6IDMsXG4gICAgICAgIE1BWTogNCxcbiAgICAgICAgSlVOOiA1LFxuICAgICAgICBKVUw6IDYsXG4gICAgICAgIEFVRzogNyxcbiAgICAgICAgU0VQOiA4LFxuICAgICAgICBPQ1Q6IDksXG4gICAgICAgIE5PVjogMTAsXG4gICAgICAgIERFQzogMTFcbiAgICAgIH1bbW9udGhUZXh0LnRvVXBwZXJDYXNlKCldO1xuICAgICAgcmV0dXJuIHRoaXMueW1kKDIwMDAgKyBwYXJzZUludCh5ZWFyLCAxMCksIG1vbnRoLCBwYXJzZUludChkYXksIDEwKSk7XG4gICAgfVxuICB9LFxuXG4gIHRpbWVMb25nMTI6IHtcbiAgICByZWdleDogUmVnRXhwKCdeJyArIHJlSG91cjEyICsgJ1s6Ll0nICsgcmVNaW51dGUgKyAnWzouXScgKyByZVNlY29uZGx6ICsgcmVTcGFjZU9wdCArIHJlTWVyaWRpYW4sICdpJyksXG4gICAgbmFtZTogJ3RpbWVsb25nMTInLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgaG91ciwgbWludXRlLCBzZWNvbmQsIG1lcmlkaWFuKSB7XG4gICAgICByZXR1cm4gdGhpcy50aW1lKHByb2Nlc3NNZXJpZGlhbigraG91ciwgbWVyaWRpYW4pLCArbWludXRlLCArc2Vjb25kLCAwKTtcbiAgICB9XG4gIH0sXG5cbiAgdGltZVNob3J0MTI6IHtcbiAgICByZWdleDogUmVnRXhwKCdeJyArIHJlSG91cjEyICsgJ1s6Ll0nICsgcmVNaW51dGVseiArIHJlU3BhY2VPcHQgKyByZU1lcmlkaWFuLCAnaScpLFxuICAgIG5hbWU6ICd0aW1lc2hvcnQxMicsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCBob3VyLCBtaW51dGUsIG1lcmlkaWFuKSB7XG4gICAgICByZXR1cm4gdGhpcy50aW1lKHByb2Nlc3NNZXJpZGlhbigraG91ciwgbWVyaWRpYW4pLCArbWludXRlLCAwLCAwKTtcbiAgICB9XG4gIH0sXG5cbiAgdGltZVRpbnkxMjoge1xuICAgIHJlZ2V4OiBSZWdFeHAoJ14nICsgcmVIb3VyMTIgKyByZVNwYWNlT3B0ICsgcmVNZXJpZGlhbiwgJ2knKSxcbiAgICBuYW1lOiAndGltZXRpbnkxMicsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCBob3VyLCBtZXJpZGlhbikge1xuICAgICAgcmV0dXJuIHRoaXMudGltZShwcm9jZXNzTWVyaWRpYW4oK2hvdXIsIG1lcmlkaWFuKSwgMCwgMCwgMCk7XG4gICAgfVxuICB9LFxuXG4gIHNvYXA6IHtcbiAgICByZWdleDogUmVnRXhwKCdeJyArIHJlWWVhcjQgKyAnLScgKyByZU1vbnRobHogKyAnLScgKyByZURheWx6ICsgJ1QnICsgcmVIb3VyMjRseiArICc6JyArIHJlTWludXRlbHogKyAnOicgKyByZVNlY29uZGx6ICsgcmVGcmFjICsgcmVUekNvcnJlY3Rpb24gKyAnPycsICdpJyksXG4gICAgbmFtZTogJ3NvYXAnLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgeWVhciwgbW9udGgsIGRheSwgaG91ciwgbWludXRlLCBzZWNvbmQsIGZyYWMsIHR6Q29ycmVjdGlvbikge1xuICAgICAgcmV0dXJuIHRoaXMueW1kKCt5ZWFyLCBtb250aCAtIDEsICtkYXkpICYmIHRoaXMudGltZSgraG91ciwgK21pbnV0ZSwgK3NlY29uZCwgK2ZyYWMuc3Vic3RyKDAsIDMpKSAmJiB0aGlzLnpvbmUocHJvY2Vzc1R6Q29ycmVjdGlvbih0ekNvcnJlY3Rpb24pKTtcbiAgICB9XG4gIH0sXG5cbiAgd2RkeDoge1xuICAgIHJlZ2V4OiBSZWdFeHAoJ14nICsgcmVZZWFyNCArICctJyArIHJlTW9udGggKyAnLScgKyByZURheSArICdUJyArIHJlSG91cjI0ICsgJzonICsgcmVNaW51dGUgKyAnOicgKyByZVNlY29uZCksXG4gICAgbmFtZTogJ3dkZHgnLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgeWVhciwgbW9udGgsIGRheSwgaG91ciwgbWludXRlLCBzZWNvbmQpIHtcbiAgICAgIHJldHVybiB0aGlzLnltZCgreWVhciwgbW9udGggLSAxLCArZGF5KSAmJiB0aGlzLnRpbWUoK2hvdXIsICttaW51dGUsICtzZWNvbmQsIDApO1xuICAgIH1cbiAgfSxcblxuICBleGlmOiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyByZVllYXI0ICsgJzonICsgcmVNb250aGx6ICsgJzonICsgcmVEYXlseiArICcgJyArIHJlSG91cjI0bHogKyAnOicgKyByZU1pbnV0ZWx6ICsgJzonICsgcmVTZWNvbmRseiwgJ2knKSxcbiAgICBuYW1lOiAnZXhpZicsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCB5ZWFyLCBtb250aCwgZGF5LCBob3VyLCBtaW51dGUsIHNlY29uZCkge1xuICAgICAgcmV0dXJuIHRoaXMueW1kKCt5ZWFyLCBtb250aCAtIDEsICtkYXkpICYmIHRoaXMudGltZSgraG91ciwgK21pbnV0ZSwgK3NlY29uZCwgMCk7XG4gICAgfVxuICB9LFxuXG4gIHhtbFJwYzoge1xuICAgIHJlZ2V4OiBSZWdFeHAoJ14nICsgcmVZZWFyNCArIHJlTW9udGhseiArIHJlRGF5bHogKyAnVCcgKyByZUhvdXIyNCArICc6JyArIHJlTWludXRlbHogKyAnOicgKyByZVNlY29uZGx6KSxcbiAgICBuYW1lOiAneG1scnBjJyxcbiAgICBjYWxsYmFjazogZnVuY3Rpb24gY2FsbGJhY2sobWF0Y2gsIHllYXIsIG1vbnRoLCBkYXksIGhvdXIsIG1pbnV0ZSwgc2Vjb25kKSB7XG4gICAgICByZXR1cm4gdGhpcy55bWQoK3llYXIsIG1vbnRoIC0gMSwgK2RheSkgJiYgdGhpcy50aW1lKCtob3VyLCArbWludXRlLCArc2Vjb25kLCAwKTtcbiAgICB9XG4gIH0sXG5cbiAgeG1sUnBjTm9Db2xvbjoge1xuICAgIHJlZ2V4OiBSZWdFeHAoJ14nICsgcmVZZWFyNCArIHJlTW9udGhseiArIHJlRGF5bHogKyAnW1R0XScgKyByZUhvdXIyNCArIHJlTWludXRlbHogKyByZVNlY29uZGx6KSxcbiAgICBuYW1lOiAneG1scnBjbm9jb2xvbicsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCB5ZWFyLCBtb250aCwgZGF5LCBob3VyLCBtaW51dGUsIHNlY29uZCkge1xuICAgICAgcmV0dXJuIHRoaXMueW1kKCt5ZWFyLCBtb250aCAtIDEsICtkYXkpICYmIHRoaXMudGltZSgraG91ciwgK21pbnV0ZSwgK3NlY29uZCwgMCk7XG4gICAgfVxuICB9LFxuXG4gIGNsZjoge1xuICAgIHJlZ2V4OiBSZWdFeHAoJ14nICsgcmVEYXkgKyAnLygnICsgcmVNb250aEFiYnIgKyAnKS8nICsgcmVZZWFyNCArICc6JyArIHJlSG91cjI0bHogKyAnOicgKyByZU1pbnV0ZWx6ICsgJzonICsgcmVTZWNvbmRseiArIHJlU3BhY2UgKyByZVR6Q29ycmVjdGlvbiwgJ2knKSxcbiAgICBuYW1lOiAnY2xmJyxcbiAgICBjYWxsYmFjazogZnVuY3Rpb24gY2FsbGJhY2sobWF0Y2gsIGRheSwgbW9udGgsIHllYXIsIGhvdXIsIG1pbnV0ZSwgc2Vjb25kLCB0ekNvcnJlY3Rpb24pIHtcbiAgICAgIHJldHVybiB0aGlzLnltZCgreWVhciwgbG9va3VwTW9udGgobW9udGgpLCArZGF5KSAmJiB0aGlzLnRpbWUoK2hvdXIsICttaW51dGUsICtzZWNvbmQsIDApICYmIHRoaXMuem9uZShwcm9jZXNzVHpDb3JyZWN0aW9uKHR6Q29ycmVjdGlvbikpO1xuICAgIH1cbiAgfSxcblxuICBpc284NjAxbG9uZzoge1xuICAgIHJlZ2V4OiBSZWdFeHAoJ150PycgKyByZUhvdXIyNCArICdbOi5dJyArIHJlTWludXRlICsgJ1s6Ll0nICsgcmVTZWNvbmQgKyByZUZyYWMsICdpJyksXG4gICAgbmFtZTogJ2lzbzg2MDFsb25nJyxcbiAgICBjYWxsYmFjazogZnVuY3Rpb24gY2FsbGJhY2sobWF0Y2gsIGhvdXIsIG1pbnV0ZSwgc2Vjb25kLCBmcmFjKSB7XG4gICAgICByZXR1cm4gdGhpcy50aW1lKCtob3VyLCArbWludXRlLCArc2Vjb25kLCArZnJhYy5zdWJzdHIoMCwgMykpO1xuICAgIH1cbiAgfSxcblxuICBkYXRlVGV4dHVhbDoge1xuICAgIHJlZ2V4OiBSZWdFeHAoJ14nICsgcmVNb250aFRleHQgKyAnWyAuXFxcXHQtXSonICsgcmVEYXkgKyAnWywuc3RuZHJoXFxcXHQgXSsnICsgcmVZZWFyLCAnaScpLFxuICAgIG5hbWU6ICdkYXRldGV4dHVhbCcsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCBtb250aCwgZGF5LCB5ZWFyKSB7XG4gICAgICByZXR1cm4gdGhpcy55bWQocHJvY2Vzc1llYXIoeWVhciksIGxvb2t1cE1vbnRoKG1vbnRoKSwgK2RheSk7XG4gICAgfVxuICB9LFxuXG4gIHBvaW50ZWREYXRlNDoge1xuICAgIHJlZ2V4OiBSZWdFeHAoJ14nICsgcmVEYXkgKyAnWy5cXFxcdC1dJyArIHJlTW9udGggKyAnWy4tXScgKyByZVllYXI0KSxcbiAgICBuYW1lOiAncG9pbnRlZGRhdGU0JyxcbiAgICBjYWxsYmFjazogZnVuY3Rpb24gY2FsbGJhY2sobWF0Y2gsIGRheSwgbW9udGgsIHllYXIpIHtcbiAgICAgIHJldHVybiB0aGlzLnltZCgreWVhciwgbW9udGggLSAxLCArZGF5KTtcbiAgICB9XG4gIH0sXG5cbiAgcG9pbnRlZERhdGUyOiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyByZURheSArICdbLlxcXFx0XScgKyByZU1vbnRoICsgJ1xcXFwuJyArIHJlWWVhcjIpLFxuICAgIG5hbWU6ICdwb2ludGVkZGF0ZTInLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgZGF5LCBtb250aCwgeWVhcikge1xuICAgICAgcmV0dXJuIHRoaXMueW1kKHByb2Nlc3NZZWFyKHllYXIpLCBtb250aCAtIDEsICtkYXkpO1xuICAgIH1cbiAgfSxcblxuICB0aW1lTG9uZzI0OiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXnQ/JyArIHJlSG91cjI0ICsgJ1s6Ll0nICsgcmVNaW51dGUgKyAnWzouXScgKyByZVNlY29uZCksXG4gICAgbmFtZTogJ3RpbWVsb25nMjQnLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgaG91ciwgbWludXRlLCBzZWNvbmQpIHtcbiAgICAgIHJldHVybiB0aGlzLnRpbWUoK2hvdXIsICttaW51dGUsICtzZWNvbmQsIDApO1xuICAgIH1cbiAgfSxcblxuICBkYXRlTm9Db2xvbjoge1xuICAgIHJlZ2V4OiBSZWdFeHAoJ14nICsgcmVZZWFyNCArIHJlTW9udGhseiArIHJlRGF5bHopLFxuICAgIG5hbWU6ICdkYXRlbm9jb2xvbicsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCB5ZWFyLCBtb250aCwgZGF5KSB7XG4gICAgICByZXR1cm4gdGhpcy55bWQoK3llYXIsIG1vbnRoIC0gMSwgK2RheSk7XG4gICAgfVxuICB9LFxuXG4gIHBneWRvdGQ6IHtcbiAgICByZWdleDogUmVnRXhwKCdeJyArIHJlWWVhcjQgKyAnXFxcXC4/JyArIHJlRGF5T2ZZZWFyKSxcbiAgICBuYW1lOiAncGd5ZG90ZCcsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCB5ZWFyLCBkYXkpIHtcbiAgICAgIHJldHVybiB0aGlzLnltZCgreWVhciwgMCwgK2RheSk7XG4gICAgfVxuICB9LFxuXG4gIHRpbWVTaG9ydDI0OiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXnQ/JyArIHJlSG91cjI0ICsgJ1s6Ll0nICsgcmVNaW51dGUsICdpJyksXG4gICAgbmFtZTogJ3RpbWVzaG9ydDI0JyxcbiAgICBjYWxsYmFjazogZnVuY3Rpb24gY2FsbGJhY2sobWF0Y2gsIGhvdXIsIG1pbnV0ZSkge1xuICAgICAgcmV0dXJuIHRoaXMudGltZSgraG91ciwgK21pbnV0ZSwgMCwgMCk7XG4gICAgfVxuICB9LFxuXG4gIGlzbzg2MDFub0NvbG9uOiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXnQ/JyArIHJlSG91cjI0bHogKyByZU1pbnV0ZWx6ICsgcmVTZWNvbmRseiwgJ2knKSxcbiAgICBuYW1lOiAnaXNvODYwMW5vY29sb24nLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgaG91ciwgbWludXRlLCBzZWNvbmQpIHtcbiAgICAgIHJldHVybiB0aGlzLnRpbWUoK2hvdXIsICttaW51dGUsICtzZWNvbmQsIDApO1xuICAgIH1cbiAgfSxcblxuICBpc284NjAxZGF0ZVNsYXNoOiB7XG4gICAgLy8gZXZlbnRob3VnaCB0aGUgdHJhaWxpbmcgc2xhc2ggaXMgb3B0aW9uYWwgaW4gUEhQXG4gICAgLy8gaGVyZSBpdCdzIG1hbmRhdG9yeSBhbmQgaW5wdXRzIHdpdGhvdXQgdGhlIHNsYXNoXG4gICAgLy8gYXJlIGhhbmRsZWQgYnkgZGF0ZXNsYXNoXG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyByZVllYXI0ICsgJy8nICsgcmVNb250aGx6ICsgJy8nICsgcmVEYXlseiArICcvJyksXG4gICAgbmFtZTogJ2lzbzg2MDFkYXRlc2xhc2gnLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgeWVhciwgbW9udGgsIGRheSkge1xuICAgICAgcmV0dXJuIHRoaXMueW1kKCt5ZWFyLCBtb250aCAtIDEsICtkYXkpO1xuICAgIH1cbiAgfSxcblxuICBkYXRlU2xhc2g6IHtcbiAgICByZWdleDogUmVnRXhwKCdeJyArIHJlWWVhcjQgKyAnLycgKyByZU1vbnRoICsgJy8nICsgcmVEYXkpLFxuICAgIG5hbWU6ICdkYXRlc2xhc2gnLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgeWVhciwgbW9udGgsIGRheSkge1xuICAgICAgcmV0dXJuIHRoaXMueW1kKCt5ZWFyLCBtb250aCAtIDEsICtkYXkpO1xuICAgIH1cbiAgfSxcblxuICBhbWVyaWNhbjoge1xuICAgIHJlZ2V4OiBSZWdFeHAoJ14nICsgcmVNb250aCArICcvJyArIHJlRGF5ICsgJy8nICsgcmVZZWFyKSxcbiAgICBuYW1lOiAnYW1lcmljYW4nLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgbW9udGgsIGRheSwgeWVhcikge1xuICAgICAgcmV0dXJuIHRoaXMueW1kKHByb2Nlc3NZZWFyKHllYXIpLCBtb250aCAtIDEsICtkYXkpO1xuICAgIH1cbiAgfSxcblxuICBhbWVyaWNhblNob3J0OiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyByZU1vbnRoICsgJy8nICsgcmVEYXkpLFxuICAgIG5hbWU6ICdhbWVyaWNhbnNob3J0JyxcbiAgICBjYWxsYmFjazogZnVuY3Rpb24gY2FsbGJhY2sobWF0Y2gsIG1vbnRoLCBkYXkpIHtcbiAgICAgIHJldHVybiB0aGlzLnltZCh0aGlzLnksIG1vbnRoIC0gMSwgK2RheSk7XG4gICAgfVxuICB9LFxuXG4gIGdudURhdGVTaG9ydE9ySXNvODYwMWRhdGUyOiB7XG4gICAgLy8gaXNvODYwMWRhdGUyIGlzIGNvbXBsZXRlIHN1YnNldCBvZiBnbnVkYXRlc2hvcnRcbiAgICByZWdleDogUmVnRXhwKCdeJyArIHJlWWVhciArICctJyArIHJlTW9udGggKyAnLScgKyByZURheSksXG4gICAgbmFtZTogJ2dudWRhdGVzaG9ydCB8IGlzbzg2MDFkYXRlMicsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCB5ZWFyLCBtb250aCwgZGF5KSB7XG4gICAgICByZXR1cm4gdGhpcy55bWQocHJvY2Vzc1llYXIoeWVhciksIG1vbnRoIC0gMSwgK2RheSk7XG4gICAgfVxuICB9LFxuXG4gIGlzbzg2MDFkYXRlNDoge1xuICAgIHJlZ2V4OiBSZWdFeHAoJ14nICsgcmVZZWFyNHdpdGhTaWduICsgJy0nICsgcmVNb250aGx6ICsgJy0nICsgcmVEYXlseiksXG4gICAgbmFtZTogJ2lzbzg2MDFkYXRlNCcsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCB5ZWFyLCBtb250aCwgZGF5KSB7XG4gICAgICByZXR1cm4gdGhpcy55bWQoK3llYXIsIG1vbnRoIC0gMSwgK2RheSk7XG4gICAgfVxuICB9LFxuXG4gIGdudU5vQ29sb246IHtcbiAgICByZWdleDogUmVnRXhwKCdedD8nICsgcmVIb3VyMjRseiArIHJlTWludXRlbHosICdpJyksXG4gICAgbmFtZTogJ2dudW5vY29sb24nLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgaG91ciwgbWludXRlKSB7XG4gICAgICAvLyB0aGlzIHJ1bGUgaXMgYSBzcGVjaWFsIGNhc2VcbiAgICAgIC8vIGlmIHRpbWUgd2FzIGFscmVhZHkgc2V0IG9uY2UgYnkgYW55IHByZWNlZGluZyBydWxlLCBpdCBzZXRzIHRoZSBjYXB0dXJlZCB2YWx1ZSBhcyB5ZWFyXG4gICAgICBzd2l0Y2ggKHRoaXMudGltZXMpIHtcbiAgICAgICAgY2FzZSAwOlxuICAgICAgICAgIHJldHVybiB0aGlzLnRpbWUoK2hvdXIsICttaW51dGUsIDAsIHRoaXMuZik7XG4gICAgICAgIGNhc2UgMTpcbiAgICAgICAgICB0aGlzLnkgPSBob3VyICogMTAwICsgK21pbnV0ZTtcbiAgICAgICAgICB0aGlzLnRpbWVzKys7XG5cbiAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICB9XG4gICAgfVxuICB9LFxuXG4gIGdudURhdGVTaG9ydGVyOiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyByZVllYXI0ICsgJy0nICsgcmVNb250aCksXG4gICAgbmFtZTogJ2dudWRhdGVzaG9ydGVyJyxcbiAgICBjYWxsYmFjazogZnVuY3Rpb24gY2FsbGJhY2sobWF0Y2gsIHllYXIsIG1vbnRoKSB7XG4gICAgICByZXR1cm4gdGhpcy55bWQoK3llYXIsIG1vbnRoIC0gMSwgMSk7XG4gICAgfVxuICB9LFxuXG4gIHBnVGV4dFJldmVyc2U6IHtcbiAgICAvLyBub3RlOiBhbGxvd2VkIHllYXJzIGFyZSBmcm9tIDMyLTk5OTlcbiAgICAvLyB5ZWFycyBiZWxvdyAzMiBzaG91bGQgYmUgdHJlYXRlZCBhcyBkYXlzIGluIGRhdGVmdWxsXG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyAnKFxcXFxkezMsNH18WzQtOV1cXFxcZHwzWzItOV0pLSgnICsgcmVNb250aEFiYnIgKyAnKS0nICsgcmVEYXlseiwgJ2knKSxcbiAgICBuYW1lOiAncGd0ZXh0cmV2ZXJzZScsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCB5ZWFyLCBtb250aCwgZGF5KSB7XG4gICAgICByZXR1cm4gdGhpcy55bWQocHJvY2Vzc1llYXIoeWVhciksIGxvb2t1cE1vbnRoKG1vbnRoKSwgK2RheSk7XG4gICAgfVxuICB9LFxuXG4gIGRhdGVGdWxsOiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyByZURheSArICdbIFxcXFx0Li1dKicgKyByZU1vbnRoVGV4dCArICdbIFxcXFx0Li1dKicgKyByZVllYXIsICdpJyksXG4gICAgbmFtZTogJ2RhdGVmdWxsJyxcbiAgICBjYWxsYmFjazogZnVuY3Rpb24gY2FsbGJhY2sobWF0Y2gsIGRheSwgbW9udGgsIHllYXIpIHtcbiAgICAgIHJldHVybiB0aGlzLnltZChwcm9jZXNzWWVhcih5ZWFyKSwgbG9va3VwTW9udGgobW9udGgpLCArZGF5KTtcbiAgICB9XG4gIH0sXG5cbiAgZGF0ZU5vRGF5OiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyByZU1vbnRoVGV4dCArICdbIC5cXFxcdC1dKicgKyByZVllYXI0LCAnaScpLFxuICAgIG5hbWU6ICdkYXRlbm9kYXknLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgbW9udGgsIHllYXIpIHtcbiAgICAgIHJldHVybiB0aGlzLnltZCgreWVhciwgbG9va3VwTW9udGgobW9udGgpLCAxKTtcbiAgICB9XG4gIH0sXG5cbiAgZGF0ZU5vRGF5UmV2OiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyByZVllYXI0ICsgJ1sgLlxcXFx0LV0qJyArIHJlTW9udGhUZXh0LCAnaScpLFxuICAgIG5hbWU6ICdkYXRlbm9kYXlyZXYnLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgeWVhciwgbW9udGgpIHtcbiAgICAgIHJldHVybiB0aGlzLnltZCgreWVhciwgbG9va3VwTW9udGgobW9udGgpLCAxKTtcbiAgICB9XG4gIH0sXG5cbiAgcGdUZXh0U2hvcnQ6IHtcbiAgICByZWdleDogUmVnRXhwKCdeKCcgKyByZU1vbnRoQWJiciArICcpLScgKyByZURheWx6ICsgJy0nICsgcmVZZWFyLCAnaScpLFxuICAgIG5hbWU6ICdwZ3RleHRzaG9ydCcsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCBtb250aCwgZGF5LCB5ZWFyKSB7XG4gICAgICByZXR1cm4gdGhpcy55bWQocHJvY2Vzc1llYXIoeWVhciksIGxvb2t1cE1vbnRoKG1vbnRoKSwgK2RheSk7XG4gICAgfVxuICB9LFxuXG4gIGRhdGVOb1llYXI6IHtcbiAgICByZWdleDogUmVnRXhwKCdeJyArIHJlRGF0ZU5vWWVhciwgJ2knKSxcbiAgICBuYW1lOiAnZGF0ZW5veWVhcicsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCBtb250aCwgZGF5KSB7XG4gICAgICByZXR1cm4gdGhpcy55bWQodGhpcy55LCBsb29rdXBNb250aChtb250aCksICtkYXkpO1xuICAgIH1cbiAgfSxcblxuICBkYXRlTm9ZZWFyUmV2OiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyByZURheSArICdbIC5cXFxcdC1dKicgKyByZU1vbnRoVGV4dCwgJ2knKSxcbiAgICBuYW1lOiAnZGF0ZW5veWVhcnJldicsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCBkYXksIG1vbnRoKSB7XG4gICAgICByZXR1cm4gdGhpcy55bWQodGhpcy55LCBsb29rdXBNb250aChtb250aCksICtkYXkpO1xuICAgIH1cbiAgfSxcblxuICBpc29XZWVrRGF5OiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyByZVllYXI0ICsgJy0/VycgKyByZVdlZWtPZlllYXIgKyAnKD86LT8oWzAtN10pKT8nKSxcbiAgICBuYW1lOiAnaXNvd2Vla2RheSB8IGlzb3dlZWsnLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgeWVhciwgd2VlaywgZGF5KSB7XG4gICAgICBkYXkgPSBkYXkgPyArZGF5IDogMTtcblxuICAgICAgaWYgKCF0aGlzLnltZCgreWVhciwgMCwgMSkpIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgfVxuXG4gICAgICAvLyBnZXQgZGF5IG9mIHdlZWsgZm9yIEphbiAxc3RcbiAgICAgIHZhciBkYXlPZldlZWsgPSBuZXcgRGF0ZSh0aGlzLnksIHRoaXMubSwgdGhpcy5kKS5nZXREYXkoKTtcblxuICAgICAgLy8gYW5kIHVzZSB0aGUgZGF5IHRvIGZpZ3VyZSBvdXQgdGhlIG9mZnNldCBmb3IgZGF5IDEgb2Ygd2VlayAxXG4gICAgICBkYXlPZldlZWsgPSAwIC0gKGRheU9mV2VlayA+IDQgPyBkYXlPZldlZWsgLSA3IDogZGF5T2ZXZWVrKTtcblxuICAgICAgdGhpcy5yZCArPSBkYXlPZldlZWsgKyAod2VlayAtIDEpICogNyArIGRheTtcbiAgICB9XG4gIH0sXG5cbiAgcmVsYXRpdmVUZXh0OiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXignICsgcmVSZWx0ZXh0bnVtYmVyICsgJ3wnICsgcmVSZWx0ZXh0dGV4dCArICcpJyArIHJlU3BhY2UgKyAnKCcgKyByZVJlbHRleHR1bml0ICsgJyknLCAnaScpLFxuICAgIG5hbWU6ICdyZWxhdGl2ZXRleHQnLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgcmVsVmFsdWUsIHJlbFVuaXQpIHtcbiAgICAgIC8vIHRvZG86IGltcGxlbWVudCBoYW5kbGluZyBvZiAndGhpcyB0aW1lLXVuaXQnXG4gICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW51c2VkLXZhcnNcbiAgICAgIHZhciBfbG9va3VwUmVsYXRpdmUgPSBsb29rdXBSZWxhdGl2ZShyZWxWYWx1ZSksXG4gICAgICAgICAgYW1vdW50ID0gX2xvb2t1cFJlbGF0aXZlLmFtb3VudCxcbiAgICAgICAgICBiZWhhdmlvciA9IF9sb29rdXBSZWxhdGl2ZS5iZWhhdmlvcjtcblxuICAgICAgc3dpdGNoIChyZWxVbml0LnRvTG93ZXJDYXNlKCkpIHtcbiAgICAgICAgY2FzZSAnc2VjJzpcbiAgICAgICAgY2FzZSAnc2Vjcyc6XG4gICAgICAgIGNhc2UgJ3NlY29uZCc6XG4gICAgICAgIGNhc2UgJ3NlY29uZHMnOlxuICAgICAgICAgIHRoaXMucnMgKz0gYW1vdW50O1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICdtaW4nOlxuICAgICAgICBjYXNlICdtaW5zJzpcbiAgICAgICAgY2FzZSAnbWludXRlJzpcbiAgICAgICAgY2FzZSAnbWludXRlcyc6XG4gICAgICAgICAgdGhpcy5yaSArPSBhbW91bnQ7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgJ2hvdXInOlxuICAgICAgICBjYXNlICdob3Vycyc6XG4gICAgICAgICAgdGhpcy5yaCArPSBhbW91bnQ7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgJ2RheSc6XG4gICAgICAgIGNhc2UgJ2RheXMnOlxuICAgICAgICAgIHRoaXMucmQgKz0gYW1vdW50O1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICdmb3J0bmlnaHQnOlxuICAgICAgICBjYXNlICdmb3J0bmlnaHRzJzpcbiAgICAgICAgY2FzZSAnZm9ydGhuaWdodCc6XG4gICAgICAgIGNhc2UgJ2ZvcnRobmlnaHRzJzpcbiAgICAgICAgICB0aGlzLnJkICs9IGFtb3VudCAqIDE0O1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICd3ZWVrJzpcbiAgICAgICAgY2FzZSAnd2Vla3MnOlxuICAgICAgICAgIHRoaXMucmQgKz0gYW1vdW50ICogNztcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAnbW9udGgnOlxuICAgICAgICBjYXNlICdtb250aHMnOlxuICAgICAgICAgIHRoaXMucm0gKz0gYW1vdW50O1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICd5ZWFyJzpcbiAgICAgICAgY2FzZSAneWVhcnMnOlxuICAgICAgICAgIHRoaXMucnkgKz0gYW1vdW50O1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICdtb24nOlxuICAgICAgICBjYXNlICdtb25kYXknOlxuICAgICAgICBjYXNlICd0dWUnOlxuICAgICAgICBjYXNlICd0dWVzZGF5JzpcbiAgICAgICAgY2FzZSAnd2VkJzpcbiAgICAgICAgY2FzZSAnd2VkbmVzZGF5JzpcbiAgICAgICAgY2FzZSAndGh1JzpcbiAgICAgICAgY2FzZSAndGh1cnNkYXknOlxuICAgICAgICBjYXNlICdmcmknOlxuICAgICAgICBjYXNlICdmcmlkYXknOlxuICAgICAgICBjYXNlICdzYXQnOlxuICAgICAgICBjYXNlICdzYXR1cmRheSc6XG4gICAgICAgIGNhc2UgJ3N1bic6XG4gICAgICAgIGNhc2UgJ3N1bmRheSc6XG4gICAgICAgICAgdGhpcy5yZXNldFRpbWUoKTtcbiAgICAgICAgICB0aGlzLndlZWtkYXkgPSBsb29rdXBXZWVrZGF5KHJlbFVuaXQsIDcpO1xuICAgICAgICAgIHRoaXMud2Vla2RheUJlaGF2aW9yID0gMTtcbiAgICAgICAgICB0aGlzLnJkICs9IChhbW91bnQgPiAwID8gYW1vdW50IC0gMSA6IGFtb3VudCkgKiA3O1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICd3ZWVrZGF5JzpcbiAgICAgICAgY2FzZSAnd2Vla2RheXMnOlxuICAgICAgICAgIC8vIHRvZG9cbiAgICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICB9XG4gIH0sXG5cbiAgcmVsYXRpdmU6IHtcbiAgICByZWdleDogUmVnRXhwKCdeKFsrLV0qKVsgXFxcXHRdKihcXFxcZCspJyArIHJlU3BhY2VPcHQgKyAnKCcgKyByZVJlbHRleHR1bml0ICsgJ3x3ZWVrKScsICdpJyksXG4gICAgbmFtZTogJ3JlbGF0aXZlJyxcbiAgICBjYWxsYmFjazogZnVuY3Rpb24gY2FsbGJhY2sobWF0Y2gsIHNpZ25zLCByZWxWYWx1ZSwgcmVsVW5pdCkge1xuICAgICAgdmFyIG1pbnVzZXMgPSBzaWducy5yZXBsYWNlKC9bXi1dL2csICcnKS5sZW5ndGg7XG5cbiAgICAgIHZhciBhbW91bnQgPSArcmVsVmFsdWUgKiBNYXRoLnBvdygtMSwgbWludXNlcyk7XG5cbiAgICAgIHN3aXRjaCAocmVsVW5pdC50b0xvd2VyQ2FzZSgpKSB7XG4gICAgICAgIGNhc2UgJ3NlYyc6XG4gICAgICAgIGNhc2UgJ3NlY3MnOlxuICAgICAgICBjYXNlICdzZWNvbmQnOlxuICAgICAgICBjYXNlICdzZWNvbmRzJzpcbiAgICAgICAgICB0aGlzLnJzICs9IGFtb3VudDtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAnbWluJzpcbiAgICAgICAgY2FzZSAnbWlucyc6XG4gICAgICAgIGNhc2UgJ21pbnV0ZSc6XG4gICAgICAgIGNhc2UgJ21pbnV0ZXMnOlxuICAgICAgICAgIHRoaXMucmkgKz0gYW1vdW50O1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICdob3VyJzpcbiAgICAgICAgY2FzZSAnaG91cnMnOlxuICAgICAgICAgIHRoaXMucmggKz0gYW1vdW50O1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICdkYXknOlxuICAgICAgICBjYXNlICdkYXlzJzpcbiAgICAgICAgICB0aGlzLnJkICs9IGFtb3VudDtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAnZm9ydG5pZ2h0JzpcbiAgICAgICAgY2FzZSAnZm9ydG5pZ2h0cyc6XG4gICAgICAgIGNhc2UgJ2ZvcnRobmlnaHQnOlxuICAgICAgICBjYXNlICdmb3J0aG5pZ2h0cyc6XG4gICAgICAgICAgdGhpcy5yZCArPSBhbW91bnQgKiAxNDtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAnd2Vlayc6XG4gICAgICAgIGNhc2UgJ3dlZWtzJzpcbiAgICAgICAgICB0aGlzLnJkICs9IGFtb3VudCAqIDc7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgJ21vbnRoJzpcbiAgICAgICAgY2FzZSAnbW9udGhzJzpcbiAgICAgICAgICB0aGlzLnJtICs9IGFtb3VudDtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAneWVhcic6XG4gICAgICAgIGNhc2UgJ3llYXJzJzpcbiAgICAgICAgICB0aGlzLnJ5ICs9IGFtb3VudDtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAnbW9uJzpcbiAgICAgICAgY2FzZSAnbW9uZGF5JzpcbiAgICAgICAgY2FzZSAndHVlJzpcbiAgICAgICAgY2FzZSAndHVlc2RheSc6XG4gICAgICAgIGNhc2UgJ3dlZCc6XG4gICAgICAgIGNhc2UgJ3dlZG5lc2RheSc6XG4gICAgICAgIGNhc2UgJ3RodSc6XG4gICAgICAgIGNhc2UgJ3RodXJzZGF5JzpcbiAgICAgICAgY2FzZSAnZnJpJzpcbiAgICAgICAgY2FzZSAnZnJpZGF5JzpcbiAgICAgICAgY2FzZSAnc2F0JzpcbiAgICAgICAgY2FzZSAnc2F0dXJkYXknOlxuICAgICAgICBjYXNlICdzdW4nOlxuICAgICAgICBjYXNlICdzdW5kYXknOlxuICAgICAgICAgIHRoaXMucmVzZXRUaW1lKCk7XG4gICAgICAgICAgdGhpcy53ZWVrZGF5ID0gbG9va3VwV2Vla2RheShyZWxVbml0LCA3KTtcbiAgICAgICAgICB0aGlzLndlZWtkYXlCZWhhdmlvciA9IDE7XG4gICAgICAgICAgdGhpcy5yZCArPSAoYW1vdW50ID4gMCA/IGFtb3VudCAtIDEgOiBhbW91bnQpICogNztcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAnd2Vla2RheSc6XG4gICAgICAgIGNhc2UgJ3dlZWtkYXlzJzpcbiAgICAgICAgICAvLyB0b2RvXG4gICAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgfVxuICB9LFxuXG4gIGRheVRleHQ6IHtcbiAgICByZWdleDogUmVnRXhwKCdeKCcgKyByZURheXRleHQgKyAnKScsICdpJyksXG4gICAgbmFtZTogJ2RheXRleHQnLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgZGF5VGV4dCkge1xuICAgICAgdGhpcy5yZXNldFRpbWUoKTtcbiAgICAgIHRoaXMud2Vla2RheSA9IGxvb2t1cFdlZWtkYXkoZGF5VGV4dCwgMCk7XG5cbiAgICAgIGlmICh0aGlzLndlZWtkYXlCZWhhdmlvciAhPT0gMikge1xuICAgICAgICB0aGlzLndlZWtkYXlCZWhhdmlvciA9IDE7XG4gICAgICB9XG4gICAgfVxuICB9LFxuXG4gIHJlbGF0aXZlVGV4dFdlZWs6IHtcbiAgICByZWdleDogUmVnRXhwKCdeKCcgKyByZVJlbHRleHR0ZXh0ICsgJyknICsgcmVTcGFjZSArICd3ZWVrJywgJ2knKSxcbiAgICBuYW1lOiAncmVsYXRpdmV0ZXh0d2VlaycsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCByZWxUZXh0KSB7XG4gICAgICB0aGlzLndlZWtkYXlCZWhhdmlvciA9IDI7XG5cbiAgICAgIHN3aXRjaCAocmVsVGV4dC50b0xvd2VyQ2FzZSgpKSB7XG4gICAgICAgIGNhc2UgJ3RoaXMnOlxuICAgICAgICAgIHRoaXMucmQgKz0gMDtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAnbmV4dCc6XG4gICAgICAgICAgdGhpcy5yZCArPSA3O1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICdsYXN0JzpcbiAgICAgICAgY2FzZSAncHJldmlvdXMnOlxuICAgICAgICAgIHRoaXMucmQgLT0gNztcbiAgICAgICAgICBicmVhaztcbiAgICAgIH1cblxuICAgICAgaWYgKGlzTmFOKHRoaXMud2Vla2RheSkpIHtcbiAgICAgICAgdGhpcy53ZWVrZGF5ID0gMTtcbiAgICAgIH1cbiAgICB9XG4gIH0sXG5cbiAgbW9udGhGdWxsT3JNb250aEFiYnI6IHtcbiAgICByZWdleDogUmVnRXhwKCdeKCcgKyByZU1vbnRoRnVsbCArICd8JyArIHJlTW9udGhBYmJyICsgJyknLCAnaScpLFxuICAgIG5hbWU6ICdtb250aGZ1bGwgfCBtb250aGFiYnInLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgbW9udGgpIHtcbiAgICAgIHJldHVybiB0aGlzLnltZCh0aGlzLnksIGxvb2t1cE1vbnRoKG1vbnRoKSwgdGhpcy5kKTtcbiAgICB9XG4gIH0sXG5cbiAgdHpDb3JyZWN0aW9uOiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyByZVR6Q29ycmVjdGlvbiwgJ2knKSxcbiAgICBuYW1lOiAndHpjb3JyZWN0aW9uJyxcbiAgICBjYWxsYmFjazogZnVuY3Rpb24gY2FsbGJhY2sodHpDb3JyZWN0aW9uKSB7XG4gICAgICByZXR1cm4gdGhpcy56b25lKHByb2Nlc3NUekNvcnJlY3Rpb24odHpDb3JyZWN0aW9uKSk7XG4gICAgfVxuICB9LFxuXG4gIHR6QWJicjoge1xuICAgIHJlZ2V4OiBSZWdFeHAoJ14nICsgcmVUekFiYnIpLFxuICAgIG5hbWU6ICd0emFiYnInLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgYWJicikge1xuICAgICAgdmFyIG9mZnNldCA9IHR6QWJick9mZnNldHNbYWJici50b0xvd2VyQ2FzZSgpXTtcblxuICAgICAgaWYgKGlzTmFOKG9mZnNldCkpIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgfVxuXG4gICAgICByZXR1cm4gdGhpcy56b25lKG9mZnNldCk7XG4gICAgfVxuICB9LFxuXG4gIGFnbzoge1xuICAgIHJlZ2V4OiAvXmFnby9pLFxuICAgIG5hbWU6ICdhZ28nLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjaygpIHtcbiAgICAgIHRoaXMucnkgPSAtdGhpcy5yeTtcbiAgICAgIHRoaXMucm0gPSAtdGhpcy5ybTtcbiAgICAgIHRoaXMucmQgPSAtdGhpcy5yZDtcbiAgICAgIHRoaXMucmggPSAtdGhpcy5yaDtcbiAgICAgIHRoaXMucmkgPSAtdGhpcy5yaTtcbiAgICAgIHRoaXMucnMgPSAtdGhpcy5ycztcbiAgICAgIHRoaXMucmYgPSAtdGhpcy5yZjtcbiAgICB9XG4gIH0sXG5cbiAgeWVhcjQ6IHtcbiAgICByZWdleDogUmVnRXhwKCdeJyArIHJlWWVhcjQpLFxuICAgIG5hbWU6ICd5ZWFyNCcsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCB5ZWFyKSB7XG4gICAgICB0aGlzLnkgPSAreWVhcjtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cbiAgfSxcblxuICB3aGl0ZXNwYWNlOiB7XG4gICAgcmVnZXg6IC9eWyAuLFxcdF0rLyxcbiAgICBuYW1lOiAnd2hpdGVzcGFjZSdcbiAgICAvLyBkbyBub3RoaW5nXG4gIH0sXG5cbiAgZGF0ZVNob3J0V2l0aFRpbWVMb25nOiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyByZURhdGVOb1llYXIgKyAndD8nICsgcmVIb3VyMjQgKyAnWzouXScgKyByZU1pbnV0ZSArICdbOi5dJyArIHJlU2Vjb25kLCAnaScpLFxuICAgIG5hbWU6ICdkYXRlc2hvcnR3aXRodGltZWxvbmcnLFxuICAgIGNhbGxiYWNrOiBmdW5jdGlvbiBjYWxsYmFjayhtYXRjaCwgbW9udGgsIGRheSwgaG91ciwgbWludXRlLCBzZWNvbmQpIHtcbiAgICAgIHJldHVybiB0aGlzLnltZCh0aGlzLnksIGxvb2t1cE1vbnRoKG1vbnRoKSwgK2RheSkgJiYgdGhpcy50aW1lKCtob3VyLCArbWludXRlLCArc2Vjb25kLCAwKTtcbiAgICB9XG4gIH0sXG5cbiAgZGF0ZVNob3J0V2l0aFRpbWVMb25nMTI6IHtcbiAgICByZWdleDogUmVnRXhwKCdeJyArIHJlRGF0ZU5vWWVhciArIHJlSG91cjEyICsgJ1s6Ll0nICsgcmVNaW51dGUgKyAnWzouXScgKyByZVNlY29uZGx6ICsgcmVTcGFjZU9wdCArIHJlTWVyaWRpYW4sICdpJyksXG4gICAgbmFtZTogJ2RhdGVzaG9ydHdpdGh0aW1lbG9uZzEyJyxcbiAgICBjYWxsYmFjazogZnVuY3Rpb24gY2FsbGJhY2sobWF0Y2gsIG1vbnRoLCBkYXksIGhvdXIsIG1pbnV0ZSwgc2Vjb25kLCBtZXJpZGlhbikge1xuICAgICAgcmV0dXJuIHRoaXMueW1kKHRoaXMueSwgbG9va3VwTW9udGgobW9udGgpLCArZGF5KSAmJiB0aGlzLnRpbWUocHJvY2Vzc01lcmlkaWFuKCtob3VyLCBtZXJpZGlhbiksICttaW51dGUsICtzZWNvbmQsIDApO1xuICAgIH1cbiAgfSxcblxuICBkYXRlU2hvcnRXaXRoVGltZVNob3J0OiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyByZURhdGVOb1llYXIgKyAndD8nICsgcmVIb3VyMjQgKyAnWzouXScgKyByZU1pbnV0ZSwgJ2knKSxcbiAgICBuYW1lOiAnZGF0ZXNob3J0d2l0aHRpbWVzaG9ydCcsXG4gICAgY2FsbGJhY2s6IGZ1bmN0aW9uIGNhbGxiYWNrKG1hdGNoLCBtb250aCwgZGF5LCBob3VyLCBtaW51dGUpIHtcbiAgICAgIHJldHVybiB0aGlzLnltZCh0aGlzLnksIGxvb2t1cE1vbnRoKG1vbnRoKSwgK2RheSkgJiYgdGhpcy50aW1lKCtob3VyLCArbWludXRlLCAwLCAwKTtcbiAgICB9XG4gIH0sXG5cbiAgZGF0ZVNob3J0V2l0aFRpbWVTaG9ydDEyOiB7XG4gICAgcmVnZXg6IFJlZ0V4cCgnXicgKyByZURhdGVOb1llYXIgKyByZUhvdXIxMiArICdbOi5dJyArIHJlTWludXRlbHogKyByZVNwYWNlT3B0ICsgcmVNZXJpZGlhbiwgJ2knKSxcbiAgICBuYW1lOiAnZGF0ZXNob3J0d2l0aHRpbWVzaG9ydDEyJyxcbiAgICBjYWxsYmFjazogZnVuY3Rpb24gY2FsbGJhY2sobWF0Y2gsIG1vbnRoLCBkYXksIGhvdXIsIG1pbnV0ZSwgbWVyaWRpYW4pIHtcbiAgICAgIHJldHVybiB0aGlzLnltZCh0aGlzLnksIGxvb2t1cE1vbnRoKG1vbnRoKSwgK2RheSkgJiYgdGhpcy50aW1lKHByb2Nlc3NNZXJpZGlhbigraG91ciwgbWVyaWRpYW4pLCArbWludXRlLCAwLCAwKTtcbiAgICB9XG4gIH1cbn07XG5cbnZhciByZXN1bHRQcm90byA9IHtcbiAgLy8gZGF0ZVxuICB5OiBOYU4sXG4gIG06IE5hTixcbiAgZDogTmFOLFxuICAvLyB0aW1lXG4gIGg6IE5hTixcbiAgaTogTmFOLFxuICBzOiBOYU4sXG4gIGY6IE5hTixcblxuICAvLyByZWxhdGl2ZSBzaGlmdHNcbiAgcnk6IDAsXG4gIHJtOiAwLFxuICByZDogMCxcbiAgcmg6IDAsXG4gIHJpOiAwLFxuICByczogMCxcbiAgcmY6IDAsXG5cbiAgLy8gd2Vla2RheSByZWxhdGVkIHNoaWZ0c1xuICB3ZWVrZGF5OiBOYU4sXG4gIHdlZWtkYXlCZWhhdmlvcjogMCxcblxuICAvLyBmaXJzdCBvciBsYXN0IGRheSBvZiBtb250aFxuICAvLyAwIG5vbmUsIDEgZmlyc3QsIC0xIGxhc3RcbiAgZmlyc3RPckxhc3REYXlPZk1vbnRoOiAwLFxuXG4gIC8vIHRpbWV6b25lIGNvcnJlY3Rpb24gaW4gbWludXRlc1xuICB6OiBOYU4sXG5cbiAgLy8gY291bnRlcnNcbiAgZGF0ZXM6IDAsXG4gIHRpbWVzOiAwLFxuICB6b25lczogMCxcblxuICAvLyBoZWxwZXIgZnVuY3Rpb25zXG4gIHltZDogZnVuY3Rpb24geW1kKHksIG0sIGQpIHtcbiAgICBpZiAodGhpcy5kYXRlcyA+IDApIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG5cbiAgICB0aGlzLmRhdGVzKys7XG4gICAgdGhpcy55ID0geTtcbiAgICB0aGlzLm0gPSBtO1xuICAgIHRoaXMuZCA9IGQ7XG4gICAgcmV0dXJuIHRydWU7XG4gIH0sXG4gIHRpbWU6IGZ1bmN0aW9uIHRpbWUoaCwgaSwgcywgZikge1xuICAgIGlmICh0aGlzLnRpbWVzID4gMCkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cblxuICAgIHRoaXMudGltZXMrKztcbiAgICB0aGlzLmggPSBoO1xuICAgIHRoaXMuaSA9IGk7XG4gICAgdGhpcy5zID0gcztcbiAgICB0aGlzLmYgPSBmO1xuXG4gICAgcmV0dXJuIHRydWU7XG4gIH0sXG4gIHJlc2V0VGltZTogZnVuY3Rpb24gcmVzZXRUaW1lKCkge1xuICAgIHRoaXMuaCA9IDA7XG4gICAgdGhpcy5pID0gMDtcbiAgICB0aGlzLnMgPSAwO1xuICAgIHRoaXMuZiA9IDA7XG4gICAgdGhpcy50aW1lcyA9IDA7XG5cbiAgICByZXR1cm4gdHJ1ZTtcbiAgfSxcbiAgem9uZTogZnVuY3Rpb24gem9uZShtaW51dGVzKSB7XG4gICAgaWYgKHRoaXMuem9uZXMgPD0gMSkge1xuICAgICAgdGhpcy56b25lcysrO1xuICAgICAgdGhpcy56ID0gbWludXRlcztcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cblxuICAgIHJldHVybiBmYWxzZTtcbiAgfSxcbiAgdG9EYXRlOiBmdW5jdGlvbiB0b0RhdGUocmVsYXRpdmVUbykge1xuICAgIGlmICh0aGlzLmRhdGVzICYmICF0aGlzLnRpbWVzKSB7XG4gICAgICB0aGlzLmggPSB0aGlzLmkgPSB0aGlzLnMgPSB0aGlzLmYgPSAwO1xuICAgIH1cblxuICAgIC8vIGZpbGwgaG9sZXNcbiAgICBpZiAoaXNOYU4odGhpcy55KSkge1xuICAgICAgdGhpcy55ID0gcmVsYXRpdmVUby5nZXRGdWxsWWVhcigpO1xuICAgIH1cblxuICAgIGlmIChpc05hTih0aGlzLm0pKSB7XG4gICAgICB0aGlzLm0gPSByZWxhdGl2ZVRvLmdldE1vbnRoKCk7XG4gICAgfVxuXG4gICAgaWYgKGlzTmFOKHRoaXMuZCkpIHtcbiAgICAgIHRoaXMuZCA9IHJlbGF0aXZlVG8uZ2V0RGF0ZSgpO1xuICAgIH1cblxuICAgIGlmIChpc05hTih0aGlzLmgpKSB7XG4gICAgICB0aGlzLmggPSByZWxhdGl2ZVRvLmdldEhvdXJzKCk7XG4gICAgfVxuXG4gICAgaWYgKGlzTmFOKHRoaXMuaSkpIHtcbiAgICAgIHRoaXMuaSA9IHJlbGF0aXZlVG8uZ2V0TWludXRlcygpO1xuICAgIH1cblxuICAgIGlmIChpc05hTih0aGlzLnMpKSB7XG4gICAgICB0aGlzLnMgPSByZWxhdGl2ZVRvLmdldFNlY29uZHMoKTtcbiAgICB9XG5cbiAgICBpZiAoaXNOYU4odGhpcy5mKSkge1xuICAgICAgdGhpcy5mID0gcmVsYXRpdmVUby5nZXRNaWxsaXNlY29uZHMoKTtcbiAgICB9XG5cbiAgICAvLyBhZGp1c3Qgc3BlY2lhbCBlYXJseVxuICAgIHN3aXRjaCAodGhpcy5maXJzdE9yTGFzdERheU9mTW9udGgpIHtcbiAgICAgIGNhc2UgMTpcbiAgICAgICAgdGhpcy5kID0gMTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlIC0xOlxuICAgICAgICB0aGlzLmQgPSAwO1xuICAgICAgICB0aGlzLm0gKz0gMTtcbiAgICAgICAgYnJlYWs7XG4gICAgfVxuXG4gICAgaWYgKCFpc05hTih0aGlzLndlZWtkYXkpKSB7XG4gICAgICB2YXIgZGF0ZSA9IG5ldyBEYXRlKHJlbGF0aXZlVG8uZ2V0VGltZSgpKTtcbiAgICAgIGRhdGUuc2V0RnVsbFllYXIodGhpcy55LCB0aGlzLm0sIHRoaXMuZCk7XG4gICAgICBkYXRlLnNldEhvdXJzKHRoaXMuaCwgdGhpcy5pLCB0aGlzLnMsIHRoaXMuZik7XG5cbiAgICAgIHZhciBkb3cgPSBkYXRlLmdldERheSgpO1xuXG4gICAgICBpZiAodGhpcy53ZWVrZGF5QmVoYXZpb3IgPT09IDIpIHtcbiAgICAgICAgLy8gVG8gbWFrZSBcInRoaXMgd2Vla1wiIHdvcmssIHdoZXJlIHRoZSBjdXJyZW50IGRheSBvZiB3ZWVrIGlzIGEgXCJzdW5kYXlcIlxuICAgICAgICBpZiAoZG93ID09PSAwICYmIHRoaXMud2Vla2RheSAhPT0gMCkge1xuICAgICAgICAgIHRoaXMud2Vla2RheSA9IC02O1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gVG8gbWFrZSBcInN1bmRheSB0aGlzIHdlZWtcIiB3b3JrLCB3aGVyZSB0aGUgY3VycmVudCBkYXkgb2Ygd2VlayBpcyBub3QgYSBcInN1bmRheVwiXG4gICAgICAgIGlmICh0aGlzLndlZWtkYXkgPT09IDAgJiYgZG93ICE9PSAwKSB7XG4gICAgICAgICAgdGhpcy53ZWVrZGF5ID0gNztcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuZCAtPSBkb3c7XG4gICAgICAgIHRoaXMuZCArPSB0aGlzLndlZWtkYXk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB2YXIgZGlmZiA9IHRoaXMud2Vla2RheSAtIGRvdztcblxuICAgICAgICAvLyBzb21lIFBIUCBtYWdpY1xuICAgICAgICBpZiAodGhpcy5yZCA8IDAgJiYgZGlmZiA8IDAgfHwgdGhpcy5yZCA+PSAwICYmIGRpZmYgPD0gLXRoaXMud2Vla2RheUJlaGF2aW9yKSB7XG4gICAgICAgICAgZGlmZiArPSA3O1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHRoaXMud2Vla2RheSA+PSAwKSB7XG4gICAgICAgICAgdGhpcy5kICs9IGRpZmY7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgdGhpcy5kIC09IDcgLSAoTWF0aC5hYnModGhpcy53ZWVrZGF5KSAtIGRvdyk7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLndlZWtkYXkgPSBOYU47XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gYWRqdXN0IHJlbGF0aXZlXG4gICAgdGhpcy55ICs9IHRoaXMucnk7XG4gICAgdGhpcy5tICs9IHRoaXMucm07XG4gICAgdGhpcy5kICs9IHRoaXMucmQ7XG5cbiAgICB0aGlzLmggKz0gdGhpcy5yaDtcbiAgICB0aGlzLmkgKz0gdGhpcy5yaTtcbiAgICB0aGlzLnMgKz0gdGhpcy5ycztcbiAgICB0aGlzLmYgKz0gdGhpcy5yZjtcblxuICAgIHRoaXMucnkgPSB0aGlzLnJtID0gdGhpcy5yZCA9IDA7XG4gICAgdGhpcy5yaCA9IHRoaXMucmkgPSB0aGlzLnJzID0gdGhpcy5yZiA9IDA7XG5cbiAgICB2YXIgcmVzdWx0ID0gbmV3IERhdGUocmVsYXRpdmVUby5nZXRUaW1lKCkpO1xuICAgIC8vIHNpbmNlIERhdGUgY29uc3RydWN0b3IgdHJlYXRzIHllYXJzIDw9IDk5IGFzIDE5MDArXG4gICAgLy8gaXQgY2FuJ3QgYmUgdXNlZCwgdGh1cyB0aGlzIHdlaXJkIHdheVxuICAgIHJlc3VsdC5zZXRGdWxsWWVhcih0aGlzLnksIHRoaXMubSwgdGhpcy5kKTtcbiAgICByZXN1bHQuc2V0SG91cnModGhpcy5oLCB0aGlzLmksIHRoaXMucywgdGhpcy5mKTtcblxuICAgIC8vIG5vdGU6IHRoaXMgaXMgZG9uZSB0d2ljZSBpbiBQSFBcbiAgICAvLyBlYXJseSB3aGVuIHByb2Nlc3Npbmcgc3BlY2lhbCByZWxhdGl2ZXNcbiAgICAvLyBhbmQgbGF0ZVxuICAgIC8vIHRvZG86IGNoZWNrIGlmIHRoZSBsb2dpYyBjYW4gYmUgcmVkdWNlZFxuICAgIC8vIHRvIGp1c3Qgb25lIHRpbWUgYWN0aW9uXG4gICAgc3dpdGNoICh0aGlzLmZpcnN0T3JMYXN0RGF5T2ZNb250aCkge1xuICAgICAgY2FzZSAxOlxuICAgICAgICByZXN1bHQuc2V0RGF0ZSgxKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlIC0xOlxuICAgICAgICByZXN1bHQuc2V0TW9udGgocmVzdWx0LmdldE1vbnRoKCkgKyAxLCAwKTtcbiAgICAgICAgYnJlYWs7XG4gICAgfVxuXG4gICAgLy8gYWRqdXN0IHRpbWV6b25lXG4gICAgaWYgKCFpc05hTih0aGlzLnopICYmIHJlc3VsdC5nZXRUaW1lem9uZU9mZnNldCgpICE9PSB0aGlzLnopIHtcbiAgICAgIHJlc3VsdC5zZXRVVENGdWxsWWVhcihyZXN1bHQuZ2V0RnVsbFllYXIoKSwgcmVzdWx0LmdldE1vbnRoKCksIHJlc3VsdC5nZXREYXRlKCkpO1xuXG4gICAgICByZXN1bHQuc2V0VVRDSG91cnMocmVzdWx0LmdldEhvdXJzKCksIHJlc3VsdC5nZXRNaW51dGVzKCksIHJlc3VsdC5nZXRTZWNvbmRzKCkgLSB0aGlzLnosIHJlc3VsdC5nZXRNaWxsaXNlY29uZHMoKSk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxufTtcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiBzdHJ0b3RpbWUoc3RyLCBub3cpIHtcbiAgLy8gICAgICAgZGlzY3VzcyBhdDogaHR0cHM6Ly9sb2N1dHVzLmlvL3BocC9zdHJ0b3RpbWUvXG4gIC8vICAgICAgb3JpZ2luYWwgYnk6IENhaW8gQXJpZWRlIChodHRwczovL2NhaW9hcmllZGUuY29tKVxuICAvLyAgICAgIGltcHJvdmVkIGJ5OiBLZXZpbiB2YW4gWm9ubmV2ZWxkIChodHRwczovL2t2ei5pbylcbiAgLy8gICAgICBpbXByb3ZlZCBieTogQ2FpbyBBcmllZGUgKGh0dHBzOi8vY2Fpb2FyaWVkZS5jb20pXG4gIC8vICAgICAgaW1wcm92ZWQgYnk6IEEuIE1hdMOtYXMgUXVlemFkYSAoaHR0cHM6Ly9hbWF0aWFzcS5jb20pXG4gIC8vICAgICAgaW1wcm92ZWQgYnk6IHByZXV0ZXJcbiAgLy8gICAgICBpbXByb3ZlZCBieTogQnJldHQgWmFtaXIgKGh0dHBzOi8vYnJldHQtemFtaXIubWUpXG4gIC8vICAgICAgaW1wcm92ZWQgYnk6IE1pcmtvIEZhYmVyXG4gIC8vICAgICAgICAgaW5wdXQgYnk6IERhdmlkXG4gIC8vICAgICAgYnVnZml4ZWQgYnk6IFdhZ25lciBCLiBTb2FyZXNcbiAgLy8gICAgICBidWdmaXhlZCBieTogQXJ0dXIgVGNoZXJueWNoZXZcbiAgLy8gICAgICBidWdmaXhlZCBieTogU3RlcGhhbiBCw7ZzY2gtUGxlcGVsaXRzIChodHRwczovL2dpdGh1Yi5jb20vcGxlcGUpXG4gIC8vIHJlaW1wbGVtZW50ZWQgYnk6IFJhZmHFgiBLdWthd3NraVxuICAvLyAgICAgICAgICAgbm90ZSAxOiBFeGFtcGxlcyBhbGwgaGF2ZSBhIGZpeGVkIHRpbWVzdGFtcCB0byBwcmV2ZW50XG4gIC8vICAgICAgICAgICBub3RlIDE6IHRlc3RzIHRvIGZhaWwgYmVjYXVzZSBvZiB2YXJpYWJsZSB0aW1lKHpvbmVzKVxuICAvLyAgICAgICAgZXhhbXBsZSAxOiBzdHJ0b3RpbWUoJysxIGRheScsIDExMjk2MzMyMDApXG4gIC8vICAgICAgICByZXR1cm5zIDE6IDExMjk3MTk2MDBcbiAgLy8gICAgICAgIGV4YW1wbGUgMjogc3RydG90aW1lKCcrMSB3ZWVrIDIgZGF5cyA0IGhvdXJzIDIgc2Vjb25kcycsIDExMjk2MzMyMDApXG4gIC8vICAgICAgICByZXR1cm5zIDI6IDExMzA0MjUyMDJcbiAgLy8gICAgICAgIGV4YW1wbGUgMzogc3RydG90aW1lKCdsYXN0IG1vbnRoJywgMTEyOTYzMzIwMClcbiAgLy8gICAgICAgIHJldHVybnMgMzogMTEyNzA0MTIwMFxuICAvLyAgICAgICAgZXhhbXBsZSA0OiBzdHJ0b3RpbWUoJzIwMDktMDUtMDQgMDg6MzA6MDArMDAnKVxuICAvLyAgICAgICAgcmV0dXJucyA0OiAxMjQxNDI1ODAwXG4gIC8vICAgICAgICBleGFtcGxlIDU6IHN0cnRvdGltZSgnMjAwOS0wNS0wNCAwODozMDowMCswMjowMCcpXG4gIC8vICAgICAgICByZXR1cm5zIDU6IDEyNDE0MTg2MDBcbiAgLy8gICAgICAgIGV4YW1wbGUgNjogc3RydG90aW1lKCcyMDA5LTA1LTA0IDA4OjMwOjAwIFlXVCcpXG4gIC8vICAgICAgICByZXR1cm5zIDY6IDEyNDE0NTQ2MDBcbiAgLy8gICAgICAgIGV4YW1wbGUgNzogc3RydG90aW1lKCcxMC1KVUwtMTcnKVxuICAvLyAgICAgICAgcmV0dXJucyA3OiAxNDk5NjQ0ODAwXG5cbiAgaWYgKG5vdyA9PSBudWxsKSB7XG4gICAgbm93ID0gTWF0aC5mbG9vcihEYXRlLm5vdygpIC8gMTAwMCk7XG4gIH1cblxuICAvLyB0aGUgcnVsZSBvcmRlciBpcyBpbXBvcnRhbnRcbiAgLy8gaWYgbXVsdGlwbGUgcnVsZXMgbWF0Y2gsIHRoZSBsb25nZXN0IG1hdGNoIHdpbnNcbiAgLy8gaWYgbXVsdGlwbGUgcnVsZXMgbWF0Y2ggdGhlIHNhbWUgc3RyaW5nLCB0aGUgZmlyc3QgbWF0Y2ggd2luc1xuICB2YXIgcnVsZXMgPSBbZm9ybWF0cy55ZXN0ZXJkYXksIGZvcm1hdHMubm93LCBmb3JtYXRzLm5vb24sIGZvcm1hdHMubWlkbmlnaHRPclRvZGF5LCBmb3JtYXRzLnRvbW9ycm93LCBmb3JtYXRzLnRpbWVzdGFtcCwgZm9ybWF0cy5maXJzdE9yTGFzdERheSwgZm9ybWF0cy5iYWNrT3JGcm9udE9mLFxuICAvLyBmb3JtYXRzLndlZWtkYXlPZiwgLy8gbm90IHlldCBpbXBsZW1lbnRlZFxuICBmb3JtYXRzLnRpbWVUaW55MTIsIGZvcm1hdHMudGltZVNob3J0MTIsIGZvcm1hdHMudGltZUxvbmcxMiwgZm9ybWF0cy5tc3NxbHRpbWUsIGZvcm1hdHMub3JhY2xlZGF0ZSwgZm9ybWF0cy50aW1lU2hvcnQyNCwgZm9ybWF0cy50aW1lTG9uZzI0LCBmb3JtYXRzLmlzbzg2MDFsb25nLCBmb3JtYXRzLmdudU5vQ29sb24sIGZvcm1hdHMuaXNvODYwMW5vQ29sb24sIGZvcm1hdHMuYW1lcmljYW5TaG9ydCwgZm9ybWF0cy5hbWVyaWNhbiwgZm9ybWF0cy5pc284NjAxZGF0ZTQsIGZvcm1hdHMuaXNvODYwMWRhdGVTbGFzaCwgZm9ybWF0cy5kYXRlU2xhc2gsIGZvcm1hdHMuZ251RGF0ZVNob3J0T3JJc284NjAxZGF0ZTIsIGZvcm1hdHMuZ251RGF0ZVNob3J0ZXIsIGZvcm1hdHMuZGF0ZUZ1bGwsIGZvcm1hdHMucG9pbnRlZERhdGU0LCBmb3JtYXRzLnBvaW50ZWREYXRlMiwgZm9ybWF0cy5kYXRlTm9EYXksIGZvcm1hdHMuZGF0ZU5vRGF5UmV2LCBmb3JtYXRzLmRhdGVUZXh0dWFsLCBmb3JtYXRzLmRhdGVOb1llYXIsIGZvcm1hdHMuZGF0ZU5vWWVhclJldiwgZm9ybWF0cy5kYXRlTm9Db2xvbiwgZm9ybWF0cy54bWxScGMsIGZvcm1hdHMueG1sUnBjTm9Db2xvbiwgZm9ybWF0cy5zb2FwLCBmb3JtYXRzLndkZHgsIGZvcm1hdHMuZXhpZiwgZm9ybWF0cy5wZ3lkb3RkLCBmb3JtYXRzLmlzb1dlZWtEYXksIGZvcm1hdHMucGdUZXh0U2hvcnQsIGZvcm1hdHMucGdUZXh0UmV2ZXJzZSwgZm9ybWF0cy5jbGYsIGZvcm1hdHMueWVhcjQsIGZvcm1hdHMuYWdvLCBmb3JtYXRzLmRheVRleHQsIGZvcm1hdHMucmVsYXRpdmVUZXh0V2VlaywgZm9ybWF0cy5yZWxhdGl2ZVRleHQsIGZvcm1hdHMubW9udGhGdWxsT3JNb250aEFiYnIsIGZvcm1hdHMudHpDb3JyZWN0aW9uLCBmb3JtYXRzLnR6QWJiciwgZm9ybWF0cy5kYXRlU2hvcnRXaXRoVGltZVNob3J0MTIsIGZvcm1hdHMuZGF0ZVNob3J0V2l0aFRpbWVMb25nMTIsIGZvcm1hdHMuZGF0ZVNob3J0V2l0aFRpbWVTaG9ydCwgZm9ybWF0cy5kYXRlU2hvcnRXaXRoVGltZUxvbmcsIGZvcm1hdHMucmVsYXRpdmUsIGZvcm1hdHMud2hpdGVzcGFjZV07XG5cbiAgdmFyIHJlc3VsdCA9IE9iamVjdC5jcmVhdGUocmVzdWx0UHJvdG8pO1xuXG4gIHdoaWxlIChzdHIubGVuZ3RoKSB7XG4gICAgdmFyIGxvbmdlc3RNYXRjaCA9IG51bGw7XG4gICAgdmFyIGZpbmFsUnVsZSA9IG51bGw7XG5cbiAgICBmb3IgKHZhciBpID0gMCwgbCA9IHJ1bGVzLmxlbmd0aDsgaSA8IGw7IGkrKykge1xuICAgICAgdmFyIGZvcm1hdCA9IHJ1bGVzW2ldO1xuXG4gICAgICB2YXIgbWF0Y2ggPSBzdHIubWF0Y2goZm9ybWF0LnJlZ2V4KTtcblxuICAgICAgaWYgKG1hdGNoKSB7XG4gICAgICAgIGlmICghbG9uZ2VzdE1hdGNoIHx8IG1hdGNoWzBdLmxlbmd0aCA+IGxvbmdlc3RNYXRjaFswXS5sZW5ndGgpIHtcbiAgICAgICAgICBsb25nZXN0TWF0Y2ggPSBtYXRjaDtcbiAgICAgICAgICBmaW5hbFJ1bGUgPSBmb3JtYXQ7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoIWZpbmFsUnVsZSB8fCBmaW5hbFJ1bGUuY2FsbGJhY2sgJiYgZmluYWxSdWxlLmNhbGxiYWNrLmFwcGx5KHJlc3VsdCwgbG9uZ2VzdE1hdGNoKSA9PT0gZmFsc2UpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG5cbiAgICBzdHIgPSBzdHIuc3Vic3RyKGxvbmdlc3RNYXRjaFswXS5sZW5ndGgpO1xuICAgIGZpbmFsUnVsZSA9IG51bGw7XG4gICAgbG9uZ2VzdE1hdGNoID0gbnVsbDtcbiAgfVxuXG4gIHJldHVybiBNYXRoLmZsb29yKHJlc3VsdC50b0RhdGUobmV3IERhdGUobm93ICogMTAwMCkpIC8gMTAwMCk7XG59O1xuLy8jIHNvdXJjZU1hcHBpbmdVUkw9c3RydG90aW1lLmpzLm1hcCIsIid1c2Ugc3RyaWN0JztcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiBpbmlfZ2V0KHZhcm5hbWUpIHtcbiAgLy8gIGRpc2N1c3MgYXQ6IGh0dHBzOi8vbG9jdXR1cy5pby9waHAvaW5pX2dldC9cbiAgLy8gb3JpZ2luYWwgYnk6IEJyZXR0IFphbWlyIChodHRwczovL2JyZXR0LXphbWlyLm1lKVxuICAvLyAgICAgIG5vdGUgMTogVGhlIGluaSB2YWx1ZXMgbXVzdCBiZSBzZXQgYnkgaW5pX3NldCBvciBtYW51YWxseSB3aXRoaW4gYW4gaW5pIGZpbGVcbiAgLy8gICBleGFtcGxlIDE6IGluaV9zZXQoJ2RhdGUudGltZXpvbmUnLCAnQXNpYS9Ib25nX0tvbmcnKVxuICAvLyAgIGV4YW1wbGUgMTogaW5pX2dldCgnZGF0ZS50aW1lem9uZScpXG4gIC8vICAgcmV0dXJucyAxOiAnQXNpYS9Ib25nX0tvbmcnXG5cbiAgdmFyICRnbG9iYWwgPSB0eXBlb2Ygd2luZG93ICE9PSAndW5kZWZpbmVkJyA/IHdpbmRvdyA6IGdsb2JhbDtcbiAgJGdsb2JhbC4kbG9jdXR1cyA9ICRnbG9iYWwuJGxvY3V0dXMgfHwge307XG4gIHZhciAkbG9jdXR1cyA9ICRnbG9iYWwuJGxvY3V0dXM7XG4gICRsb2N1dHVzLnBocCA9ICRsb2N1dHVzLnBocCB8fCB7fTtcbiAgJGxvY3V0dXMucGhwLmluaSA9ICRsb2N1dHVzLnBocC5pbmkgfHwge307XG5cbiAgaWYgKCRsb2N1dHVzLnBocC5pbmlbdmFybmFtZV0gJiYgJGxvY3V0dXMucGhwLmluaVt2YXJuYW1lXS5sb2NhbF92YWx1ZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgaWYgKCRsb2N1dHVzLnBocC5pbmlbdmFybmFtZV0ubG9jYWxfdmFsdWUgPT09IG51bGwpIHtcbiAgICAgIHJldHVybiAnJztcbiAgICB9XG4gICAgcmV0dXJuICRsb2N1dHVzLnBocC5pbmlbdmFybmFtZV0ubG9jYWxfdmFsdWU7XG4gIH1cblxuICByZXR1cm4gJyc7XG59O1xuLy8jIHNvdXJjZU1hcHBpbmdVUkw9aW5pX2dldC5qcy5tYXAiLCIndXNlIHN0cmljdCc7XG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gc3RybGVuKHN0cmluZykge1xuICAvLyAgZGlzY3VzcyBhdDogaHR0cHM6Ly9sb2N1dHVzLmlvL3BocC9zdHJsZW4vXG4gIC8vIG9yaWdpbmFsIGJ5OiBLZXZpbiB2YW4gWm9ubmV2ZWxkIChodHRwczovL2t2ei5pbylcbiAgLy8gaW1wcm92ZWQgYnk6IFNha2ltb3JpXG4gIC8vIGltcHJvdmVkIGJ5OiBLZXZpbiB2YW4gWm9ubmV2ZWxkIChodHRwczovL2t2ei5pbylcbiAgLy8gICAgaW5wdXQgYnk6IEtpcmsgU3Ryb2JlY2tcbiAgLy8gYnVnZml4ZWQgYnk6IE9ubm8gTWFyc21hbiAoaHR0cHM6Ly90d2l0dGVyLmNvbS9vbm5vbWFyc21hbilcbiAgLy8gIHJldmlzZWQgYnk6IEJyZXR0IFphbWlyIChodHRwczovL2JyZXR0LXphbWlyLm1lKVxuICAvLyAgICAgIG5vdGUgMTogTWF5IGxvb2sgbGlrZSBvdmVya2lsbCwgYnV0IGluIG9yZGVyIHRvIGJlIHRydWx5IGZhaXRoZnVsIHRvIGhhbmRsaW5nIGFsbCBVbmljb2RlXG4gIC8vICAgICAgbm90ZSAxOiBjaGFyYWN0ZXJzIGFuZCB0byB0aGlzIGZ1bmN0aW9uIGluIFBIUCB3aGljaCBkb2VzIG5vdCBjb3VudCB0aGUgbnVtYmVyIG9mIGJ5dGVzXG4gIC8vICAgICAgbm90ZSAxOiBidXQgY291bnRzIHRoZSBudW1iZXIgb2YgY2hhcmFjdGVycywgc29tZXRoaW5nIGxpa2UgdGhpcyBpcyByZWFsbHkgbmVjZXNzYXJ5LlxuICAvLyAgIGV4YW1wbGUgMTogc3RybGVuKCdLZXZpbiB2YW4gWm9ubmV2ZWxkJylcbiAgLy8gICByZXR1cm5zIDE6IDE5XG4gIC8vICAgZXhhbXBsZSAyOiBpbmlfc2V0KCd1bmljb2RlLnNlbWFudGljcycsICdvbicpXG4gIC8vICAgZXhhbXBsZSAyOiBzdHJsZW4oJ0FcXHVkODdlXFx1ZGMwNFonKVxuICAvLyAgIHJldHVybnMgMjogM1xuXG4gIHZhciBzdHIgPSBzdHJpbmcgKyAnJztcblxuICB2YXIgaW5pVmFsID0gKHR5cGVvZiByZXF1aXJlICE9PSAndW5kZWZpbmVkJyA/IHJlcXVpcmUoJy4uL2luZm8vaW5pX2dldCcpKCd1bmljb2RlLnNlbWFudGljcycpIDogdW5kZWZpbmVkKSB8fCAnb2ZmJztcbiAgaWYgKGluaVZhbCA9PT0gJ29mZicpIHtcbiAgICByZXR1cm4gc3RyLmxlbmd0aDtcbiAgfVxuXG4gIHZhciBpID0gMDtcbiAgdmFyIGxndGggPSAwO1xuXG4gIHZhciBnZXRXaG9sZUNoYXIgPSBmdW5jdGlvbiBnZXRXaG9sZUNoYXIoc3RyLCBpKSB7XG4gICAgdmFyIGNvZGUgPSBzdHIuY2hhckNvZGVBdChpKTtcbiAgICB2YXIgbmV4dCA9ICcnO1xuICAgIHZhciBwcmV2ID0gJyc7XG4gICAgaWYgKGNvZGUgPj0gMHhkODAwICYmIGNvZGUgPD0gMHhkYmZmKSB7XG4gICAgICAvLyBIaWdoIHN1cnJvZ2F0ZSAoY291bGQgY2hhbmdlIGxhc3QgaGV4IHRvIDB4REI3RiB0b1xuICAgICAgLy8gdHJlYXQgaGlnaCBwcml2YXRlIHN1cnJvZ2F0ZXMgYXMgc2luZ2xlIGNoYXJhY3RlcnMpXG4gICAgICBpZiAoc3RyLmxlbmd0aCA8PSBpICsgMSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0hpZ2ggc3Vycm9nYXRlIHdpdGhvdXQgZm9sbG93aW5nIGxvdyBzdXJyb2dhdGUnKTtcbiAgICAgIH1cbiAgICAgIG5leHQgPSBzdHIuY2hhckNvZGVBdChpICsgMSk7XG4gICAgICBpZiAobmV4dCA8IDB4ZGMwMCB8fCBuZXh0ID4gMHhkZmZmKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignSGlnaCBzdXJyb2dhdGUgd2l0aG91dCBmb2xsb3dpbmcgbG93IHN1cnJvZ2F0ZScpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHN0ci5jaGFyQXQoaSkgKyBzdHIuY2hhckF0KGkgKyAxKTtcbiAgICB9IGVsc2UgaWYgKGNvZGUgPj0gMHhkYzAwICYmIGNvZGUgPD0gMHhkZmZmKSB7XG4gICAgICAvLyBMb3cgc3Vycm9nYXRlXG4gICAgICBpZiAoaSA9PT0gMCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0xvdyBzdXJyb2dhdGUgd2l0aG91dCBwcmVjZWRpbmcgaGlnaCBzdXJyb2dhdGUnKTtcbiAgICAgIH1cbiAgICAgIHByZXYgPSBzdHIuY2hhckNvZGVBdChpIC0gMSk7XG4gICAgICBpZiAocHJldiA8IDB4ZDgwMCB8fCBwcmV2ID4gMHhkYmZmKSB7XG4gICAgICAgIC8vIChjb3VsZCBjaGFuZ2UgbGFzdCBoZXggdG8gMHhEQjdGIHRvIHRyZWF0IGhpZ2ggcHJpdmF0ZSBzdXJyb2dhdGVzXG4gICAgICAgIC8vIGFzIHNpbmdsZSBjaGFyYWN0ZXJzKVxuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0xvdyBzdXJyb2dhdGUgd2l0aG91dCBwcmVjZWRpbmcgaGlnaCBzdXJyb2dhdGUnKTtcbiAgICAgIH1cbiAgICAgIC8vIFdlIGNhbiBwYXNzIG92ZXIgbG93IHN1cnJvZ2F0ZXMgbm93IGFzIHRoZSBzZWNvbmRcbiAgICAgIC8vIGNvbXBvbmVudCBpbiBhIHBhaXIgd2hpY2ggd2UgaGF2ZSBhbHJlYWR5IHByb2Nlc3NlZFxuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICByZXR1cm4gc3RyLmNoYXJBdChpKTtcbiAgfTtcblxuICBmb3IgKGkgPSAwLCBsZ3RoID0gMDsgaSA8IHN0ci5sZW5ndGg7IGkrKykge1xuICAgIGlmIChnZXRXaG9sZUNoYXIoc3RyLCBpKSA9PT0gZmFsc2UpIHtcbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cbiAgICAvLyBBZGFwdCB0aGlzIGxpbmUgYXQgdGhlIHRvcCBvZiBhbnkgbG9vcCwgcGFzc2luZyBpbiB0aGUgd2hvbGUgc3RyaW5nIGFuZFxuICAgIC8vIHRoZSBjdXJyZW50IGl0ZXJhdGlvbiBhbmQgcmV0dXJuaW5nIGEgdmFyaWFibGUgdG8gcmVwcmVzZW50IHRoZSBpbmRpdmlkdWFsIGNoYXJhY3RlcjtcbiAgICAvLyBwdXJwb3NlIGlzIHRvIHRyZWF0IHRoZSBmaXJzdCBwYXJ0IG9mIGEgc3Vycm9nYXRlIHBhaXIgYXMgdGhlIHdob2xlIGNoYXJhY3RlciBhbmQgdGhlblxuICAgIC8vIGlnbm9yZSB0aGUgc2Vjb25kIHBhcnRcbiAgICBsZ3RoKys7XG4gIH1cblxuICByZXR1cm4gbGd0aDtcbn07XG4vLyMgc291cmNlTWFwcGluZ1VSTD1zdHJsZW4uanMubWFwIiwiJ3VzZSBzdHJpY3QnO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIGlzX251bWVyaWMobWl4ZWRWYXIpIHtcbiAgLy8gIGRpc2N1c3MgYXQ6IGh0dHBzOi8vbG9jdXR1cy5pby9waHAvaXNfbnVtZXJpYy9cbiAgLy8gb3JpZ2luYWwgYnk6IEtldmluIHZhbiBab25uZXZlbGQgKGh0dHBzOi8va3Z6LmlvKVxuICAvLyBpbXByb3ZlZCBieTogRGF2aWRcbiAgLy8gaW1wcm92ZWQgYnk6IHRhaXRoXG4gIC8vIGJ1Z2ZpeGVkIGJ5OiBUaW0gZGUgS29uaW5nXG4gIC8vIGJ1Z2ZpeGVkIGJ5OiBXZWJEZXZIb2JvIChodHRwczovL3dlYmRldmhvYm8uYmxvZ3Nwb3QuY29tLylcbiAgLy8gYnVnZml4ZWQgYnk6IEJyZXR0IFphbWlyIChodHRwczovL2JyZXR0LXphbWlyLm1lKVxuICAvLyBidWdmaXhlZCBieTogRGVuaXMgQ2hlbnUgKGh0dHBzOi8vc2hub3VsbGUubmV0KVxuICAvLyAgIGV4YW1wbGUgMTogaXNfbnVtZXJpYygxODYuMzEpXG4gIC8vICAgcmV0dXJucyAxOiB0cnVlXG4gIC8vICAgZXhhbXBsZSAyOiBpc19udW1lcmljKCdLZXZpbiB2YW4gWm9ubmV2ZWxkJylcbiAgLy8gICByZXR1cm5zIDI6IGZhbHNlXG4gIC8vICAgZXhhbXBsZSAzOiBpc19udW1lcmljKCcgKzE4Ni4zMWUyJylcbiAgLy8gICByZXR1cm5zIDM6IHRydWVcbiAgLy8gICBleGFtcGxlIDQ6IGlzX251bWVyaWMoJycpXG4gIC8vICAgcmV0dXJucyA0OiBmYWxzZVxuICAvLyAgIGV4YW1wbGUgNTogaXNfbnVtZXJpYyhbXSlcbiAgLy8gICByZXR1cm5zIDU6IGZhbHNlXG4gIC8vICAgZXhhbXBsZSA2OiBpc19udW1lcmljKCcxICcpXG4gIC8vICAgcmV0dXJucyA2OiBmYWxzZVxuXG4gIHZhciB3aGl0ZXNwYWNlID0gWycgJywgJ1xcbicsICdcXHInLCAnXFx0JywgJ1xcZicsICdcXHgwYicsICdcXHhhMCcsICdcXHUyMDAwJywgJ1xcdTIwMDEnLCAnXFx1MjAwMicsICdcXHUyMDAzJywgJ1xcdTIwMDQnLCAnXFx1MjAwNScsICdcXHUyMDA2JywgJ1xcdTIwMDcnLCAnXFx1MjAwOCcsICdcXHUyMDA5JywgJ1xcdTIwMEEnLCAnXFx1MjAwQicsICdcXHUyMDI4JywgJ1xcdTIwMjknLCAnXFx1MzAwMCddLmpvaW4oJycpO1xuXG4gIC8vIEB0b2RvOiBCcmVhayB0aGlzIHVwIHVzaW5nIG1hbnkgc2luZ2xlIGNvbmRpdGlvbnMgd2l0aCBlYXJseSByZXR1cm5zXG4gIHJldHVybiAodHlwZW9mIG1peGVkVmFyID09PSAnbnVtYmVyJyB8fCB0eXBlb2YgbWl4ZWRWYXIgPT09ICdzdHJpbmcnICYmIHdoaXRlc3BhY2UuaW5kZXhPZihtaXhlZFZhci5zbGljZSgtMSkpID09PSAtMSkgJiYgbWl4ZWRWYXIgIT09ICcnICYmICFpc05hTihtaXhlZFZhcik7XG59O1xuLy8jIHNvdXJjZU1hcHBpbmdVUkw9aXNfbnVtZXJpYy5qcy5tYXAiLCIvLyBUaGUgbW9kdWxlIGNhY2hlXG52YXIgX193ZWJwYWNrX21vZHVsZV9jYWNoZV9fID0ge307XG5cbi8vIFRoZSByZXF1aXJlIGZ1bmN0aW9uXG5mdW5jdGlvbiBfX3dlYnBhY2tfcmVxdWlyZV9fKG1vZHVsZUlkKSB7XG5cdC8vIENoZWNrIGlmIG1vZHVsZSBpcyBpbiBjYWNoZVxuXHR2YXIgY2FjaGVkTW9kdWxlID0gX193ZWJwYWNrX21vZHVsZV9jYWNoZV9fW21vZHVsZUlkXTtcblx0aWYgKGNhY2hlZE1vZHVsZSAhPT0gdW5kZWZpbmVkKSB7XG5cdFx0cmV0dXJuIGNhY2hlZE1vZHVsZS5leHBvcnRzO1xuXHR9XG5cdC8vIENyZWF0ZSBhIG5ldyBtb2R1bGUgKGFuZCBwdXQgaXQgaW50byB0aGUgY2FjaGUpXG5cdHZhciBtb2R1bGUgPSBfX3dlYnBhY2tfbW9kdWxlX2NhY2hlX19bbW9kdWxlSWRdID0ge1xuXHRcdC8vIG5vIG1vZHVsZS5pZCBuZWVkZWRcblx0XHQvLyBubyBtb2R1bGUubG9hZGVkIG5lZWRlZFxuXHRcdGV4cG9ydHM6IHt9XG5cdH07XG5cblx0Ly8gRXhlY3V0ZSB0aGUgbW9kdWxlIGZ1bmN0aW9uXG5cdF9fd2VicGFja19tb2R1bGVzX19bbW9kdWxlSWRdKG1vZHVsZSwgbW9kdWxlLmV4cG9ydHMsIF9fd2VicGFja19yZXF1aXJlX18pO1xuXG5cdC8vIFJldHVybiB0aGUgZXhwb3J0cyBvZiB0aGUgbW9kdWxlXG5cdHJldHVybiBtb2R1bGUuZXhwb3J0cztcbn1cblxuIiwiLy8gZ2V0RGVmYXVsdEV4cG9ydCBmdW5jdGlvbiBmb3IgY29tcGF0aWJpbGl0eSB3aXRoIG5vbi1oYXJtb255IG1vZHVsZXNcbl9fd2VicGFja19yZXF1aXJlX18ubiA9IGZ1bmN0aW9uKG1vZHVsZSkge1xuXHR2YXIgZ2V0dGVyID0gbW9kdWxlICYmIG1vZHVsZS5fX2VzTW9kdWxlID9cblx0XHRmdW5jdGlvbigpIHsgcmV0dXJuIG1vZHVsZVsnZGVmYXVsdCddOyB9IDpcblx0XHRmdW5jdGlvbigpIHsgcmV0dXJuIG1vZHVsZTsgfTtcblx0X193ZWJwYWNrX3JlcXVpcmVfXy5kKGdldHRlciwgeyBhOiBnZXR0ZXIgfSk7XG5cdHJldHVybiBnZXR0ZXI7XG59OyIsIi8vIGRlZmluZSBnZXR0ZXIgZnVuY3Rpb25zIGZvciBoYXJtb255IGV4cG9ydHNcbl9fd2VicGFja19yZXF1aXJlX18uZCA9IGZ1bmN0aW9uKGV4cG9ydHMsIGRlZmluaXRpb24pIHtcblx0Zm9yKHZhciBrZXkgaW4gZGVmaW5pdGlvbikge1xuXHRcdGlmKF9fd2VicGFja19yZXF1aXJlX18ubyhkZWZpbml0aW9uLCBrZXkpICYmICFfX3dlYnBhY2tfcmVxdWlyZV9fLm8oZXhwb3J0cywga2V5KSkge1xuXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIGtleSwgeyBlbnVtZXJhYmxlOiB0cnVlLCBnZXQ6IGRlZmluaXRpb25ba2V5XSB9KTtcblx0XHR9XG5cdH1cbn07IiwiX193ZWJwYWNrX3JlcXVpcmVfXy5nID0gKGZ1bmN0aW9uKCkge1xuXHRpZiAodHlwZW9mIGdsb2JhbFRoaXMgPT09ICdvYmplY3QnKSByZXR1cm4gZ2xvYmFsVGhpcztcblx0dHJ5IHtcblx0XHRyZXR1cm4gdGhpcyB8fCBuZXcgRnVuY3Rpb24oJ3JldHVybiB0aGlzJykoKTtcblx0fSBjYXRjaCAoZSkge1xuXHRcdGlmICh0eXBlb2Ygd2luZG93ID09PSAnb2JqZWN0JykgcmV0dXJuIHdpbmRvdztcblx0fVxufSkoKTsiLCJfX3dlYnBhY2tfcmVxdWlyZV9fLm8gPSBmdW5jdGlvbihvYmosIHByb3ApIHsgcmV0dXJuIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChvYmosIHByb3ApOyB9IiwiLy8gZGVmaW5lIF9fZXNNb2R1bGUgb24gZXhwb3J0c1xuX193ZWJwYWNrX3JlcXVpcmVfXy5yID0gZnVuY3Rpb24oZXhwb3J0cykge1xuXHRpZih0eXBlb2YgU3ltYm9sICE9PSAndW5kZWZpbmVkJyAmJiBTeW1ib2wudG9TdHJpbmdUYWcpIHtcblx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgU3ltYm9sLnRvU3RyaW5nVGFnLCB7IHZhbHVlOiAnTW9kdWxlJyB9KTtcblx0fVxuXHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgJ19fZXNNb2R1bGUnLCB7IHZhbHVlOiB0cnVlIH0pO1xufTsiLCIvKiFcbiAqIExhcmF2ZWwgSmF2YXNjcmlwdCBWYWxpZGF0aW9uXG4gKlxuICogaHR0cHM6Ly9naXRodWIuY29tL3Byb2VuZ3NvZnQvbGFyYXZlbC1qc3ZhbGlkYXRpb25cbiAqXG4gKiBIZWxwZXIgZnVuY3Rpb25zIHVzZWQgYnkgdmFsaWRhdG9yc1xuICpcbiAqIENvcHlyaWdodCAoYykgMjAxNyBQcm9lbmdzb2Z0XG4gKiBSZWxlYXNlZCB1bmRlciB0aGUgTUlUIGxpY2Vuc2VcbiAqL1xuXG5pbXBvcnQgc3RybGVuIGZyb20gJ2xvY3V0dXMvcGhwL3N0cmluZ3Mvc3RybGVuJztcbmltcG9ydCBhcnJheV9kaWZmIGZyb20gJ2xvY3V0dXMvcGhwL2FycmF5L2FycmF5X2RpZmYnO1xuaW1wb3J0IHN0cnRvdGltZSBmcm9tICdsb2N1dHVzL3BocC9kYXRldGltZS9zdHJ0b3RpbWUnO1xuaW1wb3J0IGlzX251bWVyaWMgZnJvbSAnbG9jdXR1cy9waHAvdmFyL2lzX251bWVyaWMnO1xuXG4kLmV4dGVuZCh0cnVlLCBsYXJhdmVsVmFsaWRhdGlvbiwge1xuXG4gICAgaGVscGVyczoge1xuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBOdW1lcmljIHJ1bGVzXG4gICAgICAgICAqL1xuICAgICAgICBudW1lcmljUnVsZXM6IFsnSW50ZWdlcicsICdOdW1lcmljJ10sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIEdldHMgdGhlIGZpbGUgaW5mb3JtYXRpb24gZnJvbSBmaWxlIGlucHV0LlxuICAgICAgICAgKlxuICAgICAgICAgKiBAcGFyYW0gZmllbGRPYmpcbiAgICAgICAgICogQHBhcmFtIGluZGV4XG4gICAgICAgICAqIEByZXR1cm5zIHt7ZmlsZTogKiwgZXh0ZW5zaW9uOiBzdHJpbmcsIHNpemU6IG51bWJlcn19XG4gICAgICAgICAqL1xuICAgICAgICBmaWxlaW5mbzogZnVuY3Rpb24gKGZpZWxkT2JqLCBpbmRleCkge1xuICAgICAgICAgICAgdmFyIEZpbGVOYW1lID0gZmllbGRPYmoudmFsdWU7XG4gICAgICAgICAgICBpbmRleCA9IHR5cGVvZiBpbmRleCAhPT0gJ3VuZGVmaW5lZCcgPyBpbmRleCA6IDA7XG4gICAgICAgICAgICBpZiAoIGZpZWxkT2JqLmZpbGVzICE9PSBudWxsICkge1xuICAgICAgICAgICAgICAgIGlmICh0eXBlb2YgZmllbGRPYmouZmlsZXNbaW5kZXhdICE9PSAndW5kZWZpbmVkJykge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICAgICAgICAgICAgZmlsZTogRmlsZU5hbWUsXG4gICAgICAgICAgICAgICAgICAgICAgICBleHRlbnNpb246IEZpbGVOYW1lLnN1YnN0cihGaWxlTmFtZS5sYXN0SW5kZXhPZignLicpICsgMSksXG4gICAgICAgICAgICAgICAgICAgICAgICBzaXplOiBmaWVsZE9iai5maWxlc1tpbmRleF0uc2l6ZSAvIDEwMjQsXG4gICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiBmaWVsZE9iai5maWxlc1tpbmRleF0udHlwZVxuICAgICAgICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgfSxcblxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBHZXRzIHRoZSBzZWxlY3RvcnMgZm9yIHRoIHNwZWNpZmllZCBmaWVsZCBuYW1lcy5cbiAgICAgICAgICpcbiAgICAgICAgICogQHBhcmFtIG5hbWVzXG4gICAgICAgICAqIEByZXR1cm5zIHtzdHJpbmd9XG4gICAgICAgICAqL1xuICAgICAgICBzZWxlY3RvcjogZnVuY3Rpb24gKG5hbWVzKSB7XG4gICAgICAgICAgICB2YXIgc2VsZWN0b3IgPSBbXTtcbiAgICAgICAgICAgIGlmICghIHRoaXMuaXNBcnJheShuYW1lcykpICB7XG4gICAgICAgICAgICAgICAgbmFtZXMgPSBbbmFtZXNdO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBuYW1lcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgIHNlbGVjdG9yLnB1c2goXCJbbmFtZT0nXCIgKyBuYW1lc1tpXSArIFwiJ11cIik7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gc2VsZWN0b3Iuam9pbigpO1xuICAgICAgICB9LFxuXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIENoZWNrIGlmIGVsZW1lbnQgaGFzIG51bWVyaWMgcnVsZXMuXG4gICAgICAgICAqXG4gICAgICAgICAqIEBwYXJhbSBlbGVtZW50XG4gICAgICAgICAqIEByZXR1cm5zIHtib29sZWFufVxuICAgICAgICAgKi9cbiAgICAgICAgaGFzTnVtZXJpY1J1bGVzOiBmdW5jdGlvbiAoZWxlbWVudCkge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMuaGFzUnVsZXMoZWxlbWVudCwgdGhpcy5udW1lcmljUnVsZXMpO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBDaGVjayBpZiBlbGVtZW50IGhhcyBwYXNzZWQgcnVsZXMuXG4gICAgICAgICAqXG4gICAgICAgICAqIEBwYXJhbSBlbGVtZW50XG4gICAgICAgICAqIEBwYXJhbSBydWxlc1xuICAgICAgICAgKiBAcmV0dXJucyB7Ym9vbGVhbn1cbiAgICAgICAgICovXG4gICAgICAgIGhhc1J1bGVzOiBmdW5jdGlvbiAoZWxlbWVudCwgcnVsZXMpIHtcblxuICAgICAgICAgICAgdmFyIGZvdW5kID0gZmFsc2U7XG4gICAgICAgICAgICBpZiAodHlwZW9mIHJ1bGVzID09PSAnc3RyaW5nJykge1xuICAgICAgICAgICAgICAgIHJ1bGVzID0gW3J1bGVzXTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdmFyIHZhbGlkYXRvciA9ICQuZGF0YShlbGVtZW50LmZvcm0sIFwidmFsaWRhdG9yXCIpO1xuICAgICAgICAgICAgdmFyIGxpc3RSdWxlcyA9IFtdO1xuICAgICAgICAgICAgdmFyIGNhY2hlID0gdmFsaWRhdG9yLmFycmF5UnVsZXNDYWNoZTtcbiAgICAgICAgICAgIGlmIChlbGVtZW50Lm5hbWUgaW4gY2FjaGUpIHtcbiAgICAgICAgICAgICAgICAkLmVhY2goY2FjaGVbZWxlbWVudC5uYW1lXSwgZnVuY3Rpb24gKGluZGV4LCBhcnJheVJ1bGUpIHtcbiAgICAgICAgICAgICAgICAgICAgbGlzdFJ1bGVzLnB1c2goYXJyYXlSdWxlKTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmIChlbGVtZW50Lm5hbWUgaW4gdmFsaWRhdG9yLnNldHRpbmdzLnJ1bGVzKSB7XG4gICAgICAgICAgICAgICAgbGlzdFJ1bGVzLnB1c2godmFsaWRhdG9yLnNldHRpbmdzLnJ1bGVzW2VsZW1lbnQubmFtZV0pO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgJC5lYWNoKGxpc3RSdWxlcywgZnVuY3Rpb24oaW5kZXgsb2JqUnVsZXMpe1xuICAgICAgICAgICAgICAgIGlmICgnbGFyYXZlbFZhbGlkYXRpb24nIGluIG9ialJ1bGVzKSB7XG4gICAgICAgICAgICAgICAgICAgIHZhciBfcnVsZXM9b2JqUnVsZXMubGFyYXZlbFZhbGlkYXRpb247XG4gICAgICAgICAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgX3J1bGVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoJC5pbkFycmF5KF9ydWxlc1tpXVswXSxydWxlcykgIT09IC0xKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZm91bmQgPSB0cnVlO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICByZXR1cm4gZm91bmQ7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIFJldHVybiB0aGUgc3RyaW5nIGxlbmd0aCB1c2luZyBQSFAgZnVuY3Rpb24uXG4gICAgICAgICAqIGh0dHA6Ly9waHAubmV0L21hbnVhbC9lbi9mdW5jdGlvbi5zdHJsZW4ucGhwXG4gICAgICAgICAqIGh0dHA6Ly9waHBqcy5vcmcvZnVuY3Rpb25zL3N0cmxlbi9cbiAgICAgICAgICpcbiAgICAgICAgICogQHBhcmFtIHN0cmluZ1xuICAgICAgICAgKi9cbiAgICAgICAgc3RybGVuOiBmdW5jdGlvbiAoc3RyaW5nKSB7XG4gICAgICAgICAgICByZXR1cm4gc3RybGVuKHN0cmluZyk7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIEdldCB0aGUgc2l6ZSBvZiB0aGUgb2JqZWN0IGRlcGVuZGluZyBvZiBoaXMgdHlwZS5cbiAgICAgICAgICpcbiAgICAgICAgICogQHBhcmFtIG9ialxuICAgICAgICAgKiBAcGFyYW0gZWxlbWVudFxuICAgICAgICAgKiBAcGFyYW0gdmFsdWVcbiAgICAgICAgICogQHJldHVybnMgaW50XG4gICAgICAgICAqL1xuICAgICAgICBnZXRTaXplOiBmdW5jdGlvbiBnZXRTaXplKG9iaiwgZWxlbWVudCwgdmFsdWUpIHtcblxuICAgICAgICAgICAgaWYgKHRoaXMuaGFzTnVtZXJpY1J1bGVzKGVsZW1lbnQpICYmIHRoaXMuaXNfbnVtZXJpYyh2YWx1ZSkpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gcGFyc2VGbG9hdCh2YWx1ZSk7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKHRoaXMuaXNBcnJheSh2YWx1ZSkpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gcGFyc2VGbG9hdCh2YWx1ZS5sZW5ndGgpO1xuICAgICAgICAgICAgfSBlbHNlIGlmIChlbGVtZW50LnR5cGUgPT09ICdmaWxlJykge1xuICAgICAgICAgICAgICAgIHJldHVybiBwYXJzZUZsb2F0KE1hdGguZmxvb3IodGhpcy5maWxlaW5mbyhlbGVtZW50KS5zaXplKSk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHJldHVybiBwYXJzZUZsb2F0KHRoaXMuc3RybGVuKHZhbHVlKSk7XG4gICAgICAgIH0sXG5cblxuICAgICAgICAvKipcbiAgICAgICAgICogUmV0dXJuIHNwZWNpZmllZCBydWxlIGZyb20gZWxlbWVudC5cbiAgICAgICAgICpcbiAgICAgICAgICogQHBhcmFtIHJ1bGVcbiAgICAgICAgICogQHBhcmFtIGVsZW1lbnRcbiAgICAgICAgICogQHJldHVybnMgb2JqZWN0XG4gICAgICAgICAqL1xuICAgICAgICBnZXRMYXJhdmVsVmFsaWRhdGlvbjogZnVuY3Rpb24ocnVsZSwgZWxlbWVudCkge1xuXG4gICAgICAgICAgICB2YXIgZm91bmQgPSB1bmRlZmluZWQ7XG4gICAgICAgICAgICAkLmVhY2goJC52YWxpZGF0b3Iuc3RhdGljUnVsZXMoZWxlbWVudCksIGZ1bmN0aW9uKGtleSwgcnVsZXMpIHtcbiAgICAgICAgICAgICAgICBpZiAoa2V5PT09XCJsYXJhdmVsVmFsaWRhdGlvblwiKSB7XG4gICAgICAgICAgICAgICAgICAgICQuZWFjaChydWxlcywgZnVuY3Rpb24gKGksIHZhbHVlKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAodmFsdWVbMF09PT1ydWxlKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZm91bmQ9dmFsdWU7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICByZXR1cm4gZm91bmQ7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIFJldHVybiBoZSB0aW1lc3RhbXAgb2YgdmFsdWUgcGFzc2VkIHVzaW5nIGZvcm1hdCBvciBkZWZhdWx0IGZvcm1hdCBpbiBlbGVtZW50LlxuICAgICAgICAgKlxuICAgICAgICAgKiBAcGFyYW0gdmFsdWVcbiAgICAgICAgICogQHBhcmFtIGZvcm1hdFxuICAgICAgICAgKiBAcmV0dXJucyB7Ym9vbGVhbnxpbnR9XG4gICAgICAgICAqL1xuICAgICAgICBwYXJzZVRpbWU6IGZ1bmN0aW9uICh2YWx1ZSwgZm9ybWF0KSB7XG5cbiAgICAgICAgICAgIHZhciB0aW1lVmFsdWUgPSBmYWxzZTtcbiAgICAgICAgICAgIHZhciBmbXQgPSBuZXcgRGF0ZUZvcm1hdHRlcigpO1xuXG4gICAgICAgICAgICBpZiAodHlwZW9mIHZhbHVlID09PSAnbnVtYmVyJyAmJiB0eXBlb2YgZm9ybWF0ID09PSAndW5kZWZpbmVkJykge1xuICAgICAgICAgICAgICAgIHJldHVybiB2YWx1ZTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKHR5cGVvZiBmb3JtYXQgPT09ICdvYmplY3QnKSB7XG4gICAgICAgICAgICAgICAgdmFyIGRhdGVSdWxlID0gdGhpcy5nZXRMYXJhdmVsVmFsaWRhdGlvbignRGF0ZUZvcm1hdCcsIGZvcm1hdCk7XG4gICAgICAgICAgICAgICAgaWYgKGRhdGVSdWxlICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgICAgICAgZm9ybWF0ID0gZGF0ZVJ1bGVbMV1bMF07XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgZm9ybWF0ID0gbnVsbDtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmIChmb3JtYXQgPT0gbnVsbCkge1xuICAgICAgICAgICAgICAgIHRpbWVWYWx1ZSA9IHRoaXMuc3RydG90aW1lKHZhbHVlKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgdGltZVZhbHVlID0gZm10LnBhcnNlRGF0ZSh2YWx1ZSwgZm9ybWF0KTtcbiAgICAgICAgICAgICAgICBpZiAodGltZVZhbHVlIGluc3RhbmNlb2YgRGF0ZSAmJiBmbXQuZm9ybWF0RGF0ZSh0aW1lVmFsdWUsIGZvcm1hdCkgPT09IHZhbHVlKSB7XG4gICAgICAgICAgICAgICAgICAgIHRpbWVWYWx1ZSA9IE1hdGgucm91bmQoKHRpbWVWYWx1ZS5nZXRUaW1lKCkgLyAxMDAwKSk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgdGltZVZhbHVlID0gZmFsc2U7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICByZXR1cm4gdGltZVZhbHVlO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBDb21wYXJlIGEgZ2l2ZW4gZGF0ZSBhZ2FpbnN0IGFub3RoZXIgdXNpbmcgYW4gb3BlcmF0b3IuXG4gICAgICAgICAqXG4gICAgICAgICAqIEBwYXJhbSB2YWxpZGF0b3JcbiAgICAgICAgICogQHBhcmFtIHZhbHVlXG4gICAgICAgICAqIEBwYXJhbSBlbGVtZW50XG4gICAgICAgICAqIEBwYXJhbSBwYXJhbXNcbiAgICAgICAgICogQHBhcmFtIG9wZXJhdG9yXG4gICAgICAgICAqIEByZXR1cm4ge2Jvb2xlYW59XG4gICAgICAgICAqL1xuICAgICAgICBjb21wYXJlRGF0ZXM6IGZ1bmN0aW9uICh2YWxpZGF0b3IsIHZhbHVlLCBlbGVtZW50LCBwYXJhbXMsIG9wZXJhdG9yKSB7XG5cbiAgICAgICAgICAgIHZhciB0aW1lQ29tcGFyZSA9IHRoaXMucGFyc2VUaW1lKHBhcmFtcyk7XG5cbiAgICAgICAgICAgIGlmICghdGltZUNvbXBhcmUpIHtcbiAgICAgICAgICAgICAgICB2YXIgdGFyZ2V0ID0gdGhpcy5kZXBlbmRlbnRFbGVtZW50KHZhbGlkYXRvciwgZWxlbWVudCwgcGFyYW1zKTtcbiAgICAgICAgICAgICAgICBpZiAodGFyZ2V0ID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB0aW1lQ29tcGFyZSA9IHRoaXMucGFyc2VUaW1lKHZhbGlkYXRvci5lbGVtZW50VmFsdWUodGFyZ2V0KSwgdGFyZ2V0KTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdmFyIHRpbWVWYWx1ZSA9IHRoaXMucGFyc2VUaW1lKHZhbHVlLCBlbGVtZW50KTtcbiAgICAgICAgICAgIGlmICh0aW1lVmFsdWUgPT09IGZhbHNlKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBzd2l0Y2ggKG9wZXJhdG9yKSB7XG4gICAgICAgICAgICAgICAgY2FzZSAnPCc6XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiB0aW1lVmFsdWUgPCB0aW1lQ29tcGFyZTtcblxuICAgICAgICAgICAgICAgIGNhc2UgJzw9JzpcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHRpbWVWYWx1ZSA8PSB0aW1lQ29tcGFyZTtcblxuICAgICAgICAgICAgICAgIGNhc2UgJz09JzpcbiAgICAgICAgICAgICAgICBjYXNlICc9PT0nOlxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gdGltZVZhbHVlID09PSB0aW1lQ29tcGFyZTtcblxuICAgICAgICAgICAgICAgIGNhc2UgJz4nOlxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gdGltZVZhbHVlID4gdGltZUNvbXBhcmU7XG5cbiAgICAgICAgICAgICAgICBjYXNlICc+PSc6XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiB0aW1lVmFsdWUgPj0gdGltZUNvbXBhcmU7XG5cbiAgICAgICAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1Vuc3VwcG9ydGVkIG9wZXJhdG9yLicpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBUaGlzIG1ldGhvZCBhbGxvd3MgeW91IHRvIGludGVsbGlnZW50bHkgZ3Vlc3MgdGhlIGRhdGUgYnkgY2xvc2VseSBtYXRjaGluZyB0aGUgc3BlY2lmaWMgZm9ybWF0LlxuICAgICAgICAgKlxuICAgICAgICAgKiBAcGFyYW0gdmFsdWVcbiAgICAgICAgICogQHBhcmFtIGZvcm1hdFxuICAgICAgICAgKiBAcmV0dXJucyB7RGF0ZX1cbiAgICAgICAgICovXG4gICAgICAgIGd1ZXNzRGF0ZTogZnVuY3Rpb24gKHZhbHVlLCBmb3JtYXQpIHtcbiAgICAgICAgICAgIHZhciBmbXQgPSBuZXcgRGF0ZUZvcm1hdHRlcigpO1xuICAgICAgICAgICAgcmV0dXJuIGZtdC5ndWVzc0RhdGUodmFsdWUsIGZvcm1hdClcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogUmV0dXJucyBVbml4IHRpbWVzdGFtcCBiYXNlZCBvbiBQSFAgZnVuY3Rpb24gc3Ryb3RvdGltZS5cbiAgICAgICAgICogaHR0cDovL3BocC5uZXQvbWFudWFsL2VzL2Z1bmN0aW9uLnN0cnRvdGltZS5waHBcbiAgICAgICAgICogaHR0cDovL3BocGpzLm9yZy9mdW5jdGlvbnMvc3RydG90aW1lL1xuICAgICAgICAgKlxuICAgICAgICAgKiBAcGFyYW0gdGV4dFxuICAgICAgICAgKiBAcGFyYW0gbm93XG4gICAgICAgICAqIEByZXR1cm5zIHsqfVxuICAgICAgICAgKi9cbiAgICAgICAgc3RydG90aW1lOiBmdW5jdGlvbiAodGV4dCwgbm93KSB7XG4gICAgICAgICAgICByZXR1cm4gc3RydG90aW1lKHRleHQsIG5vdylcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogUmV0dXJucyBpZiB2YWx1ZSBpcyBudW1lcmljLlxuICAgICAgICAgKiBodHRwOi8vcGhwLm5ldC9tYW51YWwvZXMvdmFyLmlzX251bWVyaWMucGhwXG4gICAgICAgICAqIGh0dHA6Ly9waHBqcy5vcmcvZnVuY3Rpb25zL2lzX251bWVyaWMvXG4gICAgICAgICAqXG4gICAgICAgICAqIEBwYXJhbSBtaXhlZF92YXJcbiAgICAgICAgICogQHJldHVybnMgeyp9XG4gICAgICAgICAqL1xuICAgICAgICBpc19udW1lcmljOiBmdW5jdGlvbiAobWl4ZWRfdmFyKSB7XG4gICAgICAgICAgICByZXR1cm4gaXNfbnVtZXJpYyhtaXhlZF92YXIpXG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIENoZWNrIHdoZXRoZXIgdGhlIGFyZ3VtZW50IGlzIG9mIHR5cGUgQXJyYXkuXG4gICAgICAgICAqIGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0phdmFTY3JpcHQvUmVmZXJlbmNlL0dsb2JhbF9PYmplY3RzL0FycmF5L2lzQXJyYXkjUG9seWZpbGxcbiAgICAgICAgICpcbiAgICAgICAgICogQHBhcmFtIGFyZ1xuICAgICAgICAgKiBAcmV0dXJucyB7Ym9vbGVhbn1cbiAgICAgICAgICovXG4gICAgICAgIGlzQXJyYXk6IGZ1bmN0aW9uKGFyZykge1xuICAgICAgICAgICAgcmV0dXJuIE9iamVjdC5wcm90b3R5cGUudG9TdHJpbmcuY2FsbChhcmcpID09PSAnW29iamVjdCBBcnJheV0nO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBSZXR1cm5zIEFycmF5IGRpZmYgYmFzZWQgb24gUEhQIGZ1bmN0aW9uIGFycmF5X2RpZmYuXG4gICAgICAgICAqIGh0dHA6Ly9waHAubmV0L21hbnVhbC9lcy9mdW5jdGlvbi5hcnJheV9kaWZmLnBocFxuICAgICAgICAgKiBodHRwOi8vcGhwanMub3JnL2Z1bmN0aW9ucy9hcnJheV9kaWZmL1xuICAgICAgICAgKlxuICAgICAgICAgKiBAcGFyYW0gYXJyMVxuICAgICAgICAgKiBAcGFyYW0gYXJyMlxuICAgICAgICAgKiBAcmV0dXJucyB7Kn1cbiAgICAgICAgICovXG4gICAgICAgIGFycmF5RGlmZjogZnVuY3Rpb24gKGFycjEsIGFycjIpIHtcbiAgICAgICAgICAgIHJldHVybiBhcnJheV9kaWZmKGFycjEsIGFycjIpO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBDaGVjayB3aGV0aGVyIHR3byBhcnJheXMgYXJlIGVxdWFsIHRvIG9uZSBhbm90aGVyLlxuICAgICAgICAgKlxuICAgICAgICAgKiBAcGFyYW0gYXJyMVxuICAgICAgICAgKiBAcGFyYW0gYXJyMlxuICAgICAgICAgKiBAcmV0dXJucyB7Kn1cbiAgICAgICAgICovXG4gICAgICAgIGFycmF5RXF1YWxzOiBmdW5jdGlvbiAoYXJyMSwgYXJyMikge1xuICAgICAgICAgICAgaWYgKCEgdGhpcy5pc0FycmF5KGFycjEpIHx8ICEgdGhpcy5pc0FycmF5KGFycjIpKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgXG4gICAgICAgICAgICBpZiAoYXJyMS5sZW5ndGggIT09IGFycjIubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgXG4gICAgICAgICAgICByZXR1cm4gJC5pc0VtcHR5T2JqZWN0KHRoaXMuYXJyYXlEaWZmKGFycjEsIGFycjIpKTtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogTWFrZXMgZWxlbWVudCBkZXBlbmRhbnQgZnJvbSBvdGhlci5cbiAgICAgICAgICpcbiAgICAgICAgICogQHBhcmFtIHZhbGlkYXRvclxuICAgICAgICAgKiBAcGFyYW0gZWxlbWVudFxuICAgICAgICAgKiBAcGFyYW0gbmFtZVxuICAgICAgICAgKiBAcmV0dXJucyB7Kn1cbiAgICAgICAgICovXG4gICAgICAgIGRlcGVuZGVudEVsZW1lbnQ6IGZ1bmN0aW9uKHZhbGlkYXRvciwgZWxlbWVudCwgbmFtZSkge1xuICAgICAgICAgICAgdmFyIGVsID0gdmFsaWRhdG9yLmZpbmRCeU5hbWUobmFtZSk7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIHZhciB0YXJnZXRFbGVtZW50ID0gZWxbZWwubGVuZ3RoIC0gMV07XG4gICAgXG4gICAgICAgICAgICBpZiAodGFyZ2V0RWxlbWVudCAhPT0gdW5kZWZpbmVkICYmIHZhbGlkYXRvci5zZXR0aW5ncy5vbmZvY3Vzb3V0KSB7XG4gICAgICAgICAgICAgICAgdmFyIGV2ZW50ID0gJ2JsdXInO1xuICAgICAgICAgICAgICAgIGlmIChcbiAgICAgICAgICAgICAgICAgICAgdGFyZ2V0RWxlbWVudC50YWdOYW1lID09PSAnU0VMRUNUJyB8fFxuICAgICAgICAgICAgICAgICAgICB0YXJnZXRFbGVtZW50LnRhZ05hbWUgPT09ICdPUFRJT04nIHx8XG4gICAgICAgICAgICAgICAgICAgIHRhcmdldEVsZW1lbnQudHlwZSA9PT0gJ2NoZWNrYm94JyB8fFxuICAgICAgICAgICAgICAgICAgICB0YXJnZXRFbGVtZW50LnR5cGUgPT09ICdyYWRpbydcbiAgICAgICAgICAgICAgICApIHtcbiAgICAgICAgICAgICAgICAgICAgZXZlbnQgPSAnY2xpY2snO1xuICAgICAgICAgICAgICAgIH1cbiAgICBcbiAgICAgICAgICAgICAgICB2YXIgcnVsZU5hbWUgPSAnLnZhbGlkYXRlLWxhcmF2ZWxWYWxpZGF0aW9uJztcbiAgICAgICAgICAgICAgICAkKHRhcmdldEVsZW1lbnQpXG4gICAgICAgICAgICAgICAgICAgIC5vZmYocnVsZU5hbWUpXG4gICAgICAgICAgICAgICAgICAgIC5vZmYoZXZlbnQgKyBydWxlTmFtZSArICctJyArIGVsZW1lbnQubmFtZSlcbiAgICAgICAgICAgICAgICAgICAgLm9uKGV2ZW50ICsgcnVsZU5hbWUgKyAnLScgKyBlbGVtZW50Lm5hbWUsIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICQoZWxlbWVudCkudmFsaWQoKTtcbiAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9XG4gICAgXG4gICAgICAgICAgICByZXR1cm4gdGFyZ2V0RWxlbWVudDtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogUGFyc2VzIGVycm9yIEFqYXggcmVzcG9uc2UgYW5kIGdldHMgdGhlIG1lc3NhZ2UuXG4gICAgICAgICAqXG4gICAgICAgICAqIEBwYXJhbSByZXNwb25zZVxuICAgICAgICAgKiBAcmV0dXJucyB7c3RyaW5nW119XG4gICAgICAgICAqL1xuICAgICAgICBwYXJzZUVycm9yUmVzcG9uc2U6IGZ1bmN0aW9uIChyZXNwb25zZSkge1xuICAgICAgICAgICAgdmFyIG5ld1Jlc3BvbnNlID0gWydXaG9vcHMsIGxvb2tzIGxpa2Ugc29tZXRoaW5nIHdlbnQgd3JvbmcuJ107XG4gICAgICAgICAgICBpZiAoJ3Jlc3BvbnNlVGV4dCcgaW4gcmVzcG9uc2UpIHtcbiAgICAgICAgICAgICAgICB2YXIgZXJyb3JNc2cgPSByZXNwb25zZS5yZXNwb25zZVRleHQubWF0Y2goLzxoMVxccyo+KC4qKTxcXC9oMVxccyo+L2kpO1xuICAgICAgICAgICAgICAgIGlmICh0aGlzLmlzQXJyYXkoZXJyb3JNc2cpKSB7XG4gICAgICAgICAgICAgICAgICAgIG5ld1Jlc3BvbnNlID0gW2Vycm9yTXNnWzFdXTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gbmV3UmVzcG9uc2U7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIEVzY2FwZSBzdHJpbmcgdG8gdXNlIGFzIFJlZ3VsYXIgRXhwcmVzc2lvbi5cbiAgICAgICAgICpcbiAgICAgICAgICogQHBhcmFtIHN0clxuICAgICAgICAgKiBAcmV0dXJucyBzdHJpbmdcbiAgICAgICAgICovXG4gICAgICAgIGVzY2FwZVJlZ0V4cDogZnVuY3Rpb24gKHN0cikge1xuICAgICAgICAgICAgcmV0dXJuIHN0ci5yZXBsYWNlKC9bXFwtXFxbXFxdXFwvXFx7XFx9XFwoXFwpXFwqXFwrXFw/XFwuXFxcXFxcXlxcJFxcfF0vZywgXCJcXFxcJCZcIik7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIEdlbmVyYXRlIFJlZ0V4cCBmcm9tIHdpbGRjYXJkIGF0dHJpYnV0ZXMuXG4gICAgICAgICAqXG4gICAgICAgICAqIEBwYXJhbSBuYW1lXG4gICAgICAgICAqIEByZXR1cm5zIHtSZWdFeHB9XG4gICAgICAgICAqL1xuICAgICAgICByZWdleEZyb21XaWxkY2FyZDogZnVuY3Rpb24gKG5hbWUpIHtcbiAgICAgICAgICAgIHZhciBuYW1lUGFydHMgPSBuYW1lLnNwbGl0KCdbKl0nKTtcbiAgICAgICAgICAgIGlmIChuYW1lUGFydHMubGVuZ3RoID09PSAxKSBuYW1lUGFydHMucHVzaCgnJyk7XG5cbiAgICAgICAgICAgIHJldHVybiBuZXcgUmVnRXhwKCdeJyArIG5hbWVQYXJ0cy5tYXAoZnVuY3Rpb24oeCkge1xuICAgICAgICAgICAgICAgIHJldHVybiBsYXJhdmVsVmFsaWRhdGlvbi5oZWxwZXJzLmVzY2FwZVJlZ0V4cCh4KVxuICAgICAgICAgICAgfSkuam9pbignXFxcXFtbXlxcXFxdXSpcXFxcXScpICsgJyQnKTtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogTWVyZ2UgYWRkaXRpb25hbCBsYXJhdmVsIHZhbGlkYXRpb24gcnVsZXMgaW50byB0aGUgY3VycmVudCBydWxlIHNldC5cbiAgICAgICAgICpcbiAgICAgICAgICogQHBhcmFtIHtvYmplY3R9IHJ1bGVzXG4gICAgICAgICAqIEBwYXJhbSB7b2JqZWN0fSBuZXdSdWxlc1xuICAgICAgICAgKiBAcmV0dXJucyB7b2JqZWN0fVxuICAgICAgICAgKi9cbiAgICAgICAgbWVyZ2VSdWxlczogZnVuY3Rpb24gKHJ1bGVzLCBuZXdSdWxlcykge1xuICAgICAgICAgICAgdmFyIHJ1bGVzTGlzdCA9IHtcbiAgICAgICAgICAgICAgICAnbGFyYXZlbFZhbGlkYXRpb24nOiBuZXdSdWxlcy5sYXJhdmVsVmFsaWRhdGlvbiB8fCBbXSxcbiAgICAgICAgICAgICAgICAnbGFyYXZlbFZhbGlkYXRpb25SZW1vdGUnOiBuZXdSdWxlcy5sYXJhdmVsVmFsaWRhdGlvblJlbW90ZSB8fCBbXVxuICAgICAgICAgICAgfTtcblxuICAgICAgICAgICAgZm9yICh2YXIga2V5IGluIHJ1bGVzTGlzdCkge1xuICAgICAgICAgICAgICAgIGlmIChydWxlc0xpc3Rba2V5XS5sZW5ndGggPT09IDApIHtcbiAgICAgICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgaWYgKHR5cGVvZiBydWxlc1trZXldID09PSBcInVuZGVmaW5lZFwiKSB7XG4gICAgICAgICAgICAgICAgICAgIHJ1bGVzW2tleV0gPSBbXTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBydWxlc1trZXldID0gcnVsZXNba2V5XS5jb25jYXQocnVsZXNMaXN0W2tleV0pO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICByZXR1cm4gcnVsZXM7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIEhUTUwgZW50aXR5IGVuY29kZSBhIHN0cmluZy5cbiAgICAgICAgICpcbiAgICAgICAgICogQHBhcmFtIHN0cmluZ1xuICAgICAgICAgKiBAcmV0dXJucyB7c3RyaW5nfVxuICAgICAgICAgKi9cbiAgICAgICAgZW5jb2RlOiBmdW5jdGlvbiAoc3RyaW5nKSB7XG4gICAgICAgICAgICByZXR1cm4gJCgnPGRpdi8+JykudGV4dChzdHJpbmcpLmh0bWwoKTtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogTG9va3VwIG5hbWUgaW4gYW4gYXJyYXkuXG4gICAgICAgICAqXG4gICAgICAgICAqIEBwYXJhbSB2YWxpZGF0b3JcbiAgICAgICAgICogQHBhcmFtIHtzdHJpbmd9IG5hbWUgTmFtZSBpbiBkb3Qgbm90YXRpb24gZm9ybWF0LlxuICAgICAgICAgKiBAcmV0dXJucyB7Kn1cbiAgICAgICAgICovXG4gICAgICAgIGZpbmRCeUFycmF5TmFtZTogZnVuY3Rpb24gKHZhbGlkYXRvciwgbmFtZSkge1xuICAgICAgICAgICAgdmFyIHNxTmFtZSA9IG5hbWUucmVwbGFjZSgvXFwuKFteXFwuXSspL2csICdbJDFdJyksXG4gICAgICAgICAgICAgICAgbG9va3VwcyA9IFtcbiAgICAgICAgICAgICAgICAgICAgLy8gQ29udmVydCBkb3QgdG8gc3F1YXJlIGJyYWNrZXRzLiBlLmcuIGZvby5iYXIuMCBiZWNvbWVzIGZvb1tiYXJdWzBdXG4gICAgICAgICAgICAgICAgICAgIHNxTmFtZSxcbiAgICAgICAgICAgICAgICAgICAgLy8gQXBwZW5kIFtdIHRvIHRoZSBuYW1lIGUuZy4gZm9vIGJlY29tZXMgZm9vW10gb3IgZm9vLmJhci4wIGJlY29tZXMgZm9vW2Jhcl1bMF1bXVxuICAgICAgICAgICAgICAgICAgICBzcU5hbWUgKyAnW10nLFxuICAgICAgICAgICAgICAgICAgICAvLyBSZW1vdmUga2V5IGZyb20gbGFzdCBhcnJheSBlLmcuIGZvb1tiYXJdWzBdIGJlY29tZXMgZm9vW2Jhcl1bXVxuICAgICAgICAgICAgICAgICAgICBzcU5hbWUucmVwbGFjZSgvKC4qKVxcWyguKilcXF0kL2csICckMVtdJylcbiAgICAgICAgICAgICAgICBdO1xuXG4gICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGxvb2t1cHMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICB2YXIgZWxlbSA9IHZhbGlkYXRvci5maW5kQnlOYW1lKGxvb2t1cHNbaV0pO1xuICAgICAgICAgICAgICAgIGlmIChlbGVtLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGVsZW07XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICByZXR1cm4gJChudWxsKTtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogQXR0ZW1wdCB0byBmaW5kIGFuIGVsZW1lbnQgaW4gdGhlIERPTSBtYXRjaGluZyB0aGUgZ2l2ZW4gbmFtZS5cbiAgICAgICAgICogRXhhbXBsZSBuYW1lcyBpbmNsdWRlOlxuICAgICAgICAgKiAgICAtIGRvbWFpbi4wIHdoaWNoIG1hdGNoZXMgZG9tYWluW11cbiAgICAgICAgICogICAgLSBjdXN0b21maWVsZC4zIHdoaWNoIG1hdGNoZXMgY3VzdG9tZmllbGRbM11cbiAgICAgICAgICpcbiAgICAgICAgICogQHBhcmFtIHZhbGlkYXRvclxuICAgICAgICAgKiBAcGFyYW0ge3N0cmluZ30gbmFtZVxuICAgICAgICAgKiBAcmV0dXJucyB7Kn1cbiAgICAgICAgICovXG4gICAgICAgIGZpbmRCeU5hbWU6IGZ1bmN0aW9uICh2YWxpZGF0b3IsIG5hbWUpIHtcbiAgICAgICAgICAgIC8vIEV4YWN0IG1hdGNoLlxuICAgICAgICAgICAgdmFyIGVsZW0gPSB2YWxpZGF0b3IuZmluZEJ5TmFtZShuYW1lKTtcbiAgICAgICAgICAgIGlmIChlbGVtLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gZWxlbTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gRmluZCBuYW1lIGluIGRhdGEsIHVzaW5nIGRvdCBub3RhdGlvbi5cbiAgICAgICAgICAgIHZhciBkZWxpbSA9ICcuJyxcbiAgICAgICAgICAgICAgICBwYXJ0cyAgPSBuYW1lLnNwbGl0KGRlbGltKTtcbiAgICAgICAgICAgIGZvciAodmFyIGkgPSBwYXJ0cy5sZW5ndGg7IGkgPiAwOyBpLS0pIHtcbiAgICAgICAgICAgICAgICB2YXIgcmVjb25zdHJ1Y3RlZCA9IFtdO1xuICAgICAgICAgICAgICAgIGZvciAodmFyIGMgPSAwOyBjIDwgaTsgYysrKSB7XG4gICAgICAgICAgICAgICAgICAgIHJlY29uc3RydWN0ZWQucHVzaChwYXJ0c1tjXSk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgZWxlbSA9IHRoaXMuZmluZEJ5QXJyYXlOYW1lKHZhbGlkYXRvciwgcmVjb25zdHJ1Y3RlZC5qb2luKGRlbGltKSk7XG4gICAgICAgICAgICAgICAgaWYgKGVsZW0ubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gZWxlbTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHJldHVybiAkKG51bGwpO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBJZiBpdCdzIGFuIGFycmF5IGVsZW1lbnQsIGdldCBhbGwgdmFsdWVzLlxuICAgICAgICAgKlxuICAgICAgICAgKiBAcGFyYW0gdmFsaWRhdG9yXG4gICAgICAgICAqIEBwYXJhbSBlbGVtZW50XG4gICAgICAgICAqIEByZXR1cm5zIHsqfHN0cmluZ31cbiAgICAgICAgICovXG4gICAgICAgIGFsbEVsZW1lbnRWYWx1ZXM6IGZ1bmN0aW9uICh2YWxpZGF0b3IsIGVsZW1lbnQpIHtcbiAgICAgICAgICAgIGlmIChlbGVtZW50Lm5hbWUuaW5kZXhPZignW10nKSAhPT0gLTEpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gdmFsaWRhdG9yLmZpbmRCeU5hbWUoZWxlbWVudC5uYW1lKS5tYXAoZnVuY3Rpb24gKGksIGUpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHZhbGlkYXRvci5lbGVtZW50VmFsdWUoZSk7XG4gICAgICAgICAgICAgICAgfSkuZ2V0KCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHJldHVybiB2YWxpZGF0b3IuZWxlbWVudFZhbHVlKGVsZW1lbnQpO1xuICAgICAgICB9XG4gICAgfVxufSk7XG4iXSwibmFtZXMiOlsic3RybGVuIiwiYXJyYXlfZGlmZiIsInN0cnRvdGltZSIsImlzX251bWVyaWMiLCIkIiwiZXh0ZW5kIiwibGFyYXZlbFZhbGlkYXRpb24iLCJoZWxwZXJzIiwibnVtZXJpY1J1bGVzIiwiZmlsZWluZm8iLCJmaWVsZE9iaiIsImluZGV4IiwiRmlsZU5hbWUiLCJ2YWx1ZSIsImZpbGVzIiwiZmlsZSIsImV4dGVuc2lvbiIsInN1YnN0ciIsImxhc3RJbmRleE9mIiwic2l6ZSIsInR5cGUiLCJzZWxlY3RvciIsIm5hbWVzIiwiaXNBcnJheSIsImkiLCJsZW5ndGgiLCJwdXNoIiwiam9pbiIsImhhc051bWVyaWNSdWxlcyIsImVsZW1lbnQiLCJoYXNSdWxlcyIsInJ1bGVzIiwiZm91bmQiLCJ2YWxpZGF0b3IiLCJkYXRhIiwiZm9ybSIsImxpc3RSdWxlcyIsImNhY2hlIiwiYXJyYXlSdWxlc0NhY2hlIiwibmFtZSIsImVhY2giLCJhcnJheVJ1bGUiLCJzZXR0aW5ncyIsIm9ialJ1bGVzIiwiX3J1bGVzIiwiaW5BcnJheSIsInN0cmluZyIsImdldFNpemUiLCJvYmoiLCJwYXJzZUZsb2F0IiwiTWF0aCIsImZsb29yIiwiZ2V0TGFyYXZlbFZhbGlkYXRpb24iLCJydWxlIiwidW5kZWZpbmVkIiwic3RhdGljUnVsZXMiLCJrZXkiLCJwYXJzZVRpbWUiLCJmb3JtYXQiLCJ0aW1lVmFsdWUiLCJmbXQiLCJEYXRlRm9ybWF0dGVyIiwiZGF0ZVJ1bGUiLCJwYXJzZURhdGUiLCJEYXRlIiwiZm9ybWF0RGF0ZSIsInJvdW5kIiwiZ2V0VGltZSIsImNvbXBhcmVEYXRlcyIsInBhcmFtcyIsIm9wZXJhdG9yIiwidGltZUNvbXBhcmUiLCJ0YXJnZXQiLCJkZXBlbmRlbnRFbGVtZW50IiwiZWxlbWVudFZhbHVlIiwiRXJyb3IiLCJndWVzc0RhdGUiLCJ0ZXh0Iiwibm93IiwibWl4ZWRfdmFyIiwiYXJnIiwiT2JqZWN0IiwicHJvdG90eXBlIiwidG9TdHJpbmciLCJjYWxsIiwiYXJyYXlEaWZmIiwiYXJyMSIsImFycjIiLCJhcnJheUVxdWFscyIsImlzRW1wdHlPYmplY3QiLCJlbCIsImZpbmRCeU5hbWUiLCJ0YXJnZXRFbGVtZW50Iiwib25mb2N1c291dCIsImV2ZW50IiwidGFnTmFtZSIsInJ1bGVOYW1lIiwib2ZmIiwib24iLCJ2YWxpZCIsInBhcnNlRXJyb3JSZXNwb25zZSIsInJlc3BvbnNlIiwibmV3UmVzcG9uc2UiLCJlcnJvck1zZyIsInJlc3BvbnNlVGV4dCIsIm1hdGNoIiwiZXNjYXBlUmVnRXhwIiwic3RyIiwicmVwbGFjZSIsInJlZ2V4RnJvbVdpbGRjYXJkIiwibmFtZVBhcnRzIiwic3BsaXQiLCJSZWdFeHAiLCJtYXAiLCJ4IiwibWVyZ2VSdWxlcyIsIm5ld1J1bGVzIiwicnVsZXNMaXN0IiwibGFyYXZlbFZhbGlkYXRpb25SZW1vdGUiLCJjb25jYXQiLCJlbmNvZGUiLCJodG1sIiwiZmluZEJ5QXJyYXlOYW1lIiwic3FOYW1lIiwibG9va3VwcyIsImVsZW0iLCJkZWxpbSIsInBhcnRzIiwicmVjb25zdHJ1Y3RlZCIsImMiLCJhbGxFbGVtZW50VmFsdWVzIiwiaW5kZXhPZiIsImUiLCJnZXQiXSwic291cmNlUm9vdCI6IiJ9","/*!\n * Laravel Javascript Validation\n *\n * https://github.com/proengsoft/laravel-jsvalidation\n *\n * Timezone Helper functions used by validators\n *\n * Copyright (c) 2017 Proengsoft\n * Released under the MIT license\n */\n\n$.extend(true, laravelValidation, {\n\n helpers: {\n\n /**\n * Check if the specified timezone is valid.\n *\n * @param value\n * @returns {boolean}\n */\n isTimezone: function (value) {\n\n var timezones={\n \"africa\": [\n \"abidjan\",\n \"accra\",\n \"addis_ababa\",\n \"algiers\",\n \"asmara\",\n \"bamako\",\n \"bangui\",\n \"banjul\",\n \"bissau\",\n \"blantyre\",\n \"brazzaville\",\n \"bujumbura\",\n \"cairo\",\n \"casablanca\",\n \"ceuta\",\n \"conakry\",\n \"dakar\",\n \"dar_es_salaam\",\n \"djibouti\",\n \"douala\",\n \"el_aaiun\",\n \"freetown\",\n \"gaborone\",\n \"harare\",\n \"johannesburg\",\n \"juba\",\n \"kampala\",\n \"khartoum\",\n \"kigali\",\n \"kinshasa\",\n \"lagos\",\n \"libreville\",\n \"lome\",\n \"luanda\",\n \"lubumbashi\",\n \"lusaka\",\n \"malabo\",\n \"maputo\",\n \"maseru\",\n \"mbabane\",\n \"mogadishu\",\n \"monrovia\",\n \"nairobi\",\n \"ndjamena\",\n \"niamey\",\n \"nouakchott\",\n \"ouagadougou\",\n \"porto-novo\",\n \"sao_tome\",\n \"tripoli\",\n \"tunis\",\n \"windhoek\"\n ],\n \"america\": [\n \"adak\",\n \"anchorage\",\n \"anguilla\",\n \"antigua\",\n \"araguaina\",\n \"argentina\\/buenos_aires\",\n \"argentina\\/catamarca\",\n \"argentina\\/cordoba\",\n \"argentina\\/jujuy\",\n \"argentina\\/la_rioja\",\n \"argentina\\/mendoza\",\n \"argentina\\/rio_gallegos\",\n \"argentina\\/salta\",\n \"argentina\\/san_juan\",\n \"argentina\\/san_luis\",\n \"argentina\\/tucuman\",\n \"argentina\\/ushuaia\",\n \"aruba\",\n \"asuncion\",\n \"atikokan\",\n \"bahia\",\n \"bahia_banderas\",\n \"barbados\",\n \"belem\",\n \"belize\",\n \"blanc-sablon\",\n \"boa_vista\",\n \"bogota\",\n \"boise\",\n \"cambridge_bay\",\n \"campo_grande\",\n \"cancun\",\n \"caracas\",\n \"cayenne\",\n \"cayman\",\n \"chicago\",\n \"chihuahua\",\n \"costa_rica\",\n \"creston\",\n \"cuiaba\",\n \"curacao\",\n \"danmarkshavn\",\n \"dawson\",\n \"dawson_creek\",\n \"denver\",\n \"detroit\",\n \"dominica\",\n \"edmonton\",\n \"eirunepe\",\n \"el_salvador\",\n \"fortaleza\",\n \"glace_bay\",\n \"godthab\",\n \"goose_bay\",\n \"grand_turk\",\n \"grenada\",\n \"guadeloupe\",\n \"guatemala\",\n \"guayaquil\",\n \"guyana\",\n \"halifax\",\n \"havana\",\n \"hermosillo\",\n \"indiana\\/indianapolis\",\n \"indiana\\/knox\",\n \"indiana\\/marengo\",\n \"indiana\\/petersburg\",\n \"indiana\\/tell_city\",\n \"indiana\\/vevay\",\n \"indiana\\/vincennes\",\n \"indiana\\/winamac\",\n \"inuvik\",\n \"iqaluit\",\n \"jamaica\",\n \"juneau\",\n \"kentucky\\/louisville\",\n \"kentucky\\/monticello\",\n \"kralendijk\",\n \"la_paz\",\n \"lima\",\n \"los_angeles\",\n \"lower_princes\",\n \"maceio\",\n \"managua\",\n \"manaus\",\n \"marigot\",\n \"martinique\",\n \"matamoros\",\n \"mazatlan\",\n \"menominee\",\n \"merida\",\n \"metlakatla\",\n \"mexico_city\",\n \"miquelon\",\n \"moncton\",\n \"monterrey\",\n \"montevideo\",\n \"montreal\",\n \"montserrat\",\n \"nassau\",\n \"new_york\",\n \"nipigon\",\n \"nome\",\n \"noronha\",\n \"north_dakota\\/beulah\",\n \"north_dakota\\/center\",\n \"north_dakota\\/new_salem\",\n \"ojinaga\",\n \"panama\",\n \"pangnirtung\",\n \"paramaribo\",\n \"phoenix\",\n \"port-au-prince\",\n \"port_of_spain\",\n \"porto_velho\",\n \"puerto_rico\",\n \"rainy_river\",\n \"rankin_inlet\",\n \"recife\",\n \"regina\",\n \"resolute\",\n \"rio_branco\",\n \"santa_isabel\",\n \"santarem\",\n \"santiago\",\n \"santo_domingo\",\n \"sao_paulo\",\n \"scoresbysund\",\n \"shiprock\",\n \"sitka\",\n \"st_barthelemy\",\n \"st_johns\",\n \"st_kitts\",\n \"st_lucia\",\n \"st_thomas\",\n \"st_vincent\",\n \"swift_current\",\n \"tegucigalpa\",\n \"thule\",\n \"thunder_bay\",\n \"tijuana\",\n \"toronto\",\n \"tortola\",\n \"vancouver\",\n \"whitehorse\",\n \"winnipeg\",\n \"yakutat\",\n \"yellowknife\"\n ],\n \"antarctica\": [\n \"casey\",\n \"davis\",\n \"dumontdurville\",\n \"macquarie\",\n \"mawson\",\n \"mcmurdo\",\n \"palmer\",\n \"rothera\",\n \"south_pole\",\n \"syowa\",\n \"vostok\"\n ],\n \"arctic\": [\n \"longyearbyen\"\n ],\n \"asia\": [\n \"aden\",\n \"almaty\",\n \"amman\",\n \"anadyr\",\n \"aqtau\",\n \"aqtobe\",\n \"ashgabat\",\n \"baghdad\",\n \"bahrain\",\n \"baku\",\n \"bangkok\",\n \"beirut\",\n \"bishkek\",\n \"brunei\",\n \"choibalsan\",\n \"chongqing\",\n \"colombo\",\n \"damascus\",\n \"dhaka\",\n \"dili\",\n \"dubai\",\n \"dushanbe\",\n \"gaza\",\n \"harbin\",\n \"hebron\",\n \"ho_chi_minh\",\n \"hong_kong\",\n \"hovd\",\n \"irkutsk\",\n \"jakarta\",\n \"jayapura\",\n \"jerusalem\",\n \"kabul\",\n \"kamchatka\",\n \"karachi\",\n \"kashgar\",\n \"kathmandu\",\n \"khandyga\",\n \"kolkata\",\n \"krasnoyarsk\",\n \"kuala_lumpur\",\n \"kuching\",\n \"kuwait\",\n \"macau\",\n \"magadan\",\n \"makassar\",\n \"manila\",\n \"muscat\",\n \"nicosia\",\n \"novokuznetsk\",\n \"novosibirsk\",\n \"omsk\",\n \"oral\",\n \"phnom_penh\",\n \"pontianak\",\n \"pyongyang\",\n \"qatar\",\n \"qyzylorda\",\n \"rangoon\",\n \"riyadh\",\n \"sakhalin\",\n \"samarkand\",\n \"seoul\",\n \"shanghai\",\n \"singapore\",\n \"taipei\",\n \"tashkent\",\n \"tbilisi\",\n \"tehran\",\n \"thimphu\",\n \"tokyo\",\n \"ulaanbaatar\",\n \"urumqi\",\n \"ust-nera\",\n \"vientiane\",\n \"vladivostok\",\n \"yakutsk\",\n \"yekaterinburg\",\n \"yerevan\"\n ],\n \"atlantic\": [\n \"azores\",\n \"bermuda\",\n \"canary\",\n \"cape_verde\",\n \"faroe\",\n \"madeira\",\n \"reykjavik\",\n \"south_georgia\",\n \"st_helena\",\n \"stanley\"\n ],\n \"australia\": [\n \"adelaide\",\n \"brisbane\",\n \"broken_hill\",\n \"currie\",\n \"darwin\",\n \"eucla\",\n \"hobart\",\n \"lindeman\",\n \"lord_howe\",\n \"melbourne\",\n \"perth\",\n \"sydney\"\n ],\n \"europe\": [\n \"amsterdam\",\n \"andorra\",\n \"athens\",\n \"belgrade\",\n \"berlin\",\n \"bratislava\",\n \"brussels\",\n \"bucharest\",\n \"budapest\",\n \"busingen\",\n \"chisinau\",\n \"copenhagen\",\n \"dublin\",\n \"gibraltar\",\n \"guernsey\",\n \"helsinki\",\n \"isle_of_man\",\n \"istanbul\",\n \"jersey\",\n \"kaliningrad\",\n \"kiev\",\n \"lisbon\",\n \"ljubljana\",\n \"london\",\n \"luxembourg\",\n \"madrid\",\n \"malta\",\n \"mariehamn\",\n \"minsk\",\n \"monaco\",\n \"moscow\",\n \"oslo\",\n \"paris\",\n \"podgorica\",\n \"prague\",\n \"riga\",\n \"rome\",\n \"samara\",\n \"san_marino\",\n \"sarajevo\",\n \"simferopol\",\n \"skopje\",\n \"sofia\",\n \"stockholm\",\n \"tallinn\",\n \"tirane\",\n \"uzhgorod\",\n \"vaduz\",\n \"vatican\",\n \"vienna\",\n \"vilnius\",\n \"volgograd\",\n \"warsaw\",\n \"zagreb\",\n \"zaporozhye\",\n \"zurich\"\n ],\n \"indian\": [\n \"antananarivo\",\n \"chagos\",\n \"christmas\",\n \"cocos\",\n \"comoro\",\n \"kerguelen\",\n \"mahe\",\n \"maldives\",\n \"mauritius\",\n \"mayotte\",\n \"reunion\"\n ],\n \"pacific\": [\n \"apia\",\n \"auckland\",\n \"chatham\",\n \"chuuk\",\n \"easter\",\n \"efate\",\n \"enderbury\",\n \"fakaofo\",\n \"fiji\",\n \"funafuti\",\n \"galapagos\",\n \"gambier\",\n \"guadalcanal\",\n \"guam\",\n \"honolulu\",\n \"johnston\",\n \"kiritimati\",\n \"kosrae\",\n \"kwajalein\",\n \"majuro\",\n \"marquesas\",\n \"midway\",\n \"nauru\",\n \"niue\",\n \"norfolk\",\n \"noumea\",\n \"pago_pago\",\n \"palau\",\n \"pitcairn\",\n \"pohnpei\",\n \"port_moresby\",\n \"rarotonga\",\n \"saipan\",\n \"tahiti\",\n \"tarawa\",\n \"tongatapu\",\n \"wake\",\n \"wallis\"\n ],\n \"utc\": [\n \"\"\n ]\n };\n\n var tzparts= value.split('/',2);\n var continent=tzparts[0].toLowerCase();\n var city='';\n if (tzparts[1]) {\n city=tzparts[1].toLowerCase();\n }\n\n return (continent in timezones && ( timezones[continent].length===0 || timezones[continent].indexOf(city)!==-1))\n }\n }\n});\n","/*!\n * Laravel Javascript Validation\n *\n * https://github.com/proengsoft/laravel-jsvalidation\n *\n * Methods that implement Laravel Validations\n *\n * Copyright (c) 2017 Proengsoft\n * Released under the MIT license\n */\n\n$.extend(true, laravelValidation, {\n\n methods:{\n\n helpers: laravelValidation.helpers,\n\n jsRemoteTimer:0,\n\n /**\n * \"Validate\" optional attributes.\n * Always returns true, just lets us put sometimes in rules.\n *\n * @return {boolean}\n */\n Sometimes: function() {\n return true;\n },\n\n /**\n * Bail This is the default behaivour os JSValidation.\n * Always returns true, just lets us put sometimes in rules.\n *\n * @return {boolean}\n */\n Bail: function() {\n return true;\n },\n\n /**\n * \"Indicate\" validation should pass if value is null.\n * Always returns true, just lets us put \"nullable\" in rules.\n *\n * @return {boolean}\n */\n Nullable: function() {\n return true;\n },\n\n /**\n * Validate the given attribute is filled if it is present.\n */\n Filled: function(value, element) {\n return $.validator.methods.required.call(this, value, element, true);\n },\n\n\n /**\n * Validate that a required attribute exists.\n */\n Required: function(value, element) {\n return $.validator.methods.required.call(this, value, element);\n },\n\n /**\n * Validate that an attribute exists when any other attribute exists.\n *\n * @return {boolean}\n */\n RequiredWith: function(value, element, params) {\n var validator=this,\n required=false;\n var currentObject=this;\n\n $.each(params,function(i,param) {\n var target=laravelValidation.helpers.dependentElement(\n currentObject, element, param\n );\n required=required || (\n target!==undefined &&\n $.validator.methods.required.call(\n validator,\n currentObject.elementValue(target),\n target,true\n ));\n });\n\n if (required) {\n return $.validator.methods.required.call(this, value, element, true);\n }\n return true;\n },\n\n /**\n * Validate that an attribute exists when all other attribute exists.\n *\n * @return {boolean}\n */\n RequiredWithAll: function(value, element, params) {\n var validator=this,\n required=true;\n var currentObject=this;\n\n $.each(params,function(i,param) {\n var target=laravelValidation.helpers.dependentElement(\n currentObject, element, param\n );\n required = required && (\n target!==undefined &&\n $.validator.methods.required.call(\n validator,\n currentObject.elementValue(target),\n target,true\n ));\n });\n\n if (required) {\n return $.validator.methods.required.call(this, value, element, true);\n }\n return true;\n },\n\n /**\n * Validate that an attribute exists when any other attribute does not exists.\n *\n * @return {boolean}\n */\n RequiredWithout: function(value, element, params) {\n var validator=this,\n required=false;\n var currentObject=this;\n\n $.each(params,function(i,param) {\n var target=laravelValidation.helpers.dependentElement(\n currentObject, element, param\n );\n required = required ||\n target===undefined||\n !$.validator.methods.required.call(\n validator,\n currentObject.elementValue(target),\n target,true\n );\n });\n\n if (required) {\n return $.validator.methods.required.call(this, value, element, true);\n }\n return true;\n },\n\n /**\n * Validate that an attribute exists when all other attribute does not exists.\n *\n * @return {boolean}\n */\n RequiredWithoutAll: function(value, element, params) {\n var validator=this,\n required=true,\n currentObject=this;\n\n $.each(params,function(i, param) {\n var target=laravelValidation.helpers.dependentElement(\n currentObject, element, param\n );\n required = required && (\n target===undefined ||\n !$.validator.methods.required.call(\n validator,\n currentObject.elementValue(target),\n target,true\n ));\n });\n\n if (required) {\n return $.validator.methods.required.call(this, value, element, true);\n }\n return true;\n },\n\n /**\n * Validate that an attribute exists when another attribute has a given value.\n *\n * @return {boolean}\n */\n RequiredIf: function(value, element, params) {\n\n var target=laravelValidation.helpers.dependentElement(\n this, element, params[0]\n );\n\n if (target!==undefined) {\n var val=String(this.elementValue(target));\n if (typeof val !== 'undefined') {\n var data = params.slice(1);\n if ($.inArray(val, data) !== -1) {\n return $.validator.methods.required.call(\n this, value, element, true\n );\n }\n }\n }\n\n return true;\n },\n\n /**\n * Validate that an attribute exists when another\n * attribute does not have a given value.\n *\n * @return {boolean}\n */\n RequiredUnless: function(value, element, params) {\n\n var target=laravelValidation.helpers.dependentElement(\n this, element, params[0]\n );\n\n if (target!==undefined) {\n var val=String(this.elementValue(target));\n if (typeof val !== 'undefined') {\n var data = params.slice(1);\n if ($.inArray(val, data) !== -1) {\n return true;\n }\n }\n }\n\n return $.validator.methods.required.call(\n this, value, element, true\n );\n\n },\n\n /**\n * Validate that an attribute has a matching confirmation.\n *\n * @return {boolean}\n */\n Confirmed: function(value, element, params) {\n return laravelValidation.methods.Same.call(this,value, element, params);\n },\n\n /**\n * Validate that two attributes match.\n *\n * @return {boolean}\n */\n Same: function(value, element, params) {\n\n var target=laravelValidation.helpers.dependentElement(\n this, element, params[0]\n );\n\n if (target!==undefined) {\n return String(value) === String(this.elementValue(target));\n }\n return false;\n },\n\n /**\n * Validate that the values of an attribute is in another attribute.\n *\n * @param value\n * @param element\n * @param params\n * @returns {boolean}\n * @constructor\n */\n InArray: function (value, element, params) {\n if (typeof params[0] === 'undefined') {\n return false;\n }\n var elements = this.elements();\n var found = false;\n var nameRegExp = laravelValidation.helpers.regexFromWildcard(params[0]);\n\n for ( var i = 0; i < elements.length ; i++ ) {\n var targetName = elements[i].name;\n if (targetName.match(nameRegExp)) {\n var equals = laravelValidation.methods.Same.call(this,value, element, [targetName]);\n found = found || equals;\n }\n }\n\n return found;\n },\n\n /**\n * Validate an attribute is unique among other values.\n *\n * @param value\n * @param element\n * @param params\n * @returns {boolean}\n */\n Distinct: function (value, element, params) {\n if (typeof params[0] === 'undefined') {\n return false;\n }\n\n var elements = this.elements();\n var found = false;\n var nameRegExp = laravelValidation.helpers.regexFromWildcard(params[0]);\n\n for ( var i = 0; i < elements.length ; i++ ) {\n var targetName = elements[i].name;\n if (targetName !== element.name && targetName.match(nameRegExp)) {\n var equals = laravelValidation.methods.Same.call(this,value, element, [targetName]);\n found = found || equals;\n }\n }\n\n return !found;\n },\n\n\n /**\n * Validate that an attribute is different from another attribute.\n *\n * @return {boolean}\n */\n Different: function(value, element, params) {\n return ! laravelValidation.methods.Same.call(this,value, element, params);\n },\n\n /**\n * Validate that an attribute was \"accepted\".\n * This validation rule implies the attribute is \"required\".\n *\n * @return {boolean}\n */\n Accepted: function(value) {\n var regex = new RegExp(\"^(?:(yes|on|1|true))$\",'i');\n return regex.test(value);\n },\n\n /**\n * Validate that an attribute is an array.\n *\n * @param value\n * @param element\n */\n Array: function(value, element) {\n if (element.name.indexOf('[') !== -1 && element.name.indexOf(']') !== -1) {\n return true;\n }\n\n return laravelValidation.helpers.isArray(value);\n },\n\n /**\n * Validate that an attribute is a boolean.\n *\n * @return {boolean}\n */\n Boolean: function(value) {\n var regex= new RegExp(\"^(?:(true|false|1|0))$\",'i');\n return regex.test(value);\n },\n\n /**\n * Validate that an attribute is an integer.\n *\n * @return {boolean}\n */\n Integer: function(value) {\n var regex= new RegExp(\"^(?:-?\\\\d+)$\",'i');\n return regex.test(value);\n },\n\n /**\n * Validate that an attribute is numeric.\n */\n Numeric: function(value, element) {\n return $.validator.methods.number.call(this, value, element, true);\n },\n\n /**\n * Validate that an attribute is a string.\n *\n * @return {boolean}\n */\n String: function(value) {\n return typeof value === 'string';\n },\n\n /**\n * The field under validation must be numeric and must have an exact length of value.\n */\n Digits: function(value, element, params) {\n return (\n $.validator.methods.number.call(this, value, element, true) &&\n value.length === parseInt(params, 10)\n );\n },\n\n /**\n * The field under validation must have a length between the given min and max.\n */\n DigitsBetween: function(value, element, params) {\n return ($.validator.methods.number.call(this, value, element, true)\n && value.length>=parseFloat(params[0]) && value.length<=parseFloat(params[1]));\n },\n\n /**\n * Validate the size of an attribute.\n *\n * @return {boolean}\n */\n Size: function(value, element, params) {\n return laravelValidation.helpers.getSize(this, element,value) === parseFloat(params[0]);\n },\n\n /**\n * Validate the size of an attribute is between a set of values.\n *\n * @return {boolean}\n */\n Between: function(value, element, params) {\n return ( laravelValidation.helpers.getSize(this, element,value) >= parseFloat(params[0]) &&\n laravelValidation.helpers.getSize(this,element,value) <= parseFloat(params[1]));\n },\n\n /**\n * Validate the size of an attribute is greater than a minimum value.\n *\n * @return {boolean}\n */\n Min: function(value, element, params) {\n value = laravelValidation.helpers.allElementValues(this, element);\n\n return laravelValidation.helpers.getSize(this, element, value) >= parseFloat(params[0]);\n },\n\n /**\n * Validate the size of an attribute is less than a maximum value.\n *\n * @return {boolean}\n */\n Max: function(value, element, params) {\n value = laravelValidation.helpers.allElementValues(this, element);\n\n return laravelValidation.helpers.getSize(this, element, value) <= parseFloat(params[0]);\n },\n\n /**\n * Validate an attribute is contained within a list of values.\n *\n * @return {boolean}\n */\n In: function(value, element, params) {\n if (laravelValidation.helpers.isArray(value)\n && laravelValidation.helpers.hasRules(element, \"Array\")\n ) {\n var diff = laravelValidation.helpers.arrayDiff(value, params);\n\n return Object.keys(diff).length === 0;\n }\n\n return params.indexOf(value.toString()) !== -1;\n },\n\n /**\n * Validate an attribute is not contained within a list of values.\n *\n * @return {boolean}\n */\n NotIn: function(value, element, params) {\n return params.indexOf(value.toString()) === -1;\n },\n\n /**\n * Validate that an attribute is a valid IP.\n *\n * @return {boolean}\n */\n Ip: function(value) {\n return /^(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$/i.test(value) ||\n /^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\\b((25[0-5])|(1\\d{2})|(2[0-4]\\d)|(\\d{1,2}))\\b)\\.){3}(\\b((25[0-5])|(1\\d{2})|(2[0-4]\\d)|(\\d{1,2}))\\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\\b((25[0-5])|(1\\d{2})|(2[0-4]\\d)|(\\d{1,2}))\\b)\\.){3}(\\b((25[0-5])|(1\\d{2})|(2[0-4]\\d)|(\\d{1,2}))\\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\\b((25[0-5])|(1\\d{2})|(2[0-4]\\d)|(\\d{1,2}))\\b)\\.){3}(\\b((25[0-5])|(1\\d{2})|(2[0-4]\\d)|(\\d{1,2}))\\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$/i.test(value);\n },\n\n /**\n * Validate that an attribute is a valid e-mail address.\n */\n Email: function(value, element) {\n return $.validator.methods.email.call(this, value, element, true);\n },\n\n /**\n * Validate that an attribute is a valid URL.\n */\n Url: function(value, element) {\n return $.validator.methods.url.call(this, value, element, true);\n },\n\n /**\n * The field under validation must be a successfully uploaded file.\n *\n * @return {boolean}\n */\n File: function(value, element) {\n if (!window.File || !window.FileReader || !window.FileList || !window.Blob) {\n return true;\n }\n if ('files' in element ) {\n return (element.files.length > 0);\n }\n return false;\n },\n\n /**\n * Validate the MIME type of a file upload attribute is in a set of MIME types.\n *\n * @return {boolean}\n */\n Mimes: function(value, element, params) {\n if (!window.File || !window.FileReader || !window.FileList || !window.Blob) {\n return true;\n }\n var lowerParams = $.map(params, function(item) {\n return item.toLowerCase();\n });\n\n var fileinfo = laravelValidation.helpers.fileinfo(element);\n return (fileinfo !== false && lowerParams.indexOf(fileinfo.extension.toLowerCase())!==-1);\n },\n\n /**\n * The file under validation must match one of the given MIME types.\n *\n * @return {boolean}\n */\n Mimetypes: function(value, element, params) {\n if (!window.File || !window.FileReader || !window.FileList || !window.Blob) {\n return true;\n }\n var lowerParams = $.map(params, function(item) {\n return item.toLowerCase();\n });\n\n var fileinfo = laravelValidation.helpers.fileinfo(element);\n\n if (fileinfo === false) {\n return false;\n }\n return (lowerParams.indexOf(fileinfo.type.toLowerCase())!==-1);\n },\n\n /**\n * Validate the MIME type of a file upload attribute is in a set of MIME types.\n */\n Image: function(value, element) {\n return laravelValidation.methods.Mimes.call(this, value, element, [\n 'jpg', 'png', 'gif', 'bmp', 'svg', 'jpeg'\n ]);\n },\n\n /**\n * Validate dimensions of Image.\n *\n * @return {boolean|string}\n */\n Dimensions: function(value, element, params, callback) {\n if (!window.File || !window.FileReader || !window.FileList || !window.Blob) {\n return true;\n }\n if (element.files === null || typeof element.files[0] === 'undefined') {\n return false;\n }\n\n var fr = new FileReader;\n fr.onload = function () {\n var img = new Image();\n img.onload = function () {\n var height = parseFloat(img.naturalHeight);\n var width = parseFloat(img.naturalWidth);\n var ratio = width / height;\n var notValid = ((params['width']) && parseFloat(params['width'] !== width)) ||\n ((params['min_width']) && parseFloat(params['min_width']) > width) ||\n ((params['max_width']) && parseFloat(params['max_width']) < width) ||\n ((params['height']) && parseFloat(params['height']) !== height) ||\n ((params['min_height']) && parseFloat(params['min_height']) > height) ||\n ((params['max_height']) && parseFloat(params['max_height']) < height) ||\n ((params['ratio']) && ratio !== parseFloat(eval(params['ratio']))\n );\n callback(! notValid);\n };\n img.onerror = function() {\n callback(false);\n };\n img.src = fr.result;\n };\n fr.readAsDataURL(element.files[0]);\n\n return 'pending';\n },\n\n /**\n * Validate that an attribute contains only alphabetic characters.\n *\n * @return {boolean}\n */\n Alpha: function(value) {\n if (typeof value !== 'string') {\n return false;\n }\n\n var regex = new RegExp(\"^(?:^[a-z\\u00E0-\\u00FC]+$)$\",'i');\n return regex.test(value);\n\n },\n\n /**\n * Validate that an attribute contains only alpha-numeric characters.\n *\n * @return {boolean}\n */\n AlphaNum: function(value) {\n if (typeof value !== 'string') {\n return false;\n }\n var regex = new RegExp(\"^(?:^[a-z0-9\\u00E0-\\u00FC]+$)$\",'i');\n return regex.test(value);\n },\n\n /**\n * Validate that an attribute contains only alphabetic characters.\n *\n * @return {boolean}\n */\n AlphaDash: function(value) {\n if (typeof value !== 'string') {\n return false;\n }\n var regex = new RegExp(\"^(?:^[a-z0-9\\u00E0-\\u00FC_-]+$)$\",'i');\n return regex.test(value);\n },\n\n /**\n * Validate that an attribute passes a regular expression check.\n *\n * @return {boolean}\n */\n Regex: function(value, element, params) {\n var invalidModifiers=['x','s','u','X','U','A'];\n // Converting php regular expression\n var phpReg= new RegExp('^(?:\\/)(.*\\\\\\/?[^\\/]*|[^\\/]*)(?:\\/)([gmixXsuUAJ]*)?$');\n var matches=params[0].match(phpReg);\n if (matches === null) {\n return false;\n }\n // checking modifiers\n var php_modifiers=[];\n if (matches[2]!==undefined) {\n php_modifiers=matches[2].split('');\n for (var i=0; i');\n },\n\n /**\n * Validate the date is equal or after a given date.\n *\n * @return {boolean}\n */\n AfterOrEqual: function(value, element, params) {\n return laravelValidation.helpers.compareDates(this, value, element, params[0], '>=');\n },\n\n\n /**\n * Validate that an attribute is a valid date.\n */\n Timezone: function(value) {\n return laravelValidation.helpers.isTimezone(value);\n },\n\n\n /**\n * Validate the attribute is a valid JSON string.\n *\n * @param value\n * @return bool\n */\n Json: function(value) {\n var result = true;\n try {\n JSON.parse(value);\n } catch (e) {\n result = false;\n }\n return result;\n },\n\n /**\n * Noop (always returns true).\n *\n * @param value\n * @returns {boolean}\n */\n ProengsoftNoop: function (value) {\n return true;\n },\n }\n});\n"]} \ No newline at end of file diff --git a/resources/views/vendor/jsvalidation/bootstrap4.php b/resources/views/vendor/jsvalidation/bootstrap4.php index 1bf3a5de..669f4c65 100644 --- a/resources/views/vendor/jsvalidation/bootstrap4.php +++ b/resources/views/vendor/jsvalidation/bootstrap4.php @@ -1,5 +1,5 @@ -'])); @@ -325,7 +325,7 @@ public function test_detail_data_title_sanitizes_xss(): void $this->assertStringNotContainsString('', $title); } - /** @test */ + #[Test] public function test_detail_data_colomn_empty_without_filter(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.detail_data')); @@ -335,7 +335,7 @@ public function test_detail_data_colomn_empty_without_filter(): void $this->assertEquals('', $colomn); } - /** @test */ + #[Test] public function test_detail_data_colomn_with_valid_filter(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.detail_data', [ @@ -350,7 +350,7 @@ public function test_detail_data_colomn_with_valid_filter(): void $this->assertEquals('jenis_pekerjaan:petani', $colomn); } - /** @test */ + #[Test] public function test_detail_data_colomn_empty_when_tipe_missing(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.detail_data', [ @@ -364,7 +364,7 @@ public function test_detail_data_colomn_empty_when_tipe_missing(): void $this->assertEquals('', $colomn); } - /** @test */ + #[Test] public function test_detail_data_colomn_empty_when_nilai_missing(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.detail_data', [ @@ -378,7 +378,7 @@ public function test_detail_data_colomn_empty_when_nilai_missing(): void $this->assertEquals('', $colomn); } - /** @test */ + #[Test] public function test_detail_data_colomn_empty_when_tipe_empty_string(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.detail_data', [ @@ -393,7 +393,7 @@ public function test_detail_data_colomn_empty_when_tipe_empty_string(): void $this->assertEquals('', $colomn); } - /** @test */ + #[Test] public function test_detail_data_colomn_empty_when_nilai_empty_string(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.detail_data', [ @@ -408,7 +408,7 @@ public function test_detail_data_colomn_empty_when_nilai_empty_string(): void $this->assertEquals('', $colomn); } - /** @test */ + #[Test] public function test_detail_data_page_has_detail_table(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.detail_data')); @@ -418,7 +418,7 @@ public function test_detail_data_page_has_detail_table(): void $this->assertStringContainsString('id="detail-ketenagakerjaan"', $content); } - /** @test */ + #[Test] public function test_detail_data_page_has_filter_tahun_component(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.detail_data')); @@ -428,7 +428,7 @@ public function test_detail_data_page_has_filter_tahun_component(): void $this->assertStringContainsString('filter-tahun', $content); } - /** @test */ + #[Test] public function test_detail_data_page_has_correct_table_headers(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.detail_data')); @@ -453,7 +453,7 @@ public function test_detail_data_page_has_correct_table_headers(): void } } - /** @test */ + #[Test] public function test_detail_data_validation_requires_filter_tipe_when_filter_nilai_present(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.detail_data', [ @@ -468,7 +468,7 @@ public function test_detail_data_validation_requires_filter_tipe_when_filter_nil $this->assertEquals('', $colomn); } - /** @test */ + #[Test] public function test_detail_data_validation_requires_filter_nilai_when_filter_tipe_present(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.detail_data', [ @@ -483,7 +483,7 @@ public function test_detail_data_validation_requires_filter_nilai_when_filter_ti $this->assertEquals('', $colomn); } - /** @test */ + #[Test] public function test_detail_data_title_strips_html_tags(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.detail_data', ['judul' => 'Bold Italic'])); @@ -496,7 +496,7 @@ public function test_detail_data_title_strips_html_tags(): void $this->assertStringContainsString('Italic', $title); } - /** @test */ + #[Test] public function test_detail_data_title_encodes_special_characters(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.detail_data', ['judul' => 'Test "Quotes" & '])); diff --git a/tests/Feature/DataPresisiLaporanTest.php b/tests/Feature/DataPresisiLaporanTest.php index 33d4fe4e..f4b9f1c8 100644 --- a/tests/Feature/DataPresisiLaporanTest.php +++ b/tests/Feature/DataPresisiLaporanTest.php @@ -6,7 +6,7 @@ class DataPresisiLaporanTest extends BaseTestCase { - /** @test */ + #[Test] public function test_can_access_laporan_semua_desa_page() { $response = $this->get(route('laporan.data-presisi.index')); @@ -16,7 +16,7 @@ public function test_can_access_laporan_semua_desa_page() $response->assertViewHas('title', 'Data Presisi Pengisian Laporan Semua Desa'); } - /** @test */ + #[Test] public function test_laporan_semua_desa_has_required_elements() { $response = $this->get(route('laporan.data-presisi.index')); @@ -35,7 +35,7 @@ public function test_laporan_semua_desa_has_required_elements() $this->assertStringContainsString('Data Lengkap', $content); } - /** @test */ + #[Test] public function test_laporan_semua_desa_has_correct_table_columns() { $response = $this->get(route('laporan.data-presisi.index')); @@ -64,7 +64,7 @@ public function test_laporan_semua_desa_has_correct_table_columns() } } - /** @test */ + #[Test] public function test_laporan_semua_desa_has_filter_status_javascript() { $response = $this->get(route('laporan.data-presisi.index')); @@ -76,7 +76,7 @@ public function test_laporan_semua_desa_has_filter_status_javascript() $this->assertStringContainsString("$('#laporanTable').DataTable().ajax.reload()", $content); } - /** @test */ + #[Test] public function test_laporan_semua_desa_has_datatable_configuration() { $response = $this->get(route('laporan.data-presisi.index')); @@ -92,7 +92,7 @@ public function test_laporan_semua_desa_has_datatable_configuration() $this->assertStringContainsString("$('#filter-status').val()", $content); } - /** @test */ + #[Test] public function test_can_access_laporan_perdesa_page() { $response = $this->get(route('laporan.data-presisi.perdesa')); @@ -102,7 +102,7 @@ public function test_can_access_laporan_perdesa_page() $response->assertViewHas('title', 'Data Presisi Pengisian Laporan Per Desa'); } - /** @test */ + #[Test] public function test_laporan_perdesa_has_correct_table_columns() { $response = $this->get(route('laporan.data-presisi.perdesa')); @@ -123,7 +123,7 @@ public function test_laporan_perdesa_has_correct_table_columns() } } - /** @test */ + #[Test] public function test_laporan_perdesa_has_datatable_configuration() { $response = $this->get(route('laporan.data-presisi.perdesa')); @@ -141,7 +141,7 @@ public function test_laporan_perdesa_has_datatable_configuration() $this->assertStringContainsString('"config_desa"', $content); } - /** @test */ + #[Test] public function test_laporan_perdesa_number_formatting_for_columns() { $response = $this->get(route('laporan.data-presisi.perdesa')); @@ -152,7 +152,7 @@ public function test_laporan_perdesa_number_formatting_for_columns() $this->assertStringContainsString("render: $.fn.dataTable.render.number('.', ',', 0, '')", $content); } - /** @test */ + #[Test] public function test_both_pages_use_correct_api_endpoints() { // Test laporan index @@ -166,7 +166,7 @@ public function test_both_pages_use_correct_api_endpoints() $this->assertStringContainsString('/api/v1/data-presisi/laporan-perdesa', $content2); } - /** @test */ + #[Test] public function test_both_pages_have_breadcrumbs() { // Test laporan index has breadcrumb section @@ -180,7 +180,7 @@ public function test_both_pages_have_breadcrumbs() $this->assertTrue(true); } - /** @test */ + #[Test] public function test_both_pages_extend_correct_layout() { // Test laporan index uses correct layout (has main-footer) @@ -194,7 +194,7 @@ public function test_both_pages_extend_correct_layout() $this->assertStringContainsString('class="main-footer"', $content2); } - /** @test */ + #[Test] public function test_laporan_semua_desa_has_export_buttons() { $response = $this->get(route('laporan.data-presisi.index')); @@ -205,7 +205,7 @@ public function test_laporan_semua_desa_has_export_buttons() $this->assertStringContainsString('id="export-excel"', $content); } - /** @test */ + #[Test] public function test_laporan_perdesa_has_export_buttons() { $response = $this->get(route('laporan.data-presisi.perdesa')); diff --git a/tests/Feature/DataPresisiPanganControllerTest.php b/tests/Feature/DataPresisiPanganControllerTest.php index 580092d4..c126e23a 100644 --- a/tests/Feature/DataPresisiPanganControllerTest.php +++ b/tests/Feature/DataPresisiPanganControllerTest.php @@ -6,7 +6,7 @@ class DataPresisiPanganControllerTest extends BaseTestCase { - /** @test */ + #[Test] public function test_can_access_pangan_index_page() { $response = $this->get(route('data-pokok.data-presisi-pangan.index')); @@ -16,7 +16,7 @@ public function test_can_access_pangan_index_page() $response->assertViewHas('title', 'Data Presisi Pangan'); } - /** @test */ + #[Test] public function test_detail_data_with_valid_filter_parameters() { $response = $this->get(route('data-pokok.data-presisi-pangan.detail_data') . '?judul=Test%20Judul&filter[tipe]=kategori_pangan&filter[nilai]=beras'); @@ -27,7 +27,7 @@ public function test_detail_data_with_valid_filter_parameters() $response->assertViewHas('colomn', 'kategori_pangan:beras'); } - /** @test */ + #[Test] public function test_detail_data_with_missing_filter_tipe() { $response = $this->get(route('data-pokok.data-presisi-pangan.detail_data') . '?judul=Test%20Judul&filter[nilai]=beras'); @@ -36,7 +36,7 @@ public function test_detail_data_with_missing_filter_tipe() $response->assertViewHas('colomn', ''); } - /** @test */ + #[Test] public function test_detail_data_with_missing_filter_nilai() { $response = $this->get(route('data-pokok.data-presisi-pangan.detail_data') . '?judul=Test%20Judul&filter[tipe]=kategori_pangan'); @@ -45,7 +45,7 @@ public function test_detail_data_with_missing_filter_nilai() $response->assertViewHas('colomn', ''); } - /** @test */ + #[Test] public function test_detail_data_with_empty_filter_tipe() { $response = $this->get(route('data-pokok.data-presisi-pangan.detail_data') . '?judul=Test%20Judul&filter[tipe]=&filter[nilai]=beras'); @@ -54,7 +54,7 @@ public function test_detail_data_with_empty_filter_tipe() $response->assertViewHas('colomn', ''); } - /** @test */ + #[Test] public function test_detail_data_with_empty_filter_nilai() { $response = $this->get(route('data-pokok.data-presisi-pangan.detail_data') . '?judul=Test%20Judul&filter[tipe]=kategori_pangan&filter[nilai]='); @@ -63,7 +63,7 @@ public function test_detail_data_with_empty_filter_nilai() $response->assertViewHas('colomn', ''); } - /** @test */ + #[Test] public function test_detail_data_without_filter_parameter() { $response = $this->get(route('data-pokok.data-presisi-pangan.detail_data') . '?judul=Test%20Judul'); @@ -72,7 +72,7 @@ public function test_detail_data_without_filter_parameter() $response->assertViewHas('colomn', ''); } - /** @test */ + #[Test] public function test_detail_data_without_judul_parameter() { $response = $this->get(route('data-pokok.data-presisi-pangan.detail_data') . '?filter[tipe]=kategori_pangan&filter[nilai]=beras'); @@ -82,7 +82,7 @@ public function test_detail_data_without_judul_parameter() $response->assertViewHas('colomn', 'kategori_pangan:beras'); } - /** @test */ + #[Test] public function test_cetak_with_filter_parameters() { $response = $this->get(route('data-pokok.data-presisi-pangan.cetak') . '?filter[tipe]=kategori_pangan&filter[nilai]=beras&tahun=2024'); @@ -92,7 +92,7 @@ public function test_cetak_with_filter_parameters() $response->assertViewHas('filter'); } - /** @test */ + #[Test] public function test_cetak_without_parameters() { $response = $this->get(route('data-pokok.data-presisi-pangan.cetak')); @@ -101,7 +101,7 @@ public function test_cetak_without_parameters() $response->assertViewIs('data_pokok.data_presisi.pangan.cetak'); } - /** @test */ + #[Test] public function test_detail_data_column_format_with_special_characters() { $response = $this->get(route('data-pokok.data-presisi-pangan.detail_data') . '?judul=Test%20Judul&filter[tipe]=kategori_pangan&filter[nilai]=beras-organik'); @@ -110,7 +110,7 @@ public function test_detail_data_column_format_with_special_characters() $response->assertViewHas('colomn', 'kategori_pangan:beras-organik'); } - /** @test */ + #[Test] public function test_detail_data_column_format_with_spaces() { $response = $this->get(route('data-pokok.data-presisi-pangan.detail_data') . '?judul=Test%20Judul&filter[tipe]=kategori%20pangan&filter[nilai]=beras%20merah'); diff --git a/tests/Feature/DataPresisiSeniBudayaExcelDownloadTest.php b/tests/Feature/DataPresisiSeniBudayaExcelDownloadTest.php index 818b8117..02297d39 100644 --- a/tests/Feature/DataPresisiSeniBudayaExcelDownloadTest.php +++ b/tests/Feature/DataPresisiSeniBudayaExcelDownloadTest.php @@ -6,7 +6,7 @@ class DataPresisiSeniBudayaExcelDownloadTest extends BaseTestCase { - /** @test */ + #[Test] public function test_excel_download_button_exists_in_seni_budaya_page() { $response = $this->get('/data-presisi/seni-budaya'); @@ -24,7 +24,7 @@ public function test_excel_download_button_exists_in_seni_budaya_page() $this->assertStringContainsString('/api/v1/data-presisi/seni-budaya/rtm/download', $content); } - /** @test */ + #[Test] public function test_seni_budaya_page_has_filter_tahun() { $response = $this->get('/data-presisi/seni-budaya'); @@ -40,7 +40,7 @@ public function test_seni_budaya_page_has_filter_tahun() $this->assertStringContainsString('filter-tahun', $content); } - /** @test */ + #[Test] public function test_seni_budaya_page_has_print_button() { $response = $this->get('/data-presisi/seni-budaya'); diff --git a/tests/Feature/DetailDataPresisiAktivitasKeagamaanRequestTest.php b/tests/Feature/DetailDataPresisiAktivitasKeagamaanRequestTest.php index 83b0f84e..c903dc95 100644 --- a/tests/Feature/DetailDataPresisiAktivitasKeagamaanRequestTest.php +++ b/tests/Feature/DetailDataPresisiAktivitasKeagamaanRequestTest.php @@ -6,7 +6,7 @@ class DetailDataPresisiAktivitasKeagamaanRequestTest extends BaseTestCase { - /** @test */ + #[Test] public function it_validates_judul_as_nullable_string_max_255() { // Valid judul @@ -20,7 +20,7 @@ public function it_validates_judul_as_nullable_string_max_255() $response->assertSessionHasErrors('judul'); } - /** @test */ + #[Test] public function it_validates_filter_as_nullable_array() { // Valid filter @@ -33,7 +33,7 @@ public function it_validates_filter_as_nullable_array() $response->assertSessionHasErrors('filter'); } - /** @test */ + #[Test] public function it_validates_filter_tipe_required_with_filter_and_in_allowed_values() { // Valid tipe @@ -54,7 +54,7 @@ public function it_validates_filter_tipe_required_with_filter_and_in_allowed_val $response->assertSessionHasErrors('filter.tipe'); } - /** @test */ + #[Test] public function it_validates_filter_nilai_required_with_filter_as_string() { // Valid nilai diff --git a/tests/Feature/DownloadControllerCmsTest.php b/tests/Feature/DownloadControllerCmsTest.php index 0e766547..cab64373 100644 --- a/tests/Feature/DownloadControllerCmsTest.php +++ b/tests/Feature/DownloadControllerCmsTest.php @@ -12,7 +12,7 @@ class DownloadControllerCmsTest extends BaseTestCase { use DatabaseTransactions; - /** @test */ + #[Test] public function halaman_index_download_dapat_diakses() { $response = $this->get(route('downloads.index')); @@ -21,7 +21,7 @@ public function halaman_index_download_dapat_diakses() $response->assertViewIs('downloads.index'); } - /** @test */ + #[Test] public function form_tambah_download_dapat_diakses() { $response = $this->get(route('downloads.create')); @@ -30,7 +30,7 @@ public function form_tambah_download_dapat_diakses() $response->assertViewIs('downloads.create'); } - /** @test */ + #[Test] public function file_download_baru_dapat_disimpan() { Storage::fake('public'); @@ -76,7 +76,7 @@ public function file_download_baru_dapat_disimpan() $this->assertDatabaseHas('downloads', ['title' => 'File PDF']); } - /** @test */ + #[Test] public function form_edit_download_dapat_diakses() { $download = Download::factory()->create(); @@ -87,7 +87,7 @@ public function form_edit_download_dapat_diakses() $response->assertViewIs('downloads.edit'); } - /** @test */ + #[Test] public function file_download_dapat_diperbarui() { $download = Download::factory()->create([ @@ -106,7 +106,7 @@ public function file_download_dapat_diperbarui() $this->assertDatabaseHas('downloads', ['title' => 'Baru']); } - /** @test */ + #[Test] public function file_download_dapat_dihapus() { $download = Download::factory()->create(); diff --git a/tests/Feature/GlobalRateLimiterTest.php b/tests/Feature/GlobalRateLimiterTest.php index 84b5e947..0491dbfd 100644 --- a/tests/Feature/GlobalRateLimiterTest.php +++ b/tests/Feature/GlobalRateLimiterTest.php @@ -19,7 +19,7 @@ protected function setUp(): void Cache::flush(); } - /** @test */ + #[Test] public function it_allows_requests_when_rate_limiter_is_disabled() { // Disable rate limiter @@ -33,7 +33,7 @@ public function it_allows_requests_when_rate_limiter_is_disabled() } } - /** @test */ + #[Test] public function it_allows_requests_within_limit_when_rate_limiter_is_enabled() { // Enable rate limiter with low limits for testing @@ -52,7 +52,7 @@ public function it_allows_requests_within_limit_when_rate_limiter_is_enabled() } } - /** @test */ + #[Test] public function it_blocks_requests_when_limit_is_exceeded() { // Enable rate limiter with low limits for testing @@ -72,7 +72,7 @@ public function it_blocks_requests_when_limit_is_exceeded() $this->assertEquals(429, $response3->getStatusCode()); } - /** @test */ + #[Test] public function it_returns_correct_json_response_for_api_requests() { // Enable rate limiter with low limits for testing @@ -95,7 +95,7 @@ public function it_returns_correct_json_response_for_api_requests() $response->assertJsonStructure(['retry_after']); } - /** @test */ + #[Test] public function it_excludes_configured_paths_from_rate_limiting() { // Enable rate limiter with low limits @@ -114,7 +114,7 @@ public function it_excludes_configured_paths_from_rate_limiting() } } - /** @test */ + #[Test] public function it_excludes_configured_ip_addresses_from_rate_limiting() { // Enable rate limiter with low limits @@ -133,7 +133,7 @@ public function it_excludes_configured_ip_addresses_from_rate_limiting() } } - /** @test */ + #[Test] public function it_respects_wildcard_patterns_in_excluded_paths() { // Enable rate limiter with low limits @@ -155,7 +155,7 @@ public function it_respects_wildcard_patterns_in_excluded_paths() $this->assertNotEquals(429, $response3->getStatusCode()); } - /** @test */ + #[Test] public function it_respects_different_rate_limits_for_different_ips() { // Enable rate limiter with low limits @@ -176,7 +176,7 @@ public function it_respects_different_rate_limits_for_different_ips() $this->assertNotEquals(429, $response->getStatusCode()); } - /** @test */ + #[Test] public function it_works_with_simple_paths() { // Enable rate limiter with low limits diff --git a/tests/Feature/Http/Controllers/DataPresisiAktivitasKeagamaanControllerTest.php b/tests/Feature/Http/Controllers/DataPresisiAktivitasKeagamaanControllerTest.php index d797a1b7..f52a8bff 100644 --- a/tests/Feature/Http/Controllers/DataPresisiAktivitasKeagamaanControllerTest.php +++ b/tests/Feature/Http/Controllers/DataPresisiAktivitasKeagamaanControllerTest.php @@ -6,7 +6,7 @@ class DataPresisiAktivitasKeagamaanControllerTest extends BaseTestCase { - /** @test */ + #[Test] public function it_can_access_detail_data_page() { $response = $this->get(route('data-pokok.data-presisi-aktivitas-keagamaan.detail_data')); @@ -17,7 +17,7 @@ public function it_can_access_detail_data_page() ->assertViewHas('colomn'); } - /** @test */ + #[Test] public function it_handles_judul_parameter() { $judul = 'Test Title '; @@ -28,7 +28,7 @@ public function it_handles_judul_parameter() ->assertViewHas('colomn', ''); } - /** @test */ + #[Test] public function it_handles_filter_parameter() { $filter = [ @@ -41,7 +41,7 @@ public function it_handles_filter_parameter() ->assertViewHas('colomn', 'agama_id:1'); } - /** @test */ + #[Test] public function it_handles_filter_with_different_tipe() { $filter = [ @@ -54,7 +54,7 @@ public function it_handles_filter_with_different_tipe() ->assertViewHas('colomn', 'frekwensi_mengikuti_kegiatan_setahun:2'); } - /** @test */ + #[Test] public function it_handles_empty_filter() { $response = $this->get(route('data-pokok.data-presisi-aktivitas-keagamaan.detail_data', ['filter' => []])); diff --git a/tests/Feature/JaminanSosialTest.php b/tests/Feature/JaminanSosialTest.php index d3ee6de0..18c7875f 100644 --- a/tests/Feature/JaminanSosialTest.php +++ b/tests/Feature/JaminanSosialTest.php @@ -6,7 +6,7 @@ class JaminanSosialTest extends BaseTestCase { - /** @test */ + #[Test] public function test_can_access_jaminan_sosial_page() { $response = $this->get(route('jaminan-sosial')); @@ -16,7 +16,7 @@ public function test_can_access_jaminan_sosial_page() $response->assertSee('Data Kepesertaan Program dan Statistik'); } - /** @test */ + #[Test] public function test_jaminan_sosial_page_has_required_elements() { $response = $this->get(route('jaminan-sosial')); @@ -42,7 +42,7 @@ public function test_jaminan_sosial_page_has_required_elements() $this->assertStringContainsString('id="download-excel"', $content, 'Excel download button ID tidak ditemukan'); } - /** @test */ + #[Test] public function test_jaminan_sosial_has_correct_table_columns() { $response = $this->get(route('jaminan-sosial')); @@ -64,7 +64,7 @@ public function test_jaminan_sosial_has_correct_table_columns() } } - /** @test */ + #[Test] public function test_jaminan_sosial_has_print_button() { $response = $this->get(route('jaminan-sosial')); @@ -75,7 +75,7 @@ public function test_jaminan_sosial_has_print_button() $this->assertStringContainsString('data-print-url=', $content, 'Route print tidak ditemukan'); } - /** @test */ + #[Test] public function test_jaminan_sosial_has_excel_download_button() { $response = $this->get(route('jaminan-sosial')); @@ -86,7 +86,7 @@ public function test_jaminan_sosial_has_excel_download_button() $this->assertStringContainsString('data-download-url=', $content, 'Download URL tidak ditemukan'); } - /** @test */ + #[Test] public function test_jaminan_sosial_has_datatable_configuration() { $response = $this->get(route('jaminan-sosial')); @@ -101,7 +101,7 @@ public function test_jaminan_sosial_has_datatable_configuration() $this->assertStringContainsString('/api/v1/data-presisi/jaminan-sosial', $content, 'API endpoint tidak ditemukan'); } - /** @test */ + #[Test] public function test_jaminan_sosial_has_filter_tahun_functionality() { $response = $this->get(route('jaminan-sosial')); @@ -113,7 +113,7 @@ public function test_jaminan_sosial_has_filter_tahun_functionality() $this->assertStringContainsString('grafikPie()', $content, 'Grafik reload pada filter tahun tidak ditemukan'); } - /** @test */ + #[Test] public function test_jaminan_sosial_has_detail_control_functionality() { $response = $this->get(route('jaminan-sosial')); @@ -125,7 +125,7 @@ public function test_jaminan_sosial_has_detail_control_functionality() $this->assertStringContainsString('row.child.isShown()', $content, 'Logika expand/collapse detail control tidak ditemukan'); } - /** @test */ + #[Test] public function test_jaminan_sosial_detail_button_has_correct_route() { $response = $this->get(route('jaminan-sosial')); @@ -136,7 +136,7 @@ public function test_jaminan_sosial_detail_button_has_correct_route() $this->assertStringContainsString('?data=__DATA__', $content, 'Parameter data pada route detail tidak ditemukan'); } - /** @test */ + #[Test] public function test_jaminan_sosial_uses_correct_api_filters() { $response = $this->get(route('jaminan-sosial')); diff --git a/tests/Feature/LaporanBulananDetailExportTest.php b/tests/Feature/LaporanBulananDetailExportTest.php index d8eafa6c..d2d2b8f1 100644 --- a/tests/Feature/LaporanBulananDetailExportTest.php +++ b/tests/Feature/LaporanBulananDetailExportTest.php @@ -6,7 +6,7 @@ class LaporanBulananDetailExportTest extends BaseTestCase { - /** @test */ + #[Test] public function test_detail_page_has_export_excel_button() { // Set up session data yang diperlukan @@ -34,7 +34,7 @@ public function test_detail_page_has_export_excel_button() $this->assertStringNotContainsString('>Cetak<', $content, 'Label Cetak seharusnya sudah dihapus'); } - /** @test */ + #[Test] public function test_detail_page_export_excel_button_has_correct_route() { session([ diff --git a/tests/Feature/LoginControllerTest.php b/tests/Feature/LoginControllerTest.php index 1d2bf41a..d7eb5316 100644 --- a/tests/Feature/LoginControllerTest.php +++ b/tests/Feature/LoginControllerTest.php @@ -59,7 +59,7 @@ public function setUp(): void RateLimiter::clear("captcha:{$ip}:" . hash('xxh64', $userAgent)); } - /** @test */ + #[Test] public function it_shows_login_form() { $response = $this->get('/login'); @@ -70,7 +70,7 @@ public function it_shows_login_form() $response->assertViewHas('captchaView'); } - /** @test */ + #[Test] public function it_shows_login_form_with_captcha_when_threshold_reached() { // Simulate failed attempts to trigger captcha @@ -88,7 +88,7 @@ public function it_shows_login_form_with_captcha_when_threshold_reached() $response->assertViewHas('captchaView', 'auth.captcha'); } - /** @test */ + #[Test] public function it_can_login_with_email() { // Clear any existing rate limiter to ensure clean state @@ -108,7 +108,7 @@ public function it_can_login_with_email() $this->assertAuthenticatedAs($this->user); } - /** @test */ + #[Test] public function it_can_login_with_username() { // Clear any existing rate limiter to ensure clean state @@ -128,7 +128,7 @@ public function it_can_login_with_username() $this->assertAuthenticatedAs($this->user); } - /** @test */ + #[Test] public function it_redirects_to_2fa_challenge_when_2fa_enabled() { $this->twoFactorService @@ -144,7 +144,7 @@ public function it_redirects_to_2fa_challenge_when_2fa_enabled() $this->assertNull(session('2fa_verified')); } - /** @test */ + #[Test] public function it_redirects_to_password_change_when_password_is_weak() { // Delete existing weak user first @@ -170,7 +170,7 @@ public function it_redirects_to_password_change_when_password_is_weak() $this->assertTrue(session('weak_password')); } - /** @test */ + #[Test] public function it_fails_login_with_invalid_credentials() { $response = $this->post('/login', [ @@ -182,7 +182,7 @@ public function it_fails_login_with_invalid_credentials() $this->assertGuest(); } - /** @test */ + #[Test] public function it_handles_locked_account() { $this->user->lockAccount(); @@ -197,7 +197,7 @@ public function it_handles_locked_account() $this->assertGuest(); } - /** @test */ + #[Test] public function it_records_failed_login_attempts() { $response = $this->post('/login', [ @@ -212,7 +212,7 @@ public function it_records_failed_login_attempts() $this->assertTrue($this->user->failed_login_attempts > 0); } - /** @test */ + #[Test] public function it_locks_account_after_max_failed_attempts() { // Test ini memerlukan setup rate limiter yang kompleks @@ -248,7 +248,7 @@ public function it_locks_account_after_max_failed_attempts() $this->assertTrue($this->user->isLocked()); } - /** @test */ + #[Test] public function it_resets_failed_attempts_on_successful_login() { // Clear rate limiter @@ -277,7 +277,7 @@ public function it_resets_failed_attempts_on_successful_login() $this->assertFalse($this->user->isLocked()); } - /** @test */ + #[Test] public function it_finds_username_correctly_for_email() { $controller = new \App\Http\Controllers\Auth\LoginController( @@ -295,7 +295,7 @@ public function it_finds_username_correctly_for_email() $this->assertEquals('test@example.com', $request->input('email')); } - /** @test */ + #[Test] public function it_finds_username_correctly_for_username() { $controller = new \App\Http\Controllers\Auth\LoginController( @@ -313,7 +313,7 @@ public function it_finds_username_correctly_for_username() $this->assertEquals('testuser', $request->input('username')); } - /** @test */ + #[Test] public function it_handles_nonexistent_user_in_failed_login() { $response = $this->post('/login', [ @@ -325,7 +325,7 @@ public function it_handles_nonexistent_user_in_failed_login() $this->assertGuest(); } - /** @test */ + #[Test] public function it_handles_inactive_user() { // Clear rate limiter @@ -352,7 +352,7 @@ public function it_handles_inactive_user() $this->user->update(['active' => 1]); } - /** @test */ + #[Test] public function it_handles_remember_me_functionality() { // Clear rate limiter diff --git a/tests/Feature/MenuControllerCmsTest.php b/tests/Feature/MenuControllerCmsTest.php index 0c9b0c2f..108c86bc 100644 --- a/tests/Feature/MenuControllerCmsTest.php +++ b/tests/Feature/MenuControllerCmsTest.php @@ -11,9 +11,7 @@ class MenuControllerCmsTest extends BaseTestCase { use DatabaseTransactions; - /** @test - * Menguji halaman index menu dapat ditampilkan tanpa error - */ + #[Test] public function test_index_menampilkan_halaman_daftar_menu() { // Arrange: siapkan page dan category @@ -30,9 +28,7 @@ public function test_index_menampilkan_halaman_daftar_menu() $response->assertViewHas('sourceItem'); } - /** @test - * Menguji penyimpanan data menu baru berhasil dan redirect sesuai jenis menu - */ + #[Test] public function test_store_menyimpan_menu_baru_dan_redirect_sesuai_jenis() { // Arrange @@ -57,9 +53,7 @@ public function test_store_menyimpan_menu_baru_dan_redirect_sesuai_jenis() $this->assertEquals('Menu berhasil disimpan.', session('success')); } - /** @test - * Menguji redirect store jika tipe menu 2 - */ + #[Test] public function test_store_redirect_ke_index_dengan_type_dua() { $payload = [ @@ -80,9 +74,7 @@ public function test_store_redirect_ke_index_dengan_type_dua() $this->assertDatabaseHas('menus', ['name' => 'Menu Statistik']); } - /** @test - * Menguji jika parameter menu_type tidak dikirim maka default ke 1 - */ + #[Test] public function test_store_menu_type_default_ke_satu_jika_null() { $payload = [ diff --git a/tests/Feature/ModuleControllerOrgTest.php b/tests/Feature/ModuleControllerOrgTest.php index bc6d6dad..24be0cd7 100644 --- a/tests/Feature/ModuleControllerOrgTest.php +++ b/tests/Feature/ModuleControllerOrgTest.php @@ -6,7 +6,7 @@ class ModuleControllerOrgTest extends WebsiteTestCase { - /** @test */ + #[Test] public function it_can_access_org_module_page() { // Act: Akses halaman bagan organisasi @@ -19,7 +19,7 @@ public function it_can_access_org_module_page() $response->assertViewHas('content'); } - /** @test */ + #[Test] public function it_shows_org_chart_elements_in_view() { // Act: Akses halaman @@ -31,7 +31,7 @@ public function it_shows_org_chart_elements_in_view() $response->assertSee('chart-container'); } - /** @test */ + #[Test] public function it_loads_required_assets_for_org_chart() { // Act: Akses halaman @@ -44,7 +44,7 @@ public function it_loads_required_assets_for_org_chart() $response->assertSee('vendor/orgchart/html2canvas.min.js'); } - /** @test */ + #[Test] public function it_returns_org_module_with_correct_data_structure() { // Act: Akses halaman @@ -63,7 +63,7 @@ public function it_returns_org_module_with_correct_data_structure() $this->assertTrue(is_iterable($viewData['content'])); } - /** @test */ + #[Test] public function it_includes_org_partial_view() { // Act: Akses halaman diff --git a/tests/Feature/OtpControllerTest.php b/tests/Feature/OtpControllerTest.php index 821ed4e8..c99f73f1 100644 --- a/tests/Feature/OtpControllerTest.php +++ b/tests/Feature/OtpControllerTest.php @@ -44,7 +44,7 @@ public function setUp(): void $this->app->instance(TwoFactorService::class, $this->twoFactorService); } - /** @test */ + #[Test] public function it_shows_otp_settings_page() { $this->twoFactorService @@ -65,7 +65,7 @@ public function it_shows_otp_settings_page() $response->assertViewHas('twoFactorStatus'); } - /** @test */ + #[Test] public function it_shows_otp_activation_page() { $response = $this->get(route('otp.activate')); @@ -75,7 +75,7 @@ public function it_shows_otp_activation_page() $response->assertViewHas('user', $this->user); } - /** @test */ + #[Test] public function it_can_setup_otp_with_email() { $this->otpService @@ -104,7 +104,7 @@ public function it_can_setup_otp_with_email() $this->assertEquals($this->user->email, session('temp_otp_config.identifier')); } - /** @test */ + #[Test] public function it_can_setup_otp_with_telegram() { $this->otpService @@ -133,7 +133,7 @@ public function it_can_setup_otp_with_telegram() $this->assertEquals($this->user->telegram_chat_id, session('temp_otp_config.identifier')); } - /** @test */ + #[Test] public function it_handles_otp_setup_failure() { $this->otpService @@ -156,7 +156,7 @@ public function it_handles_otp_setup_failure() ]); } - /** @test */ + #[Test] public function it_enforces_rate_limiting_on_otp_setup() { // Hit rate limit with new key format (includes IP and User-Agent) @@ -176,7 +176,7 @@ public function it_enforces_rate_limiting_on_otp_setup() ]); } - /** @test */ + #[Test] public function it_can_verify_otp_activation() { session(['temp_otp_config' => [ @@ -211,7 +211,7 @@ public function it_can_verify_otp_activation() $this->assertArrayNotHasKey('temp_otp_config', session()->all()); } - /** @test */ + #[Test] public function it_rejects_otp_verification_without_temp_config() { $response = $this->postJson(route('otp.verify-activation'), [ @@ -225,7 +225,7 @@ public function it_rejects_otp_verification_without_temp_config() ]); } - /** @test */ + #[Test] public function it_handles_otp_verification_failure() { session(['temp_otp_config' => [ @@ -253,7 +253,7 @@ public function it_handles_otp_verification_failure() ]); } - /** @test */ + #[Test] public function it_enforces_rate_limiting_on_otp_verification() { session(['temp_otp_config' => [ @@ -278,7 +278,7 @@ public function it_enforces_rate_limiting_on_otp_verification() ]); } - /** @test */ + #[Test] public function it_can_disable_otp() { $this->user->update([ @@ -301,7 +301,7 @@ public function it_can_disable_otp() $this->assertNull($this->user->otp_identifier); } - /** @test */ + #[Test] public function it_can_resend_otp() { session(['temp_otp_config' => [ @@ -329,7 +329,7 @@ public function it_can_resend_otp() ]); } - /** @test */ + #[Test] public function it_rejects_resend_without_temp_config() { $response = $this->postJson(route('otp.resend')); @@ -341,7 +341,7 @@ public function it_rejects_resend_without_temp_config() ]); } - /** @test */ + #[Test] public function it_enforces_rate_limiting_on_otp_resend() { session(['temp_otp_config' => [ @@ -364,7 +364,7 @@ public function it_enforces_rate_limiting_on_otp_resend() ]); } - /** @test */ + #[Test] public function it_can_disable_2fa_from_otp_controller() { $this->twoFactorService diff --git a/tests/Feature/OtpLoginControllerTest.php b/tests/Feature/OtpLoginControllerTest.php index 97b40166..3c525e49 100644 --- a/tests/Feature/OtpLoginControllerTest.php +++ b/tests/Feature/OtpLoginControllerTest.php @@ -55,7 +55,7 @@ public function setUp(): void RateLimiter::clear('otp:resend:' . $this->user->id . ':127.0.0.1:' . hash('xxh64', $userAgent)); } - /** @test */ + #[Test] public function it_shows_otp_login_form() { $response = $this->get('/login/otp'); @@ -66,7 +66,7 @@ public function it_shows_otp_login_form() $response->assertViewHas('captchaView'); } - /** @test */ + #[Test] public function it_shows_otp_login_form_with_captcha_when_threshold_reached() { // Simulate failed attempts to trigger captcha @@ -84,7 +84,7 @@ public function it_shows_otp_login_form_with_captcha_when_threshold_reached() $response->assertViewHas('captchaView', 'auth.captcha'); } - /** @test */ + #[Test] public function it_can_send_otp_with_email_identifier() { $this->otpService @@ -112,7 +112,7 @@ public function it_can_send_otp_with_email_identifier() $this->assertEquals('email', session('otp_login_channel')); } - /** @test */ + #[Test] public function it_can_send_otp_with_username_identifier() { // Clear rate limiter untuk test ini @@ -144,7 +144,7 @@ public function it_can_send_otp_with_username_identifier() $this->assertEquals('email', session('otp_login_channel')); } - /** @test */ + #[Test] public function it_fails_to_send_otp_for_nonexistent_user() { $response = $this->postJson('/login/otp/send', [ @@ -158,7 +158,7 @@ public function it_fails_to_send_otp_for_nonexistent_user() ]); } - /** @test */ + #[Test] public function it_fails_to_send_otp_for_user_without_otp_enabled() { $userWithoutOtp = User::factory()->create([ @@ -180,7 +180,7 @@ public function it_fails_to_send_otp_for_user_without_otp_enabled() ]); } - /** @test */ + #[Test] public function it_shows_captcha_after_two_failed_username_attempts() { // First failed attempt @@ -212,7 +212,7 @@ public function it_shows_captcha_after_two_failed_username_attempts() $response->assertViewHas('shouldShowCaptcha', true); } - /** @test */ + #[Test] public function it_handles_locked_account_when_sending_otp() { $this->user->lockAccount(); @@ -226,7 +226,7 @@ public function it_handles_locked_account_when_sending_otp() $this->assertStringContainsString('AKUN TERKUNCI', $response->json('message')); } - /** @test */ + #[Test] public function it_enforces_rate_limiting_when_sending_otp() { $key = 'otp:login:test@example.com:127.0.0.1:' . hash('xxh64', $this->app['request']->userAgent() ?? 'unknown'); @@ -246,7 +246,7 @@ public function it_enforces_rate_limiting_when_sending_otp() $this->assertStringContainsString('Terlalu banyak percobaan', $response->json('message')); } - /** @test */ + #[Test] public function it_can_verify_otp_and_login() { // Test ini memerlukan setup khusus karena controller memanggil method protected @@ -285,7 +285,7 @@ public function it_can_verify_otp_and_login() $this->assertNull(session('otp_login_channel')); } - /** @test */ + #[Test] public function it_redirects_to_2fa_after_otp_verification_when_2fa_enabled() { // Test ini memerlukan setup khusus karena controller memanggil method protected @@ -323,7 +323,7 @@ public function it_redirects_to_2fa_after_otp_verification_when_2fa_enabled() $this->assertNull(session('2fa_verified')); } - /** @test */ + #[Test] public function it_fails_to_verify_otp_without_session() { $response = $this->postJson('/login/otp/verify', [ @@ -339,7 +339,7 @@ public function it_fails_to_verify_otp_without_session() $this->assertGuest(); } - /** @test */ + #[Test] public function it_fails_to_verify_otp_with_invalid_code() { session([ @@ -369,7 +369,7 @@ public function it_fails_to_verify_otp_with_invalid_code() $this->assertGuest(); } - /** @test */ + #[Test] public function it_handles_locked_account_when_verifying_otp() { session([ @@ -390,7 +390,7 @@ public function it_handles_locked_account_when_verifying_otp() $this->assertGuest(); } - /** @test */ + #[Test] public function it_enforces_rate_limiting_when_verifying_otp() { session([ @@ -417,7 +417,7 @@ public function it_enforces_rate_limiting_when_verifying_otp() $this->assertGuest(); } - /** @test */ + #[Test] public function it_can_resend_otp() { session([ @@ -445,7 +445,7 @@ public function it_can_resend_otp() ]); } - /** @test */ + #[Test] public function it_fails_to_resend_otp_without_session() { $response = $this->postJson('/login/otp/resend'); @@ -457,7 +457,7 @@ public function it_fails_to_resend_otp_without_session() ]); } - /** @test */ + #[Test] public function it_handles_locked_account_when_resending_otp() { session([ @@ -474,7 +474,7 @@ public function it_handles_locked_account_when_resending_otp() $this->assertStringContainsString('AKUN TERKUNCI', $response->json('message')); } - /** @test */ + #[Test] public function it_enforces_rate_limiting_when_resending_otp() { session([ @@ -498,7 +498,7 @@ public function it_enforces_rate_limiting_when_resending_otp() $this->assertStringContainsString('detik sebelum mengirim ulang', $response->json('message')); } - /** @test */ + #[Test] public function it_resets_failed_attempts_on_successful_otp_verification() { // Test ini memerlukan setup khusus karena bergantung pada state user @@ -543,7 +543,7 @@ public function it_resets_failed_attempts_on_successful_otp_verification() $this->assertFalse($this->user->isLocked()); } - /** @test */ + #[Test] public function it_handles_telegram_otp_channel() { $telegramUser = User::factory()->create([ @@ -582,7 +582,7 @@ public function it_handles_telegram_otp_channel() $this->assertEquals('telegram', session('otp_login_channel')); } - /** @test */ + #[Test] public function it_handles_multiple_otp_channels() { $multiChannelUser = User::factory()->create([ diff --git a/tests/Feature/PageControllerCmsTest.php b/tests/Feature/PageControllerCmsTest.php index 9f6a4fc7..1abaa0a2 100644 --- a/tests/Feature/PageControllerCmsTest.php +++ b/tests/Feature/PageControllerCmsTest.php @@ -12,7 +12,7 @@ class PageControllerCmsTest extends BaseTestCase { use DatabaseTransactions; - /** @test */ + #[Test] public function halaman_index_dapat_diakses() { $response = $this->get(route('pages.index')); @@ -21,7 +21,7 @@ public function halaman_index_dapat_diakses() $response->assertViewIs('pages.index'); } - /** @test */ + #[Test] public function halaman_form_tambah_dapat_dibuka() { $response = $this->get(route('pages.create')); @@ -30,7 +30,7 @@ public function halaman_form_tambah_dapat_dibuka() $response->assertViewIs('pages.create'); } - /** @test */ + #[Test] public function halaman_baru_dapat_disimpan() { Storage::fake('public'); @@ -50,7 +50,7 @@ public function halaman_baru_dapat_disimpan() $this->assertDatabaseHas('pages', ['title' => 'Judul Halaman']); } - /** @test */ + #[Test] public function halaman_dapat_diedit() { $page = Page::factory()->create(); @@ -62,7 +62,7 @@ public function halaman_dapat_diedit() $response->assertViewHas('page'); } - /** @test */ + #[Test] public function halaman_dapat_diperbarui() { $page = Page::factory()->create([ @@ -83,7 +83,7 @@ public function halaman_dapat_diperbarui() $this->assertDatabaseHas('pages', ['title' => 'Baru']); } - /** @test */ + #[Test] public function halaman_dapat_dihapus() { $page = Page::factory()->create(); diff --git a/tests/Feature/PageControllerWebsiteTest.php b/tests/Feature/PageControllerWebsiteTest.php index 43cc295c..eeed1f95 100644 --- a/tests/Feature/PageControllerWebsiteTest.php +++ b/tests/Feature/PageControllerWebsiteTest.php @@ -11,7 +11,7 @@ class PageControllerWebsiteTest extends WebsiteTestCase { - /** @test */ + #[Test] public function it_can_access_index_page() { $response = $this->get(route('web.index')); @@ -19,7 +19,7 @@ public function it_can_access_index_page() $response->assertViewIs('web.index'); } - /** @test */ + #[Test] public function it_can_access_category_page() { $category = Category::factory()->create(); @@ -29,7 +29,7 @@ public function it_can_access_category_page() $response->assertViewHas('title', $category->name); } - /** @test */ + #[Test] public function it_can_access_page_detail() { $page = Page::factory()->create(); @@ -39,7 +39,7 @@ public function it_can_access_page_detail() $response->assertViewHas('object', $page); } - /** @test */ + #[Test] public function it_can_access_article_detail() { $article = Article::factory()->create(); @@ -49,7 +49,7 @@ public function it_can_access_article_detail() $response->assertViewHas('object', $article); } - /** @test */ + #[Test] public function it_can_access_sitemap() { $mock = Mockery::mock(SitemapService::class); diff --git a/tests/Feature/SlideControllerCmsTest.php b/tests/Feature/SlideControllerCmsTest.php index f109af25..2363a37e 100644 --- a/tests/Feature/SlideControllerCmsTest.php +++ b/tests/Feature/SlideControllerCmsTest.php @@ -12,7 +12,7 @@ class SlideControllerCmsTest extends BaseTestCase { use DatabaseTransactions; - /** @test */ + #[Test] public function halaman_index_slide_dapat_diakses() { $response = $this->get(route('slides.index')); @@ -20,7 +20,7 @@ public function halaman_index_slide_dapat_diakses() $response->assertViewIs('slides.index'); } - /** @test */ + #[Test] public function form_tambah_slide_dapat_diakses() { $response = $this->get(route('slides.create')); @@ -28,7 +28,7 @@ public function form_tambah_slide_dapat_diakses() $response->assertViewIs('slides.create'); } - /** @test */ + #[Test] public function slide_baru_dapat_disimpan() { Storage::fake('public'); @@ -46,7 +46,7 @@ public function slide_baru_dapat_disimpan() $this->assertDatabaseHas('slides', ['title' => 'Slide Baru']); } - /** @test */ + #[Test] public function form_edit_slide_dapat_diakses() { $slide = Slide::factory()->create(); @@ -58,7 +58,7 @@ public function form_edit_slide_dapat_diakses() $response->assertViewHas('slide'); } - /** @test */ + #[Test] public function slide_dapat_diperbarui() { $slide = Slide::factory()->create([ @@ -77,7 +77,7 @@ public function slide_dapat_diperbarui() $this->assertDatabaseHas('slides', ['title' => 'Slide Diperbarui']); } - /** @test */ + #[Test] public function slide_dapat_dihapus() { $slide = Slide::factory()->create(); diff --git a/tests/Feature/StatistikPengunjungCmsControllerTest.php b/tests/Feature/StatistikPengunjungCmsControllerTest.php index c0c3260f..ce040c57 100644 --- a/tests/Feature/StatistikPengunjungCmsControllerTest.php +++ b/tests/Feature/StatistikPengunjungCmsControllerTest.php @@ -10,7 +10,7 @@ class StatistikPengunjungCmsControllerTest extends BaseTestCase { use DatabaseTransactions; - /** @test */ + #[Test] public function halaman_statistik_pengunjung_dapat_diakses() { // Arrange: siapkan data kunjungan palsu diff --git a/tests/Feature/TwoFactorControllerTest.php b/tests/Feature/TwoFactorControllerTest.php index 1ea8bbc9..3cd6ec8e 100644 --- a/tests/Feature/TwoFactorControllerTest.php +++ b/tests/Feature/TwoFactorControllerTest.php @@ -43,7 +43,7 @@ public function setUp(): void $this->app->instance(OtpService::class, $this->otpService); } - /** @test */ + #[Test] public function it_shows_2fa_activation_page() { $response = $this->get(route('2fa.activate')); @@ -53,7 +53,7 @@ public function it_shows_2fa_activation_page() $response->assertViewHas('user', $this->user); } - /** @test */ + #[Test] public function it_can_enable_2fa_with_email() { $this->otpService @@ -81,7 +81,7 @@ public function it_can_enable_2fa_with_email() $this->assertEquals($this->user->email, session('temp_2fa_config.identifier')); } - /** @test */ + #[Test] public function it_can_enable_2fa_with_telegram() { $this->otpService @@ -109,7 +109,7 @@ public function it_can_enable_2fa_with_telegram() $this->assertEquals($this->user->telegram_chat_id, session('temp_2fa_config.identifier')); } - /** @test */ + #[Test] public function it_handles_2fa_enable_failure() { $this->otpService @@ -132,7 +132,7 @@ public function it_handles_2fa_enable_failure() ]); } - /** @test */ + #[Test] public function it_enforces_rate_limiting_on_2fa_enable() { // Hit rate limit with new key format (includes IP and User-Agent) @@ -155,7 +155,7 @@ public function it_enforces_rate_limiting_on_2fa_enable() ]); } - /** @test */ + #[Test] public function it_can_verify_and_enable_2fa() { session(['temp_2fa_config' => [ @@ -194,7 +194,7 @@ public function it_can_verify_and_enable_2fa() $this->assertArrayNotHasKey('temp_2fa_config', session()->all()); } - /** @test */ + #[Test] public function it_rejects_2fa_verification_without_temp_config() { $response = $this->postJson(route('2fa.verify'), [ @@ -208,7 +208,7 @@ public function it_rejects_2fa_verification_without_temp_config() ]); } - /** @test */ + #[Test] public function it_handles_2fa_verification_failure() { session(['temp_2fa_config' => [ @@ -237,7 +237,7 @@ public function it_handles_2fa_verification_failure() $response->assertJsonPath('progressive_delay', 2); } - /** @test */ + #[Test] public function it_enforces_rate_limiting_on_2fa_verification() { session(['temp_2fa_config' => [ @@ -265,7 +265,7 @@ public function it_enforces_rate_limiting_on_2fa_verification() ]); } - /** @test */ + #[Test] public function it_can_disable_2fa() { $this->twoFactorService @@ -283,7 +283,7 @@ public function it_can_disable_2fa() ]); } - /** @test */ + #[Test] public function it_can_resend_2fa_verification_code() { session(['temp_2fa_config' => [ @@ -311,7 +311,7 @@ public function it_can_resend_2fa_verification_code() ]); } - /** @test */ + #[Test] public function it_rejects_resend_without_temp_config() { $response = $this->postJson(route('2fa.resend')); @@ -323,7 +323,7 @@ public function it_rejects_resend_without_temp_config() ]); } - /** @test */ + #[Test] public function it_enforces_rate_limiting_on_2fa_resend() { session(['temp_2fa_config' => [ @@ -346,7 +346,7 @@ public function it_enforces_rate_limiting_on_2fa_resend() ]); } - /** @test */ + #[Test] public function it_shows_2fa_challenge_page() { $this->user->update([ @@ -382,7 +382,7 @@ public function it_shows_2fa_challenge_page() $response->assertViewIs('auth.2fa-challenge'); } - /** @test */ + #[Test] public function it_redirects_to_dashboard_if_2fa_not_enabled() { $this->user->update(['2fa_enabled' => false]); @@ -393,7 +393,7 @@ public function it_redirects_to_dashboard_if_2fa_not_enabled() $response->assertRedirect(route('dasbor')); } - /** @test */ + #[Test] public function it_can_verify_2fa_challenge() { $this->user->update([ @@ -427,7 +427,7 @@ public function it_can_verify_2fa_challenge() $this->assertTrue(session('2fa_verified')); } - /** @test */ + #[Test] public function it_handles_2fa_challenge_verification_failure() { $this->user->update([ @@ -457,7 +457,7 @@ public function it_handles_2fa_challenge_verification_failure() $response->assertJsonPath('progressive_delay', 2); } - /** @test */ + #[Test] public function it_enforces_rate_limiting_on_2fa_challenge() { $this->user->update([ diff --git a/tests/Feature/WilayahFilterParameterTest.php b/tests/Feature/WilayahFilterParameterTest.php index 4464f8d4..9decab18 100644 --- a/tests/Feature/WilayahFilterParameterTest.php +++ b/tests/Feature/WilayahFilterParameterTest.php @@ -6,7 +6,7 @@ class WilayahFilterParameterTest extends BaseTestCase { - /** @test */ + #[Test] public function test_meta_tag_identitas_openkab_exists() { $response = $this->get(route('dasbor')); @@ -15,7 +15,7 @@ public function test_meta_tag_identitas_openkab_exists() $response->assertSee('name="identitas-openkab"', false); } - /** @test */ + #[Test] public function test_wilayah_filter_js_contains_kode_kabupaten_param() { $response = $this->get(route('dasbor')); @@ -24,7 +24,7 @@ public function test_wilayah_filter_js_contains_kode_kabupaten_param() $response->assertSee('kode_kabupaten', false); } - /** @test */ + #[Test] public function test_wilayah_filter_js_contains_filter_kode_kabupaten_param() { $response = $this->get(route('dasbor')); @@ -33,7 +33,7 @@ public function test_wilayah_filter_js_contains_filter_kode_kabupaten_param() $response->assertSee('filter[kode_kabupaten]', false); } - /** @test */ + #[Test] public function test_summary_api_url_contains_kode_kabupaten_param() { $response = $this->get(route('dasbor') . '?page=summary'); @@ -41,7 +41,7 @@ public function test_summary_api_url_contains_kode_kabupaten_param() $response->assertStatus(200); } - /** @test */ + #[Test] public function test_peta_api_url_contains_kode_kabupaten_param() { $response = $this->get(route('dasbor') . '?page=peta'); diff --git a/tests/Unit/ArtikelServiceTest.php b/tests/Unit/ArtikelServiceTest.php index 532a6214..f1d70b14 100644 --- a/tests/Unit/ArtikelServiceTest.php +++ b/tests/Unit/ArtikelServiceTest.php @@ -18,13 +18,13 @@ public function setUp(): void $this->service = new ArtikelService(); } - /** @test */ + #[Test] public function it_can_instantiate_artikel_service() { $this->assertInstanceOf(ArtikelService::class, $this->service); } - /** @test */ + #[Test] public function it_builds_cache_key_correctly() { $reflection = new \ReflectionClass($this->service); @@ -37,7 +37,7 @@ public function it_builds_cache_key_correctly() $this->assertStringContainsString('artikel', $cacheKey); } - /** @test */ + #[Test] public function clear_cache_removes_cached_data() { $cacheKey = 'test_artikel_cache'; @@ -50,7 +50,7 @@ public function clear_cache_removes_cached_data() $this->assertFalse(Cache::has($cacheKey)); } - /** @test */ + #[Test] public function it_has_cache_ttl_property() { $reflection = new \ReflectionClass($this->service); @@ -63,7 +63,7 @@ public function it_has_cache_ttl_property() $this->assertIsInt($ttl); } - /** @test */ + #[Test] public function artikel_method_returns_collection() { // Mock API response untuk testing @@ -72,19 +72,19 @@ public function artikel_method_returns_collection() $this->assertTrue(method_exists($this->service, 'artikel')); } - /** @test */ + #[Test] public function artikel_by_id_method_exists() { $this->assertTrue(method_exists($this->service, 'artikelById')); } - /** @test */ + #[Test] public function clear_cache_method_exists() { $this->assertTrue(method_exists($this->service, 'clearCache')); } - /** @test */ + #[Test] public function service_extends_base_api_service() { $this->assertInstanceOf(\App\Services\BaseApiService::class, $this->service); diff --git a/tests/Unit/DataPresisiPanganDetailViewTest.php b/tests/Unit/DataPresisiPanganDetailViewTest.php index a0f2e7d7..5b53ef9b 100644 --- a/tests/Unit/DataPresisiPanganDetailViewTest.php +++ b/tests/Unit/DataPresisiPanganDetailViewTest.php @@ -11,7 +11,7 @@ class DataPresisiPanganDetailViewTest extends BaseTestCase { use DatabaseTransactions; - /** @test */ + #[Test] public function it_renders_longitude_latitude_columns_in_datatable_header() { // Mock data untuk testing view @@ -33,7 +33,7 @@ public function it_renders_longitude_latitude_columns_in_datatable_header() $this->assertStringContainsString('LATITUDE', $html); } - /** @test */ + #[Test] public function it_configures_datatable_columns_for_longitude_latitude() { // Mock data untuk testing view @@ -58,7 +58,7 @@ public function it_configures_datatable_columns_for_longitude_latitude() $this->assertStringContainsString("orderable: false", $html); } - /** @test */ + #[Test] public function it_includes_longitude_latitude_in_correct_column_order() { // Mock data untuk testing view @@ -85,7 +85,7 @@ public function it_includes_longitude_latitude_in_correct_column_order() $this->assertLessThan($latitudePos, $longitudePos, 'Longitude should appear before latitude in column configuration'); } - /** @test */ + #[Test] public function it_configures_api_filter_with_rtm_id_for_coordinate_data() { // Mock data untuk testing view @@ -109,7 +109,7 @@ public function it_configures_api_filter_with_rtm_id_for_coordinate_data() $this->assertStringContainsString('/api/v1/data-presisi/pangan', $html); } - /** @test */ + #[Test] public function it_renders_household_info_table_with_correct_data_structure() { // Mock data untuk testing view @@ -138,7 +138,7 @@ public function it_renders_household_info_table_with_correct_data_structure() $this->assertStringContainsString('No Kartu Rumah Tangga (KRT)', $html); } - /** @test */ + #[Test] public function it_has_proper_datatable_initialization_for_coordinate_display() { // Mock data untuk testing view @@ -166,7 +166,7 @@ public function it_has_proper_datatable_initialization_for_coordinate_display() $this->assertStringContainsString("data: 'attributes.latitude'", $html); } - /** @test */ + #[Test] public function it_includes_all_expected_column_headers_in_correct_order() { // Mock data untuk testing view @@ -222,7 +222,7 @@ public function it_includes_all_expected_column_headers_in_correct_order() $this->assertLessThan($latitudeHeaderPos, $longitudeHeaderPos); } - /** @test */ + #[Test] public function it_validates_coordinate_columns_are_not_orderable() { // Mock data untuk testing view From 285df57ff459ec946ddd55b23588974b46a35f1f Mon Sep 17 00:00:00 2001 From: Ahmad Afandi Date: Fri, 5 Jun 2026 07:01:41 +0700 Subject: [PATCH 09/43] perbaikan test dan struktur baru laravel 13 --- app/Console/Kernel.php | 34 ---- app/Exceptions/Handler.php | 50 ------ app/Http/Kernel.php | 83 --------- app/Http/Middleware/VerifyCsrfToken.php | 4 +- app/Providers/AppServiceProvider.php | 11 ++ app/Providers/RouteServiceProvider.php | 72 -------- bootstrap/app.php | 162 ++++++++++++------ composer.lock | 22 +-- routes/console.php | 3 + tests/Feature/AdminWebControllerTest.php | 6 +- tests/Feature/ArtikelControllerTest.php | 26 +-- tests/Feature/ArtikelUploadTest.php | 20 +-- tests/Feature/ArtikelWebTest.php | 6 +- tests/Feature/BantuanControllerTest.php | 10 +- tests/Feature/DasborControllerTest.php | 6 +- .../Feature/DasborDemografiControllerTest.php | 12 +- .../Feature/DataPresisiExcelDownloadTest.php | 8 +- .../DataPresisiFilterTahunJavaScriptTest.php | 8 +- ...taPresisiKetenagakerjaanControllerTest.php | 70 ++++---- tests/Feature/DataPresisiLaporanTest.php | 28 +-- .../DataPresisiPanganControllerTest.php | 24 +-- ...DataPresisiSeniBudayaExcelDownloadTest.php | 6 +- ...taPresisiAktivitasKeagamaanRequestTest.php | 8 +- tests/Feature/DownloadControllerCmsTest.php | 12 +- tests/Feature/GlobalRateLimiterTest.php | 18 +- ...resisiAktivitasKeagamaanControllerTest.php | 10 +- tests/Feature/JaminanSosialTest.php | 20 +-- .../LaporanBulananDetailExportTest.php | 4 +- tests/Feature/LoginControllerTest.php | 32 ++-- tests/Feature/MenuControllerCmsTest.php | 16 +- tests/Feature/ModuleControllerOrgTest.php | 10 +- tests/Feature/OtpControllerTest.php | 30 ++-- tests/Feature/OtpLoginControllerTest.php | 44 ++--- tests/Feature/PageControllerCmsTest.php | 12 +- tests/Feature/PageControllerWebsiteTest.php | 10 +- tests/Feature/SlideControllerCmsTest.php | 12 +- .../StatistikPengunjungCmsControllerTest.php | 2 +- tests/Feature/TwoFactorControllerTest.php | 36 ++-- tests/Feature/WilayahFilterParameterTest.php | 10 +- tests/Unit/ArtikelServiceTest.php | 16 +- .../Unit/DataPresisiPanganDetailViewTest.php | 16 +- 41 files changed, 406 insertions(+), 583 deletions(-) delete mode 100644 app/Console/Kernel.php delete mode 100644 app/Exceptions/Handler.php delete mode 100644 app/Http/Kernel.php diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php deleted file mode 100644 index 3544a72c..00000000 --- a/app/Console/Kernel.php +++ /dev/null @@ -1,34 +0,0 @@ -command('inspire')->hourly(); - - // Cleanup expired OTP tokens every 30 minutes - $schedule->command('otp:cleanup')->everyThirtyMinutes(); - } - - /** - * Register the commands for the application. - * - * @return void - */ - protected function commands() - { - $this->load(__DIR__.'/Commands'); - - require base_path('routes/console.php'); - } -} diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php deleted file mode 100644 index 8a54bada..00000000 --- a/app/Exceptions/Handler.php +++ /dev/null @@ -1,50 +0,0 @@ -, \Psr\Log\LogLevel::*> - */ - protected $levels = [ - // - ]; - - /** - * A list of the exception types that are not reported. - * - * @var array> - */ - protected $dontReport = [ - // - ]; - - /** - * A list of the inputs that are never flashed to the session on validation exceptions. - * - * @var array - */ - protected $dontFlash = [ - 'current_password', - 'password', - 'password_confirmation', - ]; - - /** - * Register the exception handling callbacks for the application. - * - * @return void - */ - public function register() - { - $this->reportable(function (Throwable $e) { - // - }); - } -} diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php deleted file mode 100644 index 04e81f94..00000000 --- a/app/Http/Kernel.php +++ /dev/null @@ -1,83 +0,0 @@ - - */ - protected $middleware = [ - // \App\Http\Middleware\TrustHosts::class, - Middleware\TrustProxies::class, - \Illuminate\Http\Middleware\HandleCors::class, - Middleware\PreventRequestsDuringMaintenance::class, - \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class, - Middleware\TrimStrings::class, - \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class, - Middleware\GlobalRateLimiter::class, - ]; - - /** - * The application's route middleware groups. - * - * @var array> - */ - protected $middlewareGroups = [ - 'web' => [ - Middleware\EncryptCookies::class, - \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, - \Illuminate\Session\Middleware\StartSession::class, - \Illuminate\View\Middleware\ShareErrorsFromSession::class, - Middleware\VerifyCsrfToken::class, - \Illuminate\Routing\Middleware\SubstituteBindings::class, - Middleware\CspExclusion::class, - \Spatie\Csp\AddCspHeaders::class, - ], - - 'api' => [ - \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, - 'throttle:api', - \Illuminate\Routing\Middleware\SubstituteBindings::class, - ], - ]; - - /** - * The application's route middleware. - * - * These middleware may be assigned to groups or used individually. - * - * @var array - */ - protected $routeMiddleware = [ - 'auth' => Middleware\Authenticate::class, - 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, - 'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class, - 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class, - 'can' => \Illuminate\Auth\Middleware\Authorize::class, - 'guest' => Middleware\RedirectIfAuthenticated::class, - 'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class, - 'signed' => Middleware\ValidateSignature::class, - 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, - 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, - 'abilities' => \Laravel\Sanctum\Http\Middleware\CheckAbilities::class, - 'ability' => \Laravel\Sanctum\Http\Middleware\CheckForAnyAbility::class, - 'role' => \Spatie\Permission\Middleware\RoleMiddleware::class, - 'permission' => \Spatie\Permission\Middleware\PermissionMiddleware::class, - 'role_or_permission' => \Spatie\Permission\Middleware\RoleOrPermissionMiddleware::class, - 'teams_permission' => Middleware\TeamsPermission::class, - 'password.weak' => Middleware\WeakPassword::class, - 'password.expiry' => Middleware\CheckPasswordExpiry::class, - 'website.enable' => Middleware\WebsiteEnable::class, - 'log.visitor' => \Shetabit\Visitor\Middlewares\LogVisits::class, - 'easyauthorize' => Middleware\EasyAuthorize::class, - 'check.presisi' => Middleware\CheckPresisiStatus::class, - '2fa' => Middleware\TwoFactorMiddleware::class, - ]; -} diff --git a/app/Http/Middleware/VerifyCsrfToken.php b/app/Http/Middleware/VerifyCsrfToken.php index 9e865217..56fbe992 100644 --- a/app/Http/Middleware/VerifyCsrfToken.php +++ b/app/Http/Middleware/VerifyCsrfToken.php @@ -2,9 +2,9 @@ namespace App\Http\Middleware; -use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware; +use Illuminate\Foundation\Http\Middleware\PreventRequestForgery; -class VerifyCsrfToken extends Middleware +class VerifyCsrfToken extends PreventRequestForgery { /** * The URIs that should be excluded from CSRF verification. diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 9d30e713..defc5a78 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -6,9 +6,12 @@ use App\Http\Transformers\SettingTransformer; use App\Models\Identitas; use App\Models\Setting; +use Illuminate\Cache\RateLimiting\Limit; +use Illuminate\Http\Request; use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\File; +use Illuminate\Support\Facades\RateLimiter; use Illuminate\Support\Facades\URL; use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\View; @@ -33,6 +36,7 @@ public function register() */ public function boot() { + $this->configureRateLimiting(); $this->bootHttps(); $this->addValidation(); $this->addLogQuery(); @@ -64,6 +68,13 @@ public function boot() } } + protected function configureRateLimiting() + { + RateLimiter::for('api', function (Request $request) { + return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip()); + }); + } + public function bootHttps() { if (config('app.env') === 'production') { diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index 2007b37c..b3bb8b85 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -2,81 +2,9 @@ namespace App\Providers; -use App\Models\CMS\Article; -use App\Models\CMS\Category; -use App\Models\CMS\Page; -use Illuminate\Cache\RateLimiting\Limit; use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider; -use Illuminate\Http\Request; -use Illuminate\Support\Facades\RateLimiter; -use Illuminate\Support\Facades\Route; class RouteServiceProvider extends ServiceProvider { - /** - * The path to the "home" route for your application. - * - * Typically, users are redirected here after authentication. - * - * @var string - */ public const HOME = '/dasbor'; - - /** - * Define your route model bindings, pattern filters, and other route configuration. - * - * @return void - */ - public function boot() - { - $this->configureRateLimiting(); - - $this->routes(function () { - Route::middleware('api') - ->prefix('api') - ->as('api.') - ->group(base_path('routes/api.php')); - - Route::middleware('api') - ->prefix('api/v1') - ->group(base_path('routes/apiv1.php')); - - Route::middleware('web') - ->group(base_path('routes/web.php')); - }); - - $this->bootRouteParameterBinders(); - } - - /** - * Configure the rate limiters for the application. - * - * @return void - */ - protected function configureRateLimiting() - { - RateLimiter::for('api', function (Request $request) { - return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip()); - }); - } - - /** - * Do not remove GENERATOR_PARAMETER_BINDER if you want to use CMS generator properly - * Check the file app/Console/Commands/Cms/Resource.php. - * - * @return void - */ - private function bootRouteParameterBinders() - { - Route::bind('aSlug', function ($slug) { - return Article::with('category')->where('slug', $slug)->firstOrFail(); - }); - Route::bind('cSlug', function ($slug) { - return Category::with('articles')->where('slug', $slug)->firstOrFail(); - }); - Route::bind('pSlug', function ($slug) { - return Page::where('slug', $slug)->firstOrFail(); - }); - /* GENERATOR_PARAMETER_BINDER **/ - } } diff --git a/bootstrap/app.php b/bootstrap/app.php index 037e17df..33a42e6d 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -1,55 +1,111 @@ singleton( - Illuminate\Contracts\Http\Kernel::class, - App\Http\Kernel::class -); - -$app->singleton( - Illuminate\Contracts\Console\Kernel::class, - App\Console\Kernel::class -); - -$app->singleton( - Illuminate\Contracts\Debug\ExceptionHandler::class, - App\Exceptions\Handler::class -); - -/* -|-------------------------------------------------------------------------- -| Return The Application -|-------------------------------------------------------------------------- -| -| This script returns the application instance. The instance is given to -| the calling script so we can separate the building of the instances -| from the actual running of the application and sending responses. -| -*/ - -return $app; +use App\Models\CMS\Article; +use App\Models\CMS\Category; +use App\Models\CMS\Page; +use Illuminate\Cache\RateLimiting\Limit; +use Illuminate\Foundation\Application; +use Illuminate\Foundation\Configuration\Exceptions; +use Illuminate\Foundation\Configuration\Middleware; +use Illuminate\Http\Middleware\ValidatePathEncoding; +use Illuminate\Foundation\Http\Middleware\InvokeDeferredCallbacks; +use Illuminate\Http\Request; +use Illuminate\Support\Facades\RateLimiter; +use Illuminate\Support\Facades\Route; + +return Application::configure( + basePath: dirname(__DIR__), +) + ->withRouting( + then: function () { + Route::middleware('api') + ->prefix('api') + ->as('api.') + ->group(base_path('routes/api.php')); + + Route::middleware('api') + ->prefix('api/v1') + ->group(base_path('routes/apiv1.php')); + + Route::bind('aSlug', function ($slug) { + return Article::with('category')->where('slug', $slug)->firstOrFail(); + }); + + Route::bind('cSlug', function ($slug) { + return Category::with('articles')->where('slug', $slug)->firstOrFail(); + }); + + Route::bind('pSlug', function ($slug) { + return Page::where('slug', $slug)->firstOrFail(); + }); + }, + web: __DIR__.'/../routes/web.php', + commands: __DIR__.'/../routes/console.php', + ) + ->withMiddleware(function (Middleware $middleware) { + $middleware->use([ + ValidatePathEncoding::class, + InvokeDeferredCallbacks::class, + App\Http\Middleware\TrustProxies::class, + \Illuminate\Http\Middleware\HandleCors::class, + App\Http\Middleware\PreventRequestsDuringMaintenance::class, + \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class, + App\Http\Middleware\TrimStrings::class, + \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class, + App\Http\Middleware\GlobalRateLimiter::class, + ]); + + $middleware->group('web', [ + App\Http\Middleware\EncryptCookies::class, + \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, + \Illuminate\Session\Middleware\StartSession::class, + \Illuminate\View\Middleware\ShareErrorsFromSession::class, + App\Http\Middleware\VerifyCsrfToken::class, + \Illuminate\Routing\Middleware\SubstituteBindings::class, + App\Http\Middleware\CspExclusion::class, + \Spatie\Csp\AddCspHeaders::class, + ]); + + $middleware->group('api', [ + \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, + 'throttle:api', + \Illuminate\Routing\Middleware\SubstituteBindings::class, + ]); + + $middleware->alias([ + 'auth' => App\Http\Middleware\Authenticate::class, + 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, + 'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class, + 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class, + 'can' => \Illuminate\Auth\Middleware\Authorize::class, + 'guest' => App\Http\Middleware\RedirectIfAuthenticated::class, + 'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class, + 'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class, + 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, + 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, + 'abilities' => \Laravel\Sanctum\Http\Middleware\CheckAbilities::class, + 'ability' => \Laravel\Sanctum\Http\Middleware\CheckForAnyAbility::class, + 'role' => \Spatie\Permission\Middleware\RoleMiddleware::class, + 'permission' => \Spatie\Permission\Middleware\PermissionMiddleware::class, + 'role_or_permission' => \Spatie\Permission\Middleware\RoleOrPermissionMiddleware::class, + 'teams_permission' => App\Http\Middleware\TeamsPermission::class, + 'password.weak' => App\Http\Middleware\WeakPassword::class, + 'password.expiry' => App\Http\Middleware\CheckPasswordExpiry::class, + 'website.enable' => App\Http\Middleware\WebsiteEnable::class, + 'log.visitor' => \Shetabit\Visitor\Middlewares\LogVisits::class, + 'easyauthorize' => App\Http\Middleware\EasyAuthorize::class, + 'check.presisi' => App\Http\Middleware\CheckPresisiStatus::class, + '2fa' => App\Http\Middleware\TwoFactorMiddleware::class, + ]); + + $middleware->statefulApi(); + $middleware->throttleApi('api'); + }) + ->withExceptions(function (Exceptions $exceptions) { + $exceptions->dontFlash([ + 'current_password', + 'password', + 'password_confirmation', + ]); + }) + ->create(); diff --git a/composer.lock b/composer.lock index e6d7c02f..089089c0 100644 --- a/composer.lock +++ b/composer.lock @@ -2506,16 +2506,16 @@ }, { "name": "laravel/framework", - "version": "v13.13.0", + "version": "v13.14.0", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "1daa6d3b4defe46976ccfa4fb0a7ab62717712a2" + "reference": "e60b1c817a9ef7da319e4007de6cfda5301a58c0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/1daa6d3b4defe46976ccfa4fb0a7ab62717712a2", - "reference": "1daa6d3b4defe46976ccfa4fb0a7ab62717712a2", + "url": "https://api.github.com/repos/laravel/framework/zipball/e60b1c817a9ef7da319e4007de6cfda5301a58c0", + "reference": "e60b1c817a9ef7da319e4007de6cfda5301a58c0", "shasum": "" }, "require": { @@ -2726,7 +2726,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2026-06-02T14:28:17+00:00" + "time": "2026-06-04T18:46:35+00:00" }, { "name": "laravel/prompts", @@ -9998,16 +9998,16 @@ "packages-dev": [ { "name": "barryvdh/laravel-debugbar", - "version": "v4.2.8", + "version": "v4.3.0", "source": { "type": "git", "url": "https://github.com/fruitcake/laravel-debugbar.git", - "reference": "799d70c1101d3f8840dd76ff68ff6a78f9352905" + "reference": "3d76ea8d78b82225b92789de65fc630c1cd8e80c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fruitcake/laravel-debugbar/zipball/799d70c1101d3f8840dd76ff68ff6a78f9352905", - "reference": "799d70c1101d3f8840dd76ff68ff6a78f9352905", + "url": "https://api.github.com/repos/fruitcake/laravel-debugbar/zipball/3d76ea8d78b82225b92789de65fc630c1cd8e80c", + "reference": "3d76ea8d78b82225b92789de65fc630c1cd8e80c", "shasum": "" }, "require": { @@ -10081,7 +10081,7 @@ ], "support": { "issues": "https://github.com/fruitcake/laravel-debugbar/issues", - "source": "https://github.com/fruitcake/laravel-debugbar/tree/v4.2.8" + "source": "https://github.com/fruitcake/laravel-debugbar/tree/v4.3.0" }, "funding": [ { @@ -10093,7 +10093,7 @@ "type": "github" } ], - "time": "2026-04-20T13:31:29+00:00" + "time": "2026-06-04T07:54:01+00:00" }, { "name": "fakerphp/faker", diff --git a/routes/console.php b/routes/console.php index e05f4c9a..e1e5d5e9 100644 --- a/routes/console.php +++ b/routes/console.php @@ -2,6 +2,7 @@ use Illuminate\Foundation\Inspiring; use Illuminate\Support\Facades\Artisan; +use Illuminate\Support\Facades\Schedule; /* |-------------------------------------------------------------------------- @@ -17,3 +18,5 @@ Artisan::command('inspire', function () { $this->comment(Inspiring::quote()); })->purpose('Display an inspiring quote'); + +Schedule::command('otp:cleanup')->everyThirtyMinutes(); diff --git a/tests/Feature/AdminWebControllerTest.php b/tests/Feature/AdminWebControllerTest.php index 3a0b1efc..ab2754a2 100644 --- a/tests/Feature/AdminWebControllerTest.php +++ b/tests/Feature/AdminWebControllerTest.php @@ -6,7 +6,7 @@ class AdminWebControllerTest extends BaseTestCase { - /** @test */ + #[Test] public function it_can_access_kategori_index() { $response = $this->get(route('master-data-artikel.kategori', ['parrent' => 0])); @@ -14,7 +14,7 @@ public function it_can_access_kategori_index() $response->assertViewIs('master.kategori.index'); } - /** @test */ + #[Test] public function it_can_access_kategori_create() { $response = $this->get(route('master-data-artikel.kategori-create', ['parrent' => 1])); @@ -22,7 +22,7 @@ public function it_can_access_kategori_create() $response->assertViewIs('master.kategori.create'); } - /** @test */ + #[Test] public function it_can_access_kategori_edit() { $response = $this->get(route('master-data-artikel.kategori-edit', ['parrent' => 0, 'id' => 1])); diff --git a/tests/Feature/ArtikelControllerTest.php b/tests/Feature/ArtikelControllerTest.php index bfcc5802..5e281960 100644 --- a/tests/Feature/ArtikelControllerTest.php +++ b/tests/Feature/ArtikelControllerTest.php @@ -6,7 +6,7 @@ class ArtikelControllerTest extends BaseTestCase { - /** @test */ + #[Test] public function it_can_access_artikel_index() { $response = $this->get(route('master-data-artikel.index')); @@ -16,7 +16,7 @@ public function it_can_access_artikel_index() $response->assertSee('Data Artikel'); } - /** @test */ + #[Test] public function it_can_access_artikel_create() { $response = $this->get(route('master-data-artikel.create')); @@ -29,7 +29,7 @@ public function it_can_access_artikel_create() $response->assertSee('Kategori'); } - /** @test */ + #[Test] public function artikel_create_has_empty_kategori_popup_guard() { $response = $this->get(route('master-data-artikel.create')); @@ -40,7 +40,7 @@ public function artikel_create_has_empty_kategori_popup_guard() $response->assertSee('this.hasShownKategoriKosongPopup = true', false); } - /** @test */ + #[Test] public function it_can_access_artikel_edit() { $artikelId = 1; // ID artikel untuk testing @@ -51,7 +51,7 @@ public function it_can_access_artikel_edit() $response->assertSee('Edit Artikel'); } - /** @test */ + #[Test] public function artikel_index_requires_authentication() { auth()->logout(); @@ -59,7 +59,7 @@ public function artikel_index_requires_authentication() $response->assertRedirect(route('login')); } - /** @test */ + #[Test] public function artikel_create_requires_authentication() { auth()->logout(); @@ -67,7 +67,7 @@ public function artikel_create_requires_authentication() $response->assertRedirect(route('login')); } - /** @test */ + #[Test] public function artikel_edit_requires_authentication() { auth()->logout(); @@ -75,7 +75,7 @@ public function artikel_edit_requires_authentication() $response->assertRedirect(route('login')); } - /** @test */ + #[Test] public function it_can_access_upload_gambar_route() { $response = $this->post(route('artikel.upload_gambar'), [ @@ -86,7 +86,7 @@ public function it_can_access_upload_gambar_route() $this->assertContains($response->status(), [302, 400, 500]); } - /** @test */ + #[Test] public function artikel_index_shows_proper_table_structure() { $response = $this->get(route('master-data-artikel.index')); @@ -101,7 +101,7 @@ public function artikel_index_shows_proper_table_structure() $response->assertSee('Status'); } - /** @test */ + #[Test] public function artikel_create_shows_required_fields() { $response = $this->get(route('master-data-artikel.create')); @@ -114,7 +114,7 @@ public function artikel_create_shows_required_fields() $response->assertSee('text-danger'); // asterisk styling } - /** @test */ + #[Test] public function artikel_create_has_upload_functionality() { $response = $this->get(route('master-data-artikel.create')); @@ -125,7 +125,7 @@ public function artikel_create_has_upload_functionality() $response->assertSee('Gambar Utama'); } - /** @test */ + #[Test] public function artikel_edit_loads_with_id() { $artikelId = 123; @@ -137,7 +137,7 @@ public function artikel_edit_loads_with_id() $response->assertSee((string) $artikelId); } - /** @test */ + #[Test] public function artikel_routes_are_registered() { // Check if routes exist diff --git a/tests/Feature/ArtikelUploadTest.php b/tests/Feature/ArtikelUploadTest.php index 4e31502c..8aaec61b 100644 --- a/tests/Feature/ArtikelUploadTest.php +++ b/tests/Feature/ArtikelUploadTest.php @@ -14,7 +14,7 @@ public function setUp(): void Storage::fake('public'); } - /** @test */ + #[Test] public function it_can_upload_valid_image() { $file = UploadedFile::fake()->image('test-artikel.jpg', 800, 600); @@ -34,7 +34,7 @@ public function it_can_upload_valid_image() ]); } - /** @test */ + #[Test] public function it_rejects_non_image_files() { $file = UploadedFile::fake()->create('document.pdf', 100); @@ -47,7 +47,7 @@ public function it_rejects_non_image_files() $this->assertNotEquals(200, $response->status()); } - /** @test */ + #[Test] public function it_rejects_files_larger_than_2mb() { $file = UploadedFile::fake()->image('large-image.jpg')->size(3000); // 3MB @@ -60,7 +60,7 @@ public function it_rejects_files_larger_than_2mb() $this->assertNotEquals(200, $response->status()); } - /** @test */ + #[Test] public function it_accepts_jpg_format() { $file = UploadedFile::fake()->image('test.jpg'); @@ -73,7 +73,7 @@ public function it_accepts_jpg_format() $response->assertJsonFragment(['success' => true]); } - /** @test */ + #[Test] public function it_accepts_jpeg_format() { $file = UploadedFile::fake()->image('test.jpeg'); @@ -86,7 +86,7 @@ public function it_accepts_jpeg_format() $response->assertJsonFragment(['success' => true]); } - /** @test */ + #[Test] public function it_accepts_png_format() { $file = UploadedFile::fake()->image('test.png'); @@ -99,7 +99,7 @@ public function it_accepts_png_format() $response->assertJsonFragment(['success' => true]); } - /** @test */ + #[Test] public function it_requires_file_parameter() { $response = $this->post(route('artikel.upload_gambar'), []); @@ -108,7 +108,7 @@ public function it_requires_file_parameter() $this->assertContains($response->status(), [302, 400, 500]); } - /** @test */ + #[Test] public function upload_returns_valid_url() { $file = UploadedFile::fake()->image('artikel-gambar.jpg'); @@ -125,7 +125,7 @@ public function upload_returns_valid_url() $this->assertNotEmpty($data['url']); } - /** @test */ + #[Test] public function upload_returns_valid_path() { $file = UploadedFile::fake()->image('artikel-gambar.jpg'); @@ -142,7 +142,7 @@ public function upload_returns_valid_path() $this->assertStringContainsString('uploads/artikel', $data['path']); } - /** @test */ + #[Test] public function upload_requires_authentication() { auth()->logout(); diff --git a/tests/Feature/ArtikelWebTest.php b/tests/Feature/ArtikelWebTest.php index 4eb7e255..45a2688c 100644 --- a/tests/Feature/ArtikelWebTest.php +++ b/tests/Feature/ArtikelWebTest.php @@ -14,7 +14,7 @@ protected function tearDown(): void parent::tearDown(); } - /** @test */ + #[Test] public function it_can_access_public_artikel_index() { $this->withoutMiddleware([\App\Http\Middleware\WebsiteEnable::class]); @@ -44,7 +44,7 @@ public function it_can_access_public_artikel_index() $response->assertSee('Berita Desa'); } - /** @test */ + #[Test] public function it_can_access_public_artikel_show() { $this->withoutMiddleware([\App\Http\Middleware\WebsiteEnable::class]); @@ -70,7 +70,7 @@ public function it_can_access_public_artikel_show() $response->assertSee('Konten detail artikel test'); } - /** @test */ + #[Test] public function it_aborts_404_for_disabled_or_missing_artikel() { $this->withoutMiddleware([\App\Http\Middleware\WebsiteEnable::class]); diff --git a/tests/Feature/BantuanControllerTest.php b/tests/Feature/BantuanControllerTest.php index b685c806..d69fe2a0 100644 --- a/tests/Feature/BantuanControllerTest.php +++ b/tests/Feature/BantuanControllerTest.php @@ -9,7 +9,7 @@ class BantuanControllerTest extends BaseTestCase { use DatabaseTransactions; - /** @test */ + #[Test] public function it_can_access_bantuan_index() { $response = $this->get(route('bantuan')); @@ -18,7 +18,7 @@ public function it_can_access_bantuan_index() $response->assertViewIs('bantuan.index'); } - /** @test */ + #[Test] public function it_can_access_bantuan_detail() { $response = $this->get(route('bantuan.detail', ['id' => 1])); @@ -28,7 +28,7 @@ public function it_can_access_bantuan_detail() $response->assertViewHas('id', '1'); } - /** @test */ + #[Test] public function it_can_access_bantuan_cetak() { $response = $this->get('/bantuan/cetak'); @@ -38,7 +38,7 @@ public function it_can_access_bantuan_cetak() $response->assertViewHas('filter'); } - /** @test */ + #[Test] public function it_can_access_bantuan_detail_cetak_peserta() { $response = $this->get(route('bantuan.detail.cetak', ['id' => 1])); @@ -49,7 +49,7 @@ public function it_can_access_bantuan_detail_cetak_peserta() $response->assertViewHas('filter'); } - /** @test */ + #[Test] public function it_passes_filter_params_to_cetak_peserta() { $response = $this->get(route('bantuan.detail.cetak', ['id' => 1]) . '?search=test'); diff --git a/tests/Feature/DasborControllerTest.php b/tests/Feature/DasborControllerTest.php index 1a150d17..394ea7c1 100644 --- a/tests/Feature/DasborControllerTest.php +++ b/tests/Feature/DasborControllerTest.php @@ -10,7 +10,7 @@ class DasborControllerTest extends BaseTestCase { use DatabaseTransactions; - /** @test */ + #[Test] public function it_can_access_dasbor_index() { $response = $this->get(route('dasbor')); @@ -21,7 +21,7 @@ public function it_can_access_dasbor_index() $response->assertViewHas('categoriesItems'); } - /** @test */ + #[Test] public function it_returns_correct_categories_items_structure() { $response = $this->get(route('dasbor')); @@ -35,7 +35,7 @@ public function it_returns_correct_categories_items_structure() }); } - /** @test */ + #[Test] public function it_has_correct_category_keys() { $response = $this->get(route('dasbor')); diff --git a/tests/Feature/DasborDemografiControllerTest.php b/tests/Feature/DasborDemografiControllerTest.php index 6deaff88..2c6e0a7a 100644 --- a/tests/Feature/DasborDemografiControllerTest.php +++ b/tests/Feature/DasborDemografiControllerTest.php @@ -10,7 +10,7 @@ class DasborDemografiControllerTest extends BaseTestCase { use DatabaseTransactions; - /** @test */ + #[Test] public function it_can_access_demografi_index() { $response = $this->get(route('dasbor-demografi')); @@ -21,7 +21,7 @@ public function it_can_access_demografi_index() $response->assertViewHas('statistikUrl'); } - /** @test */ + #[Test] public function it_returns_correct_statistik_url() { $response = $this->get(route('dasbor-demografi')); @@ -29,7 +29,7 @@ public function it_returns_correct_statistik_url() $response->assertViewHas('statistikUrl', 'api/v1/statistik/penduduk'); } - /** @test */ + #[Test] public function it_returns_correct_chart_items_structure() { $response = $this->get(route('dasbor-demografi')); @@ -43,7 +43,7 @@ public function it_returns_correct_chart_items_structure() }); } - /** @test */ + #[Test] public function it_has_correct_chart_item_keys() { $response = $this->get(route('dasbor-demografi')); @@ -67,7 +67,7 @@ public function it_has_correct_chart_item_keys() }); } - /** @test */ + #[Test] public function it_has_correct_chart_item_labels() { $response = $this->get(route('dasbor-demografi')); @@ -91,7 +91,7 @@ public function it_has_correct_chart_item_labels() }); } - /** @test */ + #[Test] public function it_has_correct_detail_urls_in_chart_items() { $response = $this->get(route('dasbor-demografi')); diff --git a/tests/Feature/DataPresisiExcelDownloadTest.php b/tests/Feature/DataPresisiExcelDownloadTest.php index faf40212..d311e2d6 100644 --- a/tests/Feature/DataPresisiExcelDownloadTest.php +++ b/tests/Feature/DataPresisiExcelDownloadTest.php @@ -22,7 +22,7 @@ class DataPresisiExcelDownloadTest extends BaseTestCase ], ]; - /** @test */ + #[Test] public function test_excel_download_button_exists_in_pangan_page() { $response = $this->get('/data-presisi/pangan'); @@ -40,7 +40,7 @@ public function test_excel_download_button_exists_in_pangan_page() } } - /** @test */ + #[Test] public function test_excel_download_button_exists_in_pendidikan_page() { $response = $this->get('/data-presisi/pendidikan'); @@ -58,7 +58,7 @@ public function test_excel_download_button_exists_in_pendidikan_page() } } - /** @test */ + #[Test] public function test_excel_download_button_component_renders_correctly() { $successCount = 0; @@ -87,7 +87,7 @@ public function test_excel_download_button_component_renders_correctly() ); } - /** @test */ + #[Test] public function test_pangan_and_pendidikan_have_matching_excel_button_structure() { $panganResponse = $this->get('/data-presisi/pangan'); diff --git a/tests/Feature/DataPresisiFilterTahunJavaScriptTest.php b/tests/Feature/DataPresisiFilterTahunJavaScriptTest.php index f83c133d..dace1394 100644 --- a/tests/Feature/DataPresisiFilterTahunJavaScriptTest.php +++ b/tests/Feature/DataPresisiFilterTahunJavaScriptTest.php @@ -21,7 +21,7 @@ class DataPresisiFilterTahunJavaScriptTest extends BaseTestCase '/dtks/sandang' ]; - /** @test */ + #[Test] public function test_filter_tahun_javascript_event_listener_exists_in_all_modules() { $successCount = 0; @@ -60,7 +60,7 @@ public function test_filter_tahun_javascript_event_listener_exists_in_all_module } } - /** @test */ + #[Test] public function test_filter_tahun_ajax_parameter_exists_in_all_modules() { $successCount = 0; @@ -97,7 +97,7 @@ public function test_filter_tahun_ajax_parameter_exists_in_all_modules() } } - /** @test */ + #[Test] public function test_datatable_reload_functionality_exists() { $successCount = 0; @@ -134,7 +134,7 @@ public function test_datatable_reload_functionality_exists() } } - /** @test */ + #[Test] public function test_chart_reload_functionality_exists() { $successCount = 0; diff --git a/tests/Feature/DataPresisiKetenagakerjaanControllerTest.php b/tests/Feature/DataPresisiKetenagakerjaanControllerTest.php index ace04f2d..37af84e7 100644 --- a/tests/Feature/DataPresisiKetenagakerjaanControllerTest.php +++ b/tests/Feature/DataPresisiKetenagakerjaanControllerTest.php @@ -10,7 +10,7 @@ class DataPresisiKetenagakerjaanControllerTest extends BaseTestCase // Index Tests // ========================================== - /** @test */ + #[Test] public function test_can_access_ketenagakerjaan_index_page(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.index')); @@ -19,7 +19,7 @@ public function test_can_access_ketenagakerjaan_index_page(): void $response->assertViewIs('data_pokok.data_presisi.ketenagakerjaan.index'); } - /** @test */ + #[Test] public function test_index_page_has_correct_title(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.index')); @@ -27,7 +27,7 @@ public function test_index_page_has_correct_title(): void $response->assertViewHas('title', 'Data Presisi Ketenagakerjaan'); } - /** @test */ + #[Test] public function test_index_page_contains_required_elements(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.index')); @@ -44,7 +44,7 @@ public function test_index_page_contains_required_elements(): void $this->assertStringContainsString('filter-tahun', $content); } - /** @test */ + #[Test] public function test_index_page_has_table_headers(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.index')); @@ -65,7 +65,7 @@ public function test_index_page_has_table_headers(): void } } - /** @test */ + #[Test] public function test_index_page_has_excel_download_button(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.index')); @@ -78,7 +78,7 @@ public function test_index_page_has_excel_download_button(): void $this->assertStringContainsString('data_presisi_ketenagakerjaan', $content); } - /** @test */ + #[Test] public function test_index_page_has_print_button(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.index')); @@ -92,7 +92,7 @@ public function test_index_page_has_print_button(): void // Detail Tests // ========================================== - /** @test */ + #[Test] public function test_can_access_ketenagakerjaan_detail_page(): void { $data = json_encode([ @@ -110,7 +110,7 @@ public function test_can_access_ketenagakerjaan_detail_page(): void $response->assertViewIs('data_pokok.data_presisi.ketenagakerjaan.detail'); } - /** @test */ + #[Test] public function test_detail_page_has_decoded_json_data(): void { $data = json_encode([ @@ -135,7 +135,7 @@ public function test_detail_page_has_decoded_json_data(): void $this->assertEquals(1, $viewData->jumlah_kk); } - /** @test */ + #[Test] public function test_detail_page_displays_kepala_keluarga_name(): void { $data = json_encode([ @@ -155,7 +155,7 @@ public function test_detail_page_displays_kepala_keluarga_name(): void $this->assertStringContainsString('1234567890', $content); } - /** @test */ + #[Test] public function test_detail_page_has_detail_table(): void { $data = json_encode([ @@ -174,7 +174,7 @@ public function test_detail_page_has_detail_table(): void $this->assertStringContainsString('id="detail-ketenagakerjaan"', $content); } - /** @test */ + #[Test] public function test_detail_page_has_back_link_to_index(): void { $data = json_encode([ @@ -197,7 +197,7 @@ public function test_detail_page_has_back_link_to_index(): void // Cetak Tests // ========================================== - /** @test */ + #[Test] public function test_can_access_ketenagakerjaan_cetak_page(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.cetak')); @@ -206,7 +206,7 @@ public function test_can_access_ketenagakerjaan_cetak_page(): void $response->assertViewIs('data_pokok.data_presisi.ketenagakerjaan.cetak'); } - /** @test */ + #[Test] public function test_cetak_page_has_filter_view_variable(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.cetak')); @@ -214,7 +214,7 @@ public function test_cetak_page_has_filter_view_variable(): void $response->assertViewHas('filter'); } - /** @test */ + #[Test] public function test_cetak_page_passes_query_string_as_filter(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.cetak') . '?tahun=2024&status=active'); @@ -226,7 +226,7 @@ public function test_cetak_page_passes_query_string_as_filter(): void $this->assertStringContainsString('status=active', $filter); } - /** @test */ + #[Test] public function test_cetak_page_has_table_with_correct_id(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.cetak')); @@ -236,7 +236,7 @@ public function test_cetak_page_has_table_with_correct_id(): void $this->assertStringContainsString('id="tabel-ketenagakerjaan"', $content); } - /** @test */ + #[Test] public function test_cetak_page_has_correct_table_headers(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.cetak')); @@ -261,7 +261,7 @@ public function test_cetak_page_has_correct_table_headers(): void } } - /** @test */ + #[Test] public function test_cetak_page_uses_cetak_layout(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.cetak')); @@ -276,7 +276,7 @@ public function test_cetak_page_uses_cetak_layout(): void // DetailData Tests // ========================================== - /** @test */ + #[Test] public function test_can_access_ketenagakerjaan_detail_data_page(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.detail_data')); @@ -285,7 +285,7 @@ public function test_can_access_ketenagakerjaan_detail_data_page(): void $response->assertViewIs('data_pokok.data_presisi.ketenagakerjaan.detail_data'); } - /** @test */ + #[Test] public function test_detail_data_page_has_title_and_colomn(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.detail_data')); @@ -294,7 +294,7 @@ public function test_detail_data_page_has_title_and_colomn(): void $response->assertViewHas('colomn'); } - /** @test */ + #[Test] public function test_detail_data_default_title_without_judul(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.detail_data')); @@ -304,7 +304,7 @@ public function test_detail_data_default_title_without_judul(): void $this->assertEquals('Data Presisi Ketenagakerjaan - ', $title); } - /** @test */ + #[Test] public function test_detail_data_title_includes_judul(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.detail_data', ['judul' => 'Test Judul'])); @@ -314,7 +314,7 @@ public function test_detail_data_title_includes_judul(): void $this->assertStringContainsString('Test Judul', $title); } - /** @test */ + #[Test] public function test_detail_data_title_sanitizes_xss(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.detail_data', ['judul' => ''])); @@ -325,7 +325,7 @@ public function test_detail_data_title_sanitizes_xss(): void $this->assertStringNotContainsString('', $title); } - /** @test */ + #[Test] public function test_detail_data_colomn_empty_without_filter(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.detail_data')); @@ -335,7 +335,7 @@ public function test_detail_data_colomn_empty_without_filter(): void $this->assertEquals('', $colomn); } - /** @test */ + #[Test] public function test_detail_data_colomn_with_valid_filter(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.detail_data', [ @@ -350,7 +350,7 @@ public function test_detail_data_colomn_with_valid_filter(): void $this->assertEquals('jenis_pekerjaan:petani', $colomn); } - /** @test */ + #[Test] public function test_detail_data_colomn_empty_when_tipe_missing(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.detail_data', [ @@ -364,7 +364,7 @@ public function test_detail_data_colomn_empty_when_tipe_missing(): void $this->assertEquals('', $colomn); } - /** @test */ + #[Test] public function test_detail_data_colomn_empty_when_nilai_missing(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.detail_data', [ @@ -378,7 +378,7 @@ public function test_detail_data_colomn_empty_when_nilai_missing(): void $this->assertEquals('', $colomn); } - /** @test */ + #[Test] public function test_detail_data_colomn_empty_when_tipe_empty_string(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.detail_data', [ @@ -393,7 +393,7 @@ public function test_detail_data_colomn_empty_when_tipe_empty_string(): void $this->assertEquals('', $colomn); } - /** @test */ + #[Test] public function test_detail_data_colomn_empty_when_nilai_empty_string(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.detail_data', [ @@ -408,7 +408,7 @@ public function test_detail_data_colomn_empty_when_nilai_empty_string(): void $this->assertEquals('', $colomn); } - /** @test */ + #[Test] public function test_detail_data_page_has_detail_table(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.detail_data')); @@ -418,7 +418,7 @@ public function test_detail_data_page_has_detail_table(): void $this->assertStringContainsString('id="detail-ketenagakerjaan"', $content); } - /** @test */ + #[Test] public function test_detail_data_page_has_filter_tahun_component(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.detail_data')); @@ -428,7 +428,7 @@ public function test_detail_data_page_has_filter_tahun_component(): void $this->assertStringContainsString('filter-tahun', $content); } - /** @test */ + #[Test] public function test_detail_data_page_has_correct_table_headers(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.detail_data')); @@ -453,7 +453,7 @@ public function test_detail_data_page_has_correct_table_headers(): void } } - /** @test */ + #[Test] public function test_detail_data_validation_requires_filter_tipe_when_filter_nilai_present(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.detail_data', [ @@ -468,7 +468,7 @@ public function test_detail_data_validation_requires_filter_tipe_when_filter_nil $this->assertEquals('', $colomn); } - /** @test */ + #[Test] public function test_detail_data_validation_requires_filter_nilai_when_filter_tipe_present(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.detail_data', [ @@ -483,7 +483,7 @@ public function test_detail_data_validation_requires_filter_nilai_when_filter_ti $this->assertEquals('', $colomn); } - /** @test */ + #[Test] public function test_detail_data_title_strips_html_tags(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.detail_data', ['judul' => 'Bold Italic'])); @@ -496,7 +496,7 @@ public function test_detail_data_title_strips_html_tags(): void $this->assertStringContainsString('Italic', $title); } - /** @test */ + #[Test] public function test_detail_data_title_encodes_special_characters(): void { $response = $this->get(route('data-pokok.data-presisi-ketenagakerjaan.detail_data', ['judul' => 'Test "Quotes" & '])); diff --git a/tests/Feature/DataPresisiLaporanTest.php b/tests/Feature/DataPresisiLaporanTest.php index 33d4fe4e..f4b9f1c8 100644 --- a/tests/Feature/DataPresisiLaporanTest.php +++ b/tests/Feature/DataPresisiLaporanTest.php @@ -6,7 +6,7 @@ class DataPresisiLaporanTest extends BaseTestCase { - /** @test */ + #[Test] public function test_can_access_laporan_semua_desa_page() { $response = $this->get(route('laporan.data-presisi.index')); @@ -16,7 +16,7 @@ public function test_can_access_laporan_semua_desa_page() $response->assertViewHas('title', 'Data Presisi Pengisian Laporan Semua Desa'); } - /** @test */ + #[Test] public function test_laporan_semua_desa_has_required_elements() { $response = $this->get(route('laporan.data-presisi.index')); @@ -35,7 +35,7 @@ public function test_laporan_semua_desa_has_required_elements() $this->assertStringContainsString('Data Lengkap', $content); } - /** @test */ + #[Test] public function test_laporan_semua_desa_has_correct_table_columns() { $response = $this->get(route('laporan.data-presisi.index')); @@ -64,7 +64,7 @@ public function test_laporan_semua_desa_has_correct_table_columns() } } - /** @test */ + #[Test] public function test_laporan_semua_desa_has_filter_status_javascript() { $response = $this->get(route('laporan.data-presisi.index')); @@ -76,7 +76,7 @@ public function test_laporan_semua_desa_has_filter_status_javascript() $this->assertStringContainsString("$('#laporanTable').DataTable().ajax.reload()", $content); } - /** @test */ + #[Test] public function test_laporan_semua_desa_has_datatable_configuration() { $response = $this->get(route('laporan.data-presisi.index')); @@ -92,7 +92,7 @@ public function test_laporan_semua_desa_has_datatable_configuration() $this->assertStringContainsString("$('#filter-status').val()", $content); } - /** @test */ + #[Test] public function test_can_access_laporan_perdesa_page() { $response = $this->get(route('laporan.data-presisi.perdesa')); @@ -102,7 +102,7 @@ public function test_can_access_laporan_perdesa_page() $response->assertViewHas('title', 'Data Presisi Pengisian Laporan Per Desa'); } - /** @test */ + #[Test] public function test_laporan_perdesa_has_correct_table_columns() { $response = $this->get(route('laporan.data-presisi.perdesa')); @@ -123,7 +123,7 @@ public function test_laporan_perdesa_has_correct_table_columns() } } - /** @test */ + #[Test] public function test_laporan_perdesa_has_datatable_configuration() { $response = $this->get(route('laporan.data-presisi.perdesa')); @@ -141,7 +141,7 @@ public function test_laporan_perdesa_has_datatable_configuration() $this->assertStringContainsString('"config_desa"', $content); } - /** @test */ + #[Test] public function test_laporan_perdesa_number_formatting_for_columns() { $response = $this->get(route('laporan.data-presisi.perdesa')); @@ -152,7 +152,7 @@ public function test_laporan_perdesa_number_formatting_for_columns() $this->assertStringContainsString("render: $.fn.dataTable.render.number('.', ',', 0, '')", $content); } - /** @test */ + #[Test] public function test_both_pages_use_correct_api_endpoints() { // Test laporan index @@ -166,7 +166,7 @@ public function test_both_pages_use_correct_api_endpoints() $this->assertStringContainsString('/api/v1/data-presisi/laporan-perdesa', $content2); } - /** @test */ + #[Test] public function test_both_pages_have_breadcrumbs() { // Test laporan index has breadcrumb section @@ -180,7 +180,7 @@ public function test_both_pages_have_breadcrumbs() $this->assertTrue(true); } - /** @test */ + #[Test] public function test_both_pages_extend_correct_layout() { // Test laporan index uses correct layout (has main-footer) @@ -194,7 +194,7 @@ public function test_both_pages_extend_correct_layout() $this->assertStringContainsString('class="main-footer"', $content2); } - /** @test */ + #[Test] public function test_laporan_semua_desa_has_export_buttons() { $response = $this->get(route('laporan.data-presisi.index')); @@ -205,7 +205,7 @@ public function test_laporan_semua_desa_has_export_buttons() $this->assertStringContainsString('id="export-excel"', $content); } - /** @test */ + #[Test] public function test_laporan_perdesa_has_export_buttons() { $response = $this->get(route('laporan.data-presisi.perdesa')); diff --git a/tests/Feature/DataPresisiPanganControllerTest.php b/tests/Feature/DataPresisiPanganControllerTest.php index 580092d4..c126e23a 100644 --- a/tests/Feature/DataPresisiPanganControllerTest.php +++ b/tests/Feature/DataPresisiPanganControllerTest.php @@ -6,7 +6,7 @@ class DataPresisiPanganControllerTest extends BaseTestCase { - /** @test */ + #[Test] public function test_can_access_pangan_index_page() { $response = $this->get(route('data-pokok.data-presisi-pangan.index')); @@ -16,7 +16,7 @@ public function test_can_access_pangan_index_page() $response->assertViewHas('title', 'Data Presisi Pangan'); } - /** @test */ + #[Test] public function test_detail_data_with_valid_filter_parameters() { $response = $this->get(route('data-pokok.data-presisi-pangan.detail_data') . '?judul=Test%20Judul&filter[tipe]=kategori_pangan&filter[nilai]=beras'); @@ -27,7 +27,7 @@ public function test_detail_data_with_valid_filter_parameters() $response->assertViewHas('colomn', 'kategori_pangan:beras'); } - /** @test */ + #[Test] public function test_detail_data_with_missing_filter_tipe() { $response = $this->get(route('data-pokok.data-presisi-pangan.detail_data') . '?judul=Test%20Judul&filter[nilai]=beras'); @@ -36,7 +36,7 @@ public function test_detail_data_with_missing_filter_tipe() $response->assertViewHas('colomn', ''); } - /** @test */ + #[Test] public function test_detail_data_with_missing_filter_nilai() { $response = $this->get(route('data-pokok.data-presisi-pangan.detail_data') . '?judul=Test%20Judul&filter[tipe]=kategori_pangan'); @@ -45,7 +45,7 @@ public function test_detail_data_with_missing_filter_nilai() $response->assertViewHas('colomn', ''); } - /** @test */ + #[Test] public function test_detail_data_with_empty_filter_tipe() { $response = $this->get(route('data-pokok.data-presisi-pangan.detail_data') . '?judul=Test%20Judul&filter[tipe]=&filter[nilai]=beras'); @@ -54,7 +54,7 @@ public function test_detail_data_with_empty_filter_tipe() $response->assertViewHas('colomn', ''); } - /** @test */ + #[Test] public function test_detail_data_with_empty_filter_nilai() { $response = $this->get(route('data-pokok.data-presisi-pangan.detail_data') . '?judul=Test%20Judul&filter[tipe]=kategori_pangan&filter[nilai]='); @@ -63,7 +63,7 @@ public function test_detail_data_with_empty_filter_nilai() $response->assertViewHas('colomn', ''); } - /** @test */ + #[Test] public function test_detail_data_without_filter_parameter() { $response = $this->get(route('data-pokok.data-presisi-pangan.detail_data') . '?judul=Test%20Judul'); @@ -72,7 +72,7 @@ public function test_detail_data_without_filter_parameter() $response->assertViewHas('colomn', ''); } - /** @test */ + #[Test] public function test_detail_data_without_judul_parameter() { $response = $this->get(route('data-pokok.data-presisi-pangan.detail_data') . '?filter[tipe]=kategori_pangan&filter[nilai]=beras'); @@ -82,7 +82,7 @@ public function test_detail_data_without_judul_parameter() $response->assertViewHas('colomn', 'kategori_pangan:beras'); } - /** @test */ + #[Test] public function test_cetak_with_filter_parameters() { $response = $this->get(route('data-pokok.data-presisi-pangan.cetak') . '?filter[tipe]=kategori_pangan&filter[nilai]=beras&tahun=2024'); @@ -92,7 +92,7 @@ public function test_cetak_with_filter_parameters() $response->assertViewHas('filter'); } - /** @test */ + #[Test] public function test_cetak_without_parameters() { $response = $this->get(route('data-pokok.data-presisi-pangan.cetak')); @@ -101,7 +101,7 @@ public function test_cetak_without_parameters() $response->assertViewIs('data_pokok.data_presisi.pangan.cetak'); } - /** @test */ + #[Test] public function test_detail_data_column_format_with_special_characters() { $response = $this->get(route('data-pokok.data-presisi-pangan.detail_data') . '?judul=Test%20Judul&filter[tipe]=kategori_pangan&filter[nilai]=beras-organik'); @@ -110,7 +110,7 @@ public function test_detail_data_column_format_with_special_characters() $response->assertViewHas('colomn', 'kategori_pangan:beras-organik'); } - /** @test */ + #[Test] public function test_detail_data_column_format_with_spaces() { $response = $this->get(route('data-pokok.data-presisi-pangan.detail_data') . '?judul=Test%20Judul&filter[tipe]=kategori%20pangan&filter[nilai]=beras%20merah'); diff --git a/tests/Feature/DataPresisiSeniBudayaExcelDownloadTest.php b/tests/Feature/DataPresisiSeniBudayaExcelDownloadTest.php index 818b8117..02297d39 100644 --- a/tests/Feature/DataPresisiSeniBudayaExcelDownloadTest.php +++ b/tests/Feature/DataPresisiSeniBudayaExcelDownloadTest.php @@ -6,7 +6,7 @@ class DataPresisiSeniBudayaExcelDownloadTest extends BaseTestCase { - /** @test */ + #[Test] public function test_excel_download_button_exists_in_seni_budaya_page() { $response = $this->get('/data-presisi/seni-budaya'); @@ -24,7 +24,7 @@ public function test_excel_download_button_exists_in_seni_budaya_page() $this->assertStringContainsString('/api/v1/data-presisi/seni-budaya/rtm/download', $content); } - /** @test */ + #[Test] public function test_seni_budaya_page_has_filter_tahun() { $response = $this->get('/data-presisi/seni-budaya'); @@ -40,7 +40,7 @@ public function test_seni_budaya_page_has_filter_tahun() $this->assertStringContainsString('filter-tahun', $content); } - /** @test */ + #[Test] public function test_seni_budaya_page_has_print_button() { $response = $this->get('/data-presisi/seni-budaya'); diff --git a/tests/Feature/DetailDataPresisiAktivitasKeagamaanRequestTest.php b/tests/Feature/DetailDataPresisiAktivitasKeagamaanRequestTest.php index 83b0f84e..c903dc95 100644 --- a/tests/Feature/DetailDataPresisiAktivitasKeagamaanRequestTest.php +++ b/tests/Feature/DetailDataPresisiAktivitasKeagamaanRequestTest.php @@ -6,7 +6,7 @@ class DetailDataPresisiAktivitasKeagamaanRequestTest extends BaseTestCase { - /** @test */ + #[Test] public function it_validates_judul_as_nullable_string_max_255() { // Valid judul @@ -20,7 +20,7 @@ public function it_validates_judul_as_nullable_string_max_255() $response->assertSessionHasErrors('judul'); } - /** @test */ + #[Test] public function it_validates_filter_as_nullable_array() { // Valid filter @@ -33,7 +33,7 @@ public function it_validates_filter_as_nullable_array() $response->assertSessionHasErrors('filter'); } - /** @test */ + #[Test] public function it_validates_filter_tipe_required_with_filter_and_in_allowed_values() { // Valid tipe @@ -54,7 +54,7 @@ public function it_validates_filter_tipe_required_with_filter_and_in_allowed_val $response->assertSessionHasErrors('filter.tipe'); } - /** @test */ + #[Test] public function it_validates_filter_nilai_required_with_filter_as_string() { // Valid nilai diff --git a/tests/Feature/DownloadControllerCmsTest.php b/tests/Feature/DownloadControllerCmsTest.php index 0e766547..cab64373 100644 --- a/tests/Feature/DownloadControllerCmsTest.php +++ b/tests/Feature/DownloadControllerCmsTest.php @@ -12,7 +12,7 @@ class DownloadControllerCmsTest extends BaseTestCase { use DatabaseTransactions; - /** @test */ + #[Test] public function halaman_index_download_dapat_diakses() { $response = $this->get(route('downloads.index')); @@ -21,7 +21,7 @@ public function halaman_index_download_dapat_diakses() $response->assertViewIs('downloads.index'); } - /** @test */ + #[Test] public function form_tambah_download_dapat_diakses() { $response = $this->get(route('downloads.create')); @@ -30,7 +30,7 @@ public function form_tambah_download_dapat_diakses() $response->assertViewIs('downloads.create'); } - /** @test */ + #[Test] public function file_download_baru_dapat_disimpan() { Storage::fake('public'); @@ -76,7 +76,7 @@ public function file_download_baru_dapat_disimpan() $this->assertDatabaseHas('downloads', ['title' => 'File PDF']); } - /** @test */ + #[Test] public function form_edit_download_dapat_diakses() { $download = Download::factory()->create(); @@ -87,7 +87,7 @@ public function form_edit_download_dapat_diakses() $response->assertViewIs('downloads.edit'); } - /** @test */ + #[Test] public function file_download_dapat_diperbarui() { $download = Download::factory()->create([ @@ -106,7 +106,7 @@ public function file_download_dapat_diperbarui() $this->assertDatabaseHas('downloads', ['title' => 'Baru']); } - /** @test */ + #[Test] public function file_download_dapat_dihapus() { $download = Download::factory()->create(); diff --git a/tests/Feature/GlobalRateLimiterTest.php b/tests/Feature/GlobalRateLimiterTest.php index 84b5e947..0491dbfd 100644 --- a/tests/Feature/GlobalRateLimiterTest.php +++ b/tests/Feature/GlobalRateLimiterTest.php @@ -19,7 +19,7 @@ protected function setUp(): void Cache::flush(); } - /** @test */ + #[Test] public function it_allows_requests_when_rate_limiter_is_disabled() { // Disable rate limiter @@ -33,7 +33,7 @@ public function it_allows_requests_when_rate_limiter_is_disabled() } } - /** @test */ + #[Test] public function it_allows_requests_within_limit_when_rate_limiter_is_enabled() { // Enable rate limiter with low limits for testing @@ -52,7 +52,7 @@ public function it_allows_requests_within_limit_when_rate_limiter_is_enabled() } } - /** @test */ + #[Test] public function it_blocks_requests_when_limit_is_exceeded() { // Enable rate limiter with low limits for testing @@ -72,7 +72,7 @@ public function it_blocks_requests_when_limit_is_exceeded() $this->assertEquals(429, $response3->getStatusCode()); } - /** @test */ + #[Test] public function it_returns_correct_json_response_for_api_requests() { // Enable rate limiter with low limits for testing @@ -95,7 +95,7 @@ public function it_returns_correct_json_response_for_api_requests() $response->assertJsonStructure(['retry_after']); } - /** @test */ + #[Test] public function it_excludes_configured_paths_from_rate_limiting() { // Enable rate limiter with low limits @@ -114,7 +114,7 @@ public function it_excludes_configured_paths_from_rate_limiting() } } - /** @test */ + #[Test] public function it_excludes_configured_ip_addresses_from_rate_limiting() { // Enable rate limiter with low limits @@ -133,7 +133,7 @@ public function it_excludes_configured_ip_addresses_from_rate_limiting() } } - /** @test */ + #[Test] public function it_respects_wildcard_patterns_in_excluded_paths() { // Enable rate limiter with low limits @@ -155,7 +155,7 @@ public function it_respects_wildcard_patterns_in_excluded_paths() $this->assertNotEquals(429, $response3->getStatusCode()); } - /** @test */ + #[Test] public function it_respects_different_rate_limits_for_different_ips() { // Enable rate limiter with low limits @@ -176,7 +176,7 @@ public function it_respects_different_rate_limits_for_different_ips() $this->assertNotEquals(429, $response->getStatusCode()); } - /** @test */ + #[Test] public function it_works_with_simple_paths() { // Enable rate limiter with low limits diff --git a/tests/Feature/Http/Controllers/DataPresisiAktivitasKeagamaanControllerTest.php b/tests/Feature/Http/Controllers/DataPresisiAktivitasKeagamaanControllerTest.php index d797a1b7..f52a8bff 100644 --- a/tests/Feature/Http/Controllers/DataPresisiAktivitasKeagamaanControllerTest.php +++ b/tests/Feature/Http/Controllers/DataPresisiAktivitasKeagamaanControllerTest.php @@ -6,7 +6,7 @@ class DataPresisiAktivitasKeagamaanControllerTest extends BaseTestCase { - /** @test */ + #[Test] public function it_can_access_detail_data_page() { $response = $this->get(route('data-pokok.data-presisi-aktivitas-keagamaan.detail_data')); @@ -17,7 +17,7 @@ public function it_can_access_detail_data_page() ->assertViewHas('colomn'); } - /** @test */ + #[Test] public function it_handles_judul_parameter() { $judul = 'Test Title '; @@ -28,7 +28,7 @@ public function it_handles_judul_parameter() ->assertViewHas('colomn', ''); } - /** @test */ + #[Test] public function it_handles_filter_parameter() { $filter = [ @@ -41,7 +41,7 @@ public function it_handles_filter_parameter() ->assertViewHas('colomn', 'agama_id:1'); } - /** @test */ + #[Test] public function it_handles_filter_with_different_tipe() { $filter = [ @@ -54,7 +54,7 @@ public function it_handles_filter_with_different_tipe() ->assertViewHas('colomn', 'frekwensi_mengikuti_kegiatan_setahun:2'); } - /** @test */ + #[Test] public function it_handles_empty_filter() { $response = $this->get(route('data-pokok.data-presisi-aktivitas-keagamaan.detail_data', ['filter' => []])); diff --git a/tests/Feature/JaminanSosialTest.php b/tests/Feature/JaminanSosialTest.php index d3ee6de0..18c7875f 100644 --- a/tests/Feature/JaminanSosialTest.php +++ b/tests/Feature/JaminanSosialTest.php @@ -6,7 +6,7 @@ class JaminanSosialTest extends BaseTestCase { - /** @test */ + #[Test] public function test_can_access_jaminan_sosial_page() { $response = $this->get(route('jaminan-sosial')); @@ -16,7 +16,7 @@ public function test_can_access_jaminan_sosial_page() $response->assertSee('Data Kepesertaan Program dan Statistik'); } - /** @test */ + #[Test] public function test_jaminan_sosial_page_has_required_elements() { $response = $this->get(route('jaminan-sosial')); @@ -42,7 +42,7 @@ public function test_jaminan_sosial_page_has_required_elements() $this->assertStringContainsString('id="download-excel"', $content, 'Excel download button ID tidak ditemukan'); } - /** @test */ + #[Test] public function test_jaminan_sosial_has_correct_table_columns() { $response = $this->get(route('jaminan-sosial')); @@ -64,7 +64,7 @@ public function test_jaminan_sosial_has_correct_table_columns() } } - /** @test */ + #[Test] public function test_jaminan_sosial_has_print_button() { $response = $this->get(route('jaminan-sosial')); @@ -75,7 +75,7 @@ public function test_jaminan_sosial_has_print_button() $this->assertStringContainsString('data-print-url=', $content, 'Route print tidak ditemukan'); } - /** @test */ + #[Test] public function test_jaminan_sosial_has_excel_download_button() { $response = $this->get(route('jaminan-sosial')); @@ -86,7 +86,7 @@ public function test_jaminan_sosial_has_excel_download_button() $this->assertStringContainsString('data-download-url=', $content, 'Download URL tidak ditemukan'); } - /** @test */ + #[Test] public function test_jaminan_sosial_has_datatable_configuration() { $response = $this->get(route('jaminan-sosial')); @@ -101,7 +101,7 @@ public function test_jaminan_sosial_has_datatable_configuration() $this->assertStringContainsString('/api/v1/data-presisi/jaminan-sosial', $content, 'API endpoint tidak ditemukan'); } - /** @test */ + #[Test] public function test_jaminan_sosial_has_filter_tahun_functionality() { $response = $this->get(route('jaminan-sosial')); @@ -113,7 +113,7 @@ public function test_jaminan_sosial_has_filter_tahun_functionality() $this->assertStringContainsString('grafikPie()', $content, 'Grafik reload pada filter tahun tidak ditemukan'); } - /** @test */ + #[Test] public function test_jaminan_sosial_has_detail_control_functionality() { $response = $this->get(route('jaminan-sosial')); @@ -125,7 +125,7 @@ public function test_jaminan_sosial_has_detail_control_functionality() $this->assertStringContainsString('row.child.isShown()', $content, 'Logika expand/collapse detail control tidak ditemukan'); } - /** @test */ + #[Test] public function test_jaminan_sosial_detail_button_has_correct_route() { $response = $this->get(route('jaminan-sosial')); @@ -136,7 +136,7 @@ public function test_jaminan_sosial_detail_button_has_correct_route() $this->assertStringContainsString('?data=__DATA__', $content, 'Parameter data pada route detail tidak ditemukan'); } - /** @test */ + #[Test] public function test_jaminan_sosial_uses_correct_api_filters() { $response = $this->get(route('jaminan-sosial')); diff --git a/tests/Feature/LaporanBulananDetailExportTest.php b/tests/Feature/LaporanBulananDetailExportTest.php index d8eafa6c..d2d2b8f1 100644 --- a/tests/Feature/LaporanBulananDetailExportTest.php +++ b/tests/Feature/LaporanBulananDetailExportTest.php @@ -6,7 +6,7 @@ class LaporanBulananDetailExportTest extends BaseTestCase { - /** @test */ + #[Test] public function test_detail_page_has_export_excel_button() { // Set up session data yang diperlukan @@ -34,7 +34,7 @@ public function test_detail_page_has_export_excel_button() $this->assertStringNotContainsString('>Cetak<', $content, 'Label Cetak seharusnya sudah dihapus'); } - /** @test */ + #[Test] public function test_detail_page_export_excel_button_has_correct_route() { session([ diff --git a/tests/Feature/LoginControllerTest.php b/tests/Feature/LoginControllerTest.php index 1d2bf41a..d7eb5316 100644 --- a/tests/Feature/LoginControllerTest.php +++ b/tests/Feature/LoginControllerTest.php @@ -59,7 +59,7 @@ public function setUp(): void RateLimiter::clear("captcha:{$ip}:" . hash('xxh64', $userAgent)); } - /** @test */ + #[Test] public function it_shows_login_form() { $response = $this->get('/login'); @@ -70,7 +70,7 @@ public function it_shows_login_form() $response->assertViewHas('captchaView'); } - /** @test */ + #[Test] public function it_shows_login_form_with_captcha_when_threshold_reached() { // Simulate failed attempts to trigger captcha @@ -88,7 +88,7 @@ public function it_shows_login_form_with_captcha_when_threshold_reached() $response->assertViewHas('captchaView', 'auth.captcha'); } - /** @test */ + #[Test] public function it_can_login_with_email() { // Clear any existing rate limiter to ensure clean state @@ -108,7 +108,7 @@ public function it_can_login_with_email() $this->assertAuthenticatedAs($this->user); } - /** @test */ + #[Test] public function it_can_login_with_username() { // Clear any existing rate limiter to ensure clean state @@ -128,7 +128,7 @@ public function it_can_login_with_username() $this->assertAuthenticatedAs($this->user); } - /** @test */ + #[Test] public function it_redirects_to_2fa_challenge_when_2fa_enabled() { $this->twoFactorService @@ -144,7 +144,7 @@ public function it_redirects_to_2fa_challenge_when_2fa_enabled() $this->assertNull(session('2fa_verified')); } - /** @test */ + #[Test] public function it_redirects_to_password_change_when_password_is_weak() { // Delete existing weak user first @@ -170,7 +170,7 @@ public function it_redirects_to_password_change_when_password_is_weak() $this->assertTrue(session('weak_password')); } - /** @test */ + #[Test] public function it_fails_login_with_invalid_credentials() { $response = $this->post('/login', [ @@ -182,7 +182,7 @@ public function it_fails_login_with_invalid_credentials() $this->assertGuest(); } - /** @test */ + #[Test] public function it_handles_locked_account() { $this->user->lockAccount(); @@ -197,7 +197,7 @@ public function it_handles_locked_account() $this->assertGuest(); } - /** @test */ + #[Test] public function it_records_failed_login_attempts() { $response = $this->post('/login', [ @@ -212,7 +212,7 @@ public function it_records_failed_login_attempts() $this->assertTrue($this->user->failed_login_attempts > 0); } - /** @test */ + #[Test] public function it_locks_account_after_max_failed_attempts() { // Test ini memerlukan setup rate limiter yang kompleks @@ -248,7 +248,7 @@ public function it_locks_account_after_max_failed_attempts() $this->assertTrue($this->user->isLocked()); } - /** @test */ + #[Test] public function it_resets_failed_attempts_on_successful_login() { // Clear rate limiter @@ -277,7 +277,7 @@ public function it_resets_failed_attempts_on_successful_login() $this->assertFalse($this->user->isLocked()); } - /** @test */ + #[Test] public function it_finds_username_correctly_for_email() { $controller = new \App\Http\Controllers\Auth\LoginController( @@ -295,7 +295,7 @@ public function it_finds_username_correctly_for_email() $this->assertEquals('test@example.com', $request->input('email')); } - /** @test */ + #[Test] public function it_finds_username_correctly_for_username() { $controller = new \App\Http\Controllers\Auth\LoginController( @@ -313,7 +313,7 @@ public function it_finds_username_correctly_for_username() $this->assertEquals('testuser', $request->input('username')); } - /** @test */ + #[Test] public function it_handles_nonexistent_user_in_failed_login() { $response = $this->post('/login', [ @@ -325,7 +325,7 @@ public function it_handles_nonexistent_user_in_failed_login() $this->assertGuest(); } - /** @test */ + #[Test] public function it_handles_inactive_user() { // Clear rate limiter @@ -352,7 +352,7 @@ public function it_handles_inactive_user() $this->user->update(['active' => 1]); } - /** @test */ + #[Test] public function it_handles_remember_me_functionality() { // Clear rate limiter diff --git a/tests/Feature/MenuControllerCmsTest.php b/tests/Feature/MenuControllerCmsTest.php index 0c9b0c2f..108c86bc 100644 --- a/tests/Feature/MenuControllerCmsTest.php +++ b/tests/Feature/MenuControllerCmsTest.php @@ -11,9 +11,7 @@ class MenuControllerCmsTest extends BaseTestCase { use DatabaseTransactions; - /** @test - * Menguji halaman index menu dapat ditampilkan tanpa error - */ + #[Test] public function test_index_menampilkan_halaman_daftar_menu() { // Arrange: siapkan page dan category @@ -30,9 +28,7 @@ public function test_index_menampilkan_halaman_daftar_menu() $response->assertViewHas('sourceItem'); } - /** @test - * Menguji penyimpanan data menu baru berhasil dan redirect sesuai jenis menu - */ + #[Test] public function test_store_menyimpan_menu_baru_dan_redirect_sesuai_jenis() { // Arrange @@ -57,9 +53,7 @@ public function test_store_menyimpan_menu_baru_dan_redirect_sesuai_jenis() $this->assertEquals('Menu berhasil disimpan.', session('success')); } - /** @test - * Menguji redirect store jika tipe menu 2 - */ + #[Test] public function test_store_redirect_ke_index_dengan_type_dua() { $payload = [ @@ -80,9 +74,7 @@ public function test_store_redirect_ke_index_dengan_type_dua() $this->assertDatabaseHas('menus', ['name' => 'Menu Statistik']); } - /** @test - * Menguji jika parameter menu_type tidak dikirim maka default ke 1 - */ + #[Test] public function test_store_menu_type_default_ke_satu_jika_null() { $payload = [ diff --git a/tests/Feature/ModuleControllerOrgTest.php b/tests/Feature/ModuleControllerOrgTest.php index bc6d6dad..24be0cd7 100644 --- a/tests/Feature/ModuleControllerOrgTest.php +++ b/tests/Feature/ModuleControllerOrgTest.php @@ -6,7 +6,7 @@ class ModuleControllerOrgTest extends WebsiteTestCase { - /** @test */ + #[Test] public function it_can_access_org_module_page() { // Act: Akses halaman bagan organisasi @@ -19,7 +19,7 @@ public function it_can_access_org_module_page() $response->assertViewHas('content'); } - /** @test */ + #[Test] public function it_shows_org_chart_elements_in_view() { // Act: Akses halaman @@ -31,7 +31,7 @@ public function it_shows_org_chart_elements_in_view() $response->assertSee('chart-container'); } - /** @test */ + #[Test] public function it_loads_required_assets_for_org_chart() { // Act: Akses halaman @@ -44,7 +44,7 @@ public function it_loads_required_assets_for_org_chart() $response->assertSee('vendor/orgchart/html2canvas.min.js'); } - /** @test */ + #[Test] public function it_returns_org_module_with_correct_data_structure() { // Act: Akses halaman @@ -63,7 +63,7 @@ public function it_returns_org_module_with_correct_data_structure() $this->assertTrue(is_iterable($viewData['content'])); } - /** @test */ + #[Test] public function it_includes_org_partial_view() { // Act: Akses halaman diff --git a/tests/Feature/OtpControllerTest.php b/tests/Feature/OtpControllerTest.php index 821ed4e8..c99f73f1 100644 --- a/tests/Feature/OtpControllerTest.php +++ b/tests/Feature/OtpControllerTest.php @@ -44,7 +44,7 @@ public function setUp(): void $this->app->instance(TwoFactorService::class, $this->twoFactorService); } - /** @test */ + #[Test] public function it_shows_otp_settings_page() { $this->twoFactorService @@ -65,7 +65,7 @@ public function it_shows_otp_settings_page() $response->assertViewHas('twoFactorStatus'); } - /** @test */ + #[Test] public function it_shows_otp_activation_page() { $response = $this->get(route('otp.activate')); @@ -75,7 +75,7 @@ public function it_shows_otp_activation_page() $response->assertViewHas('user', $this->user); } - /** @test */ + #[Test] public function it_can_setup_otp_with_email() { $this->otpService @@ -104,7 +104,7 @@ public function it_can_setup_otp_with_email() $this->assertEquals($this->user->email, session('temp_otp_config.identifier')); } - /** @test */ + #[Test] public function it_can_setup_otp_with_telegram() { $this->otpService @@ -133,7 +133,7 @@ public function it_can_setup_otp_with_telegram() $this->assertEquals($this->user->telegram_chat_id, session('temp_otp_config.identifier')); } - /** @test */ + #[Test] public function it_handles_otp_setup_failure() { $this->otpService @@ -156,7 +156,7 @@ public function it_handles_otp_setup_failure() ]); } - /** @test */ + #[Test] public function it_enforces_rate_limiting_on_otp_setup() { // Hit rate limit with new key format (includes IP and User-Agent) @@ -176,7 +176,7 @@ public function it_enforces_rate_limiting_on_otp_setup() ]); } - /** @test */ + #[Test] public function it_can_verify_otp_activation() { session(['temp_otp_config' => [ @@ -211,7 +211,7 @@ public function it_can_verify_otp_activation() $this->assertArrayNotHasKey('temp_otp_config', session()->all()); } - /** @test */ + #[Test] public function it_rejects_otp_verification_without_temp_config() { $response = $this->postJson(route('otp.verify-activation'), [ @@ -225,7 +225,7 @@ public function it_rejects_otp_verification_without_temp_config() ]); } - /** @test */ + #[Test] public function it_handles_otp_verification_failure() { session(['temp_otp_config' => [ @@ -253,7 +253,7 @@ public function it_handles_otp_verification_failure() ]); } - /** @test */ + #[Test] public function it_enforces_rate_limiting_on_otp_verification() { session(['temp_otp_config' => [ @@ -278,7 +278,7 @@ public function it_enforces_rate_limiting_on_otp_verification() ]); } - /** @test */ + #[Test] public function it_can_disable_otp() { $this->user->update([ @@ -301,7 +301,7 @@ public function it_can_disable_otp() $this->assertNull($this->user->otp_identifier); } - /** @test */ + #[Test] public function it_can_resend_otp() { session(['temp_otp_config' => [ @@ -329,7 +329,7 @@ public function it_can_resend_otp() ]); } - /** @test */ + #[Test] public function it_rejects_resend_without_temp_config() { $response = $this->postJson(route('otp.resend')); @@ -341,7 +341,7 @@ public function it_rejects_resend_without_temp_config() ]); } - /** @test */ + #[Test] public function it_enforces_rate_limiting_on_otp_resend() { session(['temp_otp_config' => [ @@ -364,7 +364,7 @@ public function it_enforces_rate_limiting_on_otp_resend() ]); } - /** @test */ + #[Test] public function it_can_disable_2fa_from_otp_controller() { $this->twoFactorService diff --git a/tests/Feature/OtpLoginControllerTest.php b/tests/Feature/OtpLoginControllerTest.php index 97b40166..3c525e49 100644 --- a/tests/Feature/OtpLoginControllerTest.php +++ b/tests/Feature/OtpLoginControllerTest.php @@ -55,7 +55,7 @@ public function setUp(): void RateLimiter::clear('otp:resend:' . $this->user->id . ':127.0.0.1:' . hash('xxh64', $userAgent)); } - /** @test */ + #[Test] public function it_shows_otp_login_form() { $response = $this->get('/login/otp'); @@ -66,7 +66,7 @@ public function it_shows_otp_login_form() $response->assertViewHas('captchaView'); } - /** @test */ + #[Test] public function it_shows_otp_login_form_with_captcha_when_threshold_reached() { // Simulate failed attempts to trigger captcha @@ -84,7 +84,7 @@ public function it_shows_otp_login_form_with_captcha_when_threshold_reached() $response->assertViewHas('captchaView', 'auth.captcha'); } - /** @test */ + #[Test] public function it_can_send_otp_with_email_identifier() { $this->otpService @@ -112,7 +112,7 @@ public function it_can_send_otp_with_email_identifier() $this->assertEquals('email', session('otp_login_channel')); } - /** @test */ + #[Test] public function it_can_send_otp_with_username_identifier() { // Clear rate limiter untuk test ini @@ -144,7 +144,7 @@ public function it_can_send_otp_with_username_identifier() $this->assertEquals('email', session('otp_login_channel')); } - /** @test */ + #[Test] public function it_fails_to_send_otp_for_nonexistent_user() { $response = $this->postJson('/login/otp/send', [ @@ -158,7 +158,7 @@ public function it_fails_to_send_otp_for_nonexistent_user() ]); } - /** @test */ + #[Test] public function it_fails_to_send_otp_for_user_without_otp_enabled() { $userWithoutOtp = User::factory()->create([ @@ -180,7 +180,7 @@ public function it_fails_to_send_otp_for_user_without_otp_enabled() ]); } - /** @test */ + #[Test] public function it_shows_captcha_after_two_failed_username_attempts() { // First failed attempt @@ -212,7 +212,7 @@ public function it_shows_captcha_after_two_failed_username_attempts() $response->assertViewHas('shouldShowCaptcha', true); } - /** @test */ + #[Test] public function it_handles_locked_account_when_sending_otp() { $this->user->lockAccount(); @@ -226,7 +226,7 @@ public function it_handles_locked_account_when_sending_otp() $this->assertStringContainsString('AKUN TERKUNCI', $response->json('message')); } - /** @test */ + #[Test] public function it_enforces_rate_limiting_when_sending_otp() { $key = 'otp:login:test@example.com:127.0.0.1:' . hash('xxh64', $this->app['request']->userAgent() ?? 'unknown'); @@ -246,7 +246,7 @@ public function it_enforces_rate_limiting_when_sending_otp() $this->assertStringContainsString('Terlalu banyak percobaan', $response->json('message')); } - /** @test */ + #[Test] public function it_can_verify_otp_and_login() { // Test ini memerlukan setup khusus karena controller memanggil method protected @@ -285,7 +285,7 @@ public function it_can_verify_otp_and_login() $this->assertNull(session('otp_login_channel')); } - /** @test */ + #[Test] public function it_redirects_to_2fa_after_otp_verification_when_2fa_enabled() { // Test ini memerlukan setup khusus karena controller memanggil method protected @@ -323,7 +323,7 @@ public function it_redirects_to_2fa_after_otp_verification_when_2fa_enabled() $this->assertNull(session('2fa_verified')); } - /** @test */ + #[Test] public function it_fails_to_verify_otp_without_session() { $response = $this->postJson('/login/otp/verify', [ @@ -339,7 +339,7 @@ public function it_fails_to_verify_otp_without_session() $this->assertGuest(); } - /** @test */ + #[Test] public function it_fails_to_verify_otp_with_invalid_code() { session([ @@ -369,7 +369,7 @@ public function it_fails_to_verify_otp_with_invalid_code() $this->assertGuest(); } - /** @test */ + #[Test] public function it_handles_locked_account_when_verifying_otp() { session([ @@ -390,7 +390,7 @@ public function it_handles_locked_account_when_verifying_otp() $this->assertGuest(); } - /** @test */ + #[Test] public function it_enforces_rate_limiting_when_verifying_otp() { session([ @@ -417,7 +417,7 @@ public function it_enforces_rate_limiting_when_verifying_otp() $this->assertGuest(); } - /** @test */ + #[Test] public function it_can_resend_otp() { session([ @@ -445,7 +445,7 @@ public function it_can_resend_otp() ]); } - /** @test */ + #[Test] public function it_fails_to_resend_otp_without_session() { $response = $this->postJson('/login/otp/resend'); @@ -457,7 +457,7 @@ public function it_fails_to_resend_otp_without_session() ]); } - /** @test */ + #[Test] public function it_handles_locked_account_when_resending_otp() { session([ @@ -474,7 +474,7 @@ public function it_handles_locked_account_when_resending_otp() $this->assertStringContainsString('AKUN TERKUNCI', $response->json('message')); } - /** @test */ + #[Test] public function it_enforces_rate_limiting_when_resending_otp() { session([ @@ -498,7 +498,7 @@ public function it_enforces_rate_limiting_when_resending_otp() $this->assertStringContainsString('detik sebelum mengirim ulang', $response->json('message')); } - /** @test */ + #[Test] public function it_resets_failed_attempts_on_successful_otp_verification() { // Test ini memerlukan setup khusus karena bergantung pada state user @@ -543,7 +543,7 @@ public function it_resets_failed_attempts_on_successful_otp_verification() $this->assertFalse($this->user->isLocked()); } - /** @test */ + #[Test] public function it_handles_telegram_otp_channel() { $telegramUser = User::factory()->create([ @@ -582,7 +582,7 @@ public function it_handles_telegram_otp_channel() $this->assertEquals('telegram', session('otp_login_channel')); } - /** @test */ + #[Test] public function it_handles_multiple_otp_channels() { $multiChannelUser = User::factory()->create([ diff --git a/tests/Feature/PageControllerCmsTest.php b/tests/Feature/PageControllerCmsTest.php index 9f6a4fc7..1abaa0a2 100644 --- a/tests/Feature/PageControllerCmsTest.php +++ b/tests/Feature/PageControllerCmsTest.php @@ -12,7 +12,7 @@ class PageControllerCmsTest extends BaseTestCase { use DatabaseTransactions; - /** @test */ + #[Test] public function halaman_index_dapat_diakses() { $response = $this->get(route('pages.index')); @@ -21,7 +21,7 @@ public function halaman_index_dapat_diakses() $response->assertViewIs('pages.index'); } - /** @test */ + #[Test] public function halaman_form_tambah_dapat_dibuka() { $response = $this->get(route('pages.create')); @@ -30,7 +30,7 @@ public function halaman_form_tambah_dapat_dibuka() $response->assertViewIs('pages.create'); } - /** @test */ + #[Test] public function halaman_baru_dapat_disimpan() { Storage::fake('public'); @@ -50,7 +50,7 @@ public function halaman_baru_dapat_disimpan() $this->assertDatabaseHas('pages', ['title' => 'Judul Halaman']); } - /** @test */ + #[Test] public function halaman_dapat_diedit() { $page = Page::factory()->create(); @@ -62,7 +62,7 @@ public function halaman_dapat_diedit() $response->assertViewHas('page'); } - /** @test */ + #[Test] public function halaman_dapat_diperbarui() { $page = Page::factory()->create([ @@ -83,7 +83,7 @@ public function halaman_dapat_diperbarui() $this->assertDatabaseHas('pages', ['title' => 'Baru']); } - /** @test */ + #[Test] public function halaman_dapat_dihapus() { $page = Page::factory()->create(); diff --git a/tests/Feature/PageControllerWebsiteTest.php b/tests/Feature/PageControllerWebsiteTest.php index 43cc295c..eeed1f95 100644 --- a/tests/Feature/PageControllerWebsiteTest.php +++ b/tests/Feature/PageControllerWebsiteTest.php @@ -11,7 +11,7 @@ class PageControllerWebsiteTest extends WebsiteTestCase { - /** @test */ + #[Test] public function it_can_access_index_page() { $response = $this->get(route('web.index')); @@ -19,7 +19,7 @@ public function it_can_access_index_page() $response->assertViewIs('web.index'); } - /** @test */ + #[Test] public function it_can_access_category_page() { $category = Category::factory()->create(); @@ -29,7 +29,7 @@ public function it_can_access_category_page() $response->assertViewHas('title', $category->name); } - /** @test */ + #[Test] public function it_can_access_page_detail() { $page = Page::factory()->create(); @@ -39,7 +39,7 @@ public function it_can_access_page_detail() $response->assertViewHas('object', $page); } - /** @test */ + #[Test] public function it_can_access_article_detail() { $article = Article::factory()->create(); @@ -49,7 +49,7 @@ public function it_can_access_article_detail() $response->assertViewHas('object', $article); } - /** @test */ + #[Test] public function it_can_access_sitemap() { $mock = Mockery::mock(SitemapService::class); diff --git a/tests/Feature/SlideControllerCmsTest.php b/tests/Feature/SlideControllerCmsTest.php index f109af25..2363a37e 100644 --- a/tests/Feature/SlideControllerCmsTest.php +++ b/tests/Feature/SlideControllerCmsTest.php @@ -12,7 +12,7 @@ class SlideControllerCmsTest extends BaseTestCase { use DatabaseTransactions; - /** @test */ + #[Test] public function halaman_index_slide_dapat_diakses() { $response = $this->get(route('slides.index')); @@ -20,7 +20,7 @@ public function halaman_index_slide_dapat_diakses() $response->assertViewIs('slides.index'); } - /** @test */ + #[Test] public function form_tambah_slide_dapat_diakses() { $response = $this->get(route('slides.create')); @@ -28,7 +28,7 @@ public function form_tambah_slide_dapat_diakses() $response->assertViewIs('slides.create'); } - /** @test */ + #[Test] public function slide_baru_dapat_disimpan() { Storage::fake('public'); @@ -46,7 +46,7 @@ public function slide_baru_dapat_disimpan() $this->assertDatabaseHas('slides', ['title' => 'Slide Baru']); } - /** @test */ + #[Test] public function form_edit_slide_dapat_diakses() { $slide = Slide::factory()->create(); @@ -58,7 +58,7 @@ public function form_edit_slide_dapat_diakses() $response->assertViewHas('slide'); } - /** @test */ + #[Test] public function slide_dapat_diperbarui() { $slide = Slide::factory()->create([ @@ -77,7 +77,7 @@ public function slide_dapat_diperbarui() $this->assertDatabaseHas('slides', ['title' => 'Slide Diperbarui']); } - /** @test */ + #[Test] public function slide_dapat_dihapus() { $slide = Slide::factory()->create(); diff --git a/tests/Feature/StatistikPengunjungCmsControllerTest.php b/tests/Feature/StatistikPengunjungCmsControllerTest.php index c0c3260f..ce040c57 100644 --- a/tests/Feature/StatistikPengunjungCmsControllerTest.php +++ b/tests/Feature/StatistikPengunjungCmsControllerTest.php @@ -10,7 +10,7 @@ class StatistikPengunjungCmsControllerTest extends BaseTestCase { use DatabaseTransactions; - /** @test */ + #[Test] public function halaman_statistik_pengunjung_dapat_diakses() { // Arrange: siapkan data kunjungan palsu diff --git a/tests/Feature/TwoFactorControllerTest.php b/tests/Feature/TwoFactorControllerTest.php index 1ea8bbc9..3cd6ec8e 100644 --- a/tests/Feature/TwoFactorControllerTest.php +++ b/tests/Feature/TwoFactorControllerTest.php @@ -43,7 +43,7 @@ public function setUp(): void $this->app->instance(OtpService::class, $this->otpService); } - /** @test */ + #[Test] public function it_shows_2fa_activation_page() { $response = $this->get(route('2fa.activate')); @@ -53,7 +53,7 @@ public function it_shows_2fa_activation_page() $response->assertViewHas('user', $this->user); } - /** @test */ + #[Test] public function it_can_enable_2fa_with_email() { $this->otpService @@ -81,7 +81,7 @@ public function it_can_enable_2fa_with_email() $this->assertEquals($this->user->email, session('temp_2fa_config.identifier')); } - /** @test */ + #[Test] public function it_can_enable_2fa_with_telegram() { $this->otpService @@ -109,7 +109,7 @@ public function it_can_enable_2fa_with_telegram() $this->assertEquals($this->user->telegram_chat_id, session('temp_2fa_config.identifier')); } - /** @test */ + #[Test] public function it_handles_2fa_enable_failure() { $this->otpService @@ -132,7 +132,7 @@ public function it_handles_2fa_enable_failure() ]); } - /** @test */ + #[Test] public function it_enforces_rate_limiting_on_2fa_enable() { // Hit rate limit with new key format (includes IP and User-Agent) @@ -155,7 +155,7 @@ public function it_enforces_rate_limiting_on_2fa_enable() ]); } - /** @test */ + #[Test] public function it_can_verify_and_enable_2fa() { session(['temp_2fa_config' => [ @@ -194,7 +194,7 @@ public function it_can_verify_and_enable_2fa() $this->assertArrayNotHasKey('temp_2fa_config', session()->all()); } - /** @test */ + #[Test] public function it_rejects_2fa_verification_without_temp_config() { $response = $this->postJson(route('2fa.verify'), [ @@ -208,7 +208,7 @@ public function it_rejects_2fa_verification_without_temp_config() ]); } - /** @test */ + #[Test] public function it_handles_2fa_verification_failure() { session(['temp_2fa_config' => [ @@ -237,7 +237,7 @@ public function it_handles_2fa_verification_failure() $response->assertJsonPath('progressive_delay', 2); } - /** @test */ + #[Test] public function it_enforces_rate_limiting_on_2fa_verification() { session(['temp_2fa_config' => [ @@ -265,7 +265,7 @@ public function it_enforces_rate_limiting_on_2fa_verification() ]); } - /** @test */ + #[Test] public function it_can_disable_2fa() { $this->twoFactorService @@ -283,7 +283,7 @@ public function it_can_disable_2fa() ]); } - /** @test */ + #[Test] public function it_can_resend_2fa_verification_code() { session(['temp_2fa_config' => [ @@ -311,7 +311,7 @@ public function it_can_resend_2fa_verification_code() ]); } - /** @test */ + #[Test] public function it_rejects_resend_without_temp_config() { $response = $this->postJson(route('2fa.resend')); @@ -323,7 +323,7 @@ public function it_rejects_resend_without_temp_config() ]); } - /** @test */ + #[Test] public function it_enforces_rate_limiting_on_2fa_resend() { session(['temp_2fa_config' => [ @@ -346,7 +346,7 @@ public function it_enforces_rate_limiting_on_2fa_resend() ]); } - /** @test */ + #[Test] public function it_shows_2fa_challenge_page() { $this->user->update([ @@ -382,7 +382,7 @@ public function it_shows_2fa_challenge_page() $response->assertViewIs('auth.2fa-challenge'); } - /** @test */ + #[Test] public function it_redirects_to_dashboard_if_2fa_not_enabled() { $this->user->update(['2fa_enabled' => false]); @@ -393,7 +393,7 @@ public function it_redirects_to_dashboard_if_2fa_not_enabled() $response->assertRedirect(route('dasbor')); } - /** @test */ + #[Test] public function it_can_verify_2fa_challenge() { $this->user->update([ @@ -427,7 +427,7 @@ public function it_can_verify_2fa_challenge() $this->assertTrue(session('2fa_verified')); } - /** @test */ + #[Test] public function it_handles_2fa_challenge_verification_failure() { $this->user->update([ @@ -457,7 +457,7 @@ public function it_handles_2fa_challenge_verification_failure() $response->assertJsonPath('progressive_delay', 2); } - /** @test */ + #[Test] public function it_enforces_rate_limiting_on_2fa_challenge() { $this->user->update([ diff --git a/tests/Feature/WilayahFilterParameterTest.php b/tests/Feature/WilayahFilterParameterTest.php index 4464f8d4..9decab18 100644 --- a/tests/Feature/WilayahFilterParameterTest.php +++ b/tests/Feature/WilayahFilterParameterTest.php @@ -6,7 +6,7 @@ class WilayahFilterParameterTest extends BaseTestCase { - /** @test */ + #[Test] public function test_meta_tag_identitas_openkab_exists() { $response = $this->get(route('dasbor')); @@ -15,7 +15,7 @@ public function test_meta_tag_identitas_openkab_exists() $response->assertSee('name="identitas-openkab"', false); } - /** @test */ + #[Test] public function test_wilayah_filter_js_contains_kode_kabupaten_param() { $response = $this->get(route('dasbor')); @@ -24,7 +24,7 @@ public function test_wilayah_filter_js_contains_kode_kabupaten_param() $response->assertSee('kode_kabupaten', false); } - /** @test */ + #[Test] public function test_wilayah_filter_js_contains_filter_kode_kabupaten_param() { $response = $this->get(route('dasbor')); @@ -33,7 +33,7 @@ public function test_wilayah_filter_js_contains_filter_kode_kabupaten_param() $response->assertSee('filter[kode_kabupaten]', false); } - /** @test */ + #[Test] public function test_summary_api_url_contains_kode_kabupaten_param() { $response = $this->get(route('dasbor') . '?page=summary'); @@ -41,7 +41,7 @@ public function test_summary_api_url_contains_kode_kabupaten_param() $response->assertStatus(200); } - /** @test */ + #[Test] public function test_peta_api_url_contains_kode_kabupaten_param() { $response = $this->get(route('dasbor') . '?page=peta'); diff --git a/tests/Unit/ArtikelServiceTest.php b/tests/Unit/ArtikelServiceTest.php index 532a6214..f1d70b14 100644 --- a/tests/Unit/ArtikelServiceTest.php +++ b/tests/Unit/ArtikelServiceTest.php @@ -18,13 +18,13 @@ public function setUp(): void $this->service = new ArtikelService(); } - /** @test */ + #[Test] public function it_can_instantiate_artikel_service() { $this->assertInstanceOf(ArtikelService::class, $this->service); } - /** @test */ + #[Test] public function it_builds_cache_key_correctly() { $reflection = new \ReflectionClass($this->service); @@ -37,7 +37,7 @@ public function it_builds_cache_key_correctly() $this->assertStringContainsString('artikel', $cacheKey); } - /** @test */ + #[Test] public function clear_cache_removes_cached_data() { $cacheKey = 'test_artikel_cache'; @@ -50,7 +50,7 @@ public function clear_cache_removes_cached_data() $this->assertFalse(Cache::has($cacheKey)); } - /** @test */ + #[Test] public function it_has_cache_ttl_property() { $reflection = new \ReflectionClass($this->service); @@ -63,7 +63,7 @@ public function it_has_cache_ttl_property() $this->assertIsInt($ttl); } - /** @test */ + #[Test] public function artikel_method_returns_collection() { // Mock API response untuk testing @@ -72,19 +72,19 @@ public function artikel_method_returns_collection() $this->assertTrue(method_exists($this->service, 'artikel')); } - /** @test */ + #[Test] public function artikel_by_id_method_exists() { $this->assertTrue(method_exists($this->service, 'artikelById')); } - /** @test */ + #[Test] public function clear_cache_method_exists() { $this->assertTrue(method_exists($this->service, 'clearCache')); } - /** @test */ + #[Test] public function service_extends_base_api_service() { $this->assertInstanceOf(\App\Services\BaseApiService::class, $this->service); diff --git a/tests/Unit/DataPresisiPanganDetailViewTest.php b/tests/Unit/DataPresisiPanganDetailViewTest.php index a0f2e7d7..5b53ef9b 100644 --- a/tests/Unit/DataPresisiPanganDetailViewTest.php +++ b/tests/Unit/DataPresisiPanganDetailViewTest.php @@ -11,7 +11,7 @@ class DataPresisiPanganDetailViewTest extends BaseTestCase { use DatabaseTransactions; - /** @test */ + #[Test] public function it_renders_longitude_latitude_columns_in_datatable_header() { // Mock data untuk testing view @@ -33,7 +33,7 @@ public function it_renders_longitude_latitude_columns_in_datatable_header() $this->assertStringContainsString('LATITUDE', $html); } - /** @test */ + #[Test] public function it_configures_datatable_columns_for_longitude_latitude() { // Mock data untuk testing view @@ -58,7 +58,7 @@ public function it_configures_datatable_columns_for_longitude_latitude() $this->assertStringContainsString("orderable: false", $html); } - /** @test */ + #[Test] public function it_includes_longitude_latitude_in_correct_column_order() { // Mock data untuk testing view @@ -85,7 +85,7 @@ public function it_includes_longitude_latitude_in_correct_column_order() $this->assertLessThan($latitudePos, $longitudePos, 'Longitude should appear before latitude in column configuration'); } - /** @test */ + #[Test] public function it_configures_api_filter_with_rtm_id_for_coordinate_data() { // Mock data untuk testing view @@ -109,7 +109,7 @@ public function it_configures_api_filter_with_rtm_id_for_coordinate_data() $this->assertStringContainsString('/api/v1/data-presisi/pangan', $html); } - /** @test */ + #[Test] public function it_renders_household_info_table_with_correct_data_structure() { // Mock data untuk testing view @@ -138,7 +138,7 @@ public function it_renders_household_info_table_with_correct_data_structure() $this->assertStringContainsString('No Kartu Rumah Tangga (KRT)', $html); } - /** @test */ + #[Test] public function it_has_proper_datatable_initialization_for_coordinate_display() { // Mock data untuk testing view @@ -166,7 +166,7 @@ public function it_has_proper_datatable_initialization_for_coordinate_display() $this->assertStringContainsString("data: 'attributes.latitude'", $html); } - /** @test */ + #[Test] public function it_includes_all_expected_column_headers_in_correct_order() { // Mock data untuk testing view @@ -222,7 +222,7 @@ public function it_includes_all_expected_column_headers_in_correct_order() $this->assertLessThan($latitudeHeaderPos, $longitudeHeaderPos); } - /** @test */ + #[Test] public function it_validates_coordinate_columns_are_not_orderable() { // Mock data untuk testing view From 6177e341d7c04bcfe30eb91824263a411184f164 Mon Sep 17 00:00:00 2001 From: Ahmad Afandi Date: Fri, 5 Jun 2026 07:16:06 +0700 Subject: [PATCH 10/43] perbaiki test --- tests/Feature/BruteForceSimulationTest.php | 50 +++++----------------- 1 file changed, 10 insertions(+), 40 deletions(-) diff --git a/tests/Feature/BruteForceSimulationTest.php b/tests/Feature/BruteForceSimulationTest.php index d914703e..772800cb 100644 --- a/tests/Feature/BruteForceSimulationTest.php +++ b/tests/Feature/BruteForceSimulationTest.php @@ -39,10 +39,7 @@ protected function setUp(): void ]); } - /** - * @test - * Simulasi brute force pada API login - akun terkunci setelah 5 gagal attempt - */ + #[Test] public function account_locked_after_multiple_failed_api_login_attempts() { Config::set('app.account_lockout_max_attempts', 5); @@ -71,10 +68,7 @@ public function account_locked_after_multiple_failed_api_login_attempts() $this->assertNotNull($this->user->lockout_expires_at); } - /** - * @test - * Simulasi brute force - akun terkunci tidak bisa login meski password benar - */ + #[Test] public function locked_account_rejects_even_correct_password() { Config::set('app.account_lockout_max_attempts', 3); @@ -105,10 +99,7 @@ public function locked_account_rejects_even_correct_password() $this->assertStringContainsString('TERKUNCI', $response->json('message')); } - /** - * @test - * Simulasi successful login resets failed attempts - verify reset method works - */ + #[Test] public function successful_login_resets_failed_attempts() { // Test ini memverifikasi bahwa method resetFailedLogins() bekerja @@ -135,10 +126,7 @@ public function successful_login_resets_failed_attempts() $this->assertFalse($this->user->isLocked()); } - /** - * @test - * Simulasi distributed attack - different IPs tapi same User-Agent tetap di-rate limit - */ + #[Test] public function resists_distributed_attack_with_same_user_agent() { Config::set('rate-limiter.enabled', true); @@ -176,10 +164,7 @@ public function resists_distributed_attack_with_same_user_agent() ); } - /** - * @test - * Simulasi VPN rotation - different IPs tapi same browser fingerprint tetap di-rate limit - */ + #[Test] public function resists_vpn_ip_rotation_attack() { Config::set('rate-limiter.enabled', true); @@ -218,10 +203,7 @@ public function resists_vpn_ip_rotation_attack() ); } - /** - * @test - * Simulasi brute force dengan username (bukan email) - */ + #[Test] public function brute_force_protection_works_with_username() { Config::set('app.account_lockout_max_attempts', 3); @@ -251,10 +233,7 @@ public function brute_force_protection_works_with_username() $response->assertJson(['locked' => true]); } - /** - * @test - * Simulasi progressive delay - delay meningkat setiap failed attempt - */ + #[Test] public function progressive_delay_increases_with_failed_attempts() { Config::set('app.progressive_delay_base_seconds', 2); @@ -285,10 +264,7 @@ public function progressive_delay_increases_with_failed_attempts() } } - /** - * @test - * Simulasi lockout expiration - verify lockout has expiration time set - */ + #[Test] public function account_lockout_has_expiration_time() { // Clear cache dari test sebelumnya @@ -315,10 +291,7 @@ public function account_lockout_has_expiration_time() $this->assertLessThanOrEqual(now()->addMinutes(15), $this->user->lockout_expires_at); } - /** - * @test - * Simulasi different accounts independent lockout - verify second account not locked - */ + #[Test] public function different_accounts_have_independent_lockout() { // Clear cache dari test sebelumnya @@ -356,10 +329,7 @@ public function different_accounts_have_independent_lockout() $this->assertFalse($user2->isLocked()); } - /** - * @test - * Simulasi rate limit response headers - */ + #[Test] public function rate_limit_responses_include_proper_headers() { Config::set('rate-limiter.enabled', true); From 782901094ceabd09ba4d935637baf6f69cb4722a Mon Sep 17 00:00:00 2001 From: Ahmad Afandi Date: Fri, 5 Jun 2026 07:22:53 +0700 Subject: [PATCH 11/43] verbose test --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c048e20d..5b063646 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -59,4 +59,4 @@ jobs: DB_USERNAME: root DB_PASSWORD: secret - run: php artisan migrate && php artisan test + run: php artisan migrate && php artisan test --verbose From 69a51da67767930cfeede7fa03f4720ba34b1db1 Mon Sep 17 00:00:00 2001 From: Ahmad Afandi Date: Fri, 5 Jun 2026 08:00:17 +0700 Subject: [PATCH 12/43] perbaikan test --- .github/workflows/test.yml | 2 +- tests/Feature/AdminWebControllerTest.php | 1 + tests/Feature/ArticleControllerCmsTest.php | 1 + tests/Feature/ArtikelControllerTest.php | 1 + tests/Feature/ArtikelUploadTest.php | 1 + tests/Feature/ArtikelWebTest.php | 1 + tests/Feature/BantuanControllerTest.php | 1 + tests/Feature/BruteForceSimulationTest.php | 1 + tests/Feature/CategoryControllerCmsTest.php | 1 + tests/Feature/CspPolicyTest.php | 1 + tests/Feature/DasborControllerTest.php | 1 + tests/Feature/DasborDemografiControllerTest.php | 1 + tests/Feature/DataPresisiExcelDownloadTest.php | 1 + tests/Feature/DataPresisiFilterTahunJavaScriptTest.php | 1 + tests/Feature/DataPresisiJaminanSosialControllerTest.php | 1 + tests/Feature/DataPresisiKetenagakerjaanControllerTest.php | 1 + tests/Feature/DataPresisiLaporanTest.php | 1 + tests/Feature/DataPresisiPanganControllerTest.php | 1 + tests/Feature/DataPresisiPapanControllerTest.php | 1 + tests/Feature/DataPresisiPendidikanControllerTest.php | 1 + tests/Feature/DataPresisiSandangControllerTest.php | 1 + tests/Feature/DataPresisiSeniBudayaControllerTest.php | 1 + tests/Feature/DataPresisiSeniBudayaExcelDownloadTest.php | 1 + .../Feature/DetailDataPresisiAktivitasKeagamaanRequestTest.php | 1 + tests/Feature/DownloadControllerCmsTest.php | 1 + tests/Feature/FilterTahunTest.php | 1 + tests/Feature/GlobalRateLimiterTest.php | 1 + tests/Feature/GroupMenuDisplayTest.php | 1 + tests/Feature/Http/Controllers/ApiProxyControllerTest.php | 1 + .../Controllers/DataPresisiAktivitasKeagamaanControllerTest.php | 1 + tests/Feature/Http/Controllers/ImageProxyControllerTest.php | 1 + .../Feature/Http/Controllers/LaporanDesaAktifControllerTest.php | 1 + tests/Feature/IdentitasControllerApiTest.php | 1 + tests/Feature/IdorPreventionTest.php | 1 + tests/Feature/JaminanSosialTest.php | 1 + tests/Feature/LaporanBulananDetailExportTest.php | 1 + tests/Feature/LoginControllerTest.php | 1 + tests/Feature/MenuControllerCmsTest.php | 1 + tests/Feature/OtpControllerTest.php | 1 + tests/Feature/OtpLoginControllerTest.php | 1 + tests/Feature/PageControllerCmsTest.php | 1 + tests/Feature/PengaturanGroupTeamTest.php | 1 + tests/Feature/PengaturanSettingTest.php | 1 + tests/Feature/Services/ApiProxyServiceTest.php | 1 + tests/Feature/SlideControllerCmsTest.php | 1 + tests/Feature/StatistikPengunjungCmsControllerTest.php | 1 + tests/Feature/StrongPasswordRuleTest.php | 1 + tests/Feature/TwoFactorControllerTest.php | 1 + tests/Feature/WilayahFilterParameterTest.php | 1 + tests/Feature/XssPreventionTest.php | 1 + tests/Unit/ArtikelServiceTest.php | 2 +- tests/Unit/DataPresisiPanganDetailViewTest.php | 1 + 52 files changed, 52 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5b063646..c048e20d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -59,4 +59,4 @@ jobs: DB_USERNAME: root DB_PASSWORD: secret - run: php artisan migrate && php artisan test --verbose + run: php artisan migrate && php artisan test diff --git a/tests/Feature/AdminWebControllerTest.php b/tests/Feature/AdminWebControllerTest.php index ab2754a2..e5b523c6 100644 --- a/tests/Feature/AdminWebControllerTest.php +++ b/tests/Feature/AdminWebControllerTest.php @@ -2,6 +2,7 @@ namespace Tests\Feature; +use PHPUnit\Framework\Attributes\Test; use Tests\BaseTestCase; class AdminWebControllerTest extends BaseTestCase diff --git a/tests/Feature/ArticleControllerCmsTest.php b/tests/Feature/ArticleControllerCmsTest.php index e7243f7e..a98d7724 100644 --- a/tests/Feature/ArticleControllerCmsTest.php +++ b/tests/Feature/ArticleControllerCmsTest.php @@ -7,6 +7,7 @@ use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Http\UploadedFile; use Illuminate\Support\Facades\Storage; +use PHPUnit\Framework\Attributes\Test; use Tests\BaseTestCase; class ArticleControllerCmsTest extends BaseTestCase diff --git a/tests/Feature/ArtikelControllerTest.php b/tests/Feature/ArtikelControllerTest.php index 5e281960..41bb765e 100644 --- a/tests/Feature/ArtikelControllerTest.php +++ b/tests/Feature/ArtikelControllerTest.php @@ -2,6 +2,7 @@ namespace Tests\Feature; +use PHPUnit\Framework\Attributes\Test; use Tests\BaseTestCase; class ArtikelControllerTest extends BaseTestCase diff --git a/tests/Feature/ArtikelUploadTest.php b/tests/Feature/ArtikelUploadTest.php index 8aaec61b..ee07c2df 100644 --- a/tests/Feature/ArtikelUploadTest.php +++ b/tests/Feature/ArtikelUploadTest.php @@ -4,6 +4,7 @@ use Illuminate\Http\UploadedFile; use Illuminate\Support\Facades\Storage; +use PHPUnit\Framework\Attributes\Test; use Tests\BaseTestCase; class ArtikelUploadTest extends BaseTestCase diff --git a/tests/Feature/ArtikelWebTest.php b/tests/Feature/ArtikelWebTest.php index 45a2688c..824eb172 100644 --- a/tests/Feature/ArtikelWebTest.php +++ b/tests/Feature/ArtikelWebTest.php @@ -4,6 +4,7 @@ use App\Services\ArtikelService; use Mockery; +use PHPUnit\Framework\Attributes\Test; use Tests\BaseTestCase; class ArtikelWebTest extends BaseTestCase diff --git a/tests/Feature/BantuanControllerTest.php b/tests/Feature/BantuanControllerTest.php index d69fe2a0..e03d3b52 100644 --- a/tests/Feature/BantuanControllerTest.php +++ b/tests/Feature/BantuanControllerTest.php @@ -3,6 +3,7 @@ namespace Tests\Feature; use Illuminate\Foundation\Testing\DatabaseTransactions; +use PHPUnit\Framework\Attributes\Test; use Tests\BaseTestCase; class BantuanControllerTest extends BaseTestCase diff --git a/tests/Feature/BruteForceSimulationTest.php b/tests/Feature/BruteForceSimulationTest.php index 772800cb..0c0825c3 100644 --- a/tests/Feature/BruteForceSimulationTest.php +++ b/tests/Feature/BruteForceSimulationTest.php @@ -8,6 +8,7 @@ use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\RateLimiter; +use PHPUnit\Framework\Attributes\Test; use Tests\TestCase; /** diff --git a/tests/Feature/CategoryControllerCmsTest.php b/tests/Feature/CategoryControllerCmsTest.php index 4da89278..c73a6818 100644 --- a/tests/Feature/CategoryControllerCmsTest.php +++ b/tests/Feature/CategoryControllerCmsTest.php @@ -5,6 +5,7 @@ use App\Models\CMS\Article; use App\Models\CMS\Category; use Illuminate\Foundation\Testing\DatabaseTransactions; +use PHPUnit\Framework\Attributes\Test; use Tests\BaseTestCase; class CategoryControllerCmsTest extends BaseTestCase diff --git a/tests/Feature/CspPolicyTest.php b/tests/Feature/CspPolicyTest.php index c890b109..ce1285b1 100644 --- a/tests/Feature/CspPolicyTest.php +++ b/tests/Feature/CspPolicyTest.php @@ -5,6 +5,7 @@ use App\Policies\CustomCspPreset; use Spatie\Csp\Policy; use Spatie\Csp\Preset; +use PHPUnit\Framework\Attributes\Test; use Tests\TestCase; class CspPolicyTest extends TestCase diff --git a/tests/Feature/DasborControllerTest.php b/tests/Feature/DasborControllerTest.php index 394ea7c1..3bb81a38 100644 --- a/tests/Feature/DasborControllerTest.php +++ b/tests/Feature/DasborControllerTest.php @@ -4,6 +4,7 @@ use Illuminate\Foundation\Testing\DatabaseTransactions; use Mockery; +use PHPUnit\Framework\Attributes\Test; use Tests\BaseTestCase; class DasborControllerTest extends BaseTestCase diff --git a/tests/Feature/DasborDemografiControllerTest.php b/tests/Feature/DasborDemografiControllerTest.php index 2c6e0a7a..b6480ec9 100644 --- a/tests/Feature/DasborDemografiControllerTest.php +++ b/tests/Feature/DasborDemografiControllerTest.php @@ -4,6 +4,7 @@ use App\Models\Enums\StatistikPendudukEnum; use Illuminate\Foundation\Testing\DatabaseTransactions; +use PHPUnit\Framework\Attributes\Test; use Tests\BaseTestCase; class DasborDemografiControllerTest extends BaseTestCase diff --git a/tests/Feature/DataPresisiExcelDownloadTest.php b/tests/Feature/DataPresisiExcelDownloadTest.php index d311e2d6..b1d24760 100644 --- a/tests/Feature/DataPresisiExcelDownloadTest.php +++ b/tests/Feature/DataPresisiExcelDownloadTest.php @@ -2,6 +2,7 @@ namespace Tests\Feature; +use PHPUnit\Framework\Attributes\Test; use Tests\BaseTestCase; class DataPresisiExcelDownloadTest extends BaseTestCase diff --git a/tests/Feature/DataPresisiFilterTahunJavaScriptTest.php b/tests/Feature/DataPresisiFilterTahunJavaScriptTest.php index dace1394..8aa1f85b 100644 --- a/tests/Feature/DataPresisiFilterTahunJavaScriptTest.php +++ b/tests/Feature/DataPresisiFilterTahunJavaScriptTest.php @@ -2,6 +2,7 @@ namespace Tests\Feature; +use PHPUnit\Framework\Attributes\Test; use Tests\BaseTestCase; class DataPresisiFilterTahunJavaScriptTest extends BaseTestCase diff --git a/tests/Feature/DataPresisiJaminanSosialControllerTest.php b/tests/Feature/DataPresisiJaminanSosialControllerTest.php index f8bb37e4..b028c834 100644 --- a/tests/Feature/DataPresisiJaminanSosialControllerTest.php +++ b/tests/Feature/DataPresisiJaminanSosialControllerTest.php @@ -4,6 +4,7 @@ use Illuminate\Foundation\Testing\WithoutMiddleware; use Illuminate\Support\Facades\View; +use PHPUnit\Framework\Attributes\Test; use Tests\BaseTestCase; use Tests\TestCase; diff --git a/tests/Feature/DataPresisiKetenagakerjaanControllerTest.php b/tests/Feature/DataPresisiKetenagakerjaanControllerTest.php index 37af84e7..c28c8991 100644 --- a/tests/Feature/DataPresisiKetenagakerjaanControllerTest.php +++ b/tests/Feature/DataPresisiKetenagakerjaanControllerTest.php @@ -2,6 +2,7 @@ namespace Tests\Feature; +use PHPUnit\Framework\Attributes\Test; use Tests\BaseTestCase; class DataPresisiKetenagakerjaanControllerTest extends BaseTestCase diff --git a/tests/Feature/DataPresisiLaporanTest.php b/tests/Feature/DataPresisiLaporanTest.php index f4b9f1c8..ed457afb 100644 --- a/tests/Feature/DataPresisiLaporanTest.php +++ b/tests/Feature/DataPresisiLaporanTest.php @@ -2,6 +2,7 @@ namespace Tests\Feature; +use PHPUnit\Framework\Attributes\Test; use Tests\BaseTestCase; class DataPresisiLaporanTest extends BaseTestCase diff --git a/tests/Feature/DataPresisiPanganControllerTest.php b/tests/Feature/DataPresisiPanganControllerTest.php index c126e23a..c422ce1b 100644 --- a/tests/Feature/DataPresisiPanganControllerTest.php +++ b/tests/Feature/DataPresisiPanganControllerTest.php @@ -2,6 +2,7 @@ namespace Tests\Feature; +use PHPUnit\Framework\Attributes\Test; use Tests\BaseTestCase; class DataPresisiPanganControllerTest extends BaseTestCase diff --git a/tests/Feature/DataPresisiPapanControllerTest.php b/tests/Feature/DataPresisiPapanControllerTest.php index 4a7ff597..46fe21be 100644 --- a/tests/Feature/DataPresisiPapanControllerTest.php +++ b/tests/Feature/DataPresisiPapanControllerTest.php @@ -4,6 +4,7 @@ use App\Models\User; use Illuminate\Foundation\Testing\DatabaseTransactions; +use PHPUnit\Framework\Attributes\Test; use Tests\BaseTestCase; class DataPresisiPapanControllerTest extends BaseTestCase diff --git a/tests/Feature/DataPresisiPendidikanControllerTest.php b/tests/Feature/DataPresisiPendidikanControllerTest.php index 7419ba30..e3228206 100644 --- a/tests/Feature/DataPresisiPendidikanControllerTest.php +++ b/tests/Feature/DataPresisiPendidikanControllerTest.php @@ -6,6 +6,7 @@ use App\Http\Requests\DetailDataPresisiPendidikanRequest; use Illuminate\Http\Request; use Illuminate\View\View; +use PHPUnit\Framework\Attributes\Test; use Tests\BaseTestCase; class DataPresisiPendidikanControllerTest extends BaseTestCase diff --git a/tests/Feature/DataPresisiSandangControllerTest.php b/tests/Feature/DataPresisiSandangControllerTest.php index 7ff28442..ba51618e 100644 --- a/tests/Feature/DataPresisiSandangControllerTest.php +++ b/tests/Feature/DataPresisiSandangControllerTest.php @@ -5,6 +5,7 @@ use App\Http\Controllers\DataPresisiSandangController; use Illuminate\Http\Request; use Illuminate\View\View; +use PHPUnit\Framework\Attributes\Test; use Tests\BaseTestCase; class DataPresisiSandangControllerTest extends BaseTestCase diff --git a/tests/Feature/DataPresisiSeniBudayaControllerTest.php b/tests/Feature/DataPresisiSeniBudayaControllerTest.php index e48507a5..8e7c76f7 100644 --- a/tests/Feature/DataPresisiSeniBudayaControllerTest.php +++ b/tests/Feature/DataPresisiSeniBudayaControllerTest.php @@ -6,6 +6,7 @@ use App\Http\Requests\DetailDataPresisiSeniBudayaRequest; use Illuminate\Http\Request; use Illuminate\View\View; +use PHPUnit\Framework\Attributes\Test; use Tests\BaseTestCase; /** diff --git a/tests/Feature/DataPresisiSeniBudayaExcelDownloadTest.php b/tests/Feature/DataPresisiSeniBudayaExcelDownloadTest.php index 02297d39..211fd61d 100644 --- a/tests/Feature/DataPresisiSeniBudayaExcelDownloadTest.php +++ b/tests/Feature/DataPresisiSeniBudayaExcelDownloadTest.php @@ -2,6 +2,7 @@ namespace Tests\Feature; +use PHPUnit\Framework\Attributes\Test; use Tests\BaseTestCase; class DataPresisiSeniBudayaExcelDownloadTest extends BaseTestCase diff --git a/tests/Feature/DetailDataPresisiAktivitasKeagamaanRequestTest.php b/tests/Feature/DetailDataPresisiAktivitasKeagamaanRequestTest.php index c903dc95..49638848 100644 --- a/tests/Feature/DetailDataPresisiAktivitasKeagamaanRequestTest.php +++ b/tests/Feature/DetailDataPresisiAktivitasKeagamaanRequestTest.php @@ -2,6 +2,7 @@ namespace Tests\Feature; +use PHPUnit\Framework\Attributes\Test; use Tests\BaseTestCase; class DetailDataPresisiAktivitasKeagamaanRequestTest extends BaseTestCase diff --git a/tests/Feature/DownloadControllerCmsTest.php b/tests/Feature/DownloadControllerCmsTest.php index cab64373..8d6df82a 100644 --- a/tests/Feature/DownloadControllerCmsTest.php +++ b/tests/Feature/DownloadControllerCmsTest.php @@ -6,6 +6,7 @@ use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Http\UploadedFile; use Illuminate\Support\Facades\Storage; +use PHPUnit\Framework\Attributes\Test; use Tests\BaseTestCase; class DownloadControllerCmsTest extends BaseTestCase diff --git a/tests/Feature/FilterTahunTest.php b/tests/Feature/FilterTahunTest.php index 2e11545f..4d8cb7c7 100644 --- a/tests/Feature/FilterTahunTest.php +++ b/tests/Feature/FilterTahunTest.php @@ -2,6 +2,7 @@ namespace Tests\Feature; +use PHPUnit\Framework\Attributes\Test; use Tests\BaseTestCase; class FilterTahunTest extends BaseTestCase diff --git a/tests/Feature/GlobalRateLimiterTest.php b/tests/Feature/GlobalRateLimiterTest.php index 0491dbfd..b9a346ab 100644 --- a/tests/Feature/GlobalRateLimiterTest.php +++ b/tests/Feature/GlobalRateLimiterTest.php @@ -5,6 +5,7 @@ use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Config; +use PHPUnit\Framework\Attributes\Test; use Tests\TestCase; class GlobalRateLimiterTest extends TestCase diff --git a/tests/Feature/GroupMenuDisplayTest.php b/tests/Feature/GroupMenuDisplayTest.php index 902533af..ed498c8e 100644 --- a/tests/Feature/GroupMenuDisplayTest.php +++ b/tests/Feature/GroupMenuDisplayTest.php @@ -4,6 +4,7 @@ use App\Models\Team; use App\Models\User; +use PHPUnit\Framework\Attributes\Test; use Tests\BaseTestCase; class GroupMenuDisplayTest extends BaseTestCase diff --git a/tests/Feature/Http/Controllers/ApiProxyControllerTest.php b/tests/Feature/Http/Controllers/ApiProxyControllerTest.php index 053aa72a..b80fde63 100644 --- a/tests/Feature/Http/Controllers/ApiProxyControllerTest.php +++ b/tests/Feature/Http/Controllers/ApiProxyControllerTest.php @@ -7,6 +7,7 @@ use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\Http; +use PHPUnit\Framework\Attributes\Test; use Tests\BaseTestCase; class ApiProxyControllerTest extends BaseTestCase diff --git a/tests/Feature/Http/Controllers/DataPresisiAktivitasKeagamaanControllerTest.php b/tests/Feature/Http/Controllers/DataPresisiAktivitasKeagamaanControllerTest.php index f52a8bff..17af0073 100644 --- a/tests/Feature/Http/Controllers/DataPresisiAktivitasKeagamaanControllerTest.php +++ b/tests/Feature/Http/Controllers/DataPresisiAktivitasKeagamaanControllerTest.php @@ -2,6 +2,7 @@ namespace Tests\Feature\Http\Controllers; +use PHPUnit\Framework\Attributes\Test; use Tests\BaseTestCase; class DataPresisiAktivitasKeagamaanControllerTest extends BaseTestCase diff --git a/tests/Feature/Http/Controllers/ImageProxyControllerTest.php b/tests/Feature/Http/Controllers/ImageProxyControllerTest.php index a4b7c552..51ea90ea 100644 --- a/tests/Feature/Http/Controllers/ImageProxyControllerTest.php +++ b/tests/Feature/Http/Controllers/ImageProxyControllerTest.php @@ -6,6 +6,7 @@ use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Log; +use PHPUnit\Framework\Attributes\Test; use Tests\BaseTestCase; class ImageProxyControllerTest extends BaseTestCase diff --git a/tests/Feature/Http/Controllers/LaporanDesaAktifControllerTest.php b/tests/Feature/Http/Controllers/LaporanDesaAktifControllerTest.php index a52e054f..a0587cd2 100644 --- a/tests/Feature/Http/Controllers/LaporanDesaAktifControllerTest.php +++ b/tests/Feature/Http/Controllers/LaporanDesaAktifControllerTest.php @@ -2,6 +2,7 @@ namespace Tests\Feature\Http\Controllers; +use PHPUnit\Framework\Attributes\Test; use Tests\BaseTestCase; class LaporanDesaAktifControllerTest extends BaseTestCase diff --git a/tests/Feature/IdentitasControllerApiTest.php b/tests/Feature/IdentitasControllerApiTest.php index 5def5775..06100a4d 100644 --- a/tests/Feature/IdentitasControllerApiTest.php +++ b/tests/Feature/IdentitasControllerApiTest.php @@ -8,6 +8,7 @@ use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\Storage; use Laravel\Sanctum\Sanctum; +use PHPUnit\Framework\Attributes\Test; use Tests\TestCase; class IdentitasControllerApiTest extends TestCase diff --git a/tests/Feature/IdorPreventionTest.php b/tests/Feature/IdorPreventionTest.php index a867c63d..9cb04d8e 100644 --- a/tests/Feature/IdorPreventionTest.php +++ b/tests/Feature/IdorPreventionTest.php @@ -5,6 +5,7 @@ use App\Models\User; use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Foundation\Testing\RefreshDatabase; +use PHPUnit\Framework\Attributes\Test; use Tests\TestCase; /** diff --git a/tests/Feature/JaminanSosialTest.php b/tests/Feature/JaminanSosialTest.php index 18c7875f..01f3f82e 100644 --- a/tests/Feature/JaminanSosialTest.php +++ b/tests/Feature/JaminanSosialTest.php @@ -2,6 +2,7 @@ namespace Tests\Feature; +use PHPUnit\Framework\Attributes\Test; use Tests\BaseTestCase; class JaminanSosialTest extends BaseTestCase diff --git a/tests/Feature/LaporanBulananDetailExportTest.php b/tests/Feature/LaporanBulananDetailExportTest.php index d2d2b8f1..3a7fbc7e 100644 --- a/tests/Feature/LaporanBulananDetailExportTest.php +++ b/tests/Feature/LaporanBulananDetailExportTest.php @@ -2,6 +2,7 @@ namespace Tests\Feature; +use PHPUnit\Framework\Attributes\Test; use Tests\BaseTestCase; class LaporanBulananDetailExportTest extends BaseTestCase diff --git a/tests/Feature/LoginControllerTest.php b/tests/Feature/LoginControllerTest.php index d7eb5316..a066a109 100644 --- a/tests/Feature/LoginControllerTest.php +++ b/tests/Feature/LoginControllerTest.php @@ -10,6 +10,7 @@ use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\RateLimiter; use Mockery; +use PHPUnit\Framework\Attributes\Test; use Tests\TestCase; class LoginControllerTest extends TestCase diff --git a/tests/Feature/MenuControllerCmsTest.php b/tests/Feature/MenuControllerCmsTest.php index 108c86bc..809b18c5 100644 --- a/tests/Feature/MenuControllerCmsTest.php +++ b/tests/Feature/MenuControllerCmsTest.php @@ -5,6 +5,7 @@ use App\Models\CMS\Category; use App\Models\CMS\Page; use Illuminate\Foundation\Testing\DatabaseTransactions; +use PHPUnit\Framework\Attributes\Test; use Tests\BaseTestCase; class MenuControllerCmsTest extends BaseTestCase diff --git a/tests/Feature/OtpControllerTest.php b/tests/Feature/OtpControllerTest.php index c99f73f1..aca27951 100644 --- a/tests/Feature/OtpControllerTest.php +++ b/tests/Feature/OtpControllerTest.php @@ -8,6 +8,7 @@ use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Support\Facades\RateLimiter; use Mockery; +use PHPUnit\Framework\Attributes\Test; use Tests\BaseTestCase; class OtpControllerTest extends BaseTestCase diff --git a/tests/Feature/OtpLoginControllerTest.php b/tests/Feature/OtpLoginControllerTest.php index 3c525e49..5eff789f 100644 --- a/tests/Feature/OtpLoginControllerTest.php +++ b/tests/Feature/OtpLoginControllerTest.php @@ -9,6 +9,7 @@ use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Support\Facades\RateLimiter; use Mockery; +use PHPUnit\Framework\Attributes\Test; use Tests\TestCase; class OtpLoginControllerTest extends TestCase diff --git a/tests/Feature/PageControllerCmsTest.php b/tests/Feature/PageControllerCmsTest.php index 1abaa0a2..d6d61cf3 100644 --- a/tests/Feature/PageControllerCmsTest.php +++ b/tests/Feature/PageControllerCmsTest.php @@ -6,6 +6,7 @@ use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Http\UploadedFile; use Illuminate\Support\Facades\Storage; +use PHPUnit\Framework\Attributes\Test; use Tests\BaseTestCase; class PageControllerCmsTest extends BaseTestCase diff --git a/tests/Feature/PengaturanGroupTeamTest.php b/tests/Feature/PengaturanGroupTeamTest.php index 59eb6f0f..d7c3377d 100644 --- a/tests/Feature/PengaturanGroupTeamTest.php +++ b/tests/Feature/PengaturanGroupTeamTest.php @@ -6,6 +6,7 @@ use App\Models\User; use Illuminate\Http\Response; use Laravel\Sanctum\Sanctum; +use PHPUnit\Framework\Attributes\Test; use Tests\TestCase; class PengaturanGroupTeamTest extends TestCase diff --git a/tests/Feature/PengaturanSettingTest.php b/tests/Feature/PengaturanSettingTest.php index ffd2a66d..809e7bd9 100644 --- a/tests/Feature/PengaturanSettingTest.php +++ b/tests/Feature/PengaturanSettingTest.php @@ -6,6 +6,7 @@ use App\Models\User; use Illuminate\Http\Response; use Laravel\Sanctum\Sanctum; +use PHPUnit\Framework\Attributes\Test; use Tests\TestCase; class PengaturanSettingTest extends TestCase diff --git a/tests/Feature/Services/ApiProxyServiceTest.php b/tests/Feature/Services/ApiProxyServiceTest.php index bc075b0f..99f2ebf6 100644 --- a/tests/Feature/Services/ApiProxyServiceTest.php +++ b/tests/Feature/Services/ApiProxyServiceTest.php @@ -6,6 +6,7 @@ use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Log; +use PHPUnit\Framework\Attributes\Test; use Tests\TestCase; class ApiProxyServiceTest extends TestCase diff --git a/tests/Feature/SlideControllerCmsTest.php b/tests/Feature/SlideControllerCmsTest.php index 2363a37e..d942b4ca 100644 --- a/tests/Feature/SlideControllerCmsTest.php +++ b/tests/Feature/SlideControllerCmsTest.php @@ -6,6 +6,7 @@ use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Http\UploadedFile; use Illuminate\Support\Facades\Storage; +use PHPUnit\Framework\Attributes\Test; use Tests\BaseTestCase; class SlideControllerCmsTest extends BaseTestCase diff --git a/tests/Feature/StatistikPengunjungCmsControllerTest.php b/tests/Feature/StatistikPengunjungCmsControllerTest.php index ce040c57..e4ca16e2 100644 --- a/tests/Feature/StatistikPengunjungCmsControllerTest.php +++ b/tests/Feature/StatistikPengunjungCmsControllerTest.php @@ -4,6 +4,7 @@ use App\Models\CMS\Visit; use Illuminate\Foundation\Testing\DatabaseTransactions; +use PHPUnit\Framework\Attributes\Test; use Tests\BaseTestCase; class StatistikPengunjungCmsControllerTest extends BaseTestCase diff --git a/tests/Feature/StrongPasswordRuleTest.php b/tests/Feature/StrongPasswordRuleTest.php index 9173c374..491c57cb 100644 --- a/tests/Feature/StrongPasswordRuleTest.php +++ b/tests/Feature/StrongPasswordRuleTest.php @@ -6,6 +6,7 @@ use App\Rules\StrongPassword; use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Support\Facades\Validator; +use PHPUnit\Framework\Attributes\Test; use Tests\TestCase; class StrongPasswordRuleTest extends TestCase diff --git a/tests/Feature/TwoFactorControllerTest.php b/tests/Feature/TwoFactorControllerTest.php index 3cd6ec8e..2389aee4 100644 --- a/tests/Feature/TwoFactorControllerTest.php +++ b/tests/Feature/TwoFactorControllerTest.php @@ -7,6 +7,7 @@ use App\Services\OtpService; use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Support\Facades\RateLimiter; +use PHPUnit\Framework\Attributes\Test; use Tests\BaseTestCase; use Mockery; diff --git a/tests/Feature/WilayahFilterParameterTest.php b/tests/Feature/WilayahFilterParameterTest.php index 9decab18..21f1db47 100644 --- a/tests/Feature/WilayahFilterParameterTest.php +++ b/tests/Feature/WilayahFilterParameterTest.php @@ -2,6 +2,7 @@ namespace Tests\Feature; +use PHPUnit\Framework\Attributes\Test; use Tests\BaseTestCase; class WilayahFilterParameterTest extends BaseTestCase diff --git a/tests/Feature/XssPreventionTest.php b/tests/Feature/XssPreventionTest.php index c8661d72..3a8378a0 100644 --- a/tests/Feature/XssPreventionTest.php +++ b/tests/Feature/XssPreventionTest.php @@ -10,6 +10,7 @@ use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Support\Facades\Storage; use Mockery; +use PHPUnit\Framework\Attributes\Test; use Tests\BaseTestCase; use Mews\Purifier\Facades\Purifier; diff --git a/tests/Unit/ArtikelServiceTest.php b/tests/Unit/ArtikelServiceTest.php index f1d70b14..8eea6a42 100644 --- a/tests/Unit/ArtikelServiceTest.php +++ b/tests/Unit/ArtikelServiceTest.php @@ -5,7 +5,7 @@ use App\Services\ArtikelService; use Illuminate\Support\Facades\Cache; use Mockery; -use ReflectionClass; +use PHPUnit\Framework\Attributes\Test; use Tests\TestCase; class ArtikelServiceTest extends TestCase diff --git a/tests/Unit/DataPresisiPanganDetailViewTest.php b/tests/Unit/DataPresisiPanganDetailViewTest.php index 5b53ef9b..2c20dbe3 100644 --- a/tests/Unit/DataPresisiPanganDetailViewTest.php +++ b/tests/Unit/DataPresisiPanganDetailViewTest.php @@ -2,6 +2,7 @@ namespace Tests\Unit; +use PHPUnit\Framework\Attributes\Test; use Tests\BaseTestCase; use Illuminate\View\View; use Illuminate\Support\Facades\View as ViewFacade; From 513b27a2acfff89a3e76a557b100108de928506f Mon Sep 17 00:00:00 2001 From: Ahmad Afandi Date: Fri, 5 Jun 2026 08:39:10 +0700 Subject: [PATCH 13/43] perbaikan test --- tests/Feature/ModuleControllerOrgTest.php | 1 + tests/Feature/PageControllerWebsiteTest.php | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/Feature/ModuleControllerOrgTest.php b/tests/Feature/ModuleControllerOrgTest.php index 24be0cd7..415b4c50 100644 --- a/tests/Feature/ModuleControllerOrgTest.php +++ b/tests/Feature/ModuleControllerOrgTest.php @@ -2,6 +2,7 @@ namespace Tests\Feature; +use PHPUnit\Framework\Attributes\Test; use Tests\WebsiteTestCase; class ModuleControllerOrgTest extends WebsiteTestCase diff --git a/tests/Feature/PageControllerWebsiteTest.php b/tests/Feature/PageControllerWebsiteTest.php index eeed1f95..90e5b283 100644 --- a/tests/Feature/PageControllerWebsiteTest.php +++ b/tests/Feature/PageControllerWebsiteTest.php @@ -7,6 +7,7 @@ use App\Models\CMS\Page; use App\Services\SitemapService; use Mockery; +use PHPUnit\Framework\Attributes\Test; use Tests\WebsiteTestCase; class PageControllerWebsiteTest extends WebsiteTestCase From 9361783b254844129cc9aeab4df92fe58f0b48f9 Mon Sep 17 00:00:00 2001 From: Ahmad Afandi Date: Sat, 6 Jun 2026 06:20:14 +0700 Subject: [PATCH 14/43] perbaikan install awal --- app/Providers/RecaptchaV3ServiceProvider.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/Providers/RecaptchaV3ServiceProvider.php b/app/Providers/RecaptchaV3ServiceProvider.php index 6431ab08..8d49ce82 100644 --- a/app/Providers/RecaptchaV3ServiceProvider.php +++ b/app/Providers/RecaptchaV3ServiceProvider.php @@ -25,8 +25,13 @@ public function register() */ public function boot() { - // Override reCAPTCHA v3 configuration with database values - $settings = \App\Models\Setting::pluck('value', 'key')->toArray(); + try { + // Override reCAPTCHA v3 configuration with database values + $settings = \App\Models\Setting::pluck('value', 'key')->toArray(); + } catch (\Exception $e) { + // Settings table may not exist yet during initial installation + return; + } // Only override if captcha is enabled and type is not builtin $captchaEnabled = filter_var($settings['captcha_enabled'] ?? true, FILTER_VALIDATE_BOOLEAN); From 3d2989695c7a291c6005ea0cf48780c981133795 Mon Sep 17 00:00:00 2001 From: Ahmad Afandi Date: Sat, 6 Jun 2026 06:23:42 +0700 Subject: [PATCH 15/43] sesuaikan dengan struktur laravel 13 --- bootstrap/providers.php | 10 ++++ config/app.php | 108 +++------------------------------------- 2 files changed, 16 insertions(+), 102 deletions(-) create mode 100644 bootstrap/providers.php diff --git a/bootstrap/providers.php b/bootstrap/providers.php new file mode 100644 index 00000000..d2415c3b --- /dev/null +++ b/bootstrap/providers.php @@ -0,0 +1,10 @@ + env('APP_URL', 'http://localhost'), - 'asset_url' => env('ASSET_URL'), - /* |-------------------------------------------------------------------------- | Application Timezone @@ -96,30 +92,8 @@ 'locale' => 'id', - /* - |-------------------------------------------------------------------------- - | Application Fallback Locale - |-------------------------------------------------------------------------- - | - | The fallback locale determines the locale to use when the current one - | is not available. You may change the value to correspond to any of - | the language folders that are provided through your application. - | - */ - 'fallback_locale' => 'id', - /* - |-------------------------------------------------------------------------- - | Faker Locale - |-------------------------------------------------------------------------- - | - | This locale will be used by the Faker PHP library when generating fake - | data for your database seeds. For example, this will be used to get - | localized telephone numbers, street address information and more. - | - */ - 'faker_locale' => 'id_ID', /* @@ -137,6 +111,12 @@ 'cipher' => 'AES-256-CBC', + 'previous_keys' => [ + ...array_filter( + explode(',', (string) env('APP_PREVIOUS_KEYS', '')) + ), + ], + /* |-------------------------------------------------------------------------- | Maintenance Mode Driver @@ -155,82 +135,6 @@ // 'store' => 'redis', ], - /* - |-------------------------------------------------------------------------- - | Autoloaded Service Providers - |-------------------------------------------------------------------------- - | - | The service providers listed here will be automatically loaded on the - | request to your application. Feel free to add your own services to - | this array to grant expanded functionality to your applications. - | - */ - - 'providers' => [ - - /* - * Laravel Framework Service Providers... - */ - Illuminate\Auth\AuthServiceProvider::class, - Illuminate\Broadcasting\BroadcastServiceProvider::class, - Illuminate\Bus\BusServiceProvider::class, - Illuminate\Cache\CacheServiceProvider::class, - Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class, - Illuminate\Cookie\CookieServiceProvider::class, - Illuminate\Database\DatabaseServiceProvider::class, - Illuminate\Encryption\EncryptionServiceProvider::class, - Illuminate\Filesystem\FilesystemServiceProvider::class, - Illuminate\Foundation\Providers\FoundationServiceProvider::class, - Illuminate\Hashing\HashServiceProvider::class, - Illuminate\Mail\MailServiceProvider::class, - Illuminate\Notifications\NotificationServiceProvider::class, - Illuminate\Pagination\PaginationServiceProvider::class, - Illuminate\Pipeline\PipelineServiceProvider::class, - Illuminate\Queue\QueueServiceProvider::class, - Illuminate\Redis\RedisServiceProvider::class, - Illuminate\Auth\Passwords\PasswordResetServiceProvider::class, - Illuminate\Session\SessionServiceProvider::class, - Illuminate\Translation\TranslationServiceProvider::class, - Illuminate\Validation\ValidationServiceProvider::class, - Illuminate\View\ViewServiceProvider::class, - Intervention\Image\Laravel\ServiceProvider::class, - Spatie\Permission\PermissionServiceProvider::class, - Spatie\Csp\CspServiceProvider::class, - /* - * Package Service Providers... - */ - - /* - * Application Service Providers... - */ - App\Providers\AppServiceProvider::class, - App\Providers\AuthServiceProvider::class, - // App\Providers\BroadcastServiceProvider::class, - App\Providers\EventServiceProvider::class, - App\Providers\MacroServiceProvider::class, - App\Providers\RouteServiceProvider::class, - App\Providers\RecaptchaV3ServiceProvider::class, - - ], - - /* - |-------------------------------------------------------------------------- - | Class Aliases - |-------------------------------------------------------------------------- - | - | This array of class aliases will be registered when this application - | is started. However, feel free to register as many as you wish as - | the aliases are "lazy" loaded so they don't hinder performance. - | - */ - - 'aliases' => Facade::defaultAliases()->merge([ - // 'ExampleClass' => App\Example\ExampleClass::class, - 'Image' => Intervention\Image\Laravel\Facades\Image::class, - 'Html' => Spatie\Html\Facades\Html::class, - 'Captcha' => Mews\Captcha\Facades\Captcha::class, - ])->toArray(), - 'format' => [ 'date' => env('FORMAT_DATE', 'd/m/Y'), 'date_js' => env('FORMAT_DATE_JS', 'DD/MM/YYYY'), From 4d93a5d0a15627e26eb7a991f9cf677ec5178027 Mon Sep 17 00:00:00 2001 From: Ahmad Afandi Date: Sat, 6 Jun 2026 06:23:42 +0700 Subject: [PATCH 16/43] sesuaikan dengan struktur laravel 13 --- bootstrap/providers.php | 10 ++++ config/app.php | 106 +++------------------------------------- 2 files changed, 16 insertions(+), 100 deletions(-) create mode 100644 bootstrap/providers.php diff --git a/bootstrap/providers.php b/bootstrap/providers.php new file mode 100644 index 00000000..d2415c3b --- /dev/null +++ b/bootstrap/providers.php @@ -0,0 +1,10 @@ + 'id', - /* - |-------------------------------------------------------------------------- - | Application Fallback Locale - |-------------------------------------------------------------------------- - | - | The fallback locale determines the locale to use when the current one - | is not available. You may change the value to correspond to any of - | the language folders that are provided through your application. - | - */ - 'fallback_locale' => 'id', - /* - |-------------------------------------------------------------------------- - | Faker Locale - |-------------------------------------------------------------------------- - | - | This locale will be used by the Faker PHP library when generating fake - | data for your database seeds. For example, this will be used to get - | localized telephone numbers, street address information and more. - | - */ - 'faker_locale' => 'id_ID', /* @@ -137,6 +113,12 @@ 'cipher' => 'AES-256-CBC', + 'previous_keys' => [ + ...array_filter( + explode(',', (string) env('APP_PREVIOUS_KEYS', '')) + ), + ], + /* |-------------------------------------------------------------------------- | Maintenance Mode Driver @@ -155,82 +137,6 @@ // 'store' => 'redis', ], - /* - |-------------------------------------------------------------------------- - | Autoloaded Service Providers - |-------------------------------------------------------------------------- - | - | The service providers listed here will be automatically loaded on the - | request to your application. Feel free to add your own services to - | this array to grant expanded functionality to your applications. - | - */ - - 'providers' => [ - - /* - * Laravel Framework Service Providers... - */ - Illuminate\Auth\AuthServiceProvider::class, - Illuminate\Broadcasting\BroadcastServiceProvider::class, - Illuminate\Bus\BusServiceProvider::class, - Illuminate\Cache\CacheServiceProvider::class, - Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class, - Illuminate\Cookie\CookieServiceProvider::class, - Illuminate\Database\DatabaseServiceProvider::class, - Illuminate\Encryption\EncryptionServiceProvider::class, - Illuminate\Filesystem\FilesystemServiceProvider::class, - Illuminate\Foundation\Providers\FoundationServiceProvider::class, - Illuminate\Hashing\HashServiceProvider::class, - Illuminate\Mail\MailServiceProvider::class, - Illuminate\Notifications\NotificationServiceProvider::class, - Illuminate\Pagination\PaginationServiceProvider::class, - Illuminate\Pipeline\PipelineServiceProvider::class, - Illuminate\Queue\QueueServiceProvider::class, - Illuminate\Redis\RedisServiceProvider::class, - Illuminate\Auth\Passwords\PasswordResetServiceProvider::class, - Illuminate\Session\SessionServiceProvider::class, - Illuminate\Translation\TranslationServiceProvider::class, - Illuminate\Validation\ValidationServiceProvider::class, - Illuminate\View\ViewServiceProvider::class, - Intervention\Image\Laravel\ServiceProvider::class, - Spatie\Permission\PermissionServiceProvider::class, - Spatie\Csp\CspServiceProvider::class, - /* - * Package Service Providers... - */ - - /* - * Application Service Providers... - */ - App\Providers\AppServiceProvider::class, - App\Providers\AuthServiceProvider::class, - // App\Providers\BroadcastServiceProvider::class, - App\Providers\EventServiceProvider::class, - App\Providers\MacroServiceProvider::class, - App\Providers\RouteServiceProvider::class, - App\Providers\RecaptchaV3ServiceProvider::class, - - ], - - /* - |-------------------------------------------------------------------------- - | Class Aliases - |-------------------------------------------------------------------------- - | - | This array of class aliases will be registered when this application - | is started. However, feel free to register as many as you wish as - | the aliases are "lazy" loaded so they don't hinder performance. - | - */ - - 'aliases' => Facade::defaultAliases()->merge([ - // 'ExampleClass' => App\Example\ExampleClass::class, - 'Image' => Intervention\Image\Laravel\Facades\Image::class, - 'Html' => Spatie\Html\Facades\Html::class, - 'Captcha' => Mews\Captcha\Facades\Captcha::class, - ])->toArray(), - 'format' => [ 'date' => env('FORMAT_DATE', 'd/m/Y'), 'date_js' => env('FORMAT_DATE_JS', 'DD/MM/YYYY'), From 02d47a93f3f29e51c7a4ce7ba783fe0f3372628e Mon Sep 17 00:00:00 2001 From: Ahmad Afandi Date: Sat, 6 Jun 2026 06:25:51 +0700 Subject: [PATCH 17/43] kembalikan --- config/app.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/app.php b/config/app.php index a7270866..86771279 100644 --- a/config/app.php +++ b/config/app.php @@ -66,6 +66,8 @@ 'url' => env('APP_URL', 'http://localhost'), + 'asset_url' => env('ASSET_URL'), + /* |-------------------------------------------------------------------------- | Application Timezone From 9a0abb5d525985d300e28e03f580a4cd6d50bb74 Mon Sep 17 00:00:00 2001 From: Ahmad Afandi Date: Sat, 6 Jun 2026 07:14:13 +0700 Subject: [PATCH 18/43] sesuaikan standart struktur file dan folder laravel 13 --- .../Auth/ConfirmPasswordController.php | 4 +- app/Http/Controllers/Auth/LoginController.php | 4 +- .../Controllers/Auth/OtpLoginController.php | 2 +- .../Controllers/Auth/RegisterController.php | 4 +- .../Auth/ResetPasswordController.php | 4 +- .../Auth/VerificationController.php | 4 +- .../ForcePasswordResetController.php | 4 +- .../Middleware/RedirectIfAuthenticated.php | 4 +- app/Providers/AppServiceProvider.php | 2 + app/Providers/AuthServiceProvider.php | 35 -------------- app/Providers/BroadcastServiceProvider.php | 21 --------- app/Providers/EventServiceProvider.php | 34 +------------- app/Providers/RouteServiceProvider.php | 10 ---- artisan | 46 ++----------------- bootstrap/providers.php | 2 - public/index.php | 44 ++---------------- 16 files changed, 25 insertions(+), 199 deletions(-) delete mode 100644 app/Providers/AuthServiceProvider.php delete mode 100644 app/Providers/BroadcastServiceProvider.php delete mode 100644 app/Providers/RouteServiceProvider.php mode change 100644 => 100755 artisan diff --git a/app/Http/Controllers/Auth/ConfirmPasswordController.php b/app/Http/Controllers/Auth/ConfirmPasswordController.php index 138c1f08..d98812ce 100644 --- a/app/Http/Controllers/Auth/ConfirmPasswordController.php +++ b/app/Http/Controllers/Auth/ConfirmPasswordController.php @@ -3,7 +3,7 @@ namespace App\Http\Controllers\Auth; use App\Http\Controllers\Controller; -use App\Providers\RouteServiceProvider; +use App\Providers\AppServiceProvider; use Illuminate\Foundation\Auth\ConfirmsPasswords; class ConfirmPasswordController extends Controller @@ -26,7 +26,7 @@ class ConfirmPasswordController extends Controller * * @var string */ - protected $redirectTo = RouteServiceProvider::HOME; + protected $redirectTo = AppServiceProvider::HOME; /** * Create a new controller instance. diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php index f8291c50..6a9042c9 100644 --- a/app/Http/Controllers/Auth/LoginController.php +++ b/app/Http/Controllers/Auth/LoginController.php @@ -4,7 +4,7 @@ use App\Http\Controllers\Controller; use App\Models\User; -use App\Providers\RouteServiceProvider; +use App\Providers\AppServiceProvider; use App\Services\CaptchaService; use App\Services\OtpService; use App\Services\TwoFactorService; @@ -32,7 +32,7 @@ class LoginController extends Controller * * @var string */ - protected $redirectTo = RouteServiceProvider::HOME; + protected $redirectTo = AppServiceProvider::HOME; /** * Create a new controller instance. diff --git a/app/Http/Controllers/Auth/OtpLoginController.php b/app/Http/Controllers/Auth/OtpLoginController.php index 2427e0a3..5939b782 100644 --- a/app/Http/Controllers/Auth/OtpLoginController.php +++ b/app/Http/Controllers/Auth/OtpLoginController.php @@ -199,7 +199,7 @@ public function verifyOtp(OtpVerifyRequest $request) return response()->json([ 'success' => true, 'message' => 'Login berhasil', - 'redirect' => \App\Providers\RouteServiceProvider::HOME + 'redirect' => \App\Providers\AppServiceProvider::HOME ]); } diff --git a/app/Http/Controllers/Auth/RegisterController.php b/app/Http/Controllers/Auth/RegisterController.php index 0ecaa966..ead5a29e 100644 --- a/app/Http/Controllers/Auth/RegisterController.php +++ b/app/Http/Controllers/Auth/RegisterController.php @@ -5,7 +5,7 @@ use App\Http\Controllers\Controller; use App\Http\Requests\RegisterRequest; use App\Models\User; -use App\Providers\RouteServiceProvider; +use App\Providers\AppServiceProvider; use Illuminate\Foundation\Auth\RegistersUsers; use Illuminate\Http\Request; use Illuminate\Support\Facades\Hash; @@ -30,7 +30,7 @@ class RegisterController extends Controller * * @var string */ - protected $redirectTo = RouteServiceProvider::HOME; + protected $redirectTo = AppServiceProvider::HOME; /** * Create a new controller instance. diff --git a/app/Http/Controllers/Auth/ResetPasswordController.php b/app/Http/Controllers/Auth/ResetPasswordController.php index ad4b0f2d..22f8fa74 100644 --- a/app/Http/Controllers/Auth/ResetPasswordController.php +++ b/app/Http/Controllers/Auth/ResetPasswordController.php @@ -5,7 +5,7 @@ use App\Http\Controllers\Controller; use App\Http\Requests\ResetPasswordRequest; use App\Models\PasswordHistory; -use App\Providers\RouteServiceProvider; +use App\Providers\AppServiceProvider; use Illuminate\Foundation\Auth\ResetsPasswords; use Illuminate\Support\Facades\Password; @@ -29,7 +29,7 @@ class ResetPasswordController extends Controller * * @var string */ - protected $redirectTo = RouteServiceProvider::HOME; + protected $redirectTo = AppServiceProvider::HOME; /** * Display the password reset view. diff --git a/app/Http/Controllers/Auth/VerificationController.php b/app/Http/Controllers/Auth/VerificationController.php index 5e749af8..074f2e7d 100644 --- a/app/Http/Controllers/Auth/VerificationController.php +++ b/app/Http/Controllers/Auth/VerificationController.php @@ -3,7 +3,7 @@ namespace App\Http\Controllers\Auth; use App\Http\Controllers\Controller; -use App\Providers\RouteServiceProvider; +use App\Providers\AppServiceProvider; use Illuminate\Foundation\Auth\VerifiesEmails; class VerificationController extends Controller @@ -26,7 +26,7 @@ class VerificationController extends Controller * * @var string */ - protected $redirectTo = RouteServiceProvider::HOME; + protected $redirectTo = AppServiceProvider::HOME; /** * Create a new controller instance. diff --git a/app/Http/Controllers/ForcePasswordResetController.php b/app/Http/Controllers/ForcePasswordResetController.php index 24557473..411e3d46 100644 --- a/app/Http/Controllers/ForcePasswordResetController.php +++ b/app/Http/Controllers/ForcePasswordResetController.php @@ -4,7 +4,7 @@ use App\Http\Requests\ForcePasswordResetRequest; use App\Models\PasswordHistory; -use App\Providers\RouteServiceProvider; +use App\Providers\AppServiceProvider; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; @@ -59,7 +59,7 @@ public function reset(ForcePasswordResetRequest $request) $user->save(); // Redirect to intended URL or home - $intendedUrl = session('intended_url', url(RouteServiceProvider::HOME)); + $intendedUrl = session('intended_url', url(AppServiceProvider::HOME)); session()->forget('intended_url'); return redirect($intendedUrl) diff --git a/app/Http/Middleware/RedirectIfAuthenticated.php b/app/Http/Middleware/RedirectIfAuthenticated.php index b2043b93..8755f470 100644 --- a/app/Http/Middleware/RedirectIfAuthenticated.php +++ b/app/Http/Middleware/RedirectIfAuthenticated.php @@ -2,7 +2,7 @@ namespace App\Http\Middleware; -use App\Providers\RouteServiceProvider; +use App\Providers\AppServiceProvider; use Closure; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; @@ -23,7 +23,7 @@ public function handle(Request $request, Closure $next, ...$guards) foreach ($guards as $guard) { if (Auth::guard($guard)->check()) { - return redirect(RouteServiceProvider::HOME); + return redirect(AppServiceProvider::HOME); } } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index defc5a78..abafe1ed 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -19,6 +19,8 @@ class AppServiceProvider extends ServiceProvider { + public const HOME = '/dasbor'; + /** * Register any application services. * diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php deleted file mode 100644 index 10e4731a..00000000 --- a/app/Providers/AuthServiceProvider.php +++ /dev/null @@ -1,35 +0,0 @@ - - */ - protected $policies = [ - User::class => UserPolicy::class, - Team::class => TeamPolicy::class, - Article::class => ArticlePolicy::class, - ]; - - /** - * Register any authentication / authorization services. - * - * @return void - */ - public function boot() - { - $this->registerPolicies(); - } -} diff --git a/app/Providers/BroadcastServiceProvider.php b/app/Providers/BroadcastServiceProvider.php deleted file mode 100644 index 395c518b..00000000 --- a/app/Providers/BroadcastServiceProvider.php +++ /dev/null @@ -1,21 +0,0 @@ -> - */ - protected $listen = [ - BuildingMenu::class => [ - \App\Listeners\MenuListener::class, - ], - Registered::class => [ - SendEmailVerificationNotification::class, - ], - Login::class => [ - LoginListener::class, - ], - Logout::class => [ - LogoutListener::class, - ], - Failed::class => [ - FailedLoginListener::class, - ], - ]; - /** * Register any events for your application. * @@ -57,6 +25,6 @@ public function boot() */ public function shouldDiscoverEvents() { - return false; + return true; } } diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php deleted file mode 100644 index b3bb8b85..00000000 --- a/app/Providers/RouteServiceProvider.php +++ /dev/null @@ -1,10 +0,0 @@ -make(Illuminate\Contracts\Console\Kernel::class); - -$status = $kernel->handle( - $input = new Symfony\Component\Console\Input\ArgvInput, - new Symfony\Component\Console\Output\ConsoleOutput -); - -/* -|-------------------------------------------------------------------------- -| Shutdown The Application -|-------------------------------------------------------------------------- -| -| Once Artisan has finished running, we will fire off the shutdown events -| so that any final work may be done by the application before we shut -| down the process. This is the last thing to happen to the request. -| -*/ - -$kernel->terminate($input, $status); +$status = $app->handleCommand(new ArgvInput); exit($status); diff --git a/bootstrap/providers.php b/bootstrap/providers.php index d2415c3b..70390ba8 100644 --- a/bootstrap/providers.php +++ b/bootstrap/providers.php @@ -2,9 +2,7 @@ return [ App\Providers\AppServiceProvider::class, - App\Providers\AuthServiceProvider::class, App\Providers\EventServiceProvider::class, App\Providers\MacroServiceProvider::class, - App\Providers\RouteServiceProvider::class, App\Providers\RecaptchaV3ServiceProvider::class, ]; diff --git a/public/index.php b/public/index.php index 1d69f3a2..d10ec7cc 100644 --- a/public/index.php +++ b/public/index.php @@ -1,55 +1,17 @@ make(Kernel::class); - -$response = $kernel->handle( - $request = Request::capture() -)->send(); - -$kernel->terminate($request, $response); +$app->handleRequest(Request::capture()); From bdc47d6f3cce4a6818252da4e085adabe89dd30a Mon Sep 17 00:00:00 2001 From: Ahmad Afandi Date: Mon, 8 Jun 2026 07:26:40 +0700 Subject: [PATCH 19/43] perbaikan observer --- app/Observers/VisitorObserver.php | 62 +++++++++++++++++++++------- app/Providers/AppServiceProvider.php | 18 ++++---- 2 files changed, 57 insertions(+), 23 deletions(-) diff --git a/app/Observers/VisitorObserver.php b/app/Observers/VisitorObserver.php index e268ef2f..991826b7 100644 --- a/app/Observers/VisitorObserver.php +++ b/app/Observers/VisitorObserver.php @@ -2,33 +2,63 @@ namespace App\Observers; +use Illuminate\Support\Facades\Log; use Shetabit\Visitor\Models\Visit; use Stevebauman\Location\Facades\Location; class VisitorObserver { + protected static bool $updating = false; + /** - * Handle the ShetabitVisitorModelsVisit "created" event. - * - * @param Shetabit\Visitor\Models\Visit $shetabitVisitorModelsVisit - * - * @return void + * Handle the Visit "created" event. */ - public function created(Visit $shetabitVisitorModelsVisit) + public function created(Visit $visit): void { + if (static::$updating) { + return; + } + try { - $location = Location::get($shetabitVisitorModelsVisit->ip); - - if ($location) { - $shetabitVisitorModelsVisit->location = json_encode($location); - $shetabitVisitorModelsVisit->country_code = $location->countryCode; - $shetabitVisitorModelsVisit->country = $location->countryName; - $shetabitVisitorModelsVisit->region = $location->regionCode; - $shetabitVisitorModelsVisit->region_name = $location->regionName; - $shetabitVisitorModelsVisit->save(); + $location = Location::get($visit->ip); + + if (! $location) { + return; } + + static::$updating = true; + + $visit->update([ + 'location' => $location->toJson(), + 'country_code' => $location->countryCode, + 'country' => $location->countryName, + 'region' => $location->regionCode, + 'region_name' => $location->regionName, + ]); } catch (\Exception $e) { - \Log::error($e->getMessage()); + Log::error('Failed to fetch visitor location', [ + 'ip' => $visit->ip, + 'error' => $e->getMessage(), + ]); + } finally { + static::$updating = false; + } + } + + /** + * Handle the Visit "updated" event. + * + * This method is intentionally left with a guard check to prevent + * infinite loops when the "created" event calls update() on the model. + * Without this guard, the update() call would trigger this method, + * which could recursively trigger update() again if logic is added here. + * + * @see https://github.com/laravel/framework/issues/xxxxx + */ + public function updated(Visit $visit): void + { + if (static::$updating) { + return; } } } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index abafe1ed..418dfaf5 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -16,6 +16,8 @@ use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\View; use Illuminate\Support\ServiceProvider; +use App\Observers\VisitorObserver; +use Shetabit\Visitor\Models\Visit; class AppServiceProvider extends ServiceProvider { @@ -23,21 +25,18 @@ class AppServiceProvider extends ServiceProvider /** * Register any application services. - * - * @return void */ - public function register() + public function register(): void { // } /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { + $this->configureObservers(); $this->configureRateLimiting(); $this->bootHttps(); $this->addValidation(); @@ -70,7 +69,12 @@ public function boot() } } - protected function configureRateLimiting() + protected function configureObservers(): void + { + Visit::observe(VisitorObserver::class); + } + + protected function configureRateLimiting(): void { RateLimiter::for('api', function (Request $request) { return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip()); From e321f36beb837b70b98836465cc32eb98230749b Mon Sep 17 00:00:00 2001 From: Ahmad Afandi Date: Mon, 8 Jun 2026 07:26:40 +0700 Subject: [PATCH 20/43] perbaikan observer --- app/Observers/VisitorObserver.php | 62 +++++++++++++++++++------- app/Providers/AppServiceProvider.php | 18 +++++--- app/Providers/EventServiceProvider.php | 30 ------------- 3 files changed, 57 insertions(+), 53 deletions(-) delete mode 100644 app/Providers/EventServiceProvider.php diff --git a/app/Observers/VisitorObserver.php b/app/Observers/VisitorObserver.php index e268ef2f..991826b7 100644 --- a/app/Observers/VisitorObserver.php +++ b/app/Observers/VisitorObserver.php @@ -2,33 +2,63 @@ namespace App\Observers; +use Illuminate\Support\Facades\Log; use Shetabit\Visitor\Models\Visit; use Stevebauman\Location\Facades\Location; class VisitorObserver { + protected static bool $updating = false; + /** - * Handle the ShetabitVisitorModelsVisit "created" event. - * - * @param Shetabit\Visitor\Models\Visit $shetabitVisitorModelsVisit - * - * @return void + * Handle the Visit "created" event. */ - public function created(Visit $shetabitVisitorModelsVisit) + public function created(Visit $visit): void { + if (static::$updating) { + return; + } + try { - $location = Location::get($shetabitVisitorModelsVisit->ip); - - if ($location) { - $shetabitVisitorModelsVisit->location = json_encode($location); - $shetabitVisitorModelsVisit->country_code = $location->countryCode; - $shetabitVisitorModelsVisit->country = $location->countryName; - $shetabitVisitorModelsVisit->region = $location->regionCode; - $shetabitVisitorModelsVisit->region_name = $location->regionName; - $shetabitVisitorModelsVisit->save(); + $location = Location::get($visit->ip); + + if (! $location) { + return; } + + static::$updating = true; + + $visit->update([ + 'location' => $location->toJson(), + 'country_code' => $location->countryCode, + 'country' => $location->countryName, + 'region' => $location->regionCode, + 'region_name' => $location->regionName, + ]); } catch (\Exception $e) { - \Log::error($e->getMessage()); + Log::error('Failed to fetch visitor location', [ + 'ip' => $visit->ip, + 'error' => $e->getMessage(), + ]); + } finally { + static::$updating = false; + } + } + + /** + * Handle the Visit "updated" event. + * + * This method is intentionally left with a guard check to prevent + * infinite loops when the "created" event calls update() on the model. + * Without this guard, the update() call would trigger this method, + * which could recursively trigger update() again if logic is added here. + * + * @see https://github.com/laravel/framework/issues/xxxxx + */ + public function updated(Visit $visit): void + { + if (static::$updating) { + return; } } } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index abafe1ed..418dfaf5 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -16,6 +16,8 @@ use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\View; use Illuminate\Support\ServiceProvider; +use App\Observers\VisitorObserver; +use Shetabit\Visitor\Models\Visit; class AppServiceProvider extends ServiceProvider { @@ -23,21 +25,18 @@ class AppServiceProvider extends ServiceProvider /** * Register any application services. - * - * @return void */ - public function register() + public function register(): void { // } /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { + $this->configureObservers(); $this->configureRateLimiting(); $this->bootHttps(); $this->addValidation(); @@ -70,7 +69,12 @@ public function boot() } } - protected function configureRateLimiting() + protected function configureObservers(): void + { + Visit::observe(VisitorObserver::class); + } + + protected function configureRateLimiting(): void { RateLimiter::for('api', function (Request $request) { return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip()); diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php deleted file mode 100644 index 16bf0b07..00000000 --- a/app/Providers/EventServiceProvider.php +++ /dev/null @@ -1,30 +0,0 @@ - Date: Mon, 8 Jun 2026 07:28:33 +0700 Subject: [PATCH 21/43] sesuaikan --- bootstrap/providers.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bootstrap/providers.php b/bootstrap/providers.php index 70390ba8..38713edf 100644 --- a/bootstrap/providers.php +++ b/bootstrap/providers.php @@ -1,8 +1,7 @@ Date: Wed, 10 Jun 2026 07:40:26 +0700 Subject: [PATCH 22/43] inisialisasi smoke test --- .gitignore | 5 + composer.json | 4 +- composer.lock | 3895 +++++++++++++++++++++----- docs/smoke-test-browser.md | 415 +++ package-lock.json | 107 +- package.json | 1 + phpunit.xml | 4 +- routes/web.php | 7 + tests/Browser/SessionState.php | 88 + tests/Browser/SmokeDashboardTest.php | 18 + tests/Browser/SmokeLoginTest.php | 53 + tests/Pest.php | 7 + 12 files changed, 3903 insertions(+), 701 deletions(-) create mode 100644 docs/smoke-test-browser.md create mode 100644 tests/Browser/SessionState.php create mode 100644 tests/Browser/SmokeDashboardTest.php create mode 100644 tests/Browser/SmokeLoginTest.php create mode 100644 tests/Pest.php diff --git a/.gitignore b/.gitignore index 0e7b63b7..1a7206bd 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,8 @@ package-lock.json .env.e2e /template_ai/ AGENTS.md + +# Pest Browser Testing +tests/Browser/Screenshots/ +tests/Browser/screenshots/ +tests/Browser/.session_state.json diff --git a/composer.json b/composer.json index 114126c0..d6c6503c 100644 --- a/composer.json +++ b/composer.json @@ -47,7 +47,9 @@ "laravel/sail": "^1.26", "mockery/mockery": "^1.4.4", "nunomaduro/collision": "^8.1", - "phpunit/phpunit": "^11.0", + "pestphp/pest": "^4.0", + "pestphp/pest-plugin-browser": "^4.0", + "phpunit/phpunit": "^12.0", "spatie/laravel-ignition": "^2.0" }, "minimum-stability": "stable", diff --git a/composer.lock b/composer.lock index 089089c0..5620ba7c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "6de5b947fbacc232082338e96f464f46", + "content-hash": "27d97b095c210931e907629054c7d0ad", "packages": [ { "name": "akaunting/laravel-apexcharts", @@ -8026,16 +8026,16 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.38.1", + "version": "v1.38.2", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "14c5439eec4ccff081ac14eca2dc57feb2a66d92" + "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/14c5439eec4ccff081ac14eca2dc57feb2a66d92", - "reference": "14c5439eec4ccff081ac14eca2dc57feb2a66d92", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6", + "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6", "shasum": "" }, "require": { @@ -8087,7 +8087,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.38.1" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.38.2" }, "funding": [ { @@ -8107,7 +8107,7 @@ "type": "tidelift" } ], - "time": "2026-05-26T12:51:13+00:00" + "time": "2026-05-27T06:59:30+00:00" }, { "name": "symfony/polyfill-php80", @@ -9997,62 +9997,37 @@ ], "packages-dev": [ { - "name": "barryvdh/laravel-debugbar", - "version": "v4.3.0", + "name": "amphp/amp", + "version": "v3.1.1", "source": { "type": "git", - "url": "https://github.com/fruitcake/laravel-debugbar.git", - "reference": "3d76ea8d78b82225b92789de65fc630c1cd8e80c" + "url": "https://github.com/amphp/amp.git", + "reference": "fa0ab33a6f47a82929c38d03ca47ebb71086a93f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fruitcake/laravel-debugbar/zipball/3d76ea8d78b82225b92789de65fc630c1cd8e80c", - "reference": "3d76ea8d78b82225b92789de65fc630c1cd8e80c", + "url": "https://api.github.com/repos/amphp/amp/zipball/fa0ab33a6f47a82929c38d03ca47ebb71086a93f", + "reference": "fa0ab33a6f47a82929c38d03ca47ebb71086a93f", "shasum": "" }, "require": { - "illuminate/routing": "^11|^12|^13.0", - "illuminate/session": "^11|^12|^13.0", - "illuminate/support": "^11|^12|^13.0", - "php": "^8.2", - "php-debugbar/php-debugbar": "^3.7.2", - "php-debugbar/symfony-bridge": "^1.1" + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" }, "require-dev": { - "larastan/larastan": "^3", - "laravel/octane": "^2", - "laravel/pennant": "^1", - "laravel/pint": "^1", - "laravel/telescope": "^5.16", - "livewire/livewire": "^3.7|^4", - "mockery/mockery": "^1.3.3", - "orchestra/testbench-dusk": "^9|^10|^11", - "php-debugbar/twig-bridge": "^2.0", - "phpstan/phpstan-phpunit": "^2", - "phpstan/phpstan-strict-rules": "^2.0", - "phpunit/phpunit": "^11", - "shipmonk/phpstan-rules": "^4.3" + "amphp/php-cs-fixer-config": "^2", + "phpunit/phpunit": "^9", + "psalm/phar": "5.23.1" }, "type": "library", - "extra": { - "laravel": { - "aliases": { - "Debugbar": "Fruitcake\\LaravelDebugbar\\Facades\\Debugbar" - }, - "providers": [ - "Fruitcake\\LaravelDebugbar\\ServiceProvider" - ] - }, - "branch-alias": { - "dev-master": "4.2-dev" - } - }, "autoload": { "files": [ - "src/helpers.php" + "src/functions.php", + "src/Future/functions.php", + "src/Internal/functions.php" ], "psr-4": { - "Fruitcake\\LaravelDebugbar\\": "src/" + "Amp\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -10061,80 +10036,84 @@ ], "authors": [ { - "name": "Fruitcake", - "homepage": "https://fruitcake.nl" + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" }, { - "name": "Barry vd. Heuvel", - "email": "barryvdh@gmail.com" + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" } ], - "description": "PHP Debugbar integration for Laravel", + "description": "A non-blocking concurrency framework for PHP applications.", + "homepage": "https://amphp.org/amp", "keywords": [ - "barryvdh", - "debug", - "debugbar", - "dev", - "laravel", - "profiler", - "webprofiler" + "async", + "asynchronous", + "awaitable", + "concurrency", + "event", + "event-loop", + "future", + "non-blocking", + "promise" ], "support": { - "issues": "https://github.com/fruitcake/laravel-debugbar/issues", - "source": "https://github.com/fruitcake/laravel-debugbar/tree/v4.3.0" + "issues": "https://github.com/amphp/amp/issues", + "source": "https://github.com/amphp/amp/tree/v3.1.1" }, "funding": [ { - "url": "https://fruitcake.nl", - "type": "custom" - }, - { - "url": "https://github.com/barryvdh", + "url": "https://github.com/amphp", "type": "github" } ], - "time": "2026-06-04T07:54:01+00:00" + "time": "2025-08-27T21:42:00+00:00" }, { - "name": "fakerphp/faker", - "version": "v1.24.1", + "name": "amphp/byte-stream", + "version": "v2.1.2", "source": { "type": "git", - "url": "https://github.com/FakerPHP/Faker.git", - "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5" + "url": "https://github.com/amphp/byte-stream.git", + "reference": "55a6bd071aec26fa2a3e002618c20c35e3df1b46" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", - "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", + "url": "https://api.github.com/repos/amphp/byte-stream/zipball/55a6bd071aec26fa2a3e002618c20c35e3df1b46", + "reference": "55a6bd071aec26fa2a3e002618c20c35e3df1b46", "shasum": "" }, "require": { - "php": "^7.4 || ^8.0", - "psr/container": "^1.0 || ^2.0", - "symfony/deprecation-contracts": "^2.2 || ^3.0" - }, - "conflict": { - "fzaninotto/faker": "*" + "amphp/amp": "^3", + "amphp/parser": "^1.1", + "amphp/pipeline": "^1", + "amphp/serialization": "^1", + "amphp/sync": "^2", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2.3" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.4.1", - "doctrine/persistence": "^1.3 || ^2.0", - "ext-intl": "*", - "phpunit/phpunit": "^9.5.26", - "symfony/phpunit-bridge": "^5.4.16" - }, - "suggest": { - "doctrine/orm": "Required to use Faker\\ORM\\Doctrine", - "ext-curl": "Required by Faker\\Provider\\Image to download images.", - "ext-dom": "Required by Faker\\Provider\\HtmlLorem for generating random HTML.", - "ext-iconv": "Required by Faker\\Provider\\ru_RU\\Text::realText() for generating real Russian text.", - "ext-mbstring": "Required for multibyte Unicode string functionality." + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "5.22.1" }, "type": "library", "autoload": { + "files": [ + "src/functions.php", + "src/Internal/functions.php" + ], "psr-4": { - "Faker\\": "src/Faker/" + "Amp\\ByteStream\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -10143,57 +10122,67 @@ ], "authors": [ { - "name": "François Zaninotto" + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" } ], - "description": "Faker is a PHP library that generates fake data for you.", + "description": "A stream abstraction to make working with non-blocking I/O simple.", + "homepage": "https://amphp.org/byte-stream", "keywords": [ - "data", - "faker", - "fixtures" + "amp", + "amphp", + "async", + "io", + "non-blocking", + "stream" ], "support": { - "issues": "https://github.com/FakerPHP/Faker/issues", - "source": "https://github.com/FakerPHP/Faker/tree/v1.24.1" + "issues": "https://github.com/amphp/byte-stream/issues", + "source": "https://github.com/amphp/byte-stream/tree/v2.1.2" }, - "time": "2024-11-21T13:46:39+00:00" + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2025-03-16T17:10:27+00:00" }, { - "name": "filp/whoops", - "version": "2.18.4", + "name": "amphp/cache", + "version": "v2.0.1", "source": { "type": "git", - "url": "https://github.com/filp/whoops.git", - "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d" + "url": "https://github.com/amphp/cache.git", + "reference": "46912e387e6aa94933b61ea1ead9cf7540b7797c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/d2102955e48b9fd9ab24280a7ad12ed552752c4d", - "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d", + "url": "https://api.github.com/repos/amphp/cache/zipball/46912e387e6aa94933b61ea1ead9cf7540b7797c", + "reference": "46912e387e6aa94933b61ea1ead9cf7540b7797c", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0", - "psr/log": "^1.0.1 || ^2.0 || ^3.0" + "amphp/amp": "^3", + "amphp/serialization": "^1", + "amphp/sync": "^2", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" }, "require-dev": { - "mockery/mockery": "^1.0", - "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.3.3", - "symfony/var-dumper": "^4.0 || ^5.0" - }, - "suggest": { - "symfony/var-dumper": "Pretty print complex values better with var-dumper available", - "whoops/soap": "Formats errors as SOAP responses" + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.4" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.7-dev" - } - }, "autoload": { "psr-4": { - "Whoops\\": "src/Whoops/" + "Amp\\Cache\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -10202,124 +10191,153 @@ ], "authors": [ { - "name": "Filipe Dobreira", - "homepage": "https://github.com/filp", - "role": "Developer" + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" } ], - "description": "php error handling for cool kids", - "homepage": "https://filp.github.io/whoops/", - "keywords": [ - "error", - "exception", - "handling", - "library", - "throwable", - "whoops" - ], + "description": "A fiber-aware cache API based on Amp and Revolt.", + "homepage": "https://amphp.org/cache", "support": { - "issues": "https://github.com/filp/whoops/issues", - "source": "https://github.com/filp/whoops/tree/2.18.4" + "issues": "https://github.com/amphp/cache/issues", + "source": "https://github.com/amphp/cache/tree/v2.0.1" }, "funding": [ { - "url": "https://github.com/denis-sokolov", + "url": "https://github.com/amphp", "type": "github" } ], - "time": "2025-08-08T12:00:00+00:00" + "time": "2024-04-19T03:38:06+00:00" }, { - "name": "hamcrest/hamcrest-php", - "version": "v2.1.1", + "name": "amphp/dns", + "version": "v2.4.0", "source": { "type": "git", - "url": "https://github.com/hamcrest/hamcrest-php.git", - "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487" + "url": "https://github.com/amphp/dns.git", + "reference": "78eb3db5fc69bf2fc0cb503c4fcba667bc223c71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", - "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", + "url": "https://api.github.com/repos/amphp/dns/zipball/78eb3db5fc69bf2fc0cb503c4fcba667bc223c71", + "reference": "78eb3db5fc69bf2fc0cb503c4fcba667bc223c71", "shasum": "" }, "require": { - "php": "^7.4|^8.0" - }, - "replace": { - "cordoval/hamcrest-php": "*", - "davedevelopment/hamcrest-php": "*", - "kodova/hamcrest-php": "*" + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/cache": "^2", + "amphp/parser": "^1", + "amphp/process": "^2", + "daverandom/libdns": "^2.0.2", + "ext-filter": "*", + "ext-json": "*", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" }, "require-dev": { - "phpunit/php-file-iterator": "^1.4 || ^2.0 || ^3.0", - "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0 || ^8.0 || ^9.0" + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "5.20" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.1-dev" - } - }, "autoload": { - "classmap": [ - "hamcrest" - ] + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Dns\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "This is the PHP port of Hamcrest Matchers", + "authors": [ + { + "name": "Chris Wright", + "email": "addr@daverandom.com" + }, + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + }, + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + } + ], + "description": "Async DNS resolution for Amp.", + "homepage": "https://github.com/amphp/dns", "keywords": [ - "test" + "amp", + "amphp", + "async", + "client", + "dns", + "resolve" ], "support": { - "issues": "https://github.com/hamcrest/hamcrest-php/issues", - "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.1.1" + "issues": "https://github.com/amphp/dns/issues", + "source": "https://github.com/amphp/dns/tree/v2.4.0" }, - "time": "2025-04-30T06:54:44+00:00" + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2025-01-19T15:43:40+00:00" }, { - "name": "laravel/pint", - "version": "v1.29.1", + "name": "amphp/hpack", + "version": "v3.2.2", "source": { "type": "git", - "url": "https://github.com/laravel/pint.git", - "reference": "0770e9b7fafd50d4586881d456d6eb41c9247a80" + "url": "https://github.com/amphp/hpack.git", + "reference": "291da27078e7e149a9bad4d08ff05bf7d81c89f4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/0770e9b7fafd50d4586881d456d6eb41c9247a80", - "reference": "0770e9b7fafd50d4586881d456d6eb41c9247a80", + "url": "https://api.github.com/repos/amphp/hpack/zipball/291da27078e7e149a9bad4d08ff05bf7d81c89f4", + "reference": "291da27078e7e149a9bad4d08ff05bf7d81c89f4", "shasum": "" }, "require": { - "ext-json": "*", - "ext-mbstring": "*", - "ext-tokenizer": "*", - "ext-xml": "*", - "php": "^8.2.0" + "php": ">=7.1" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.95.1", - "illuminate/view": "^12.56.0", - "larastan/larastan": "^3.9.6", - "laravel-zero/framework": "^12.1.0", - "mockery/mockery": "^1.6.12", - "nunomaduro/termwind": "^2.4.0", - "pestphp/pest": "^3.8.6", - "shipfastlabs/agent-detector": "^1.1.3" + "amphp/php-cs-fixer-config": "^2", + "http2jp/hpack-test-case": "^1", + "nikic/php-fuzzer": "^0.0.11", + "phpunit/phpunit": "^7 | ^8 | ^9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } }, - "bin": [ - "builds/pint" - ], - "type": "project", "autoload": { "psr-4": { - "App\\": "app/", - "Database\\Seeders\\": "database/seeders/", - "Database\\Factories\\": "database/factories/" + "Amp\\Http\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -10328,66 +10346,2136 @@ ], "authors": [ { - "name": "Nuno Maduro", - "email": "enunomaduro@gmail.com" + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + }, + { + "name": "Bob Weinand" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" } ], - "description": "An opinionated code formatter for PHP.", - "homepage": "https://laravel.com", + "description": "HTTP/2 HPack implementation.", + "homepage": "https://github.com/amphp/hpack", "keywords": [ + "headers", + "hpack", + "http-2" + ], + "support": { + "issues": "https://github.com/amphp/hpack/issues", + "source": "https://github.com/amphp/hpack/tree/v3.2.2" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2026-05-03T19:28:59+00:00" + }, + { + "name": "amphp/http", + "version": "v2.1.2", + "source": { + "type": "git", + "url": "https://github.com/amphp/http.git", + "reference": "3680d80bd38b5d6f3c2cef2214ca6dd6cef26588" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/http/zipball/3680d80bd38b5d6f3c2cef2214ca6dd6cef26588", + "reference": "3680d80bd38b5d6f3c2cef2214ca6dd6cef26588", + "shasum": "" + }, + "require": { + "amphp/hpack": "^3", + "amphp/parser": "^1.1", + "league/uri-components": "^2.4.2 | ^7.1", + "php": ">=8.1", + "psr/http-message": "^1 | ^2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "league/uri": "^6.8 | ^7.1", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.26.1" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php", + "src/Internal/constants.php" + ], + "psr-4": { + "Amp\\Http\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + } + ], + "description": "Basic HTTP primitives which can be shared by servers and clients.", + "support": { + "issues": "https://github.com/amphp/http/issues", + "source": "https://github.com/amphp/http/tree/v2.1.2" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-11-23T14:57:26+00:00" + }, + { + "name": "amphp/http-client", + "version": "v5.3.6", + "source": { + "type": "git", + "url": "https://github.com/amphp/http-client.git", + "reference": "ca155026acafa74a612d776a97202d53077fee86" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/http-client/zipball/ca155026acafa74a612d776a97202d53077fee86", + "reference": "ca155026acafa74a612d776a97202d53077fee86", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/hpack": "^3", + "amphp/http": "^2", + "amphp/pipeline": "^1", + "amphp/socket": "^2", + "amphp/sync": "^2", + "league/uri": "^7", + "league/uri-components": "^7", + "league/uri-interfaces": "^7.1", + "php": ">=8.1", + "psr/http-message": "^1 | ^2", + "revolt/event-loop": "^1" + }, + "conflict": { + "amphp/file": "<3 | >=5" + }, + "require-dev": { + "amphp/file": "^3 | ^4", + "amphp/http-server": "^3", + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "ext-json": "*", + "kelunik/link-header-rfc5988": "^1", + "phpunit/phpunit": "^9", + "psalm/phar": "6.16.1" + }, + "suggest": { + "amphp/file": "Required for file request bodies and HTTP archive logging", + "ext-json": "Required for logging HTTP archives", + "ext-zlib": "Allows using compression for response bodies." + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php", + "src/Internal/functions.php" + ], + "psr-4": { + "Amp\\Http\\Client\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Lowrey", + "email": "rdlowrey@gmail.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + } + ], + "description": "An advanced async HTTP client library for PHP, enabling efficient, non-blocking, and concurrent requests and responses.", + "homepage": "https://amphp.org/http-client", + "keywords": [ + "async", + "client", + "concurrent", + "http", + "non-blocking", + "rest" + ], + "support": { + "issues": "https://github.com/amphp/http-client/issues", + "source": "https://github.com/amphp/http-client/tree/v5.3.6" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2026-05-15T23:29:38+00:00" + }, + { + "name": "amphp/http-server", + "version": "v3.4.5", + "source": { + "type": "git", + "url": "https://github.com/amphp/http-server.git", + "reference": "ae0fd01e16aba336247852df0c3f8c649a31896d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/http-server/zipball/ae0fd01e16aba336247852df0c3f8c649a31896d", + "reference": "ae0fd01e16aba336247852df0c3f8c649a31896d", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/cache": "^2", + "amphp/hpack": "^3", + "amphp/http": "^2", + "amphp/pipeline": "^1", + "amphp/socket": "^2.1", + "amphp/sync": "^2.2", + "league/uri": "^7.1", + "league/uri-interfaces": "^7.1", + "php": ">=8.1", + "psr/http-message": "^1 | ^2", + "psr/log": "^1 | ^2 | ^3", + "revolt/event-loop": "^1" + }, + "require-dev": { + "amphp/http-client": "^5", + "amphp/log": "^2", + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "league/uri-components": "^7.1", + "monolog/monolog": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "6.16.1" + }, + "suggest": { + "ext-zlib": "Allows GZip compression of response bodies" + }, + "type": "library", + "autoload": { + "files": [ + "src/Driver/functions.php", + "src/Middleware/functions.php", + "src/functions.php" + ], + "psr-4": { + "Amp\\Http\\Server\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + }, + { + "name": "Bob Weinand" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + } + ], + "description": "A non-blocking HTTP application server for PHP based on Amp.", + "homepage": "https://github.com/amphp/http-server", + "keywords": [ + "amp", + "amphp", + "async", + "http", + "non-blocking", + "server" + ], + "support": { + "issues": "https://github.com/amphp/http-server/issues", + "source": "https://github.com/amphp/http-server/tree/v3.4.5" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2026-05-01T03:55:07+00:00" + }, + { + "name": "amphp/parser", + "version": "v1.1.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/parser.git", + "reference": "3cf1f8b32a0171d4b1bed93d25617637a77cded7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/parser/zipball/3cf1f8b32a0171d4b1bed93d25617637a77cded7", + "reference": "3cf1f8b32a0171d4b1bed93d25617637a77cded7", + "shasum": "" + }, + "require": { + "php": ">=7.4" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Amp\\Parser\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A generator parser to make streaming parsers simple.", + "homepage": "https://github.com/amphp/parser", + "keywords": [ + "async", + "non-blocking", + "parser", + "stream" + ], + "support": { + "issues": "https://github.com/amphp/parser/issues", + "source": "https://github.com/amphp/parser/tree/v1.1.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-03-21T19:16:53+00:00" + }, + { + "name": "amphp/pipeline", + "version": "v1.2.4", + "source": { + "type": "git", + "url": "https://github.com/amphp/pipeline.git", + "reference": "a044733e080940d1483f56caff0c412ad6982776" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/pipeline/zipball/a044733e080940d1483f56caff0c412ad6982776", + "reference": "a044733e080940d1483f56caff0c412ad6982776", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "php": ">=8.1", + "revolt/event-loop": "^1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "6.16.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Amp\\Pipeline\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Asynchronous iterators and operators.", + "homepage": "https://amphp.org/pipeline", + "keywords": [ + "amp", + "amphp", + "async", + "io", + "iterator", + "non-blocking" + ], + "support": { + "issues": "https://github.com/amphp/pipeline/issues", + "source": "https://github.com/amphp/pipeline/tree/v1.2.4" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2026-05-06T05:37:57+00:00" + }, + { + "name": "amphp/process", + "version": "v2.1.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/process.git", + "reference": "583959df17d00304ad7b0b32285373f985935643" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/process/zipball/583959df17d00304ad7b0b32285373f985935643", + "reference": "583959df17d00304ad7b0b32285373f985935643", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/sync": "^2", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "6.16.1" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Process\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A fiber-aware process manager based on Amp and Revolt.", + "homepage": "https://amphp.org/process", + "support": { + "issues": "https://github.com/amphp/process/issues", + "source": "https://github.com/amphp/process/tree/v2.1.0" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2026-05-31T15:11:55+00:00" + }, + { + "name": "amphp/serialization", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/serialization.git", + "reference": "fdf2834d78cebb0205fb2672676c1b1eb84371f0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/serialization/zipball/fdf2834d78cebb0205fb2672676c1b1eb84371f0", + "reference": "fdf2834d78cebb0205fb2672676c1b1eb84371f0", + "shasum": "" + }, + "require": { + "php": ">=7.4" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "ext-json": "*", + "ext-zlib": "*", + "phpunit/phpunit": "^9", + "psalm/phar": "6.16.1" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Serialization\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Serialization tools for IPC and data storage in PHP.", + "homepage": "https://github.com/amphp/serialization", + "keywords": [ + "async", + "asynchronous", + "serialization", + "serialize" + ], + "support": { + "issues": "https://github.com/amphp/serialization/issues", + "source": "https://github.com/amphp/serialization/tree/v1.1.0" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2026-04-05T15:59:53+00:00" + }, + { + "name": "amphp/socket", + "version": "v2.4.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/socket.git", + "reference": "dadb63c5d3179fd83803e29dfeac27350e619314" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/socket/zipball/dadb63c5d3179fd83803e29dfeac27350e619314", + "reference": "dadb63c5d3179fd83803e29dfeac27350e619314", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/dns": "^2", + "ext-openssl": "*", + "kelunik/certificate": "^1.1", + "league/uri": "^7", + "league/uri-interfaces": "^7", + "php": ">=8.1", + "revolt/event-loop": "^1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "amphp/process": "^2", + "phpunit/phpunit": "^9", + "psalm/phar": "6.16.1" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php", + "src/Internal/functions.php", + "src/SocketAddress/functions.php" + ], + "psr-4": { + "Amp\\Socket\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Lowrey", + "email": "rdlowrey@gmail.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Non-blocking socket connection / server implementations based on Amp and Revolt.", + "homepage": "https://github.com/amphp/socket", + "keywords": [ + "amp", + "async", + "encryption", + "non-blocking", + "sockets", + "tcp", + "tls" + ], + "support": { + "issues": "https://github.com/amphp/socket/issues", + "source": "https://github.com/amphp/socket/tree/v2.4.0" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2026-04-19T15:09:56+00:00" + }, + { + "name": "amphp/sync", + "version": "v2.3.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/sync.git", + "reference": "217097b785130d77cfcc58ff583cf26cd1770bf1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/sync/zipball/217097b785130d77cfcc58ff583cf26cd1770bf1", + "reference": "217097b785130d77cfcc58ff583cf26cd1770bf1", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/pipeline": "^1", + "amphp/serialization": "^1", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "5.23" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Sync\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Stephen Coakley", + "email": "me@stephencoakley.com" + } + ], + "description": "Non-blocking synchronization primitives for PHP based on Amp and Revolt.", + "homepage": "https://github.com/amphp/sync", + "keywords": [ + "async", + "asynchronous", + "mutex", + "semaphore", + "synchronization" + ], + "support": { + "issues": "https://github.com/amphp/sync/issues", + "source": "https://github.com/amphp/sync/tree/v2.3.0" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-08-03T19:31:26+00:00" + }, + { + "name": "amphp/websocket", + "version": "v2.0.4", + "source": { + "type": "git", + "url": "https://github.com/amphp/websocket.git", + "reference": "963904b6a883c4b62d9222d1d9749814fac96a3b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/websocket/zipball/963904b6a883c4b62d9222d1d9749814fac96a3b", + "reference": "963904b6a883c4b62d9222d1d9749814fac96a3b", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/parser": "^1", + "amphp/pipeline": "^1", + "amphp/socket": "^2", + "php": ">=8.1", + "revolt/event-loop": "^1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.18" + }, + "suggest": { + "ext-zlib": "Required for compression" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Websocket\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + } + ], + "description": "Shared code for websocket servers and clients.", + "homepage": "https://github.com/amphp/websocket", + "keywords": [ + "amp", + "amphp", + "async", + "http", + "non-blocking", + "websocket" + ], + "support": { + "issues": "https://github.com/amphp/websocket/issues", + "source": "https://github.com/amphp/websocket/tree/v2.0.4" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-10-28T21:28:45+00:00" + }, + { + "name": "amphp/websocket-client", + "version": "v2.0.2", + "source": { + "type": "git", + "url": "https://github.com/amphp/websocket-client.git", + "reference": "dc033fdce0af56295a23f63ac4f579b34d470d6c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/websocket-client/zipball/dc033fdce0af56295a23f63ac4f579b34d470d6c", + "reference": "dc033fdce0af56295a23f63ac4f579b34d470d6c", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/byte-stream": "^2.1", + "amphp/http": "^2.1", + "amphp/http-client": "^5", + "amphp/socket": "^2.2", + "amphp/websocket": "^2", + "league/uri": "^7.1", + "php": ">=8.1", + "psr/http-message": "^1|^2", + "revolt/event-loop": "^1" + }, + "require-dev": { + "amphp/http-server": "^3", + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "amphp/websocket-server": "^3|^4", + "phpunit/phpunit": "^9", + "psalm/phar": "~5.26.1", + "psr/log": "^1" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Websocket\\Client\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Async WebSocket client for PHP based on Amp.", + "keywords": [ + "amp", + "amphp", + "async", + "client", + "http", + "non-blocking", + "websocket" + ], + "support": { + "issues": "https://github.com/amphp/websocket-client/issues", + "source": "https://github.com/amphp/websocket-client/tree/v2.0.2" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2025-08-24T17:25:34+00:00" + }, + { + "name": "barryvdh/laravel-debugbar", + "version": "v4.3.0", + "source": { + "type": "git", + "url": "https://github.com/fruitcake/laravel-debugbar.git", + "reference": "3d76ea8d78b82225b92789de65fc630c1cd8e80c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fruitcake/laravel-debugbar/zipball/3d76ea8d78b82225b92789de65fc630c1cd8e80c", + "reference": "3d76ea8d78b82225b92789de65fc630c1cd8e80c", + "shasum": "" + }, + "require": { + "illuminate/routing": "^11|^12|^13.0", + "illuminate/session": "^11|^12|^13.0", + "illuminate/support": "^11|^12|^13.0", + "php": "^8.2", + "php-debugbar/php-debugbar": "^3.7.2", + "php-debugbar/symfony-bridge": "^1.1" + }, + "require-dev": { + "larastan/larastan": "^3", + "laravel/octane": "^2", + "laravel/pennant": "^1", + "laravel/pint": "^1", + "laravel/telescope": "^5.16", + "livewire/livewire": "^3.7|^4", + "mockery/mockery": "^1.3.3", + "orchestra/testbench-dusk": "^9|^10|^11", + "php-debugbar/twig-bridge": "^2.0", + "phpstan/phpstan-phpunit": "^2", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^11", + "shipmonk/phpstan-rules": "^4.3" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Debugbar": "Fruitcake\\LaravelDebugbar\\Facades\\Debugbar" + }, + "providers": [ + "Fruitcake\\LaravelDebugbar\\ServiceProvider" + ] + }, + "branch-alias": { + "dev-master": "4.2-dev" + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Fruitcake\\LaravelDebugbar\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fruitcake", + "homepage": "https://fruitcake.nl" + }, + { + "name": "Barry vd. Heuvel", + "email": "barryvdh@gmail.com" + } + ], + "description": "PHP Debugbar integration for Laravel", + "keywords": [ + "barryvdh", + "debug", + "debugbar", + "dev", + "laravel", + "profiler", + "webprofiler" + ], + "support": { + "issues": "https://github.com/fruitcake/laravel-debugbar/issues", + "source": "https://github.com/fruitcake/laravel-debugbar/tree/v4.3.0" + }, + "funding": [ + { + "url": "https://fruitcake.nl", + "type": "custom" + }, + { + "url": "https://github.com/barryvdh", + "type": "github" + } + ], + "time": "2026-06-04T07:54:01+00:00" + }, + { + "name": "brianium/paratest", + "version": "v7.20.0", + "source": { + "type": "git", + "url": "https://github.com/paratestphp/paratest.git", + "reference": "81c80677c9ec0ed4ef16b246167f11dec81a6e3d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paratestphp/paratest/zipball/81c80677c9ec0ed4ef16b246167f11dec81a6e3d", + "reference": "81c80677c9ec0ed4ef16b246167f11dec81a6e3d", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-simplexml": "*", + "fidry/cpu-core-counter": "^1.3.0", + "jean85/pretty-package-versions": "^2.1.1", + "php": "~8.3.0 || ~8.4.0 || ~8.5.0", + "phpunit/php-code-coverage": "^12.5.3 || ^13.0.1", + "phpunit/php-file-iterator": "^6.0.1 || ^7", + "phpunit/php-timer": "^8 || ^9", + "phpunit/phpunit": "^12.5.14 || ^13.0.5", + "sebastian/environment": "^8.0.3 || ^9", + "symfony/console": "^7.4.7 || ^8.0.7", + "symfony/process": "^7.4.5 || ^8.0.5" + }, + "require-dev": { + "doctrine/coding-standard": "^14.0.0", + "ext-pcntl": "*", + "ext-pcov": "*", + "ext-posix": "*", + "phpstan/phpstan": "^2.1.44", + "phpstan/phpstan-deprecation-rules": "^2.0.4", + "phpstan/phpstan-phpunit": "^2.0.16", + "phpstan/phpstan-strict-rules": "^2.0.10", + "symfony/filesystem": "^7.4.6 || ^8.0.6" + }, + "bin": [ + "bin/paratest", + "bin/paratest_for_phpstorm" + ], + "type": "library", + "autoload": { + "psr-4": { + "ParaTest\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Scaturro", + "email": "scaturrob@gmail.com", + "role": "Developer" + }, + { + "name": "Filippo Tessarotto", + "email": "zoeslam@gmail.com", + "role": "Developer" + } + ], + "description": "Parallel testing for PHP", + "homepage": "https://github.com/paratestphp/paratest", + "keywords": [ + "concurrent", + "parallel", + "phpunit", + "testing" + ], + "support": { + "issues": "https://github.com/paratestphp/paratest/issues", + "source": "https://github.com/paratestphp/paratest/tree/v7.20.0" + }, + "funding": [ + { + "url": "https://github.com/sponsors/Slamdunk", + "type": "github" + }, + { + "url": "https://paypal.me/filippotessarotto", + "type": "paypal" + } + ], + "time": "2026-03-29T15:46:14+00:00" + }, + { + "name": "composer/xdebug-handler", + "version": "3.0.5", + "source": { + "type": "git", + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef", + "shasum": "" + }, + "require": { + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" + }, + "require-dev": { + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without Xdebug.", + "keywords": [ + "Xdebug", + "performance" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/3.0.5" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-05-06T16:37:16+00:00" + }, + { + "name": "daverandom/libdns", + "version": "v2.1.0", + "source": { + "type": "git", + "url": "https://github.com/DaveRandom/LibDNS.git", + "reference": "b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/DaveRandom/LibDNS/zipball/b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a", + "reference": "b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "Required for IDN support" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "LibDNS\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "DNS protocol implementation written in pure PHP", + "keywords": [ + "dns" + ], + "support": { + "issues": "https://github.com/DaveRandom/LibDNS/issues", + "source": "https://github.com/DaveRandom/LibDNS/tree/v2.1.0" + }, + "time": "2024-04-12T12:12:48+00:00" + }, + { + "name": "doctrine/deprecations", + "version": "1.1.6", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "phpunit/phpunit": "<=7.5 || >=14" + }, + "require-dev": { + "doctrine/coding-standard": "^9 || ^12 || ^14", + "phpstan/phpstan": "1.4.10 || 2.1.30", + "phpstan/phpstan-phpunit": "^1.0 || ^2", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12.4 || ^13.0", + "psr/log": "^1 || ^2 || ^3" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.6" + }, + "time": "2026-02-07T07:09:04+00:00" + }, + { + "name": "fakerphp/faker", + "version": "v1.24.1", + "source": { + "type": "git", + "url": "https://github.com/FakerPHP/Faker.git", + "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", + "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "psr/container": "^1.0 || ^2.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "conflict": { + "fzaninotto/faker": "*" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "doctrine/persistence": "^1.3 || ^2.0", + "ext-intl": "*", + "phpunit/phpunit": "^9.5.26", + "symfony/phpunit-bridge": "^5.4.16" + }, + "suggest": { + "doctrine/orm": "Required to use Faker\\ORM\\Doctrine", + "ext-curl": "Required by Faker\\Provider\\Image to download images.", + "ext-dom": "Required by Faker\\Provider\\HtmlLorem for generating random HTML.", + "ext-iconv": "Required by Faker\\Provider\\ru_RU\\Text::realText() for generating real Russian text.", + "ext-mbstring": "Required for multibyte Unicode string functionality." + }, + "type": "library", + "autoload": { + "psr-4": { + "Faker\\": "src/Faker/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "François Zaninotto" + } + ], + "description": "Faker is a PHP library that generates fake data for you.", + "keywords": [ + "data", + "faker", + "fixtures" + ], + "support": { + "issues": "https://github.com/FakerPHP/Faker/issues", + "source": "https://github.com/FakerPHP/Faker/tree/v1.24.1" + }, + "time": "2024-11-21T13:46:39+00:00" + }, + { + "name": "fidry/cpu-core-counter", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/theofidry/cpu-core-counter.git", + "reference": "db9508f7b1474469d9d3c53b86f817e344732678" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/db9508f7b1474469d9d3c53b86f817e344732678", + "reference": "db9508f7b1474469d9d3c53b86f817e344732678", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "fidry/makefile": "^0.2.0", + "fidry/php-cs-fixer-config": "^1.1.2", + "phpstan/extension-installer": "^1.2.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-deprecation-rules": "^2.0.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^8.5.31 || ^9.5.26", + "webmozarts/strict-phpunit": "^7.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Fidry\\CpuCoreCounter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Théo FIDRY", + "email": "theo.fidry@gmail.com" + } + ], + "description": "Tiny utility to get the number of CPU cores.", + "keywords": [ + "CPU", + "core" + ], + "support": { + "issues": "https://github.com/theofidry/cpu-core-counter/issues", + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.3.0" + }, + "funding": [ + { + "url": "https://github.com/theofidry", + "type": "github" + } + ], + "time": "2025-08-14T07:29:31+00:00" + }, + { + "name": "filp/whoops", + "version": "2.18.4", + "source": { + "type": "git", + "url": "https://github.com/filp/whoops.git", + "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/filp/whoops/zipball/d2102955e48b9fd9ab24280a7ad12ed552752c4d", + "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0", + "psr/log": "^1.0.1 || ^2.0 || ^3.0" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.3.3", + "symfony/var-dumper": "^4.0 || ^5.0" + }, + "suggest": { + "symfony/var-dumper": "Pretty print complex values better with var-dumper available", + "whoops/soap": "Formats errors as SOAP responses" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Whoops\\": "src/Whoops/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Filipe Dobreira", + "homepage": "https://github.com/filp", + "role": "Developer" + } + ], + "description": "php error handling for cool kids", + "homepage": "https://filp.github.io/whoops/", + "keywords": [ + "error", + "exception", + "handling", + "library", + "throwable", + "whoops" + ], + "support": { + "issues": "https://github.com/filp/whoops/issues", + "source": "https://github.com/filp/whoops/tree/2.18.4" + }, + "funding": [ + { + "url": "https://github.com/denis-sokolov", + "type": "github" + } + ], + "time": "2025-08-08T12:00:00+00:00" + }, + { + "name": "hamcrest/hamcrest-php", + "version": "v2.1.1", + "source": { + "type": "git", + "url": "https://github.com/hamcrest/hamcrest-php.git", + "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", + "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0" + }, + "replace": { + "cordoval/hamcrest-php": "*", + "davedevelopment/hamcrest-php": "*", + "kodova/hamcrest-php": "*" + }, + "require-dev": { + "phpunit/php-file-iterator": "^1.4 || ^2.0 || ^3.0", + "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + }, + "autoload": { + "classmap": [ + "hamcrest" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "This is the PHP port of Hamcrest Matchers", + "keywords": [ + "test" + ], + "support": { + "issues": "https://github.com/hamcrest/hamcrest-php/issues", + "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.1.1" + }, + "time": "2025-04-30T06:54:44+00:00" + }, + { + "name": "jean85/pretty-package-versions", + "version": "2.1.1", + "source": { + "type": "git", + "url": "https://github.com/Jean85/pretty-package-versions.git", + "reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/4d7aa5dab42e2a76d99559706022885de0e18e1a", + "reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.1.0", + "php": "^7.4|^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.2", + "jean85/composer-provided-replaced-stub-package": "^1.0", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^7.5|^8.5|^9.6", + "rector/rector": "^2.0", + "vimeo/psalm": "^4.3 || ^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Jean85\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alessandro Lai", + "email": "alessandro.lai85@gmail.com" + } + ], + "description": "A library to get pretty versions strings of installed dependencies", + "keywords": [ + "composer", + "package", + "release", + "versions" + ], + "support": { + "issues": "https://github.com/Jean85/pretty-package-versions/issues", + "source": "https://github.com/Jean85/pretty-package-versions/tree/2.1.1" + }, + "time": "2025-03-19T14:43:43+00:00" + }, + { + "name": "kelunik/certificate", + "version": "v1.1.3", + "source": { + "type": "git", + "url": "https://github.com/kelunik/certificate.git", + "reference": "7e00d498c264d5eb4f78c69f41c8bd6719c0199e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/kelunik/certificate/zipball/7e00d498c264d5eb4f78c69f41c8bd6719c0199e", + "reference": "7e00d498c264d5eb4f78c69f41c8bd6719c0199e", + "shasum": "" + }, + "require": { + "ext-openssl": "*", + "php": ">=7.0" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "phpunit/phpunit": "^6 | 7 | ^8 | ^9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Kelunik\\Certificate\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Access certificate details and transform between different formats.", + "keywords": [ + "DER", + "certificate", + "certificates", + "openssl", + "pem", + "x509" + ], + "support": { + "issues": "https://github.com/kelunik/certificate/issues", + "source": "https://github.com/kelunik/certificate/tree/v1.1.3" + }, + "time": "2023-02-03T21:26:53+00:00" + }, + { + "name": "laravel/pint", + "version": "v1.29.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/pint.git", + "reference": "0770e9b7fafd50d4586881d456d6eb41c9247a80" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/pint/zipball/0770e9b7fafd50d4586881d456d6eb41c9247a80", + "reference": "0770e9b7fafd50d4586881d456d6eb41c9247a80", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "ext-tokenizer": "*", + "ext-xml": "*", + "php": "^8.2.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.95.1", + "illuminate/view": "^12.56.0", + "larastan/larastan": "^3.9.6", + "laravel-zero/framework": "^12.1.0", + "mockery/mockery": "^1.6.12", + "nunomaduro/termwind": "^2.4.0", + "pestphp/pest": "^3.8.6", + "shipfastlabs/agent-detector": "^1.1.3" + }, + "bin": [ + "builds/pint" + ], + "type": "project", + "autoload": { + "psr-4": { + "App\\": "app/", + "Database\\Seeders\\": "database/seeders/", + "Database\\Factories\\": "database/factories/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "An opinionated code formatter for PHP.", + "homepage": "https://laravel.com", + "keywords": [ + "dev", + "format", + "formatter", + "lint", + "linter", + "php" + ], + "support": { + "issues": "https://github.com/laravel/pint/issues", + "source": "https://github.com/laravel/pint" + }, + "time": "2026-04-20T15:26:14+00:00" + }, + { + "name": "laravel/sail", + "version": "v1.62.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/sail.git", + "reference": "3aaeefc979f8ba6586fbc5b6e0b1b3638058f98e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/sail/zipball/3aaeefc979f8ba6586fbc5b6e0b1b3638058f98e", + "reference": "3aaeefc979f8ba6586fbc5b6e0b1b3638058f98e", + "shasum": "" + }, + "require": { + "illuminate/console": "^9.52.16|^10.0|^11.0|^12.0|^13.0", + "illuminate/contracts": "^9.52.16|^10.0|^11.0|^12.0|^13.0", + "illuminate/support": "^9.52.16|^10.0|^11.0|^12.0|^13.0", + "php": "^8.0", + "symfony/console": "^6.0|^7.0|^8.0", + "symfony/yaml": "^6.0|^7.0|^8.0" + }, + "require-dev": { + "orchestra/testbench": "^7.0|^8.0|^9.0|^10.0|^11.0", + "phpstan/phpstan": "^2.0" + }, + "bin": [ + "bin/sail" + ], + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Sail\\SailServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Sail\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Docker files for running a basic Laravel application.", + "keywords": [ + "docker", + "laravel" + ], + "support": { + "issues": "https://github.com/laravel/sail/issues", + "source": "https://github.com/laravel/sail" + }, + "time": "2026-05-27T04:02:01+00:00" + }, + { + "name": "league/uri-components", + "version": "7.8.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri-components.git", + "reference": "848ff9db2f0be06229d6034b7c2e33d41b4fd675" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri-components/zipball/848ff9db2f0be06229d6034b7c2e33d41b4fd675", + "reference": "848ff9db2f0be06229d6034b7c2e33d41b4fd675", + "shasum": "" + }, + "require": { + "league/uri": "^7.8.1", + "php": "^8.1" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-fileinfo": "to create Data URI from file contennts", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "ext-mbstring": "to use the sorting algorithm of URLSearchParams", + "jeremykendall/php-domain-parser": "to further parse the URI host and resolve its Public Suffix and Top Level Domain", + "league/uri-polyfill": "to backport the PHP URI extension for older versions of PHP", + "php-64bit": "to improve IPV4 host parsing", + "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Uri\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "URI components manipulation library", + "homepage": "http://uri.thephpleague.com", + "keywords": [ + "authority", + "components", + "fragment", + "host", + "middleware", + "modifier", + "path", + "port", + "query", + "rfc3986", + "scheme", + "uri", + "url", + "userinfo" + ], + "support": { + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri-components/tree/7.8.1" + }, + "funding": [ + { + "url": "https://github.com/nyamsprod", + "type": "github" + } + ], + "time": "2026-03-15T20:22:25+00:00" + }, + { + "name": "mockery/mockery", + "version": "1.6.12", + "source": { + "type": "git", + "url": "https://github.com/mockery/mockery.git", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mockery/mockery/zipball/1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "shasum": "" + }, + "require": { + "hamcrest/hamcrest-php": "^2.0.1", + "lib-pcre": ">=7.0", + "php": ">=7.3" + }, + "conflict": { + "phpunit/phpunit": "<8.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.5 || ^9.6.17", + "symplify/easy-coding-standard": "^12.1.14" + }, + "type": "library", + "autoload": { + "files": [ + "library/helpers.php", + "library/Mockery.php" + ], + "psr-4": { + "Mockery\\": "library/Mockery" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Pádraic Brady", + "email": "padraic.brady@gmail.com", + "homepage": "https://github.com/padraic", + "role": "Author" + }, + { + "name": "Dave Marshall", + "email": "dave.marshall@atstsolutions.co.uk", + "homepage": "https://davedevelopment.co.uk", + "role": "Developer" + }, + { + "name": "Nathanael Esayeas", + "email": "nathanael.esayeas@protonmail.com", + "homepage": "https://github.com/ghostwriter", + "role": "Lead Developer" + } + ], + "description": "Mockery is a simple yet flexible PHP mock object framework", + "homepage": "https://github.com/mockery/mockery", + "keywords": [ + "BDD", + "TDD", + "library", + "mock", + "mock objects", + "mockery", + "stub", + "test", + "test double", + "testing" + ], + "support": { + "docs": "https://docs.mockery.io/", + "issues": "https://github.com/mockery/mockery/issues", + "rss": "https://github.com/mockery/mockery/releases.atom", + "security": "https://github.com/mockery/mockery/security/advisories", + "source": "https://github.com/mockery/mockery" + }, + "time": "2024-05-16T03:13:13+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.13.4", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2025-08-01T08:46:24+00:00" + }, + { + "name": "nunomaduro/collision", + "version": "v8.9.4", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/collision.git", + "reference": "716af8f95a470e9094cfca09ed897b023be191a5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/716af8f95a470e9094cfca09ed897b023be191a5", + "reference": "716af8f95a470e9094cfca09ed897b023be191a5", + "shasum": "" + }, + "require": { + "filp/whoops": "^2.18.4", + "nunomaduro/termwind": "^2.4.0", + "php": "^8.2.0", + "symfony/console": "^7.4.8 || ^8.0.8" + }, + "conflict": { + "laravel/framework": "<11.48.0 || >=14.0.0", + "phpunit/phpunit": "<11.5.50 || >=14.0.0" + }, + "require-dev": { + "brianium/paratest": "^7.8.5", + "larastan/larastan": "^3.9.6", + "laravel/framework": "^11.48.0 || ^12.56.0 || ^13.5.0", + "laravel/pint": "^1.29.1", + "orchestra/testbench-core": "^9.12.0 || ^10.12.1 || ^11.2.1", + "pestphp/pest": "^3.8.5 || ^4.4.3 || ^5.0.0", + "sebastian/environment": "^7.2.1 || ^8.0.4 || ^9.3.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider" + ] + }, + "branch-alias": { + "dev-8.x": "8.x-dev" + } + }, + "autoload": { + "files": [ + "./src/Adapters/Phpunit/Autoload.php" + ], + "psr-4": { + "NunoMaduro\\Collision\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Cli error handling for console/command-line PHP applications.", + "keywords": [ + "artisan", + "cli", + "command-line", + "console", "dev", - "format", - "formatter", - "lint", - "linter", - "php" + "error", + "handling", + "laravel", + "laravel-zero", + "php", + "symfony" ], "support": { - "issues": "https://github.com/laravel/pint/issues", - "source": "https://github.com/laravel/pint" + "issues": "https://github.com/nunomaduro/collision/issues", + "source": "https://github.com/nunomaduro/collision" }, - "time": "2026-04-20T15:26:14+00:00" + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://www.patreon.com/nunomaduro", + "type": "patreon" + } + ], + "time": "2026-04-21T14:04:20+00:00" }, { - "name": "laravel/sail", - "version": "v1.62.0", + "name": "pestphp/pest", + "version": "v4.7.2", "source": { "type": "git", - "url": "https://github.com/laravel/sail.git", - "reference": "3aaeefc979f8ba6586fbc5b6e0b1b3638058f98e" + "url": "https://github.com/pestphp/pest.git", + "reference": "40b88b62ef8a7c6fcae5fc28f1fa747f601c131b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/sail/zipball/3aaeefc979f8ba6586fbc5b6e0b1b3638058f98e", - "reference": "3aaeefc979f8ba6586fbc5b6e0b1b3638058f98e", + "url": "https://api.github.com/repos/pestphp/pest/zipball/40b88b62ef8a7c6fcae5fc28f1fa747f601c131b", + "reference": "40b88b62ef8a7c6fcae5fc28f1fa747f601c131b", "shasum": "" }, "require": { - "illuminate/console": "^9.52.16|^10.0|^11.0|^12.0|^13.0", - "illuminate/contracts": "^9.52.16|^10.0|^11.0|^12.0|^13.0", - "illuminate/support": "^9.52.16|^10.0|^11.0|^12.0|^13.0", - "php": "^8.0", - "symfony/console": "^6.0|^7.0|^8.0", - "symfony/yaml": "^6.0|^7.0|^8.0" + "brianium/paratest": "^7.20.0", + "composer/xdebug-handler": "^3.0.5", + "nunomaduro/collision": "^8.9.4", + "nunomaduro/termwind": "^2.4.0", + "pestphp/pest-plugin": "^4.0.0", + "pestphp/pest-plugin-arch": "^4.0.2", + "pestphp/pest-plugin-mutate": "^4.0.1", + "pestphp/pest-plugin-profanity": "^4.2.1", + "php": "^8.3.0", + "phpunit/phpunit": "^12.5.28", + "symfony/process": "^7.4.13|^8.1.0" + }, + "conflict": { + "filp/whoops": "<2.18.3", + "phpunit/phpunit": ">12.5.28", + "sebastian/exporter": "<7.0.0", + "webmozart/assert": "<1.11.0" }, "require-dev": { - "orchestra/testbench": "^7.0|^8.0|^9.0|^10.0|^11.0", - "phpstan/phpstan": "^2.0" + "mrpunyapal/peststan": "^0.2.10", + "pestphp/pest-dev-tools": "^4.1.0", + "pestphp/pest-plugin-browser": "^4.3.1", + "pestphp/pest-plugin-type-coverage": "^4.0.4", + "psy/psysh": "^0.12.23" }, "bin": [ - "bin/sail" + "bin/pest" ], "type": "library", "extra": { - "laravel": { - "providers": [ - "Laravel\\Sail\\SailServiceProvider" + "pest": { + "plugins": [ + "Pest\\Mutate\\Plugins\\Mutate", + "Pest\\Plugins\\Configuration", + "Pest\\Plugins\\Bail", + "Pest\\Plugins\\Cache", + "Pest\\Plugins\\Coverage", + "Pest\\Plugins\\Init", + "Pest\\Plugins\\Environment", + "Pest\\Plugins\\Help", + "Pest\\Plugins\\Memory", + "Pest\\Plugins\\Only", + "Pest\\Plugins\\Printer", + "Pest\\Plugins\\ProcessIsolation", + "Pest\\Plugins\\Profile", + "Pest\\Plugins\\Retry", + "Pest\\Plugins\\Snapshot", + "Pest\\Plugins\\Verbose", + "Pest\\Plugins\\Version", + "Pest\\Plugins\\Shard", + "Pest\\Plugins\\Tia", + "Pest\\Plugins\\Parallel" + ] + }, + "phpstan": { + "includes": [ + "extension.neon" ] } }, "autoload": { + "files": [ + "src/Functions.php", + "src/Pest.php" + ], "psr-4": { - "Laravel\\Sail\\": "src/" + "Pest\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -10396,214 +12484,287 @@ ], "authors": [ { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" } ], - "description": "Docker files for running a basic Laravel application.", + "description": "The elegant PHP Testing Framework.", "keywords": [ - "docker", - "laravel" + "framework", + "pest", + "php", + "test", + "testing", + "unit" ], "support": { - "issues": "https://github.com/laravel/sail/issues", - "source": "https://github.com/laravel/sail" + "issues": "https://github.com/pestphp/pest/issues", + "source": "https://github.com/pestphp/pest/tree/v4.7.2" }, - "time": "2026-05-27T04:02:01+00:00" + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + } + ], + "time": "2026-06-01T06:08:59+00:00" }, { - "name": "mockery/mockery", - "version": "1.6.12", + "name": "pestphp/pest-plugin", + "version": "v4.0.0", "source": { "type": "git", - "url": "https://github.com/mockery/mockery.git", - "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699" + "url": "https://github.com/pestphp/pest-plugin.git", + "reference": "9d4b93d7f73d3f9c3189bb22c220fef271cdf568" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mockery/mockery/zipball/1f4efdd7d3beafe9807b08156dfcb176d18f1699", - "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "url": "https://api.github.com/repos/pestphp/pest-plugin/zipball/9d4b93d7f73d3f9c3189bb22c220fef271cdf568", + "reference": "9d4b93d7f73d3f9c3189bb22c220fef271cdf568", "shasum": "" }, "require": { - "hamcrest/hamcrest-php": "^2.0.1", - "lib-pcre": ">=7.0", - "php": ">=7.3" + "composer-plugin-api": "^2.0.0", + "composer-runtime-api": "^2.2.2", + "php": "^8.3" }, "conflict": { - "phpunit/phpunit": "<8.0" + "pestphp/pest": "<4.0.0" }, "require-dev": { - "phpunit/phpunit": "^8.5 || ^9.6.17", - "symplify/easy-coding-standard": "^12.1.14" + "composer/composer": "^2.8.10", + "pestphp/pest": "^4.0.0", + "pestphp/pest-dev-tools": "^4.0.0" + }, + "type": "composer-plugin", + "extra": { + "class": "Pest\\Plugin\\Manager" }, - "type": "library", "autoload": { - "files": [ - "library/helpers.php", - "library/Mockery.php" - ], "psr-4": { - "Mockery\\": "library/Mockery" + "Pest\\Plugin\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "authors": [ + "description": "The Pest plugin manager", + "keywords": [ + "framework", + "manager", + "pest", + "php", + "plugin", + "test", + "testing", + "unit" + ], + "support": { + "source": "https://github.com/pestphp/pest-plugin/tree/v4.0.0" + }, + "funding": [ { - "name": "Pádraic Brady", - "email": "padraic.brady@gmail.com", - "homepage": "https://github.com/padraic", - "role": "Author" + "url": "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=66BYDWAT92N6L", + "type": "custom" }, { - "name": "Dave Marshall", - "email": "dave.marshall@atstsolutions.co.uk", - "homepage": "https://davedevelopment.co.uk", - "role": "Developer" + "url": "https://github.com/nunomaduro", + "type": "github" }, { - "name": "Nathanael Esayeas", - "email": "nathanael.esayeas@protonmail.com", - "homepage": "https://github.com/ghostwriter", - "role": "Lead Developer" + "url": "https://www.patreon.com/nunomaduro", + "type": "patreon" } ], - "description": "Mockery is a simple yet flexible PHP mock object framework", - "homepage": "https://github.com/mockery/mockery", + "time": "2025-08-20T12:35:58+00:00" + }, + { + "name": "pestphp/pest-plugin-arch", + "version": "v4.0.2", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest-plugin-arch.git", + "reference": "3fb0d02a91b9da504b139dc7ab2a31efb7c3215c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest-plugin-arch/zipball/3fb0d02a91b9da504b139dc7ab2a31efb7c3215c", + "reference": "3fb0d02a91b9da504b139dc7ab2a31efb7c3215c", + "shasum": "" + }, + "require": { + "pestphp/pest-plugin": "^4.0.0", + "php": "^8.3", + "ta-tikoma/phpunit-architecture-test": "^0.8.7" + }, + "require-dev": { + "pestphp/pest": "^4.4.6", + "pestphp/pest-dev-tools": "^4.1.0" + }, + "type": "library", + "extra": { + "pest": { + "plugins": [ + "Pest\\Arch\\Plugin" + ] + } + }, + "autoload": { + "files": [ + "src/Autoload.php" + ], + "psr-4": { + "Pest\\Arch\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "The Arch plugin for Pest PHP.", "keywords": [ - "BDD", - "TDD", - "library", - "mock", - "mock objects", - "mockery", - "stub", + "arch", + "architecture", + "framework", + "pest", + "php", + "plugin", "test", - "test double", - "testing" + "testing", + "unit" ], "support": { - "docs": "https://docs.mockery.io/", - "issues": "https://github.com/mockery/mockery/issues", - "rss": "https://github.com/mockery/mockery/releases.atom", - "security": "https://github.com/mockery/mockery/security/advisories", - "source": "https://github.com/mockery/mockery" + "source": "https://github.com/pestphp/pest-plugin-arch/tree/v4.0.2" }, - "time": "2024-05-16T03:13:13+00:00" + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + } + ], + "time": "2026-04-10T17:20:19+00:00" }, { - "name": "myclabs/deep-copy", - "version": "1.13.4", + "name": "pestphp/pest-plugin-browser", + "version": "v4.3.1", "source": { "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" + "url": "https://github.com/pestphp/pest-plugin-browser.git", + "reference": "b6e76d3e4a2f81da9f050ec54be2a29b402287c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", - "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "url": "https://api.github.com/repos/pestphp/pest-plugin-browser/zipball/b6e76d3e4a2f81da9f050ec54be2a29b402287c4", + "reference": "b6e76d3e4a2f81da9f050ec54be2a29b402287c4", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" - }, - "conflict": { - "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3 <3.2.2" + "amphp/amp": "^3.1.1", + "amphp/http-server": "^3.4.4", + "amphp/websocket-client": "^2.0.2", + "ext-sockets": "*", + "pestphp/pest": "^4.4.5", + "pestphp/pest-plugin": "^4.0.0", + "php": "^8.3", + "symfony/process": "^7.4.8|^8.0.5" }, "require-dev": { - "doctrine/collections": "^1.6.8", - "doctrine/common": "^2.13.3 || ^3.2.2", - "phpspec/prophecy": "^1.10", - "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + "ext-pcntl": "*", + "ext-posix": "*", + "livewire/livewire": "^3.7.15", + "nunomaduro/collision": "^8.9.3", + "orchestra/testbench": "^10.11.0", + "pestphp/pest-dev-tools": "^4.1.0", + "pestphp/pest-plugin-laravel": "^4.1", + "pestphp/pest-plugin-type-coverage": "^4.0.4" }, "type": "library", + "extra": { + "pest": { + "plugins": [ + "Pest\\Browser\\Plugin" + ] + } + }, "autoload": { "files": [ - "src/DeepCopy/deep_copy.php" + "src/Autoload.php" ], "psr-4": { - "DeepCopy\\": "src/DeepCopy/" + "Pest\\Browser\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "Create deep copies (clones) of your objects", + "description": "Pest plugin to test browser interactions", "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" + "browser", + "framework", + "pest", + "php", + "test", + "testing", + "unit" ], "support": { - "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" + "source": "https://github.com/pestphp/pest-plugin-browser/tree/v4.3.1" }, "funding": [ { - "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", - "type": "tidelift" + "url": "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=66BYDWAT92N6L", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://www.patreon.com/nunomaduro", + "type": "patreon" } ], - "time": "2025-08-01T08:46:24+00:00" + "time": "2026-04-08T21:04:12+00:00" }, { - "name": "nunomaduro/collision", - "version": "v8.9.4", + "name": "pestphp/pest-plugin-mutate", + "version": "v4.0.1", "source": { "type": "git", - "url": "https://github.com/nunomaduro/collision.git", - "reference": "716af8f95a470e9094cfca09ed897b023be191a5" + "url": "https://github.com/pestphp/pest-plugin-mutate.git", + "reference": "d9b32b60b2385e1688a68cc227594738ec26d96c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/collision/zipball/716af8f95a470e9094cfca09ed897b023be191a5", - "reference": "716af8f95a470e9094cfca09ed897b023be191a5", + "url": "https://api.github.com/repos/pestphp/pest-plugin-mutate/zipball/d9b32b60b2385e1688a68cc227594738ec26d96c", + "reference": "d9b32b60b2385e1688a68cc227594738ec26d96c", "shasum": "" }, "require": { - "filp/whoops": "^2.18.4", - "nunomaduro/termwind": "^2.4.0", - "php": "^8.2.0", - "symfony/console": "^7.4.8 || ^8.0.8" - }, - "conflict": { - "laravel/framework": "<11.48.0 || >=14.0.0", - "phpunit/phpunit": "<11.5.50 || >=14.0.0" + "nikic/php-parser": "^5.6.1", + "pestphp/pest-plugin": "^4.0.0", + "php": "^8.3", + "psr/simple-cache": "^3.0.0" }, "require-dev": { - "brianium/paratest": "^7.8.5", - "larastan/larastan": "^3.9.6", - "laravel/framework": "^11.48.0 || ^12.56.0 || ^13.5.0", - "laravel/pint": "^1.29.1", - "orchestra/testbench-core": "^9.12.0 || ^10.12.1 || ^11.2.1", - "pestphp/pest": "^3.8.5 || ^4.4.3 || ^5.0.0", - "sebastian/environment": "^7.2.1 || ^8.0.4 || ^9.3.0" + "pestphp/pest": "^4.0.0", + "pestphp/pest-dev-tools": "^4.0.0", + "pestphp/pest-plugin-type-coverage": "^4.0.0" }, "type": "library", - "extra": { - "laravel": { - "providers": [ - "NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider" - ] - }, - "branch-alias": { - "dev-8.x": "8.x-dev" - } - }, "autoload": { - "files": [ - "./src/Adapters/Phpunit/Autoload.php" - ], "psr-4": { - "NunoMaduro\\Collision\\": "src/" + "Pest\\Mutate\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -10614,25 +12775,26 @@ { "name": "Nuno Maduro", "email": "enunomaduro@gmail.com" + }, + { + "name": "Sandro Gehri", + "email": "sandrogehri@gmail.com" } ], - "description": "Cli error handling for console/command-line PHP applications.", + "description": "Mutates your code to find untested cases", "keywords": [ - "artisan", - "cli", - "command-line", - "console", - "dev", - "error", - "handling", - "laravel", - "laravel-zero", + "framework", + "mutate", + "mutation", + "pest", "php", - "symfony" + "plugin", + "test", + "testing", + "unit" ], "support": { - "issues": "https://github.com/nunomaduro/collision/issues", - "source": "https://github.com/nunomaduro/collision" + "source": "https://github.com/pestphp/pest-plugin-mutate/tree/v4.0.1" }, "funding": [ { @@ -10640,15 +12802,71 @@ "type": "custom" }, { - "url": "https://github.com/nunomaduro", + "url": "https://github.com/gehrisandro", "type": "github" }, { - "url": "https://www.patreon.com/nunomaduro", - "type": "patreon" + "url": "https://github.com/nunomaduro", + "type": "github" } ], - "time": "2026-04-21T14:04:20+00:00" + "time": "2025-08-21T20:19:25+00:00" + }, + { + "name": "pestphp/pest-plugin-profanity", + "version": "v4.2.1", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest-plugin-profanity.git", + "reference": "343cfa6f3564b7e35df0ebb77b7fa97039f72b27" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest-plugin-profanity/zipball/343cfa6f3564b7e35df0ebb77b7fa97039f72b27", + "reference": "343cfa6f3564b7e35df0ebb77b7fa97039f72b27", + "shasum": "" + }, + "require": { + "pestphp/pest-plugin": "^4.0.0", + "php": "^8.3" + }, + "require-dev": { + "faissaloux/pest-plugin-inside": "^1.9", + "pestphp/pest": "^4.0.0", + "pestphp/pest-dev-tools": "^4.0.0" + }, + "type": "library", + "extra": { + "pest": { + "plugins": [ + "Pest\\Profanity\\Plugin" + ] + } + }, + "autoload": { + "psr-4": { + "Pest\\Profanity\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "The Pest Profanity Plugin", + "keywords": [ + "framework", + "pest", + "php", + "plugin", + "profanity", + "test", + "testing", + "unit" + ], + "support": { + "source": "https://github.com/pestphp/pest-plugin-profanity/tree/v4.2.1" + }, + "time": "2025-12-08T00:13:17+00:00" }, { "name": "phar-io/manifest", @@ -10936,18 +13154,241 @@ }, "time": "2026-01-15T14:47:34+00:00" }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "6.0.3", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "7bae67520aa9f5ecc506d646810bd40d9da54582" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/7bae67520aa9f5ecc506d646810bd40d9da54582", + "reference": "7bae67520aa9f5ecc506d646810bd40d9da54582", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.1", + "ext-filter": "*", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^2.0", + "phpstan/phpdoc-parser": "^2.0", + "webmozart/assert": "^1.9.1 || ^2" + }, + "require-dev": { + "mockery/mockery": "~1.3.5 || ~1.6.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-webmozart-assert": "^1.2", + "phpunit/phpunit": "^9.5", + "psalm/phar": "^5.26", + "shipmonk/dead-code-detector": "^0.5.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/6.0.3" + }, + "time": "2026-03-18T20:49:53+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "327a05bbee54120d4786a0dc67aad30226ad4cf9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/327a05bbee54120d4786a0dc67aad30226ad4cf9", + "reference": "327a05bbee54120d4786a0dc67aad30226ad4cf9", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^2.0" + }, + "require-dev": { + "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", + "psalm/phar": "^4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev", + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/2.0.0" + }, + "time": "2026-01-06T21:53:42+00:00" + }, + { + "name": "phpstan/phpdoc-parser", + "version": "2.3.2", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a004701b11273a26cd7955a61d67a7f1e525a45a", + "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^5.3.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.6", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.2" + }, + "time": "2026-01-25T14:56:51+00:00" + }, { "name": "phpunit/php-code-coverage", - "version": "11.0.12", + "version": "12.5.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "2c1ed04922802c15e1de5d7447b4856de949cf56" + "reference": "186dab580576598076de6818596d12b61801880e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2c1ed04922802c15e1de5d7447b4856de949cf56", - "reference": "2c1ed04922802c15e1de5d7447b4856de949cf56", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/186dab580576598076de6818596d12b61801880e", + "reference": "186dab580576598076de6818596d12b61801880e", "shasum": "" }, "require": { @@ -10955,18 +13396,16 @@ "ext-libxml": "*", "ext-xmlwriter": "*", "nikic/php-parser": "^5.7.0", - "php": ">=8.2", - "phpunit/php-file-iterator": "^5.1.0", - "phpunit/php-text-template": "^4.0.1", - "sebastian/code-unit-reverse-lookup": "^4.0.1", - "sebastian/complexity": "^4.0.1", - "sebastian/environment": "^7.2.1", - "sebastian/lines-of-code": "^3.0.1", - "sebastian/version": "^5.0.2", - "theseer/tokenizer": "^1.3.1" + "php": ">=8.3", + "phpunit/php-text-template": "^5.0", + "sebastian/complexity": "^5.0", + "sebastian/environment": "^8.1.2", + "sebastian/lines-of-code": "^4.0.1", + "sebastian/version": "^6.0", + "theseer/tokenizer": "^2.0.1" }, "require-dev": { - "phpunit/phpunit": "^11.5.46" + "phpunit/phpunit": "^12.5.28" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", @@ -10975,7 +13414,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "11.0.x-dev" + "dev-main": "12.5.x-dev" } }, "autoload": { @@ -11004,7 +13443,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.12" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.5.7" }, "funding": [ { @@ -11024,32 +13463,32 @@ "type": "tidelift" } ], - "time": "2025-12-24T07:01:01+00:00" + "time": "2026-06-01T13:24:19+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "5.1.1", + "version": "6.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "2f3a64888c814fc235386b7387dd5b5ed92ad903" + "reference": "3d1cd096ef6bea4bf2762ba586e35dbd317cbfd5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/2f3a64888c814fc235386b7387dd5b5ed92ad903", - "reference": "2f3a64888c814fc235386b7387dd5b5ed92ad903", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3d1cd096ef6bea4bf2762ba586e35dbd317cbfd5", + "reference": "3d1cd096ef6bea4bf2762ba586e35dbd317cbfd5", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^11.3" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.1-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -11077,7 +13516,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.1.1" + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/6.0.1" }, "funding": [ { @@ -11097,28 +13536,28 @@ "type": "tidelift" } ], - "time": "2026-02-02T13:52:54+00:00" + "time": "2026-02-02T14:04:18+00:00" }, { "name": "phpunit/php-invoker", - "version": "5.0.1", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2" + "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/c1ca3814734c07492b3d4c5f794f4b0995333da2", - "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/12b54e689b07a25a9b41e57736dfab6ec9ae5406", + "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.3" }, "require-dev": { "ext-pcntl": "*", - "phpunit/phpunit": "^11.0" + "phpunit/phpunit": "^12.0" }, "suggest": { "ext-pcntl": "*" @@ -11126,7 +13565,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -11153,7 +13592,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-invoker/issues", "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/5.0.1" + "source": "https://github.com/sebastianbergmann/php-invoker/tree/6.0.0" }, "funding": [ { @@ -11161,32 +13600,32 @@ "type": "github" } ], - "time": "2024-07-03T05:07:44+00:00" + "time": "2025-02-07T04:58:58+00:00" }, { "name": "phpunit/php-text-template", - "version": "4.0.1", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964" + "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/3e0404dc6b300e6bf56415467ebcb3fe4f33e964", - "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/e1367a453f0eda562eedb4f659e13aa900d66c53", + "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^11.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "4.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -11213,7 +13652,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-text-template/issues", "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/4.0.1" + "source": "https://github.com/sebastianbergmann/php-text-template/tree/5.0.0" }, "funding": [ { @@ -11221,32 +13660,32 @@ "type": "github" } ], - "time": "2024-07-03T05:08:43+00:00" + "time": "2025-02-07T04:59:16+00:00" }, { "name": "phpunit/php-timer", - "version": "7.0.1", + "version": "8.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3" + "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", - "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc", + "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^11.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "7.0-dev" + "dev-main": "8.0-dev" } }, "autoload": { @@ -11273,7 +13712,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-timer/issues", "security": "https://github.com/sebastianbergmann/php-timer/security/policy", - "source": "https://github.com/sebastianbergmann/php-timer/tree/7.0.1" + "source": "https://github.com/sebastianbergmann/php-timer/tree/8.0.0" }, "funding": [ { @@ -11281,20 +13720,20 @@ "type": "github" } ], - "time": "2024-07-03T05:09:35+00:00" + "time": "2025-02-07T04:59:38+00:00" }, { "name": "phpunit/phpunit", - "version": "11.5.55", + "version": "12.5.28", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "adc7262fccc12de2b30f12a8aa0b33775d814f00" + "reference": "5895d05f5bf421ed230fbd76e1277e4b8955def4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/adc7262fccc12de2b30f12a8aa0b33775d814f00", - "reference": "adc7262fccc12de2b30f12a8aa0b33775d814f00", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/5895d05f5bf421ed230fbd76e1277e4b8955def4", + "reference": "5895d05f5bf421ed230fbd76e1277e4b8955def4", "shasum": "" }, "require": { @@ -11307,35 +13746,31 @@ "myclabs/deep-copy": "^1.13.4", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", - "php": ">=8.2", - "phpunit/php-code-coverage": "^11.0.12", - "phpunit/php-file-iterator": "^5.1.1", - "phpunit/php-invoker": "^5.0.1", - "phpunit/php-text-template": "^4.0.1", - "phpunit/php-timer": "^7.0.1", - "sebastian/cli-parser": "^3.0.2", - "sebastian/code-unit": "^3.0.3", - "sebastian/comparator": "^6.3.3", - "sebastian/diff": "^6.0.2", - "sebastian/environment": "^7.2.1", - "sebastian/exporter": "^6.3.2", - "sebastian/global-state": "^7.0.2", - "sebastian/object-enumerator": "^6.0.1", - "sebastian/recursion-context": "^6.0.3", - "sebastian/type": "^5.1.3", - "sebastian/version": "^5.0.2", + "php": ">=8.3", + "phpunit/php-code-coverage": "^12.5.6", + "phpunit/php-file-iterator": "^6.0.1", + "phpunit/php-invoker": "^6.0.0", + "phpunit/php-text-template": "^5.0.0", + "phpunit/php-timer": "^8.0.0", + "sebastian/cli-parser": "^4.2.1", + "sebastian/comparator": "^7.1.8", + "sebastian/diff": "^7.0.0", + "sebastian/environment": "^8.1.2", + "sebastian/exporter": "^7.0.3", + "sebastian/global-state": "^8.0.2", + "sebastian/object-enumerator": "^7.0.0", + "sebastian/recursion-context": "^7.0.1", + "sebastian/type": "^6.0.4", + "sebastian/version": "^6.0.0", "staabm/side-effects-detector": "^1.0.5" }, - "suggest": { - "ext-soap": "To be able to generate mocks based on WSDL files" - }, "bin": [ "phpunit" ], "type": "library", "extra": { "branch-alias": { - "dev-main": "11.5-dev" + "dev-main": "12.5-dev" } }, "autoload": { @@ -11367,113 +13802,112 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.55" + "source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.28" }, "funding": [ { - "url": "https://phpunit.de/sponsors.html", - "type": "custom" - }, - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", - "type": "tidelift" + "url": "https://phpunit.de/sponsoring.html", + "type": "other" } ], - "time": "2026-02-18T12:37:06+00:00" + "time": "2026-05-27T14:01:10+00:00" }, { - "name": "sebastian/cli-parser", - "version": "3.0.2", + "name": "revolt/event-loop", + "version": "v1.0.9", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180" + "url": "https://github.com/revoltphp/event-loop.git", + "reference": "44061cf513e53c6200372fc935ac42271566295d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/15c5dd40dc4f38794d383bb95465193f5e0ae180", - "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180", + "url": "https://api.github.com/repos/revoltphp/event-loop/zipball/44061cf513e53c6200372fc935ac42271566295d", + "reference": "44061cf513e53c6200372fc935ac42271566295d", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^11.0" + "ext-json": "*", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^9", + "psalm/phar": "6.16.*" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "1.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Revolt\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "ceesjank@gmail.com" + }, + { + "name": "Christian Lück", + "email": "christian@clue.engineering" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" } ], - "description": "Library for parsing CLI options", - "homepage": "https://github.com/sebastianbergmann/cli-parser", + "description": "Rock-solid event loop for concurrent PHP applications.", + "keywords": [ + "async", + "asynchronous", + "concurrency", + "event", + "event-loop", + "non-blocking", + "scheduler" + ], "support": { - "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/3.0.2" + "issues": "https://github.com/revoltphp/event-loop/issues", + "source": "https://github.com/revoltphp/event-loop/tree/v1.0.9" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-07-03T04:41:36+00:00" + "time": "2026-05-16T17:55:38+00:00" }, { - "name": "sebastian/code-unit", - "version": "3.0.3", + "name": "sebastian/cli-parser", + "version": "4.2.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64" + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "7d05781b13f7dec9043a629a21d086ed74582a15" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/54391c61e4af8078e5b276ab082b6d3c54c9ad64", - "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/7d05781b13f7dec9043a629a21d086ed74582a15", + "reference": "7d05781b13f7dec9043a629a21d086ed74582a15", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^11.5" + "phpunit/phpunit": "^12.5.25" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "4.2-dev" } }, "autoload": { @@ -11492,100 +13926,56 @@ "role": "lead" } ], - "description": "Collection of value objects that represent the PHP code units", - "homepage": "https://github.com/sebastianbergmann/code-unit", + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", "support": { - "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "security": "https://github.com/sebastianbergmann/code-unit/security/policy", - "source": "https://github.com/sebastianbergmann/code-unit/tree/3.0.3" + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/4.2.1" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" - } - ], - "time": "2025-03-19T07:56:08+00:00" - }, - { - "name": "sebastian/code-unit-reverse-lookup", - "version": "4.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "183a9b2632194febd219bb9246eee421dad8d45e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/183a9b2632194febd219bb9246eee421dad8d45e", - "reference": "183a9b2632194febd219bb9246eee421dad8d45e", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "require-dev": { - "phpunit/phpunit": "^11.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ + }, { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "security": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/security/policy", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/4.0.1" - }, - "funding": [ + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, { - "url": "https://github.com/sebastianbergmann", - "type": "github" + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/cli-parser", + "type": "tidelift" } ], - "time": "2024-07-03T04:45:54+00:00" + "time": "2026-05-17T05:29:34+00:00" }, { "name": "sebastian/comparator", - "version": "6.3.3", + "version": "7.1.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "2c95e1e86cb8dd41beb8d502057d1081ccc8eca9" + "reference": "7c65c1e79836812819705b473a90c12399542485" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2c95e1e86cb8dd41beb8d502057d1081ccc8eca9", - "reference": "2c95e1e86cb8dd41beb8d502057d1081ccc8eca9", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/7c65c1e79836812819705b473a90c12399542485", + "reference": "7c65c1e79836812819705b473a90c12399542485", "shasum": "" }, "require": { "ext-dom": "*", "ext-mbstring": "*", - "php": ">=8.2", - "sebastian/diff": "^6.0", - "sebastian/exporter": "^6.0" + "php": ">=8.3", + "sebastian/diff": "^7.0", + "sebastian/exporter": "^7.0.3" }, "require-dev": { - "phpunit/phpunit": "^11.4" + "phpunit/phpunit": "^12.5.25" }, "suggest": { "ext-bcmath": "For comparing BcMath\\Number objects" @@ -11593,7 +13983,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "6.3-dev" + "dev-main": "7.1-dev" } }, "autoload": { @@ -11633,7 +14023,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/6.3.3" + "source": "https://github.com/sebastianbergmann/comparator/tree/7.1.8" }, "funding": [ { @@ -11653,33 +14043,33 @@ "type": "tidelift" } ], - "time": "2026-01-24T09:26:40+00:00" + "time": "2026-05-21T04:45:25+00:00" }, { "name": "sebastian/complexity", - "version": "4.0.1", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "ee41d384ab1906c68852636b6de493846e13e5a0" + "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/ee41d384ab1906c68852636b6de493846e13e5a0", - "reference": "ee41d384ab1906c68852636b6de493846e13e5a0", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/bad4316aba5303d0221f43f8cee37eb58d384bbb", + "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb", "shasum": "" }, "require": { "nikic/php-parser": "^5.0", - "php": ">=8.2" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^11.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "4.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -11703,7 +14093,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", "security": "https://github.com/sebastianbergmann/complexity/security/policy", - "source": "https://github.com/sebastianbergmann/complexity/tree/4.0.1" + "source": "https://github.com/sebastianbergmann/complexity/tree/5.0.0" }, "funding": [ { @@ -11711,33 +14101,33 @@ "type": "github" } ], - "time": "2024-07-03T04:49:50+00:00" + "time": "2025-02-07T04:55:25+00:00" }, { "name": "sebastian/diff", - "version": "6.0.2", + "version": "7.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544" + "reference": "7ab1ea946c012266ca32390913653d844ecd085f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/b4ccd857127db5d41a5b676f24b51371d76d8544", - "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7ab1ea946c012266ca32390913653d844ecd085f", + "reference": "7ab1ea946c012266ca32390913653d844ecd085f", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^11.0", - "symfony/process": "^4.2 || ^5" + "phpunit/phpunit": "^12.0", + "symfony/process": "^7.2" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "6.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -11770,7 +14160,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", "security": "https://github.com/sebastianbergmann/diff/security/policy", - "source": "https://github.com/sebastianbergmann/diff/tree/6.0.2" + "source": "https://github.com/sebastianbergmann/diff/tree/7.0.0" }, "funding": [ { @@ -11778,27 +14168,27 @@ "type": "github" } ], - "time": "2024-07-03T04:53:05+00:00" + "time": "2025-02-07T04:55:46+00:00" }, { "name": "sebastian/environment", - "version": "7.2.1", + "version": "8.1.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4" + "reference": "9d32c685773823b1983e256ae4ecd48a10d6e439" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/a5c75038693ad2e8d4b6c15ba2403532647830c4", - "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/9d32c685773823b1983e256ae4ecd48a10d6e439", + "reference": "9d32c685773823b1983e256ae4ecd48a10d6e439", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^11.3" + "phpunit/phpunit": "^12.5.26" }, "suggest": { "ext-posix": "*" @@ -11806,7 +14196,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "7.2-dev" + "dev-main": "8.1-dev" } }, "autoload": { @@ -11834,7 +14224,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", "security": "https://github.com/sebastianbergmann/environment/security/policy", - "source": "https://github.com/sebastianbergmann/environment/tree/7.2.1" + "source": "https://github.com/sebastianbergmann/environment/tree/8.1.2" }, "funding": [ { @@ -11854,34 +14244,34 @@ "type": "tidelift" } ], - "time": "2025-05-21T11:55:47+00:00" + "time": "2026-05-25T13:40:20+00:00" }, { "name": "sebastian/exporter", - "version": "6.3.2", + "version": "7.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "70a298763b40b213ec087c51c739efcaa90bcd74" + "reference": "c5e21b5de653ce0a769fb36f5cdfcb5e7a32cf23" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/70a298763b40b213ec087c51c739efcaa90bcd74", - "reference": "70a298763b40b213ec087c51c739efcaa90bcd74", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/c5e21b5de653ce0a769fb36f5cdfcb5e7a32cf23", + "reference": "c5e21b5de653ce0a769fb36f5cdfcb5e7a32cf23", "shasum": "" }, "require": { "ext-mbstring": "*", - "php": ">=8.2", - "sebastian/recursion-context": "^6.0" + "php": ">=8.3", + "sebastian/recursion-context": "^7.0.1" }, "require-dev": { - "phpunit/phpunit": "^11.3" + "phpunit/phpunit": "^12.5.25" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "6.3-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -11924,7 +14314,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", "security": "https://github.com/sebastianbergmann/exporter/security/policy", - "source": "https://github.com/sebastianbergmann/exporter/tree/6.3.2" + "source": "https://github.com/sebastianbergmann/exporter/tree/7.0.3" }, "funding": [ { @@ -11944,35 +14334,35 @@ "type": "tidelift" } ], - "time": "2025-09-24T06:12:51+00:00" + "time": "2026-05-20T04:37:17+00:00" }, { "name": "sebastian/global-state", - "version": "7.0.2", + "version": "8.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "3be331570a721f9a4b5917f4209773de17f747d7" + "reference": "b164d3274d6537ab462591c5755f76a8f5b1aae9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/3be331570a721f9a4b5917f4209773de17f747d7", - "reference": "3be331570a721f9a4b5917f4209773de17f747d7", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/b164d3274d6537ab462591c5755f76a8f5b1aae9", + "reference": "b164d3274d6537ab462591c5755f76a8f5b1aae9", "shasum": "" }, "require": { - "php": ">=8.2", - "sebastian/object-reflector": "^4.0", - "sebastian/recursion-context": "^6.0" + "php": ">=8.3", + "sebastian/object-reflector": "^5.0", + "sebastian/recursion-context": "^7.0.1" }, "require-dev": { "ext-dom": "*", - "phpunit/phpunit": "^11.0" + "phpunit/phpunit": "^12.5.28" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "7.0-dev" + "dev-main": "8.0-dev" } }, "autoload": { @@ -11998,41 +14388,53 @@ "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", "security": "https://github.com/sebastianbergmann/global-state/security/policy", - "source": "https://github.com/sebastianbergmann/global-state/tree/7.0.2" + "source": "https://github.com/sebastianbergmann/global-state/tree/8.0.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/global-state", + "type": "tidelift" } ], - "time": "2024-07-03T04:57:36+00:00" + "time": "2026-06-01T15:10:33+00:00" }, { "name": "sebastian/lines-of-code", - "version": "3.0.1", + "version": "4.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a" + "reference": "d543b8ef219dcd8da262cbb958639a96bedba10e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/d36ad0d782e5756913e42ad87cb2890f4ffe467a", - "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/d543b8ef219dcd8da262cbb958639a96bedba10e", + "reference": "d543b8ef219dcd8da262cbb958639a96bedba10e", "shasum": "" }, "require": { - "nikic/php-parser": "^5.0", - "php": ">=8.2" + "nikic/php-parser": "^5.7.0", + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^11.0" + "phpunit/phpunit": "^12.5.25" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -12056,42 +14458,54 @@ "support": { "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/3.0.1" + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/4.0.1" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/lines-of-code", + "type": "tidelift" } ], - "time": "2024-07-03T04:58:38+00:00" + "time": "2026-05-19T16:22:07+00:00" }, { "name": "sebastian/object-enumerator", - "version": "6.0.1", + "version": "7.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "f5b498e631a74204185071eb41f33f38d64608aa" + "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/f5b498e631a74204185071eb41f33f38d64608aa", - "reference": "f5b498e631a74204185071eb41f33f38d64608aa", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1effe8e9b8e068e9ae228e542d5d11b5d16db894", + "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894", "shasum": "" }, "require": { - "php": ">=8.2", - "sebastian/object-reflector": "^4.0", - "sebastian/recursion-context": "^6.0" + "php": ">=8.3", + "sebastian/object-reflector": "^5.0", + "sebastian/recursion-context": "^7.0" }, "require-dev": { - "phpunit/phpunit": "^11.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "6.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -12114,7 +14528,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/6.0.1" + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/7.0.0" }, "funding": [ { @@ -12122,32 +14536,32 @@ "type": "github" } ], - "time": "2024-07-03T05:00:13+00:00" + "time": "2025-02-07T04:57:48+00:00" }, { "name": "sebastian/object-reflector", - "version": "4.0.1", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9" + "reference": "4bfa827c969c98be1e527abd576533293c634f6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/6e1a43b411b2ad34146dee7524cb13a068bb35f9", - "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/4bfa827c969c98be1e527abd576533293c634f6a", + "reference": "4bfa827c969c98be1e527abd576533293c634f6a", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^11.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "4.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -12170,7 +14584,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/object-reflector/issues", "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/4.0.1" + "source": "https://github.com/sebastianbergmann/object-reflector/tree/5.0.0" }, "funding": [ { @@ -12178,32 +14592,32 @@ "type": "github" } ], - "time": "2024-07-03T05:01:32+00:00" + "time": "2025-02-07T04:58:17+00:00" }, { "name": "sebastian/recursion-context", - "version": "6.0.3", + "version": "7.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc" + "reference": "0b01998a7d5b1f122911a66bebcb8d46f0c82d8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/f6458abbf32a6c8174f8f26261475dc133b3d9dc", - "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/0b01998a7d5b1f122911a66bebcb8d46f0c82d8c", + "reference": "0b01998a7d5b1f122911a66bebcb8d46f0c82d8c", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^11.3" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "6.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -12234,7 +14648,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.3" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/7.0.1" }, "funding": [ { @@ -12254,32 +14668,32 @@ "type": "tidelift" } ], - "time": "2025-08-13T04:42:22+00:00" + "time": "2025-08-13T04:44:59+00:00" }, { "name": "sebastian/type", - "version": "5.1.3", + "version": "6.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449" + "reference": "82ff822c2edc46724be9f7411d3163021f602773" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/f77d2d4e78738c98d9a68d2596fe5e8fa380f449", - "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/82ff822c2edc46724be9f7411d3163021f602773", + "reference": "82ff822c2edc46724be9f7411d3163021f602773", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^11.3" + "phpunit/phpunit": "^12.5.25" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.1-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -12303,7 +14717,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/type/issues", "security": "https://github.com/sebastianbergmann/type/security/policy", - "source": "https://github.com/sebastianbergmann/type/tree/5.1.3" + "source": "https://github.com/sebastianbergmann/type/tree/6.0.4" }, "funding": [ { @@ -12323,29 +14737,29 @@ "type": "tidelift" } ], - "time": "2025-08-09T06:55:48+00:00" + "time": "2026-05-20T06:45:45+00:00" }, { "name": "sebastian/version", - "version": "5.0.2", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874" + "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c687e3387b99f5b03b6caa64c74b63e2936ff874", - "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/3e6ccf7657d4f0a59200564b08cead899313b53c", + "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.3" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -12369,7 +14783,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/version/issues", "security": "https://github.com/sebastianbergmann/version/security/policy", - "source": "https://github.com/sebastianbergmann/version/tree/5.0.2" + "source": "https://github.com/sebastianbergmann/version/tree/6.0.0" }, "funding": [ { @@ -12377,7 +14791,7 @@ "type": "github" } ], - "time": "2024-10-09T05:16:32+00:00" + "time": "2025-02-07T05:00:38+00:00" }, { "name": "spatie/backtrace", @@ -12892,25 +15306,84 @@ ], "time": "2026-05-29T05:06:50+00:00" }, + { + "name": "ta-tikoma/phpunit-architecture-test", + "version": "0.8.7", + "source": { + "type": "git", + "url": "https://github.com/ta-tikoma/phpunit-architecture-test.git", + "reference": "1248f3f506ca9641d4f68cebcd538fa489754db8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ta-tikoma/phpunit-architecture-test/zipball/1248f3f506ca9641d4f68cebcd538fa489754db8", + "reference": "1248f3f506ca9641d4f68cebcd538fa489754db8", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18.0 || ^5.0.0", + "php": "^8.1.0", + "phpdocumentor/reflection-docblock": "^5.3.0 || ^6.0.0", + "phpunit/phpunit": "^10.5.5 || ^11.0.0 || ^12.0.0 || ^13.0.0", + "symfony/finder": "^6.4.0 || ^7.0.0 || ^8.0.0" + }, + "require-dev": { + "laravel/pint": "^1.13.7", + "phpstan/phpstan": "^1.10.52" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPUnit\\Architecture\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ni Shi", + "email": "futik0ma011@gmail.com" + }, + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Methods for testing application architecture", + "keywords": [ + "architecture", + "phpunit", + "stucture", + "test", + "testing" + ], + "support": { + "issues": "https://github.com/ta-tikoma/phpunit-architecture-test/issues", + "source": "https://github.com/ta-tikoma/phpunit-architecture-test/tree/0.8.7" + }, + "time": "2026-02-17T17:25:14+00:00" + }, { "name": "theseer/tokenizer", - "version": "1.3.1", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "b7489ce515e168639d17feec34b8847c326b0b3c" + "reference": "7989e43bf381af0eac72e4f0ca5bcbfa81658be4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c", - "reference": "b7489ce515e168639d17feec34b8847c326b0b3c", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/7989e43bf381af0eac72e4f0ca5bcbfa81658be4", + "reference": "7989e43bf381af0eac72e4f0ca5bcbfa81658be4", "shasum": "" }, "require": { "ext-dom": "*", "ext-tokenizer": "*", "ext-xmlwriter": "*", - "php": "^7.2 || ^8.0" + "php": "^8.1" }, "type": "library", "autoload": { @@ -12932,7 +15405,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.3.1" + "source": "https://github.com/theseer/tokenizer/tree/2.0.1" }, "funding": [ { @@ -12940,7 +15413,73 @@ "type": "github" } ], - "time": "2025-11-17T20:03:58+00:00" + "time": "2025-12-08T11:19:18+00:00" + }, + { + "name": "webmozart/assert", + "version": "2.4.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "9007ea6f45ecf352a9422b36644e4bfc039b9155" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/9007ea6f45ecf352a9422b36644e4bfc039b9155", + "reference": "9007ea6f45ecf352a9422b36644e4bfc039b9155", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-date": "*", + "ext-filter": "*", + "php": "^8.2" + }, + "suggest": { + "ext-intl": "", + "ext-simplexml": "", + "ext-spl": "" + }, + "type": "library", + "extra": { + "psalm": { + "pluginClass": "Webmozart\\Assert\\PsalmPlugin" + }, + "branch-alias": { + "dev-master": "2.0-dev", + "dev-feature/2-0": "2.0-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + }, + { + "name": "Woody Gilk", + "email": "woody.gilk@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/2.4.0" + }, + "time": "2026-05-20T13:07:01+00:00" } ], "aliases": [], diff --git a/docs/smoke-test-browser.md b/docs/smoke-test-browser.md new file mode 100644 index 00000000..0a0683e7 --- /dev/null +++ b/docs/smoke-test-browser.md @@ -0,0 +1,415 @@ +# Smoke Test dengan Pest Browser (Playwright) + +Panduan lengkap pembuatan smoke test menggunakan **Pest v4** + **pest-plugin-browser** (Playwright backend) untuk testing browser di Laravel. + +## Daftar Isi + +- [Arsitektur](#arsitektur) +- [Prasyarat](#prasyarat) +- [Instalasi](#instalasi) +- [Konfigurasi](#konfigurasi) +- [Penulisan Test](#penulisan-test) +- [Session State Management](#session-state-management) +- [Custom Login Route](#custom-login-route) +- [Troubleshooting](#troubleshooting) +- [Referensi](#referensi) + +--- + +## Arsitektur + +``` +Pest PHP (tests/Browser/*.php) + │ + ▼ +pest-plugin-browser + │ + ▼ +Playwright Server (WebSocket) + │ + ▼ +Chromium Browser + │ + ▼ +Laravel HTTP Server (amphp) + │ + ▼ +Aplikasi Laravel +``` + +Pest browser plugin menjalankan **Laravel HTTP server lokal** (amphp) dan mengontrol **Chromium browser** via Playwright WebSocket. Test ditulis 100% dalam syntax PHP Pest. + +--- + +## Prasyarat + +| Komponen | Versi | Keterangan | +|----------|-------|------------| +| PHP | 8.4+ | | +| Laravel | 13+ | | +| Node.js | 18+ | Untuk Playwright | +| Composer | 2+ | | + +--- + +## Instalasi + +### 1. Install Pest v4 + +```bash +composer require pestphp/pest:^4.0 --dev -W +composer require pestphp/pest-plugin-browser:^4.0 --dev -W +``` + +### 2. Install Playwright (npm) + +```bash +npm install playwright @playwright/test --save-dev +npx playwright install chromium +``` + +### 3. Publish Pest Configuration + +Buat `tests/Pest.php`: + +```php +in('Feature', 'Browser'); + +// Timeout untuk browser tests (dalam milidetik) +pest()->browser()->timeout(30000); +``` + +### 4. Update phpunit.xml + +Pastikan `APP_ENV` di-set ke `testing`: + +```xml + + + +``` + +### 5. Update .gitignore + +``` +# Pest Browser Testing +tests/Browser/Screenshots/ +tests/Browser/.session_state.json +``` + +--- + +## Konfigurasi + +### Environment + +Gunakan `.env` atau `.env.testing` untuk konfigurasi testing: + +```env +APP_ENV=testing +APP_URL=http://127.0.0.1:8000 +DB_DATABASE=openkab +DB_USERNAME=root +DB_PASSWORD=secret +``` + +### Playwright Options + +Konfigurasi Playwright di `tests/Pest.php`: + +```php +// Timeout default +pest()->browser()->timeout(30000); + +// Headed mode (untuk debugging) +pest()->browser()->headless(false); +``` + +--- + +## Penulisan Test + +### Struktur File + +``` +tests/ +├── Browser/ +│ ├── SmokeLoginTest.php # Test login +│ ├── SmokeDashboardTest.php # Test dashboard +│ └── SessionState.php # Helper session management +├── Feature/ +├── Unit/ +└── Pest.php # Pest configuration +``` + +### Test Dasar + +#### Menampilkan Halaman + +```php +it('displays login page correctly', function () { + visit('/login') + ->assertSee('Masuk') + ->assertVisible('input[name="login"]') + ->assertVisible('input[name="password"]') + ->assertVisible('button[type="submit"]'); +}); +``` + +#### Fill Form & Submit + +```php +it('can login with valid credentials', function () { + $email = 'pest-' . time() . '@login.test'; + $user = User::factory()->create([ + 'email' => $email, + 'password' => 'password123', // Plain text - User model auto-hash + ]); + + visit('/login') + ->fill('input[name="login"]', $email) + ->fill('input[name="password"]', 'password123') + ->press('Masuk') + ->assertPathIsNot('/login'); +}); +``` + +#### Assert Error Message + +```php +it('shows error for invalid credentials', function () { + visit('/login') + ->fill('input[name="login"]', 'wrong@email.com') + ->fill('input[name="password"]', 'wrongpassword') + ->submit() + ->assertSee('Masuk'); +}); +``` + +### API Reference + +| Method | Keterangan | +|--------|------------| +| `visit($url)` | Buka halaman | +| `navigate($url)` | Navigasi ke URL | +| `fill($selector, $value)` | Isi input field | +| `type($selector, $value)` | Ketik di input field | +| `typeSlowly($selector, $value, $delay)` | Ketik perlahan | +| `press($text)` | Klik tombol/link | +| `click($selector)` | Klik element | +| `submit()` | Submit form pertama | +| `value($selector)` | Ambil nilai input | +| `script($js)` | Jalankan JavaScript | +| `assertSee($text)` | Assert teks ada di halaman | +| `assertVisible($selector)` | Assert element terlihat | +| `assertPathIs($path)` | Assert URL path | +| `assertPathIsNot($path)` | Assert URL path bukan | +| `wait($seconds)` | Tunggu beberapa detik | +| `screenshot($name)` | Ambil screenshot | +| `dd()` | Dump & die | + +--- + +## Session State Management + +### Masalah + +Setiap Pest browser test membuat **browser context baru**, sehingga session/cookies hilang antar test. Login via form membutuhkan ~1.5 detik. + +### Solusi + +Gunakan `SessionState` helper untuk save/restore user ID ke file, lalu bypass form login via route `/_pest/login/{id}`. + +### SessionState Helper + +```php + $user->id, + 'email' => $user->email, + 'created_at' => now()->toIso8601String(), + ]; + file_put_contents(self::STORAGE_PATH, json_encode($state, JSON_PRETTY_PRINT)); + } + + // Load state dari file + public static function load(): ?array { /* ... */ } + + // Hapus state file + public static function clear(): void { /* ... */ } + + // Buat atau ambil user exist + public static function getOrCreateUser(string $emailPrefix = 'pest-session'): User { /* ... */ } + + // Login langsung via route + public static function loginAs(User $user): \Pest\Browser\Api\AwaitableWebpage + { + $result = visit("/_pest/login/{$user->id}"); + self::saveForUser($user); + return $result; + } + + // Restore session dari file + public static function restoreSession(): ?\Pest\Browser\Api\AwaitableWebpage { /* ... */ } +} +``` + +### Penggunaan + +```php +use Tests\Browser\SessionState; + +it('can restore session from saved state', function () { + // Buat user + $user = SessionState::getOrCreateUser('pest-restore'); + + // Simpan state + SessionState::saveForUser($user); + + // Load state + $state = SessionState::load(); + expect($state)->not->toBeNull(); + expect($state['user_id'])->toBe($user->id); + + // Login via quick route + visit("/_pest/login/{$user->id}") + ->navigate('/dasbor') + ->assertPathIs('/dasbor'); + + // Cleanup + SessionState::clear(); +}); +``` + +### Performance + +| Operasi | Waktu | +|---------|-------| +| Login via form | ~1.5s | +| Login via `/_pest/login/{id}` | ~1.4s | +| **Session restore** | **~0.55s** (3x lebih cepat) | + +--- + +## Custom Login Route + +### Tujuan + +Bypass form login untuk testing lebih cepat. + +### Route Definition + +Tambahkan di `routes/web.php`: + +```php +// Pest Browser Testing - Quick login route (hanya untuk testing) +Route::get('/_pest/login/{userId}', function ($userId) { + $user = App\Models\User::findOrFail($userId); + Auth::login($user); + return response(status: 200); +})->middleware('web'); +``` + +### Cara Kerja + +1. Test mengirim request ke `/_pest/login/{userId}` +2. Route melakukan `Auth::login($user)` tanpa validasi password +3. Session Laravel dibuat +4. Return response 200 +5. Test dapat mengakses halaman yang memerlukan autentikasi + +--- + +## Troubleshooting + +### Password Field Tidak Terisi + +**Gejala:** Form login gagal meski sudah pakai `fill()`. + +**Root Cause:** User model punya `password()` mutator yang otomatis `Hash::make()`: + +```php +// User.php +protected function password(): Attribute +{ + return Attribute::make( + set: fn (string $value) => Hash::make($value), + ); +} +``` + +**Solusi:** Pass plain text password, JANGAN pakai `Hash::make()`: + +```php +// ❌ Salah - double hash +User::factory()->create([ + 'password' => Hash::make('password123'), +]); + +// ✅ Benar - mutator akan hash otomatis +User::factory()->create([ + 'password' => 'password123', +]); +``` + +### Variable Undefined di View + +**Gejala:** `Undefined variable $settingAplikasi` di view. + +**Kemungkinan:** View partial yang di-include sebelum AppServiceProvider boot selesai. + +**Solusi:** Gunakan `assertPathIs()` alih-alih `assertSee()`: + +```php +// ❌ Bisa gagal +->assertSee('Dasbor'); + +// ✅ Lebih reliable +->assertPathIs('/dasbor'); +``` + +### Test Timeout + +**Gejala:** Test timeout setelah 30 detik. + +**Solusi:** Increase timeout di `tests/Pest.php`: + +```php +pest()->browser()->timeout(60000); // 60 detik +``` + +### Playwright Tidak Ditemukan + +**Gejala:** Error "playwright not found". + +**Solusi:** + +```bash +npm install playwright @playwright/test +npx playwright install chromium +``` + +--- + +## Referensi + +- [Pest v4 Documentation](https://pestphp.com/docs/browser) +- [pest-plugin-browser](https://github.com/pestphp/pest-browser) +- [Playwright for PHP](https://playwright.dev/php/) +- [Laravel HTTP Testing](https://laravel.com/docs/http-tests) diff --git a/package-lock.json b/package-lock.json index c23cf637..dcbd3561 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "bootstrap-datepicker": "^1.10.0", "bootstrap-icons": "^1.4.1", "owl.carousel": "^2.3.4", + "playwright": "^1.60.0", "select2": "^4.1.0-rc.0", "select2-bootstrap4-theme": "^1.0.0", "sweetalert2": "^11.7.27", @@ -525,6 +526,53 @@ "node": ">=18" } }, + "node_modules/@playwright/test/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/@playwright/test/node_modules/playwright": { + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.57.0.tgz", + "integrity": "sha512-ilYQj1s8sr2ppEJ2YVadYBN0Mb3mdo9J0wQ+UuDhzYqURwSoW4n1Xs5vs7ORwgDGmyEh33tRMeS8KhdkMoLXQw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.57.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/@playwright/test/node_modules/playwright-core": { + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.57.0.tgz", + "integrity": "sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@popperjs/core": { "version": "2.11.8", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", @@ -3387,13 +3435,12 @@ } }, "node_modules/playwright": { - "version": "1.57.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.57.0.tgz", - "integrity": "sha512-ilYQj1s8sr2ppEJ2YVadYBN0Mb3mdo9J0wQ+UuDhzYqURwSoW4n1Xs5vs7ORwgDGmyEh33tRMeS8KhdkMoLXQw==", - "dev": true, + "version": "1.60.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.60.0.tgz", + "integrity": "sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA==", "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.57.0" + "playwright-core": "1.60.0" }, "bin": { "playwright": "cli.js" @@ -3406,10 +3453,9 @@ } }, "node_modules/playwright-core": { - "version": "1.57.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.57.0.tgz", - "integrity": "sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ==", - "dev": true, + "version": "1.60.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.60.0.tgz", + "integrity": "sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA==", "license": "Apache-2.0", "bin": { "playwright-core": "cli.js" @@ -3422,7 +3468,6 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -4857,6 +4902,31 @@ "dev": true, "requires": { "playwright": "1.57.0" + }, + "dependencies": { + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "playwright": { + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.57.0.tgz", + "integrity": "sha512-ilYQj1s8sr2ppEJ2YVadYBN0Mb3mdo9J0wQ+UuDhzYqURwSoW4n1Xs5vs7ORwgDGmyEh33tRMeS8KhdkMoLXQw==", + "dev": true, + "requires": { + "fsevents": "2.3.2", + "playwright-core": "1.57.0" + } + }, + "playwright-core": { + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.57.0.tgz", + "integrity": "sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ==", + "dev": true + } } }, "@popperjs/core": { @@ -7158,29 +7228,26 @@ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" }, "playwright": { - "version": "1.57.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.57.0.tgz", - "integrity": "sha512-ilYQj1s8sr2ppEJ2YVadYBN0Mb3mdo9J0wQ+UuDhzYqURwSoW4n1Xs5vs7ORwgDGmyEh33tRMeS8KhdkMoLXQw==", - "dev": true, + "version": "1.60.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.60.0.tgz", + "integrity": "sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA==", "requires": { "fsevents": "2.3.2", - "playwright-core": "1.57.0" + "playwright-core": "1.60.0" }, "dependencies": { "fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, "optional": true } } }, "playwright-core": { - "version": "1.57.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.57.0.tgz", - "integrity": "sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ==", - "dev": true + "version": "1.60.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.60.0.tgz", + "integrity": "sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA==" }, "png-js": { "version": "1.0.0", diff --git a/package.json b/package.json index f6c2b498..f16546d8 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "bootstrap-datepicker": "^1.10.0", "bootstrap-icons": "^1.4.1", "owl.carousel": "^2.3.4", + "playwright": "^1.60.0", "select2": "^4.1.0-rc.0", "select2-bootstrap4-theme": "^1.0.0", "sweetalert2": "^11.7.27", diff --git a/phpunit.xml b/phpunit.xml index 2ac86a18..e9f533da 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -12,11 +12,11 @@ ./tests/Feature - + ./app - + diff --git a/routes/web.php b/routes/web.php index ab44f800..1fc1ae03 100644 --- a/routes/web.php +++ b/routes/web.php @@ -467,3 +467,10 @@ // Image proxy route with rate limiting Route::middleware('throttle:30,1')->get('/image-proxy', [ImageProxyController::class, 'proxy'])->name('image.proxy'); + +// Pest Browser Testing - Quick login route (hanya untuk testing) +Route::get('/_pest/login/{userId}', function ($userId) { + $user = App\Models\User::findOrFail($userId); + Auth::login($user); + return response(status: 200); +})->middleware('web'); diff --git a/tests/Browser/SessionState.php b/tests/Browser/SessionState.php new file mode 100644 index 00000000..4951f0b2 --- /dev/null +++ b/tests/Browser/SessionState.php @@ -0,0 +1,88 @@ + $user->id, + 'email' => $user->email, + 'created_at' => now()->toIso8601String(), + ]; + + file_put_contents(self::STORAGE_PATH, json_encode($state, JSON_PRETTY_PRINT)); + } + + public static function load(): ?array + { + if (! file_exists(self::STORAGE_PATH)) { + return null; + } + + $content = file_get_contents(self::STORAGE_PATH); + $state = json_decode($content, true); + + if (! is_array($state) || ! isset($state['user_id'])) { + return null; + } + + return $state; + } + + public static function clear(): void + { + if (file_exists(self::STORAGE_PATH)) { + unlink(self::STORAGE_PATH); + } + } + + public static function getOrCreateUser(string $emailPrefix = 'pest-session'): User + { + $email = $emailPrefix . '@' . config('app.url', 'localhost') . '.test'; + $existing = User::where('email', $email)->first(); + + if ($existing) { + return $existing; + } + + return User::factory()->create([ + 'email' => $email, + 'password' => 'PestTest123!', + ]); + } + + public static function loginAs(User $user, ?\Pest\Browser\Api\AwaitableWebpage $page = null): \Pest\Browser\Api\AwaitableWebpage + { + $result = visit("/_pest/login/{$user->id}"); + self::saveForUser($user); + + return $result; + } + + public static function restoreSession(): ?\Pest\Browser\Api\AwaitableWebpage + { + $state = self::load(); + + if ($state === null) { + return null; + } + + $user = User::find($state['user_id']); + + if ($user === null) { + self::clear(); + + return null; + } + + return visit("/_pest/login/{$user->id}"); + } +} diff --git a/tests/Browser/SmokeDashboardTest.php b/tests/Browser/SmokeDashboardTest.php new file mode 100644 index 00000000..6cf89e7e --- /dev/null +++ b/tests/Browser/SmokeDashboardTest.php @@ -0,0 +1,18 @@ + 'pest-test@opendesa.test'], + [ + 'name' => 'Pest Test User', + 'password' => 'password', + 'username' => 'pesttest', + ] + ); + + visit("/_pest/login/{$user->id}") + ->navigate('/dasbor') + ->assertPathIs('/dasbor'); +}); diff --git a/tests/Browser/SmokeLoginTest.php b/tests/Browser/SmokeLoginTest.php new file mode 100644 index 00000000..6c6afadb --- /dev/null +++ b/tests/Browser/SmokeLoginTest.php @@ -0,0 +1,53 @@ +assertSee('Masuk') + ->assertVisible('input[name="login"]') + ->assertVisible('input[name="password"]') + ->assertVisible('button[type="submit"]'); +}); + +it('can login with valid credentials', function () { + $email = 'pest-' . time() . '@login.test'; + $user = User::factory()->create([ + 'email' => $email, + 'password' => 'password123', + ]); + + visit('/login') + ->fill('input[name="login"]', $email) + ->fill('input[name="password"]', 'password123') + ->press('Masuk') + ->assertPathIsNot('/login'); + + SessionState::saveForUser($user); +}); + +it('shows error for invalid credentials', function () { + visit('/login') + ->fill('input[name="login"]', 'wrong@email.com') + ->fill('input[name="password"]', 'wrongpassword') + ->submit() + ->assertSee('Masuk'); +}); + +it('can restore session from saved state', function () { + SessionState::clear(); + + $user = SessionState::getOrCreateUser('pest-restore'); + SessionState::saveForUser($user); + + $state = SessionState::load(); + expect($state)->not->toBeNull(); + expect($state['user_id'])->toBe($user->id); + + visit("/_pest/login/{$user->id}") + ->navigate('/dasbor') + ->assertPathIs('/dasbor'); + + SessionState::clear(); +}); diff --git a/tests/Pest.php b/tests/Pest.php new file mode 100644 index 00000000..970b490d --- /dev/null +++ b/tests/Pest.php @@ -0,0 +1,7 @@ +in('Feature', 'Browser'); + +pest()->browser()->timeout(30000); From d9ae00e39e7441a69905da0550d1c2c58f118f5e Mon Sep 17 00:00:00 2001 From: Ahmad Afandi Date: Wed, 10 Jun 2026 08:52:53 +0700 Subject: [PATCH 23/43] perbaikan test --- .gitignore | 1 + app/Providers/AppServiceProvider.php | 44 ++-- docs/smoke-test-browser.md | 218 +++++++++++++++++- phpunit.xml | 4 + resources/views/auth/login.blade.php | 12 +- .../views/partials/flash_message.blade.php | 6 +- tests/Browser/ScreenshotHelper.php | 39 ++++ tests/Browser/SessionState.php | 11 +- tests/Browser/SmokeDashboardTest.php | 7 +- tests/Browser/SmokeLoginTest.php | 33 ++- tests/Feature/PengaturanGroupTeamTest.php | 4 +- 11 files changed, 325 insertions(+), 54 deletions(-) create mode 100644 tests/Browser/ScreenshotHelper.php diff --git a/.gitignore b/.gitignore index 1a7206bd..aaec5046 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ /storage/*.key /vendor .env +.env.testing .env.backup .env.production .phpunit.result.cache diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 418dfaf5..636767cf 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -42,30 +42,30 @@ public function boot(): void $this->addValidation(); $this->addLogQuery(); + // Share data ke semua view (termasuk Pest browser test context) + $identitasAplikasi = fractal( + Identitas::first(), + IdentitasTransformer::class, + \League\Fractal\Serializer\JsonApiSerializer::class + )->toArray()['data']['attributes']; + + $settingAplikasi = collect( + fractal( + Setting::all(), + SettingTransformer::class, + \League\Fractal\Serializer\JsonApiSerializer::class + )->toArray()['data'] + )->pluck('attributes.value', 'attributes.key'); + + View::share('identitasAplikasi', $identitasAplikasi); + View::share('settingAplikasi', $settingAplikasi); + config()->set(['app.sebutanDesa' => $identitasAplikasi['sebutan_desa'] ?? 'Desa']); + config()->set(['app.sebutanKab' => $identitasAplikasi['sebutan_kab'] ?? 'Kabupaten']); + config()->set(['app.kodeKabupatenApi' => $identitasAplikasi['kode_kabupaten_api'] ?? '']); + $this->bootConfigAdminLTE($identitasAplikasi, $settingAplikasi); + if (App::runningInConsole()) { activity()->disableLogging(); - } else { - $identitasAplikasi = fractal( - Identitas::first(), - IdentitasTransformer::class, - \League\Fractal\Serializer\JsonApiSerializer::class - )->toArray()['data']['attributes']; - - $settingAplikasi = collect( - fractal( - Setting::all(), - SettingTransformer::class, - \League\Fractal\Serializer\JsonApiSerializer::class - )->toArray()['data'] - )->pluck('attributes.value', 'attributes.key'); - - // daftarkan data identitas aplikasi disini, karena akan dipakai di hampir semua view - View::share('identitasAplikasi', $identitasAplikasi); - View::share('settingAplikasi', $settingAplikasi); - config()->set(['app.sebutanDesa' => $identitasAplikasi['sebutan_desa'] ?? 'Desa']); - config()->set(['app.sebutanKab' => $identitasAplikasi['sebutan_kab'] ?? 'Kabupaten']); - config()->set(['app.kodeKabupatenApi' => $identitasAplikasi['kode_kabupaten_api'] ?? '']); - $this->bootConfigAdminLTE($identitasAplikasi, $settingAplikasi); } } diff --git a/docs/smoke-test-browser.md b/docs/smoke-test-browser.md index 0a0683e7..ca156130 100644 --- a/docs/smoke-test-browser.md +++ b/docs/smoke-test-browser.md @@ -133,6 +133,73 @@ pest()->browser()->headless(false); ## Penulisan Test +### Menggunakan data-testid (Recommended) + +Playwright merekomendasikan menggunakan `data-testid` untuk selector yang paling stabil dan resilient terhadap perubahan UI. + +**Keuntungan:** +- Tidak berubah saat text/label berubah +- Tidak berubah saat DOM structure berubah +- Explicit contract antara developer dan tester +- Tidak bergantung pada styling atau layout + +#### 1. Tambahkan data-testid di Blade Template + +```html +
+ @csrf + + + + +
+``` + +#### 2. Gunakan `@` Shorthand di Test + +Pest browser plugin mendukung shorthand `@` untuk `data-testid`: + +```php +// ✅ Recommended - gunakan @ shorthand +visit('/login') + ->fill('@login-email', $email) + ->fill('@login-password', 'password123') + ->press('@login-submit') + ->assertPathIsNot('/login'); + +// ✅ Alternatif - CSS selector eksplisit +visit('/login') + ->fill('[data-testid="login-email"]', $email) + ->fill('[data-testid="login-password"]', 'password123') + ->press('[data-testid="login-submit"]') + ->assertPathIsNot('/login'); +``` + +#### 3. Naming Convention + +Gunakan format `feature-element` untuk data-testid: + +| Element | data-testid | Keterangan | +|---------|-------------|------------| +| Login form | `login-form` | Form container | +| Email input | `login-email` | Input email/username | +| Password input | `login-password` | Input password | +| Submit button | `login-submit` | Tombol submit | +| Remember checkbox | `login-remember` | Checkbox remember me | +| Error message | `login-error` | Error validation | +| Flash success | `flash-success` | Success notification | +| Flash error | `flash-error` | Error notification | + +#### 4. Selector Hierarchy + +``` +@ → data-testid shorthand (recommended) +[data-testid="..."] → CSS selector eksplisit +#id → ID selector (hindari jika bisa berubah) +[name="..."] → Name selector (untuk form elements) +"text" → Text selector (untuk links/buttons) +``` + ### Struktur File ``` @@ -154,9 +221,9 @@ tests/ it('displays login page correctly', function () { visit('/login') ->assertSee('Masuk') - ->assertVisible('input[name="login"]') - ->assertVisible('input[name="password"]') - ->assertVisible('button[type="submit"]'); + ->assertVisible('@login-email') + ->assertVisible('@login-password') + ->assertVisible('@login-submit'); }); ``` @@ -171,9 +238,9 @@ it('can login with valid credentials', function () { ]); visit('/login') - ->fill('input[name="login"]', $email) - ->fill('input[name="password"]', 'password123') - ->press('Masuk') + ->fill('@login-email', $email) + ->fill('@login-password', 'password123') + ->press('@login-submit') ->assertPathIsNot('/login'); }); ``` @@ -183,9 +250,9 @@ it('can login with valid credentials', function () { ```php it('shows error for invalid credentials', function () { visit('/login') - ->fill('input[name="login"]', 'wrong@email.com') - ->fill('input[name="password"]', 'wrongpassword') - ->submit() + ->fill('@login-email', 'wrong@email.com') + ->fill('@login-password', 'wrongpassword') + ->press('@login-submit') ->assertSee('Masuk'); }); ``` @@ -407,6 +474,139 @@ npx playwright install chromium --- +## Best Practices + +### 1. Gunakan data-testid untuk Selector + +```php +// ❌ Hindari - CSS selector yang fragile +->fill('input[name="login"]', $email) +->fill('input[type="password"]', $password) + +// ✅ Recommended - testid selector yang stable +->fill('@login-email', $email) +->fill('@login-password', $password) +``` + +### 2. Gunakan `@` Shorthand + +```php +// ✅ Lebih pendek dan readable +->fill('@login-email', $email) + +// ✅ Alternatif - CSS selector eksplisit (jika perlu) +->fill('[data-testid="login-email"]', $email) +``` + +### 3. Naming Convention + +``` +data-testid="{feature}-{element}" + +Contoh: +- login-email +- login-password +- login-submit +- dashboard-sidebar +- user-profile-avatar +``` + +### 4. Jangan Test Internal Implementation + +```php +// ❌ Hindari - test implementation detail +->fill('input[name="_token"]', $token) + +// ✅ Test user behavior +->fill('@login-email', $email) +->press('@login-submit') +->assertPathIs('/dasbor') +``` + +### 5. Gunakan Assertion yang Reliable + +```php +// ❌ Hindari - text bisa berubah +->assertSee('Selamat datang di Dashboard') + +// ✅ Gunakan path assertion +->assertPathIs('/dasbor') + +// ✅ Atau gunakan testid untuk element yang spesifik +->assertVisible('@dashboard-welcome') +``` + +--- + +## Screenshot Management + +### Konfigurasi + +Screenshot diatur via environment variable: + +```env +# Aktifkan screenshot saat test pass +SCREENSHOT_ON_SUCCESS=true + +# Nonaktifkan (default) +SCREENSHOT_ON_SUCCESS=false +``` + +### Penggunaan + +```php +use Tests\Browser\ScreenshotHelper; + +it('can login with valid credentials', function () { + $page = visit('/login') + ->fill('@login-email', $email) + ->fill('@login-password', 'password123') + ->press('@login-submit') + ->assertPathIsNot('/login'); + + // Screenshot hanya disimpan jika SCREENSHOT_ON_SUCCESS=true + ScreenshotHelper::saveIfEnabled($page, 'login-success'); +}); +``` + +### Output + +Screenshot tersimpan di `tests/Browser/Screenshots/` dengan format: +``` +{nama-test}_{timestamp}.png +``` + +Contoh: +``` +login-page_2026-06-10_07-50-09.png +login-success_2026-06-10_07-50-11.png +login-error_2026-06-10_07-50-14.png +``` + +### Performa + +| Kondisi | Waktu (4 tests) | Tambahan | +|---------|-----------------|----------| +| Tanpa screenshot | ~7.5s | baseline | +| Dengan screenshot | ~10s | **+30%** | + +**Rekomendasi:** +- **CI/CD cepat**: Nonaktifkan screenshot (`SCREENSHOT_ON_SUCCESS=false`) +- **Debugging/Reporting**: Aktifkan screenshot (`SCREENSHOT_ON_SUCCESS=true`) +- **Test gagal**: Pest browser otomatis screenshot saat assertion gagal (tidak perlu konfigurasi) + +### Screenshot saat Gagal + +Pest browser otomatis menyimpan screenshot saat test gagal: + +``` +A screenshot of the page has been saved to [Tests/Browser/Screenshots/test_name.png] +``` + +Fitur ini **selalu aktif** tanpa perlu konfigurasi. + +--- + ## Referensi - [Pest v4 Documentation](https://pestphp.com/docs/browser) diff --git a/phpunit.xml b/phpunit.xml index e9f533da..eb719d12 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -11,6 +11,9 @@ ./tests/Feature + + ./tests/Browser + @@ -27,5 +30,6 @@ +
diff --git a/resources/views/auth/login.blade.php b/resources/views/auth/login.blade.php index c86670f2..a80f46e8 100644 --- a/resources/views/auth/login.blade.php +++ b/resources/views/auth/login.blade.php @@ -18,12 +18,12 @@ @section('auth_body') @include('partials.flash_message') -
+ @csrf {{-- Email / Username field --}}
- @@ -42,7 +42,8 @@ class="form-control {{ $errors->has('username') || $errors->has('email') ? ' is- {{-- Password field --}}
-
@@ -63,7 +64,8 @@ class="form-control {{ $errors->has('username') || $errors->has('email') ? ' is-
- +
-

Sukses!

{{ $message }}

@elseif($message = Session::get('error')) -
+

Gagal!!

{{ $message }}

@elseif($message = Session::get('warning')) -
+

Perhatian!

{{ $message }}

diff --git a/tests/Browser/ScreenshotHelper.php b/tests/Browser/ScreenshotHelper.php new file mode 100644 index 00000000..8d91cecc --- /dev/null +++ b/tests/Browser/ScreenshotHelper.php @@ -0,0 +1,39 @@ +screenshot(true, $filename); + } + + public static function getPath(): string + { + return self::STORAGE_PATH; + } +} diff --git a/tests/Browser/SessionState.php b/tests/Browser/SessionState.php index 4951f0b2..4fe61ba0 100644 --- a/tests/Browser/SessionState.php +++ b/tests/Browser/SessionState.php @@ -59,7 +59,16 @@ public static function getOrCreateUser(string $emailPrefix = 'pest-session'): Us ]); } - public static function loginAs(User $user, ?\Pest\Browser\Api\AwaitableWebpage $page = null): \Pest\Browser\Api\AwaitableWebpage + /** + * Assign administrator role to user. + */ + public static function assignAdminRole(User $user): void + { + setPermissionsTeamId(1); + $user->assignRole('administrator'); + } + + public static function loginAs(User $user): \Pest\Browser\Api\AwaitableWebpage { $result = visit("/_pest/login/{$user->id}"); self::saveForUser($user); diff --git a/tests/Browser/SmokeDashboardTest.php b/tests/Browser/SmokeDashboardTest.php index 6cf89e7e..f833fd82 100644 --- a/tests/Browser/SmokeDashboardTest.php +++ b/tests/Browser/SmokeDashboardTest.php @@ -1,6 +1,8 @@ 'pesttest', ] ); + SessionState::assignAdminRole($user); - visit("/_pest/login/{$user->id}") + $page = visit("/_pest/login/{$user->id}") ->navigate('/dasbor') ->assertPathIs('/dasbor'); + + ScreenshotHelper::saveIfEnabled($page, 'dashboard'); }); diff --git a/tests/Browser/SmokeLoginTest.php b/tests/Browser/SmokeLoginTest.php index 6c6afadb..5bbf6b31 100644 --- a/tests/Browser/SmokeLoginTest.php +++ b/tests/Browser/SmokeLoginTest.php @@ -1,14 +1,17 @@ assertSee('Masuk') - ->assertVisible('input[name="login"]') - ->assertVisible('input[name="password"]') - ->assertVisible('button[type="submit"]'); + ->assertVisible('@login-email') + ->assertVisible('@login-password') + ->assertVisible('@login-submit'); + + ScreenshotHelper::saveIfEnabled($page, 'login-page'); }); it('can login with valid credentials', function () { @@ -17,37 +20,43 @@ 'email' => $email, 'password' => 'password123', ]); + SessionState::assignAdminRole($user); - visit('/login') - ->fill('input[name="login"]', $email) - ->fill('input[name="password"]', 'password123') + $page = visit('/login') + ->fill('@login-email', $email) + ->fill('@login-password', 'password123') ->press('Masuk') ->assertPathIsNot('/login'); SessionState::saveForUser($user); + ScreenshotHelper::saveIfEnabled($page, 'login-success'); }); it('shows error for invalid credentials', function () { - visit('/login') - ->fill('input[name="login"]', 'wrong@email.com') - ->fill('input[name="password"]', 'wrongpassword') - ->submit() + $page = visit('/login') + ->fill('@login-email', 'wrong@email.com') + ->fill('@login-password', 'wrongpassword') + ->press('@login-submit') ->assertSee('Masuk'); + + ScreenshotHelper::saveIfEnabled($page, 'login-error'); }); it('can restore session from saved state', function () { SessionState::clear(); $user = SessionState::getOrCreateUser('pest-restore'); + SessionState::assignAdminRole($user); SessionState::saveForUser($user); $state = SessionState::load(); expect($state)->not->toBeNull(); expect($state['user_id'])->toBe($user->id); - visit("/_pest/login/{$user->id}") + $page = visit("/_pest/login/{$user->id}") ->navigate('/dasbor') ->assertPathIs('/dasbor'); SessionState::clear(); + ScreenshotHelper::saveIfEnabled($page, 'session-restore'); }); diff --git a/tests/Feature/PengaturanGroupTeamTest.php b/tests/Feature/PengaturanGroupTeamTest.php index d7c3377d..c0076bdc 100644 --- a/tests/Feature/PengaturanGroupTeamTest.php +++ b/tests/Feature/PengaturanGroupTeamTest.php @@ -4,6 +4,7 @@ use App\Models\Team; use App\Models\User; +use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Http\Response; use Laravel\Sanctum\Sanctum; use PHPUnit\Framework\Attributes\Test; @@ -11,7 +12,8 @@ class PengaturanGroupTeamTest extends TestCase { - protected $team; + use DatabaseTransactions; + protected Team $team; public function setUp(): void { From 54062010b81e981fa3c468fa60fc80c3feee3034 Mon Sep 17 00:00:00 2001 From: Ahmad Afandi Date: Wed, 10 Jun 2026 10:42:34 +0700 Subject: [PATCH 24/43] buat mock server --- phpunit.xml | 2 +- .../views/dasbor/category_item.blade.php | 8 +- resources/views/dasbor/peta.blade.php | 2 +- resources/views/dasbor/summary.blade.php | 2 +- .../views/dasbor/tabel_penduduk.blade.php | 2 +- .../views/demografi/chart_item.blade.php | 6 +- tests/Browser/SessionState.php | 85 ++++++++++++- tests/Browser/SmokeDashboardDemografiTest.php | 120 ++++++++++++++++++ tests/Browser/SmokeDashboardTest.php | 114 +++++++++++++++-- tests/Browser/fixtures/coordinates.json | 18 +++ tests/Browser/fixtures/dasbor.json | 9 ++ tests/Browser/fixtures/data-website.json | 20 +++ tests/Browser/fixtures/desa-50.01.01.json | 10 ++ tests/Browser/fixtures/desa-50.01.02.json | 10 ++ tests/Browser/fixtures/desa-5271010.json | 10 ++ tests/Browser/fixtures/desa-5271020.json | 10 ++ tests/Browser/fixtures/kabupaten.json | 10 ++ tests/Browser/fixtures/kecamatan-50.01.json | 10 ++ tests/Browser/fixtures/kecamatan-50.02.json | 10 ++ tests/Browser/fixtures/kecamatan-5271.json | 10 ++ tests/Browser/fixtures/kecamatan-5272.json | 10 ++ tests/Browser/fixtures/penduduk.json | 44 +++++++ .../fixtures/statistik-penduduk-agama.json | 28 ++++ .../statistik-penduduk-golongan-darah.json | 28 ++++ .../statistik-penduduk-jenis-kelamin.json | 16 +++ ...tatistik-penduduk-pendidikan-dalam-kk.json | 52 ++++++++ .../statistik-penduduk-penyakit-menahun.json | 28 ++++ .../statistik-penduduk-penyandang-cacat.json | 28 ++++ .../statistik-penduduk-rentang-umur.json | 52 ++++++++ .../statistik-penduduk-status-perkawinan.json | 28 ++++ .../fixtures/statistik-penduduk-suku.json | 34 +++++ tests/Browser/mock-server.php | 72 +++++++++++ tests/Pest.php | 9 ++ 33 files changed, 875 insertions(+), 22 deletions(-) create mode 100644 tests/Browser/SmokeDashboardDemografiTest.php create mode 100644 tests/Browser/fixtures/coordinates.json create mode 100644 tests/Browser/fixtures/dasbor.json create mode 100644 tests/Browser/fixtures/data-website.json create mode 100644 tests/Browser/fixtures/desa-50.01.01.json create mode 100644 tests/Browser/fixtures/desa-50.01.02.json create mode 100644 tests/Browser/fixtures/desa-5271010.json create mode 100644 tests/Browser/fixtures/desa-5271020.json create mode 100644 tests/Browser/fixtures/kabupaten.json create mode 100644 tests/Browser/fixtures/kecamatan-50.01.json create mode 100644 tests/Browser/fixtures/kecamatan-50.02.json create mode 100644 tests/Browser/fixtures/kecamatan-5271.json create mode 100644 tests/Browser/fixtures/kecamatan-5272.json create mode 100644 tests/Browser/fixtures/penduduk.json create mode 100644 tests/Browser/fixtures/statistik-penduduk-agama.json create mode 100644 tests/Browser/fixtures/statistik-penduduk-golongan-darah.json create mode 100644 tests/Browser/fixtures/statistik-penduduk-jenis-kelamin.json create mode 100644 tests/Browser/fixtures/statistik-penduduk-pendidikan-dalam-kk.json create mode 100644 tests/Browser/fixtures/statistik-penduduk-penyakit-menahun.json create mode 100644 tests/Browser/fixtures/statistik-penduduk-penyandang-cacat.json create mode 100644 tests/Browser/fixtures/statistik-penduduk-rentang-umur.json create mode 100644 tests/Browser/fixtures/statistik-penduduk-status-perkawinan.json create mode 100644 tests/Browser/fixtures/statistik-penduduk-suku.json create mode 100644 tests/Browser/mock-server.php diff --git a/phpunit.xml b/phpunit.xml index eb719d12..9829adfe 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -30,6 +30,6 @@ - + diff --git a/resources/views/dasbor/category_item.blade.php b/resources/views/dasbor/category_item.blade.php index c4189aaa..470d9a36 100644 --- a/resources/views/dasbor/category_item.blade.php +++ b/resources/views/dasbor/category_item.blade.php @@ -1,15 +1,15 @@ -
+
-

{{ $value }}

+

{{ $value }}

-

{{ $text }}

+

{{ $text }}

- Selengkapnya Selengkapnya
diff --git a/resources/views/dasbor/peta.blade.php b/resources/views/dasbor/peta.blade.php index bc3a4484..ae16649b 100644 --- a/resources/views/dasbor/peta.blade.php +++ b/resources/views/dasbor/peta.blade.php @@ -1,7 +1,7 @@
-
+
diff --git a/resources/views/dasbor/summary.blade.php b/resources/views/dasbor/summary.blade.php index 431ccb3d..ba1f8708 100644 --- a/resources/views/dasbor/summary.blade.php +++ b/resources/views/dasbor/summary.blade.php @@ -1,4 +1,4 @@ -
+
@php $colors = ['primary', 'warning', 'success', 'info']; // Array warna @endphp diff --git a/resources/views/dasbor/tabel_penduduk.blade.php b/resources/views/dasbor/tabel_penduduk.blade.php index b399b6ca..614f1429 100644 --- a/resources/views/dasbor/tabel_penduduk.blade.php +++ b/resources/views/dasbor/tabel_penduduk.blade.php @@ -1,4 +1,4 @@ -
+
@include('dasbor.data-desa')
diff --git a/resources/views/demografi/chart_item.blade.php b/resources/views/demografi/chart_item.blade.php index 5e73d99c..86309681 100644 --- a/resources/views/demografi/chart_item.blade.php +++ b/resources/views/demografi/chart_item.blade.php @@ -1,11 +1,11 @@ -
+
-
Komposisi {{ $chart['text'] }}
+
Komposisi {{ $chart['text'] }}
+ data-url="{{ $statistikUrl }}?filter[id]={{ $chart['key'] }}" class="chart-container" data-testid="chart-content-{{ $chart['key'] }}">
diff --git a/tests/Browser/SessionState.php b/tests/Browser/SessionState.php index 4fe61ba0..34b834f7 100644 --- a/tests/Browser/SessionState.php +++ b/tests/Browser/SessionState.php @@ -9,6 +9,8 @@ final class SessionState { private const STORAGE_PATH = __DIR__ . '/.session_state.json'; + private const MOCK_SERVER_PID_PATH = __DIR__ . '/.mock_server.pid'; + private const MOCK_SERVER_PORT = 8001; public static function saveForUser(User $user): void { @@ -68,7 +70,7 @@ public static function assignAdminRole(User $user): void $user->assignRole('administrator'); } - public static function loginAs(User $user): \Pest\Browser\Api\AwaitableWebpage + public static function loginAs(User $user) { $result = visit("/_pest/login/{$user->id}"); self::saveForUser($user); @@ -76,7 +78,7 @@ public static function loginAs(User $user): \Pest\Browser\Api\AwaitableWebpage return $result; } - public static function restoreSession(): ?\Pest\Browser\Api\AwaitableWebpage + public static function restoreSession() { $state = self::load(); @@ -94,4 +96,83 @@ public static function restoreSession(): ?\Pest\Browser\Api\AwaitableWebpage return visit("/_pest/login/{$user->id}"); } + + /** + * Start the mock API server in the background. + */ + public static function startMockServer(): void + { + if (self::isMockServerRunning()) { + return; + } + + $mockServerScript = __DIR__ . '/mock-server.php'; + $logPath = __DIR__ . '/.mock_server.log'; + + // Use PHP built-in server with router script + $cmd = sprintf( + 'php -S %s:%d %s > %s 2>&1 & echo $!', + '127.0.0.1', + self::MOCK_SERVER_PORT, + escapeshellarg($mockServerScript), + escapeshellarg($logPath) + ); + + $pid = trim(shell_exec($cmd)); + + if ($pid && is_numeric($pid)) { + file_put_contents(self::MOCK_SERVER_PID_PATH, $pid); + self::waitForServer(self::MOCK_SERVER_PORT, 10); + } + } + + /** + * Stop the mock API server. + */ + public static function stopMockServer(): void + { + if (!file_exists(self::MOCK_SERVER_PID_PATH)) { + return; + } + + $pid = (int) file_get_contents(self::MOCK_SERVER_PID_PATH); + + if ($pid > 0) { + posix_kill($pid, SIGTERM); + usleep(200000); // Wait 200ms for graceful shutdown + } + + @unlink(self::MOCK_SERVER_PID_PATH); + } + + /** + * Check if mock server is running. + */ + public static function isMockServerRunning(): bool + { + if (!file_exists(self::MOCK_SERVER_PID_PATH)) { + return false; + } + + $pid = (int) file_get_contents(self::MOCK_SERVER_PID_PATH); + + return $pid > 0 && posix_kill($pid, 0); // Signal 0 = check existence + } + + /** + * Wait for a server to become available on the given port. + */ + private static function waitForServer(int $port, int $timeoutSeconds): void + { + $start = time(); + + while ((time() - $start) < $timeoutSeconds) { + $fp = @fsockopen('127.0.0.1', $port, $errno, $errstr, 1); + if ($fp) { + fclose($fp); + return; + } + usleep(100000); // 100ms + } + } } diff --git a/tests/Browser/SmokeDashboardDemografiTest.php b/tests/Browser/SmokeDashboardDemografiTest.php new file mode 100644 index 00000000..dea1ac79 --- /dev/null +++ b/tests/Browser/SmokeDashboardDemografiTest.php @@ -0,0 +1,120 @@ +user = User::firstOrCreate( + ['email' => 'pest-demografi@opendesa.test'], + [ + 'name' => 'Pest Demografi User', + 'password' => 'password', + 'username' => 'pestdemografi', + ] + ); + SessionState::assignAdminRole($this->user); + SessionState::saveForUser($this->user); +}); + +afterEach(function () { + SessionState::clear(); +}); + +it('can open demografi dashboard page', function () { + $page = SessionState::loginAs($this->user) + ->navigate('/dasbor-demografi') + ->assertPathIs('/dasbor-demografi') + ->assertSee('Dasbor Demografi'); + + ScreenshotHelper::saveIfEnabled($page, 'demografi-open'); +}); + +it('displays wilayah filter', function () { + $page = SessionState::loginAs($this->user) + ->navigate('/dasbor-demografi') + ->assertVisible('@filter_kabupaten') + ->assertVisible('@filter_kecamatan') + ->assertVisible('@filter_desa') + ->assertVisible('@bt_filter'); + + ScreenshotHelper::saveIfEnabled($page, 'demografi-filter'); +}); + +it('displays all 9 chart cards', function () { + $chartKeys = [ + 'rentang-umur', + 'status-perkawinan', + 'pendidikan-dalam-kk', + 'golongan-darah', + 'penyakit-menahun', + 'agama', + 'jenis-kelamin', + 'suku', + 'penyandang-cacat', + ]; + + $page = SessionState::loginAs($this->user) + ->navigate('/dasbor-demografi'); + + foreach ($chartKeys as $key) { + $page->assertSee($key); + } + + ScreenshotHelper::saveIfEnabled($page, 'demografi-charts'); +}); + +it('displays chart titles', function () { + $page = SessionState::loginAs($this->user) + ->navigate('/dasbor-demografi') + ->assertSee('Rentang Umur') + ->assertSee('Status Perkawinan') + ->assertSee('Pendidikan Dalam KK') + ->assertSee('Golongan Darah') + ->assertSee('Penyakit Menahun') + ->assertSee('Agama') + ->assertSee('Jenis Kelamin') + ->assertSee('Suku / Etnis') + ->assertSee('Penyandang Cacat'); + + ScreenshotHelper::saveIfEnabled($page, 'demografi-titles'); +}); + +it('updates charts when filtering by kabupaten', function () { + $page = SessionState::loginAs($this->user) + ->navigate('/dasbor-demografi'); + + $page->script(' + $("#filter_kabupaten").val("50.01").trigger("change"); + $("#bt_filter").click(); + '); + + $page->wait(5000); + + $page->assertSee('Rentang Umur'); + + ScreenshotHelper::saveIfEnabled($page, 'demografi-filtered'); +}); + +it('clear filter button works', function () { + $page = SessionState::loginAs($this->user) + ->navigate('/dasbor-demografi'); + + $page->script(' + $("#filter_kabupaten").val("50.01").trigger("change"); + $("#bt_filter").click(); + '); + + $page->wait(5000); + + $page->script(' + $("#filter_kabupaten").val(null).trigger("change"); + $("#bt_filter").click(); + '); + + $page->wait(3000); + + $page->assertSee('Rentang Umur'); + + ScreenshotHelper::saveIfEnabled($page, 'demografi-clear-filter'); +}); diff --git a/tests/Browser/SmokeDashboardTest.php b/tests/Browser/SmokeDashboardTest.php index f833fd82..6059d6be 100644 --- a/tests/Browser/SmokeDashboardTest.php +++ b/tests/Browser/SmokeDashboardTest.php @@ -4,20 +4,116 @@ use Tests\Browser\ScreenshotHelper; use Tests\Browser\SessionState; -it('can access dashboard after quick login', function () { - $user = User::firstOrCreate( - ['email' => 'pest-test@opendesa.test'], +beforeEach(function () { + $this->user = User::firstOrCreate( + ['email' => 'pest-dashboard@opendesa.test'], [ - 'name' => 'Pest Test User', + 'name' => 'Pest Dashboard User', 'password' => 'password', - 'username' => 'pesttest', + 'username' => 'pestdashboard', ] ); - SessionState::assignAdminRole($user); + SessionState::assignAdminRole($this->user); + SessionState::saveForUser($this->user); +}); + +afterEach(function () { + SessionState::clear(); +}); + +it('can open dashboard page', function () { + $page = SessionState::loginAs($this->user) + ->navigate('/dasbor') + ->assertPathIs('/dasbor') + ->assertSee('Dasbor'); + + ScreenshotHelper::saveIfEnabled($page, 'dashboard-open'); +}); + +it('displays wilayah filter', function () { + $page = SessionState::loginAs($this->user) + ->navigate('/dasbor') + ->assertVisible('@filter_kabupaten') + ->assertVisible('@filter_kecamatan') + ->assertVisible('@filter_desa') + ->assertVisible('@bt_filter'); + + ScreenshotHelper::saveIfEnabled($page, 'dashboard-filter'); +}); + +it('displays summary cards', function () { + $page = SessionState::loginAs($this->user) + ->navigate('/dasbor') + ->assertVisible('#summary_block') + ->assertSee('kecamatan') + ->assertSee('jumlah penduduk') + ->assertSee('jumlah keluarga'); + + ScreenshotHelper::saveIfEnabled($page, 'dashboard-summary'); +}); + +it('displays map', function () { + $page = SessionState::loginAs($this->user) + ->navigate('/dasbor') + ->assertVisible('#map'); + + ScreenshotHelper::saveIfEnabled($page, 'dashboard-map'); +}); - $page = visit("/_pest/login/{$user->id}") +it('displays data table', function () { + $page = SessionState::loginAs($this->user) ->navigate('/dasbor') - ->assertPathIs('/dasbor'); + ->assertVisible('#tabel_penduduk_block') + ->assertVisible('#summary-penduduk'); + + ScreenshotHelper::saveIfEnabled($page, 'dashboard-table'); +}); + +it('updates summary when filtering by kabupaten', function () { + $page = SessionState::loginAs($this->user) + ->navigate('/dasbor'); + + $page->script(' + $("#filter_kabupaten").val("50.01").trigger("change"); + $("#bt_filter").click(); + '); + + $page->wait(3000); + + $page->assertVisible('#summary_block'); + + ScreenshotHelper::saveIfEnabled($page, 'dashboard-filtered'); +}); + +it('displays data in table', function () { + $page = SessionState::loginAs($this->user) + ->navigate('/dasbor') + ->wait(3000); + + $page->assertVisible('#summary-penduduk'); + + ScreenshotHelper::saveIfEnabled($page, 'dashboard-table-data'); +}); + +it('clear filter button works', function () { + $page = SessionState::loginAs($this->user) + ->navigate('/dasbor'); + + $page->script(' + $("#filter_kabupaten").val("50.01").trigger("change"); + $("#bt_filter").click(); + '); + + $page->wait(3000); + + $page->script(' + $("#filter_kabupaten").val(null).trigger("change"); + $("#bt_filter").click(); + '); + + $page->wait(2000); + + $page->assertVisible('#summary_block'); - ScreenshotHelper::saveIfEnabled($page, 'dashboard'); + ScreenshotHelper::saveIfEnabled($page, 'dashboard-clear-filter'); }); diff --git a/tests/Browser/fixtures/coordinates.json b/tests/Browser/fixtures/coordinates.json new file mode 100644 index 00000000..1ff2d4ef --- /dev/null +++ b/tests/Browser/fixtures/coordinates.json @@ -0,0 +1,18 @@ +[ + { + "lat": -8.5833, + "lng": 116.1167, + "nama_propinsi": "Nusa Tenggara Barat", + "nama_kabupaten": "Kota Mataram", + "nama_kecamatan": "Kecamatan Ampenan", + "nama_desa": "Desa Ampenan Tua" + }, + { + "lat": -8.5667, + "lng": 116.1333, + "nama_propinsi": "Nusa Tenggara Barat", + "nama_kabupaten": "Kota Mataram", + "nama_kecamatan": "Kecamatan Cakranegara", + "nama_desa": "Desa Cakranegara" + } +] diff --git a/tests/Browser/fixtures/dasbor.json b/tests/Browser/fixtures/dasbor.json new file mode 100644 index 00000000..b44a815f --- /dev/null +++ b/tests/Browser/fixtures/dasbor.json @@ -0,0 +1,9 @@ +{ + "data": { + "grafik_penduduk": { + "kategori": ["2020", "2021", "2022", "2023", "2024"], + "laki_laki": [12000, 12200, 12500, 12800, 13000], + "perempuan": [11800, 12000, 12300, 12600, 12800] + } + } +} diff --git a/tests/Browser/fixtures/data-website.json b/tests/Browser/fixtures/data-website.json new file mode 100644 index 00000000..3851cf8d --- /dev/null +++ b/tests/Browser/fixtures/data-website.json @@ -0,0 +1,20 @@ +{ + "data": { + "categoriesItems": { + "kecamatan": { + "value": 2 + }, + "desa": { + "value": 4 + }, + "penduduk": { + "value": 25000 + }, + "keluarga": { + "value": 6500 + } + }, + "listDesa": [], + "listKecamatan": [] + } +} diff --git a/tests/Browser/fixtures/desa-50.01.01.json b/tests/Browser/fixtures/desa-50.01.01.json new file mode 100644 index 00000000..cc766436 --- /dev/null +++ b/tests/Browser/fixtures/desa-50.01.01.json @@ -0,0 +1,10 @@ +[ + { + "kode_desa": "50.01.01.001", + "nama_desa": "Desa Ampenan Tua" + }, + { + "kode_desa": "50.01.01.002", + "nama_desa": "Desa Taman Sari" + } +] diff --git a/tests/Browser/fixtures/desa-50.01.02.json b/tests/Browser/fixtures/desa-50.01.02.json new file mode 100644 index 00000000..d2412eba --- /dev/null +++ b/tests/Browser/fixtures/desa-50.01.02.json @@ -0,0 +1,10 @@ +[ + { + "kode_desa": "50.01.02.001", + "nama_desa": "Desa Cakranegara" + }, + { + "kode_desa": "50.01.02.002", + "nama_desa": "Desa Sayang-Sayang" + } +] diff --git a/tests/Browser/fixtures/desa-5271010.json b/tests/Browser/fixtures/desa-5271010.json new file mode 100644 index 00000000..0492f332 --- /dev/null +++ b/tests/Browser/fixtures/desa-5271010.json @@ -0,0 +1,10 @@ +[ + { + "kode_desa": "5271010001", + "nama_desa": "Desa Ampenan Tua" + }, + { + "kode_desa": "5271010002", + "nama_desa": "Desa Taman Sari" + } +] diff --git a/tests/Browser/fixtures/desa-5271020.json b/tests/Browser/fixtures/desa-5271020.json new file mode 100644 index 00000000..f9ae4077 --- /dev/null +++ b/tests/Browser/fixtures/desa-5271020.json @@ -0,0 +1,10 @@ +[ + { + "kode_desa": "5271020001", + "nama_desa": "Desa Cakranegara" + }, + { + "kode_desa": "5271020002", + "nama_desa": "Desa Sayang-Sayang" + } +] diff --git a/tests/Browser/fixtures/kabupaten.json b/tests/Browser/fixtures/kabupaten.json new file mode 100644 index 00000000..0a5f6245 --- /dev/null +++ b/tests/Browser/fixtures/kabupaten.json @@ -0,0 +1,10 @@ +[ + { + "kode_kabupaten": "50.01", + "nama_kabupaten": "Kota Mataram" + }, + { + "kode_kabupaten": "50.02", + "nama_kabupaten": "Kabupaten Lombok Timur" + } +] diff --git a/tests/Browser/fixtures/kecamatan-50.01.json b/tests/Browser/fixtures/kecamatan-50.01.json new file mode 100644 index 00000000..2a71aaa4 --- /dev/null +++ b/tests/Browser/fixtures/kecamatan-50.01.json @@ -0,0 +1,10 @@ +[ + { + "kode_kecamatan": "50.01.01", + "nama_kecamatan": "Kecamatan Ampenan" + }, + { + "kode_kecamatan": "50.01.02", + "nama_kecamatan": "Kecamatan Cakranegara" + } +] diff --git a/tests/Browser/fixtures/kecamatan-50.02.json b/tests/Browser/fixtures/kecamatan-50.02.json new file mode 100644 index 00000000..d9096f17 --- /dev/null +++ b/tests/Browser/fixtures/kecamatan-50.02.json @@ -0,0 +1,10 @@ +[ + { + "kode_kecamatan": "50.02.01", + "nama_kecamatan": "Kecamatan Aikmel" + }, + { + "kode_kecamatan": "50.02.02", + "nama_kecamatan": "Kecamatan Pringgabaya" + } +] diff --git a/tests/Browser/fixtures/kecamatan-5271.json b/tests/Browser/fixtures/kecamatan-5271.json new file mode 100644 index 00000000..dbee2d77 --- /dev/null +++ b/tests/Browser/fixtures/kecamatan-5271.json @@ -0,0 +1,10 @@ +[ + { + "kode_kecamatan": "5271010", + "nama_kecamatan": "Kecamatan Ampenan" + }, + { + "kode_kecamatan": "5271020", + "nama_kecamatan": "Kecamatan Cakranegara" + } +] diff --git a/tests/Browser/fixtures/kecamatan-5272.json b/tests/Browser/fixtures/kecamatan-5272.json new file mode 100644 index 00000000..4c8e5801 --- /dev/null +++ b/tests/Browser/fixtures/kecamatan-5272.json @@ -0,0 +1,10 @@ +[ + { + "kode_kecamatan": "5272010", + "nama_kecamatan": "Kecamatan Aikmel" + }, + { + "kode_kecamatan": "5272020", + "nama_kecamatan": "Kecamatan Pringgabaya" + } +] diff --git a/tests/Browser/fixtures/penduduk.json b/tests/Browser/fixtures/penduduk.json new file mode 100644 index 00000000..5a3c02f6 --- /dev/null +++ b/tests/Browser/fixtures/penduduk.json @@ -0,0 +1,44 @@ +{ + "data": [ + { + "attributes": { + "nama_desa": "Desa Ampenan Tua", + "nama_kecamatan": "Kecamatan Ampenan", + "kode_desa": "5271010001", + "kode_kecamatan": "5271010", + "kode_kabupaten": "5271", + "nama_kabupaten": "Kota Mataram", + "penduduk_count": 5000 + } + }, + { + "attributes": { + "nama_desa": "Desa Taman Sari", + "nama_kecamatan": "Kecamatan Ampenan", + "kode_desa": "5271010002", + "kode_kecamatan": "5271010", + "kode_kabupaten": "5271", + "nama_kabupaten": "Kota Mataram", + "penduduk_count": 4500 + } + }, + { + "attributes": { + "nama_desa": "Desa Cakranegara", + "nama_kecamatan": "Kecamatan Cakranegara", + "kode_desa": "5271020001", + "kode_kecamatan": "5271020", + "kode_kabupaten": "5271", + "nama_kabupaten": "Kota Mataram", + "penduduk_count": 6000 + } + } + ], + "meta": { + "pagination": { + "total": 3, + "current_page": 1, + "last_page": 1 + } + } +} diff --git a/tests/Browser/fixtures/statistik-penduduk-agama.json b/tests/Browser/fixtures/statistik-penduduk-agama.json new file mode 100644 index 00000000..dad7c074 --- /dev/null +++ b/tests/Browser/fixtures/statistik-penduduk-agama.json @@ -0,0 +1,28 @@ +{ + "data": [ + { + "attributes": { + "nama": "Islam", + "jumlah": 20000 + } + }, + { + "attributes": { + "nama": "Kristen", + "jumlah": 2000 + } + }, + { + "attributes": { + "nama": "Hindu", + "jumlah": 2500 + } + }, + { + "attributes": { + "nama": "Buddha", + "jumlah": 500 + } + } + ] +} diff --git a/tests/Browser/fixtures/statistik-penduduk-golongan-darah.json b/tests/Browser/fixtures/statistik-penduduk-golongan-darah.json new file mode 100644 index 00000000..5bb5bb3d --- /dev/null +++ b/tests/Browser/fixtures/statistik-penduduk-golongan-darah.json @@ -0,0 +1,28 @@ +{ + "data": [ + { + "attributes": { + "nama": "A", + "jumlah": 6000 + } + }, + { + "attributes": { + "nama": "B", + "jumlah": 5000 + } + }, + { + "attributes": { + "nama": "O", + "jumlah": 10000 + } + }, + { + "attributes": { + "nama": "AB", + "jumlah": 4000 + } + } + ] +} diff --git a/tests/Browser/fixtures/statistik-penduduk-jenis-kelamin.json b/tests/Browser/fixtures/statistik-penduduk-jenis-kelamin.json new file mode 100644 index 00000000..64980120 --- /dev/null +++ b/tests/Browser/fixtures/statistik-penduduk-jenis-kelamin.json @@ -0,0 +1,16 @@ +{ + "data": [ + { + "attributes": { + "nama": "Laki-Laki", + "jumlah": 12500 + } + }, + { + "attributes": { + "nama": "Perempuan", + "jumlah": 12500 + } + } + ] +} diff --git a/tests/Browser/fixtures/statistik-penduduk-pendidikan-dalam-kk.json b/tests/Browser/fixtures/statistik-penduduk-pendidikan-dalam-kk.json new file mode 100644 index 00000000..a77fa1d3 --- /dev/null +++ b/tests/Browser/fixtures/statistik-penduduk-pendidikan-dalam-kk.json @@ -0,0 +1,52 @@ +{ + "data": [ + { + "attributes": { + "nama": "Tidak Sekolah", + "jumlah": 1500 + } + }, + { + "attributes": { + "nama": "Tidak Tamat SD", + "jumlah": 2000 + } + }, + { + "attributes": { + "nama": "Tamat SD", + "jumlah": 5000 + } + }, + { + "attributes": { + "nama": "SLTP", + "jumlah": 4500 + } + }, + { + "attributes": { + "nama": "SLTA", + "jumlah": 6000 + } + }, + { + "attributes": { + "nama": "Diploma", + "jumlah": 2000 + } + }, + { + "attributes": { + "nama": "Sarjana", + "jumlah": 3000 + } + }, + { + "attributes": { + "nama": "Pasca Sarjana", + "jumlah": 1000 + } + } + ] +} diff --git a/tests/Browser/fixtures/statistik-penduduk-penyakit-menahun.json b/tests/Browser/fixtures/statistik-penduduk-penyakit-menahun.json new file mode 100644 index 00000000..6f3b08e2 --- /dev/null +++ b/tests/Browser/fixtures/statistik-penduduk-penyakit-menahun.json @@ -0,0 +1,28 @@ +{ + "data": [ + { + "attributes": { + "nama": "Diabetes", + "jumlah": 500 + } + }, + { + "attributes": { + "nama": "Hipertensi", + "jumlah": 800 + } + }, + { + "attributes": { + "nama": "Jantung", + "jumlah": 300 + } + }, + { + "attributes": { + "nama": "Stroke", + "jumlah": 200 + } + } + ] +} diff --git a/tests/Browser/fixtures/statistik-penduduk-penyandang-cacat.json b/tests/Browser/fixtures/statistik-penduduk-penyandang-cacat.json new file mode 100644 index 00000000..da7f34a9 --- /dev/null +++ b/tests/Browser/fixtures/statistik-penduduk-penyandang-cacat.json @@ -0,0 +1,28 @@ +{ + "data": [ + { + "attributes": { + "nama": "Fisik", + "jumlah": 150 + } + }, + { + "attributes": { + "nama": "Mental", + "jumlah": 50 + } + }, + { + "attributes": { + "nama": "Sensorik", + "jumlah": 100 + } + }, + { + "attributes": { + "nama": "Ganda", + "jumlah": 30 + } + } + ] +} diff --git a/tests/Browser/fixtures/statistik-penduduk-rentang-umur.json b/tests/Browser/fixtures/statistik-penduduk-rentang-umur.json new file mode 100644 index 00000000..523e29b0 --- /dev/null +++ b/tests/Browser/fixtures/statistik-penduduk-rentang-umur.json @@ -0,0 +1,52 @@ +{ + "data": [ + { + "attributes": { + "nama": "0-5 Tahun", + "jumlah": 2000 + } + }, + { + "attributes": { + "nama": "6-12 Tahun", + "jumlah": 3000 + } + }, + { + "attributes": { + "nama": "13-18 Tahun", + "jumlah": 3500 + } + }, + { + "attributes": { + "nama": "19-25 Tahun", + "jumlah": 4000 + } + }, + { + "attributes": { + "nama": "26-35 Tahun", + "jumlah": 5000 + } + }, + { + "attributes": { + "nama": "36-50 Tahun", + "jumlah": 4500 + } + }, + { + "attributes": { + "nama": "51-65 Tahun", + "jumlah": 2000 + } + }, + { + "attributes": { + "nama": "> 65 Tahun", + "jumlah": 1000 + } + } + ] +} diff --git a/tests/Browser/fixtures/statistik-penduduk-status-perkawinan.json b/tests/Browser/fixtures/statistik-penduduk-status-perkawinan.json new file mode 100644 index 00000000..c7615e16 --- /dev/null +++ b/tests/Browser/fixtures/statistik-penduduk-status-perkawinan.json @@ -0,0 +1,28 @@ +{ + "data": [ + { + "attributes": { + "nama": "Belum Kawin", + "jumlah": 8000 + } + }, + { + "attributes": { + "nama": "Kawin", + "jumlah": 14000 + } + }, + { + "attributes": { + "nama": "Cerai Hidup", + "jumlah": 1500 + } + }, + { + "attributes": { + "nama": "Cerai Mati", + "jumlah": 1500 + } + } + ] +} diff --git a/tests/Browser/fixtures/statistik-penduduk-suku.json b/tests/Browser/fixtures/statistik-penduduk-suku.json new file mode 100644 index 00000000..79374be9 --- /dev/null +++ b/tests/Browser/fixtures/statistik-penduduk-suku.json @@ -0,0 +1,34 @@ +{ + "data": [ + { + "attributes": { + "nama": "Sasak", + "jumlah": 18000 + } + }, + { + "attributes": { + "nama": "Jawa", + "jumlah": 3000 + } + }, + { + "attributes": { + "nama": "Bali", + "jumlah": 2000 + } + }, + { + "attributes": { + "nama": "Sumbawa", + "jumlah": 1500 + } + }, + { + "attributes": { + "nama": "Lainnya", + "jumlah": 500 + } + } + ] +} diff --git a/tests/Browser/mock-server.php b/tests/Browser/mock-server.php new file mode 100644 index 00000000..7eba2c68 --- /dev/null +++ b/tests/Browser/mock-server.php @@ -0,0 +1,72 @@ + 'Not Found', 'path' => $path]); + return true; +} + +$fixturePath = $fixturesDir . '/' . $fixture; + +if (!file_exists($fixturePath)) { + http_response_code(404); + echo json_encode(['error' => 'Fixture not found', 'fixture' => $fixture]); + return true; +} + +echo file_get_contents($fixturePath); +return true; diff --git a/tests/Pest.php b/tests/Pest.php index 970b490d..b1d6fd72 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -1,7 +1,16 @@ in('Feature', 'Browser'); pest()->browser()->timeout(30000); + +beforeAll(function () { + SessionState::startMockServer(); +}); + +afterAll(function () { + SessionState::stopMockServer(); +}); From 09ca479a641db26ef07a1a6ec08531f29b443e21 Mon Sep 17 00:00:00 2001 From: Ahmad Afandi Date: Wed, 10 Jun 2026 12:03:53 +0700 Subject: [PATCH 25/43] perbaikan menu tidak tampil dan rule csp --- app/Policies/CustomCspPreset.php | 3 +++ app/Providers/AppServiceProvider.php | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/Policies/CustomCspPreset.php b/app/Policies/CustomCspPreset.php index 120eef5c..706c601d 100644 --- a/app/Policies/CustomCspPreset.php +++ b/app/Policies/CustomCspPreset.php @@ -17,6 +17,8 @@ public function configure(Policy $policy): void ->add(Directive::IMG, [Keyword::SELF, 'data:', 'https://tile.openstreetmap.org/', 'blob:']) ->add(Directive::STYLE, [ Keyword::SELF, + Keyword::UNSAFE_INLINE, + 'sha256-4Hp34kejS2ebnG2znG00qXJODVa0mXDBGtDnbB1o+dk=', 'https://fonts.googleapis.com/', 'https://fonts.bunny.net/', 'https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css', @@ -37,6 +39,7 @@ public function configure(Policy $policy): void 'https://cdn.datatables.net/2.0.7/js/dataTables.min.js', 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js', 'https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js', + 'https://cdn.jsdelivr.net/npm/sweetalert2@11', ]) ->addNonce(Directive::SCRIPT) ->add(Directive::FONT, [ diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 418dfaf5..d41ea872 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -117,7 +117,7 @@ protected function bootConfigAdminLTE($identitasAplikasi, $settingAplikasi) $this->app->config['adminlte.title'] = $identitasAplikasi['nama_aplikasi']; $this->app->config['adminlte.title_postfix'] = "| {$identitasAplikasi['sebutan_kab']}"; $this->app->config['adminlte.logo'] = $identitasAplikasi['nama_aplikasi']; - if ($settingAplikasi->get('layout_menu') !== 'Vertikal') { + if (strtolower($settingAplikasi->get('layout_menu')) !== 'vertikal') { $this->app->config['adminlte.layout_topnav'] = true; $this->app->config['adminlte.classes_content'] = 'col-12 p-3'; $this->app->config['adminlte.classes_sidebar'] = 'sidebar-dark-primary elevation-4'; From 9909943681fecbd5294c31cdfd7e1f73a551c77d Mon Sep 17 00:00:00 2001 From: Ahmad Afandi Date: Wed, 10 Jun 2026 12:35:03 +0700 Subject: [PATCH 26/43] perbaikan sitemap --- app/Services/SitemapService.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Services/SitemapService.php b/app/Services/SitemapService.php index 1c1b8c66..c6f256bd 100644 --- a/app/Services/SitemapService.php +++ b/app/Services/SitemapService.php @@ -26,7 +26,7 @@ public function render() { return cache()->remember('sitemap', $this->cacheDuration, function () { $this->init(); - $this->addUrl(route('root')); + $this->addUrl(route('web.index')); Page::all()->each(function ($p) { $this->addUrl($p->link, $p->updated_at); }); From 0826be59889eed619742e380a83fe9079c296d9c Mon Sep 17 00:00:00 2001 From: Ahmad Afandi Date: Wed, 10 Jun 2026 15:56:31 +0700 Subject: [PATCH 27/43] update test --- tests/Browser/SessionState.php | 172 ++++++++++++++++-- tests/Browser/SmokeDashboardDemografiTest.php | 84 ++------- tests/Browser/SmokeDashboardTest.php | 96 ++-------- 3 files changed, 185 insertions(+), 167 deletions(-) diff --git a/tests/Browser/SessionState.php b/tests/Browser/SessionState.php index 34b834f7..4a6011e9 100644 --- a/tests/Browser/SessionState.php +++ b/tests/Browser/SessionState.php @@ -97,8 +97,71 @@ public static function restoreSession() return visit("/_pest/login/{$user->id}"); } + /** + * Get or create admin user, assign role, and save session. + */ + public static function loginAdminUser(): User + { + $email = 'pest-smoke@opendesa.test'; + $existing = User::where('email', $email)->first(); + + if ($existing) { + self::saveForUser($existing); + + return $existing; + } + + $user = User::create([ + 'email' => $email, + 'name' => 'Pest Smoke User', + 'password' => 'password', + 'username' => 'pestsmoke', + ]); + self::assignAdminRole($user); + self::saveForUser($user); + + return $user; + } + + /** + * Login as user and navigate to path. + */ + public static function loginAndNavigate(User $user, string $path) + { + $page = visit("/_pest/login/{$user->id}"); + self::saveForUser($user); + + return $page->navigate($path); + } + + /** + * Apply wilayah filter by kabupaten code. + */ + public static function applyFilter($page, string $kodeKabupaten): void + { + $page->script(' + $("#filter_kabupaten").val("' . $kodeKabupaten . '").trigger("change"); + $("#bt_filter").click(); + '); + $page->wait(1500); + } + + /** + * Clear wilayah filter. + */ + public static function clearFilter($page): void + { + $page->script(' + $("#filter_kabupaten").val(null).trigger("change"); + $("#bt_filter").click(); + '); + $page->wait(1000); + } + /** * Start the mock API server in the background. + * + * @throws \RuntimeException if the server fails to start */ public static function startMockServer(): void { @@ -106,10 +169,11 @@ public static function startMockServer(): void return; } + self::killStaleProcess(); + $mockServerScript = __DIR__ . '/mock-server.php'; $logPath = __DIR__ . '/.mock_server.log'; - // Use PHP built-in server with router script $cmd = sprintf( 'php -S %s:%d %s > %s 2>&1 & echo $!', '127.0.0.1', @@ -120,9 +184,20 @@ public static function startMockServer(): void $pid = trim(shell_exec($cmd)); - if ($pid && is_numeric($pid)) { - file_put_contents(self::MOCK_SERVER_PID_PATH, $pid); - self::waitForServer(self::MOCK_SERVER_PORT, 10); + if (! $pid || ! is_numeric($pid)) { + throw new \RuntimeException( + "Failed to start mock server. Command: {$cmd}" + ); + } + + file_put_contents(self::MOCK_SERVER_PID_PATH, $pid); + + if (! self::waitForServer(self::MOCK_SERVER_PORT, 15)) { + $logContent = file_exists($logPath) ? file_get_contents($logPath) : '(no log)'; + throw new \RuntimeException( + "Mock server started (PID: {$pid}) but not responding on port " + . self::MOCK_SERVER_PORT . ". Log: {$logContent}" + ); } } @@ -131,38 +206,96 @@ public static function startMockServer(): void */ public static function stopMockServer(): void { - if (!file_exists(self::MOCK_SERVER_PID_PATH)) { - return; - } + if (file_exists(self::MOCK_SERVER_PID_PATH)) { + $pid = (int) file_get_contents(self::MOCK_SERVER_PID_PATH); - $pid = (int) file_get_contents(self::MOCK_SERVER_PID_PATH); + if ($pid > 0 && posix_kill($pid, 0)) { + posix_kill($pid, SIGTERM); + usleep(200000); + } - if ($pid > 0) { - posix_kill($pid, SIGTERM); - usleep(200000); // Wait 200ms for graceful shutdown + @unlink(self::MOCK_SERVER_PID_PATH); } - @unlink(self::MOCK_SERVER_PID_PATH); + self::killStaleProcess(); } /** - * Check if mock server is running. + * Check if mock server is running and responsive. */ public static function isMockServerRunning(): bool { - if (!file_exists(self::MOCK_SERVER_PID_PATH)) { + if (! file_exists(self::MOCK_SERVER_PID_PATH)) { return false; } $pid = (int) file_get_contents(self::MOCK_SERVER_PID_PATH); - return $pid > 0 && posix_kill($pid, 0); // Signal 0 = check existence + if ($pid <= 0) { + return false; + } + + $processAlive = posix_kill($pid, 0); + $fp = @fsockopen('127.0.0.1', self::MOCK_SERVER_PORT, $errno, $errstr, 1); + $portActive = $fp !== false; + if ($fp) { + fclose($fp); + } + + if ($processAlive !== $portActive) { + self::cleanupMockServer(); + + return false; + } + + return $processAlive; + } + + /** + * Ensure mock server is running, restart if needed. + * + * @throws \RuntimeException if the server fails to start + */ + public static function ensureMockServerRunning(): void + { + if (! self::isMockServerRunning()) { + self::cleanupMockServer(); + self::startMockServer(); + } + } + + /** + * Kill any process occupying the mock server port. + */ + private static function killStaleProcess(): void + { + $output = []; + exec("lsof -i :" . self::MOCK_SERVER_PORT . " -t 2>/dev/null", $output); + + foreach ($output as $pid) { + $pid = (int) trim($pid); + if ($pid > 0) { + posix_kill($pid, SIGTERM); + } + } + + if (! empty($output)) { + usleep(300000); + } + } + + /** + * Cleanup mock server state files without killing the process. + */ + private static function cleanupMockServer(): void + { + @unlink(self::MOCK_SERVER_PID_PATH); } /** * Wait for a server to become available on the given port. */ - private static function waitForServer(int $port, int $timeoutSeconds): void + private static function waitForServer(int $port, int $timeoutSeconds): bool { $start = time(); @@ -170,9 +303,12 @@ private static function waitForServer(int $port, int $timeoutSeconds): void $fp = @fsockopen('127.0.0.1', $port, $errno, $errstr, 1); if ($fp) { fclose($fp); - return; + + return true; } - usleep(100000); // 100ms + usleep(100000); } + + return false; } } diff --git a/tests/Browser/SmokeDashboardDemografiTest.php b/tests/Browser/SmokeDashboardDemografiTest.php index dea1ac79..3cc729c1 100644 --- a/tests/Browser/SmokeDashboardDemografiTest.php +++ b/tests/Browser/SmokeDashboardDemografiTest.php @@ -1,47 +1,17 @@ user = User::firstOrCreate( - ['email' => 'pest-demografi@opendesa.test'], - [ - 'name' => 'Pest Demografi User', - 'password' => 'password', - 'username' => 'pestdemografi', - ] - ); - SessionState::assignAdminRole($this->user); - SessionState::saveForUser($this->user); + $this->user = SessionState::loginAdminUser(); }); afterEach(function () { SessionState::clear(); }); -it('can open demografi dashboard page', function () { - $page = SessionState::loginAs($this->user) - ->navigate('/dasbor-demografi') - ->assertPathIs('/dasbor-demografi') - ->assertSee('Dasbor Demografi'); - - ScreenshotHelper::saveIfEnabled($page, 'demografi-open'); -}); - -it('displays wilayah filter', function () { - $page = SessionState::loginAs($this->user) - ->navigate('/dasbor-demografi') - ->assertVisible('@filter_kabupaten') - ->assertVisible('@filter_kecamatan') - ->assertVisible('@filter_desa') - ->assertVisible('@bt_filter'); - - ScreenshotHelper::saveIfEnabled($page, 'demografi-filter'); -}); - -it('displays all 9 chart cards', function () { +it('displays all dashboard elements', function () { $chartKeys = [ 'rentang-umur', 'status-perkawinan', @@ -54,20 +24,20 @@ 'penyandang-cacat', ]; - $page = SessionState::loginAs($this->user) - ->navigate('/dasbor-demografi'); + $page = SessionState::loginAndNavigate($this->user, '/dasbor-demografi') + ->wait(3000) + ->assertPathIs('/dasbor-demografi') + ->assertSee('Dasbor Demografi') + ->assertVisible('@filter_kabupaten') + ->assertVisible('@filter_kecamatan') + ->assertVisible('@filter_desa') + ->assertVisible('@bt_filter'); foreach ($chartKeys as $key) { $page->assertSee($key); } - ScreenshotHelper::saveIfEnabled($page, 'demografi-charts'); -}); - -it('displays chart titles', function () { - $page = SessionState::loginAs($this->user) - ->navigate('/dasbor-demografi') - ->assertSee('Rentang Umur') + $page->assertSee('Rentang Umur') ->assertSee('Status Perkawinan') ->assertSee('Pendidikan Dalam KK') ->assertSee('Golongan Darah') @@ -77,43 +47,23 @@ ->assertSee('Suku / Etnis') ->assertSee('Penyandang Cacat'); - ScreenshotHelper::saveIfEnabled($page, 'demografi-titles'); + ScreenshotHelper::saveIfEnabled($page, 'demografi-all'); }); it('updates charts when filtering by kabupaten', function () { - $page = SessionState::loginAs($this->user) - ->navigate('/dasbor-demografi'); - - $page->script(' - $("#filter_kabupaten").val("50.01").trigger("change"); - $("#bt_filter").click(); - '); - - $page->wait(5000); + $page = SessionState::loginAndNavigate($this->user, '/dasbor-demografi'); + SessionState::applyFilter($page, '50.01'); $page->assertSee('Rentang Umur'); ScreenshotHelper::saveIfEnabled($page, 'demografi-filtered'); }); it('clear filter button works', function () { - $page = SessionState::loginAs($this->user) - ->navigate('/dasbor-demografi'); - - $page->script(' - $("#filter_kabupaten").val("50.01").trigger("change"); - $("#bt_filter").click(); - '); - - $page->wait(5000); - - $page->script(' - $("#filter_kabupaten").val(null).trigger("change"); - $("#bt_filter").click(); - '); - - $page->wait(3000); + $page = SessionState::loginAndNavigate($this->user, '/dasbor-demografi'); + SessionState::applyFilter($page, '50.01'); + SessionState::clearFilter($page); $page->assertSee('Rentang Umur'); ScreenshotHelper::saveIfEnabled($page, 'demografi-clear-filter'); diff --git a/tests/Browser/SmokeDashboardTest.php b/tests/Browser/SmokeDashboardTest.php index 6059d6be..2a0bee73 100644 --- a/tests/Browser/SmokeDashboardTest.php +++ b/tests/Browser/SmokeDashboardTest.php @@ -1,118 +1,50 @@ user = User::firstOrCreate( - ['email' => 'pest-dashboard@opendesa.test'], - [ - 'name' => 'Pest Dashboard User', - 'password' => 'password', - 'username' => 'pestdashboard', - ] - ); - SessionState::assignAdminRole($this->user); - SessionState::saveForUser($this->user); + $this->user = SessionState::loginAdminUser(); }); afterEach(function () { SessionState::clear(); }); -it('can open dashboard page', function () { - $page = SessionState::loginAs($this->user) - ->navigate('/dasbor') +it('displays all dashboard elements', function () { + $page = SessionState::loginAndNavigate($this->user, '/dasbor') + ->wait(3000) ->assertPathIs('/dasbor') - ->assertSee('Dasbor'); - - ScreenshotHelper::saveIfEnabled($page, 'dashboard-open'); -}); - -it('displays wilayah filter', function () { - $page = SessionState::loginAs($this->user) - ->navigate('/dasbor') + ->assertSee('Dasbor') ->assertVisible('@filter_kabupaten') ->assertVisible('@filter_kecamatan') ->assertVisible('@filter_desa') - ->assertVisible('@bt_filter'); - - ScreenshotHelper::saveIfEnabled($page, 'dashboard-filter'); -}); - -it('displays summary cards', function () { - $page = SessionState::loginAs($this->user) - ->navigate('/dasbor') + ->assertVisible('@bt_filter') ->assertVisible('#summary_block') ->assertSee('kecamatan') ->assertSee('jumlah penduduk') - ->assertSee('jumlah keluarga'); - - ScreenshotHelper::saveIfEnabled($page, 'dashboard-summary'); -}); - -it('displays map', function () { - $page = SessionState::loginAs($this->user) - ->navigate('/dasbor') - ->assertVisible('#map'); - - ScreenshotHelper::saveIfEnabled($page, 'dashboard-map'); -}); - -it('displays data table', function () { - $page = SessionState::loginAs($this->user) - ->navigate('/dasbor') + ->assertSee('jumlah keluarga') + ->assertVisible('#map') ->assertVisible('#tabel_penduduk_block') ->assertVisible('#summary-penduduk'); - ScreenshotHelper::saveIfEnabled($page, 'dashboard-table'); + ScreenshotHelper::saveIfEnabled($page, 'dashboard-all'); }); it('updates summary when filtering by kabupaten', function () { - $page = SessionState::loginAs($this->user) - ->navigate('/dasbor'); - - $page->script(' - $("#filter_kabupaten").val("50.01").trigger("change"); - $("#bt_filter").click(); - '); - - $page->wait(3000); + $page = SessionState::loginAndNavigate($this->user, '/dasbor'); + SessionState::applyFilter($page, '50.01'); $page->assertVisible('#summary_block'); ScreenshotHelper::saveIfEnabled($page, 'dashboard-filtered'); }); -it('displays data in table', function () { - $page = SessionState::loginAs($this->user) - ->navigate('/dasbor') - ->wait(3000); - - $page->assertVisible('#summary-penduduk'); - - ScreenshotHelper::saveIfEnabled($page, 'dashboard-table-data'); -}); - it('clear filter button works', function () { - $page = SessionState::loginAs($this->user) - ->navigate('/dasbor'); - - $page->script(' - $("#filter_kabupaten").val("50.01").trigger("change"); - $("#bt_filter").click(); - '); - - $page->wait(3000); - - $page->script(' - $("#filter_kabupaten").val(null).trigger("change"); - $("#bt_filter").click(); - '); - - $page->wait(2000); + $page = SessionState::loginAndNavigate($this->user, '/dasbor'); + SessionState::applyFilter($page, '50.01'); + SessionState::clearFilter($page); $page->assertVisible('#summary_block'); ScreenshotHelper::saveIfEnabled($page, 'dashboard-clear-filter'); From 6b6d8d285c040d019b219b0309779856dff53e9c Mon Sep 17 00:00:00 2001 From: Ahmad Afandi Date: Wed, 10 Jun 2026 16:01:41 +0700 Subject: [PATCH 28/43] update test --- .github/workflows/test.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c048e20d..ae977a06 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -37,6 +37,12 @@ jobs: - name: Install Dependencies run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist + + - name: Install Node.js Dependencies + run: npm install + + - name: Install Playwright Browsers + run: npm install playwright && npx playwright install - name: Directory Permissions run: chmod -R 777 storage bootstrap/cache From 6e92d61056609c423febe52566b1117d660ad937 Mon Sep 17 00:00:00 2001 From: Ahmad Afandi Date: Thu, 11 Jun 2026 11:09:04 +0700 Subject: [PATCH 29/43] update smoke test --- composer.json | 8 +- composer.lock | 357 +++++- docs/smoke-test-browser.md | 415 +++++-- package-lock.json | 1000 ++++++++++++++++- package.json | 86 +- phpunit.xml | 3 +- public/mockServiceWorker.js | 349 ++++++ .../views/components/wilayah_filter.blade.php | 10 +- resources/views/dasbor/data-desa.blade.php | 2 +- routes/web.php | 12 +- tests/Browser/MswSetup.php | 201 ++++ tests/Browser/SessionState.php | 174 +-- tests/Browser/SmokeDashboardDemografiTest.php | 66 +- tests/Browser/SmokeDashboardTest.php | 52 +- tests/Browser/SmokeLoginTest.php | 4 +- tests/Browser/apply-patch.sh | 111 ++ tests/Browser/mock-server.php | 72 -- tests/Pest.php | 8 +- 18 files changed, 2488 insertions(+), 442 deletions(-) create mode 100644 public/mockServiceWorker.js create mode 100644 tests/Browser/MswSetup.php create mode 100755 tests/Browser/apply-patch.sh delete mode 100644 tests/Browser/mock-server.php diff --git a/composer.json b/composer.json index d6c6503c..3831d54d 100644 --- a/composer.json +++ b/composer.json @@ -50,6 +50,7 @@ "pestphp/pest": "^4.0", "pestphp/pest-plugin-browser": "^4.0", "phpunit/phpunit": "^12.0", + "react/http": "^0.4.0", "spatie/laravel-ignition": "^2.0" }, "minimum-stability": "stable", @@ -92,8 +93,13 @@ } }, "scripts": { + "post-install-cmd": [ + "@php artisan package:discover --ansi", + "bash tests/Browser/apply-patch.sh" + ], "post-update-cmd": [ - "@php artisan vendor:publish --tag=laravel-assets --ansi --force" + "@php artisan vendor:publish --tag=laravel-assets --ansi --force", + "bash tests/Browser/apply-patch.sh" ], "post-autoload-dump": [ "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump", diff --git a/composer.lock b/composer.lock index 5620ba7c..a7a5aefa 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "27d97b095c210931e907629054c7d0ad", + "content-hash": "a525832afb80adb040854dc43cc97469", "packages": [ { "name": "akaunting/laravel-apexcharts", @@ -11576,6 +11576,58 @@ }, "time": "2026-02-07T07:09:04+00:00" }, + { + "name": "evenement/evenement", + "version": "v2.1.0", + "source": { + "type": "git", + "url": "https://github.com/igorw/evenement.git", + "reference": "6ba9a777870ab49f417e703229d53931ed40fd7a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/igorw/evenement/zipball/6ba9a777870ab49f417e703229d53931ed40fd7a", + "reference": "6ba9a777870ab49f417e703229d53931ed40fd7a", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0||^5.7||^4.8.35" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "psr-0": { + "Evenement": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + } + ], + "description": "Événement is a very simple event dispatching library for PHP", + "keywords": [ + "event-dispatcher", + "event-emitter" + ], + "support": { + "issues": "https://github.com/igorw/evenement/issues", + "source": "https://github.com/igorw/evenement/tree/master" + }, + "time": "2017-07-17T17:39:19+00:00" + }, { "name": "fakerphp/faker", "version": "v1.24.1", @@ -11771,6 +11823,54 @@ ], "time": "2025-08-08T12:00:00+00:00" }, + { + "name": "guzzle/parser", + "version": "v3.9.2", + "target-dir": "Guzzle/Parser", + "source": { + "type": "git", + "url": "https://github.com/Guzzle3/parser.git", + "reference": "6874d171318a8e93eb6d224cf85e4678490b625c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Guzzle3/parser/zipball/6874d171318a8e93eb6d224cf85e4678490b625c", + "reference": "6874d171318a8e93eb6d224cf85e4678490b625c", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + }, + "autoload": { + "psr-0": { + "Guzzle\\Parser": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Interchangeable parsers used by Guzzle", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "URI Template", + "cookie", + "http", + "message", + "url" + ], + "support": { + "source": "https://github.com/Guzzle3/parser/tree/v3.9.2" + }, + "abandoned": "guzzle/guzzle", + "time": "2014-02-05T18:29:46+00:00" + }, { "name": "hamcrest/hamcrest-php", "version": "v2.1.1", @@ -13812,6 +13912,261 @@ ], "time": "2026-05-27T14:01:10+00:00" }, + { + "name": "react/event-loop", + "version": "v0.4.3", + "source": { + "type": "git", + "url": "https://github.com/reactphp/event-loop.git", + "reference": "8bde03488ee897dc6bb3d91e4e17c353f9c5252f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/event-loop/zipball/8bde03488ee897dc6bb3d91e4e17c353f9c5252f", + "reference": "8bde03488ee897dc6bb3d91e4e17c353f9c5252f", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.8" + }, + "suggest": { + "ext-event": "~1.0", + "ext-libev": "*", + "ext-libevent": ">=0.1.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\EventLoop\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Event loop abstraction layer that libraries can use for evented I/O.", + "keywords": [ + "asynchronous", + "event-loop" + ], + "support": { + "issues": "https://github.com/reactphp/event-loop/issues", + "source": "https://github.com/reactphp/event-loop/tree/0.4" + }, + "time": "2017-04-27T10:56:23+00:00" + }, + { + "name": "react/http", + "version": "v0.4.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/http.git", + "reference": "7b9d293b7a3f73acd840a341497e267d8562d637" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/http/zipball/7b9d293b7a3f73acd840a341497e267d8562d637", + "reference": "7b9d293b7a3f73acd840a341497e267d8562d637", + "shasum": "" + }, + "require": { + "guzzle/parser": "~3.0", + "php": ">=5.4.0", + "react/socket": "0.4.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.4-dev" + } + }, + "autoload": { + "psr-4": { + "React\\Http\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Library for building an evented http server.", + "keywords": [ + "http" + ], + "support": { + "source": "https://github.com/reactphp/http/tree/v0.4.1" + }, + "time": "2014-02-02T01:11:26+00:00" + }, + { + "name": "react/promise", + "version": "v2.11.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/promise.git", + "reference": "1a8460931ea36dc5c76838fec5734d55c88c6831" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/promise/zipball/1a8460931ea36dc5c76838fec5734d55c88c6831", + "reference": "1a8460931ea36dc5c76838fec5734d55c88c6831", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "React\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "A lightweight implementation of CommonJS Promises/A for PHP", + "keywords": [ + "promise", + "promises" + ], + "support": { + "issues": "https://github.com/reactphp/promise/issues", + "source": "https://github.com/reactphp/promise/tree/v2.11.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2023-11-16T16:16:50+00:00" + }, + { + "name": "react/socket", + "version": "v0.4.6", + "source": { + "type": "git", + "url": "https://github.com/reactphp/socket.git", + "reference": "cf074e53c974df52388ebd09710a9018894745d2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/socket/zipball/cf074e53c974df52388ebd09710a9018894745d2", + "reference": "cf074e53c974df52388ebd09710a9018894745d2", + "shasum": "" + }, + "require": { + "evenement/evenement": "~2.0|~1.0", + "php": ">=5.3.0", + "react/event-loop": "0.4.*|0.3.*", + "react/promise": "^2.0 || ^1.1", + "react/stream": "^0.4.5" + }, + "require-dev": { + "clue/block-react": "^1.1", + "phpunit/phpunit": "~4.8", + "react/socket-client": "^0.5.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Socket\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Async, streaming plaintext TCP/IP and secure TLS socket server for React PHP", + "keywords": [ + "Socket" + ], + "support": { + "issues": "https://github.com/reactphp/socket/issues", + "source": "https://github.com/reactphp/socket/tree/v0.4.6" + }, + "time": "2017-01-26T09:23:38+00:00" + }, + { + "name": "react/stream", + "version": "v0.4.6", + "source": { + "type": "git", + "url": "https://github.com/reactphp/stream.git", + "reference": "44dc7f51ea48624110136b535b9ba44fd7d0c1ee" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/stream/zipball/44dc7f51ea48624110136b535b9ba44fd7d0c1ee", + "reference": "44dc7f51ea48624110136b535b9ba44fd7d0c1ee", + "shasum": "" + }, + "require": { + "evenement/evenement": "^2.0|^1.0", + "php": ">=5.3.8" + }, + "require-dev": { + "clue/stream-filter": "~1.2", + "react/event-loop": "^0.4|^0.3", + "react/promise": "^2.0|^1.0" + }, + "suggest": { + "react/event-loop": "^0.4", + "react/promise": "^2.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Stream\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Basic readable and writable stream interfaces that support piping.", + "keywords": [ + "pipe", + "stream" + ], + "support": { + "issues": "https://github.com/reactphp/stream/issues", + "source": "https://github.com/reactphp/stream/tree/v0.4.6" + }, + "time": "2017-01-25T14:44:14+00:00" + }, { "name": "revolt/event-loop", "version": "v1.0.9", diff --git a/docs/smoke-test-browser.md b/docs/smoke-test-browser.md index ca156130..92f87764 100644 --- a/docs/smoke-test-browser.md +++ b/docs/smoke-test-browser.md @@ -8,6 +8,7 @@ Panduan lengkap pembuatan smoke test menggunakan **Pest v4** + **pest-plugin-bro - [Prasyarat](#prasyarat) - [Instalasi](#instalasi) - [Konfigurasi](#konfigurasi) +- [MSW Setup](#msw-setup) - [Penulisan Test](#penulisan-test) - [Session State Management](#session-state-management) - [Custom Login Route](#custom-login-route) @@ -28,8 +29,8 @@ pest-plugin-browser Playwright Server (WebSocket) │ ▼ -Chromium Browser - │ +Chromium Browser + MSW Service Worker + │ (intercepts fetch/XHR requests) ▼ Laravel HTTP Server (amphp) │ @@ -37,7 +38,23 @@ Laravel HTTP Server (amphp) Aplikasi Laravel ``` -Pest browser plugin menjalankan **Laravel HTTP server lokal** (amphp) dan mengontrol **Chromium browser** via Playwright WebSocket. Test ditulis 100% dalam syntax PHP Pest. +Pest browser plugin menjalankan **Laravel HTTP server lokal** (amphp) dan mengontrol **Chromium browser** via Playwright WebSocket. **MSW (Mock Service Worker)** berjalan di browser untuk mengintersep request API dan mengembalikan data fixture. + +### Request Flow + +``` +Browser Page (JS) + │ + ├─ fetch('/api/v1/data-website') + │ │ + │ ▼ + │ MSW Interceptor (init script) + │ │ + │ ├─ Route match → return fixture JSON + │ └─ No match → forward to real server + │ + └─ Page renders with mock data +``` --- @@ -47,7 +64,7 @@ Pest browser plugin menjalankan **Laravel HTTP server lokal** (amphp) dan mengon |----------|-------|------------| | PHP | 8.4+ | | | Laravel | 13+ | | -| Node.js | 18+ | Untuk Playwright | +| Node.js | 18+ | Untuk MSW | | Composer | 2+ | | --- @@ -61,11 +78,11 @@ composer require pestphp/pest:^4.0 --dev -W composer require pestphp/pest-plugin-browser:^4.0 --dev -W ``` -### 2. Install Playwright (npm) +### 2. Install MSW (npm) ```bash -npm install playwright @playwright/test --save-dev -npx playwright install chromium +npm install msw --save-dev +npx msw init public/ --save ``` ### 3. Publish Pest Configuration @@ -81,6 +98,11 @@ uses(TestCase::class)->in('Feature', 'Browser'); // Timeout untuk browser tests (dalam milidetik) pest()->browser()->timeout(30000); + +// Disable Google Fonts di test environment +beforeEach(function () { + config(['adminlte.google_fonts.allowed' => false]); +}); ``` ### 4. Update phpunit.xml @@ -99,8 +121,24 @@ Pastikan `APP_ENV` di-set ke `testing`: # Pest Browser Testing tests/Browser/Screenshots/ tests/Browser/.session_state.json +public/mockServiceWorker.js +``` + +### 6. Vendor Patching + +Jalankan `apply-patch.sh` untuk menginjek MSW init script ke vendor: + +```bash +# Otomatis dijalankan oleh composer hook +composer install +composer update + +# Atau manual +bash tests/Browser/apply-patch.sh ``` +Script ini menambahkan MSW init script ke `vendor/pestphp/pest-plugin-browser/src/Playwright/InitScript.php`. + --- ## Konfigurasi @@ -131,6 +169,180 @@ pest()->browser()->headless(false); --- +## MSW Setup + +### Cara Kerja + +MSW (Mock Service Worker) berjalan di browser sebagai service worker yang mengintersep request fetch/XHR. Setiap kali halaman dibuka: + +1. Playwright inject init script ke browser +2. Init script mendaftarkan MSW service worker +3. Service worker mengintersep request API +4. Request yang cocok dengan route dikembalikan dari fixture JSON +5. Request yang tidak cocok diteruskan ke server asli + +### Menambahkan Route Baru + +#### Langkah 1: Buat Fixture JSON + +Simpan file JSON di `tests/Browser/fixtures/`: + +``` +tests/Browser/fixtures/ +├── kabupaten.json +├── kecamatan-50.01.json +├── kecamatan-50.02.json +├── desa-50.01.01.json +├── data-website.json +├── statistik-penduduk-rentang-umur.json +└── ... +``` + +#### Langkah 2: Tambah Route di MswSetup.php + +Edit `tests/Browser/MswSetup.php` — tambahkan route ke constant `ROUTES` atau `REGEX_ROUTES`: + +```php +// Exact match (URL statis) +private const ROUTES = [ + '/api/v1/statistik-web/get-list-kabupaten' => 'kabupaten.json', + '/api/v1/data-website' => 'data-website.json', + '/api/v1/endpoint-baru' => 'endpoint-baru.json', // ← tambah di sini +]; + +// Regex match (URL dengan parameter dinamik) +private const REGEX_ROUTES = [ + '#/api/v1/statistik-web/get-list-kecamatan/([\d.]+)$#' => 'kecamatan-*.json', + '#/api/v1/statistik/penduduk\?.*filter\[id\]=([^&]+)#' => 'statistik-penduduk-*.json', + '#/api/v1/baru/([\w-]+)$#' => 'baru-*.json', // ← tambah di sini +]; +``` + +#### Langkah 3: Jalankan Test + +```bash +php vendor/bin/pest --filter="TestBaru" +``` + +**Tidak perlu edit vendor** — `InitScript.php` sudah ter-patch dan otomatis memanggil `MswSetup::getInitScriptJs()`. + +#### Workflow Lengkap + +``` +1. Buat fixture JSON + tests/Browser/fixtures/baru.json + +2. Tambah route di MswSetup.php + - Exact: tambah ke ROUTES + - Regex: tambah ke REGEX_ROUTES + +3. Jalankan test + php vendor/bin/pest --filter="TestBaru" +``` + +### Route Matching + +MSW menggunakan dua jenis matching: + +| Type | Kapan Digunakan | Contoh | +|------|-----------------|--------| +| Exact | URL statis, tidak ada parameter | `/api/v1/data-website` | +| Regex | URL dengan parameter dinamik | `/api/v1/statistik/penduduk?filter[id]=rentang-umur` | + +#### Exact Route + +```php +private const ROUTES = [ + '/api/v1/data-website' => 'data-website.json', + // Request ke /api/v1/data-website → return data-website.json +]; +``` + +#### Regex Route + +```php +private const REGEX_ROUTES = [ + '#/api/v1/statistik/penduduk\?.*filter\[id\]=([^&]+)#' => 'statistik-penduduk-*.json', + // Request ke /api/v1/statistik/penduduk?filter[id]=rentang-umur + // → return statistik-penduduk-rentang-umur.json +]; +``` + +Regex route menggunakan glob pattern (`*.json`) untuk resolve file fixture secara otomatis berdasarkan parameter URL. + +### Vendor Patching: Mengapa Edit InitScript.php? + +#### Masalah + +Pest browser plugin meng-inject init script ke browser via `InitScript::get()`: + +```php +// vendor/pestphp/pest-plugin-browser/src/Playwright/Page.php +$context->addInitScript(InitScript::get()); // hardcoded +``` + +`InitScript::get()` hanya mengembalikan script Pest default (axe.js). **Tidak ada hook/callback untuk menambahkan script lain.** + +#### Solusi + +Edit `InitScript.php` via composer hook (`apply-patch.sh`) untuk memanggil `MswSetup::getInitScriptJs()`: + +```php +// vendor/.../InitScript.php (patched) +public static function get(): string +{ + $initScriptJs = <<browser()->initScript(fn() => MswSetup::getInitScriptJs()); +``` + +### External CDN Blocking + +MSW otomatis memblokir request ke external CDN (Google Fonts, dll) untuk mempercepat test: + +```javascript +// Di MswSetup.php +if (url.includes('fonts.googleapis.com') || + url.includes('fonts.gstatic.com') || + url.includes('cdn.jsdelivr.net')) { + return new Response('', {status: 204}); +} +``` + +Untuk CSS `` tags (Google Fonts), MSW tidak bisa memblokir. Gunakan config Laravel: + +```php +// tests/Pest.php +beforeEach(function () { + config(['adminlte.google_fonts.allowed' => false]); +}); +``` + +--- + ## Penulisan Test ### Menggunakan data-testid (Recommended) @@ -185,10 +397,14 @@ Gunakan format `feature-element` untuk data-testid: | Email input | `login-email` | Input email/username | | Password input | `login-password` | Input password | | Submit button | `login-submit` | Tombol submit | -| Remember checkbox | `login-remember` | Checkbox remember me | -| Error message | `login-error` | Error validation | -| Flash success | `flash-success` | Success notification | -| Flash error | `flash-error` | Error notification | +| Filter Kabupaten | `filter-kabupaten` | Select filter kabupaten | +| Filter Kecamatan | `filter-kecamatan` | Select filter kecamatan | +| Filter Desa | `filter-desa` | Select filter desa | +| Tombol Filter | `bt-filter` | Tombol Tampilkan | +| Summary Block | `summary-block` | Container kartu summary | +| Peta | `peta` | Container peta | +| Tabel Penduduk | `tabel-penduduk-block` | Container tabel | +| Chart Item | `chart-item-{key}` | Container chart demografi | #### 4. Selector Hierarchy @@ -205,12 +421,22 @@ Gunakan format `feature-element` untuk data-testid: ``` tests/ ├── Browser/ -│ ├── SmokeLoginTest.php # Test login -│ ├── SmokeDashboardTest.php # Test dashboard -│ └── SessionState.php # Helper session management +│ ├── SmokeLoginTest.php # Test login +│ ├── SmokeDashboardTest.php # Test dashboard +│ ├── SmokeDashboardDemografiTest.php # Test demografi +│ ├── SessionState.php # Helper session management +│ ├── ScreenshotHelper.php # Screenshot management +│ ├── MswSetup.php # MSW route & init script generator +│ ├── apply-patch.sh # Vendor patch script (composer hook) +│ └── fixtures/ # JSON fixture files +│ ├── kabupaten.json +│ ├── kecamatan-*.json +│ ├── desa-*.json +│ ├── data-website.json +│ └── statistik-penduduk-*.json ├── Feature/ ├── Unit/ -└── Pest.php # Pest configuration +└── Pest.php # Pest configuration ``` ### Test Dasar @@ -257,6 +483,25 @@ it('shows error for invalid credentials', function () { }); ``` +#### Test dengan Filter Interaksi + +```php +it('applies filter and elements remain visible', function () { + $page = SessionState::loginAndNavigate($this->user, '/dasbor') + ->assertPathIs('/dasbor') + ->assertVisible('@filter-kabupaten') + ->assertVisible('@bt-filter'); + + // Set filter value via JavaScript (karena Select2) + $page->script("$('#filter_kabupaten').val('50.01').trigger('change')"); + $page->click('@bt-filter'); + + // Assert elemen masih terlihat setelah filter + $page->assertVisible('@peta') + ->assertVisible('@tabel-penduduk-block'); +}); +``` + ### API Reference | Method | Keterangan | @@ -268,6 +513,7 @@ it('shows error for invalid credentials', function () { | `typeSlowly($selector, $value, $delay)` | Ketik perlahan | | `press($text)` | Klik tombol/link | | `click($selector)` | Klik element | +| `select($field, $option)` | Pilih option di select | | `submit()` | Submit form pertama | | `value($selector)` | Ambil nilai input | | `script($js)` | Jalankan JavaScript | @@ -275,9 +521,8 @@ it('shows error for invalid credentials', function () { | `assertVisible($selector)` | Assert element terlihat | | `assertPathIs($path)` | Assert URL path | | `assertPathIsNot($path)` | Assert URL path bukan | -| `wait($seconds)` | Tunggu beberapa detik | +| `assertNoJavaScriptErrors()` | Assert tidak ada JS error | | `screenshot($name)` | Ambil screenshot | -| `dd()` | Dump & die | --- @@ -289,7 +534,7 @@ Setiap Pest browser test membuat **browser context baru**, sehingga session/cook ### Solusi -Gunakan `SessionState` helper untuk save/restore user ID ke file, lalu bypass form login via route `/_pest/login/{id}`. +Gunakan `SessionState` helper untuk bypass form login via route `/_pest/login/{id}`. ### SessionState Helper @@ -304,36 +549,31 @@ final class SessionState { private const STORAGE_PATH = __DIR__ . '/.session_state.json'; - // Simpan state user ke file - public static function saveForUser(User $user): void + // Login user dan bypass force password reset + public static function loginAdminUser(): User { - $state = [ - 'user_id' => $user->id, - 'email' => $user->email, - 'created_at' => now()->toIso8601String(), - ]; - file_put_contents(self::STORAGE_PATH, json_encode($state, JSON_PRETTY_PRINT)); + $user = User::where('email', 'admin@admin.com')->firstOrFail(); + $user->update([ + 'force_password_reset' => false, + 'password_expires_at' => null, + ]); + return $user; } - // Load state dari file - public static function load(): ?array { /* ... */ } - - // Hapus state file - public static function clear(): void { /* ... */ } - - // Buat atau ambil user exist - public static function getOrCreateUser(string $emailPrefix = 'pest-session'): User { /* ... */ } - - // Login langsung via route - public static function loginAs(User $user): \Pest\Browser\Api\AwaitableWebpage - { - $result = visit("/_pest/login/{$user->id}"); - self::saveForUser($user); - return $result; + // Login via route dan navigate ke URL + public static function loginAndNavigate( + User $user, + string $url, + array $options = [] + ): \Pest\Browser\Api\Webpage { + $options['headers'] = [ + 'Cookie' => self::getSessionCookie($user), + ]; + return visit($url, $options); } - // Restore session dari file - public static function restoreSession(): ?\Pest\Browser\Api\AwaitableWebpage { /* ... */ } + // Cleanup + public static function clear(): void { /* ... */ } } ``` @@ -342,26 +582,20 @@ final class SessionState ```php use Tests\Browser\SessionState; -it('can restore session from saved state', function () { - // Buat user - $user = SessionState::getOrCreateUser('pest-restore'); - - // Simpan state - SessionState::saveForUser($user); - - // Load state - $state = SessionState::load(); - expect($state)->not->toBeNull(); - expect($state['user_id'])->toBe($user->id); - - // Login via quick route - visit("/_pest/login/{$user->id}") - ->navigate('/dasbor') - ->assertPathIs('/dasbor'); +beforeEach(function () { + $this->user = SessionState::loginAdminUser(); +}); - // Cleanup +afterEach(function () { SessionState::clear(); }); + +it('displays dashboard elements', function () { + SessionState::loginAndNavigate($this->user, '/dasbor') + ->assertPathIs('/dasbor') + ->assertVisible('@summary-block') + ->assertVisible('@peta'); +}); ``` ### Performance @@ -370,7 +604,6 @@ it('can restore session from saved state', function () { |---------|-------| | Login via form | ~1.5s | | Login via `/_pest/login/{id}` | ~1.4s | -| **Session restore** | **~0.55s** (3x lebih cepat) | --- @@ -435,20 +668,34 @@ User::factory()->create([ ]); ``` -### Variable Undefined di View +### Force Password Reset Redirect -**Gejala:** `Undefined variable $settingAplikasi` di view. +**Gejala:** Test redirect ke `/password-reset/force` setelah login. -**Kemungkinan:** View partial yang di-include sebelum AppServiceProvider boot selesai. +**Root Cause:** User memiliki `force_password_reset=true` atau password expired. -**Solusi:** Gunakan `assertPathIs()` alih-alih `assertSee()`: +**Solusi:** Bersihkan flag sebelum test: ```php -// ❌ Bisa gagal -->assertSee('Dasbor'); +$user->update([ + 'force_password_reset' => false, + 'password_expires_at' => null, +]); +``` + +### Google Fonts Blocking + +**Gejala:** Test hang atau timeout karena menunggu Google Fonts load. + +**Root Cause:** `` tag untuk Google Fonts memblokir page load. -// ✅ Lebih reliable -->assertPathIs('/dasbor'); +**Solusi:** Disable Google Fonts di test environment: + +```php +// tests/Pest.php +beforeEach(function () { + config(['adminlte.google_fonts.allowed' => false]); +}); ``` ### Test Timeout @@ -472,6 +719,15 @@ npm install playwright @playwright/test npx playwright install chromium ``` +### MSW Routes Not Matching + +**Gejala:** Request API tidak di-intercept, mengembalikan 404 atau data asli. + +**Solusi:** +1. Pastikan route di `MswSetup.php` benar +2. Pastikan file fixture ada di `tests/Browser/fixtures/` +3. Pastikan regex escape benar (`\\/` untuk `/`) + --- ## Best Practices @@ -507,8 +763,14 @@ Contoh: - login-email - login-password - login-submit -- dashboard-sidebar -- user-profile-avatar +- filter-kabupaten +- filter-kecamatan +- filter-desa +- bt-filter +- summary-block +- peta +- tabel-penduduk-block +- chart-item-rentang-umur ``` ### 4. Jangan Test Internal Implementation @@ -536,6 +798,16 @@ Contoh: ->assertVisible('@dashboard-welcome') ``` +### 6. Hindari `wait()` Method + +```php +// ❌ Hindari - memblokir event loop +$page->wait(3); + +// ✅ Gunakan assertVisible yang polling +$page->assertVisible('@element'); +``` + --- ## Screenshot Management @@ -612,4 +884,5 @@ Fitur ini **selalu aktif** tanpa perlu konfigurasi. - [Pest v4 Documentation](https://pestphp.com/docs/browser) - [pest-plugin-browser](https://github.com/pestphp/pest-browser) - [Playwright for PHP](https://playwright.dev/php/) +- [MSW Documentation](https://mswjs.io/docs/) - [Laravel HTTP Testing](https://laravel.com/docs/http-tests) diff --git a/package-lock.json b/package-lock.json index dcbd3561..e574f108 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,6 +26,7 @@ "jquery": "^3.6", "laravel-vite-plugin": "^0.7.2", "lodash": "^4.17.19", + "msw": "^2.14.6", "popper.js": "^1.16.1", "postcss": "^8.1.14", "sass": "^1.56.1", @@ -441,6 +442,93 @@ "node": ">=6" } }, + "node_modules/@inquirer/ansi": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-2.0.7.tgz", + "integrity": "sha512-3eTuUO1vH2cZm2ZKHeQxnOqlTi9EfZDGgIe3BL3I4u+rJHocr9Fz86M4fjYABPvFnQG/gGK551HqDiIcETwU6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + } + }, + "node_modules/@inquirer/confirm": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-6.1.1.tgz", + "integrity": "sha512-eb8DBZcz/2qHWQda4rk2JiQk5h9QV/cVHi1yjt0f69WFZMRFn0sJTye3EAP8icut8UDMjQPsaH5KbcOogefrFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^11.2.1", + "@inquirer/type": "^4.0.7" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core": { + "version": "11.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-11.2.1.tgz", + "integrity": "sha512-Qd6GJT1yVyrZZCfN8W2qKF5ApmqryXRhRKCuip8h01x2w/esJQ2XIYc6f9abMIHgKQdBfFTSOdbHRLAhuM09UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^2.0.7", + "@inquirer/figures": "^2.0.7", + "@inquirer/type": "^4.0.7", + "cli-width": "^4.1.0", + "fast-wrap-ansi": "^0.2.0", + "mute-stream": "^3.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/figures": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-2.0.7.tgz", + "integrity": "sha512-aJ8TBPOGB6f/2qziPfElISTCEd5XOYTFckA2SGjhNmiKzfK/u4ot3v0DUzGVdUnKjN10EqnnEPck36BkyfLnJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + } + }, + "node_modules/@inquirer/type": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-4.0.7.tgz", + "integrity": "sha512-t28inv14nMQ1PhKpsJPY+kEs/c00qzeCOS2gTNRyTjG5d6qsVA2fItxW4hkvGZ5lvanGLdtCzVIx5dwdRpN1+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", @@ -510,6 +598,56 @@ "integrity": "sha512-GMoTcF6WBpno7a7Iyx7M79os26d5bCDbh7YTZmXZM8YuLR3DDtwo0/CBYddStGD6QIBTieFDz4IAQiO0dAdRGw==", "dev": true }, + "node_modules/@mswjs/interceptors": { + "version": "0.41.9", + "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.41.9.tgz", + "integrity": "sha512-VVPPgHyQ6ShqnrmDWuxjmUIsO9gWyOZFmuOfLd9LfBGQJwZfy0gvv9pbHSJuoFNIYC7ZDX9aoFwowjcdSC4E8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@open-draft/deferred-promise": "^2.2.0", + "@open-draft/logger": "^0.3.0", + "@open-draft/until": "^2.0.0", + "is-node-process": "^1.2.0", + "outvariant": "^1.4.3", + "strict-event-emitter": "^0.5.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@mswjs/interceptors/node_modules/@open-draft/deferred-promise": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", + "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@open-draft/deferred-promise": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-3.0.0.tgz", + "integrity": "sha512-XW375UK8/9SqUVNVa6M0yEy8+iTi4QN5VZ7aZuRFQmy76LRwI9wy5F4YIBU6T+eTe2/DNDo8tqu8RHlwLHM6RA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@open-draft/logger": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz", + "integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-node-process": "^1.2.0", + "outvariant": "^1.4.0" + } + }, + "node_modules/@open-draft/until": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz", + "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==", + "dev": true, + "license": "MIT" + }, "node_modules/@playwright/test": { "version": "1.57.0", "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.57.0.tgz", @@ -678,8 +816,24 @@ "version": "20.5.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.1.tgz", "integrity": "sha512-4tT2UrL5LBqDwoed9wZ6N3umC4Yhz3W3FloMmiiG4JwmUJWpie0c7lcnUNd4gtMKuDEO4wRVS8B6Xa0uMRsMKg==", + "dev": true + }, + "node_modules/@types/set-cookie-parser": { + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/@types/set-cookie-parser/-/set-cookie-parser-2.4.10.tgz", + "integrity": "sha512-GGmQVGpQWUe5qglJozEjZV/5dyxbOOZ0LHe/lqyWssB88Y4svNfst0uqBVscdDeIKl5Jy5+aPSvy7mI9tYRguw==", "dev": true, - "peer": true + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/statuses": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.6.tgz", + "integrity": "sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==", + "dev": true, + "license": "MIT" }, "node_modules/@vue/reactivity": { "version": "3.1.5", @@ -1121,6 +1275,45 @@ "node": ">=0.4.2" } }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ansi-styles/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -1534,6 +1727,31 @@ "node": ">=6.0" } }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/clone": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", @@ -1625,6 +1843,20 @@ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", "dev": true }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -2055,6 +2287,13 @@ "license": "ISC", "peer": true }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, "node_modules/emojis-list": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", @@ -2253,7 +2492,6 @@ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -2474,6 +2712,23 @@ "integrity": "sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw==", "dev": true }, + "node_modules/fast-string-truncated-width": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz", + "integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-string-width": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz", + "integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-string-truncated-width": "^3.0.2" + } + }, "node_modules/fast-uri": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", @@ -2492,6 +2747,16 @@ "license": "BSD-3-Clause", "peer": true }, + "node_modules/fast-wrap-ansi": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.2.tgz", + "integrity": "sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-string-width": "^3.0.2" + } + }, "node_modules/fastclick": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/fastclick/-/fastclick-1.0.6.tgz", @@ -2631,6 +2896,16 @@ "integrity": "sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ==", "dev": true }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -2709,6 +2984,16 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, + "node_modules/graphql": { + "version": "16.14.2", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.14.2.tgz", + "integrity": "sha512-Chq1s4CY7jmh8gO2qvLIJyfCDIN+EHLFW/9iShnp1z8FjBQMoodWP1kDC36VAMXXIvAjj4ARa7ntfAV2BrjsbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -2786,6 +3071,17 @@ "node": ">= 0.4" } }, + "node_modules/headers-polyfill": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-5.0.1.tgz", + "integrity": "sha512-1TJ6Fih/b8h5TIcv+1+Hw0PDQWJTKDKzFZzcKOiW1wJza3XoAQlkCuXLbymPYB8+ZQyw8mHvdw560e8zVFIWyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/set-cookie-parser": "^2.4.10", + "set-cookie-parser": "^3.0.1" + } + }, "node_modules/icheck-bootstrap": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/icheck-bootstrap/-/icheck-bootstrap-3.0.1.tgz", @@ -2910,6 +3206,16 @@ "node": ">=0.10.0" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -2922,6 +3228,13 @@ "node": ">=0.10.0" } }, + "node_modules/is-node-process": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz", + "integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==", + "dev": true, + "license": "MIT" + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -3276,6 +3589,61 @@ "node": "*" } }, + "node_modules/msw": { + "version": "2.14.6", + "resolved": "https://registry.npmjs.org/msw/-/msw-2.14.6.tgz", + "integrity": "sha512-ALe+N10S72cyx94cMcy3Zs4HhXCj35sgeAL4c+WTvKi0zWnbd8/h0lcFqv0mb2P+aSgAdD7p9HzvA0DiUPxsyg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@inquirer/confirm": "^6.0.11", + "@mswjs/interceptors": "^0.41.3", + "@open-draft/deferred-promise": "^3.0.0", + "@types/statuses": "^2.0.6", + "cookie": "^1.1.1", + "graphql": "^16.13.2", + "headers-polyfill": "^5.0.1", + "is-node-process": "^1.2.0", + "outvariant": "^1.4.3", + "path-to-regexp": "^6.3.0", + "picocolors": "^1.1.1", + "rettime": "^0.11.11", + "statuses": "^2.0.2", + "strict-event-emitter": "^0.5.1", + "tough-cookie": "^6.0.1", + "type-fest": "^5.5.0", + "until-async": "^3.0.2", + "yargs": "^17.7.2" + }, + "bin": { + "msw": "cli/index.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mswjs" + }, + "peerDependencies": { + "typescript": ">= 4.8.x" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/mute-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-3.0.0.tgz", + "integrity": "sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/nanoid": { "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", @@ -3375,6 +3743,13 @@ "node": ">= 0.8.0" } }, + "node_modules/outvariant": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz", + "integrity": "sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==", + "dev": true, + "license": "MIT" + }, "node_modules/overlayscrollbars": { "version": "1.13.3", "resolved": "https://registry.npmjs.org/overlayscrollbars/-/overlayscrollbars-1.13.3.tgz", @@ -3401,6 +3776,13 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "node_modules/path-to-regexp": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "dev": true, + "license": "MIT" + }, "node_modules/pdfmake": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/pdfmake/-/pdfmake-0.2.7.tgz", @@ -3637,6 +4019,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -3665,6 +4057,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/rettime": { + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/rettime/-/rettime-0.11.11.tgz", + "integrity": "sha512-ILJRqVWBCTlg9r42fFgwVZx1gnFAcQF8mRoMkbgQfIrjEDf9nbBFDFx00oloOa+Q869FUtaYDXZvEfnecQSCoQ==", + "dev": true, + "license": "MIT" + }, "node_modules/rollup": { "version": "3.29.5", "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.5.tgz", @@ -3818,6 +4217,13 @@ "randombytes": "^2.1.0" } }, + "node_modules/set-cookie-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-3.1.0.tgz", + "integrity": "sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw==", + "dev": true, + "license": "MIT" + }, "node_modules/setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", @@ -3842,6 +4248,19 @@ "integrity": "sha512-b6i4ZpVuUxB9h5gfCxPiusKYkqTMOjEbBs4wMaFbkfia4yFv92UKZ6Df8WXcKbn08JNL/abvg3FnMAOfakDvUw==", "dev": true }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -4013,6 +4432,23 @@ "sourcemap-codec": "^1.4.1" } }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/strict-event-emitter": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz", + "integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==", + "dev": true, + "license": "MIT" + }, "node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -4022,6 +4458,34 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/summernote": { "version": "0.8.20", "resolved": "https://registry.npmjs.org/summernote/-/summernote-0.8.20.tgz", @@ -4068,6 +4532,19 @@ "url": "https://github.com/sponsors/limonte" } }, + "node_modules/tagged-tag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz", + "integrity": "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/tapable": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", @@ -4262,6 +4739,26 @@ "integrity": "sha512-DnKEfPNQnOJc8Ca1roZBs/GSbkAZyIIbC4p8eHZyZQi85OSAXtiVNYMaRxo4mzsGKpa0sA4/Us4KXQkX8q7w2A==", "license": "SEE LICENSE IN license.md" }, + "node_modules/tldts": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.2.tgz", + "integrity": "sha512-kCwffuaH8ntKtygnWe1b4BJKWiCUH30n5KfoTr6IchcXOwR7chAOFJxFrH3vjANafUYrIA4a7SDL+nn7SiR4Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^7.4.2" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.2.tgz", + "integrity": "sha512-nwEyF4vl4RSJjwSjBUmOSxc3BFPoIFdlRthJ6e+5v9P3bHNsoD06UjuqMUspqp7vsEZ1beaHi1km+optiE17yA==", + "dev": true, + "license": "MIT" + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -4284,6 +4781,19 @@ "jquery": ">=1.12.0" } }, + "node_modules/tough-cookie": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.1.tgz", + "integrity": "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^7.0.5" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/type": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", @@ -4302,6 +4812,22 @@ "node": ">= 0.8.0" } }, + "node_modules/type-fest": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.7.0.tgz", + "integrity": "sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "dependencies": { + "tagged-tag": "^1.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -4343,6 +4869,16 @@ "node": ">= 10.0.0" } }, + "node_modules/until-async": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/until-async/-/until-async-3.0.2.tgz", + "integrity": "sha512-IiSk4HlzAMqTUseHHe3VhIGyuFmN90zMTpD3Z3y8jeQbzLIq500MVM7Jq2vUAnTKAFPJrqwkzr6PoTcPhGcOiw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/kettanaito" + } + }, "node_modules/update-browserslist-db": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", @@ -4610,6 +5146,24 @@ "node": ">=0.10.0" } }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/xmldoc": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/xmldoc/-/xmldoc-1.3.0.tgz", @@ -4627,6 +5181,45 @@ "engines": { "node": ">=0.4" } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } } }, "dependencies": { @@ -4836,6 +5429,50 @@ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.4.2.tgz", "integrity": "sha512-m5cPn3e2+FDCOgi1mz0RexTUvvQibBebOUlUlW0+YrMjDTPkiJ6VTKukA1GRsvRw+12KyJndNjj0O4AgTxm2Pg==" }, + "@inquirer/ansi": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-2.0.7.tgz", + "integrity": "sha512-3eTuUO1vH2cZm2ZKHeQxnOqlTi9EfZDGgIe3BL3I4u+rJHocr9Fz86M4fjYABPvFnQG/gGK551HqDiIcETwU6Q==", + "dev": true + }, + "@inquirer/confirm": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-6.1.1.tgz", + "integrity": "sha512-eb8DBZcz/2qHWQda4rk2JiQk5h9QV/cVHi1yjt0f69WFZMRFn0sJTye3EAP8icut8UDMjQPsaH5KbcOogefrFQ==", + "dev": true, + "requires": { + "@inquirer/core": "^11.2.1", + "@inquirer/type": "^4.0.7" + } + }, + "@inquirer/core": { + "version": "11.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-11.2.1.tgz", + "integrity": "sha512-Qd6GJT1yVyrZZCfN8W2qKF5ApmqryXRhRKCuip8h01x2w/esJQ2XIYc6f9abMIHgKQdBfFTSOdbHRLAhuM09UA==", + "dev": true, + "requires": { + "@inquirer/ansi": "^2.0.7", + "@inquirer/figures": "^2.0.7", + "@inquirer/type": "^4.0.7", + "cli-width": "^4.1.0", + "fast-wrap-ansi": "^0.2.0", + "mute-stream": "^3.0.0", + "signal-exit": "^4.1.0" + } + }, + "@inquirer/figures": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-2.0.7.tgz", + "integrity": "sha512-aJ8TBPOGB6f/2qziPfElISTCEd5XOYTFckA2SGjhNmiKzfK/u4ot3v0DUzGVdUnKjN10EqnnEPck36BkyfLnJw==", + "dev": true + }, + "@inquirer/type": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-4.0.7.tgz", + "integrity": "sha512-t28inv14nMQ1PhKpsJPY+kEs/c00qzeCOS2gTNRyTjG5d6qsVA2fItxW4hkvGZ5lvanGLdtCzVIx5dwdRpN1+g==", + "dev": true, + "requires": {} + }, "@jridgewell/gen-mapping": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", @@ -4895,6 +5532,50 @@ "integrity": "sha512-GMoTcF6WBpno7a7Iyx7M79os26d5bCDbh7YTZmXZM8YuLR3DDtwo0/CBYddStGD6QIBTieFDz4IAQiO0dAdRGw==", "dev": true }, + "@mswjs/interceptors": { + "version": "0.41.9", + "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.41.9.tgz", + "integrity": "sha512-VVPPgHyQ6ShqnrmDWuxjmUIsO9gWyOZFmuOfLd9LfBGQJwZfy0gvv9pbHSJuoFNIYC7ZDX9aoFwowjcdSC4E8w==", + "dev": true, + "requires": { + "@open-draft/deferred-promise": "^2.2.0", + "@open-draft/logger": "^0.3.0", + "@open-draft/until": "^2.0.0", + "is-node-process": "^1.2.0", + "outvariant": "^1.4.3", + "strict-event-emitter": "^0.5.1" + }, + "dependencies": { + "@open-draft/deferred-promise": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", + "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==", + "dev": true + } + } + }, + "@open-draft/deferred-promise": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-3.0.0.tgz", + "integrity": "sha512-XW375UK8/9SqUVNVa6M0yEy8+iTi4QN5VZ7aZuRFQmy76LRwI9wy5F4YIBU6T+eTe2/DNDo8tqu8RHlwLHM6RA==", + "dev": true + }, + "@open-draft/logger": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz", + "integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==", + "dev": true, + "requires": { + "is-node-process": "^1.2.0", + "outvariant": "^1.4.0" + } + }, + "@open-draft/until": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz", + "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==", + "dev": true + }, "@playwright/test": { "version": "1.57.0", "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.57.0.tgz", @@ -5004,8 +5685,22 @@ "version": "20.5.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.1.tgz", "integrity": "sha512-4tT2UrL5LBqDwoed9wZ6N3umC4Yhz3W3FloMmiiG4JwmUJWpie0c7lcnUNd4gtMKuDEO4wRVS8B6Xa0uMRsMKg==", + "dev": true + }, + "@types/set-cookie-parser": { + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/@types/set-cookie-parser/-/set-cookie-parser-2.4.10.tgz", + "integrity": "sha512-GGmQVGpQWUe5qglJozEjZV/5dyxbOOZ0LHe/lqyWssB88Y4svNfst0uqBVscdDeIKl5Jy5+aPSvy7mI9tYRguw==", "dev": true, - "peer": true + "requires": { + "@types/node": "*" + } + }, + "@types/statuses": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.6.tgz", + "integrity": "sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==", + "dev": true }, "@vue/reactivity": { "version": "3.1.5", @@ -5384,6 +6079,32 @@ "dev": true, "optional": true }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + }, + "dependencies": { + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + } + } + }, "anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -5682,6 +6403,23 @@ "dev": true, "peer": true }, + "cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true + }, + "cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + } + }, "clone": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", @@ -5762,6 +6500,12 @@ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", "dev": true }, + "cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "dev": true + }, "core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -6173,6 +6917,12 @@ "dev": true, "peer": true }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, "emojis-list": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", @@ -6333,8 +7083,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "peer": true + "dev": true }, "escodegen": { "version": "1.2.0", @@ -6515,6 +7264,21 @@ "integrity": "sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw==", "dev": true }, + "fast-string-truncated-width": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz", + "integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==", + "dev": true + }, + "fast-string-width": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz", + "integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==", + "dev": true, + "requires": { + "fast-string-truncated-width": "^3.0.2" + } + }, "fast-uri": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", @@ -6522,6 +7286,15 @@ "dev": true, "peer": true }, + "fast-wrap-ansi": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.2.tgz", + "integrity": "sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==", + "dev": true, + "requires": { + "fast-string-width": "^3.0.2" + } + }, "fastclick": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/fastclick/-/fastclick-1.0.6.tgz", @@ -6620,6 +7393,12 @@ "integrity": "sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ==", "dev": true }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, "get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -6676,6 +7455,12 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, + "graphql": { + "version": "16.14.2", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.14.2.tgz", + "integrity": "sha512-Chq1s4CY7jmh8gO2qvLIJyfCDIN+EHLFW/9iShnp1z8FjBQMoodWP1kDC36VAMXXIvAjj4ARa7ntfAV2BrjsbA==", + "dev": true + }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -6725,6 +7510,16 @@ "function-bind": "^1.1.2" } }, + "headers-polyfill": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-5.0.1.tgz", + "integrity": "sha512-1TJ6Fih/b8h5TIcv+1+Hw0PDQWJTKDKzFZzcKOiW1wJza3XoAQlkCuXLbymPYB8+ZQyw8mHvdw560e8zVFIWyA==", + "dev": true, + "requires": { + "@types/set-cookie-parser": "^2.4.10", + "set-cookie-parser": "^3.0.1" + } + }, "icheck-bootstrap": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/icheck-bootstrap/-/icheck-bootstrap-3.0.1.tgz", @@ -6823,6 +7618,12 @@ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, "is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -6832,6 +7633,12 @@ "is-extglob": "^2.1.1" } }, + "is-node-process": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz", + "integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==", + "dev": true + }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -7111,6 +7918,38 @@ "moment": "^2.29.4" } }, + "msw": { + "version": "2.14.6", + "resolved": "https://registry.npmjs.org/msw/-/msw-2.14.6.tgz", + "integrity": "sha512-ALe+N10S72cyx94cMcy3Zs4HhXCj35sgeAL4c+WTvKi0zWnbd8/h0lcFqv0mb2P+aSgAdD7p9HzvA0DiUPxsyg==", + "dev": true, + "requires": { + "@inquirer/confirm": "^6.0.11", + "@mswjs/interceptors": "^0.41.3", + "@open-draft/deferred-promise": "^3.0.0", + "@types/statuses": "^2.0.6", + "cookie": "^1.1.1", + "graphql": "^16.13.2", + "headers-polyfill": "^5.0.1", + "is-node-process": "^1.2.0", + "outvariant": "^1.4.3", + "path-to-regexp": "^6.3.0", + "picocolors": "^1.1.1", + "rettime": "^0.11.11", + "statuses": "^2.0.2", + "strict-event-emitter": "^0.5.1", + "tough-cookie": "^6.0.1", + "type-fest": "^5.5.0", + "until-async": "^3.0.2", + "yargs": "^17.7.2" + } + }, + "mute-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-3.0.0.tgz", + "integrity": "sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==", + "dev": true + }, "nanoid": { "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", @@ -7178,6 +8017,12 @@ "word-wrap": "~1.2.3" } }, + "outvariant": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz", + "integrity": "sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==", + "dev": true + }, "overlayscrollbars": { "version": "1.13.3", "resolved": "https://registry.npmjs.org/overlayscrollbars/-/overlayscrollbars-1.13.3.tgz", @@ -7204,6 +8049,12 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "path-to-regexp": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "dev": true + }, "pdfmake": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/pdfmake/-/pdfmake-0.2.7.tgz", @@ -7368,6 +8219,12 @@ "functions-have-names": "^1.2.3" } }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true + }, "require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -7386,6 +8243,12 @@ "supports-preserve-symlinks-flag": "^1.0.0" } }, + "rettime": { + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/rettime/-/rettime-0.11.11.tgz", + "integrity": "sha512-ILJRqVWBCTlg9r42fFgwVZx1gnFAcQF8mRoMkbgQfIrjEDf9nbBFDFx00oloOa+Q869FUtaYDXZvEfnecQSCoQ==", + "dev": true + }, "rollup": { "version": "3.29.5", "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.5.tgz", @@ -7489,6 +8352,12 @@ "randombytes": "^2.1.0" } }, + "set-cookie-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-3.1.0.tgz", + "integrity": "sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw==", + "dev": true + }, "setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", @@ -7510,6 +8379,12 @@ "integrity": "sha512-b6i4ZpVuUxB9h5gfCxPiusKYkqTMOjEbBs4wMaFbkfia4yFv92UKZ6Df8WXcKbn08JNL/abvg3FnMAOfakDvUw==", "dev": true }, + "signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -7639,6 +8514,18 @@ } } }, + "statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "dev": true + }, + "strict-event-emitter": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz", + "integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==", + "dev": true + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -7648,6 +8535,26 @@ "safe-buffer": "~5.1.0" } }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, "summernote": { "version": "0.8.20", "resolved": "https://registry.npmjs.org/summernote/-/summernote-0.8.20.tgz", @@ -7675,6 +8582,12 @@ "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.26.3.tgz", "integrity": "sha512-VU0hGw/WfI9h7Mh+SCsDlWgtxDwWZ6ccqS7QcO8zEeWnwplN1GptcLstq76OluUBSLUza6ldvKd3558OhjpJ9A==" }, + "tagged-tag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz", + "integrity": "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==", + "dev": true + }, "tapable": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", @@ -7806,6 +8719,21 @@ "resolved": "https://registry.npmjs.org/tinymce/-/tinymce-8.5.0.tgz", "integrity": "sha512-DnKEfPNQnOJc8Ca1roZBs/GSbkAZyIIbC4p8eHZyZQi85OSAXtiVNYMaRxo4mzsGKpa0sA4/Us4KXQkX8q7w2A==" }, + "tldts": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.2.tgz", + "integrity": "sha512-kCwffuaH8ntKtygnWe1b4BJKWiCUH30n5KfoTr6IchcXOwR7chAOFJxFrH3vjANafUYrIA4a7SDL+nn7SiR4Sw==", + "dev": true, + "requires": { + "tldts-core": "^7.4.2" + } + }, + "tldts-core": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.2.tgz", + "integrity": "sha512-nwEyF4vl4RSJjwSjBUmOSxc3BFPoIFdlRthJ6e+5v9P3bHNsoD06UjuqMUspqp7vsEZ1beaHi1km+optiE17yA==", + "dev": true + }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -7824,6 +8752,15 @@ "jquery": ">=1.12.0" } }, + "tough-cookie": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.1.tgz", + "integrity": "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==", + "dev": true, + "requires": { + "tldts": "^7.0.5" + } + }, "type": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", @@ -7839,6 +8776,15 @@ "prelude-ls": "~1.1.2" } }, + "type-fest": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.7.0.tgz", + "integrity": "sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg==", + "dev": true, + "requires": { + "tagged-tag": "^1.0.0" + } + }, "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -7879,6 +8825,12 @@ "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", "dev": true }, + "until-async": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/until-async/-/until-async-3.0.2.tgz", + "integrity": "sha512-IiSk4HlzAMqTUseHHe3VhIGyuFmN90zMTpD3Z3y8jeQbzLIq500MVM7Jq2vUAnTKAFPJrqwkzr6PoTcPhGcOiw==", + "dev": true + }, "update-browserslist-db": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", @@ -8036,6 +8988,17 @@ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, "xmldoc": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/xmldoc/-/xmldoc-1.3.0.tgz", @@ -8050,6 +9013,33 @@ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", "dev": true + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "requires": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + } + }, + "yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true } } } diff --git a/package.json b/package.json index f16546d8..b272645d 100644 --- a/package.json +++ b/package.json @@ -1,41 +1,47 @@ { - "private": true, - "scripts": { - "dev": "vite", - "build": "vite build", - "build-web": "vite --config vite.config-web.js build && node replace", - "test:e2e": "playwright test", - "test:e2e:headed": "playwright test --headed", - "test:e2e:ui": "playwright test --ui", - "test:e2e:debug": "playwright test --debug", - "test:e2e:report": "playwright show-report" - }, - "devDependencies": { - "@playwright/test": "^1.54.1", - "admin-lte": "3.2.0", - "axios": "^1.1.2", - "jquery": "^3.6", - "laravel-vite-plugin": "^0.7.2", - "lodash": "^4.17.19", - "popper.js": "^1.16.1", - "postcss": "^8.1.14", - "sass": "^1.56.1", - "sass-loader": "^8.0.0", - "vite": "^4.0.0" - }, - "dependencies": { - "@fortawesome/fontawesome-free": "^6.4.2", - "@rollup/plugin-inject": "^5.0.3", - "alpinejs": "^3.12.3", - "bootstrap": "^4.6.2", - "bootstrap-colorpicker": "^3.4.0", - "bootstrap-datepicker": "^1.10.0", - "bootstrap-icons": "^1.4.1", - "owl.carousel": "^2.3.4", - "playwright": "^1.60.0", - "select2": "^4.1.0-rc.0", - "select2-bootstrap4-theme": "^1.0.0", - "sweetalert2": "^11.7.27", - "tinymce": "^8.5.0" - } -} + "private": true, + "scripts": { + "dev": "vite", + "build": "vite build", + "build-web": "vite --config vite.config-web.js build && node replace", + "test:e2e": "playwright test", + "test:e2e:headed": "playwright test --headed", + "test:e2e:ui": "playwright test --ui", + "test:e2e:debug": "playwright test --debug", + "test:e2e:report": "playwright show-report" + }, + "devDependencies": { + "@playwright/test": "^1.54.1", + "admin-lte": "3.2.0", + "axios": "^1.1.2", + "jquery": "^3.6", + "laravel-vite-plugin": "^0.7.2", + "lodash": "^4.17.19", + "msw": "^2.14.6", + "popper.js": "^1.16.1", + "postcss": "^8.1.14", + "sass": "^1.56.1", + "sass-loader": "^8.0.0", + "vite": "^4.0.0" + }, + "dependencies": { + "@fortawesome/fontawesome-free": "^6.4.2", + "@rollup/plugin-inject": "^5.0.3", + "alpinejs": "^3.12.3", + "bootstrap": "^4.6.2", + "bootstrap-colorpicker": "^3.4.0", + "bootstrap-datepicker": "^1.10.0", + "bootstrap-icons": "^1.4.1", + "owl.carousel": "^2.3.4", + "playwright": "^1.60.0", + "select2": "^4.1.0-rc.0", + "select2-bootstrap4-theme": "^1.0.0", + "sweetalert2": "^11.7.27", + "tinymce": "^8.5.0" + }, + "msw": { + "workerDirectory": [ + "public" + ] + } +} \ No newline at end of file diff --git a/phpunit.xml b/phpunit.xml index 9829adfe..9776049d 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -29,7 +29,6 @@ - - + diff --git a/public/mockServiceWorker.js b/public/mockServiceWorker.js new file mode 100644 index 00000000..33dde9e7 --- /dev/null +++ b/public/mockServiceWorker.js @@ -0,0 +1,349 @@ +/* eslint-disable */ +/* tslint:disable */ + +/** + * Mock Service Worker. + * @see https://github.com/mswjs/msw + * - Please do NOT modify this file. + */ + +const PACKAGE_VERSION = '2.14.6' +const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82' +const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') +const activeClientIds = new Set() + +addEventListener('install', function () { + self.skipWaiting() +}) + +addEventListener('activate', function (event) { + event.waitUntil(self.clients.claim()) +}) + +addEventListener('message', async function (event) { + const clientId = Reflect.get(event.source || {}, 'id') + + if (!clientId || !self.clients) { + return + } + + const client = await self.clients.get(clientId) + + if (!client) { + return + } + + const allClients = await self.clients.matchAll({ + type: 'window', + }) + + switch (event.data) { + case 'KEEPALIVE_REQUEST': { + sendToClient(client, { + type: 'KEEPALIVE_RESPONSE', + }) + break + } + + case 'INTEGRITY_CHECK_REQUEST': { + sendToClient(client, { + type: 'INTEGRITY_CHECK_RESPONSE', + payload: { + packageVersion: PACKAGE_VERSION, + checksum: INTEGRITY_CHECKSUM, + }, + }) + break + } + + case 'MOCK_ACTIVATE': { + activeClientIds.add(clientId) + + sendToClient(client, { + type: 'MOCKING_ENABLED', + payload: { + client: { + id: client.id, + frameType: client.frameType, + }, + }, + }) + break + } + + case 'CLIENT_CLOSED': { + activeClientIds.delete(clientId) + + const remainingClients = allClients.filter((client) => { + return client.id !== clientId + }) + + // Unregister itself when there are no more clients + if (remainingClients.length === 0) { + self.registration.unregister() + } + + break + } + } +}) + +addEventListener('fetch', function (event) { + const requestInterceptedAt = Date.now() + + // Bypass navigation requests. + if (event.request.mode === 'navigate') { + return + } + + // Opening the DevTools triggers the "only-if-cached" request + // that cannot be handled by the worker. Bypass such requests. + if ( + event.request.cache === 'only-if-cached' && + event.request.mode !== 'same-origin' + ) { + return + } + + // Bypass all requests when there are no active clients. + // Prevents the self-unregistered worked from handling requests + // after it's been terminated (still remains active until the next reload). + if (activeClientIds.size === 0) { + return + } + + const requestId = crypto.randomUUID() + event.respondWith(handleRequest(event, requestId, requestInterceptedAt)) +}) + +/** + * @param {FetchEvent} event + * @param {string} requestId + * @param {number} requestInterceptedAt + */ +async function handleRequest(event, requestId, requestInterceptedAt) { + const client = await resolveMainClient(event) + const requestCloneForEvents = event.request.clone() + const response = await getResponse( + event, + client, + requestId, + requestInterceptedAt, + ) + + // Send back the response clone for the "response:*" life-cycle events. + // Ensure MSW is active and ready to handle the message, otherwise + // this message will pend indefinitely. + if (client && activeClientIds.has(client.id)) { + const serializedRequest = await serializeRequest(requestCloneForEvents) + + // Clone the response so both the client and the library could consume it. + const responseClone = response.clone() + + sendToClient( + client, + { + type: 'RESPONSE', + payload: { + isMockedResponse: IS_MOCKED_RESPONSE in response, + request: { + id: requestId, + ...serializedRequest, + }, + response: { + type: responseClone.type, + status: responseClone.status, + statusText: responseClone.statusText, + headers: Object.fromEntries(responseClone.headers.entries()), + body: responseClone.body, + }, + }, + }, + responseClone.body ? [serializedRequest.body, responseClone.body] : [], + ) + } + + return response +} + +/** + * Resolve the main client for the given event. + * Client that issues a request doesn't necessarily equal the client + * that registered the worker. It's with the latter the worker should + * communicate with during the response resolving phase. + * @param {FetchEvent} event + * @returns {Promise} + */ +async function resolveMainClient(event) { + const client = await self.clients.get(event.clientId) + + if (activeClientIds.has(event.clientId)) { + return client + } + + if (client?.frameType === 'top-level') { + return client + } + + const allClients = await self.clients.matchAll({ + type: 'window', + }) + + return allClients + .filter((client) => { + // Get only those clients that are currently visible. + return client.visibilityState === 'visible' + }) + .find((client) => { + // Find the client ID that's recorded in the + // set of clients that have registered the worker. + return activeClientIds.has(client.id) + }) +} + +/** + * @param {FetchEvent} event + * @param {Client | undefined} client + * @param {string} requestId + * @param {number} requestInterceptedAt + * @returns {Promise} + */ +async function getResponse(event, client, requestId, requestInterceptedAt) { + // Clone the request because it might've been already used + // (i.e. its body has been read and sent to the client). + const requestClone = event.request.clone() + + function passthrough() { + // Cast the request headers to a new Headers instance + // so the headers can be manipulated with. + const headers = new Headers(requestClone.headers) + + // Remove the "accept" header value that marked this request as passthrough. + // This prevents request alteration and also keeps it compliant with the + // user-defined CORS policies. + const acceptHeader = headers.get('accept') + if (acceptHeader) { + const values = acceptHeader.split(',').map((value) => value.trim()) + const filteredValues = values.filter( + (value) => value !== 'msw/passthrough', + ) + + if (filteredValues.length > 0) { + headers.set('accept', filteredValues.join(', ')) + } else { + headers.delete('accept') + } + } + + return fetch(requestClone, { headers }) + } + + // Bypass mocking when the client is not active. + if (!client) { + return passthrough() + } + + // Bypass initial page load requests (i.e. static assets). + // The absence of the immediate/parent client in the map of the active clients + // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet + // and is not ready to handle requests. + if (!activeClientIds.has(client.id)) { + return passthrough() + } + + // Notify the client that a request has been intercepted. + const serializedRequest = await serializeRequest(event.request) + const clientMessage = await sendToClient( + client, + { + type: 'REQUEST', + payload: { + id: requestId, + interceptedAt: requestInterceptedAt, + ...serializedRequest, + }, + }, + [serializedRequest.body], + ) + + switch (clientMessage.type) { + case 'MOCK_RESPONSE': { + return respondWithMock(clientMessage.data) + } + + case 'PASSTHROUGH': { + return passthrough() + } + } + + return passthrough() +} + +/** + * @param {Client} client + * @param {any} message + * @param {Array} transferrables + * @returns {Promise} + */ +function sendToClient(client, message, transferrables = []) { + return new Promise((resolve, reject) => { + const channel = new MessageChannel() + + channel.port1.onmessage = (event) => { + if (event.data && event.data.error) { + return reject(event.data.error) + } + + resolve(event.data) + } + + client.postMessage(message, [ + channel.port2, + ...transferrables.filter(Boolean), + ]) + }) +} + +/** + * @param {Response} response + * @returns {Response} + */ +function respondWithMock(response) { + // Setting response status code to 0 is a no-op. + // However, when responding with a "Response.error()", the produced Response + // instance will have status code set to 0. Since it's not possible to create + // a Response instance with status code 0, handle that use-case separately. + if (response.status === 0) { + return Response.error() + } + + const mockedResponse = new Response(response.body, response) + + Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, { + value: true, + enumerable: true, + }) + + return mockedResponse +} + +/** + * @param {Request} request + */ +async function serializeRequest(request) { + return { + url: request.url, + mode: request.mode, + method: request.method, + headers: Object.fromEntries(request.headers.entries()), + cache: request.cache, + credentials: request.credentials, + destination: request.destination, + integrity: request.integrity, + redirect: request.redirect, + referrer: request.referrer, + referrerPolicy: request.referrerPolicy, + body: await request.arrayBuffer(), + keepalive: request.keepalive, + } +} diff --git a/resources/views/components/wilayah_filter.blade.php b/resources/views/components/wilayah_filter.blade.php index f8641fe0..fa246e4d 100644 --- a/resources/views/components/wilayah_filter.blade.php +++ b/resources/views/components/wilayah_filter.blade.php @@ -4,19 +4,19 @@
-
-
- @@ -26,11 +26,11 @@ diff --git a/resources/views/dasbor/data-desa.blade.php b/resources/views/dasbor/data-desa.blade.php index 703d2ea7..7622d370 100644 --- a/resources/views/dasbor/data-desa.blade.php +++ b/resources/views/dasbor/data-desa.blade.php @@ -4,7 +4,7 @@
-
- -
+
diff --git a/routes/web.php b/routes/web.php index 1fc1ae03..8d0c95aa 100644 --- a/routes/web.php +++ b/routes/web.php @@ -469,8 +469,10 @@ Route::middleware('throttle:30,1')->get('/image-proxy', [ImageProxyController::class, 'proxy'])->name('image.proxy'); // Pest Browser Testing - Quick login route (hanya untuk testing) -Route::get('/_pest/login/{userId}', function ($userId) { - $user = App\Models\User::findOrFail($userId); - Auth::login($user); - return response(status: 200); -})->middleware('web'); +if (app()->environment('testing')) { + Route::get('/_pest/login/{userId}', function ($userId) { + $user = App\Models\User::findOrFail($userId); + Auth::login($user); + return response(status: 200); + })->middleware('web'); +} diff --git a/tests/Browser/MswSetup.php b/tests/Browser/MswSetup.php new file mode 100644 index 00000000..8f8de702 --- /dev/null +++ b/tests/Browser/MswSetup.php @@ -0,0 +1,201 @@ + 'kabupaten.json', + '/api/v1/data-website' => 'data-website.json', + '/api/v1/statistik-web/get-list-coordinate' => 'coordinates.json', + '/api/v1/wilayah/penduduk' => 'penduduk.json', + '/api/v1/dasbor' => 'dasbor.json', + ]; + + /** + * Regex route patterns for dynamic URLs. + * Keys are regex patterns, values are [fixture_dir_pattern, filename_regex]. + */ + private const REGEX_ROUTES = [ + '#/api/v1/statistik-web/get-list-kecamatan/([\d.]+)$#' => 'kecamatan-*.json', + '#/api/v1/statistik-web/get-list-desa/([\d.]+)$#' => 'desa-*.json', + '#/api/v1/statistik/penduduk\?.*filter\[id\]=([^&]+)#' => 'statistik-penduduk-*.json', + ]; + + /** + * Build the complete init script JS that registers MSW and sets up handlers. + */ + public static function getInitScriptJs(): string + { + $fixturesJson = self::buildFixturesJson(); + $blockedDomains = json_encode([ + 'fonts.googleapis.com', + 'fonts.gstatic.com', + ]); + $swPath = self::SW_PATH; + + return << $file) { + $path = $fixturesDir . '/' . $file; + if (file_exists($path)) { + $content = json_decode(file_get_contents($path), true); + $fixtures[$pattern] = $content; + } + } + + // Load and resolve regex route fixtures + $allFiles = glob($fixturesDir . '/*.json'); + foreach (self::REGEX_ROUTES as $pattern => $globPattern) { + $resolved = []; + // Convert glob pattern like "kecamatan-*.json" to regex + $prefix = strtok($globPattern, '*'); + $suffix = substr($globPattern, strlen($prefix) + 1); // After the * + $fileRegex = '/^' . preg_quote($prefix, '/') . '(.+)' . preg_quote($suffix, '/') . '$/'; + foreach ($allFiles as $file) { + $basename = basename($file); + if (preg_match($fileRegex, $basename, $m)) { + $key = $m[1]; + $resolved[$key] = json_decode(file_get_contents($file), true); + } + } + if (! empty($resolved)) { + $fixtures[$pattern] = $resolved; + } + } + + return json_encode($fixtures, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT); + } +} diff --git a/tests/Browser/SessionState.php b/tests/Browser/SessionState.php index 4a6011e9..2e0a0040 100644 --- a/tests/Browser/SessionState.php +++ b/tests/Browser/SessionState.php @@ -9,8 +9,6 @@ final class SessionState { private const STORAGE_PATH = __DIR__ . '/.session_state.json'; - private const MOCK_SERVER_PID_PATH = __DIR__ . '/.mock_server.pid'; - private const MOCK_SERVER_PORT = 8001; public static function saveForUser(User $user): void { @@ -106,6 +104,13 @@ public static function loginAdminUser(): User $existing = User::where('email', $email)->first(); if ($existing) { + // Ensure no forced password reset blocks navigation + if ($existing->requiresPasswordReset()) { + $existing->update([ + 'force_password_reset' => false, + 'password_expires_at' => null, + ]); + } self::saveForUser($existing); return $existing; @@ -116,6 +121,8 @@ public static function loginAdminUser(): User 'name' => 'Pest Smoke User', 'password' => 'password', 'username' => 'pestsmoke', + 'force_password_reset' => false, + 'password_expires_at' => null, ]); self::assignAdminRole($user); self::saveForUser($user); @@ -126,12 +133,17 @@ public static function loginAdminUser(): User /** * Login as user and navigate to path. */ - public static function loginAndNavigate(User $user, string $path) + public static function loginAndNavigate(User $user, string $path, array $options = []) { $page = visit("/_pest/login/{$user->id}"); self::saveForUser($user); - return $page->navigate($path); + // If login already redirected to target path, no need to navigate again + if ($page->url() === url($path)) { + return $page; + } + + return $page->navigate($path, $options); } /** @@ -157,158 +169,4 @@ public static function clearFilter($page): void '); $page->wait(1000); } - - /** - * Start the mock API server in the background. - * - * @throws \RuntimeException if the server fails to start - */ - public static function startMockServer(): void - { - if (self::isMockServerRunning()) { - return; - } - - self::killStaleProcess(); - - $mockServerScript = __DIR__ . '/mock-server.php'; - $logPath = __DIR__ . '/.mock_server.log'; - - $cmd = sprintf( - 'php -S %s:%d %s > %s 2>&1 & echo $!', - '127.0.0.1', - self::MOCK_SERVER_PORT, - escapeshellarg($mockServerScript), - escapeshellarg($logPath) - ); - - $pid = trim(shell_exec($cmd)); - - if (! $pid || ! is_numeric($pid)) { - throw new \RuntimeException( - "Failed to start mock server. Command: {$cmd}" - ); - } - - file_put_contents(self::MOCK_SERVER_PID_PATH, $pid); - - if (! self::waitForServer(self::MOCK_SERVER_PORT, 15)) { - $logContent = file_exists($logPath) ? file_get_contents($logPath) : '(no log)'; - throw new \RuntimeException( - "Mock server started (PID: {$pid}) but not responding on port " - . self::MOCK_SERVER_PORT . ". Log: {$logContent}" - ); - } - } - - /** - * Stop the mock API server. - */ - public static function stopMockServer(): void - { - if (file_exists(self::MOCK_SERVER_PID_PATH)) { - $pid = (int) file_get_contents(self::MOCK_SERVER_PID_PATH); - - if ($pid > 0 && posix_kill($pid, 0)) { - posix_kill($pid, SIGTERM); - usleep(200000); - } - - @unlink(self::MOCK_SERVER_PID_PATH); - } - - self::killStaleProcess(); - } - - /** - * Check if mock server is running and responsive. - */ - public static function isMockServerRunning(): bool - { - if (! file_exists(self::MOCK_SERVER_PID_PATH)) { - return false; - } - - $pid = (int) file_get_contents(self::MOCK_SERVER_PID_PATH); - - if ($pid <= 0) { - return false; - } - - $processAlive = posix_kill($pid, 0); - $fp = @fsockopen('127.0.0.1', self::MOCK_SERVER_PORT, $errno, $errstr, 1); - $portActive = $fp !== false; - if ($fp) { - fclose($fp); - } - - if ($processAlive !== $portActive) { - self::cleanupMockServer(); - - return false; - } - - return $processAlive; - } - - /** - * Ensure mock server is running, restart if needed. - * - * @throws \RuntimeException if the server fails to start - */ - public static function ensureMockServerRunning(): void - { - if (! self::isMockServerRunning()) { - self::cleanupMockServer(); - self::startMockServer(); - } - } - - /** - * Kill any process occupying the mock server port. - */ - private static function killStaleProcess(): void - { - $output = []; - exec("lsof -i :" . self::MOCK_SERVER_PORT . " -t 2>/dev/null", $output); - - foreach ($output as $pid) { - $pid = (int) trim($pid); - if ($pid > 0) { - posix_kill($pid, SIGTERM); - } - } - - if (! empty($output)) { - usleep(300000); - } - } - - /** - * Cleanup mock server state files without killing the process. - */ - private static function cleanupMockServer(): void - { - @unlink(self::MOCK_SERVER_PID_PATH); - } - - /** - * Wait for a server to become available on the given port. - */ - private static function waitForServer(int $port, int $timeoutSeconds): bool - { - $start = time(); - - while ((time() - $start) < $timeoutSeconds) { - $fp = @fsockopen('127.0.0.1', $port, $errno, $errstr, 1); - if ($fp) { - fclose($fp); - - return true; - } - usleep(100000); - } - - return false; - } } diff --git a/tests/Browser/SmokeDashboardDemografiTest.php b/tests/Browser/SmokeDashboardDemografiTest.php index 3cc729c1..35a3bc52 100644 --- a/tests/Browser/SmokeDashboardDemografiTest.php +++ b/tests/Browser/SmokeDashboardDemografiTest.php @@ -11,60 +11,38 @@ SessionState::clear(); }); -it('displays all dashboard elements', function () { - $chartKeys = [ - 'rentang-umur', - 'status-perkawinan', - 'pendidikan-dalam-kk', - 'golongan-darah', - 'penyakit-menahun', - 'agama', - 'jenis-kelamin', - 'suku', - 'penyandang-cacat', - ]; +$chartKeys = [ + 'rentang-umur', 'status-perkawinan', 'pendidikan-dalam-kk', + 'golongan-darah', 'penyakit-menahun', 'agama', + 'jenis-kelamin', 'suku', 'penyandang-cacat', +]; +it('displays all dashboard elements', function () use ($chartKeys) { $page = SessionState::loginAndNavigate($this->user, '/dasbor-demografi') - ->wait(3000) ->assertPathIs('/dasbor-demografi') ->assertSee('Dasbor Demografi') - ->assertVisible('@filter_kabupaten') - ->assertVisible('@filter_kecamatan') - ->assertVisible('@filter_desa') - ->assertVisible('@bt_filter'); + ->assertVisible('@filter-kabupaten') + ->assertVisible('@filter-kecamatan') + ->assertVisible('@filter-desa') + ->assertVisible('@bt-filter'); foreach ($chartKeys as $key) { - $page->assertSee($key); + $page->assertVisible("@chart-item-{$key}"); } - - $page->assertSee('Rentang Umur') - ->assertSee('Status Perkawinan') - ->assertSee('Pendidikan Dalam KK') - ->assertSee('Golongan Darah') - ->assertSee('Penyakit Menahun') - ->assertSee('Agama') - ->assertSee('Jenis Kelamin') - ->assertSee('Suku / Etnis') - ->assertSee('Penyandang Cacat'); - - ScreenshotHelper::saveIfEnabled($page, 'demografi-all'); }); -it('updates charts when filtering by kabupaten', function () { - $page = SessionState::loginAndNavigate($this->user, '/dasbor-demografi'); - - SessionState::applyFilter($page, '50.01'); - $page->assertSee('Rentang Umur'); - - ScreenshotHelper::saveIfEnabled($page, 'demografi-filtered'); -}); +it('applies filter and all charts remain visible', function () use ($chartKeys) { + $page = SessionState::loginAndNavigate($this->user, '/dasbor-demografi') + ->assertPathIs('/dasbor-demografi') + ->assertVisible('@filter-kabupaten') + ->assertVisible('@bt-filter'); -it('clear filter button works', function () { - $page = SessionState::loginAndNavigate($this->user, '/dasbor-demografi'); + $page->script("$('#filter_kabupaten').val('50.01').trigger('change')"); + $page->click('@bt-filter'); - SessionState::applyFilter($page, '50.01'); - SessionState::clearFilter($page); - $page->assertSee('Rentang Umur'); + foreach ($chartKeys as $key) { + $page->assertVisible("@chart-item-{$key}"); + } - ScreenshotHelper::saveIfEnabled($page, 'demografi-clear-filter'); + ScreenshotHelper::saveIfEnabled($page, 'demografi-filter'); }); diff --git a/tests/Browser/SmokeDashboardTest.php b/tests/Browser/SmokeDashboardTest.php index 2a0bee73..403bad6b 100644 --- a/tests/Browser/SmokeDashboardTest.php +++ b/tests/Browser/SmokeDashboardTest.php @@ -12,40 +12,34 @@ }); it('displays all dashboard elements', function () { - $page = SessionState::loginAndNavigate($this->user, '/dasbor') - ->wait(3000) + SessionState::loginAndNavigate($this->user, '/dasbor') ->assertPathIs('/dasbor') ->assertSee('Dasbor') - ->assertVisible('@filter_kabupaten') - ->assertVisible('@filter_kecamatan') - ->assertVisible('@filter_desa') - ->assertVisible('@bt_filter') - ->assertVisible('#summary_block') - ->assertSee('kecamatan') - ->assertSee('jumlah penduduk') - ->assertSee('jumlah keluarga') - ->assertVisible('#map') - ->assertVisible('#tabel_penduduk_block') - ->assertVisible('#summary-penduduk'); - - ScreenshotHelper::saveIfEnabled($page, 'dashboard-all'); + ->assertVisible('@filter-kabupaten') + ->assertVisible('@filter-kecamatan') + ->assertVisible('@filter-desa') + ->assertVisible('@bt-filter') + ->assertVisible('@summary-card-kecamatan') + ->assertVisible('@summary-card-desa') + ->assertVisible('@summary-card-penduduk') + ->assertVisible('@summary-card-keluarga') + ->assertVisible('@peta') + ->assertVisible('@tabel-penduduk-block') + ->assertVisible('@summary-penduduk'); }); -it('updates summary when filtering by kabupaten', function () { - $page = SessionState::loginAndNavigate($this->user, '/dasbor'); - - SessionState::applyFilter($page, '50.01'); - $page->assertVisible('#summary_block'); - - ScreenshotHelper::saveIfEnabled($page, 'dashboard-filtered'); -}); +it('applies filter and elements remain visible', function () { + $page = SessionState::loginAndNavigate($this->user, '/dasbor') + ->assertPathIs('/dasbor') + ->assertVisible('@filter-kabupaten') + ->assertVisible('@bt-filter'); -it('clear filter button works', function () { - $page = SessionState::loginAndNavigate($this->user, '/dasbor'); + $page->script("$('#filter_kabupaten').val('50.01').trigger('change')"); + $page->click('@bt-filter'); - SessionState::applyFilter($page, '50.01'); - SessionState::clearFilter($page); - $page->assertVisible('#summary_block'); + $page->assertVisible('@peta') + ->assertVisible('@tabel-penduduk-block') + ->assertVisible('@summary-penduduk'); - ScreenshotHelper::saveIfEnabled($page, 'dashboard-clear-filter'); + ScreenshotHelper::saveIfEnabled($page, 'dashboard-filter'); }); diff --git a/tests/Browser/SmokeLoginTest.php b/tests/Browser/SmokeLoginTest.php index 5bbf6b31..08caeb14 100644 --- a/tests/Browser/SmokeLoginTest.php +++ b/tests/Browser/SmokeLoginTest.php @@ -18,13 +18,13 @@ $email = 'pest-' . time() . '@login.test'; $user = User::factory()->create([ 'email' => $email, - 'password' => 'password123', + 'password' => 'paSsword@123Quat', ]); SessionState::assignAdminRole($user); $page = visit('/login') ->fill('@login-email', $email) - ->fill('@login-password', 'password123') + ->fill('@login-password', 'paSsword@123Quat') ->press('Masuk') ->assertPathIsNot('/login'); diff --git a/tests/Browser/apply-patch.sh b/tests/Browser/apply-patch.sh new file mode 100755 index 00000000..cdf38d3b --- /dev/null +++ b/tests/Browser/apply-patch.sh @@ -0,0 +1,111 @@ +#!/bin/bash +# +# Patch Pest Browser InitScript to include MSW (Mock Service Worker) setup. +# Run this after composer install or composer update. +# +# Usage: bash tests/Browser/apply-patch.sh +# + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_DIR="$(dirname "$(dirname "$SCRIPT_DIR")")" +VENDOR_INIT="$PROJECT_DIR/vendor/pestphp/pest-plugin-browser/src/Playwright/InitScript.php" + +if [ ! -f "$VENDOR_INIT" ]; then + echo "SKIP: Vendor InitScript.php not found at: $VENDOR_INIT" + exit 0 +fi + +# Check if already patched +if grep -q "MswSetup" "$VENDOR_INIT" 2>/dev/null; then + echo "OK: InitScript.php already patched with MSW setup" + exit 0 +fi + +# Backup original +cp "$VENDOR_INIT" "$VENDOR_INIT.bak" + +# Generate the MSW init script JS via PHP +MSW_JS=$(cd "$PROJECT_DIR" && php -r " +require 'vendor/autoload.php'; +use Tests\Browser\MswSetup; +echo MswSetup::getInitScriptJs(); +") + +if [ -z "$MSW_JS" ]; then + echo "ERROR: Failed to generate MSW init script" + exit 1 +fi + +# Create the patched InitScript.php +cat > "$VENDOR_INIT" << 'PATCHPHP' +> "$VENDOR_INIT" << 'ORIGMETHOD' + $initScriptJs = << String(arg)).join(' ') + }); + originalConsoleLog.apply(console, args); + }; + + window.addEventListener('error', (e) => { + window.__pestBrowser.jsErrors.push({ + message: e.message, + filename: e.filename, + lineno: e.lineno, + colno: e.colno + }); + }); + JS; + +ORIGMETHOD + +# Append the MSW setup call +cat >> "$VENDOR_INIT" << MSWCALL + \$mswSetupJs = \Tests\Browser\MswSetup::getInitScriptJs(); + + return \$initScriptJs . "\n" . \$mswSetupJs; + } +} +MSWCALL + +if [ $? -eq 0 ]; then + echo "OK: Patched InitScript.php with MSW (Mock Service Worker) support" +else + echo "ERROR: Failed to patch InitScript.php" + cp "$VENDOR_INIT.bak" "$VENDOR_INIT" + exit 1 +fi diff --git a/tests/Browser/mock-server.php b/tests/Browser/mock-server.php deleted file mode 100644 index 7eba2c68..00000000 --- a/tests/Browser/mock-server.php +++ /dev/null @@ -1,72 +0,0 @@ - 'Not Found', 'path' => $path]); - return true; -} - -$fixturePath = $fixturesDir . '/' . $fixture; - -if (!file_exists($fixturePath)) { - http_response_code(404); - echo json_encode(['error' => 'Fixture not found', 'fixture' => $fixture]); - return true; -} - -echo file_get_contents($fixturePath); -return true; diff --git a/tests/Pest.php b/tests/Pest.php index b1d6fd72..fb051f54 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -7,10 +7,6 @@ pest()->browser()->timeout(30000); -beforeAll(function () { - SessionState::startMockServer(); -}); - -afterAll(function () { - SessionState::stopMockServer(); +beforeEach(function () { + config(['adminlte.google_fonts.allowed' => false]); }); From 5518c13fcb0cc55f116c34d4d77e4efe2b0563dd Mon Sep 17 00:00:00 2001 From: Ahmad Afandi Date: Thu, 11 Jun 2026 14:27:27 +0700 Subject: [PATCH 30/43] perbaikan test , bandingkan dengan data fixture --- tests/Browser/FixtureReader.php | 99 +++++++++++++++++++ tests/Browser/MswSetup.php | 47 ++++++++- tests/Browser/SessionState.php | 4 + tests/Browser/SmokeDashboardDemografiTest.php | 45 +++++++-- tests/Browser/SmokeDashboardTest.php | 35 ++++++- 5 files changed, 214 insertions(+), 16 deletions(-) create mode 100644 tests/Browser/FixtureReader.php diff --git a/tests/Browser/FixtureReader.php b/tests/Browser/FixtureReader.php new file mode 100644 index 00000000..b6ddf1b6 --- /dev/null +++ b/tests/Browser/FixtureReader.php @@ -0,0 +1,99 @@ + $item['nama_kabupaten'], $data ?? []); + } + + public static function firstKabupatenKode(): ?string + { + $data = self::read('kabupaten.json'); + + return $data ? $data[0]['kode_kabupaten'] : null; + } + + public static function categoriesValues(): array + { + $data = self::read('data-website.json'); + $items = $data['data']['categoriesItems'] ?? []; + + return array_map(fn (array $item) => (string) $item['value'], $items); + } + + public static function demografiChartKeys(): array + { + $files = glob(self::FIXTURES_DIR . '/statistik-penduduk-*.json'); + $keys = []; + foreach ($files as $file) { + $basename = basename($file, '.json'); + $key = substr($basename, strlen('statistik-penduduk-')); + $keys[] = $key; + } + + return $keys; + } + + public static function demografiChartLabels(): array + { + $kategori = StatistikPendudukEnum::KATEGORI_STATISTIK; + $keys = self::demografiChartKeys(); + $labels = []; + foreach ($keys as $key) { + if (isset($kategori[$key])) { + $labels[$key] = $kategori[$key]; + } + } + + return $labels; + } + + public static function demografiChartData(string $key): ?array + { + return self::read("statistik-penduduk-{$key}.json"); + } + + public static function demografiFirstItemName(string $key): ?string + { + $data = self::demografiChartData($key); + $items = $data['data'] ?? []; + + foreach ($items as $item) { + $nama = $item['attributes']['nama'] ?? null; + if ($nama !== null && strtoupper($nama) !== 'TOTAL' && strtoupper($nama) !== 'JUMLAH') { + return $nama; + } + } + + return null; + } +} \ No newline at end of file diff --git a/tests/Browser/MswSetup.php b/tests/Browser/MswSetup.php index 8f8de702..e28faeeb 100644 --- a/tests/Browser/MswSetup.php +++ b/tests/Browser/MswSetup.php @@ -122,20 +122,26 @@ function matchFixture(url) { return origFetch.apply(this, arguments); }; - // --- XHR Interception (fallback while SW activates) --- var OrigXHR = window.XMLHttpRequest; function MockXHR() { var r = new OrigXHR(), self = this; self._r = r; self._f = null; self._blocked = false; - self.readyState = 0; self.status = 0; self.statusText = ''; + self._rs = 0; self.status = 0; self.statusText = ''; self.responseText = ''; self.response = ''; self.responseType = ''; self.onreadystatechange = null; self.onload = null; self.onerror = null; + self.ontimeout = null; self.onabort = null; + self.withCredentials = false; + self.timeout = 0; Object.defineProperty(self, 'readyState', {get:function(){return self._rs||0;},set:function(v){self._rs=v;}}); + var mockUpload = {addEventListener:function(){},removeEventListener:function(){},dispatchEvent:function(){return true;},onload:null,onerror:null,onabort:null,onprogress:null,ontimeout:null}; + Object.defineProperty(self, 'upload', {get:function(){return self._f||self._blocked?mockUpload:r.upload;}}); r.onreadystatechange = function(){self._rs=r.readyState;self.status=r.status;self.statusText=r.statusText;self.responseText=r.responseText;self.response=r.response;if(self.onreadystatechange)self.onreadystatechange();}; r.onload = function(){self._rs=4;self.status=r.status;self.responseText=r.responseText;self.response=r.response;if(self.onload)self.onload();}; r.onerror = function(){if(self.onerror)self.onerror();}; + r.ontimeout = function(){if(self.ontimeout)self.ontimeout();}; + r.onabort = function(){if(self.onabort)self.onabort();}; } - MockXHR.prototype.open = function(m, u) { + MockXHR.prototype.open = function(m, u, a, user, pass) { this._m = m; this._u = u; try { var h = new URL(u, location.origin).hostname; @@ -146,14 +152,45 @@ function MockXHR() { else { this._r.open.apply(this._r, arguments); } }; MockXHR.prototype.setRequestHeader = function(){if(!this._f && !this._blocked) this._r.setRequestHeader.apply(this._r, arguments);}; + MockXHR.prototype.overrideMimeType = function(){if(!this._f && !this._blocked) this._r.overrideMimeType.apply(this._r, arguments);}; MockXHR.prototype.send = function(b) { if (this._blocked) { var s=this; s._rs=4; s.status=204; setTimeout(function(){if(s.onreadystatechange)s.onreadystatechange();if(s.onload)s.onload();},0); return; } - if (this._f) { var s=this; s._rs=2;if(s.onreadystatechange)s.onreadystatechange();s._rs=3;if(s.onreadystatechange)s.onreadystatechange();s.status=200;s.statusText='OK';s.responseText=JSON.stringify(this._f);s.response=this._f;s._rs=4;setTimeout(function(){if(s.onreadystatechange)s.onreadystatechange();if(s.onload)s.onload();},0); } + if (this._f) { + var s=this; + setTimeout(function(){ + s._rs=2;if(s.onreadystatechange)s.onreadystatechange(); + s._rs=3;if(s.onreadystatechange)s.onreadystatechange(); + s.status=200;s.statusText='OK'; + s.responseText=JSON.stringify(s._f); + s.response=s.responseText; + s._rs=4; + if(s.onreadystatechange)s.onreadystatechange(); + if(s.onload)s.onload(); + },0); + } else { this._r.send.apply(this._r, arguments); } }; - MockXHR.prototype.abort = function(){if(!this._f && !this._blocked) this._r.abort();}; + MockXHR.prototype.abort = function(){ + this._aborted=true; + if(!this._f && !this._blocked) this._r.abort(); + else { this._rs=0; this.status=0; if(this.onabort)this.onabort(); } + }; MockXHR.prototype.getResponseHeader = function(n){if(this._f)return n.toLowerCase()==='content-type'?'application/json':null;if(this._blocked)return null;return this._r.getResponseHeader(n);}; MockXHR.prototype.getAllResponseHeaders = function(){if(this._f)return 'content-type: application/json';if(this._blocked)return '';return this._r.getAllResponseHeaders();}; + MockXHR.prototype.addEventListener = function(type, fn) { + var prop = 'on' + type; + if (typeof this[prop] === 'function' && this[prop] !== null) { + var prev = this[prop]; + this[prop] = function() { prev.apply(this, arguments); fn.apply(this, arguments); }; + } else { + this[prop] = fn; + } + }; + MockXHR.prototype.removeEventListener = function(type, fn) { + var prop = 'on' + type; + if (this[prop] === fn) this[prop] = null; + }; + MockXHR.prototype.dispatchEvent = function(){return true;}; window.XMLHttpRequest = MockXHR; })(); JS; diff --git a/tests/Browser/SessionState.php b/tests/Browser/SessionState.php index 2e0a0040..81f424ce 100644 --- a/tests/Browser/SessionState.php +++ b/tests/Browser/SessionState.php @@ -125,6 +125,10 @@ public static function loginAdminUser(): User 'password_expires_at' => null, ]); self::assignAdminRole($user); + + // Assign to team with menu data (team ID 1 = administrator) + $user->teams()->attach(1); + self::saveForUser($user); return $user; diff --git a/tests/Browser/SmokeDashboardDemografiTest.php b/tests/Browser/SmokeDashboardDemografiTest.php index 35a3bc52..6f1e881c 100644 --- a/tests/Browser/SmokeDashboardDemografiTest.php +++ b/tests/Browser/SmokeDashboardDemografiTest.php @@ -1,5 +1,6 @@ user, '/dasbor-demografi') @@ -31,13 +31,42 @@ } }); -it('applies filter and all charts remain visible', function () use ($chartKeys) { +it('loads kabupaten dropdown options from ajax', function () use ($kabupatenNames) { + $page = SessionState::loginAndNavigate($this->user, '/dasbor-demografi') + ->assertPathIs('/dasbor-demografi'); + + $page->assertScript("document.querySelectorAll('#filter_kabupaten option').length > 1", true); + + foreach ($kabupatenNames as $name) { + $page->assertSourceInHas('#filter_kabupaten', $name); + } +}); + +it('displays correct chart titles from fixture', function () use ($chartLabels) { + $page = SessionState::loginAndNavigate($this->user, '/dasbor-demografi') + ->assertPathIs('/dasbor-demografi'); + + foreach ($chartLabels as $key => $label) { + $page->assertSeeIn("@chart-title-{$key}", "Komposisi {$label}"); + } +}); + +it('renders chart content from fixture data', function () use ($chartKeys) { + $page = SessionState::loginAndNavigate($this->user, '/dasbor-demografi') + ->assertPathIs('/dasbor-demografi'); + + foreach ($chartKeys as $key) { + $page->assertSourceInHas("@chart-content-{$key}", "donutChart-{$key}"); + } +}); + +it('applies filter and all charts remain visible', function () use ($chartKeys, $firstKabupatenKode) { $page = SessionState::loginAndNavigate($this->user, '/dasbor-demografi') ->assertPathIs('/dasbor-demografi') ->assertVisible('@filter-kabupaten') ->assertVisible('@bt-filter'); - $page->script("$('#filter_kabupaten').val('50.01').trigger('change')"); + $page->script("$('#filter_kabupaten').val('{$firstKabupatenKode}').trigger('change')"); $page->click('@bt-filter'); foreach ($chartKeys as $key) { @@ -45,4 +74,4 @@ } ScreenshotHelper::saveIfEnabled($page, 'demografi-filter'); -}); +}); \ No newline at end of file diff --git a/tests/Browser/SmokeDashboardTest.php b/tests/Browser/SmokeDashboardTest.php index 403bad6b..49038805 100644 --- a/tests/Browser/SmokeDashboardTest.php +++ b/tests/Browser/SmokeDashboardTest.php @@ -1,5 +1,6 @@ user, '/dasbor') ->assertPathIs('/dasbor') @@ -28,13 +33,37 @@ ->assertVisible('@summary-penduduk'); }); -it('applies filter and elements remain visible', function () { +it('loads kabupaten dropdown options from ajax', function () use ($kabupatenNames) { + $page = SessionState::loginAndNavigate($this->user, '/dasbor') + ->assertPathIs('/dasbor'); + + $page->assertScript("document.querySelectorAll('#filter_kabupaten option').length > 1", true); + + foreach ($kabupatenNames as $name) { + $page->assertSourceInHas('#filter_kabupaten', $name); + } +}); + +it('displays correct card values from fixture', function () use ($categoriesValues) { + $page = SessionState::loginAndNavigate($this->user, '/dasbor') + ->assertPathIs('/dasbor'); + + foreach ($categoriesValues as $key => $value) { + $page->assertVisible("@summary-value-{$key}"); + } + + foreach ($categoriesValues as $key => $value) { + $page->assertSeeIn("@summary-value-{$key}", $value); + } +}); + +it('applies filter and elements remain visible', function () use ($firstKabupatenKode) { $page = SessionState::loginAndNavigate($this->user, '/dasbor') ->assertPathIs('/dasbor') ->assertVisible('@filter-kabupaten') ->assertVisible('@bt-filter'); - $page->script("$('#filter_kabupaten').val('50.01').trigger('change')"); + $page->script("$('#filter_kabupaten').val('{$firstKabupatenKode}').trigger('change')"); $page->click('@bt-filter'); $page->assertVisible('@peta') @@ -42,4 +71,4 @@ ->assertVisible('@summary-penduduk'); ScreenshotHelper::saveIfEnabled($page, 'dashboard-filter'); -}); +}); \ No newline at end of file From 555effd0c8f7435fd81930fedd3c13315144501f Mon Sep 17 00:00:00 2001 From: Ahmad Afandi Date: Fri, 12 Jun 2026 07:59:33 +0700 Subject: [PATCH 31/43] perbaikan test yang gagal --- tests/Browser/MswSetup.php | 11 +++-- tests/Browser/SmokeDashboardDemografiTest.php | 44 ++++++++++++++++++- tests/Browser/SmokeDashboardTest.php | 17 ++++++- 3 files changed, 64 insertions(+), 8 deletions(-) diff --git a/tests/Browser/MswSetup.php b/tests/Browser/MswSetup.php index e28faeeb..010f29d0 100644 --- a/tests/Browser/MswSetup.php +++ b/tests/Browser/MswSetup.php @@ -38,7 +38,7 @@ final class MswSetup private const REGEX_ROUTES = [ '#/api/v1/statistik-web/get-list-kecamatan/([\d.]+)$#' => 'kecamatan-*.json', '#/api/v1/statistik-web/get-list-desa/([\d.]+)$#' => 'desa-*.json', - '#/api/v1/statistik/penduduk\?.*filter\[id\]=([^&]+)#' => 'statistik-penduduk-*.json', + '#/api/v1/statistik/penduduk\?.*filter(?:\[id\]|%5Bid%5D)=([^&]+)#' => 'statistik-penduduk-*.json', ]; /** @@ -73,8 +73,8 @@ function matchFixture(url) { var fullUrl = path + (search || ''); for (var pattern in FIXTURES) { - if (pattern.charAt(0) === '#') { - var regex = new RegExp(pattern); + if (pattern.charAt(0) === '~') { + var regex = new RegExp(pattern.substring(1)); var m = fullUrl.match(regex); if (m && m[1]) { var resolved = FIXTURES[pattern]; @@ -229,7 +229,10 @@ private static function buildFixturesJson(): string } } if (! empty($resolved)) { - $fixtures[$pattern] = $resolved; + // Strip PHP regex delimiters (#...#) and prefix with ~ for JS detection + $stripped = substr($pattern, 1, -1); + $jsPattern = '~' . $stripped; + $fixtures[$jsPattern] = $resolved; } } diff --git a/tests/Browser/SmokeDashboardDemografiTest.php b/tests/Browser/SmokeDashboardDemografiTest.php index 6f1e881c..98997c39 100644 --- a/tests/Browser/SmokeDashboardDemografiTest.php +++ b/tests/Browser/SmokeDashboardDemografiTest.php @@ -35,10 +35,23 @@ $page = SessionState::loginAndNavigate($this->user, '/dasbor-demografi') ->assertPathIs('/dasbor-demografi'); - $page->assertScript("document.querySelectorAll('#filter_kabupaten option').length > 1", true); + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const count = document.querySelectorAll('#filter_kabupaten option').length; + if (count > 1) { resolve(true); } else { setTimeout(check, 200); } + }; + check(); + })", + true + ); foreach ($kabupatenNames as $name) { - $page->assertSourceInHas('#filter_kabupaten', $name); + $escaped = addslashes($name); + $page->assertScript( + "Array.from(document.querySelectorAll('#filter_kabupaten option')).some(o => o.textContent.trim() === '{$escaped}')", + true + ); } }); @@ -55,6 +68,33 @@ $page = SessionState::loginAndNavigate($this->user, '/dasbor-demografi') ->assertPathIs('/dasbor-demografi'); + $keysJson = json_encode($chartKeys); + $page->assertScript( + "new Promise((resolve) => { + const keys = {$keysJson}; + const elFor = key => document.querySelector('[data-testid=\"chart-content-' + key + '\"]'); + const hasCanvas = key => { const e = elFor(key); return e && e.querySelector('canvas'); }; + const hasLoading = key => { const e = elFor(key); return e && e.textContent.includes('Sedang memuat'); }; + + const phase1 = () => { + if (keys.some(hasLoading)) { setTimeout(phase2, 100); } + else if (keys.every(hasCanvas)) { + setTimeout(() => { + if (keys.every(k => hasCanvas(k) && !hasLoading(k))) { resolve(true); } + else { setTimeout(phase2, 200); } + }, 2000); + } + else { setTimeout(phase1, 50); } + }; + const phase2 = () => { + if (keys.every(k => hasCanvas(k) && !hasLoading(k))) { resolve(true); } + else { setTimeout(phase2, 200); } + }; + phase1(); + })", + true + ); + foreach ($chartKeys as $key) { $page->assertSourceInHas("@chart-content-{$key}", "donutChart-{$key}"); } diff --git a/tests/Browser/SmokeDashboardTest.php b/tests/Browser/SmokeDashboardTest.php index 49038805..c05b04c0 100644 --- a/tests/Browser/SmokeDashboardTest.php +++ b/tests/Browser/SmokeDashboardTest.php @@ -37,10 +37,23 @@ $page = SessionState::loginAndNavigate($this->user, '/dasbor') ->assertPathIs('/dasbor'); - $page->assertScript("document.querySelectorAll('#filter_kabupaten option').length > 1", true); + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const count = document.querySelectorAll('#filter_kabupaten option').length; + if (count > 1) { resolve(true); } else { setTimeout(check, 200); } + }; + check(); + })", + true + ); foreach ($kabupatenNames as $name) { - $page->assertSourceInHas('#filter_kabupaten', $name); + $escaped = addslashes($name); + $page->assertScript( + "Array.from(document.querySelectorAll('#filter_kabupaten option')).some(o => o.textContent.trim() === '{$escaped}')", + true + ); } }); From e79a2e0863cdc0597910bd32b9db96fc06723ba1 Mon Sep 17 00:00:00 2001 From: Ahmad Afandi Date: Fri, 12 Jun 2026 14:43:17 +0700 Subject: [PATCH 32/43] simpan dulu --- app/Http/Controllers/Auth/LoginController.php | 15 ++- app/Listeners/LoginListener.php | 60 +++++----- tests/Browser/MswSetup.php | 8 ++ tests/Browser/ScreenshotHelper.php | 2 +- tests/Browser/SmokeKelembagaanTest.php | 64 +++++++++++ tests/Browser/SmokeKesehatanTest.php | 88 +++++++++++++++ tests/Browser/SmokeKetenagakerjaanTest.php | 106 ++++++++++++++++++ tests/Browser/SmokeLoginTest.php | 53 ++++++--- tests/Browser/SmokePendidikanTest.php | 93 +++++++++++++++ tests/Browser/SmokePendudukTest.php | 96 ++++++++++++++++ tests/Browser/SmokePenerimaBantuanTest.php | 85 ++++++++++++++ tests/Browser/fixtures/bantuan-sasaran.json | 5 + tests/Browser/fixtures/bantuan-tahun.json | 5 + tests/Browser/fixtures/bantuan.json | 24 ++++ tests/Browser/fixtures/kesehatan.json | 25 +++++ tests/Browser/fixtures/ketenagakerjaan.json | 22 ++++ tests/Browser/fixtures/lembaga.json | 22 ++++ tests/Browser/fixtures/pendidikan.json | 24 ++++ tests/Browser/fixtures/penduduk-data.json | 33 ++++++ tests/Pest.php | 3 +- 20 files changed, 777 insertions(+), 56 deletions(-) create mode 100644 tests/Browser/SmokeKelembagaanTest.php create mode 100644 tests/Browser/SmokeKesehatanTest.php create mode 100644 tests/Browser/SmokeKetenagakerjaanTest.php create mode 100644 tests/Browser/SmokePendidikanTest.php create mode 100644 tests/Browser/SmokePendudukTest.php create mode 100644 tests/Browser/SmokePenerimaBantuanTest.php create mode 100644 tests/Browser/fixtures/bantuan-sasaran.json create mode 100644 tests/Browser/fixtures/bantuan-tahun.json create mode 100644 tests/Browser/fixtures/bantuan.json create mode 100644 tests/Browser/fixtures/kesehatan.json create mode 100644 tests/Browser/fixtures/ketenagakerjaan.json create mode 100644 tests/Browser/fixtures/lembaga.json create mode 100644 tests/Browser/fixtures/pendidikan.json create mode 100644 tests/Browser/fixtures/penduduk-data.json diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php index 6a9042c9..2c497d3e 100644 --- a/app/Http/Controllers/Auth/LoginController.php +++ b/app/Http/Controllers/Auth/LoginController.php @@ -147,14 +147,17 @@ protected function attemptLogin(Request $request) if ($successLogin) { try { + $passwordRule = Password::min(8) + ->letters() + ->mixedCase() + ->numbers() + ->symbols(); + + $passwordRule->uncompromised(); + $request->validate(['password' => [ 'required', - Password::min(8) - ->letters() - ->mixedCase() - ->numbers() - ->symbols() - ->uncompromised(), + $passwordRule, ]]); session(['weak_password' => false]); } catch (ValidationException $th) { diff --git a/app/Listeners/LoginListener.php b/app/Listeners/LoginListener.php index ad3aa5d8..d9e77f1f 100644 --- a/app/Listeners/LoginListener.php +++ b/app/Listeners/LoginListener.php @@ -33,40 +33,42 @@ public function __construct(Request $request) public function handle(Login $event) { $presisiStatus = false; - try { - $url = config('app.databaseGabunganUrl').'/api/v1/setting-modul'; - $setting = Setting::where('key', 'database_gabungan_api_key')->first(); - $settingModul = Http::withHeaders([ - 'Content-Type' => 'application/json', - 'Accept' => 'application/json', - 'Authorization' => 'Bearer '.$setting->value ?? '', - ])->get($url, [ - 'filter[slug]' => 'data-presisi', - 'page[size]' => 1, - ])->throw() - ->json(); + $prodeskelStatus = false; - $prodeskel = Http::withHeaders([ - 'Content-Type' => 'application/json', - 'Accept' => 'application/json', - 'Authorization' => 'Bearer '.$setting->value ?? '', - ])->get($url, [ - 'filter[slug]' => 'prodeskel', - 'page[size]' => 1, - ])->throw() - ->json(); + if (!app()->environment('testing')) { + try { + $url = config('app.databaseGabunganUrl').'/api/v1/setting-modul'; + $setting = Setting::where('key', 'database_gabungan_api_key')->first(); + $settingModul = Http::withHeaders([ + 'Content-Type' => 'application/json', + 'Accept' => 'application/json', + 'Authorization' => 'Bearer '.$setting->value ?? '', + ])->get($url, [ + 'filter[slug]' => 'data-presisi', + 'page[size]' => 1, + ])->throw() + ->json(); - // Assuming the response contains a 'data' key with the status - $presisiStatus = count($settingModul['data']) > 0 ? true : false; - $prodeskelStatus = count($prodeskel['data']) > 0 ? true : false; + $prodeskel = Http::withHeaders([ + 'Content-Type' => 'application/json', + 'Accept' => 'application/json', + 'Authorization' => 'Bearer '.$setting->value ?? '', + ])->get($url, [ + 'filter[slug]' => 'prodeskel', + 'page[size]' => 1, + ])->throw() + ->json(); - session(['presisi_enabled' => $presisiStatus, 'prodeskel_enabled' => $prodeskelStatus]); + $presisiStatus = count($settingModul['data']) > 0 ? true : false; + $prodeskelStatus = count($prodeskel['data']) > 0 ? true : false; - activity('authentication-log')->event('login')->withProperties($this->request)->log('Login'); - } catch (Exception $e) { - Log::error('Error fetching setting-modul: '.$e->getMessage()); + session(['presisi_enabled' => $presisiStatus, 'prodeskel_enabled' => $prodeskelStatus]); + } catch (Exception $e) { + Log::error('Error fetching setting-modul: '.$e->getMessage()); + } } - session(['presisi_enabled' => $presisiStatus, 'kabupaten.kode_kabupaten' => auth()->user()->kode_kabupaten ?? null]); + + session(['presisi_enabled' => $presisiStatus, 'prodeskel_enabled' => $prodeskelStatus, 'kabupaten.kode_kabupaten' => auth()->user()->kode_kabupaten ?? null]); activity('authentication-log')->event('login')->withProperties($this->request)->log('Login'); } diff --git a/tests/Browser/MswSetup.php b/tests/Browser/MswSetup.php index 010f29d0..b866c7b3 100644 --- a/tests/Browser/MswSetup.php +++ b/tests/Browser/MswSetup.php @@ -29,6 +29,14 @@ final class MswSetup '/api/v1/statistik-web/get-list-coordinate' => 'coordinates.json', '/api/v1/wilayah/penduduk' => 'penduduk.json', '/api/v1/dasbor' => 'dasbor.json', + '/api/v1/penduduk' => 'penduduk-data.json', + '/api/v1/data/kesehatan' => 'kesehatan.json', + '/api/v1/pendidikan' => 'pendidikan.json', + '/api/v1/ketenagakerjaan' => 'ketenagakerjaan.json', + '/api/v1/bantuan' => 'bantuan.json', + '/api/v1/bantuan/sasaran' => 'bantuan-sasaran.json', + '/api/v1/bantuan/tahun' => 'bantuan-tahun.json', + '/api/v1/lembaga' => 'lembaga.json', ]; /** diff --git a/tests/Browser/ScreenshotHelper.php b/tests/Browser/ScreenshotHelper.php index 8d91cecc..8b080ea5 100644 --- a/tests/Browser/ScreenshotHelper.php +++ b/tests/Browser/ScreenshotHelper.php @@ -16,7 +16,7 @@ public static function enabled(): bool ); } - public static function saveIfEnabled(\Pest\Browser\Api\AwaitableWebpage $page, string $name): void + public static function saveIfEnabled(\Pest\Browser\Api\PendingAwaitablePage|\Pest\Browser\Api\AwaitableWebpage $page, string $name): void { if (! self::enabled()) { return; diff --git a/tests/Browser/SmokeKelembagaanTest.php b/tests/Browser/SmokeKelembagaanTest.php new file mode 100644 index 00000000..da8ed315 --- /dev/null +++ b/tests/Browser/SmokeKelembagaanTest.php @@ -0,0 +1,64 @@ +user = SessionState::loginAdminUser(); +}); + +afterEach(function () { + SessionState::clear(); +}); + +it('opens the lembaga page', function () { + SessionState::loginAndNavigate($this->user, '/lembaga') + ->assertPathIs('/lembaga') + ->assertSee('Lembaga'); +}); + +it('displays filter button', function () { + SessionState::loginAndNavigate($this->user, '/lembaga') + ->assertVisible('[href="#collapse-filter"]'); +}); + +it('displays cetak button', function () { + SessionState::loginAndNavigate($this->user, '/lembaga') + ->assertVisible('#print-btn-table-lembaga'); +}); + +it('displays excel button', function () { + SessionState::loginAndNavigate($this->user, '/lembaga') + ->assertVisible('#download-excel'); +}); + +it('displays datatable', function () { + SessionState::loginAndNavigate($this->user, '/lembaga') + ->assertVisible('#table-lembaga'); +}); + +it('displays at least one data row', function () { + $page = SessionState::loginAndNavigate($this->user, '/lembaga') + ->assertPathIs('/lembaga'); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const rows = document.querySelectorAll('#table-lembaga tbody tr'); + if (rows.length > 0 && !rows[0].classList.contains('dataTables_empty')) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })", + true + ); +}); + +it('has no javascript errors', function () { + SessionState::loginAndNavigate($this->user, '/lembaga') + ->assertPathIs('/lembaga') + ->assertNoJavaScriptErrors(); +}); diff --git a/tests/Browser/SmokeKesehatanTest.php b/tests/Browser/SmokeKesehatanTest.php new file mode 100644 index 00000000..c503b8b1 --- /dev/null +++ b/tests/Browser/SmokeKesehatanTest.php @@ -0,0 +1,88 @@ +user = SessionState::loginAdminUser(); +}); + +afterEach(function () { + SessionState::clear(); +}); + +it('opens the kesehatan page', function () { + SessionState::loginAndNavigate($this->user, '/data-pokok/kesehatan') + ->assertPathIs('/data-pokok/kesehatan'); +}); + +it('displays statistik golongan darah', function () { + SessionState::loginAndNavigate($this->user, '/data-pokok/kesehatan') + ->assertSee('Statistik Golongan Darah') + ->assertVisible('#donutChart'); +}); + +it('displays statistik status gizi balita', function () { + SessionState::loginAndNavigate($this->user, '/data-pokok/kesehatan') + ->assertSee('Statistik Status Gizi Balita') + ->assertVisible('#barChart'); +}); + +it('renders all charts successfully', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/kesehatan') + ->assertPathIs('/data-pokok/kesehatan'); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const donut = document.querySelector('#donutChart'); + const bar = document.querySelector('#barChart'); + const donutReady = donut && donut.getContext && donut.width > 0; + const barReady = bar && bar.getContext && bar.width > 0; + if (donutReady && barReady) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })", + true + ); +}); + +it('displays cetak button', function () { + SessionState::loginAndNavigate($this->user, '/data-pokok/kesehatan') + ->assertVisible('#print-btn-kesehatan'); +}); + +it('displays excel button', function () { + SessionState::loginAndNavigate($this->user, '/data-pokok/kesehatan') + ->assertVisible('#download-excel'); +}); + +it('displays datatable with data', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/kesehatan') + ->assertPathIs('/data-pokok/kesehatan'); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const rows = document.querySelectorAll('#kesehatan tbody tr'); + if (rows.length > 0 && !rows[0].classList.contains('dataTables_empty')) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })", + true + ); +}); + +it('has no javascript errors', function () { + SessionState::loginAndNavigate($this->user, '/data-pokok/kesehatan') + ->assertPathIs('/data-pokok/kesehatan') + ->assertNoJavaScriptErrors(); +}); diff --git a/tests/Browser/SmokeKetenagakerjaanTest.php b/tests/Browser/SmokeKetenagakerjaanTest.php new file mode 100644 index 00000000..ad82742e --- /dev/null +++ b/tests/Browser/SmokeKetenagakerjaanTest.php @@ -0,0 +1,106 @@ +user = SessionState::loginAdminUser(); +}); + +afterEach(function () { + SessionState::clear(); +}); + +it('opens the ketenagakerjaan page', function () { + SessionState::loginAndNavigate($this->user, '/data-pokok/ketenagakerjaan') + ->assertPathIs('/data-pokok/ketenagakerjaan') + ->assertSee('Statistik Jumlah Penghasilan') + ->assertSee('Statistik Pelatihan'); +}); + +it('displays statistik jumlah penghasilan', function () { + SessionState::loginAndNavigate($this->user, '/data-pokok/ketenagakerjaan') + ->assertSee('Statistik Jumlah Penghasilan') + ->assertVisible('#barChart'); +}); + +it('displays statistik pelatihan', function () { + SessionState::loginAndNavigate($this->user, '/data-pokok/ketenagakerjaan') + ->assertSee('Statistik Pelatihan') + ->assertVisible('#donutChart'); +}); + +it('renders all charts successfully', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/ketenagakerjaan') + ->assertPathIs('/data-pokok/ketenagakerjaan'); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const donut = document.querySelector('#donutChart'); + const bar = document.querySelector('#barChart'); + const donutReady = donut && donut.getContext && donut.width > 0; + const barReady = bar && bar.getContext && bar.width > 0; + if (donutReady && barReady) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })", + true + ); +}); + +it('displays cetak button', function () { + SessionState::loginAndNavigate($this->user, '/data-pokok/ketenagakerjaan') + ->assertVisible('#print-btn-ketenagakerjaan'); +}); + +it('displays excel button', function () { + SessionState::loginAndNavigate($this->user, '/data-pokok/ketenagakerjaan') + ->assertVisible('#download-excel'); +}); + +it('displays datatable', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/ketenagakerjaan') + ->assertPathIs('/data-pokok/ketenagakerjaan'); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const table = document.querySelector('#ketenagakerjaan'); + resolve(!!table); + }; + check(); + })", + true + ); +}); + +it('displays at least one data row', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/ketenagakerjaan') + ->assertPathIs('/data-pokok/ketenagakerjaan'); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const rows = document.querySelectorAll('#ketenagakerjaan tbody tr'); + if (rows.length > 0 && !rows[0].classList.contains('dataTables_empty')) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })", + true + ); +}); + +it('has no javascript errors', function () { + SessionState::loginAndNavigate($this->user, '/data-pokok/ketenagakerjaan') + ->assertPathIs('/data-pokok/ketenagakerjaan') + ->assertNoJavaScriptErrors(); +}); diff --git a/tests/Browser/SmokeLoginTest.php b/tests/Browser/SmokeLoginTest.php index 08caeb14..68f0bb8f 100644 --- a/tests/Browser/SmokeLoginTest.php +++ b/tests/Browser/SmokeLoginTest.php @@ -1,9 +1,17 @@ 'captcha_enabled'], + ['value' => 'false'] + ); +}); + it('displays login page correctly', function () { $page = visit('/login') ->assertSee('Masuk') @@ -15,29 +23,38 @@ }); it('can login with valid credentials', function () { - $email = 'pest-' . time() . '@login.test'; - $user = User::factory()->create([ - 'email' => $email, - 'password' => 'paSsword@123Quat', - ]); - SessionState::assignAdminRole($user); - - $page = visit('/login') - ->fill('@login-email', $email) - ->fill('@login-password', 'paSsword@123Quat') - ->press('Masuk') - ->assertPathIsNot('/login'); + $email = 'pest-login@test.com'; + $password = 'Oytrettt@123Quat'; + $user = User::where('email', $email)->first() + ?? User::factory()->create([ + 'email' => $email, + 'password' => Hash::make($password), + ]); + $user->password = $password; + $user->save(); + $page = visit('/login'); - SessionState::saveForUser($user); + $page->script(" + document.querySelector('[data-testid=login-email]').value = '".addslashes($email)."'; + document.querySelector('[data-testid=login-password]').value = '".$password."'; + document.querySelector('[data-testid=login-submit]').click(); + "); + + $page->assertPathIsNot('/login'); + ScreenshotHelper::saveIfEnabled($page, 'login-success'); }); it('shows error for invalid credentials', function () { - $page = visit('/login') - ->fill('@login-email', 'wrong@email.com') - ->fill('@login-password', 'wrongpassword') - ->press('@login-submit') - ->assertSee('Masuk'); + $page = visit('/login'); + + $page->script(" + document.querySelector('[data-testid=login-email]').value = 'wrong@email.com'; + document.querySelector('[data-testid=login-password]').value = 'wrongpassword'; + document.querySelector('[data-testid=login-submit]').click(); + "); + + $page->assertSee('Masuk'); ScreenshotHelper::saveIfEnabled($page, 'login-error'); }); diff --git a/tests/Browser/SmokePendidikanTest.php b/tests/Browser/SmokePendidikanTest.php new file mode 100644 index 00000000..6245ca7f --- /dev/null +++ b/tests/Browser/SmokePendidikanTest.php @@ -0,0 +1,93 @@ +user = SessionState::loginAdminUser(); +}); + +afterEach(function () { + SessionState::clear(); +}); + +it('opens the pendidikan page', function () { + SessionState::loginAndNavigate($this->user, '/data-pokok/pendidikan') + ->assertPathIs('/data-pokok/pendidikan'); +}); + +it('displays statistik partisipasi sekolah', function () { + SessionState::loginAndNavigate($this->user, '/data-pokok/pendidikan') + ->assertSee('Statistik Partisipasi Sekolah') + ->assertVisible('#donutChart'); +}); + +it('displays statistik ijazah tertinggi', function () { + SessionState::loginAndNavigate($this->user, '/data-pokok/pendidikan') + ->assertSee('Statistik Ijazah Tertinggi') + ->assertVisible('#barChart'); +}); + +it('renders all charts successfully', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/pendidikan') + ->assertPathIs('/data-pokok/pendidikan'); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const donut = document.querySelector('#donutChart'); + const bar = document.querySelector('#barChart'); + const donutReady = donut && donut.getContext && donut.width > 0; + const barReady = bar && bar.getContext && bar.width > 0; + if (donutReady && barReady) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })", + true + ); +}); + +it('displays cetak button', function () { + SessionState::loginAndNavigate($this->user, '/data-pokok/pendidikan') + ->assertVisible('#print-btn-pendidikan'); +}); + +it('displays excel button', function () { + SessionState::loginAndNavigate($this->user, '/data-pokok/pendidikan') + ->assertVisible('#download-excel'); +}); + +it('displays datatable', function () { + SessionState::loginAndNavigate($this->user, '/data-pokok/pendidikan') + ->assertVisible('#pendidikan'); +}); + +it('displays at least one data row', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/pendidikan') + ->assertPathIs('/data-pokok/pendidikan'); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const rows = document.querySelectorAll('#pendidikan tbody tr'); + if (rows.length > 0 && !rows[0].classList.contains('dataTables_empty')) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })", + true + ); +}); + +it('has no javascript errors', function () { + SessionState::loginAndNavigate($this->user, '/data-pokok/pendidikan') + ->assertPathIs('/data-pokok/pendidikan') + ->assertNoJavaScriptErrors(); +}); diff --git a/tests/Browser/SmokePendudukTest.php b/tests/Browser/SmokePendudukTest.php new file mode 100644 index 00000000..df9d54dd --- /dev/null +++ b/tests/Browser/SmokePendudukTest.php @@ -0,0 +1,96 @@ +user = SessionState::loginAdminUser(); +}); + +afterEach(function () { + SessionState::clear(); +}); + +it('opens the penduduk page', function () { + SessionState::loginAndNavigate($this->user, '/penduduk') + ->assertPathIs('/penduduk') + ->assertSee('Data Penduduk'); +}); + +it('displays filter button', function () { + SessionState::loginAndNavigate($this->user, '/penduduk') + ->assertVisible('[href="#collapse-filter"]'); +}); + +it('displays cetak button', function () { + SessionState::loginAndNavigate($this->user, '/penduduk') + ->assertVisible('#cetak'); +}); + +it('displays excel button', function () { + $page = SessionState::loginAndNavigate($this->user, '/penduduk') + ->assertPathIs('/penduduk'); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const btn = document.querySelector('#download-excel') || document.querySelector('button[data-download-url]'); + resolve(!!btn); + }; + check(); + })", + true + ); +}); + +it('displays datatable', function () { + SessionState::loginAndNavigate($this->user, '/penduduk') + ->assertVisible('#penduduk'); +}); + +it('displays at least one data row', function () { + $page = SessionState::loginAndNavigate($this->user, '/penduduk') + ->assertPathIs('/penduduk'); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const rows = document.querySelectorAll('#penduduk tbody tr'); + if (rows.length > 0 && !rows[0].classList.contains('dataTables_empty')) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })", + true + ); +}); + +it('displays select aksi button per row', function () { + $page = SessionState::loginAndNavigate($this->user, '/penduduk') + ->assertPathIs('/penduduk'); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const rows = document.querySelectorAll('#penduduk tbody tr'); + if (rows.length > 0 && !rows[0].classList.contains('dataTables_empty')) { + const hasAction = document.querySelector('#penduduk tbody td:nth-child(2) a, #penduduk tbody td:nth-child(2) button, #penduduk tbody td:nth-child(2) .dropdown'); + resolve(!!hasAction); + } else { + setTimeout(check, 500); + } + }; + check(); + })", + true + ); +}); + +it('has no javascript errors', function () { + SessionState::loginAndNavigate($this->user, '/penduduk') + ->assertPathIs('/penduduk') + ->assertNoJavaScriptErrors(); +}); diff --git a/tests/Browser/SmokePenerimaBantuanTest.php b/tests/Browser/SmokePenerimaBantuanTest.php new file mode 100644 index 00000000..568f6f06 --- /dev/null +++ b/tests/Browser/SmokePenerimaBantuanTest.php @@ -0,0 +1,85 @@ +user = SessionState::loginAdminUser(); +}); + +afterEach(function () { + SessionState::clear(); +}); + +it('opens the bantuan page', function () { + SessionState::loginAndNavigate($this->user, '/bantuan') + ->assertPathIs('/bantuan') + ->assertSee('Bantuan'); +}); + +it('displays filter button', function () { + SessionState::loginAndNavigate($this->user, '/bantuan') + ->assertVisible('[href="#collapse-filter"]'); +}); + +it('displays cetak button', function () { + SessionState::loginAndNavigate($this->user, '/bantuan') + ->assertVisible('#cetak'); +}); + +it('displays excel button', function () { + SessionState::loginAndNavigate($this->user, '/bantuan') + ->assertVisible('#download-excel'); +}); + +it('displays datatable', function () { + SessionState::loginAndNavigate($this->user, '/bantuan') + ->assertVisible('#bantuan'); +}); + +it('displays at least one data row', function () { + $page = SessionState::loginAndNavigate($this->user, '/bantuan') + ->assertPathIs('/bantuan'); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const rows = document.querySelectorAll('#bantuan tbody tr'); + if (rows.length > 0 && !rows[0].classList.contains('dataTables_empty')) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })", + true + ); +}); + +it('displays detail button per row', function () { + $page = SessionState::loginAndNavigate($this->user, '/bantuan') + ->assertPathIs('/bantuan'); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const rows = document.querySelectorAll('#bantuan tbody tr'); + if (rows.length > 0 && !rows[0].classList.contains('dataTables_empty')) { + const detailBtn = document.querySelector('#bantuan tbody td:nth-child(2) a[href*=\"bantuan/detail\"]'); + resolve(!!detailBtn); + } else { + setTimeout(check, 500); + } + }; + check(); + })", + true + ); +}); + +it('has no javascript errors', function () { + SessionState::loginAndNavigate($this->user, '/bantuan') + ->assertPathIs('/bantuan') + ->assertNoJavaScriptErrors(); +}); diff --git a/tests/Browser/fixtures/bantuan-sasaran.json b/tests/Browser/fixtures/bantuan-sasaran.json new file mode 100644 index 00000000..9315acde --- /dev/null +++ b/tests/Browser/fixtures/bantuan-sasaran.json @@ -0,0 +1,5 @@ +[ + {"id": "1", "text": "Keluarga Miskin"}, + {"id": "2", "text": "Lansia"}, + {"id": "3", "text": "Disabilitas"} +] diff --git a/tests/Browser/fixtures/bantuan-tahun.json b/tests/Browser/fixtures/bantuan-tahun.json new file mode 100644 index 00000000..01335172 --- /dev/null +++ b/tests/Browser/fixtures/bantuan-tahun.json @@ -0,0 +1,5 @@ +[ + {"id": "2024", "text": "2024"}, + {"id": "2023", "text": "2023"}, + {"id": "2022", "text": "2022"} +] diff --git a/tests/Browser/fixtures/bantuan.json b/tests/Browser/fixtures/bantuan.json new file mode 100644 index 00000000..9c0973db --- /dev/null +++ b/tests/Browser/fixtures/bantuan.json @@ -0,0 +1,24 @@ +{ + "data": [ + { + "id": "1", + "attributes": { + "nama_desa": "Desa Test", + "nama": "Program Bantuan Sembako", + "asaldana": "APBD", + "jumlah_peserta": 100, + "sdate": "2024-01-01", + "edate": "2024-12-31", + "nama_sasaran": "Keluarga Miskin", + "status": "Aktif" + } + } + ], + "meta": { + "pagination": { + "total": 1, + "current_page": 1, + "last_page": 1 + } + } +} diff --git a/tests/Browser/fixtures/kesehatan.json b/tests/Browser/fixtures/kesehatan.json new file mode 100644 index 00000000..c74cfa7e --- /dev/null +++ b/tests/Browser/fixtures/kesehatan.json @@ -0,0 +1,25 @@ +{ + "data": [ + { + "id": "1", + "attributes": { + "nama_desa": "Desa Test", + "nik": "5271010101010001", + "golongan_darah": "O", + "cacat": "Tidak", + "sakit_menahun": "Tidak", + "kb": "Tidak", + "hamil": "Tidak", + "asuransi": "BPJS", + "status_gizi": "Normal" + } + } + ], + "meta": { + "pagination": { + "total": 1, + "current_page": 1, + "last_page": 1 + } + } +} diff --git a/tests/Browser/fixtures/ketenagakerjaan.json b/tests/Browser/fixtures/ketenagakerjaan.json new file mode 100644 index 00000000..cea7374b --- /dev/null +++ b/tests/Browser/fixtures/ketenagakerjaan.json @@ -0,0 +1,22 @@ +{ + "data": [ + { + "id": "1", + "attributes": { + "nama_desa": "Desa Test", + "nik": "5271010101010001", + "pekerjaan": "Petani", + "jabatan": "Kepala Keluarga", + "jumlah_penghasilan": "Rp 2.000.000", + "pelatihan": "Tidak" + } + } + ], + "meta": { + "pagination": { + "total": 1, + "current_page": 1, + "last_page": 1 + } + } +} diff --git a/tests/Browser/fixtures/lembaga.json b/tests/Browser/fixtures/lembaga.json new file mode 100644 index 00000000..0aa0f717 --- /dev/null +++ b/tests/Browser/fixtures/lembaga.json @@ -0,0 +1,22 @@ +{ + "data": [ + { + "id": "1", + "attributes": { + "nama_desa": "Desa Test", + "kode": "LMB001", + "nama": "Karang Taruna", + "nama_ketua": "Budi Santoso", + "kategori": "Keagamaan", + "anggota_count": 25 + } + } + ], + "meta": { + "pagination": { + "total": 1, + "current_page": 1, + "last_page": 1 + } + } +} diff --git a/tests/Browser/fixtures/pendidikan.json b/tests/Browser/fixtures/pendidikan.json new file mode 100644 index 00000000..8b166155 --- /dev/null +++ b/tests/Browser/fixtures/pendidikan.json @@ -0,0 +1,24 @@ +{ + "data": [ + { + "id": "1", + "attributes": { + "nama_desa": "Desa Test", + "nik": "5271010101010001", + "pendidikan_kk_id": "SMA", + "pendidikan_sedang_id": "SMA", + "partisipasi_sekolah": "Ya", + "pendidikan_tertinggi": "SMA", + "kelas_tertinggi": "XII", + "ijazah_tertinggi": "SMA" + } + } + ], + "meta": { + "pagination": { + "total": 1, + "current_page": 1, + "last_page": 1 + } + } +} diff --git a/tests/Browser/fixtures/penduduk-data.json b/tests/Browser/fixtures/penduduk-data.json new file mode 100644 index 00000000..d1ea1d78 --- /dev/null +++ b/tests/Browser/fixtures/penduduk-data.json @@ -0,0 +1,33 @@ +{ + "data": [ + { + "id": "1", + "attributes": { + "nama_desa": "Desa Test", + "nik": "5271010101010001", + "nama": "Budi Santoso", + "no_kk": "52710101010100001", + "nama_ayah": "Agus Santoso", + "nama_ibu": "Siti Rahayu", + "no_rumah_tangga": "001", + "alamat": "Jl. Test No. 1", + "dusun": "Dusun Test", + "rw": "001", + "rt": "001", + "pendidikan_kk_id": "SMA", + "umur": "30", + "pekerjaan": "Petani", + "status_kawin": "Kawin", + "tgl_peristiwa": "2020-01-01", + "tgl_terdaftar": "2020-01-01" + } + } + ], + "meta": { + "pagination": { + "total": 1, + "current_page": 1, + "last_page": 1 + } + } +} diff --git a/tests/Pest.php b/tests/Pest.php index fb051f54..2aaec030 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -1,6 +1,5 @@ in('Feature', 'Browser'); @@ -8,5 +7,5 @@ pest()->browser()->timeout(30000); beforeEach(function () { - config(['adminlte.google_fonts.allowed' => false]); + config(['adminlte.google_fonts.allowed' => false]); }); From 1e52f094898d7e79a736eef3c67cce0f55afdb63 Mon Sep 17 00:00:00 2001 From: Ahmad Afandi Date: Fri, 12 Jun 2026 14:49:08 +0700 Subject: [PATCH 33/43] perbaikan test --- docs/smoke-test-browser.md | 32 ++++++++++++++++++ tests/Browser/ScreenshotHelper.php | 2 +- tests/Browser/SmokeLoginTest.php | 53 ++++++++++++++++++++---------- 3 files changed, 68 insertions(+), 19 deletions(-) diff --git a/docs/smoke-test-browser.md b/docs/smoke-test-browser.md index 92f87764..25b8c089 100644 --- a/docs/smoke-test-browser.md +++ b/docs/smoke-test-browser.md @@ -167,6 +167,38 @@ pest()->browser()->timeout(30000); pest()->browser()->headless(false); ``` +### Menjalankan Test + +#### Mode Headless (Default) + +```bash +# Jalankan semua smoke test +php artisan pest --group=smoke + +# Jalankan test spesifik +php artisan pest --filter="SmokeLoginTest" + +# Jalankan satu test case +php artisan pest --filter="can login with valid credentials" +``` + +#### Mode Headed (Visible Browser) + +Gunakan flag `--headed` untuk menjalankan test dengan browser yang terlihat (tidak headless). Berguna untuk debugging dan melihat proses test secara visual. + +```bash +# Jalankan semua smoke test dengan browser visible +php artisan pest --group=smoke --headed + +# Jalankan test spesifik dengan browser visible +php artisan pest --filter="SmokeLoginTest" --headed + +# Jalankan satu test case dengan browser visible +php artisan pest --filter="can login with valid credentials" --headed +``` + +> **Catatan:** Mode headed membutuhkan environment dengan GUI (desktop). Untuk remote server, gunakan X11 forwarding (`ssh -X`) atau VNC. + --- ## MSW Setup diff --git a/tests/Browser/ScreenshotHelper.php b/tests/Browser/ScreenshotHelper.php index 8d91cecc..8b080ea5 100644 --- a/tests/Browser/ScreenshotHelper.php +++ b/tests/Browser/ScreenshotHelper.php @@ -16,7 +16,7 @@ public static function enabled(): bool ); } - public static function saveIfEnabled(\Pest\Browser\Api\AwaitableWebpage $page, string $name): void + public static function saveIfEnabled(\Pest\Browser\Api\PendingAwaitablePage|\Pest\Browser\Api\AwaitableWebpage $page, string $name): void { if (! self::enabled()) { return; diff --git a/tests/Browser/SmokeLoginTest.php b/tests/Browser/SmokeLoginTest.php index 08caeb14..68f0bb8f 100644 --- a/tests/Browser/SmokeLoginTest.php +++ b/tests/Browser/SmokeLoginTest.php @@ -1,9 +1,17 @@ 'captcha_enabled'], + ['value' => 'false'] + ); +}); + it('displays login page correctly', function () { $page = visit('/login') ->assertSee('Masuk') @@ -15,29 +23,38 @@ }); it('can login with valid credentials', function () { - $email = 'pest-' . time() . '@login.test'; - $user = User::factory()->create([ - 'email' => $email, - 'password' => 'paSsword@123Quat', - ]); - SessionState::assignAdminRole($user); - - $page = visit('/login') - ->fill('@login-email', $email) - ->fill('@login-password', 'paSsword@123Quat') - ->press('Masuk') - ->assertPathIsNot('/login'); + $email = 'pest-login@test.com'; + $password = 'Oytrettt@123Quat'; + $user = User::where('email', $email)->first() + ?? User::factory()->create([ + 'email' => $email, + 'password' => Hash::make($password), + ]); + $user->password = $password; + $user->save(); + $page = visit('/login'); - SessionState::saveForUser($user); + $page->script(" + document.querySelector('[data-testid=login-email]').value = '".addslashes($email)."'; + document.querySelector('[data-testid=login-password]').value = '".$password."'; + document.querySelector('[data-testid=login-submit]').click(); + "); + + $page->assertPathIsNot('/login'); + ScreenshotHelper::saveIfEnabled($page, 'login-success'); }); it('shows error for invalid credentials', function () { - $page = visit('/login') - ->fill('@login-email', 'wrong@email.com') - ->fill('@login-password', 'wrongpassword') - ->press('@login-submit') - ->assertSee('Masuk'); + $page = visit('/login'); + + $page->script(" + document.querySelector('[data-testid=login-email]').value = 'wrong@email.com'; + document.querySelector('[data-testid=login-password]').value = 'wrongpassword'; + document.querySelector('[data-testid=login-submit]').click(); + "); + + $page->assertSee('Masuk'); ScreenshotHelper::saveIfEnabled($page, 'login-error'); }); From ce495c42d98aae0008d9aab897a8c58e487ca806 Mon Sep 17 00:00:00 2001 From: Ahmad Afandi Date: Fri, 12 Jun 2026 15:17:46 +0700 Subject: [PATCH 34/43] update test CI --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ae977a06..a3d17233 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -64,5 +64,6 @@ jobs: DB_DATABASE: testing_db DB_USERNAME: root DB_PASSWORD: secret + APP_ENV: testing run: php artisan migrate && php artisan test From 592de5f6c2dfe6138571de8235715d5779b70084 Mon Sep 17 00:00:00 2001 From: Ahmad Afandi Date: Fri, 12 Jun 2026 15:22:32 +0700 Subject: [PATCH 35/43] install depedency playwright --- .github/workflows/test.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a3d17233..9215a973 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -44,6 +44,9 @@ jobs: - name: Install Playwright Browsers run: npm install playwright && npx playwright install + - name: Install Playwright System Dependencies + run: npx playwright install-deps + - name: Directory Permissions run: chmod -R 777 storage bootstrap/cache From d1de79ca221a674020c6c09d01d47a3de52582ec Mon Sep 17 00:00:00 2001 From: Ahmad Afandi Date: Fri, 12 Jun 2026 15:30:09 +0700 Subject: [PATCH 36/43] exclude testsuite Browser karena sangat berat --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9215a973..a0bbfbff 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -69,4 +69,4 @@ jobs: DB_PASSWORD: secret APP_ENV: testing - run: php artisan migrate && php artisan test + run: php artisan migrate && php artisan test --testsuite=Unit,Feature From da68b955693b4de96c5c96264665a0982cf05c83 Mon Sep 17 00:00:00 2001 From: Ahmad Afandi Date: Fri, 12 Jun 2026 15:39:54 +0700 Subject: [PATCH 37/43] kembalikan lagi --- .github/workflows/test.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a0bbfbff..9ea25953 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -42,10 +42,7 @@ jobs: run: npm install - name: Install Playwright Browsers - run: npm install playwright && npx playwright install - - - name: Install Playwright System Dependencies - run: npx playwright install-deps + run: npm install playwright && npx playwright install - name: Directory Permissions run: chmod -R 777 storage bootstrap/cache From 166ab49c8e458372a2a47cf9fae8473c6eb63f8a Mon Sep 17 00:00:00 2001 From: Ahmad Afandi Date: Fri, 12 Jun 2026 16:32:24 +0700 Subject: [PATCH 38/43] gunakan testid sebagai selector --- resources/views/bantuan/index.blade.php | 8 +-- .../excel-download-button.blade.php | 2 + .../views/components/print-button.blade.php | 2 + .../data_pokok/kesehatan/index.blade.php | 6 +- .../ketenagakerjaan/index.blade.php | 6 +- .../data_pokok/pendidikan/index.blade.php | 6 +- resources/views/lembaga/index.blade.php | 8 +-- resources/views/penduduk/index.blade.php | 8 +-- tests/Browser/SmokeKelembagaanTest.php | 36 +++++++----- tests/Browser/SmokeKesehatanTest.php | 37 +++++++++---- tests/Browser/SmokeKetenagakerjaanTest.php | 49 +++++++++-------- tests/Browser/SmokePendidikanTest.php | 40 +++++++++----- tests/Browser/SmokePendudukTest.php | 54 +++++++++--------- tests/Browser/SmokePenerimaBantuanTest.php | 55 +++++++------------ 14 files changed, 173 insertions(+), 144 deletions(-) diff --git a/resources/views/bantuan/index.blade.php b/resources/views/bantuan/index.blade.php index 4466f081..6ce9d9ed 100644 --- a/resources/views/bantuan/index.blade.php +++ b/resources/views/bantuan/index.blade.php @@ -17,13 +17,13 @@ @@ -76,7 +76,7 @@ class="fas fa-search">
-
No
+
diff --git a/resources/views/components/excel-download-button.blade.php b/resources/views/components/excel-download-button.blade.php index b687a1ab..719c85f7 100644 --- a/resources/views/components/excel-download-button.blade.php +++ b/resources/views/components/excel-download-button.blade.php @@ -12,9 +12,11 @@ 'apiHeaders' => [], 'additionalParams' => [], 'class' => '', + 'testId' => '', ])
No
+
diff --git a/resources/views/data_pokok/ketenagakerjaan/index.blade.php b/resources/views/data_pokok/ketenagakerjaan/index.blade.php index e410b6ed..e24e7781 100644 --- a/resources/views/data_pokok/ketenagakerjaan/index.blade.php +++ b/resources/views/data_pokok/ketenagakerjaan/index.blade.php @@ -45,15 +45,15 @@
{{ $title }}
- + + filename="data_ketenagakerjaan" testId="bt-excel" />
-
No
+
diff --git a/resources/views/data_pokok/pendidikan/index.blade.php b/resources/views/data_pokok/pendidikan/index.blade.php index ce7a7d98..cc6edda6 100644 --- a/resources/views/data_pokok/pendidikan/index.blade.php +++ b/resources/views/data_pokok/pendidikan/index.blade.php @@ -45,14 +45,14 @@
Data Pendidikan Penduduk dan DTKS
- - + +
-
No
+
diff --git a/resources/views/lembaga/index.blade.php b/resources/views/lembaga/index.blade.php index fe082e42..4c9ee20a 100644 --- a/resources/views/lembaga/index.blade.php +++ b/resources/views/lembaga/index.blade.php @@ -23,11 +23,11 @@ @@ -74,7 +74,7 @@ class="fas fa-search">
-
No
+
diff --git a/resources/views/penduduk/index.blade.php b/resources/views/penduduk/index.blade.php index e446f736..3fdaa59b 100644 --- a/resources/views/penduduk/index.blade.php +++ b/resources/views/penduduk/index.blade.php @@ -37,14 +37,14 @@ @@ -56,7 +56,7 @@ @if ($judul)

{{ $judul }}

@endif -
No
+
diff --git a/tests/Browser/SmokeKelembagaanTest.php b/tests/Browser/SmokeKelembagaanTest.php index da8ed315..914b09af 100644 --- a/tests/Browser/SmokeKelembagaanTest.php +++ b/tests/Browser/SmokeKelembagaanTest.php @@ -12,34 +12,38 @@ }); it('opens the lembaga page', function () { - SessionState::loginAndNavigate($this->user, '/lembaga') + $page = SessionState::loginAndNavigate($this->user, '/lembaga') ->assertPathIs('/lembaga') ->assertSee('Lembaga'); + + ScreenshotHelper::saveIfEnabled($page, 'kelembagaan-page'); }); it('displays filter button', function () { - SessionState::loginAndNavigate($this->user, '/lembaga') - ->assertVisible('[href="#collapse-filter"]'); + $page = SessionState::loginAndNavigate($this->user, '/lembaga') + ->assertVisible('[data-testid="bt-toggle-filter"]'); + + ScreenshotHelper::saveIfEnabled($page, 'kelembagaan-filter-button'); }); it('displays cetak button', function () { - SessionState::loginAndNavigate($this->user, '/lembaga') - ->assertVisible('#print-btn-table-lembaga'); + $page = SessionState::loginAndNavigate($this->user, '/lembaga') + ->assertVisible('[data-testid="bt-cetak"]'); + + ScreenshotHelper::saveIfEnabled($page, 'kelembagaan-cetak-button'); }); it('displays excel button', function () { - SessionState::loginAndNavigate($this->user, '/lembaga') - ->assertVisible('#download-excel'); -}); + $page = SessionState::loginAndNavigate($this->user, '/lembaga') + ->assertVisible('[data-testid="bt-excel"]'); -it('displays datatable', function () { - SessionState::loginAndNavigate($this->user, '/lembaga') - ->assertVisible('#table-lembaga'); + ScreenshotHelper::saveIfEnabled($page, 'kelembagaan-excel-button'); }); -it('displays at least one data row', function () { +it('displays datatable with data rows', function () { $page = SessionState::loginAndNavigate($this->user, '/lembaga') - ->assertPathIs('/lembaga'); + ->assertPathIs('/lembaga') + ->assertVisible('[data-testid="datatable-lembaga"]'); $page->assertScript( "new Promise((resolve) => { @@ -55,10 +59,14 @@ })", true ); + + ScreenshotHelper::saveIfEnabled($page, 'kelembagaan-datatable-rows'); }); it('has no javascript errors', function () { - SessionState::loginAndNavigate($this->user, '/lembaga') + $page = SessionState::loginAndNavigate($this->user, '/lembaga') ->assertPathIs('/lembaga') ->assertNoJavaScriptErrors(); + + ScreenshotHelper::saveIfEnabled($page, 'kelembagaan-no-errors'); }); diff --git a/tests/Browser/SmokeKesehatanTest.php b/tests/Browser/SmokeKesehatanTest.php index c503b8b1..de3ce04c 100644 --- a/tests/Browser/SmokeKesehatanTest.php +++ b/tests/Browser/SmokeKesehatanTest.php @@ -12,20 +12,26 @@ }); it('opens the kesehatan page', function () { - SessionState::loginAndNavigate($this->user, '/data-pokok/kesehatan') + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/kesehatan') ->assertPathIs('/data-pokok/kesehatan'); + + ScreenshotHelper::saveIfEnabled($page, 'kesehatan-page'); }); it('displays statistik golongan darah', function () { - SessionState::loginAndNavigate($this->user, '/data-pokok/kesehatan') + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/kesehatan') ->assertSee('Statistik Golongan Darah') ->assertVisible('#donutChart'); + + ScreenshotHelper::saveIfEnabled($page, 'kesehatan-donut-chart'); }); it('displays statistik status gizi balita', function () { - SessionState::loginAndNavigate($this->user, '/data-pokok/kesehatan') + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/kesehatan') ->assertSee('Statistik Status Gizi Balita') ->assertVisible('#barChart'); + + ScreenshotHelper::saveIfEnabled($page, 'kesehatan-bar-chart'); }); it('renders all charts successfully', function () { @@ -49,21 +55,28 @@ })", true ); + + ScreenshotHelper::saveIfEnabled($page, 'kesehatan-charts'); }); it('displays cetak button', function () { - SessionState::loginAndNavigate($this->user, '/data-pokok/kesehatan') - ->assertVisible('#print-btn-kesehatan'); + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/kesehatan') + ->assertVisible('[data-testid="bt-cetak"]'); + + ScreenshotHelper::saveIfEnabled($page, 'kesehatan-cetak-button'); }); it('displays excel button', function () { - SessionState::loginAndNavigate($this->user, '/data-pokok/kesehatan') - ->assertVisible('#download-excel'); + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/kesehatan') + ->assertVisible('[data-testid="bt-excel"]'); + + ScreenshotHelper::saveIfEnabled($page, 'kesehatan-excel-button'); }); -it('displays datatable with data', function () { +it('displays datatable with data rows', function () { $page = SessionState::loginAndNavigate($this->user, '/data-pokok/kesehatan') - ->assertPathIs('/data-pokok/kesehatan'); + ->assertPathIs('/data-pokok/kesehatan') + ->assertVisible('[data-testid="datatable-kesehatan"]'); $page->assertScript( "new Promise((resolve) => { @@ -79,10 +92,14 @@ })", true ); + + ScreenshotHelper::saveIfEnabled($page, 'kesehatan-datatable-rows'); }); it('has no javascript errors', function () { - SessionState::loginAndNavigate($this->user, '/data-pokok/kesehatan') + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/kesehatan') ->assertPathIs('/data-pokok/kesehatan') ->assertNoJavaScriptErrors(); + + ScreenshotHelper::saveIfEnabled($page, 'kesehatan-no-errors'); }); diff --git a/tests/Browser/SmokeKetenagakerjaanTest.php b/tests/Browser/SmokeKetenagakerjaanTest.php index ad82742e..de4fb3c3 100644 --- a/tests/Browser/SmokeKetenagakerjaanTest.php +++ b/tests/Browser/SmokeKetenagakerjaanTest.php @@ -12,22 +12,28 @@ }); it('opens the ketenagakerjaan page', function () { - SessionState::loginAndNavigate($this->user, '/data-pokok/ketenagakerjaan') + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/ketenagakerjaan') ->assertPathIs('/data-pokok/ketenagakerjaan') ->assertSee('Statistik Jumlah Penghasilan') ->assertSee('Statistik Pelatihan'); + + ScreenshotHelper::saveIfEnabled($page, 'ketenagakerjaan-page'); }); it('displays statistik jumlah penghasilan', function () { - SessionState::loginAndNavigate($this->user, '/data-pokok/ketenagakerjaan') + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/ketenagakerjaan') ->assertSee('Statistik Jumlah Penghasilan') ->assertVisible('#barChart'); + + ScreenshotHelper::saveIfEnabled($page, 'ketenagakerjaan-bar-chart'); }); it('displays statistik pelatihan', function () { - SessionState::loginAndNavigate($this->user, '/data-pokok/ketenagakerjaan') + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/ketenagakerjaan') ->assertSee('Statistik Pelatihan') ->assertVisible('#donutChart'); + + ScreenshotHelper::saveIfEnabled($page, 'ketenagakerjaan-donut-chart'); }); it('renders all charts successfully', function () { @@ -51,37 +57,28 @@ })", true ); + + ScreenshotHelper::saveIfEnabled($page, 'ketenagakerjaan-charts'); }); it('displays cetak button', function () { - SessionState::loginAndNavigate($this->user, '/data-pokok/ketenagakerjaan') - ->assertVisible('#print-btn-ketenagakerjaan'); -}); + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/ketenagakerjaan') + ->assertVisible('[data-testid="bt-cetak"]'); -it('displays excel button', function () { - SessionState::loginAndNavigate($this->user, '/data-pokok/ketenagakerjaan') - ->assertVisible('#download-excel'); + ScreenshotHelper::saveIfEnabled($page, 'ketenagakerjaan-cetak-button'); }); -it('displays datatable', function () { +it('displays excel button', function () { $page = SessionState::loginAndNavigate($this->user, '/data-pokok/ketenagakerjaan') - ->assertPathIs('/data-pokok/ketenagakerjaan'); + ->assertVisible('[data-testid="bt-excel"]'); - $page->assertScript( - "new Promise((resolve) => { - const check = () => { - const table = document.querySelector('#ketenagakerjaan'); - resolve(!!table); - }; - check(); - })", - true - ); + ScreenshotHelper::saveIfEnabled($page, 'ketenagakerjaan-excel-button'); }); -it('displays at least one data row', function () { +it('displays datatable with data rows', function () { $page = SessionState::loginAndNavigate($this->user, '/data-pokok/ketenagakerjaan') - ->assertPathIs('/data-pokok/ketenagakerjaan'); + ->assertPathIs('/data-pokok/ketenagakerjaan') + ->assertVisible('[data-testid="datatable-ketenagakerjaan"]'); $page->assertScript( "new Promise((resolve) => { @@ -97,10 +94,14 @@ })", true ); + + ScreenshotHelper::saveIfEnabled($page, 'ketenagakerjaan-datatable-rows'); }); it('has no javascript errors', function () { - SessionState::loginAndNavigate($this->user, '/data-pokok/ketenagakerjaan') + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/ketenagakerjaan') ->assertPathIs('/data-pokok/ketenagakerjaan') ->assertNoJavaScriptErrors(); + + ScreenshotHelper::saveIfEnabled($page, 'ketenagakerjaan-no-errors'); }); diff --git a/tests/Browser/SmokePendidikanTest.php b/tests/Browser/SmokePendidikanTest.php index 6245ca7f..c1fe6175 100644 --- a/tests/Browser/SmokePendidikanTest.php +++ b/tests/Browser/SmokePendidikanTest.php @@ -12,20 +12,26 @@ }); it('opens the pendidikan page', function () { - SessionState::loginAndNavigate($this->user, '/data-pokok/pendidikan') + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/pendidikan') ->assertPathIs('/data-pokok/pendidikan'); + + ScreenshotHelper::saveIfEnabled($page, 'pendidikan-page'); }); it('displays statistik partisipasi sekolah', function () { - SessionState::loginAndNavigate($this->user, '/data-pokok/pendidikan') + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/pendidikan') ->assertSee('Statistik Partisipasi Sekolah') ->assertVisible('#donutChart'); + + ScreenshotHelper::saveIfEnabled($page, 'pendidikan-donut-chart'); }); it('displays statistik ijazah tertinggi', function () { - SessionState::loginAndNavigate($this->user, '/data-pokok/pendidikan') + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/pendidikan') ->assertSee('Statistik Ijazah Tertinggi') ->assertVisible('#barChart'); + + ScreenshotHelper::saveIfEnabled($page, 'pendidikan-bar-chart'); }); it('renders all charts successfully', function () { @@ -49,26 +55,28 @@ })", true ); + + ScreenshotHelper::saveIfEnabled($page, 'pendidikan-charts'); }); it('displays cetak button', function () { - SessionState::loginAndNavigate($this->user, '/data-pokok/pendidikan') - ->assertVisible('#print-btn-pendidikan'); + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/pendidikan') + ->assertVisible('[data-testid="bt-cetak"]'); + + ScreenshotHelper::saveIfEnabled($page, 'pendidikan-cetak-button'); }); it('displays excel button', function () { - SessionState::loginAndNavigate($this->user, '/data-pokok/pendidikan') - ->assertVisible('#download-excel'); -}); + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/pendidikan') + ->assertVisible('[data-testid="bt-excel"]'); -it('displays datatable', function () { - SessionState::loginAndNavigate($this->user, '/data-pokok/pendidikan') - ->assertVisible('#pendidikan'); + ScreenshotHelper::saveIfEnabled($page, 'pendidikan-excel-button'); }); -it('displays at least one data row', function () { +it('displays datatable with data rows', function () { $page = SessionState::loginAndNavigate($this->user, '/data-pokok/pendidikan') - ->assertPathIs('/data-pokok/pendidikan'); + ->assertPathIs('/data-pokok/pendidikan') + ->assertVisible('[data-testid="datatable-pendidikan"]'); $page->assertScript( "new Promise((resolve) => { @@ -84,10 +92,14 @@ })", true ); + + ScreenshotHelper::saveIfEnabled($page, 'pendidikan-datatable-rows'); }); it('has no javascript errors', function () { - SessionState::loginAndNavigate($this->user, '/data-pokok/pendidikan') + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/pendidikan') ->assertPathIs('/data-pokok/pendidikan') ->assertNoJavaScriptErrors(); + + ScreenshotHelper::saveIfEnabled($page, 'pendidikan-no-errors'); }); diff --git a/tests/Browser/SmokePendudukTest.php b/tests/Browser/SmokePendudukTest.php index df9d54dd..93ec90ac 100644 --- a/tests/Browser/SmokePendudukTest.php +++ b/tests/Browser/SmokePendudukTest.php @@ -12,45 +12,38 @@ }); it('opens the penduduk page', function () { - SessionState::loginAndNavigate($this->user, '/penduduk') + $page = SessionState::loginAndNavigate($this->user, '/penduduk') ->assertPathIs('/penduduk') ->assertSee('Data Penduduk'); + + ScreenshotHelper::saveIfEnabled($page, 'penduduk-page'); }); it('displays filter button', function () { - SessionState::loginAndNavigate($this->user, '/penduduk') - ->assertVisible('[href="#collapse-filter"]'); + $page = SessionState::loginAndNavigate($this->user, '/penduduk') + ->assertVisible('[data-testid="bt-toggle-filter"]'); + + ScreenshotHelper::saveIfEnabled($page, 'penduduk-filter-button'); }); it('displays cetak button', function () { - SessionState::loginAndNavigate($this->user, '/penduduk') - ->assertVisible('#cetak'); + $page = SessionState::loginAndNavigate($this->user, '/penduduk') + ->assertVisible('[data-testid="bt-cetak"]'); + + ScreenshotHelper::saveIfEnabled($page, 'penduduk-cetak-button'); }); it('displays excel button', function () { $page = SessionState::loginAndNavigate($this->user, '/penduduk') - ->assertPathIs('/penduduk'); + ->assertVisible('[data-testid="bt-excel"]'); - $page->assertScript( - "new Promise((resolve) => { - const check = () => { - const btn = document.querySelector('#download-excel') || document.querySelector('button[data-download-url]'); - resolve(!!btn); - }; - check(); - })", - true - ); + ScreenshotHelper::saveIfEnabled($page, 'penduduk-excel-button'); }); -it('displays datatable', function () { - SessionState::loginAndNavigate($this->user, '/penduduk') - ->assertVisible('#penduduk'); -}); - -it('displays at least one data row', function () { +it('displays datatable with data rows', function () { $page = SessionState::loginAndNavigate($this->user, '/penduduk') - ->assertPathIs('/penduduk'); + ->assertPathIs('/penduduk') + ->assertVisible('[data-testid="datatable-penduduk"]'); $page->assertScript( "new Promise((resolve) => { @@ -66,9 +59,11 @@ })", true ); + + ScreenshotHelper::saveIfEnabled($page, 'penduduk-datatable-rows'); }); -it('displays select aksi button per row', function () { +it('displays select action button per row', function () { $page = SessionState::loginAndNavigate($this->user, '/penduduk') ->assertPathIs('/penduduk'); @@ -77,8 +72,9 @@ const check = () => { const rows = document.querySelectorAll('#penduduk tbody tr'); if (rows.length > 0 && !rows[0].classList.contains('dataTables_empty')) { - const hasAction = document.querySelector('#penduduk tbody td:nth-child(2) a, #penduduk tbody td:nth-child(2) button, #penduduk tbody td:nth-child(2) .dropdown'); - resolve(!!hasAction); + const firstRow = rows[0]; + const actionBtn = firstRow.querySelector('.dropdown-toggle, button[data-toggle=\"dropdown\"]'); + resolve(!!actionBtn); } else { setTimeout(check, 500); } @@ -87,10 +83,14 @@ })", true ); + + ScreenshotHelper::saveIfEnabled($page, 'penduduk-action-button'); }); it('has no javascript errors', function () { - SessionState::loginAndNavigate($this->user, '/penduduk') + $page = SessionState::loginAndNavigate($this->user, '/penduduk') ->assertPathIs('/penduduk') ->assertNoJavaScriptErrors(); + + ScreenshotHelper::saveIfEnabled($page, 'penduduk-no-errors'); }); diff --git a/tests/Browser/SmokePenerimaBantuanTest.php b/tests/Browser/SmokePenerimaBantuanTest.php index 568f6f06..8c8cb633 100644 --- a/tests/Browser/SmokePenerimaBantuanTest.php +++ b/tests/Browser/SmokePenerimaBantuanTest.php @@ -12,34 +12,38 @@ }); it('opens the bantuan page', function () { - SessionState::loginAndNavigate($this->user, '/bantuan') + $page = SessionState::loginAndNavigate($this->user, '/bantuan') ->assertPathIs('/bantuan') ->assertSee('Bantuan'); + + ScreenshotHelper::saveIfEnabled($page, 'bantuan-page'); }); it('displays filter button', function () { - SessionState::loginAndNavigate($this->user, '/bantuan') - ->assertVisible('[href="#collapse-filter"]'); + $page = SessionState::loginAndNavigate($this->user, '/bantuan') + ->assertVisible('[data-testid="bt-toggle-filter"]'); + + ScreenshotHelper::saveIfEnabled($page, 'bantuan-filter-button'); }); it('displays cetak button', function () { - SessionState::loginAndNavigate($this->user, '/bantuan') - ->assertVisible('#cetak'); + $page = SessionState::loginAndNavigate($this->user, '/bantuan') + ->assertVisible('[data-testid="bt-cetak"]'); + + ScreenshotHelper::saveIfEnabled($page, 'bantuan-cetak-button'); }); it('displays excel button', function () { - SessionState::loginAndNavigate($this->user, '/bantuan') - ->assertVisible('#download-excel'); -}); + $page = SessionState::loginAndNavigate($this->user, '/bantuan') + ->assertVisible('[data-testid="bt-excel"]'); -it('displays datatable', function () { - SessionState::loginAndNavigate($this->user, '/bantuan') - ->assertVisible('#bantuan'); + ScreenshotHelper::saveIfEnabled($page, 'bantuan-excel-button'); }); -it('displays at least one data row', function () { +it('displays datatable with data rows', function () { $page = SessionState::loginAndNavigate($this->user, '/bantuan') - ->assertPathIs('/bantuan'); + ->assertPathIs('/bantuan') + ->assertVisible('[data-testid="datatable-bantuan"]'); $page->assertScript( "new Promise((resolve) => { @@ -55,31 +59,14 @@ })", true ); -}); -it('displays detail button per row', function () { - $page = SessionState::loginAndNavigate($this->user, '/bantuan') - ->assertPathIs('/bantuan'); - - $page->assertScript( - "new Promise((resolve) => { - const check = () => { - const rows = document.querySelectorAll('#bantuan tbody tr'); - if (rows.length > 0 && !rows[0].classList.contains('dataTables_empty')) { - const detailBtn = document.querySelector('#bantuan tbody td:nth-child(2) a[href*=\"bantuan/detail\"]'); - resolve(!!detailBtn); - } else { - setTimeout(check, 500); - } - }; - check(); - })", - true - ); + ScreenshotHelper::saveIfEnabled($page, 'bantuan-datatable-rows'); }); it('has no javascript errors', function () { - SessionState::loginAndNavigate($this->user, '/bantuan') + $page = SessionState::loginAndNavigate($this->user, '/bantuan') ->assertPathIs('/bantuan') ->assertNoJavaScriptErrors(); + + ScreenshotHelper::saveIfEnabled($page, 'bantuan-no-errors'); }); From 3fe68cbd11233f60b5f8f9b5981f84325444b370 Mon Sep 17 00:00:00 2001 From: Ahmad Afandi Date: Mon, 15 Jun 2026 08:24:37 +0700 Subject: [PATCH 39/43] Feat: Smoketest untuk statistik kependudukan --- .../views/laporan-bulanan/index.blade.php | 12 +- .../laporan-bulanan/table_bulanan.blade.php | 2 +- resources/views/statistik/index.blade.php | 20 +- tests/Browser/FixtureReader.php | 27 +++ tests/Browser/MswSetup.php | 4 + tests/Browser/SmokeLaporanBulananTest.php | 193 ++++++++++++++++++ tests/Browser/SmokeStatistikBantuanTest.php | 191 +++++++++++++++++ tests/Browser/SmokeStatistikKeluargaTest.php | 191 +++++++++++++++++ tests/Browser/SmokeStatistikPendudukTest.php | 191 +++++++++++++++++ tests/Browser/SmokeStatistikRtmTest.php | 191 +++++++++++++++++ .../fixtures/kategori-statistik-bantuan.json | 7 + .../fixtures/kategori-statistik-keluarga.json | 6 + .../fixtures/kategori-statistik-penduduk.json | 12 ++ .../fixtures/kategori-statistik-rtm.json | 6 + .../fixtures/statistik-bantuan-keluarga.json | 46 +++++ .../fixtures/statistik-bantuan-penduduk.json | 46 +++++ .../statistik-keluarga-kelas-sosial.json | 72 +++++++ .../fixtures/statistik-penduduk-agama.json | 52 ++++- .../statistik-penduduk-golongan-darah.json | 52 ++++- .../statistik-penduduk-jenis-kelamin.json | 21 +- ...tatistik-penduduk-pendidikan-dalam-kk.json | 88 +++++++- .../statistik-penduduk-penyakit-menahun.json | 52 ++++- .../statistik-penduduk-penyandang-cacat.json | 52 ++++- .../statistik-penduduk-rentang-umur.json | 81 +++++--- .../statistik-penduduk-status-perkawinan.json | 52 ++++- .../fixtures/statistik-penduduk-suku.json | 61 +++++- tests/Browser/fixtures/statistik-rtm-bdt.json | 46 +++++ 27 files changed, 1698 insertions(+), 76 deletions(-) create mode 100644 tests/Browser/SmokeLaporanBulananTest.php create mode 100644 tests/Browser/SmokeStatistikBantuanTest.php create mode 100644 tests/Browser/SmokeStatistikKeluargaTest.php create mode 100644 tests/Browser/SmokeStatistikPendudukTest.php create mode 100644 tests/Browser/SmokeStatistikRtmTest.php create mode 100644 tests/Browser/fixtures/kategori-statistik-bantuan.json create mode 100644 tests/Browser/fixtures/kategori-statistik-keluarga.json create mode 100644 tests/Browser/fixtures/kategori-statistik-penduduk.json create mode 100644 tests/Browser/fixtures/kategori-statistik-rtm.json create mode 100644 tests/Browser/fixtures/statistik-bantuan-keluarga.json create mode 100644 tests/Browser/fixtures/statistik-bantuan-penduduk.json create mode 100644 tests/Browser/fixtures/statistik-keluarga-kelas-sosial.json create mode 100644 tests/Browser/fixtures/statistik-rtm-bdt.json diff --git a/resources/views/laporan-bulanan/index.blade.php b/resources/views/laporan-bulanan/index.blade.php index bacee061..123fb5cb 100644 --- a/resources/views/laporan-bulanan/index.blade.php +++ b/resources/views/laporan-bulanan/index.blade.php @@ -17,7 +17,7 @@
{{ $page_description }}
+ target="_blank" data-testid="bt-excel"> Excel @@ -31,7 +31,7 @@
+ onchange="formAction('mainform','{{ route('laporan-bulanan.filter') }}')" data-testid="filter-kecamatan"> @foreach ($kecamatans as $item)
No
+
diff --git a/resources/views/statistik/index.blade.php b/resources/views/statistik/index.blade.php index b0f36593..51b089e6 100644 --- a/resources/views/statistik/index.blade.php +++ b/resources/views/statistik/index.blade.php @@ -28,7 +28,7 @@
-
@@ -47,12 +47,12 @@ @endif
-
- @@ -60,13 +60,13 @@ class="fa fa-print">
@@ -126,15 +126,15 @@ class="fa fa-print">
@endif
-
-
+
+

-
-
+
+

@@ -143,7 +143,7 @@ class="fa fa-print">
-
No
+
diff --git a/tests/Browser/FixtureReader.php b/tests/Browser/FixtureReader.php index b6ddf1b6..8064f2dd 100644 --- a/tests/Browser/FixtureReader.php +++ b/tests/Browser/FixtureReader.php @@ -96,4 +96,31 @@ public static function demografiFirstItemName(string $key): ?string return null; } + + public static function kategoriStatistikNames(string $kategori): array + { + $data = self::read("kategori-statistik-{$kategori}.json"); + + return array_map(fn (array $item) => $item['nama'], $data['data'] ?? []); + } + + public static function kategoriStatistikPendudukNames(): array + { + return self::kategoriStatistikNames('penduduk'); + } + + public static function kategoriStatistikKeluargaNames(): array + { + return self::kategoriStatistikNames('keluarga'); + } + + public static function kategoriStatistikRtmNames(): array + { + return self::kategoriStatistikNames('rtm'); + } + + public static function kategoriStatistikBantuanNames(): array + { + return self::kategoriStatistikNames('bantuan'); + } } \ No newline at end of file diff --git a/tests/Browser/MswSetup.php b/tests/Browser/MswSetup.php index b866c7b3..4013083f 100644 --- a/tests/Browser/MswSetup.php +++ b/tests/Browser/MswSetup.php @@ -47,6 +47,10 @@ final class MswSetup '#/api/v1/statistik-web/get-list-kecamatan/([\d.]+)$#' => 'kecamatan-*.json', '#/api/v1/statistik-web/get-list-desa/([\d.]+)$#' => 'desa-*.json', '#/api/v1/statistik/penduduk\?.*filter(?:\[id\]|%5Bid%5D)=([^&]+)#' => 'statistik-penduduk-*.json', + '#/api/v1/statistik/kategori-statistik\?.*filter(?:\[id\]|%5Bid%5D)=([^&]+)#' => 'kategori-statistik-*.json', + '#/api/v1/statistik/keluarga\?.*filter(?:\[id\]|%5Bid%5D)=([^&]+)#' => 'statistik-keluarga-*.json', + '#/api/v1/statistik/rtm\?.*filter(?:\[id\]|%5Bid%5D)=([^&]+)#' => 'statistik-rtm-*.json', + '#/api/v1/statistik/bantuan\?.*filter(?:\[id\]|%5Bid%5D)=([^&]+)#' => 'statistik-bantuan-*.json', ]; /** diff --git a/tests/Browser/SmokeLaporanBulananTest.php b/tests/Browser/SmokeLaporanBulananTest.php new file mode 100644 index 00000000..d3870964 --- /dev/null +++ b/tests/Browser/SmokeLaporanBulananTest.php @@ -0,0 +1,193 @@ +user = SessionState::loginAdminUser(); + + Http::fake(function ($request) { + $url = $request->url(); + + // logPenduduk - returns a date string + if (str_contains($url, '/api/v1/statistik/laporan-bulanan/log-penduduk')) { + return Http::response([ + 'data' => date('Y-m-01 00:00:00'), + ], 200); + } + + // laporan bulanan data + if (str_contains($url, '/api/v1/statistik/laporan-bulanan')) { + return Http::response([ + 'data' => [ + 'penduduk_awal' => [ + 'WNI_L' => 12000, 'WNI_P' => 11500, + 'WNA_L' => 10, 'WNA_P' => 5, + 'KK_L' => 5500, 'KK_P' => 5200, 'KK' => 10700, + ], + 'kelahiran' => [ + 'WNI_L' => 50, 'WNI_P' => 45, + 'WNA_L' => 0, 'WNA_P' => 0, + 'KK_L' => 0, 'KK_P' => 0, 'KK' => 0, + ], + 'kematian' => [ + 'WNI_L' => 20, 'WNI_P' => 15, + 'WNA_L' => 0, 'WNA_P' => 0, + 'KK_L' => 0, 'KK_P' => 0, 'KK' => 0, + ], + 'pendatang' => [ + 'WNI_L' => 30, 'WNI_P' => 25, + 'WNA_L' => 1, 'WNA_P' => 0, + 'KK_L' => 0, 'KK_P' => 0, 'KK' => 0, + ], + 'pindah' => [ + 'WNI_L' => 15, 'WNI_P' => 10, + 'WNA_L' => 0, 'WNA_P' => 0, + 'KK_L' => 0, 'KK_P' => 0, 'KK' => 0, + ], + 'hilang' => [ + 'WNI_L' => 5, 'WNI_P' => 3, + 'WNA_L' => 0, 'WNA_P' => 0, + 'KK_L' => 0, 'KK_P' => 0, 'KK' => 0, + ], + 'penduduk_akhir' => [ + 'WNI_L' => 12045, 'WNI_P' => 11542, + 'WNA_L' => 11, 'WNA_P' => 5, + 'KK_L' => 5500, 'KK_P' => 5200, 'KK' => 10700, + ], + ], + ], 200); + } + + // config kabupaten + if (str_contains($url, '/api/v1/config/kabupaten')) { + return Http::response([ + 'data' => [ + [ + 'type' => 'config', + 'id' => '', + 'attributes' => [ + 'nama_kabupaten' => 'KABUPATEN TEST', + 'kode_kabupaten' => '5102', + ], + ], + ], + ], 200); + } + + // config kecamatan + if (str_contains($url, '/api/v1/config/kecamatan')) { + return Http::response([ + 'data' => [ + [ + 'type' => 'config', + 'id' => '', + 'attributes' => [ + 'nama_kecamatan' => 'KECAMATAN TEST', + 'kode_kecamatan' => '510201', + ], + ], + ], + ], 200); + } + + // config desa + if (str_contains($url, '/api/v1/config/desa')) { + return Http::response([ + 'data' => [ + [ + 'type' => 'config', + 'id' => '1', + 'attributes' => [ + 'nama_desa' => 'DESA TEST', + 'kode_desa' => '5102010001', + ], + ], + ], + ], 200); + } + + // fallback: empty response for unmatched requests + return Http::response(['data' => []], 200); + }); +}); + +afterEach(function () { + SessionState::clear(); +}); + +it('opens the laporan bulanan page', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/laporan-bulanan') + ->assertPathIs('/statistik/laporan-bulanan') + ->assertSee('Laporan Kependudukan Bulanan'); + + ScreenshotHelper::saveIfEnabled($page, 'laporan-bulanan-page'); +}); + +it('displays excel button', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/laporan-bulanan') + ->assertVisible('@bt-excel'); + + ScreenshotHelper::saveIfEnabled($page, 'laporan-bulanan-excel-button'); +}); + +it('displays filter kabupaten', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/laporan-bulanan') + ->assertVisible('@filter-kabupaten'); + + ScreenshotHelper::saveIfEnabled($page, 'laporan-bulanan-filter-kabupaten'); +}); + +it('displays filter kecamatan', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/laporan-bulanan') + ->assertVisible('@filter-kecamatan'); + + ScreenshotHelper::saveIfEnabled($page, 'laporan-bulanan-filter-kecamatan'); +}); + +it('displays filter desa', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/laporan-bulanan') + ->assertVisible('@filter-desa'); + + ScreenshotHelper::saveIfEnabled($page, 'laporan-bulanan-filter-desa'); +}); + +it('displays filter tahun', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/laporan-bulanan') + ->assertVisible('@filter-tahun'); + + ScreenshotHelper::saveIfEnabled($page, 'laporan-bulanan-filter-tahun'); +}); + +it('displays filter bulan', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/laporan-bulanan') + ->assertVisible('@filter-bulan'); + + ScreenshotHelper::saveIfEnabled($page, 'laporan-bulanan-filter-bulan'); +}); + +it('displays datatable', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/laporan-bulanan'); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const table = document.querySelector('[data-testid=\"datatable-laporan-bulanan\"]'); + if (table) { resolve(true); } else { setTimeout(check, 500); } + }; + check(); + })", + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'laporan-bulanan-datatable'); +}); + +it('has no javascript errors', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/laporan-bulanan') + ->assertPathIs('/statistik/laporan-bulanan') + ->assertNoJavaScriptErrors(); + + ScreenshotHelper::saveIfEnabled($page, 'laporan-bulanan-no-errors'); +}); diff --git a/tests/Browser/SmokeStatistikBantuanTest.php b/tests/Browser/SmokeStatistikBantuanTest.php new file mode 100644 index 00000000..2f13c2e1 --- /dev/null +++ b/tests/Browser/SmokeStatistikBantuanTest.php @@ -0,0 +1,191 @@ +user = SessionState::loginAdminUser(); +}); + +afterEach(function () { + SessionState::clear(); +}); + +$kategoriBantuan = FixtureReader::kategoriStatistikBantuanNames(); +$defaultId = 'penduduk'; + +it('opens the statistik bantuan page', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/bantuan') + ->assertPathIs('/statistik/bantuan') + ->assertSee('Data Statistik Bantuan'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-bantuan-page'); +}); + +it('displays kategori statistik list', function () use ($kategoriBantuan) { + $page = SessionState::loginAndNavigate($this->user, '/statistik/bantuan') + ->assertVisible('@daftar-statistik'); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const items = document.querySelectorAll('#daftar-statistik .pilih-kategori'); + if (items.length > 0) { resolve(true); } else { setTimeout(check, 500); } + }; + check(); + })", + true + ); + + foreach ($kategoriBantuan as $nama) { + $escaped = addslashes($nama); + $page->assertScript( + "Array.from(document.querySelectorAll('#daftar-statistik .pilih-kategori a')).some(a => a.textContent.trim().includes('{$escaped}'))", + true + ); + } + + ScreenshotHelper::saveIfEnabled($page, 'statistik-bantuan-kategori-list'); +}); + +it('displays cetak button', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/bantuan') + ->assertVisible('@bt-cetak'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-bantuan-cetak-button'); +}); + +it('displays excel button', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/bantuan') + ->assertVisible('@bt-excel'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-bantuan-excel-button'); +}); + +it('displays grafik button', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/bantuan') + ->assertVisible('@bt-grafik'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-bantuan-grafik-button'); +}); + +it('displays chart button', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/bantuan') + ->assertVisible('@bt-chart'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-bantuan-chart-button'); +}); + +it('displays datatable', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/bantuan') + ->assertVisible('@datatable-statistik'); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const rows = document.querySelectorAll('#tabel-data tbody tr'); + if (rows.length > 0 && !rows[0].classList.contains('dataTables_empty')) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })", + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-bantuan-datatable'); +}); + +it('accesses a kategori statistik and loads data', function () use ($defaultId) { + $page = SessionState::loginAndNavigate($this->user, '/statistik/bantuan') + ->assertVisible('@daftar-statistik'); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const items = document.querySelectorAll('#daftar-statistik .pilih-kategori'); + if (items.length > 0) { resolve(true); } else { setTimeout(check, 500); } + }; + check(); + })", + true + ); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const rows = document.querySelectorAll('#tabel-data tbody tr'); + if (rows.length > 0 && !rows[0].classList.contains('dataTables_empty')) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })", + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-bantuan-kategori-data'); +}); + +it('accesses grafik and renders successfully', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/bantuan') + ->assertVisible('@bt-grafik'); + + $page->click('@bt-grafik'); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const container = document.querySelector('#grafik-statistik'); + const canvas = document.querySelector('#barChart'); + if (container && canvas && canvas.getContext && canvas.width > 0) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })", + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-bantuan-grafik'); +}); + +it('accesses chart and renders successfully', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/bantuan') + ->assertVisible('@bt-chart'); + + $page->click('@bt-chart'); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const container = document.querySelector('#pie-statistik'); + const canvas = document.querySelector('#donutChart'); + if (container && canvas && canvas.getContext && canvas.width > 0) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })", + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-bantuan-chart'); +}); + +it('has no javascript errors', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/bantuan') + ->assertPathIs('/statistik/bantuan') + ->assertNoJavaScriptErrors(); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-bantuan-no-errors'); +}); diff --git a/tests/Browser/SmokeStatistikKeluargaTest.php b/tests/Browser/SmokeStatistikKeluargaTest.php new file mode 100644 index 00000000..f98c49f6 --- /dev/null +++ b/tests/Browser/SmokeStatistikKeluargaTest.php @@ -0,0 +1,191 @@ +user = SessionState::loginAdminUser(); +}); + +afterEach(function () { + SessionState::clear(); +}); + +$kategoriKeluarga = FixtureReader::kategoriStatistikKeluargaNames(); +$defaultId = 'kelas-sosial'; + +it('opens the statistik keluarga page', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/keluarga') + ->assertPathIs('/statistik/keluarga') + ->assertSee('Data Statistik Keluarga'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-keluarga-page'); +}); + +it('displays kategori statistik list', function () use ($kategoriKeluarga) { + $page = SessionState::loginAndNavigate($this->user, '/statistik/keluarga') + ->assertVisible('@daftar-statistik'); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const items = document.querySelectorAll('#daftar-statistik .pilih-kategori'); + if (items.length > 0) { resolve(true); } else { setTimeout(check, 500); } + }; + check(); + })", + true + ); + + foreach ($kategoriKeluarga as $nama) { + $escaped = addslashes($nama); + $page->assertScript( + "Array.from(document.querySelectorAll('#daftar-statistik .pilih-kategori a')).some(a => a.textContent.trim().includes('{$escaped}'))", + true + ); + } + + ScreenshotHelper::saveIfEnabled($page, 'statistik-keluarga-kategori-list'); +}); + +it('displays cetak button', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/keluarga') + ->assertVisible('@bt-cetak'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-keluarga-cetak-button'); +}); + +it('displays excel button', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/keluarga') + ->assertVisible('@bt-excel'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-keluarga-excel-button'); +}); + +it('displays grafik button', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/keluarga') + ->assertVisible('@bt-grafik'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-keluarga-grafik-button'); +}); + +it('displays chart button', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/keluarga') + ->assertVisible('@bt-chart'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-keluarga-chart-button'); +}); + +it('displays datatable', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/keluarga') + ->assertVisible('@datatable-statistik'); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const rows = document.querySelectorAll('#tabel-data tbody tr'); + if (rows.length > 0 && !rows[0].classList.contains('dataTables_empty')) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })", + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-keluarga-datatable'); +}); + +it('accesses a kategori statistik and loads data', function () use ($defaultId) { + $page = SessionState::loginAndNavigate($this->user, '/statistik/keluarga') + ->assertVisible('@daftar-statistik'); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const items = document.querySelectorAll('#daftar-statistik .pilih-kategori'); + if (items.length > 0) { resolve(true); } else { setTimeout(check, 500); } + }; + check(); + })", + true + ); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const rows = document.querySelectorAll('#tabel-data tbody tr'); + if (rows.length > 0 && !rows[0].classList.contains('dataTables_empty')) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })", + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-keluarga-kategori-data'); +}); + +it('accesses grafik and renders successfully', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/keluarga') + ->assertVisible('@bt-grafik'); + + $page->click('@bt-grafik'); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const container = document.querySelector('#grafik-statistik'); + const canvas = document.querySelector('#barChart'); + if (container && canvas && canvas.getContext && canvas.width > 0) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })", + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-keluarga-grafik'); +}); + +it('accesses chart and renders successfully', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/keluarga') + ->assertVisible('@bt-chart'); + + $page->click('@bt-chart'); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const container = document.querySelector('#pie-statistik'); + const canvas = document.querySelector('#donutChart'); + if (container && canvas && canvas.getContext && canvas.width > 0) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })", + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-keluarga-chart'); +}); + +it('has no javascript errors', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/keluarga') + ->assertPathIs('/statistik/keluarga') + ->assertNoJavaScriptErrors(); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-keluarga-no-errors'); +}); diff --git a/tests/Browser/SmokeStatistikPendudukTest.php b/tests/Browser/SmokeStatistikPendudukTest.php new file mode 100644 index 00000000..c646a67b --- /dev/null +++ b/tests/Browser/SmokeStatistikPendudukTest.php @@ -0,0 +1,191 @@ +user = SessionState::loginAdminUser(); +}); + +afterEach(function () { + SessionState::clear(); +}); + +$kategoriPenduduk = FixtureReader::kategoriStatistikPendudukNames(); +$defaultId = 'rentang-umur'; + +it('opens the statistik penduduk page', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/penduduk') + ->assertPathIs('/statistik/penduduk') + ->assertSee('Data Statistik Penduduk'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-penduduk-page'); +}); + +it('displays kategori statistik list', function () use ($kategoriPenduduk) { + $page = SessionState::loginAndNavigate($this->user, '/statistik/penduduk') + ->assertVisible('@daftar-statistik'); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const items = document.querySelectorAll('#daftar-statistik .pilih-kategori'); + if (items.length > 0) { resolve(true); } else { setTimeout(check, 500); } + }; + check(); + })", + true + ); + + foreach ($kategoriPenduduk as $nama) { + $escaped = addslashes($nama); + $page->assertScript( + "Array.from(document.querySelectorAll('#daftar-statistik .pilih-kategori a')).some(a => a.textContent.trim().includes('{$escaped}'))", + true + ); + } + + ScreenshotHelper::saveIfEnabled($page, 'statistik-penduduk-kategori-list'); +}); + +it('displays cetak button', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/penduduk') + ->assertVisible('@bt-cetak'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-penduduk-cetak-button'); +}); + +it('displays excel button', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/penduduk') + ->assertVisible('@bt-excel'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-penduduk-excel-button'); +}); + +it('displays grafik button', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/penduduk') + ->assertVisible('@bt-grafik'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-penduduk-grafik-button'); +}); + +it('displays chart button', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/penduduk') + ->assertVisible('@bt-chart'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-penduduk-chart-button'); +}); + +it('displays datatable', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/penduduk') + ->assertVisible('@datatable-statistik'); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const rows = document.querySelectorAll('#tabel-data tbody tr'); + if (rows.length > 0 && !rows[0].classList.contains('dataTables_empty')) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })", + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-penduduk-datatable'); +}); + +it('accesses a kategori statistik and loads data', function () use ($defaultId) { + $page = SessionState::loginAndNavigate($this->user, '/statistik/penduduk') + ->assertVisible('@daftar-statistik'); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const items = document.querySelectorAll('#daftar-statistik .pilih-kategori'); + if (items.length > 0) { resolve(true); } else { setTimeout(check, 500); } + }; + check(); + })", + true + ); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const rows = document.querySelectorAll('#tabel-data tbody tr'); + if (rows.length > 0 && !rows[0].classList.contains('dataTables_empty')) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })", + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-penduduk-kategori-data'); +}); + +it('accesses grafik and renders successfully', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/penduduk') + ->assertVisible('@bt-grafik'); + + $page->click('@bt-grafik'); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const container = document.querySelector('#grafik-statistik'); + const canvas = document.querySelector('#barChart'); + if (container && canvas && canvas.getContext && canvas.width > 0) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })", + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-penduduk-grafik'); +}); + +it('accesses chart and renders successfully', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/penduduk') + ->assertVisible('@bt-chart'); + + $page->click('@bt-chart'); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const container = document.querySelector('#pie-statistik'); + const canvas = document.querySelector('#donutChart'); + if (container && canvas && canvas.getContext && canvas.width > 0) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })", + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-penduduk-chart'); +}); + +it('has no javascript errors', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/penduduk') + ->assertPathIs('/statistik/penduduk') + ->assertNoJavaScriptErrors(); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-penduduk-no-errors'); +}); diff --git a/tests/Browser/SmokeStatistikRtmTest.php b/tests/Browser/SmokeStatistikRtmTest.php new file mode 100644 index 00000000..23cc51a9 --- /dev/null +++ b/tests/Browser/SmokeStatistikRtmTest.php @@ -0,0 +1,191 @@ +user = SessionState::loginAdminUser(); +}); + +afterEach(function () { + SessionState::clear(); +}); + +$kategoriRtm = FixtureReader::kategoriStatistikRtmNames(); +$defaultId = 'bdt'; + +it('opens the statistik rtm page', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/rtm') + ->assertPathIs('/statistik/rtm') + ->assertSee('Data Statistik RTM'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-rtm-page'); +}); + +it('displays kategori statistik list', function () use ($kategoriRtm) { + $page = SessionState::loginAndNavigate($this->user, '/statistik/rtm') + ->assertVisible('@daftar-statistik'); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const items = document.querySelectorAll('#daftar-statistik .pilih-kategori'); + if (items.length > 0) { resolve(true); } else { setTimeout(check, 500); } + }; + check(); + })", + true + ); + + foreach ($kategoriRtm as $nama) { + $escaped = addslashes($nama); + $page->assertScript( + "Array.from(document.querySelectorAll('#daftar-statistik .pilih-kategori a')).some(a => a.textContent.trim().includes('{$escaped}'))", + true + ); + } + + ScreenshotHelper::saveIfEnabled($page, 'statistik-rtm-kategori-list'); +}); + +it('displays cetak button', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/rtm') + ->assertVisible('@bt-cetak'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-rtm-cetak-button'); +}); + +it('displays excel button', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/rtm') + ->assertVisible('@bt-excel'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-rtm-excel-button'); +}); + +it('displays grafik button', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/rtm') + ->assertVisible('@bt-grafik'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-rtm-grafik-button'); +}); + +it('displays chart button', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/rtm') + ->assertVisible('@bt-chart'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-rtm-chart-button'); +}); + +it('displays datatable', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/rtm') + ->assertVisible('@datatable-statistik'); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const rows = document.querySelectorAll('#tabel-data tbody tr'); + if (rows.length > 0 && !rows[0].classList.contains('dataTables_empty')) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })", + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-rtm-datatable'); +}); + +it('accesses a kategori statistik and loads data', function () use ($defaultId) { + $page = SessionState::loginAndNavigate($this->user, '/statistik/rtm') + ->assertVisible('@daftar-statistik'); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const items = document.querySelectorAll('#daftar-statistik .pilih-kategori'); + if (items.length > 0) { resolve(true); } else { setTimeout(check, 500); } + }; + check(); + })", + true + ); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const rows = document.querySelectorAll('#tabel-data tbody tr'); + if (rows.length > 0 && !rows[0].classList.contains('dataTables_empty')) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })", + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-rtm-kategori-data'); +}); + +it('accesses grafik and renders successfully', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/rtm') + ->assertVisible('@bt-grafik'); + + $page->click('@bt-grafik'); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const container = document.querySelector('#grafik-statistik'); + const canvas = document.querySelector('#barChart'); + if (container && canvas && canvas.getContext && canvas.width > 0) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })", + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-rtm-grafik'); +}); + +it('accesses chart and renders successfully', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/rtm') + ->assertVisible('@bt-chart'); + + $page->click('@bt-chart'); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const container = document.querySelector('#pie-statistik'); + const canvas = document.querySelector('#donutChart'); + if (container && canvas && canvas.getContext && canvas.width > 0) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })", + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-rtm-chart'); +}); + +it('has no javascript errors', function () { + $page = SessionState::loginAndNavigate($this->user, '/statistik/rtm') + ->assertPathIs('/statistik/rtm') + ->assertNoJavaScriptErrors(); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-rtm-no-errors'); +}); diff --git a/tests/Browser/fixtures/kategori-statistik-bantuan.json b/tests/Browser/fixtures/kategori-statistik-bantuan.json new file mode 100644 index 00000000..eb2d059b --- /dev/null +++ b/tests/Browser/fixtures/kategori-statistik-bantuan.json @@ -0,0 +1,7 @@ +{ + "success": true, + "data": [ + {"id": "penduduk", "nama": "Penerima Bantuan Penduduk", "judul_halaman": "Penerima Bantuan Penduduk", "judul_kolom_nama": "Jenis Kelompok"}, + {"id": "keluarga", "nama": "Penerima Bantuan Keluarga", "judul_halaman": "Penerima Bantuan Keluarga", "judul_kolom_nama": "Jenis Kelompok"} + ] +} diff --git a/tests/Browser/fixtures/kategori-statistik-keluarga.json b/tests/Browser/fixtures/kategori-statistik-keluarga.json new file mode 100644 index 00000000..076d3460 --- /dev/null +++ b/tests/Browser/fixtures/kategori-statistik-keluarga.json @@ -0,0 +1,6 @@ +{ + "success": true, + "data": [ + {"id": "kelas-sosial", "nama": "Kelas Sosial", "judul_halaman": "Keluarga", "judul_kolom_nama": "Jenis Kelompok"} + ] +} diff --git a/tests/Browser/fixtures/kategori-statistik-penduduk.json b/tests/Browser/fixtures/kategori-statistik-penduduk.json new file mode 100644 index 00000000..91083fa9 --- /dev/null +++ b/tests/Browser/fixtures/kategori-statistik-penduduk.json @@ -0,0 +1,12 @@ +{ + "success": true, + "data": [ + {"id": "rentang-umur", "nama": "Rentang Umur", "judul_halaman": "Penduduk", "judul_kolom_nama": "Jenis Kelompok"}, + {"id": "kategori-umur", "nama": "Kategori Umur", "judul_halaman": "Penduduk", "judul_kolom_nama": "Jenis Kelompok"}, + {"id": "pendidikan-dalam-kk", "nama": "Pendidikan Dalam KK", "judul_halaman": "Penduduk", "judul_kolom_nama": "Jenis Kelompok"}, + {"id": "pekerjaan", "nama": "Pekerjaan", "judul_halaman": "Penduduk", "judul_kolom_nama": "Jenis Kelompok"}, + {"id": "status-perkawinan", "nama": "Status Perkawinan", "judul_halaman": "Penduduk", "judul_kolom_nama": "Jenis Kelompok"}, + {"id": "agama", "nama": "Agama", "judul_halaman": "Penduduk", "judul_kolom_nama": "Jenis Kelompok"}, + {"id": "jenis-kelamin", "nama": "Jenis Kelamin", "judul_halaman": "Penduduk", "judul_kolom_nama": "Jenis Kelompok"} + ] +} diff --git a/tests/Browser/fixtures/kategori-statistik-rtm.json b/tests/Browser/fixtures/kategori-statistik-rtm.json new file mode 100644 index 00000000..f2eaac27 --- /dev/null +++ b/tests/Browser/fixtures/kategori-statistik-rtm.json @@ -0,0 +1,6 @@ +{ + "success": true, + "data": [ + {"id": "bdt", "nama": "BDT", "judul_halaman": "RTM", "judul_kolom_nama": "Jenis Kelompok"} + ] +} diff --git a/tests/Browser/fixtures/statistik-bantuan-keluarga.json b/tests/Browser/fixtures/statistik-bantuan-keluarga.json new file mode 100644 index 00000000..83b2869b --- /dev/null +++ b/tests/Browser/fixtures/statistik-bantuan-keluarga.json @@ -0,0 +1,46 @@ +{ + "draw": 1, + "recordsTotal": 3, + "recordsFiltered": 3, + "data": [ + { + "id": "1", + "attributes": { + "nama": "PKH", + "jumlah": 200, + "persentase_jumlah": "40.00%", + "laki_laki": 90, + "persentase_laki_laki": "45.00%", + "perempuan": 110, + "persentase_perempuan": "55.00%", + "kriteria": "{\"bantuan\":\"pkh\"}" + } + }, + { + "id": "2", + "attributes": { + "nama": "BLT DD", + "jumlah": 300, + "persentase_jumlah": "60.00%", + "laki_laki": 140, + "persentase_laki_laki": "46.67%", + "perempuan": 160, + "persentase_perempuan": "53.33%", + "kriteria": "{\"bantuan\":\"blt-dd\"}" + } + }, + { + "id": "3", + "attributes": { + "nama": "JUMLAH", + "jumlah": 500, + "persentase_jumlah": "100.00%", + "laki_laki": 230, + "persentase_laki_laki": "46.00%", + "perempuan": 270, + "persentase_perempuan": "54.00%", + "kriteria": "{}" + } + } + ] +} diff --git a/tests/Browser/fixtures/statistik-bantuan-penduduk.json b/tests/Browser/fixtures/statistik-bantuan-penduduk.json new file mode 100644 index 00000000..7493aac1 --- /dev/null +++ b/tests/Browser/fixtures/statistik-bantuan-penduduk.json @@ -0,0 +1,46 @@ +{ + "draw": 1, + "recordsTotal": 3, + "recordsFiltered": 3, + "data": [ + { + "id": "1", + "attributes": { + "nama": "BPNT", + "jumlah": 500, + "persentase_jumlah": "25.00%", + "laki_laki": 230, + "persentase_laki_laki": "46.00%", + "perempuan": 270, + "persentase_perempuan": "54.00%", + "kriteria": "{\"bantuan\":\"bpnt\"}" + } + }, + { + "id": "2", + "attributes": { + "nama": "BST", + "jumlah": 300, + "persentase_jumlah": "15.00%", + "laki_laki": 140, + "persentase_laki_laki": "46.67%", + "perempuan": 160, + "persentase_perempuan": "53.33%", + "kriteria": "{\"bantuan\":\"bst\"}" + } + }, + { + "id": "3", + "attributes": { + "nama": "JUMLAH", + "jumlah": 800, + "persentase_jumlah": "100.00%", + "laki_laki": 370, + "persentase_laki_laki": "46.25%", + "perempuan": 430, + "persentase_perempuan": "53.75%", + "kriteria": "{}" + } + } + ] +} diff --git a/tests/Browser/fixtures/statistik-keluarga-kelas-sosial.json b/tests/Browser/fixtures/statistik-keluarga-kelas-sosial.json new file mode 100644 index 00000000..2ec8f979 --- /dev/null +++ b/tests/Browser/fixtures/statistik-keluarga-kelas-sosial.json @@ -0,0 +1,72 @@ +{ + "draw": 1, + "recordsTotal": 5, + "recordsFiltered": 5, + "data": [ + { + "id": "1", + "attributes": { + "nama": "Miskin", + "jumlah": 1500, + "persentase_jumlah": "15.00%", + "laki_laki": 700, + "persentase_laki_laki": "46.67%", + "perempuan": 800, + "persentase_perempuan": "53.33%", + "kriteria": "{\"kelas_sosial\":\"miskin\"}" + } + }, + { + "id": "2", + "attributes": { + "nama": "Rentan Miskin", + "jumlah": 2000, + "persentase_jumlah": "20.00%", + "laki_laki": 950, + "persentase_laki_laki": "47.50%", + "perempuan": 1050, + "persentase_perempuan": "52.50%", + "kriteria": "{\"kelas_sosial\":\"rentan-miskin\"}" + } + }, + { + "id": "3", + "attributes": { + "nama": "Menengah", + "jumlah": 4500, + "persentase_jumlah": "45.00%", + "laki_laki": 2200, + "persentase_laki_laki": "48.89%", + "perempuan": 2300, + "persentase_perempuan": "51.11%", + "kriteria": "{\"kelas_sosial\":\"menengah\"}" + } + }, + { + "id": "4", + "attributes": { + "nama": "Mampu", + "jumlah": 2000, + "persentase_jumlah": "20.00%", + "laki_laki": 1000, + "persentase_laki_laki": "50.00%", + "perempuan": 1000, + "persentase_perempuan": "50.00%", + "kriteria": "{\"kelas_sosial\":\"mampu\"}" + } + }, + { + "id": "5", + "attributes": { + "nama": "JUMLAH", + "jumlah": 10000, + "persentase_jumlah": "100.00%", + "laki_laki": 4850, + "persentase_laki_laki": "48.50%", + "perempuan": 5150, + "persentase_perempuan": "51.50%", + "kriteria": "{}" + } + } + ] +} diff --git a/tests/Browser/fixtures/statistik-penduduk-agama.json b/tests/Browser/fixtures/statistik-penduduk-agama.json index dad7c074..f7ef08bc 100644 --- a/tests/Browser/fixtures/statistik-penduduk-agama.json +++ b/tests/Browser/fixtures/statistik-penduduk-agama.json @@ -1,27 +1,71 @@ { + "draw": 1, + "recordsTotal": 6, + "recordsFiltered": 6, "data": [ { + "id": "1", "attributes": { "nama": "Islam", - "jumlah": 20000 + "jumlah": 20000, + "laki_laki": 10000, + "perempuan": 10000, + "persentase_jumlah": "80.00%", + "persentase_laki_laki": "40.00%", + "persentase_perempuan": "40.00%", + "kriteria": "{\"filter[id]\":\"agama\",\"filter[kriteria]\":\"Islam\"}" } }, { + "id": "2", "attributes": { "nama": "Kristen", - "jumlah": 2000 + "jumlah": 2000, + "laki_laki": 1000, + "perempuan": 1000, + "persentase_jumlah": "8.00%", + "persentase_laki_laki": "4.00%", + "persentase_perempuan": "4.00%", + "kriteria": "{\"filter[id]\":\"agama\",\"filter[kriteria]\":\"Kristen\"}" } }, { + "id": "3", "attributes": { "nama": "Hindu", - "jumlah": 2500 + "jumlah": 2500, + "laki_laki": 1200, + "perempuan": 1300, + "persentase_jumlah": "10.00%", + "persentase_laki_laki": "4.80%", + "persentase_perempuan": "5.20%", + "kriteria": "{\"filter[id]\":\"agama\",\"filter[kriteria]\":\"Hindu\"}" } }, { + "id": "4", "attributes": { "nama": "Buddha", - "jumlah": 500 + "jumlah": 500, + "laki_laki": 250, + "perempuan": 250, + "persentase_jumlah": "2.00%", + "persentase_laki_laki": "1.00%", + "persentase_perempuan": "1.00%", + "kriteria": "{\"filter[id]\":\"agama\",\"filter[kriteria]\":\"Buddha\"}" + } + }, + { + "id": "5", + "attributes": { + "nama": "JUMLAH", + "jumlah": 25000, + "laki_laki": 12500, + "perempuan": 12500, + "persentase_jumlah": "100.00%", + "persentase_laki_laki": "50.00%", + "persentase_perempuan": "50.00%", + "kriteria": "{}" } } ] diff --git a/tests/Browser/fixtures/statistik-penduduk-golongan-darah.json b/tests/Browser/fixtures/statistik-penduduk-golongan-darah.json index 5bb5bb3d..4cbb76c8 100644 --- a/tests/Browser/fixtures/statistik-penduduk-golongan-darah.json +++ b/tests/Browser/fixtures/statistik-penduduk-golongan-darah.json @@ -1,27 +1,71 @@ { + "draw": 1, + "recordsTotal": 4, + "recordsFiltered": 4, "data": [ { + "id": "1", "attributes": { "nama": "A", - "jumlah": 6000 + "jumlah": 6000, + "laki_laki": 3000, + "perempuan": 3000, + "persentase_jumlah": "24.00%", + "persentase_laki_laki": "12.00%", + "persentase_perempuan": "12.00%", + "kriteria": "{\"filter[id]\":\"golongan-darah\",\"filter[kriteria]\":\"A\"}" } }, { + "id": "2", "attributes": { "nama": "B", - "jumlah": 5000 + "jumlah": 5000, + "laki_laki": 2500, + "perempuan": 2500, + "persentase_jumlah": "20.00%", + "persentase_laki_laki": "10.00%", + "persentase_perempuan": "10.00%", + "kriteria": "{\"filter[id]\":\"golongan-darah\",\"filter[kriteria]\":\"B\"}" } }, { + "id": "3", "attributes": { "nama": "O", - "jumlah": 10000 + "jumlah": 10000, + "laki_laki": 5000, + "perempuan": 5000, + "persentase_jumlah": "40.00%", + "persentase_laki_laki": "20.00%", + "persentase_perempuan": "20.00%", + "kriteria": "{\"filter[id]\":\"golongan-darah\",\"filter[kriteria]\":\"O\"}" } }, { + "id": "4", "attributes": { "nama": "AB", - "jumlah": 4000 + "jumlah": 4000, + "laki_laki": 2000, + "perempuan": 2000, + "persentase_jumlah": "16.00%", + "persentase_laki_laki": "8.00%", + "persentase_perempuan": "8.00%", + "kriteria": "{\"filter[id]\":\"golongan-darah\",\"filter[kriteria]\":\"AB\"}" + } + }, + { + "id": "5", + "attributes": { + "nama": "JUMLAH", + "jumlah": 25000, + "laki_laki": 12500, + "perempuan": 12500, + "persentase_jumlah": "100.00%", + "persentase_laki_laki": "50.00%", + "persentase_perempuan": "50.00%", + "kriteria": "{}" } } ] diff --git a/tests/Browser/fixtures/statistik-penduduk-jenis-kelamin.json b/tests/Browser/fixtures/statistik-penduduk-jenis-kelamin.json index 64980120..84695477 100644 --- a/tests/Browser/fixtures/statistik-penduduk-jenis-kelamin.json +++ b/tests/Browser/fixtures/statistik-penduduk-jenis-kelamin.json @@ -1,15 +1,32 @@ { + "draw": 1, + "recordsTotal": 2, + "recordsFiltered": 2, "data": [ { + "id": "1", "attributes": { "nama": "Laki-Laki", - "jumlah": 12500 + "jumlah": 12500, + "laki_laki": 12500, + "perempuan": 0, + "persentase_jumlah": "50.00%", + "persentase_laki_laki": "50.00%", + "persentase_perempuan": "0.00%", + "kriteria": "{\"filter[id]\":\"jenis-kelamin\",\"filter[kriteria]\":\"Laki-Laki\"}" } }, { + "id": "2", "attributes": { "nama": "Perempuan", - "jumlah": 12500 + "jumlah": 12500, + "laki_laki": 0, + "perempuan": 12500, + "persentase_jumlah": "50.00%", + "persentase_laki_laki": "0.00%", + "persentase_perempuan": "50.00%", + "kriteria": "{\"filter[id]\":\"jenis-kelamin\",\"filter[kriteria]\":\"Perempuan\"}" } } ] diff --git a/tests/Browser/fixtures/statistik-penduduk-pendidikan-dalam-kk.json b/tests/Browser/fixtures/statistik-penduduk-pendidikan-dalam-kk.json index a77fa1d3..b1ad200c 100644 --- a/tests/Browser/fixtures/statistik-penduduk-pendidikan-dalam-kk.json +++ b/tests/Browser/fixtures/statistik-penduduk-pendidikan-dalam-kk.json @@ -1,51 +1,123 @@ { + "draw": 1, + "recordsTotal": 8, + "recordsFiltered": 8, "data": [ { + "id": "1", "attributes": { "nama": "Tidak Sekolah", - "jumlah": 1500 + "jumlah": 1500, + "laki_laki": 800, + "perempuan": 700, + "persentase_jumlah": "6.00%", + "persentase_laki_laki": "3.20%", + "persentase_perempuan": "2.80%", + "kriteria": "{\"filter[id]\":\"pendidikan-dalam-kk\",\"filter[kriteria]\":\"Tidak Sekolah\"}" } }, { + "id": "2", "attributes": { "nama": "Tidak Tamat SD", - "jumlah": 2000 + "jumlah": 2000, + "laki_laki": 1100, + "perempuan": 900, + "persentase_jumlah": "8.00%", + "persentase_laki_laki": "4.40%", + "persentase_perempuan": "3.60%", + "kriteria": "{\"filter[id]\":\"pendidikan-dalam-kk\",\"filter[kriteria]\":\"Tidak Tamat SD\"}" } }, { + "id": "3", "attributes": { "nama": "Tamat SD", - "jumlah": 5000 + "jumlah": 5000, + "laki_laki": 2500, + "perempuan": 2500, + "persentase_jumlah": "20.00%", + "persentase_laki_laki": "10.00%", + "persentase_perempuan": "10.00%", + "kriteria": "{\"filter[id]\":\"pendidikan-dalam-kk\",\"filter[kriteria]\":\"Tamat SD\"}" } }, { + "id": "4", "attributes": { "nama": "SLTP", - "jumlah": 4500 + "jumlah": 4500, + "laki_laki": 2300, + "perempuan": 2200, + "persentase_jumlah": "18.00%", + "persentase_laki_laki": "9.20%", + "persentase_perempuan": "8.80%", + "kriteria": "{\"filter[id]\":\"pendidikan-dalam-kk\",\"filter[kriteria]\":\"SLTP\"}" } }, { + "id": "5", "attributes": { "nama": "SLTA", - "jumlah": 6000 + "jumlah": 6000, + "laki_laki": 3000, + "perempuan": 3000, + "persentase_jumlah": "24.00%", + "persentase_laki_laki": "12.00%", + "persentase_perempuan": "12.00%", + "kriteria": "{\"filter[id]\":\"pendidikan-dalam-kk\",\"filter[kriteria]\":\"SLTA\"}" } }, { + "id": "6", "attributes": { "nama": "Diploma", - "jumlah": 2000 + "jumlah": 2000, + "laki_laki": 1000, + "perempuan": 1000, + "persentase_jumlah": "8.00%", + "persentase_laki_laki": "4.00%", + "persentase_perempuan": "4.00%", + "kriteria": "{\"filter[id]\":\"pendidikan-dalam-kk\",\"filter[kriteria]\":\"Diploma\"}" } }, { + "id": "7", "attributes": { "nama": "Sarjana", - "jumlah": 3000 + "jumlah": 3000, + "laki_laki": 1500, + "perempuan": 1500, + "persentase_jumlah": "12.00%", + "persentase_laki_laki": "6.00%", + "persentase_perempuan": "6.00%", + "kriteria": "{\"filter[id]\":\"pendidikan-dalam-kk\",\"filter[kriteria]\":\"Sarjana\"}" } }, { + "id": "8", "attributes": { "nama": "Pasca Sarjana", - "jumlah": 1000 + "jumlah": 1000, + "laki_laki": 500, + "perempuan": 500, + "persentase_jumlah": "4.00%", + "persentase_laki_laki": "2.00%", + "persentase_perempuan": "2.00%", + "kriteria": "{\"filter[id]\":\"pendidikan-dalam-kk\",\"filter[kriteria]\":\"Pasca Sarjana\"}" + } + }, + { + "id": "9", + "attributes": { + "nama": "JUMLAH", + "jumlah": 25000, + "laki_laki": 12500, + "perempuan": 12500, + "persentase_jumlah": "100.00%", + "persentase_laki_laki": "50.00%", + "persentase_perempuan": "50.00%", + "kriteria": "{}" } } ] diff --git a/tests/Browser/fixtures/statistik-penduduk-penyakit-menahun.json b/tests/Browser/fixtures/statistik-penduduk-penyakit-menahun.json index 6f3b08e2..173624f3 100644 --- a/tests/Browser/fixtures/statistik-penduduk-penyakit-menahun.json +++ b/tests/Browser/fixtures/statistik-penduduk-penyakit-menahun.json @@ -1,27 +1,71 @@ { + "draw": 1, + "recordsTotal": 4, + "recordsFiltered": 4, "data": [ { + "id": "1", "attributes": { "nama": "Diabetes", - "jumlah": 500 + "jumlah": 500, + "laki_laki": 250, + "perempuan": 250, + "persentase_jumlah": "2.00%", + "persentase_laki_laki": "1.00%", + "persentase_perempuan": "1.00%", + "kriteria": "{\"filter[id]\":\"penyakit-menahun\",\"filter[kriteria]\":\"Diabetes\"}" } }, { + "id": "2", "attributes": { "nama": "Hipertensi", - "jumlah": 800 + "jumlah": 800, + "laki_laki": 400, + "perempuan": 400, + "persentase_jumlah": "3.20%", + "persentase_laki_laki": "1.60%", + "persentase_perempuan": "1.60%", + "kriteria": "{\"filter[id]\":\"penyakit-menahun\",\"filter[kriteria]\":\"Hipertensi\"}" } }, { + "id": "3", "attributes": { "nama": "Jantung", - "jumlah": 300 + "jumlah": 300, + "laki_laki": 180, + "perempuan": 120, + "persentase_jumlah": "1.20%", + "persentase_laki_laki": "0.72%", + "persentase_perempuan": "0.48%", + "kriteria": "{\"filter[id]\":\"penyakit-menahun\",\"filter[kriteria]\":\"Jantung\"}" } }, { + "id": "4", "attributes": { "nama": "Stroke", - "jumlah": 200 + "jumlah": 200, + "laki_laki": 120, + "perempuan": 80, + "persentase_jumlah": "0.80%", + "persentase_laki_laki": "0.48%", + "persentase_perempuan": "0.32%", + "kriteria": "{\"filter[id]\":\"penyakit-menahun\",\"filter[kriteria]\":\"Stroke\"}" + } + }, + { + "id": "5", + "attributes": { + "nama": "JUMLAH", + "jumlah": 25000, + "laki_laki": 12500, + "perempuan": 12500, + "persentase_jumlah": "100.00%", + "persentase_laki_laki": "50.00%", + "persentase_perempuan": "50.00%", + "kriteria": "{}" } } ] diff --git a/tests/Browser/fixtures/statistik-penduduk-penyandang-cacat.json b/tests/Browser/fixtures/statistik-penduduk-penyandang-cacat.json index da7f34a9..958da611 100644 --- a/tests/Browser/fixtures/statistik-penduduk-penyandang-cacat.json +++ b/tests/Browser/fixtures/statistik-penduduk-penyandang-cacat.json @@ -1,27 +1,71 @@ { + "draw": 1, + "recordsTotal": 4, + "recordsFiltered": 4, "data": [ { + "id": "1", "attributes": { "nama": "Fisik", - "jumlah": 150 + "jumlah": 150, + "laki_laki": 80, + "perempuan": 70, + "persentase_jumlah": "0.60%", + "persentase_laki_laki": "0.32%", + "persentase_perempuan": "0.28%", + "kriteria": "{\"filter[id]\":\"penyandang-cacat\",\"filter[kriteria]\":\"Fisik\"}" } }, { + "id": "2", "attributes": { "nama": "Mental", - "jumlah": 50 + "jumlah": 50, + "laki_laki": 30, + "perempuan": 20, + "persentase_jumlah": "0.20%", + "persentase_laki_laki": "0.12%", + "persentase_perempuan": "0.08%", + "kriteria": "{\"filter[id]\":\"penyandang-cacat\",\"filter[kriteria]\":\"Mental\"}" } }, { + "id": "3", "attributes": { "nama": "Sensorik", - "jumlah": 100 + "jumlah": 100, + "laki_laki": 55, + "perempuan": 45, + "persentase_jumlah": "0.40%", + "persentase_laki_laki": "0.22%", + "persentase_perempuan": "0.18%", + "kriteria": "{\"filter[id]\":\"penyandang-cacat\",\"filter[kriteria]\":\"Sensorik\"}" } }, { + "id": "4", "attributes": { "nama": "Ganda", - "jumlah": 30 + "jumlah": 30, + "laki_laki": 18, + "perempuan": 12, + "persentase_jumlah": "0.12%", + "persentase_laki_laki": "0.07%", + "persentase_perempuan": "0.05%", + "kriteria": "{\"filter[id]\":\"penyandang-cacat\",\"filter[kriteria]\":\"Ganda\"}" + } + }, + { + "id": "5", + "attributes": { + "nama": "JUMLAH", + "jumlah": 25000, + "laki_laki": 12500, + "perempuan": 12500, + "persentase_jumlah": "100.00%", + "persentase_laki_laki": "50.00%", + "persentase_perempuan": "50.00%", + "kriteria": "{}" } } ] diff --git a/tests/Browser/fixtures/statistik-penduduk-rentang-umur.json b/tests/Browser/fixtures/statistik-penduduk-rentang-umur.json index 523e29b0..f4edc233 100644 --- a/tests/Browser/fixtures/statistik-penduduk-rentang-umur.json +++ b/tests/Browser/fixtures/statistik-penduduk-rentang-umur.json @@ -1,51 +1,84 @@ { + "draw": 1, + "recordsTotal": 8, + "recordsFiltered": 8, "data": [ { + "id": "1", "attributes": { - "nama": "0-5 Tahun", - "jumlah": 2000 + "nama": "0-4 Tahun", + "jumlah": 1500, + "laki_laki": 780, + "perempuan": 720, + "persentase_jumlah": "6.00%", + "persentase_laki_laki": "3.12%", + "persentase_perempuan": "2.88%", + "kriteria": "{\"filter[id]\":\"rentang-umur\",\"filter[kriteria]\":\"0-4 Tahun\"}" } }, { + "id": "2", "attributes": { - "nama": "6-12 Tahun", - "jumlah": 3000 + "nama": "5-9 Tahun", + "jumlah": 1800, + "laki_laki": 920, + "perempuan": 880, + "persentase_jumlah": "7.20%", + "persentase_laki_laki": "3.68%", + "persentase_perempuan": "3.52%", + "kriteria": "{\"filter[id]\":\"rentang-umur\",\"filter[kriteria]\":\"5-9 Tahun\"}" } }, { + "id": "3", "attributes": { - "nama": "13-18 Tahun", - "jumlah": 3500 + "nama": "10-14 Tahun", + "jumlah": 2000, + "laki_laki": 1020, + "perempuan": 980, + "persentase_jumlah": "8.00%", + "persentase_laki_laki": "4.08%", + "persentase_perempuan": "3.92%", + "kriteria": "{\"filter[id]\":\"rentang-umur\",\"filter[kriteria]\":\"10-14 Tahun\"}" } }, { + "id": "4", "attributes": { - "nama": "19-25 Tahun", - "jumlah": 4000 + "nama": "15-19 Tahun", + "jumlah": 2200, + "laki_laki": 1100, + "perempuan": 1100, + "persentase_jumlah": "8.80%", + "persentase_laki_laki": "4.40%", + "persentase_perempuan": "4.40%", + "kriteria": "{\"filter[id]\":\"rentang-umur\",\"filter[kriteria]\":\"15-19 Tahun\"}" } }, { + "id": "5", "attributes": { - "nama": "26-35 Tahun", - "jumlah": 5000 + "nama": "20-24 Tahun", + "jumlah": 2500, + "laki_laki": 1200, + "perempuan": 1300, + "persentase_jumlah": "10.00%", + "persentase_laki_laki": "4.80%", + "persentase_perempuan": "5.20%", + "kriteria": "{\"filter[id]\":\"rentang-umur\",\"filter[kriteria]\":\"20-24 Tahun\"}" } }, { + "id": "6", "attributes": { - "nama": "36-50 Tahun", - "jumlah": 4500 - } - }, - { - "attributes": { - "nama": "51-65 Tahun", - "jumlah": 2000 - } - }, - { - "attributes": { - "nama": "> 65 Tahun", - "jumlah": 1000 + "nama": "JUMLAH", + "jumlah": 25000, + "laki_laki": 12500, + "perempuan": 12500, + "persentase_jumlah": "100.00%", + "persentase_laki_laki": "50.00%", + "persentase_perempuan": "50.00%", + "kriteria": "{}" } } ] diff --git a/tests/Browser/fixtures/statistik-penduduk-status-perkawinan.json b/tests/Browser/fixtures/statistik-penduduk-status-perkawinan.json index c7615e16..c8862bec 100644 --- a/tests/Browser/fixtures/statistik-penduduk-status-perkawinan.json +++ b/tests/Browser/fixtures/statistik-penduduk-status-perkawinan.json @@ -1,27 +1,71 @@ { + "draw": 1, + "recordsTotal": 4, + "recordsFiltered": 4, "data": [ { + "id": "1", "attributes": { "nama": "Belum Kawin", - "jumlah": 8000 + "jumlah": 8000, + "laki_laki": 4000, + "perempuan": 4000, + "persentase_jumlah": "32.00%", + "persentase_laki_laki": "16.00%", + "persentase_perempuan": "16.00%", + "kriteria": "{\"filter[id]\":\"status-perkawinan\",\"filter[kriteria]\":\"Belum Kawin\"}" } }, { + "id": "2", "attributes": { "nama": "Kawin", - "jumlah": 14000 + "jumlah": 14000, + "laki_laki": 7000, + "perempuan": 7000, + "persentase_jumlah": "56.00%", + "persentase_laki_laki": "28.00%", + "persentase_perempuan": "28.00%", + "kriteria": "{\"filter[id]\":\"status-perkawinan\",\"filter[kriteria]\":\"Kawin\"}" } }, { + "id": "3", "attributes": { "nama": "Cerai Hidup", - "jumlah": 1500 + "jumlah": 1500, + "laki_laki": 700, + "perempuan": 800, + "persentase_jumlah": "6.00%", + "persentase_laki_laki": "2.80%", + "persentase_perempuan": "3.20%", + "kriteria": "{\"filter[id]\":\"status-perkawinan\",\"filter[kriteria]\":\"Cerai Hidup\"}" } }, { + "id": "4", "attributes": { "nama": "Cerai Mati", - "jumlah": 1500 + "jumlah": 1500, + "laki_laki": 800, + "perempuan": 700, + "persentase_jumlah": "6.00%", + "persentase_laki_laki": "3.20%", + "persentase_perempuan": "2.80%", + "kriteria": "{\"filter[id]\":\"status-perkawinan\",\"filter[kriteria]\":\"Cerai Mati\"}" + } + }, + { + "id": "5", + "attributes": { + "nama": "JUMLAH", + "jumlah": 25000, + "laki_laki": 12500, + "perempuan": 12500, + "persentase_jumlah": "100.00%", + "persentase_laki_laki": "50.00%", + "persentase_perempuan": "50.00%", + "kriteria": "{}" } } ] diff --git a/tests/Browser/fixtures/statistik-penduduk-suku.json b/tests/Browser/fixtures/statistik-penduduk-suku.json index 79374be9..430b5c9d 100644 --- a/tests/Browser/fixtures/statistik-penduduk-suku.json +++ b/tests/Browser/fixtures/statistik-penduduk-suku.json @@ -1,33 +1,84 @@ { + "draw": 1, + "recordsTotal": 5, + "recordsFiltered": 5, "data": [ { + "id": "1", "attributes": { "nama": "Sasak", - "jumlah": 18000 + "jumlah": 18000, + "laki_laki": 9000, + "perempuan": 9000, + "persentase_jumlah": "72.00%", + "persentase_laki_laki": "36.00%", + "persentase_perempuan": "36.00%", + "kriteria": "{\"filter[id]\":\"suku\",\"filter[kriteria]\":\"Sasak\"}" } }, { + "id": "2", "attributes": { "nama": "Jawa", - "jumlah": 3000 + "jumlah": 3000, + "laki_laki": 1500, + "perempuan": 1500, + "persentase_jumlah": "12.00%", + "persentase_laki_laki": "6.00%", + "persentase_perempuan": "6.00%", + "kriteria": "{\"filter[id]\":\"suku\",\"filter[kriteria]\":\"Jawa\"}" } }, { + "id": "3", "attributes": { "nama": "Bali", - "jumlah": 2000 + "jumlah": 2000, + "laki_laki": 1000, + "perempuan": 1000, + "persentase_jumlah": "8.00%", + "persentase_laki_laki": "4.00%", + "persentase_perempuan": "4.00%", + "kriteria": "{\"filter[id]\":\"suku\",\"filter[kriteria]\":\"Bali\"}" } }, { + "id": "4", "attributes": { "nama": "Sumbawa", - "jumlah": 1500 + "jumlah": 1500, + "laki_laki": 750, + "perempuan": 750, + "persentase_jumlah": "6.00%", + "persentase_laki_laki": "3.00%", + "persentase_perempuan": "3.00%", + "kriteria": "{\"filter[id]\":\"suku\",\"filter[kriteria]\":\"Sumbawa\"}" } }, { + "id": "5", "attributes": { "nama": "Lainnya", - "jumlah": 500 + "jumlah": 500, + "laki_laki": 250, + "perempuan": 250, + "persentase_jumlah": "2.00%", + "persentase_laki_laki": "1.00%", + "persentase_perempuan": "1.00%", + "kriteria": "{\"filter[id]\":\"suku\",\"filter[kriteria]\":\"Lainnya\"}" + } + }, + { + "id": "6", + "attributes": { + "nama": "JUMLAH", + "jumlah": 25000, + "laki_laki": 12500, + "perempuan": 12500, + "persentase_jumlah": "100.00%", + "persentase_laki_laki": "50.00%", + "persentase_perempuan": "50.00%", + "kriteria": "{}" } } ] diff --git a/tests/Browser/fixtures/statistik-rtm-bdt.json b/tests/Browser/fixtures/statistik-rtm-bdt.json new file mode 100644 index 00000000..3b16e40d --- /dev/null +++ b/tests/Browser/fixtures/statistik-rtm-bdt.json @@ -0,0 +1,46 @@ +{ + "draw": 1, + "recordsTotal": 3, + "recordsFiltered": 3, + "data": [ + { + "id": "1", + "attributes": { + "nama": "Ya", + "jumlah": 3000, + "persentase_jumlah": "30.00%", + "laki_laki": 1400, + "persentase_laki_laki": "46.67%", + "perempuan": 1600, + "persentase_perempuan": "53.33%", + "kriteria": "{\"bdt\":\"ya\"}" + } + }, + { + "id": "2", + "attributes": { + "nama": "Tidak", + "jumlah": 7000, + "persentase_jumlah": "70.00%", + "laki_laki": 3400, + "persentase_laki_laki": "48.57%", + "perempuan": 3600, + "persentase_perempuan": "51.43%", + "kriteria": "{\"bdt\":\"tidak\"}" + } + }, + { + "id": "3", + "attributes": { + "nama": "JUMLAH", + "jumlah": 10000, + "persentase_jumlah": "100.00%", + "laki_laki": 4800, + "persentase_laki_laki": "48.00%", + "perempuan": 5200, + "persentase_perempuan": "52.00%", + "kriteria": "{}" + } + } + ] +} From 7bc43ad1cc7a29253301b5825f8838347cbc3049 Mon Sep 17 00:00:00 2001 From: Ahmad Afandi Date: Mon, 15 Jun 2026 10:07:34 +0700 Subject: [PATCH 40/43] smoketest datapresisi --- .../views/data_pokok/agama/index.blade.php | 4 +- .../data_presisi/adat/index.blade.php | 4 +- .../data_presisi/laporan/index.blade.php | 8 +- .../data_presisi/laporan/perdesa.blade.php | 6 +- .../data_presisi/seni_budaya/index.blade.php | 4 +- .../data_pokok/infrastruktur/index.blade.php | 6 +- .../data_pokok/jaminan_sosial/index.blade.php | 8 +- .../data_pokok/pariwisata/index.blade.php | 6 +- resources/views/dtks/papan/index.blade.php | 4 +- resources/views/dtks/sandang/index.blade.php | 4 +- .../views/presisi/statistik/adat.blade.php | 14 +- .../statistik/aktivitas-keagamaan.blade.php | 14 +- .../statistik/jaminan-sosial.blade.php | 14 +- .../presisi/statistik/kesehatan.blade.php | 14 +- .../statistik/ketenagakerjaan.blade.php | 14 +- .../views/presisi/statistik/pangan.blade.php | 14 +- .../views/presisi/statistik/papan.blade.php | 14 +- .../presisi/statistik/pendidikan.blade.php | 14 +- .../views/presisi/statistik/sandang.blade.php | 14 +- .../presisi/statistik/senibudaya.blade.php | 14 +- tests/Browser/MswSetup.php | 13 + tests/Browser/SmokeDataPokokSubmenusTest.php | 319 ++++++++++++++++++ tests/Browser/SmokeInfrastrukturTest.php | 150 ++++++++ tests/Browser/SmokeJaminanSosialTest.php | 194 +++++++++++ .../SmokeLaporanPengisianPerDesaTest.php | 86 +++++ tests/Browser/SmokeLaporanPengisianTest.php | 93 +++++ tests/Browser/SmokePariwisataTest.php | 157 +++++++++ tests/Browser/SmokeStatistikSubmenusTest.php | 225 ++++++++++++ tests/Browser/fixtures/adat.json | 23 ++ tests/Browser/fixtures/agama.json | 23 ++ tests/Browser/fixtures/infrastruktur.json | 21 ++ tests/Browser/fixtures/jaminan-sosial.json | 23 ++ .../fixtures/kategori-statistik-adat.json | 7 + .../fixtures/kategori-statistik-agama.json | 7 + .../kategori-statistik-jaminan-sosial.json | 8 + .../kategori-statistik-kesehatan.json | 7 + .../kategori-statistik-ketenagakerjaan.json | 7 + .../fixtures/kategori-statistik-pangan.json | 7 + .../fixtures/kategori-statistik-papan.json | 7 + .../kategori-statistik-pendidikan.json | 7 + .../fixtures/kategori-statistik-sandang.json | 7 + .../kategori-statistik-seni-budaya.json | 7 + tests/Browser/fixtures/laporan-perdesa.json | 21 ++ tests/Browser/fixtures/laporan.json | 21 ++ tests/Browser/fixtures/pangan.json | 23 ++ tests/Browser/fixtures/papan.json | 24 ++ tests/Browser/fixtures/pariwisata.json | 23 ++ tests/Browser/fixtures/sandang.json | 23 ++ tests/Browser/fixtures/seni-budaya.json | 23 ++ tests/Browser/fixtures/statistik-adat.json | 33 ++ tests/Browser/fixtures/statistik-agama.json | 33 ++ .../fixtures/statistik-jaminan-sosial.json | 33 ++ .../Browser/fixtures/statistik-kesehatan.json | 33 ++ .../fixtures/statistik-ketenagakerjaan.json | 33 ++ tests/Browser/fixtures/statistik-pangan.json | 33 ++ tests/Browser/fixtures/statistik-papan.json | 33 ++ .../fixtures/statistik-pendidikan.json | 33 ++ tests/Browser/fixtures/statistik-sandang.json | 33 ++ .../fixtures/statistik-seni-budaya.json | 33 ++ 59 files changed, 1983 insertions(+), 97 deletions(-) create mode 100644 tests/Browser/SmokeDataPokokSubmenusTest.php create mode 100644 tests/Browser/SmokeInfrastrukturTest.php create mode 100644 tests/Browser/SmokeJaminanSosialTest.php create mode 100644 tests/Browser/SmokeLaporanPengisianPerDesaTest.php create mode 100644 tests/Browser/SmokeLaporanPengisianTest.php create mode 100644 tests/Browser/SmokePariwisataTest.php create mode 100644 tests/Browser/SmokeStatistikSubmenusTest.php create mode 100644 tests/Browser/fixtures/adat.json create mode 100644 tests/Browser/fixtures/agama.json create mode 100644 tests/Browser/fixtures/infrastruktur.json create mode 100644 tests/Browser/fixtures/jaminan-sosial.json create mode 100644 tests/Browser/fixtures/kategori-statistik-adat.json create mode 100644 tests/Browser/fixtures/kategori-statistik-agama.json create mode 100644 tests/Browser/fixtures/kategori-statistik-jaminan-sosial.json create mode 100644 tests/Browser/fixtures/kategori-statistik-kesehatan.json create mode 100644 tests/Browser/fixtures/kategori-statistik-ketenagakerjaan.json create mode 100644 tests/Browser/fixtures/kategori-statistik-pangan.json create mode 100644 tests/Browser/fixtures/kategori-statistik-papan.json create mode 100644 tests/Browser/fixtures/kategori-statistik-pendidikan.json create mode 100644 tests/Browser/fixtures/kategori-statistik-sandang.json create mode 100644 tests/Browser/fixtures/kategori-statistik-seni-budaya.json create mode 100644 tests/Browser/fixtures/laporan-perdesa.json create mode 100644 tests/Browser/fixtures/laporan.json create mode 100644 tests/Browser/fixtures/pangan.json create mode 100644 tests/Browser/fixtures/papan.json create mode 100644 tests/Browser/fixtures/pariwisata.json create mode 100644 tests/Browser/fixtures/sandang.json create mode 100644 tests/Browser/fixtures/seni-budaya.json create mode 100644 tests/Browser/fixtures/statistik-adat.json create mode 100644 tests/Browser/fixtures/statistik-agama.json create mode 100644 tests/Browser/fixtures/statistik-jaminan-sosial.json create mode 100644 tests/Browser/fixtures/statistik-kesehatan.json create mode 100644 tests/Browser/fixtures/statistik-ketenagakerjaan.json create mode 100644 tests/Browser/fixtures/statistik-pangan.json create mode 100644 tests/Browser/fixtures/statistik-papan.json create mode 100644 tests/Browser/fixtures/statistik-pendidikan.json create mode 100644 tests/Browser/fixtures/statistik-sandang.json create mode 100644 tests/Browser/fixtures/statistik-seni-budaya.json diff --git a/resources/views/data_pokok/agama/index.blade.php b/resources/views/data_pokok/agama/index.blade.php index f043ccb8..731d48a2 100644 --- a/resources/views/data_pokok/agama/index.blade.php +++ b/resources/views/data_pokok/agama/index.blade.php @@ -16,7 +16,7 @@
Statistik Agama
-
+

@@ -45,7 +45,7 @@
-
No
+
diff --git a/resources/views/data_pokok/data_presisi/adat/index.blade.php b/resources/views/data_pokok/data_presisi/adat/index.blade.php index 366ccb14..12719410 100644 --- a/resources/views/data_pokok/data_presisi/adat/index.blade.php +++ b/resources/views/data_pokok/data_presisi/adat/index.blade.php @@ -16,7 +16,7 @@
Statistik Adat
-
+

@@ -40,7 +40,7 @@
-
Aksi
+
diff --git a/resources/views/data_pokok/data_presisi/laporan/index.blade.php b/resources/views/data_pokok/data_presisi/laporan/index.blade.php index 712b1f5e..05d5e116 100644 --- a/resources/views/data_pokok/data_presisi/laporan/index.blade.php +++ b/resources/views/data_pokok/data_presisi/laporan/index.blade.php @@ -22,7 +22,7 @@
- @php $statusOptions = [ 0 => 'Tidak Lengkap', @@ -37,10 +37,10 @@
- -
@@ -48,7 +48,7 @@
-
Aksi
+
diff --git a/resources/views/data_pokok/data_presisi/laporan/perdesa.blade.php b/resources/views/data_pokok/data_presisi/laporan/perdesa.blade.php index 29027caf..3ff95121 100644 --- a/resources/views/data_pokok/data_presisi/laporan/perdesa.blade.php +++ b/resources/views/data_pokok/data_presisi/laporan/perdesa.blade.php @@ -22,10 +22,10 @@
- -
@@ -33,7 +33,7 @@
-
No
+
diff --git a/resources/views/data_pokok/data_presisi/seni_budaya/index.blade.php b/resources/views/data_pokok/data_presisi/seni_budaya/index.blade.php index de4a6e32..c47737ac 100644 --- a/resources/views/data_pokok/data_presisi/seni_budaya/index.blade.php +++ b/resources/views/data_pokok/data_presisi/seni_budaya/index.blade.php @@ -21,7 +21,7 @@
- +
@@ -39,7 +39,7 @@
-
No
+
diff --git a/resources/views/data_pokok/infrastruktur/index.blade.php b/resources/views/data_pokok/infrastruktur/index.blade.php index d1256cfe..ec18855e 100644 --- a/resources/views/data_pokok/infrastruktur/index.blade.php +++ b/resources/views/data_pokok/infrastruktur/index.blade.php @@ -16,7 +16,7 @@
Statistik Kondisi Transportasi
- +
@@ -26,7 +26,7 @@
Statistik Sanitasi
- +

@@ -48,7 +48,7 @@
-
Aksi
+
diff --git a/resources/views/data_pokok/jaminan_sosial/index.blade.php b/resources/views/data_pokok/jaminan_sosial/index.blade.php index b7ce3232..d10d97ff 100644 --- a/resources/views/data_pokok/jaminan_sosial/index.blade.php +++ b/resources/views/data_pokok/jaminan_sosial/index.blade.php @@ -18,7 +18,7 @@ Jenis Bantuan
-
+
@@ -30,7 +30,7 @@ Jenis Gangguan Mental
-
+
@@ -42,7 +42,7 @@ Jenis Gangguan Mental
-
+
@@ -70,7 +70,7 @@
-
Kategori
+
diff --git a/resources/views/data_pokok/pariwisata/index.blade.php b/resources/views/data_pokok/pariwisata/index.blade.php index f06d568c..f5e9a00d 100644 --- a/resources/views/data_pokok/pariwisata/index.blade.php +++ b/resources/views/data_pokok/pariwisata/index.blade.php @@ -17,7 +17,7 @@
- +

@@ -30,7 +30,7 @@
- +

@@ -60,7 +60,7 @@
-
Aksi
+
diff --git a/resources/views/dtks/papan/index.blade.php b/resources/views/dtks/papan/index.blade.php index 952e4958..cebc5479 100644 --- a/resources/views/dtks/papan/index.blade.php +++ b/resources/views/dtks/papan/index.blade.php @@ -21,7 +21,7 @@
- +
@@ -37,7 +37,7 @@
-
ID
+
diff --git a/resources/views/dtks/sandang/index.blade.php b/resources/views/dtks/sandang/index.blade.php index 6ad7372f..73729857 100644 --- a/resources/views/dtks/sandang/index.blade.php +++ b/resources/views/dtks/sandang/index.blade.php @@ -21,7 +21,7 @@
- +
@@ -37,7 +37,7 @@
-
Aksi
+
diff --git a/resources/views/presisi/statistik/adat.blade.php b/resources/views/presisi/statistik/adat.blade.php index 21a9d315..7a0d21f4 100644 --- a/resources/views/presisi/statistik/adat.blade.php +++ b/resources/views/presisi/statistik/adat.blade.php @@ -22,7 +22,7 @@
-
@@ -36,20 +36,20 @@
-
-
- @@ -61,14 +61,14 @@
- +

- +

@@ -76,7 +76,7 @@
-
Aksi
+
diff --git a/resources/views/presisi/statistik/aktivitas-keagamaan.blade.php b/resources/views/presisi/statistik/aktivitas-keagamaan.blade.php index ff285dcc..13964450 100644 --- a/resources/views/presisi/statistik/aktivitas-keagamaan.blade.php +++ b/resources/views/presisi/statistik/aktivitas-keagamaan.blade.php @@ -22,7 +22,7 @@
-
@@ -36,20 +36,20 @@
-
-
- @@ -61,14 +61,14 @@
- +

- +

@@ -76,7 +76,7 @@
-
No
+
diff --git a/resources/views/presisi/statistik/jaminan-sosial.blade.php b/resources/views/presisi/statistik/jaminan-sosial.blade.php index 29007785..5fcd13de 100644 --- a/resources/views/presisi/statistik/jaminan-sosial.blade.php +++ b/resources/views/presisi/statistik/jaminan-sosial.blade.php @@ -22,7 +22,7 @@
-
@@ -37,20 +37,20 @@
-
-
- @@ -62,14 +62,14 @@
- +

- +

@@ -77,7 +77,7 @@
-
No
+
diff --git a/resources/views/presisi/statistik/kesehatan.blade.php b/resources/views/presisi/statistik/kesehatan.blade.php index ed06273a..9c34f14a 100644 --- a/resources/views/presisi/statistik/kesehatan.blade.php +++ b/resources/views/presisi/statistik/kesehatan.blade.php @@ -22,7 +22,7 @@
-
@@ -37,20 +37,20 @@
-
-
- @@ -62,14 +62,14 @@
- +

- +

@@ -77,7 +77,7 @@
-
No
+
diff --git a/resources/views/presisi/statistik/ketenagakerjaan.blade.php b/resources/views/presisi/statistik/ketenagakerjaan.blade.php index 7bbfc144..eaa1dfac 100644 --- a/resources/views/presisi/statistik/ketenagakerjaan.blade.php +++ b/resources/views/presisi/statistik/ketenagakerjaan.blade.php @@ -22,7 +22,7 @@
-
@@ -36,20 +36,20 @@
-
-
- @@ -61,14 +61,14 @@
- +

- +

@@ -76,7 +76,7 @@
-
No
+
diff --git a/resources/views/presisi/statistik/pangan.blade.php b/resources/views/presisi/statistik/pangan.blade.php index 2ff1e26b..4221f1fb 100644 --- a/resources/views/presisi/statistik/pangan.blade.php +++ b/resources/views/presisi/statistik/pangan.blade.php @@ -22,7 +22,7 @@
-
@@ -36,20 +36,20 @@
-
-
- @@ -61,14 +61,14 @@
- +

- +

@@ -76,7 +76,7 @@
-
No
+
diff --git a/resources/views/presisi/statistik/papan.blade.php b/resources/views/presisi/statistik/papan.blade.php index 50c1d0df..2c4368dd 100644 --- a/resources/views/presisi/statistik/papan.blade.php +++ b/resources/views/presisi/statistik/papan.blade.php @@ -22,7 +22,7 @@
-
@@ -36,20 +36,20 @@
-
-
- @@ -61,14 +61,14 @@
- +

- +

@@ -76,7 +76,7 @@
-
No
+
diff --git a/resources/views/presisi/statistik/pendidikan.blade.php b/resources/views/presisi/statistik/pendidikan.blade.php index 30481ce3..250fd5cb 100644 --- a/resources/views/presisi/statistik/pendidikan.blade.php +++ b/resources/views/presisi/statistik/pendidikan.blade.php @@ -22,7 +22,7 @@
-
@@ -36,20 +36,20 @@
-
-
- @@ -61,14 +61,14 @@
- +

- +

@@ -76,7 +76,7 @@
-
No
+
diff --git a/resources/views/presisi/statistik/sandang.blade.php b/resources/views/presisi/statistik/sandang.blade.php index 66cae6dd..92abf52c 100644 --- a/resources/views/presisi/statistik/sandang.blade.php +++ b/resources/views/presisi/statistik/sandang.blade.php @@ -22,7 +22,7 @@
-
@@ -36,20 +36,20 @@
-
-
- @@ -61,14 +61,14 @@
- +

- +

@@ -76,7 +76,7 @@
-
No
+
diff --git a/resources/views/presisi/statistik/senibudaya.blade.php b/resources/views/presisi/statistik/senibudaya.blade.php index bf6c843f..304ff221 100644 --- a/resources/views/presisi/statistik/senibudaya.blade.php +++ b/resources/views/presisi/statistik/senibudaya.blade.php @@ -22,7 +22,7 @@
-
@@ -36,20 +36,20 @@
-
-
- @@ -61,14 +61,14 @@
- +

- +

@@ -76,7 +76,7 @@
-
No
+
diff --git a/tests/Browser/MswSetup.php b/tests/Browser/MswSetup.php index 4013083f..f7f4f045 100644 --- a/tests/Browser/MswSetup.php +++ b/tests/Browser/MswSetup.php @@ -37,6 +37,17 @@ final class MswSetup '/api/v1/bantuan/sasaran' => 'bantuan-sasaran.json', '/api/v1/bantuan/tahun' => 'bantuan-tahun.json', '/api/v1/lembaga' => 'lembaga.json', + '/api/v1/pariwisata' => 'pariwisata.json', + '/api/v1/infrastruktur' => 'infrastruktur.json', + '/api/v1/data-presisi/jaminan-sosial/rtm' => 'jaminan-sosial.json', + '/api/v1/data-presisi/laporan' => 'laporan.json', + '/api/v1/data-presisi/laporan-perdesa' => 'laporan-perdesa.json', + '/api/v1/data-presisi/pangan' => 'pangan.json', + '/api/v1/data-presisi/sandang' => 'sandang.json', + '/api/v1/data-presisi/papan' => 'papan.json', + '/api/v1/data-presisi/seni-budaya' => 'seni-budaya.json', + '/api/v1/data-presisi/adat' => 'adat.json', + '/api/v1/agama' => 'agama.json', ]; /** @@ -51,6 +62,8 @@ final class MswSetup '#/api/v1/statistik/keluarga\?.*filter(?:\[id\]|%5Bid%5D)=([^&]+)#' => 'statistik-keluarga-*.json', '#/api/v1/statistik/rtm\?.*filter(?:\[id\]|%5Bid%5D)=([^&]+)#' => 'statistik-rtm-*.json', '#/api/v1/statistik/bantuan\?.*filter(?:\[id\]|%5Bid%5D)=([^&]+)#' => 'statistik-bantuan-*.json', + '#/api/v1/data-presisi/([\w-]+)/kategori-statistik#' => 'kategori-statistik-*.json', + '#/api/v1/data-presisi/([\w-]+)/statistik#' => 'statistik-*.json', ]; /** diff --git a/tests/Browser/SmokeDataPokokSubmenusTest.php b/tests/Browser/SmokeDataPokokSubmenusTest.php new file mode 100644 index 00000000..b3eaee2c --- /dev/null +++ b/tests/Browser/SmokeDataPokokSubmenusTest.php @@ -0,0 +1,319 @@ +user = SessionState::loginAdminUser(); +}); + +afterEach(function () { + SessionState::clear(); +}); + +$dataPokokPages = [ + 'pangan' => [ + 'path' => '/data-presisi/pangan', + 'tableSelector' => '#table-pangan', + 'chartSelector' => '#barChart', + 'hasFilterTahun' => true, + 'hasFilterStatus' => true, + 'hasCetakButton' => true, + 'hasExcelButton' => true, + 'hasDetailButton' => true, + 'hasCollapse' => false, + ], + 'sandang' => [ + 'path' => '/data-pokok/sandang', + 'tableSelector' => '#table-dtks', + 'chartSelector' => null, + 'hasFilterTahun' => true, + 'hasFilterStatus' => false, + 'hasCetakButton' => true, + 'hasExcelButton' => true, + 'hasDetailButton' => true, + 'hasCollapse' => false, + ], + 'papan' => [ + 'path' => '/satu-data/dtks/papan', + 'tableSelector' => '#table-dtks', + 'chartSelector' => null, + 'hasFilterTahun' => true, + 'hasFilterStatus' => false, + 'hasCetakButton' => true, + 'hasExcelButton' => true, + 'hasDetailButton' => true, + 'hasCollapse' => false, + ], + 'kesehatan' => [ + 'path' => '/data-pokok/kesehatan', + 'tableSelector' => '[data-testid="datatable-kesehatan"]', + 'tableTestId' => 'datatable-kesehatan', + 'chartSelector' => '#donutChart', + 'chartSelector2' => '#barChart', + 'hasFilterTahun' => false, + 'hasFilterStatus' => false, + 'hasCetakButton' => true, + 'cetakTestId' => 'bt-cetak', + 'hasExcelButton' => true, + 'excelTestId' => 'bt-excel', + 'hasDetailButton' => false, + 'hasCollapse' => false, + ], + 'pendidikan' => [ + 'path' => '/data-pokok/pendidikan', + 'tableSelector' => '[data-testid="datatable-pendidikan"]', + 'tableTestId' => 'datatable-pendidikan', + 'chartSelector' => '#donutChart', + 'chartSelector2' => '#barChart', + 'hasFilterTahun' => false, + 'hasFilterStatus' => false, + 'hasCetakButton' => true, + 'cetakTestId' => 'bt-cetak', + 'hasExcelButton' => true, + 'excelTestId' => 'bt-excel', + 'hasDetailButton' => false, + 'hasCollapse' => false, + ], + 'ketenagakerjaan' => [ + 'path' => '/data-pokok/ketenagakerjaan', + 'tableSelector' => '[data-testid="datatable-ketenagakerjaan"]', + 'tableTestId' => 'datatable-ketenagakerjaan', + 'chartSelector' => '#barChart', + 'chartSelector2' => '#donutChart', + 'hasFilterTahun' => false, + 'hasFilterStatus' => false, + 'hasCetakButton' => true, + 'cetakTestId' => 'bt-cetak', + 'hasExcelButton' => true, + 'excelTestId' => 'bt-excel', + 'hasDetailButton' => false, + 'hasCollapse' => false, + ], + 'adat' => [ + 'path' => '/data-presisi/adat', + 'tableSelector' => '#adat', + 'chartSelector' => '#pie1', + 'hasFilterTahun' => true, + 'hasFilterStatus' => true, + 'hasCetakButton' => true, + 'hasExcelButton' => true, + 'hasDetailButton' => true, + 'hasCollapse' => false, + ], + 'agama' => [ + 'path' => '/data-pokok/agama', + 'tableSelector' => '#agama', + 'chartSelector' => '#pie1', + 'hasFilterTahun' => true, + 'hasFilterStatus' => true, + 'hasCetakButton' => true, + 'hasExcelButton' => true, + 'hasDetailButton' => true, + 'hasCollapse' => false, + ], + 'seni-budaya' => [ + 'path' => '/data-presisi/seni-budaya', + 'tableSelector' => '#table-seni-budaya', + 'chartSelector' => '#barChart', + 'hasFilterTahun' => true, + 'hasFilterStatus' => true, + 'hasCetakButton' => true, + 'hasExcelButton' => true, + 'hasDetailButton' => true, + 'hasCollapse' => false, + ], +]; + +foreach ($dataPokokPages as $key => $config) { + $path = $config['path']; + $tableSelector = $config['tableSelector']; + + it("opens the {$key} page", function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + ScreenshotHelper::saveIfEnabled($page, "{$key}-page"); + }); + + if ($config['chartSelector']) { + $chartSelector = $config['chartSelector']; + $chartSelector2 = $config['chartSelector2'] ?? null; + + it("renders chart on {$key}", function () use ($path, $chartSelector, $chartSelector2) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const chart1 = document.querySelector('{$chartSelector}'); + const ready1 = chart1 && (chart1.getContext ? chart1.getContext && chart1.width > 0 : chart1.offsetWidth > 0); + " . ($chartSelector2 ? " + const chart2 = document.querySelector('{$chartSelector2}'); + const ready2 = chart2 && (chart2.getContext ? chart2.getContext && chart2.width > 0 : chart2.offsetWidth > 0); + if (ready1 && ready2) { resolve(true); } else { setTimeout(check, 500); } + " : " + if (ready1) { resolve(true); } else { setTimeout(check, 500); } + ") . " + }; + check(); + })", + true + ); + + ScreenshotHelper::saveIfEnabled($page, "{$key}-chart"); + }); + } + + if ($config['hasFilterTahun']) { + it("displays filter tahun on {$key}", function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('#filter-tahun'); + + ScreenshotHelper::saveIfEnabled($page, "{$key}-filter-tahun"); + }); + } + + if ($config['hasFilterStatus']) { + it("displays filter status on {$key}", function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('#filter-status-kelengkapan'); + + ScreenshotHelper::saveIfEnabled($page, "{$key}-filter-status"); + }); + } + + if ($config['hasCetakButton']) { + $cetakTestId = $config['cetakTestId'] ?? null; + + it("displays cetak button on {$key}", function () use ($path, $cetakTestId) { + $page = SessionState::loginAndNavigate($this->user, $path); + + if ($cetakTestId) { + $page->assertVisible("[data-testid=\"{$cetakTestId}\"]"); + } else { + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const btn = document.querySelector('[data-testid=\"bt-cetak\"]') || document.querySelector('#cetak') || document.querySelector('button[data-print-url]'); + resolve(!!btn); + }; + check(); + })", + true + ); + } + + ScreenshotHelper::saveIfEnabled($page, "{$key}-cetak-button"); + }); + } + + if ($config['hasExcelButton']) { + $excelTestId = $config['excelTestId'] ?? null; + + it("displays excel button on {$key}", function () use ($path, $excelTestId) { + $page = SessionState::loginAndNavigate($this->user, $path); + + if ($excelTestId) { + $page->assertVisible("[data-testid=\"{$excelTestId}\"]"); + } else { + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const btn = document.querySelector('[data-testid=\"bt-excel\"]') || document.querySelector('#export-excel') || document.querySelector('#download-excel') || document.querySelector('button[data-download-url]'); + resolve(!!btn); + }; + check(); + })", + true + ); + } + + ScreenshotHelper::saveIfEnabled($page, "{$key}-excel-button"); + }); + } + + it("displays datatable on {$key}", function () use ($path, $tableSelector) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const table = document.querySelector('{$tableSelector}'); + if (table) { resolve(true); } else { setTimeout(check, 500); } + }; + check(); + })", + true + ); + + ScreenshotHelper::saveIfEnabled($page, "{$key}-datatable"); + }); + + it("has at least 1 data row on {$key}", function () use ($path, $tableSelector) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const table = document.querySelector('{$tableSelector}'); + if (table) { + const rows = table.querySelectorAll('tbody tr'); + if (rows.length > 0 && !rows[0].classList.contains('dataTables_empty')) { + resolve(true); + } else { + setTimeout(check, 500); + } + } else { + setTimeout(check, 500); + } + }; + check(); + })", + true + ); + + ScreenshotHelper::saveIfEnabled($page, "{$key}-data-rows"); + }); + + if ($config['hasDetailButton']) { + it("has detail button in data on {$key}", function () use ($path, $tableSelector) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const table = document.querySelector('{$tableSelector}'); + if (table) { + const rows = table.querySelectorAll('tbody tr'); + if (rows.length > 0 && !rows[0].classList.contains('dataTables_empty')) { + const detailBtn = rows[0].querySelector('[data-button=\"Detail\"]') || rows[0].querySelector('a[href*=\"detail\"]'); + resolve(!!detailBtn); + } else { + setTimeout(check, 500); + } + } else { + setTimeout(check, 500); + } + }; + check(); + })", + true + ); + + ScreenshotHelper::saveIfEnabled($page, "{$key}-detail-button"); + }); + } + + it("has no javascript errors on {$key}", function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path) + ->assertNoJavaScriptErrors(); + + ScreenshotHelper::saveIfEnabled($page, "{$key}-no-errors"); + }); +} diff --git a/tests/Browser/SmokeInfrastrukturTest.php b/tests/Browser/SmokeInfrastrukturTest.php new file mode 100644 index 00000000..2d9e8bbc --- /dev/null +++ b/tests/Browser/SmokeInfrastrukturTest.php @@ -0,0 +1,150 @@ +user = SessionState::loginAdminUser(); +}); + +afterEach(function () { + SessionState::clear(); +}); + +it('opens the infrastruktur page', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/infrastruktur') + ->assertPathIs('/data-pokok/infrastruktur'); + + ScreenshotHelper::saveIfEnabled($page, 'infrastruktur-page'); +}); + +it('displays statistik kondisi transportasi', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/infrastruktur') + ->assertSee('Statistik Kondisi Transportasi'); + + ScreenshotHelper::saveIfEnabled($page, 'infrastruktur-kondisi-transportasi'); +}); + +it('displays statistik sanitasi', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/infrastruktur') + ->assertSee('Statistik Sanitasi'); + + ScreenshotHelper::saveIfEnabled($page, 'infrastruktur-sanitasi'); +}); + +it('renders all charts successfully', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/infrastruktur') + ->assertPathIs('/data-pokok/infrastruktur'); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const kondisi = document.querySelector(\'[data-testid="chart-transportasi"]\'); + const sanitasi = document.querySelector(\'[data-testid="chart-sanitasi"]\'); + const kondisiReady = kondisi && kondisi.getContext && kondisi.width > 0; + const sanitasiReady = sanitasi && sanitasi.getContext && sanitasi.width > 0; + if (kondisiReady && sanitasiReady) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'infrastruktur-charts'); +}); + +it('displays filter tahun', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/infrastruktur') + ->assertVisible('#filter-tahun'); + + ScreenshotHelper::saveIfEnabled($page, 'infrastruktur-filter-tahun'); +}); + +it('displays cetak button', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/infrastruktur') + ->assertScript( + "new Promise((resolve) => { + const check = () => { + const btn = document.querySelector('[data-testid=\"bt-cetak\"]') || document.querySelector('#cetak') || document.querySelector('button[data-print-url]'); + resolve(!!btn); + }; + check(); + })", + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'infrastruktur-cetak-button'); +}); + +it('displays excel button', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/infrastruktur') + ->assertScript( + "new Promise((resolve) => { + const check = () => { + const btn = document.querySelector('[data-testid=\"bt-excel\"]') || document.querySelector('#export-excel') || document.querySelector('#download-excel') || document.querySelector('button[data-download-url]'); + resolve(!!btn); + }; + check(); + })", + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'infrastruktur-excel-button'); +}); + +it('displays datatable', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/infrastruktur') + ->assertPathIs('/data-pokok/infrastruktur'); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const table = document.querySelector(\'[data-testid="datatable-infrastruktur"]\'); + if (table) { resolve(true); } else { setTimeout(check, 500); } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'infrastruktur-datatable'); +}); + +it('has at least 1 data row', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/infrastruktur') + ->assertPathIs('/data-pokok/infrastruktur'); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const table = document.querySelector(\'[data-testid="datatable-infrastruktur"]\'); + if (table) { + const rows = table.querySelectorAll(\'tbody tr\'); + if (rows.length > 0 && !rows[0].classList.contains(\'dataTables_empty\')) { + resolve(true); + } else { + setTimeout(check, 500); + } + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'infrastruktur-data-rows'); +}); + +it('has no javascript errors', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/infrastruktur') + ->assertPathIs('/data-pokok/infrastruktur') + ->assertNoJavaScriptErrors(); + + ScreenshotHelper::saveIfEnabled($page, 'infrastruktur-no-errors'); +}); diff --git a/tests/Browser/SmokeJaminanSosialTest.php b/tests/Browser/SmokeJaminanSosialTest.php new file mode 100644 index 00000000..3b80db77 --- /dev/null +++ b/tests/Browser/SmokeJaminanSosialTest.php @@ -0,0 +1,194 @@ +user = SessionState::loginAdminUser(); +}); + +afterEach(function () { + SessionState::clear(); +}); + +it('opens the jaminan sosial page', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/jaminan-sosial') + ->assertPathIs('/data-pokok/jaminan-sosial'); + + ScreenshotHelper::saveIfEnabled($page, 'jaminan-sosial-page'); +}); + +it('displays statistik jenis bantuan', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/jaminan-sosial') + ->assertSee('Statistik Jenis Bantuan'); + + ScreenshotHelper::saveIfEnabled($page, 'jaminan-sosial-jenis-bantuan'); +}); + +it('displays statistik jenis gangguan mental', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/jaminan-sosial') + ->assertSee('Statistik Jenis Gangguan Mental'); + + ScreenshotHelper::saveIfEnabled($page, 'jaminan-sosial-gangguan-mental'); +}); + +it('displays statistik jenis penanganan', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/jaminan-sosial') + ->assertSee('Statistik Jenis Penanganan'); + + ScreenshotHelper::saveIfEnabled($page, 'jaminan-sosial-jenis-penanganan'); +}); + +it('renders all charts successfully', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/jaminan-sosial') + ->assertPathIs('/data-pokok/jaminan-sosial'); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const pie1 = document.querySelector(\'[data-testid="chart-pie-bantuan"]\'); + const pie2 = document.querySelector(\'[data-testid="chart-pie-mental"]\'); + const pie4 = document.querySelector(\'[data-testid="chart-pie-penanganan"]\'); + const ready1 = pie1 && pie1.offsetWidth > 0; + const ready2 = pie2 && pie2.offsetWidth > 0; + const ready4 = pie4 && pie4.offsetWidth > 0; + if (ready1 && ready2 && ready4) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'jaminan-sosial-charts'); +}); + +it('displays filter tahun', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/jaminan-sosial') + ->assertVisible('#filter-tahun'); + + ScreenshotHelper::saveIfEnabled($page, 'jaminan-sosial-filter-tahun'); +}); + +it('displays filter status', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/jaminan-sosial') + ->assertVisible('#filter-status-kelengkapan'); + + ScreenshotHelper::saveIfEnabled($page, 'jaminan-sosial-filter-status'); +}); + +it('displays cetak button', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/jaminan-sosial') + ->assertScript( + "new Promise((resolve) => { + const check = () => { + const btn = document.querySelector('[data-testid=\"bt-cetak\"]') || document.querySelector('#cetak') || document.querySelector('button[data-print-url]'); + resolve(!!btn); + }; + check(); + })", + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'jaminan-sosial-cetak-button'); +}); + +it('displays excel button', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/jaminan-sosial') + ->assertScript( + "new Promise((resolve) => { + const check = () => { + const btn = document.querySelector('[data-testid=\"bt-excel\"]') || document.querySelector('#export-excel') || document.querySelector('#download-excel') || document.querySelector('button[data-download-url]'); + resolve(!!btn); + }; + check(); + })", + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'jaminan-sosial-excel-button'); +}); + +it('displays datatable', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/jaminan-sosial') + ->assertPathIs('/data-pokok/jaminan-sosial'); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const table = document.querySelector(\'[data-testid="datatable-jaminan-sosial"]\'); + if (table) { resolve(true); } else { setTimeout(check, 500); } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'jaminan-sosial-datatable'); +}); + +it('has at least 1 data row', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/jaminan-sosial') + ->assertPathIs('/data-pokok/jaminan-sosial'); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const table = document.querySelector(\'[data-testid="datatable-jaminan-sosial"]\'); + if (table) { + const rows = table.querySelectorAll(\'tbody tr\'); + if (rows.length > 0 && !rows[0].classList.contains(\'dataTables_empty\')) { + resolve(true); + } else { + setTimeout(check, 500); + } + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'jaminan-sosial-data-rows'); +}); + +it('has detail button in data', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/jaminan-sosial') + ->assertPathIs('/data-pokok/jaminan-sosial'); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const table = document.querySelector(\'[data-testid="datatable-jaminan-sosial"]\'); + if (table) { + const rows = table.querySelectorAll(\'tbody tr\'); + if (rows.length > 0 && !rows[0].classList.contains(\'dataTables_empty\')) { + const detailBtn = rows[0].querySelector(\'[data-button="Detail"]\') || rows[0].querySelector(\'a[href*="detail"]\'); + resolve(!!detailBtn); + } else { + setTimeout(check, 500); + } + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'jaminan-sosial-detail-button'); +}); + +it('has no javascript errors', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/jaminan-sosial') + ->assertPathIs('/data-pokok/jaminan-sosial') + ->assertNoJavaScriptErrors(); + + ScreenshotHelper::saveIfEnabled($page, 'jaminan-sosial-no-errors'); +}); diff --git a/tests/Browser/SmokeLaporanPengisianPerDesaTest.php b/tests/Browser/SmokeLaporanPengisianPerDesaTest.php new file mode 100644 index 00000000..ab05021e --- /dev/null +++ b/tests/Browser/SmokeLaporanPengisianPerDesaTest.php @@ -0,0 +1,86 @@ +user = SessionState::loginAdminUser(); +}); + +afterEach(function () { + SessionState::clear(); +}); + +it('opens the laporan pengisian per desa page', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-presisi/laporan/perdesa') + ->assertPathIs('/data-presisi/laporan/perdesa'); + + ScreenshotHelper::saveIfEnabled($page, 'laporan-pengisian-perdesa-page'); +}); + +it('displays cetak button', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-presisi/laporan/perdesa') + ->assertVisible('[data-testid="btn-cetak"]'); + + ScreenshotHelper::saveIfEnabled($page, 'laporan-pengisian-perdesa-cetak-button'); +}); + +it('displays excel button', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-presisi/laporan/perdesa') + ->assertVisible('[data-testid="btn-export-excel"]'); + + ScreenshotHelper::saveIfEnabled($page, 'laporan-pengisian-perdesa-excel-button'); +}); + +it('displays datatable', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-presisi/laporan/perdesa') + ->assertPathIs('/data-presisi/laporan/perdesa'); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const table = document.querySelector(\'[data-testid="datatable-laporan-perdesa"]\'); + if (table) { resolve(true); } else { setTimeout(check, 500); } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'laporan-pengisian-perdesa-datatable'); +}); + +it('has at least 1 data row', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-presisi/laporan/perdesa') + ->assertPathIs('/data-presisi/laporan/perdesa'); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const table = document.querySelector(\'[data-testid="datatable-laporan-perdesa"]\'); + if (table) { + const rows = table.querySelectorAll(\'tbody tr\'); + if (rows.length > 0 && !rows[0].classList.contains(\'dataTables_empty\')) { + resolve(true); + } else { + setTimeout(check, 500); + } + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'laporan-pengisian-perdesa-data-rows'); +}); + +it('has no javascript errors', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-presisi/laporan/perdesa') + ->assertPathIs('/data-presisi/laporan/perdesa') + ->assertNoJavaScriptErrors(); + + ScreenshotHelper::saveIfEnabled($page, 'laporan-pengisian-perdesa-no-errors'); +}); diff --git a/tests/Browser/SmokeLaporanPengisianTest.php b/tests/Browser/SmokeLaporanPengisianTest.php new file mode 100644 index 00000000..3db97b1d --- /dev/null +++ b/tests/Browser/SmokeLaporanPengisianTest.php @@ -0,0 +1,93 @@ +user = SessionState::loginAdminUser(); +}); + +afterEach(function () { + SessionState::clear(); +}); + +it('opens the laporan pengisian page', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-presisi/laporan') + ->assertPathIs('/data-presisi/laporan'); + + ScreenshotHelper::saveIfEnabled($page, 'laporan-pengisian-page'); +}); + +it('displays filter kategori', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-presisi/laporan') + ->assertVisible('[data-testid="filter-status"]'); + + ScreenshotHelper::saveIfEnabled($page, 'laporan-pengisian-filter-kategori'); +}); + +it('displays cetak button', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-presisi/laporan') + ->assertVisible('[data-testid="btn-cetak"]'); + + ScreenshotHelper::saveIfEnabled($page, 'laporan-pengisian-cetak-button'); +}); + +it('displays excel button', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-presisi/laporan') + ->assertVisible('[data-testid="btn-export-excel"]'); + + ScreenshotHelper::saveIfEnabled($page, 'laporan-pengisian-excel-button'); +}); + +it('displays datatable', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-presisi/laporan') + ->assertPathIs('/data-presisi/laporan'); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const table = document.querySelector(\'[data-testid="datatable-laporan"]\'); + if (table) { resolve(true); } else { setTimeout(check, 500); } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'laporan-pengisian-datatable'); +}); + +it('has at least 1 data row', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-presisi/laporan') + ->assertPathIs('/data-presisi/laporan'); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const table = document.querySelector(\'[data-testid="datatable-laporan"]\'); + if (table) { + const rows = table.querySelectorAll(\'tbody tr\'); + if (rows.length > 0 && !rows[0].classList.contains(\'dataTables_empty\')) { + resolve(true); + } else { + setTimeout(check, 500); + } + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'laporan-pengisian-data-rows'); +}); + +it('has no javascript errors', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-presisi/laporan') + ->assertPathIs('/data-presisi/laporan') + ->assertNoJavaScriptErrors(); + + ScreenshotHelper::saveIfEnabled($page, 'laporan-pengisian-no-errors'); +}); diff --git a/tests/Browser/SmokePariwisataTest.php b/tests/Browser/SmokePariwisataTest.php new file mode 100644 index 00000000..b5d0a4f8 --- /dev/null +++ b/tests/Browser/SmokePariwisataTest.php @@ -0,0 +1,157 @@ +user = SessionState::loginAdminUser(); +}); + +afterEach(function () { + SessionState::clear(); +}); + +it('opens the pariwisata page', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/pariwisata') + ->assertPathIs('/data-pokok/pariwisata'); + + ScreenshotHelper::saveIfEnabled($page, 'pariwisata-page'); +}); + +it('displays statistik jumlah penginapan', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/pariwisata') + ->assertSee('Statistik Jumlah Penginapan'); + + ScreenshotHelper::saveIfEnabled($page, 'pariwisata-jumlah-penginapan'); +}); + +it('displays statistik tingkat pemanfaatan', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/pariwisata') + ->assertSee('Statistik Tingkat Pemanfaatan'); + + ScreenshotHelper::saveIfEnabled($page, 'pariwisata-tingkat-pemanfaatan'); +}); + +it('renders all charts successfully', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/pariwisata') + ->assertPathIs('/data-pokok/pariwisata'); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const bar = document.querySelector(\'[data-testid="chart-bar-penginapan"]\'); + const donut = document.querySelector(\'[data-testid="chart-donut-pemanfaatan"]\'); + const barReady = bar && bar.getContext && bar.width > 0; + const donutReady = donut && donut.getContext && donut.width > 0; + if (barReady && donutReady) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'pariwisata-charts'); +}); + +it('displays filter tahun', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/pariwisata') + ->assertVisible('#filter-tahun'); + + ScreenshotHelper::saveIfEnabled($page, 'pariwisata-filter-tahun'); +}); + +it('displays filter kategori', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/pariwisata') + ->assertVisible('#filter-kategori-wisata'); + + ScreenshotHelper::saveIfEnabled($page, 'pariwisata-filter-kategori'); +}); + +it('displays cetak button', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/pariwisata') + ->assertScript( + "new Promise((resolve) => { + const check = () => { + const btn = document.querySelector('[data-testid=\"bt-cetak\"]') || document.querySelector('#cetak') || document.querySelector('button[data-print-url]'); + resolve(!!btn); + }; + check(); + })", + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'pariwisata-cetak-button'); +}); + +it('displays excel button', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/pariwisata') + ->assertScript( + "new Promise((resolve) => { + const check = () => { + const btn = document.querySelector('[data-testid=\"bt-excel\"]') || document.querySelector('#export-excel') || document.querySelector('#download-excel') || document.querySelector('button[data-download-url]'); + resolve(!!btn); + }; + check(); + })", + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'pariwisata-excel-button'); +}); + +it('displays datatable', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/pariwisata') + ->assertPathIs('/data-pokok/pariwisata'); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const table = document.querySelector(\'[data-testid="datatable-pariwisata"]\'); + if (table) { resolve(true); } else { setTimeout(check, 500); } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'pariwisata-datatable'); +}); + +it('has at least 1 data row', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/pariwisata') + ->assertPathIs('/data-pokok/pariwisata'); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const table = document.querySelector(\'[data-testid="datatable-pariwisata"]\'); + if (table) { + const rows = table.querySelectorAll(\'tbody tr\'); + if (rows.length > 0 && !rows[0].classList.contains(\'dataTables_empty\')) { + resolve(true); + } else { + setTimeout(check, 500); + } + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'pariwisata-data-rows'); +}); + +it('has no javascript errors', function () { + $page = SessionState::loginAndNavigate($this->user, '/data-pokok/pariwisata') + ->assertPathIs('/data-pokok/pariwisata') + ->assertNoJavaScriptErrors(); + + ScreenshotHelper::saveIfEnabled($page, 'pariwisata-no-errors'); +}); diff --git a/tests/Browser/SmokeStatistikSubmenusTest.php b/tests/Browser/SmokeStatistikSubmenusTest.php new file mode 100644 index 00000000..77078e94 --- /dev/null +++ b/tests/Browser/SmokeStatistikSubmenusTest.php @@ -0,0 +1,225 @@ +user = SessionState::loginAdminUser(); +}); + +afterEach(function () { + SessionState::clear(); +}); + +$statistikPages = [ + 'pangan' => [ + 'path' => '/data-presisi/statistik/pangan', + 'judul' => 'Pangan', + 'kategoriUrl' => 'pangan/kategori-statistik', + ], + 'sandang' => [ + 'path' => '/data-presisi/statistik/sandang', + 'judul' => 'Sandang', + 'kategoriUrl' => 'sandang/kategori-statistik', + ], + 'papan' => [ + 'path' => '/data-presisi/statistik/papan', + 'judul' => 'Papan', + 'kategoriUrl' => 'papan/kategori-statistik', + ], + 'seni-budaya' => [ + 'path' => '/data-presisi/statistik/senibudaya', + 'judul' => 'Seni Budaya', + 'kategoriUrl' => 'seni-budaya/kategori-statistik', + ], + 'pendidikan' => [ + 'path' => '/data-presisi/statistik/pendidikan', + 'judul' => 'Pendidikan', + 'kategoriUrl' => 'pendidikan/kategori-statistik', + ], + 'kesehatan' => [ + 'path' => '/data-presisi/statistik/kesehatan', + 'judul' => 'Kesehatan', + 'kategoriUrl' => 'kesehatan/kategori-statistik', + ], + 'jaminan-sosial' => [ + 'path' => '/data-presisi/statistik/jaminan-sosial', + 'judul' => 'Jaminan Sosial', + 'kategoriUrl' => 'jaminan-sosial/kategori-statistik', + ], + 'aktivitas-keagamaan' => [ + 'path' => '/data-presisi/statistik/aktivitas-keagamaan', + 'judul' => 'Aktivitas Keagamaan', + 'kategoriUrl' => 'agama/kategori-statistik', + ], + 'ketenagakerjaan' => [ + 'path' => '/data-presisi/statistik/ketenagakerjaan', + 'judul' => 'Ketenagakerjaan', + 'kategoriUrl' => 'ketenagakerjaan/kategori-statistik', + ], + 'adat' => [ + 'path' => '/data-presisi/statistik/adat', + 'judul' => 'Adat', + 'kategoriUrl' => 'adat/kategori-statistik', + ], +]; + +foreach ($statistikPages as $key => $config) { + $path = $config['path']; + $judul = $config['judul']; + + it("opens the statistik {$judul} page", function () use ($path, $judul) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path) + ->assertSee($judul); + + ScreenshotHelper::saveIfEnabled($page, "statistik-{$judul}-page"); + }); + + it("displays kategori statistik list on {$judul}", function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('[data-testid="daftar-statistik"]'); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const items = document.querySelectorAll(\'[data-testid="daftar-statistik"] a\'); + if (items.length > 0) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, "statistik-{$judul}-kategori-list"); + }); + + it("displays filter tahun on {$judul}", function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('[data-testid="filter-tahun"]'); + + ScreenshotHelper::saveIfEnabled($page, "statistik-{$judul}-filter-tahun"); + }); + + it("displays excel button on {$judul}", function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('[data-testid="btn-export-excel"]'); + + ScreenshotHelper::saveIfEnabled($page, "statistik-{$judul}-excel-button"); + }); + + it("displays grafik button on {$judul}", function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('[data-testid="btn-toggle-grafik"]'); + + ScreenshotHelper::saveIfEnabled($page, "statistik-{$judul}-grafik-button"); + }); + + it("displays chart button on {$judul}", function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('[data-testid="btn-toggle-pie"]'); + + ScreenshotHelper::saveIfEnabled($page, "statistik-{$judul}-chart-button"); + }); + + it("displays datatable on {$judul}", function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('[data-testid="datatable-statistik"]'); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const table = document.querySelector(\'[data-testid="datatable-statistik"]\'); + if (table) { resolve(true); } else { setTimeout(check, 500); } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, "statistik-{$judul}-datatable"); + }); + + it("clicks a kategori statistik on {$judul}", function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('[data-testid="daftar-statistik"]'); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const items = document.querySelectorAll(\'[data-testid="daftar-statistik"] a\'); + if (items.length > 0) { + items[0].click(); + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + $page->wait(2000); + + ScreenshotHelper::saveIfEnabled($page, "statistik-{$judul}-kategori-clicked"); + }); + + it("renders bar chart on {$judul}", function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const bar = document.querySelector(\'[data-testid="chart-bar"]\'); + const ready = bar && bar.getContext && bar.width > 0; + if (ready) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, "statistik-{$judul}-bar-chart"); + }); + + it("renders pie chart on {$judul}", function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const donut = document.querySelector(\'[data-testid="chart-donut"]\'); + const ready = donut && donut.getContext && donut.width > 0; + if (ready) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, "statistik-{$judul}-pie-chart"); + }); + + it("has no javascript errors on {$judul}", function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path) + ->assertNoJavaScriptErrors(); + + ScreenshotHelper::saveIfEnabled($page, "statistik-{$judul}-no-errors"); + }); +} diff --git a/tests/Browser/fixtures/adat.json b/tests/Browser/fixtures/adat.json new file mode 100644 index 00000000..4f84d43f --- /dev/null +++ b/tests/Browser/fixtures/adat.json @@ -0,0 +1,23 @@ +{ + "data": [ + { + "id": "1", + "attributes": { + "nama_desa": "Desa Test", + "nik": "5271010101010001", + "nama": "Test User", + "jenis_adat": "Upacara Ngaben", + "jumlah_peserta": 20, + "tahun": "2024", + "status_kelengkapan": "Lengkap" + } + } + ], + "meta": { + "pagination": { + "total": 1, + "current_page": 1, + "last_page": 1 + } + } +} diff --git a/tests/Browser/fixtures/agama.json b/tests/Browser/fixtures/agama.json new file mode 100644 index 00000000..b7a98436 --- /dev/null +++ b/tests/Browser/fixtures/agama.json @@ -0,0 +1,23 @@ +{ + "data": [ + { + "id": "1", + "attributes": { + "nama_desa": "Desa Test", + "nik": "5271010101010001", + "nama": "Test User", + "jenis_agama": "Islam", + "tempat_ibadah": "Masjid Test", + "tahun": "2024", + "status_kelengkapan": "Lengkap" + } + } + ], + "meta": { + "pagination": { + "total": 1, + "current_page": 1, + "last_page": 1 + } + } +} diff --git a/tests/Browser/fixtures/infrastruktur.json b/tests/Browser/fixtures/infrastruktur.json new file mode 100644 index 00000000..ece4317d --- /dev/null +++ b/tests/Browser/fixtures/infrastruktur.json @@ -0,0 +1,21 @@ +{ + "data": [ + { + "id": "1", + "attributes": { + "nama_desa": "Desa Test", + "jenis_transportasi": "Jalan Raya", + "kondisi_transportasi": "Baik", + "jumlah": 10, + "tahun": "2024" + } + } + ], + "meta": { + "pagination": { + "total": 1, + "current_page": 1, + "last_page": 1 + } + } +} diff --git a/tests/Browser/fixtures/jaminan-sosial.json b/tests/Browser/fixtures/jaminan-sosial.json new file mode 100644 index 00000000..50ad0297 --- /dev/null +++ b/tests/Browser/fixtures/jaminan-sosial.json @@ -0,0 +1,23 @@ +{ + "data": [ + { + "id": "1", + "attributes": { + "nama_desa": "Desa Test", + "nik": "5271010101010001", + "nama": "Test User", + "program_bantuan": "BPJS Kesehatan", + "status_kepesertaan": "Aktif", + "tahun": "2024", + "status_kelengkapan": "Lengkap" + } + } + ], + "meta": { + "pagination": { + "total": 1, + "current_page": 1, + "last_page": 1 + } + } +} diff --git a/tests/Browser/fixtures/kategori-statistik-adat.json b/tests/Browser/fixtures/kategori-statistik-adat.json new file mode 100644 index 00000000..5ed87620 --- /dev/null +++ b/tests/Browser/fixtures/kategori-statistik-adat.json @@ -0,0 +1,7 @@ +{ + "success": true, + "data": [ + {"id": "adat-tradisi", "nama": "Tradisi Adat", "judul_halaman": "Adat", "judul_kolom_nama": "Jenis Adat"}, + {"id": "adat-upacara", "nama": "Upacara Adat", "judul_halaman": "Adat", "judul_kolom_nama": "Jenis Adat"} + ] +} diff --git a/tests/Browser/fixtures/kategori-statistik-agama.json b/tests/Browser/fixtures/kategori-statistik-agama.json new file mode 100644 index 00000000..79b747da --- /dev/null +++ b/tests/Browser/fixtures/kategori-statistik-agama.json @@ -0,0 +1,7 @@ +{ + "success": true, + "data": [ + {"id": "agama-islam", "nama": "Islam", "judul_halaman": "Aktivitas Keagamaan", "judul_kolom_nama": "Jenis Agama"}, + {"id": "agama-kristen", "nama": "Kristen", "judul_halaman": "Aktivitas Keagamaan", "judul_kolom_nama": "Jenis Agama"} + ] +} diff --git a/tests/Browser/fixtures/kategori-statistik-jaminan-sosial.json b/tests/Browser/fixtures/kategori-statistik-jaminan-sosial.json new file mode 100644 index 00000000..eac3cd68 --- /dev/null +++ b/tests/Browser/fixtures/kategori-statistik-jaminan-sosial.json @@ -0,0 +1,8 @@ +{ + "success": true, + "data": [ + {"id": "jenis-bantuan", "nama": "Jenis Bantuan", "judul_halaman": "Jaminan Sosial", "judul_kolom_nama": "Jenis Jaminan Sosial"}, + {"id": "jenis-gangguan-mental", "nama": "Jenis Gangguan Mental", "judul_halaman": "Jaminan Sosial", "judul_kolom_nama": "Jenis Jaminan Sosial"}, + {"id": "jenis-penanganan", "nama": "Jenis Penanganan", "judul_halaman": "Jaminan Sosial", "judul_kolom_nama": "Jenis Jaminan Sosial"} + ] +} diff --git a/tests/Browser/fixtures/kategori-statistik-kesehatan.json b/tests/Browser/fixtures/kategori-statistik-kesehatan.json new file mode 100644 index 00000000..82841f26 --- /dev/null +++ b/tests/Browser/fixtures/kategori-statistik-kesehatan.json @@ -0,0 +1,7 @@ +{ + "success": true, + "data": [ + {"id": "golongan-darah", "nama": "Golongan Darah", "judul_halaman": "Kesehatan", "judul_kolom_nama": "Jenis Kesehatan"}, + {"id": "status-gizi", "nama": "Status Gizi", "judul_halaman": "Kesehatan", "judul_kolom_nama": "Jenis Kesehatan"} + ] +} diff --git a/tests/Browser/fixtures/kategori-statistik-ketenagakerjaan.json b/tests/Browser/fixtures/kategori-statistik-ketenagakerjaan.json new file mode 100644 index 00000000..8de55d5c --- /dev/null +++ b/tests/Browser/fixtures/kategori-statistik-ketenagakerjaan.json @@ -0,0 +1,7 @@ +{ + "success": true, + "data": [ + {"id": "pekerjaan", "nama": "Pekerjaan", "judul_halaman": "Ketenagakerjaan", "judul_kolom_nama": "Jenis Ketenagakerjaan"}, + {"id": "pelatihan", "nama": "Pelatihan", "judul_halaman": "Ketenagakerjaan", "judul_kolom_nama": "Jenis Ketenagakerjaan"} + ] +} diff --git a/tests/Browser/fixtures/kategori-statistik-pangan.json b/tests/Browser/fixtures/kategori-statistik-pangan.json new file mode 100644 index 00000000..4fe7f52c --- /dev/null +++ b/tests/Browser/fixtures/kategori-statistik-pangan.json @@ -0,0 +1,7 @@ +{ + "success": true, + "data": [ + {"id": "sandang-pakaian", "nama": "Sandang Pakaian", "judul_halaman": "Pangan", "judul_kolom_nama": "Jenis Sandang"}, + {"id": "sandang-rukun", "nama": "Sandang Rukun", "judul_halaman": "Pangan", "judul_kolom_nama": "Jenis Sandang"} + ] +} diff --git a/tests/Browser/fixtures/kategori-statistik-papan.json b/tests/Browser/fixtures/kategori-statistik-papan.json new file mode 100644 index 00000000..d7fb013e --- /dev/null +++ b/tests/Browser/fixtures/kategori-statistik-papan.json @@ -0,0 +1,7 @@ +{ + "success": true, + "data": [ + {"id": "papan-rumah", "nama": "Kondisi Rumah", "judul_halaman": "Papan", "judul_kolom_nama": "Jenis Papan"}, + {"id": "papan-dinding", "nama": "Jenis Dinding", "judul_halaman": "Papan", "judul_kolom_nama": "Jenis Papan"} + ] +} diff --git a/tests/Browser/fixtures/kategori-statistik-pendidikan.json b/tests/Browser/fixtures/kategori-statistik-pendidikan.json new file mode 100644 index 00000000..e80b4fd0 --- /dev/null +++ b/tests/Browser/fixtures/kategori-statistik-pendidikan.json @@ -0,0 +1,7 @@ +{ + "success": true, + "data": [ + {"id": "partisipasi-sekolah", "nama": "Partisipasi Sekolah", "judul_halaman": "Pendidikan", "judul_kolom_nama": "Jenis Pendidikan"}, + {"id": "ijazah-tertinggi", "nama": "Ijazah Tertinggi", "judul_halaman": "Pendidikan", "judul_kolom_nama": "Jenis Pendidikan"} + ] +} diff --git a/tests/Browser/fixtures/kategori-statistik-sandang.json b/tests/Browser/fixtures/kategori-statistik-sandang.json new file mode 100644 index 00000000..aee9d646 --- /dev/null +++ b/tests/Browser/fixtures/kategori-statistik-sandang.json @@ -0,0 +1,7 @@ +{ + "success": true, + "data": [ + {"id": "sandang-pakaian", "nama": "Sandang Pakaian", "judul_halaman": "Sandang", "judul_kolom_nama": "Jenis Sandang"}, + {"id": "sandang-rukun", "nama": "Sandang Rukun", "judul_halaman": "Sandang", "judul_kolom_nama": "Jenis Sandang"} + ] +} diff --git a/tests/Browser/fixtures/kategori-statistik-seni-budaya.json b/tests/Browser/fixtures/kategori-statistik-seni-budaya.json new file mode 100644 index 00000000..3f37919f --- /dev/null +++ b/tests/Browser/fixtures/kategori-statistik-seni-budaya.json @@ -0,0 +1,7 @@ +{ + "success": true, + "data": [ + {"id": "seni-tradisional", "nama": "Seni Tradisional", "judul_halaman": "Seni Budaya", "judul_kolom_nama": "Jenis Seni Budaya"}, + {"id": "seni-modern", "nama": "Seni Modern", "judul_halaman": "Seni Budaya", "judul_kolom_nama": "Jenis Seni Budaya"} + ] +} diff --git a/tests/Browser/fixtures/laporan-perdesa.json b/tests/Browser/fixtures/laporan-perdesa.json new file mode 100644 index 00000000..2651464b --- /dev/null +++ b/tests/Browser/fixtures/laporan-perdesa.json @@ -0,0 +1,21 @@ +{ + "data": [ + { + "id": "1", + "attributes": { + "nama_desa": "Desa Test", + "kategori": "Penduduk", + "status_kelengkapan": "Lengkap", + "tahun": "2024", + "bulan": "Januari" + } + } + ], + "meta": { + "pagination": { + "total": 1, + "current_page": 1, + "last_page": 1 + } + } +} diff --git a/tests/Browser/fixtures/laporan.json b/tests/Browser/fixtures/laporan.json new file mode 100644 index 00000000..2651464b --- /dev/null +++ b/tests/Browser/fixtures/laporan.json @@ -0,0 +1,21 @@ +{ + "data": [ + { + "id": "1", + "attributes": { + "nama_desa": "Desa Test", + "kategori": "Penduduk", + "status_kelengkapan": "Lengkap", + "tahun": "2024", + "bulan": "Januari" + } + } + ], + "meta": { + "pagination": { + "total": 1, + "current_page": 1, + "last_page": 1 + } + } +} diff --git a/tests/Browser/fixtures/pangan.json b/tests/Browser/fixtures/pangan.json new file mode 100644 index 00000000..849f789c --- /dev/null +++ b/tests/Browser/fixtures/pangan.json @@ -0,0 +1,23 @@ +{ + "data": [ + { + "id": "1", + "attributes": { + "nama_desa": "Desa Test", + "nik": "5271010101010001", + "nama": "Test User", + "komoditas_pangan": "Beras", + "produksi_pangan": "100 kg", + "tahun": "2024", + "status_kelengkapan": "Lengkap" + } + } + ], + "meta": { + "pagination": { + "total": 1, + "current_page": 1, + "last_page": 1 + } + } +} diff --git a/tests/Browser/fixtures/papan.json b/tests/Browser/fixtures/papan.json new file mode 100644 index 00000000..f9598e6e --- /dev/null +++ b/tests/Browser/fixtures/papan.json @@ -0,0 +1,24 @@ +{ + "data": [ + { + "id": "1", + "attributes": { + "nama_desa": "Desa Test", + "nik": "5271010101010001", + "nama": "Test User", + "jenis_papan": "Rumah Sendiri", + "kondisi_rumah": "Baik", + "luas_rumah": "120 m2", + "tahun": "2024", + "status_kelengkapan": "Lengkap" + } + } + ], + "meta": { + "pagination": { + "total": 1, + "current_page": 1, + "last_page": 1 + } + } +} diff --git a/tests/Browser/fixtures/pariwisata.json b/tests/Browser/fixtures/pariwisata.json new file mode 100644 index 00000000..4523d45b --- /dev/null +++ b/tests/Browser/fixtures/pariwisata.json @@ -0,0 +1,23 @@ +{ + "data": [ + { + "id": "1", + "attributes": { + "nama_desa": "Desa Test", + "nama_tempat_wisata": "Pantai Test", + "kategori_wisata": "Pantai", + "sarana_wisata": "Hotel", + "komoditas_wisata": "Kuliner", + "jumlah_pengunjung": 500, + "tahun": "2024" + } + } + ], + "meta": { + "pagination": { + "total": 1, + "current_page": 1, + "last_page": 1 + } + } +} diff --git a/tests/Browser/fixtures/sandang.json b/tests/Browser/fixtures/sandang.json new file mode 100644 index 00000000..0d7dcc0c --- /dev/null +++ b/tests/Browser/fixtures/sandang.json @@ -0,0 +1,23 @@ +{ + "data": [ + { + "id": "1", + "attributes": { + "nama_desa": "Desa Test", + "nik": "5271010101010001", + "nama": "Test User", + "jenis_sandang": "Pakaian Layak", + "jumlah_pakaian": 5, + "tahun": "2024", + "status_kelengkapan": "Lengkap" + } + } + ], + "meta": { + "pagination": { + "total": 1, + "current_page": 1, + "last_page": 1 + } + } +} diff --git a/tests/Browser/fixtures/seni-budaya.json b/tests/Browser/fixtures/seni-budaya.json new file mode 100644 index 00000000..b2c04374 --- /dev/null +++ b/tests/Browser/fixtures/seni-budaya.json @@ -0,0 +1,23 @@ +{ + "data": [ + { + "id": "1", + "attributes": { + "nama_desa": "Desa Test", + "nik": "5271010101010001", + "nama": "Test User", + "jenis_seni": "Tari Tradisional", + "nama_seni": "Tari Piring", + "tahun": "2024", + "status_kelengkapan": "Lengkap" + } + } + ], + "meta": { + "pagination": { + "total": 1, + "current_page": 1, + "last_page": 1 + } + } +} diff --git a/tests/Browser/fixtures/statistik-adat.json b/tests/Browser/fixtures/statistik-adat.json new file mode 100644 index 00000000..5e6f05b4 --- /dev/null +++ b/tests/Browser/fixtures/statistik-adat.json @@ -0,0 +1,33 @@ +{ + "draw": 1, + "recordsTotal": 2, + "recordsFiltered": 2, + "data": [ + { + "id": "1", + "attributes": { + "nama": "Upacara Ngaben", + "jumlah": 30, + "laki_laki": 15, + "perempuan": 15, + "persentase_jumlah": "50.00%", + "persentase_laki_laki": "50.00%", + "persentase_perempuan": "50.00%", + "kriteria": "{\"filter[id]\":\"adat\",\"filter[kriteria]\":\"Upacara Ngaben\"}" + } + }, + { + "id": "2", + "attributes": { + "nama": "Upacara Melasti", + "jumlah": 30, + "laki_laki": 15, + "perempuan": 15, + "persentase_jumlah": "50.00%", + "persentase_laki_laki": "50.00%", + "persentase_perempuan": "50.00%", + "kriteria": "{\"filter[id]\":\"adat\",\"filter[kriteria]\":\"Upacara Melasti\"}" + } + } + ] +} diff --git a/tests/Browser/fixtures/statistik-agama.json b/tests/Browser/fixtures/statistik-agama.json new file mode 100644 index 00000000..79b9bd60 --- /dev/null +++ b/tests/Browser/fixtures/statistik-agama.json @@ -0,0 +1,33 @@ +{ + "draw": 1, + "recordsTotal": 2, + "recordsFiltered": 2, + "data": [ + { + "id": "1", + "attributes": { + "nama": "Masjid", + "jumlah": 50, + "laki_laki": 30, + "perempuan": 20, + "persentase_jumlah": "50.00%", + "persentase_laki_laki": "60.00%", + "persentase_perempuan": "40.00%", + "kriteria": "{\"filter[id]\":\"agama\",\"filter[kriteria]\":\"Masjid\"}" + } + }, + { + "id": "2", + "attributes": { + "nama": "Gereja", + "jumlah": 50, + "laki_laki": 25, + "perempuan": 25, + "persentase_jumlah": "50.00%", + "persentase_laki_laki": "50.00%", + "persentase_perempuan": "50.00%", + "kriteria": "{\"filter[id]\":\"agama\",\"filter[kriteria]\":\"Gereja\"}" + } + } + ] +} diff --git a/tests/Browser/fixtures/statistik-jaminan-sosial.json b/tests/Browser/fixtures/statistik-jaminan-sosial.json new file mode 100644 index 00000000..c7e7447b --- /dev/null +++ b/tests/Browser/fixtures/statistik-jaminan-sosial.json @@ -0,0 +1,33 @@ +{ + "draw": 1, + "recordsTotal": 2, + "recordsFiltered": 2, + "data": [ + { + "id": "1", + "attributes": { + "nama": "BPJS Kesehatan", + "jumlah": 200, + "laki_laki": 100, + "perempuan": 100, + "persentase_jumlah": "66.67%", + "persentase_laki_laki": "50.00%", + "persentase_perempuan": "50.00%", + "kriteria": "{\"filter[id]\":\"jaminan-sosial\",\"filter[kriteria]\":\"BPJS Kesehatan\"}" + } + }, + { + "id": "2", + "attributes": { + "nama": "BPJS Ketenagakerjaan", + "jumlah": 100, + "laki_laki": 60, + "perempuan": 40, + "persentase_jumlah": "33.33%", + "persentase_laki_laki": "60.00%", + "persentase_perempuan": "40.00%", + "kriteria": "{\"filter[id]\":\"jaminan-sosial\",\"filter[kriteria]\":\"BPJS Ketenagakerjaan\"}" + } + } + ] +} diff --git a/tests/Browser/fixtures/statistik-kesehatan.json b/tests/Browser/fixtures/statistik-kesehatan.json new file mode 100644 index 00000000..4f934749 --- /dev/null +++ b/tests/Browser/fixtures/statistik-kesehatan.json @@ -0,0 +1,33 @@ +{ + "draw": 1, + "recordsTotal": 2, + "recordsFiltered": 2, + "data": [ + { + "id": "1", + "attributes": { + "nama": "Golongan Darah O", + "jumlah": 150, + "laki_laki": 80, + "perempuan": 70, + "persentase_jumlah": "50.00%", + "persentase_laki_laki": "53.33%", + "persentase_perempuan": "46.67%", + "kriteria": "{\"filter[id]\":\"kesehatan\",\"filter[kriteria]\":\"Golongan Darah O\"}" + } + }, + { + "id": "2", + "attributes": { + "nama": "Golongan Darah A", + "jumlah": 150, + "laki_laki": 75, + "perempuan": 75, + "persentase_jumlah": "50.00%", + "persentase_laki_laki": "50.00%", + "persentase_perempuan": "50.00%", + "kriteria": "{\"filter[id]\":\"kesehatan\",\"filter[kriteria]\":\"Golongan Darah A\"}" + } + } + ] +} diff --git a/tests/Browser/fixtures/statistik-ketenagakerjaan.json b/tests/Browser/fixtures/statistik-ketenagakerjaan.json new file mode 100644 index 00000000..a87ab501 --- /dev/null +++ b/tests/Browser/fixtures/statistik-ketenagakerjaan.json @@ -0,0 +1,33 @@ +{ + "draw": 1, + "recordsTotal": 2, + "recordsFiltered": 2, + "data": [ + { + "id": "1", + "attributes": { + "nama": "Petani", + "jumlah": 100, + "laki_laki": 70, + "perempuan": 30, + "persentase_jumlah": "33.33%", + "persentase_laki_laki": "70.00%", + "persentase_perempuan": "30.00%", + "kriteria": "{\"filter[id]\":\"ketenagakerjaan\",\"filter[kriteria]\":\"Petani\"}" + } + }, + { + "id": "2", + "attributes": { + "nama": "Pedagang", + "jumlah": 100, + "laki_laki": 50, + "perempuan": 50, + "persentase_jumlah": "33.33%", + "persentase_laki_laki": "50.00%", + "persentase_perempuan": "50.00%", + "kriteria": "{\"filter[id]\":\"ketenagakerjaan\",\"filter[kriteria]\":\"Pedagang\"}" + } + } + ] +} diff --git a/tests/Browser/fixtures/statistik-pangan.json b/tests/Browser/fixtures/statistik-pangan.json new file mode 100644 index 00000000..5eb05f47 --- /dev/null +++ b/tests/Browser/fixtures/statistik-pangan.json @@ -0,0 +1,33 @@ +{ + "draw": 1, + "recordsTotal": 2, + "recordsFiltered": 2, + "data": [ + { + "id": "1", + "attributes": { + "nama": "Beras", + "jumlah": 150, + "laki_laki": 80, + "perempuan": 70, + "persentase_jumlah": "50.00%", + "persentase_laki_laki": "53.33%", + "persentase_perempuan": "46.67%", + "kriteria": "{\"filter[id]\":\"pangan\",\"filter[kriteria]\":\"Beras\"}" + } + }, + { + "id": "2", + "attributes": { + "nama": "Jagung", + "jumlah": 100, + "laki_laki": 55, + "perempuan": 45, + "persentase_jumlah": "50.00%", + "persentase_laki_laki": "55.00%", + "persentase_perempuan": "45.00%", + "kriteria": "{\"filter[id]\":\"pangan\",\"filter[kriteria]\":\"Jagung\"}" + } + } + ] +} diff --git a/tests/Browser/fixtures/statistik-papan.json b/tests/Browser/fixtures/statistik-papan.json new file mode 100644 index 00000000..b86fa119 --- /dev/null +++ b/tests/Browser/fixtures/statistik-papan.json @@ -0,0 +1,33 @@ +{ + "draw": 1, + "recordsTotal": 2, + "recordsFiltered": 2, + "data": [ + { + "id": "1", + "attributes": { + "nama": "Rumah Sendiri", + "jumlah": 180, + "laki_laki": 95, + "perempuan": 85, + "persentase_jumlah": "60.00%", + "persentase_laki_laki": "52.78%", + "persentase_perempuan": "47.22%", + "kriteria": "{\"filter[id]\":\"papan\",\"filter[kriteria]\":\"Rumah Sendiri\"}" + } + }, + { + "id": "2", + "attributes": { + "nama": "Rumah Kontrakan", + "jumlah": 120, + "laki_laki": 65, + "perempuan": 55, + "persentase_jumlah": "40.00%", + "persentase_laki_laki": "54.17%", + "persentase_perempuan": "45.83%", + "kriteria": "{\"filter[id]\":\"papan\",\"filter[kriteria]\":\"Rumah Kontrakan\"}" + } + } + ] +} diff --git a/tests/Browser/fixtures/statistik-pendidikan.json b/tests/Browser/fixtures/statistik-pendidikan.json new file mode 100644 index 00000000..635dadc9 --- /dev/null +++ b/tests/Browser/fixtures/statistik-pendidikan.json @@ -0,0 +1,33 @@ +{ + "draw": 1, + "recordsTotal": 2, + "recordsFiltered": 2, + "data": [ + { + "id": "1", + "attributes": { + "nama": "SD", + "jumlah": 100, + "laki_laki": 55, + "perempuan": 45, + "persentase_jumlah": "33.33%", + "persentase_laki_laki": "55.00%", + "persentase_perempuan": "45.00%", + "kriteria": "{\"filter[id]\":\"pendidikan\",\"filter[kriteria]\":\"SD\"}" + } + }, + { + "id": "2", + "attributes": { + "nama": "SMP", + "jumlah": 100, + "laki_laki": 50, + "perempuan": 50, + "persentase_jumlah": "33.33%", + "persentase_laki_laki": "50.00%", + "persentase_perempuan": "50.00%", + "kriteria": "{\"filter[id]\":\"pendidikan\",\"filter[kriteria]\":\"SMP\"}" + } + } + ] +} diff --git a/tests/Browser/fixtures/statistik-sandang.json b/tests/Browser/fixtures/statistik-sandang.json new file mode 100644 index 00000000..30aa3692 --- /dev/null +++ b/tests/Browser/fixtures/statistik-sandang.json @@ -0,0 +1,33 @@ +{ + "draw": 1, + "recordsTotal": 2, + "recordsFiltered": 2, + "data": [ + { + "id": "1", + "attributes": { + "nama": "Pakaian Layak", + "jumlah": 200, + "laki_laki": 100, + "perempuan": 100, + "persentase_jumlah": "60.00%", + "persentase_laki_laki": "50.00%", + "persentase_perempuan": "50.00%", + "kriteria": "{\"filter[id]\":\"sandang\",\"filter[kriteria]\":\"Pakaian Layak\"}" + } + }, + { + "id": "2", + "attributes": { + "nama": "Pakaian Tidak Layak", + "jumlah": 130, + "laki_laki": 70, + "perempuan": 60, + "persentase_jumlah": "40.00%", + "persentase_laki_laki": "53.85%", + "persentase_perempuan": "46.15%", + "kriteria": "{\"filter[id]\":\"sandang\",\"filter[kriteria]\":\"Pakaian Tidak Layak\"}" + } + } + ] +} diff --git a/tests/Browser/fixtures/statistik-seni-budaya.json b/tests/Browser/fixtures/statistik-seni-budaya.json new file mode 100644 index 00000000..77a62083 --- /dev/null +++ b/tests/Browser/fixtures/statistik-seni-budaya.json @@ -0,0 +1,33 @@ +{ + "draw": 1, + "recordsTotal": 2, + "recordsFiltered": 2, + "data": [ + { + "id": "1", + "attributes": { + "nama": "Tari Tradisional", + "jumlah": 50, + "laki_laki": 20, + "perempuan": 30, + "persentase_jumlah": "50.00%", + "persentase_laki_laki": "40.00%", + "persentase_perempuan": "60.00%", + "kriteria": "{\"filter[id]\":\"seni-budaya\",\"filter[kriteria]\":\"Tari Tradisional\"}" + } + }, + { + "id": "2", + "attributes": { + "nama": "Musik Tradisional", + "jumlah": 50, + "laki_laki": 30, + "perempuan": 20, + "persentase_jumlah": "50.00%", + "persentase_laki_laki": "60.00%", + "persentase_perempuan": "40.00%", + "kriteria": "{\"filter[id]\":\"seni-budaya\",\"filter[kriteria]\":\"Musik Tradisional\"}" + } + } + ] +} From 4fc9934364327e2c3f49c873715331d3d272027c Mon Sep 17 00:00:00 2001 From: Ahmad Afandi Date: Mon, 15 Jun 2026 13:23:11 +0700 Subject: [PATCH 41/43] perbaikan test --- .../filter-status-presisi.blade.php | 2 +- .../views/components/filter-tahun.blade.php | 2 +- .../views/data_pokok/agama/index.blade.php | 4 +- .../data_presisi/adat/index.blade.php | 4 +- .../data_presisi/kesehatan/index.blade.php | 4 +- .../ketenagakerjaan/index.blade.php | 4 +- .../data_presisi/pangan/index.blade.php | 6 +- .../data_presisi/pendidikan/index.blade.php | 4 +- .../data_presisi/seni_budaya/index.blade.php | 4 +- .../data_pokok/infrastruktur/index.blade.php | 4 +- .../data_pokok/jaminan_sosial/index.blade.php | 4 +- .../data_pokok/kesehatan/index.blade.php | 4 +- .../ketenagakerjaan/index.blade.php | 4 +- .../data_pokok/pariwisata/index.blade.php | 4 +- .../data_pokok/pendidikan/index.blade.php | 4 +- resources/views/dtks/papan/index.blade.php | 4 +- resources/views/dtks/sandang/index.blade.php | 4 +- tests/Browser/SmokeDataPokokAdatTest.php | 139 ++++++++ tests/Browser/SmokeDataPokokAgamaTest.php | 139 ++++++++ tests/Browser/SmokeDataPokokKesehatanTest.php | 99 ++++++ .../SmokeDataPokokKetenagakerjaanTest.php | 99 ++++++ tests/Browser/SmokeDataPokokPanganTest.php | 139 ++++++++ tests/Browser/SmokeDataPokokPapanTest.php | 113 +++++++ .../Browser/SmokeDataPokokPendidikanTest.php | 99 ++++++ tests/Browser/SmokeDataPokokSandangTest.php | 113 +++++++ .../Browser/SmokeDataPokokSeniBudayaTest.php | 139 ++++++++ tests/Browser/SmokeDataPokokSubmenusTest.php | 319 ------------------ tests/Browser/SmokeInfrastrukturTest.php | 26 +- tests/Browser/SmokeJaminanSosialTest.php | 50 +-- tests/Browser/SmokeKelembagaanTest.php | 8 +- tests/Browser/SmokeKesehatanTest.php | 16 +- tests/Browser/SmokeKetenagakerjaanTest.php | 16 +- tests/Browser/SmokeLaporanBulananTest.php | 16 +- .../SmokeLaporanPengisianPerDesaTest.php | 18 +- tests/Browser/SmokeLaporanPengisianTest.php | 20 +- tests/Browser/SmokePariwisataTest.php | 46 +-- tests/Browser/SmokePendidikanTest.php | 16 +- tests/Browser/SmokePendudukTest.php | 8 +- tests/Browser/SmokePenerimaBantuanTest.php | 8 +- tests/Browser/SmokeStatistikAdatTest.php | 185 ++++++++++ .../SmokeStatistikAktivitasKeagamaanTest.php | 185 ++++++++++ tests/Browser/SmokeStatistikBantuanTest.php | 30 +- .../SmokeStatistikJaminanSosialTest.php | 185 ++++++++++ tests/Browser/SmokeStatistikKeluargaTest.php | 30 +- tests/Browser/SmokeStatistikKesehatanTest.php | 185 ++++++++++ .../SmokeStatistikKetenagakerjaanTest.php | 185 ++++++++++ tests/Browser/SmokeStatistikPanganTest.php | 185 ++++++++++ tests/Browser/SmokeStatistikPapanTest.php | 185 ++++++++++ .../Browser/SmokeStatistikPendidikanTest.php | 185 ++++++++++ tests/Browser/SmokeStatistikPendudukTest.php | 36 +- tests/Browser/SmokeStatistikRtmTest.php | 30 +- tests/Browser/SmokeStatistikSandangTest.php | 185 ++++++++++ .../Browser/SmokeStatistikSeniBudayaTest.php | 185 ++++++++++ tests/Browser/SmokeStatistikSubmenusTest.php | 225 ------------ 54 files changed, 3100 insertions(+), 813 deletions(-) create mode 100644 tests/Browser/SmokeDataPokokAdatTest.php create mode 100644 tests/Browser/SmokeDataPokokAgamaTest.php create mode 100644 tests/Browser/SmokeDataPokokKesehatanTest.php create mode 100644 tests/Browser/SmokeDataPokokKetenagakerjaanTest.php create mode 100644 tests/Browser/SmokeDataPokokPanganTest.php create mode 100644 tests/Browser/SmokeDataPokokPapanTest.php create mode 100644 tests/Browser/SmokeDataPokokPendidikanTest.php create mode 100644 tests/Browser/SmokeDataPokokSandangTest.php create mode 100644 tests/Browser/SmokeDataPokokSeniBudayaTest.php delete mode 100644 tests/Browser/SmokeDataPokokSubmenusTest.php create mode 100644 tests/Browser/SmokeStatistikAdatTest.php create mode 100644 tests/Browser/SmokeStatistikAktivitasKeagamaanTest.php create mode 100644 tests/Browser/SmokeStatistikJaminanSosialTest.php create mode 100644 tests/Browser/SmokeStatistikKesehatanTest.php create mode 100644 tests/Browser/SmokeStatistikKetenagakerjaanTest.php create mode 100644 tests/Browser/SmokeStatistikPanganTest.php create mode 100644 tests/Browser/SmokeStatistikPapanTest.php create mode 100644 tests/Browser/SmokeStatistikPendidikanTest.php create mode 100644 tests/Browser/SmokeStatistikSandangTest.php create mode 100644 tests/Browser/SmokeStatistikSeniBudayaTest.php delete mode 100644 tests/Browser/SmokeStatistikSubmenusTest.php diff --git a/resources/views/components/filter-status-presisi.blade.php b/resources/views/components/filter-status-presisi.blade.php index b3681113..e4cefb05 100644 --- a/resources/views/components/filter-status-presisi.blade.php +++ b/resources/views/components/filter-status-presisi.blade.php @@ -1,5 +1,5 @@
- @foreach(App\Enums\StatusKelengkapanPresisiEnum::getAll() as $value => $label) diff --git a/resources/views/components/filter-tahun.blade.php b/resources/views/components/filter-tahun.blade.php index 37fe3266..84d36ffe 100644 --- a/resources/views/components/filter-tahun.blade.php +++ b/resources/views/components/filter-tahun.blade.php @@ -1,5 +1,5 @@
- @php $currentYear = date('Y'); $startYear = $currentYear - 5; diff --git a/resources/views/data_pokok/agama/index.blade.php b/resources/views/data_pokok/agama/index.blade.php index 731d48a2..cc500a35 100644 --- a/resources/views/data_pokok/agama/index.blade.php +++ b/resources/views/data_pokok/agama/index.blade.php @@ -33,14 +33,14 @@
- +
+ ]" testId="btn-export-excel" />
diff --git a/resources/views/data_pokok/data_presisi/adat/index.blade.php b/resources/views/data_pokok/data_presisi/adat/index.blade.php index 12719410..a60dc9a1 100644 --- a/resources/views/data_pokok/data_presisi/adat/index.blade.php +++ b/resources/views/data_pokok/data_presisi/adat/index.blade.php @@ -33,9 +33,9 @@
- +
- +
diff --git a/resources/views/data_pokok/data_presisi/kesehatan/index.blade.php b/resources/views/data_pokok/data_presisi/kesehatan/index.blade.php index dffa2ed7..92e4fa6a 100644 --- a/resources/views/data_pokok/data_presisi/kesehatan/index.blade.php +++ b/resources/views/data_pokok/data_presisi/kesehatan/index.blade.php @@ -31,9 +31,9 @@
- +
- +
diff --git a/resources/views/data_pokok/data_presisi/ketenagakerjaan/index.blade.php b/resources/views/data_pokok/data_presisi/ketenagakerjaan/index.blade.php index 6b5fdffc..4d79fbdb 100644 --- a/resources/views/data_pokok/data_presisi/ketenagakerjaan/index.blade.php +++ b/resources/views/data_pokok/data_presisi/ketenagakerjaan/index.blade.php @@ -31,9 +31,9 @@
- +
- +
diff --git a/resources/views/data_pokok/data_presisi/pangan/index.blade.php b/resources/views/data_pokok/data_presisi/pangan/index.blade.php index 97f79751..ef59db6a 100644 --- a/resources/views/data_pokok/data_presisi/pangan/index.blade.php +++ b/resources/views/data_pokok/data_presisi/pangan/index.blade.php @@ -31,14 +31,14 @@
- +
- +
-
No
+
diff --git a/resources/views/data_pokok/data_presisi/pendidikan/index.blade.php b/resources/views/data_pokok/data_presisi/pendidikan/index.blade.php index 80f76f02..b4d77b3f 100644 --- a/resources/views/data_pokok/data_presisi/pendidikan/index.blade.php +++ b/resources/views/data_pokok/data_presisi/pendidikan/index.blade.php @@ -31,9 +31,9 @@
- +
- +
diff --git a/resources/views/data_pokok/data_presisi/seni_budaya/index.blade.php b/resources/views/data_pokok/data_presisi/seni_budaya/index.blade.php index c47737ac..ae7a589b 100644 --- a/resources/views/data_pokok/data_presisi/seni_budaya/index.blade.php +++ b/resources/views/data_pokok/data_presisi/seni_budaya/index.blade.php @@ -31,9 +31,9 @@
- +
- +
diff --git a/resources/views/data_pokok/infrastruktur/index.blade.php b/resources/views/data_pokok/infrastruktur/index.blade.php index ec18855e..98794347 100644 --- a/resources/views/data_pokok/infrastruktur/index.blade.php +++ b/resources/views/data_pokok/infrastruktur/index.blade.php @@ -43,9 +43,9 @@
- +
- +
Aksi
diff --git a/resources/views/data_pokok/jaminan_sosial/index.blade.php b/resources/views/data_pokok/jaminan_sosial/index.blade.php index d10d97ff..03e050c8 100644 --- a/resources/views/data_pokok/jaminan_sosial/index.blade.php +++ b/resources/views/data_pokok/jaminan_sosial/index.blade.php @@ -59,13 +59,13 @@
- +
+ ]" testId="btn-export-excel" />
diff --git a/resources/views/data_pokok/kesehatan/index.blade.php b/resources/views/data_pokok/kesehatan/index.blade.php index ffa189cd..6251721d 100644 --- a/resources/views/data_pokok/kesehatan/index.blade.php +++ b/resources/views/data_pokok/kesehatan/index.blade.php @@ -45,8 +45,8 @@
{{ $title }}
- - + +
diff --git a/resources/views/data_pokok/ketenagakerjaan/index.blade.php b/resources/views/data_pokok/ketenagakerjaan/index.blade.php index e24e7781..e3344d33 100644 --- a/resources/views/data_pokok/ketenagakerjaan/index.blade.php +++ b/resources/views/data_pokok/ketenagakerjaan/index.blade.php @@ -45,9 +45,9 @@
{{ $title }}
- + + filename="data_ketenagakerjaan" testId="btn-export-excel" />
diff --git a/resources/views/data_pokok/pariwisata/index.blade.php b/resources/views/data_pokok/pariwisata/index.blade.php index f5e9a00d..e1d88860 100644 --- a/resources/views/data_pokok/pariwisata/index.blade.php +++ b/resources/views/data_pokok/pariwisata/index.blade.php @@ -55,9 +55,9 @@
- +
- +
diff --git a/resources/views/data_pokok/pendidikan/index.blade.php b/resources/views/data_pokok/pendidikan/index.blade.php index cc6edda6..7115db27 100644 --- a/resources/views/data_pokok/pendidikan/index.blade.php +++ b/resources/views/data_pokok/pendidikan/index.blade.php @@ -45,8 +45,8 @@
Data Pendidikan Penduduk dan DTKS
- - + +
diff --git a/resources/views/dtks/papan/index.blade.php b/resources/views/dtks/papan/index.blade.php index cebc5479..bbf5c0b0 100644 --- a/resources/views/dtks/papan/index.blade.php +++ b/resources/views/dtks/papan/index.blade.php @@ -30,9 +30,9 @@
- +
- +
diff --git a/resources/views/dtks/sandang/index.blade.php b/resources/views/dtks/sandang/index.blade.php index 73729857..d9d40a0d 100644 --- a/resources/views/dtks/sandang/index.blade.php +++ b/resources/views/dtks/sandang/index.blade.php @@ -30,9 +30,9 @@
- +
- +
diff --git a/tests/Browser/SmokeDataPokokAdatTest.php b/tests/Browser/SmokeDataPokokAdatTest.php new file mode 100644 index 00000000..f8e65915 --- /dev/null +++ b/tests/Browser/SmokeDataPokokAdatTest.php @@ -0,0 +1,139 @@ +user = SessionState::loginAdminUser(); +}); + +afterEach(function () { + SessionState::clear(); +}); + +$path = '/data-presisi/adat'; + +it('opens the adat page', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-adat-page'); +}); + +it('renders chart', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const chart = document.querySelector(\'[data-testid=\"chart-pie-adat\"]\'); + const ready = chart && chart.offsetWidth > 0; + if (ready) { resolve(true); } else { setTimeout(check, 500); } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-adat-chart'); +}); + +it('displays filter tahun', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@filter-tahun'); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-adat-filter-tahun'); +}); + +it('displays filter status', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@filter-status-kelengkapan'); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-adat-filter-status'); +}); + +it('displays cetak button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-cetak'); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-adat-cetak-button'); +}); + +it('displays excel button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-export-excel'); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-adat-excel-button'); +}); + +it('displays datatable', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path) + ->assertVisible('@datatable-adat'); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-adat-datatable'); +}); + +it('datatable displays data', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const table = document.querySelector(\'[data-testid=\"datatable-adat\"]\'); + if (table) { + const rows = table.querySelectorAll(\'tbody tr\'); + if (rows.length > 0 && !rows[0].classList.contains(\'dataTables_empty\')) { + resolve(true); + } else { + setTimeout(check, 500); + } + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-adat-datatable-data'); +}); + +it('has detail button in data', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const table = document.querySelector(\'[data-testid=\"datatable-adat\"]\'); + if (table) { + const rows = table.querySelectorAll(\'tbody tr\'); + if (rows.length > 0 && !rows[0].classList.contains(\'dataTables_empty\')) { + const detailBtn = rows[0].querySelector(\'[data-button="Detail"]\') || rows[0].querySelector(\'a[href*="detail"]\'); + resolve(!!detailBtn); + } else { + setTimeout(check, 500); + } + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-adat-detail-button'); +}); + +it('has no javascript errors', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path) + ->assertNoJavaScriptErrors(); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-adat-no-errors'); +}); diff --git a/tests/Browser/SmokeDataPokokAgamaTest.php b/tests/Browser/SmokeDataPokokAgamaTest.php new file mode 100644 index 00000000..83550eda --- /dev/null +++ b/tests/Browser/SmokeDataPokokAgamaTest.php @@ -0,0 +1,139 @@ +user = SessionState::loginAdminUser(); +}); + +afterEach(function () { + SessionState::clear(); +}); + +$path = '/data-pokok/agama'; + +it('opens the agama page', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-agama-page'); +}); + +it('renders chart', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const chart = document.querySelector(\'[data-testid=\"chart-pie-agama\"]\'); + const ready = chart && chart.offsetWidth > 0; + if (ready) { resolve(true); } else { setTimeout(check, 500); } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-agama-chart'); +}); + +it('displays filter tahun', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@filter-tahun'); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-agama-filter-tahun'); +}); + +it('displays filter status', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@filter-status-kelengkapan'); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-agama-filter-status'); +}); + +it('displays cetak button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-cetak'); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-agama-cetak-button'); +}); + +it('displays excel button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-export-excel'); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-agama-excel-button'); +}); + +it('displays datatable', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path) + ->assertVisible('@datatable-agama'); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-agama-datatable'); +}); + +it('datatable displays data', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const table = document.querySelector(\'[data-testid=\"datatable-agama\"]\'); + if (table) { + const rows = table.querySelectorAll(\'tbody tr\'); + if (rows.length > 0 && !rows[0].classList.contains(\'dataTables_empty\')) { + resolve(true); + } else { + setTimeout(check, 500); + } + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-agama-datatable-data'); +}); + +it('has detail button in data', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const table = document.querySelector(\'[data-testid=\"datatable-agama\"]\'); + if (table) { + const rows = table.querySelectorAll(\'tbody tr\'); + if (rows.length > 0 && !rows[0].classList.contains(\'dataTables_empty\')) { + const detailBtn = rows[0].querySelector(\'[data-button="Detail"]\') || rows[0].querySelector(\'a[href*="detail"]\'); + resolve(!!detailBtn); + } else { + setTimeout(check, 500); + } + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-agama-detail-button'); +}); + +it('has no javascript errors', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path) + ->assertNoJavaScriptErrors(); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-agama-no-errors'); +}); diff --git a/tests/Browser/SmokeDataPokokKesehatanTest.php b/tests/Browser/SmokeDataPokokKesehatanTest.php new file mode 100644 index 00000000..429be17f --- /dev/null +++ b/tests/Browser/SmokeDataPokokKesehatanTest.php @@ -0,0 +1,99 @@ +user = SessionState::loginAdminUser(); +}); + +afterEach(function () { + SessionState::clear(); +}); + +$path = '/data-pokok/kesehatan'; + +it('opens the kesehatan page', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-kesehatan-page'); +}); + +it('renders chart', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const chart1 = document.querySelector(\'canvas\'); + const ready1 = chart1 && chart1.getContext && chart1.width > 0; + const chart2 = document.querySelectorAll(\'canvas\')[1]; + const ready2 = chart2 && chart2.getContext && chart2.width > 0; + if (ready1 && ready2) { resolve(true); } else { setTimeout(check, 500); } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-kesehatan-chart'); +}); + +it('displays cetak button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-cetak'); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-kesehatan-cetak-button'); +}); + +it('displays excel button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-export-excel'); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-kesehatan-excel-button'); +}); + +it('displays datatable', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path) + ->assertVisible('@datatable-kesehatan'); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-kesehatan-datatable'); +}); + +it('datatable displays data', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const table = document.querySelector(\'[data-testid="datatable-kesehatan"]\'); + if (table) { + const rows = table.querySelectorAll(\'tbody tr\'); + if (rows.length > 0 && !rows[0].classList.contains(\'dataTables_empty\')) { + resolve(true); + } else { + setTimeout(check, 500); + } + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-kesehatan-datatable-data'); +}); + +it('has no javascript errors', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path) + ->assertNoJavaScriptErrors(); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-kesehatan-no-errors'); +}); diff --git a/tests/Browser/SmokeDataPokokKetenagakerjaanTest.php b/tests/Browser/SmokeDataPokokKetenagakerjaanTest.php new file mode 100644 index 00000000..db9a9ee3 --- /dev/null +++ b/tests/Browser/SmokeDataPokokKetenagakerjaanTest.php @@ -0,0 +1,99 @@ +user = SessionState::loginAdminUser(); +}); + +afterEach(function () { + SessionState::clear(); +}); + +$path = '/data-pokok/ketenagakerjaan'; + +it('opens the ketenagakerjaan page', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-ketenagakerjaan-page'); +}); + +it('renders chart', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const chart1 = document.querySelector(\'[data-testid="chart-bar"]\'); + const ready1 = chart1 && chart1.getContext && chart1.width > 0; + const chart2 = document.querySelector(\'[data-testid="chart-donut"]\'); + const ready2 = chart2 && chart2.getContext && chart2.width > 0; + if (ready1 && ready2) { resolve(true); } else { setTimeout(check, 500); } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-ketenagakerjaan-chart'); +}); + +it('displays cetak button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@bt-cetak'); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-ketenagakerjaan-cetak-button'); +}); + +it('displays excel button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@bt-excel'); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-ketenagakerjaan-excel-button'); +}); + +it('displays datatable', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path) + ->assertVisible('@datatable-ketenagakerjaan'); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-ketenagakerjaan-datatable'); +}); + +it('datatable displays data', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const table = document.querySelector(\'[data-testid="datatable-ketenagakerjaan"]\'); + if (table) { + const rows = table.querySelectorAll(\'tbody tr\'); + if (rows.length > 0 && !rows[0].classList.contains(\'dataTables_empty\')) { + resolve(true); + } else { + setTimeout(check, 500); + } + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-ketenagakerjaan-datatable-data'); +}); + +it('has no javascript errors', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path) + ->assertNoJavaScriptErrors(); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-ketenagakerjaan-no-errors'); +}); diff --git a/tests/Browser/SmokeDataPokokPanganTest.php b/tests/Browser/SmokeDataPokokPanganTest.php new file mode 100644 index 00000000..5708f4b0 --- /dev/null +++ b/tests/Browser/SmokeDataPokokPanganTest.php @@ -0,0 +1,139 @@ +user = SessionState::loginAdminUser(); +}); + +afterEach(function () { + SessionState::clear(); +}); + +$path = '/data-presisi/pangan'; + +it('opens the pangan page', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-pangan-page'); +}); + +it('renders chart', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const chart = document.querySelector(\'canvas\'); + const ready = chart && chart.getContext && chart.width > 0; + if (ready) { resolve(true); } else { setTimeout(check, 500); } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-pangan-chart'); +}); + +it('displays filter tahun', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@filter-tahun'); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-pangan-filter-tahun'); +}); + +it('displays filter status', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@filter-status-kelengkapan'); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-pangan-filter-status'); +}); + +it('displays cetak button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-cetak'); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-pangan-cetak-button'); +}); + +it('displays excel button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-export-excel'); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-pangan-excel-button'); +}); + +it('displays datatable', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path) + ->assertVisible('@datatable-pangan'); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-pangan-datatable'); +}); + +it('datatable displays data', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const table = document.querySelector(\'[data-testid=\"datatable-pangan\"]\'); + if (table) { + const rows = table.querySelectorAll(\'tbody tr\'); + if (rows.length > 0 && !rows[0].classList.contains(\'dataTables_empty\')) { + resolve(true); + } else { + setTimeout(check, 500); + } + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-pangan-datatable-data'); +}); + +it('has detail button in data', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const table = document.querySelector(\'[data-testid=\"datatable-pangan\"]\'); + if (table) { + const rows = table.querySelectorAll(\'tbody tr\'); + if (rows.length > 0 && !rows[0].classList.contains(\'dataTables_empty\')) { + const detailBtn = rows[0].querySelector(\'[data-button="Detail"]\') || rows[0].querySelector(\'a[href*="detail"]\'); + resolve(!!detailBtn); + } else { + setTimeout(check, 500); + } + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-pangan-detail-button'); +}); + +it('has no javascript errors', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path) + ->assertNoJavaScriptErrors(); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-pangan-no-errors'); +}); diff --git a/tests/Browser/SmokeDataPokokPapanTest.php b/tests/Browser/SmokeDataPokokPapanTest.php new file mode 100644 index 00000000..c18f9d6f --- /dev/null +++ b/tests/Browser/SmokeDataPokokPapanTest.php @@ -0,0 +1,113 @@ +user = SessionState::loginAdminUser(); +}); + +afterEach(function () { + SessionState::clear(); +}); + +$path = '/satu-data/dtks/papan'; + +it('opens the papan page', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-papan-page'); +}); + +it('displays filter tahun', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@filter-tahun'); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-papan-filter-tahun'); +}); + +it('displays cetak button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-cetak'); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-papan-cetak-button'); +}); + +it('displays excel button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-export-excel'); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-papan-excel-button'); +}); + +it('displays datatable', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path) + ->assertVisible('@datatable-papan'); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-papan-datatable'); +}); + +it('datatable displays data', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const table = document.querySelector(\'[data-testid=\"datatable-papan\"]\'); + if (table) { + const rows = table.querySelectorAll(\'tbody tr\'); + if (rows.length > 0 && !rows[0].classList.contains(\'dataTables_empty\')) { + resolve(true); + } else { + setTimeout(check, 500); + } + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-papan-datatable-data'); +}); + +it('has detail button in data', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const table = document.querySelector(\'[data-testid=\"datatable-papan\"]\'); + if (table) { + const rows = table.querySelectorAll(\'tbody tr\'); + if (rows.length > 0 && !rows[0].classList.contains(\'dataTables_empty\')) { + const detailBtn = rows[0].querySelector(\'[data-button="Detail"]\') || rows[0].querySelector(\'a[href*="detail"]\'); + resolve(!!detailBtn); + } else { + setTimeout(check, 500); + } + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-papan-detail-button'); +}); + +it('has no javascript errors', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path) + ->assertNoJavaScriptErrors(); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-papan-no-errors'); +}); diff --git a/tests/Browser/SmokeDataPokokPendidikanTest.php b/tests/Browser/SmokeDataPokokPendidikanTest.php new file mode 100644 index 00000000..83f08692 --- /dev/null +++ b/tests/Browser/SmokeDataPokokPendidikanTest.php @@ -0,0 +1,99 @@ +user = SessionState::loginAdminUser(); +}); + +afterEach(function () { + SessionState::clear(); +}); + +$path = '/data-pokok/pendidikan'; + +it('opens the pendidikan page', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-pendidikan-page'); +}); + +it('renders chart', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const chart1 = document.querySelector(\'[data-testid="chart-donut"]\'); + const ready1 = chart1 && chart1.getContext && chart1.width > 0; + const chart2 = document.querySelector(\'[data-testid="chart-bar"]\'); + const ready2 = chart2 && chart2.getContext && chart2.width > 0; + if (ready1 && ready2) { resolve(true); } else { setTimeout(check, 500); } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-pendidikan-chart'); +}); + +it('displays cetak button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@bt-cetak'); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-pendidikan-cetak-button'); +}); + +it('displays excel button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@bt-excel'); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-pendidikan-excel-button'); +}); + +it('displays datatable', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path) + ->assertVisible('@datatable-pendidikan'); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-pendidikan-datatable'); +}); + +it('datatable displays data', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const table = document.querySelector(\'[data-testid="datatable-pendidikan"]\'); + if (table) { + const rows = table.querySelectorAll(\'tbody tr\'); + if (rows.length > 0 && !rows[0].classList.contains(\'dataTables_empty\')) { + resolve(true); + } else { + setTimeout(check, 500); + } + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-pendidikan-datatable-data'); +}); + +it('has no javascript errors', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path) + ->assertNoJavaScriptErrors(); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-pendidikan-no-errors'); +}); diff --git a/tests/Browser/SmokeDataPokokSandangTest.php b/tests/Browser/SmokeDataPokokSandangTest.php new file mode 100644 index 00000000..c5213586 --- /dev/null +++ b/tests/Browser/SmokeDataPokokSandangTest.php @@ -0,0 +1,113 @@ +user = SessionState::loginAdminUser(); +}); + +afterEach(function () { + SessionState::clear(); +}); + +$path = '/data-pokok/sandang'; + +it('opens the sandang page', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-sandang-page'); +}); + +it('displays filter tahun', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@filter-tahun'); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-sandang-filter-tahun'); +}); + +it('displays cetak button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-cetak'); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-sandang-cetak-button'); +}); + +it('displays excel button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-export-excel'); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-sandang-excel-button'); +}); + +it('displays datatable', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path) + ->assertVisible('@datatable-sandang'); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-sandang-datatable'); +}); + +it('datatable displays data', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const table = document.querySelector(\'[data-testid=\"datatable-sandang\"]\'); + if (table) { + const rows = table.querySelectorAll(\'tbody tr\'); + if (rows.length > 0 && !rows[0].classList.contains(\'dataTables_empty\')) { + resolve(true); + } else { + setTimeout(check, 500); + } + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-sandang-datatable-data'); +}); + +it('has detail button in data', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const table = document.querySelector(\'[data-testid=\"datatable-sandang\"]\'); + if (table) { + const rows = table.querySelectorAll(\'tbody tr\'); + if (rows.length > 0 && !rows[0].classList.contains(\'dataTables_empty\')) { + const detailBtn = rows[0].querySelector(\'[data-button="Detail"]\') || rows[0].querySelector(\'a[href*="detail"]\'); + resolve(!!detailBtn); + } else { + setTimeout(check, 500); + } + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-sandang-detail-button'); +}); + +it('has no javascript errors', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path) + ->assertNoJavaScriptErrors(); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-sandang-no-errors'); +}); diff --git a/tests/Browser/SmokeDataPokokSeniBudayaTest.php b/tests/Browser/SmokeDataPokokSeniBudayaTest.php new file mode 100644 index 00000000..fb34a931 --- /dev/null +++ b/tests/Browser/SmokeDataPokokSeniBudayaTest.php @@ -0,0 +1,139 @@ +user = SessionState::loginAdminUser(); +}); + +afterEach(function () { + SessionState::clear(); +}); + +$path = '/data-presisi/seni-budaya'; + +it('opens the seni budaya page', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-seni-budaya-page'); +}); + +it('renders chart', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const chart = document.querySelector(\'[data-testid=\"chart-bar-seni-budaya\"]\'); + const ready = chart && chart.getContext && chart.width > 0; + if (ready) { resolve(true); } else { setTimeout(check, 500); } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-seni-budaya-chart'); +}); + +it('displays filter tahun', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@filter-tahun'); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-seni-budaya-filter-tahun'); +}); + +it('displays filter status', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@filter-status-kelengkapan'); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-seni-budaya-filter-status'); +}); + +it('displays cetak button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-cetak'); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-seni-budaya-cetak-button'); +}); + +it('displays excel button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-export-excel'); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-seni-budaya-excel-button'); +}); + +it('displays datatable', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path) + ->assertVisible('@datatable-seni-budaya'); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-seni-budaya-datatable'); +}); + +it('datatable displays data', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const table = document.querySelector(\'[data-testid=\"datatable-seni-budaya\"]\'); + if (table) { + const rows = table.querySelectorAll(\'tbody tr\'); + if (rows.length > 0 && !rows[0].classList.contains(\'dataTables_empty\')) { + resolve(true); + } else { + setTimeout(check, 500); + } + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-seni-budaya-datatable-data'); +}); + +it('has detail button in data', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const table = document.querySelector(\'[data-testid=\"datatable-seni-budaya\"]\'); + if (table) { + const rows = table.querySelectorAll(\'tbody tr\'); + if (rows.length > 0 && !rows[0].classList.contains(\'dataTables_empty\')) { + const detailBtn = rows[0].querySelector(\'[data-button="Detail"]\') || rows[0].querySelector(\'a[href*="detail"]\'); + resolve(!!detailBtn); + } else { + setTimeout(check, 500); + } + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-seni-budaya-detail-button'); +}); + +it('has no javascript errors', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path) + ->assertNoJavaScriptErrors(); + + ScreenshotHelper::saveIfEnabled($page, 'data-pokok-seni-budaya-no-errors'); +}); diff --git a/tests/Browser/SmokeDataPokokSubmenusTest.php b/tests/Browser/SmokeDataPokokSubmenusTest.php deleted file mode 100644 index b3eaee2c..00000000 --- a/tests/Browser/SmokeDataPokokSubmenusTest.php +++ /dev/null @@ -1,319 +0,0 @@ -user = SessionState::loginAdminUser(); -}); - -afterEach(function () { - SessionState::clear(); -}); - -$dataPokokPages = [ - 'pangan' => [ - 'path' => '/data-presisi/pangan', - 'tableSelector' => '#table-pangan', - 'chartSelector' => '#barChart', - 'hasFilterTahun' => true, - 'hasFilterStatus' => true, - 'hasCetakButton' => true, - 'hasExcelButton' => true, - 'hasDetailButton' => true, - 'hasCollapse' => false, - ], - 'sandang' => [ - 'path' => '/data-pokok/sandang', - 'tableSelector' => '#table-dtks', - 'chartSelector' => null, - 'hasFilterTahun' => true, - 'hasFilterStatus' => false, - 'hasCetakButton' => true, - 'hasExcelButton' => true, - 'hasDetailButton' => true, - 'hasCollapse' => false, - ], - 'papan' => [ - 'path' => '/satu-data/dtks/papan', - 'tableSelector' => '#table-dtks', - 'chartSelector' => null, - 'hasFilterTahun' => true, - 'hasFilterStatus' => false, - 'hasCetakButton' => true, - 'hasExcelButton' => true, - 'hasDetailButton' => true, - 'hasCollapse' => false, - ], - 'kesehatan' => [ - 'path' => '/data-pokok/kesehatan', - 'tableSelector' => '[data-testid="datatable-kesehatan"]', - 'tableTestId' => 'datatable-kesehatan', - 'chartSelector' => '#donutChart', - 'chartSelector2' => '#barChart', - 'hasFilterTahun' => false, - 'hasFilterStatus' => false, - 'hasCetakButton' => true, - 'cetakTestId' => 'bt-cetak', - 'hasExcelButton' => true, - 'excelTestId' => 'bt-excel', - 'hasDetailButton' => false, - 'hasCollapse' => false, - ], - 'pendidikan' => [ - 'path' => '/data-pokok/pendidikan', - 'tableSelector' => '[data-testid="datatable-pendidikan"]', - 'tableTestId' => 'datatable-pendidikan', - 'chartSelector' => '#donutChart', - 'chartSelector2' => '#barChart', - 'hasFilterTahun' => false, - 'hasFilterStatus' => false, - 'hasCetakButton' => true, - 'cetakTestId' => 'bt-cetak', - 'hasExcelButton' => true, - 'excelTestId' => 'bt-excel', - 'hasDetailButton' => false, - 'hasCollapse' => false, - ], - 'ketenagakerjaan' => [ - 'path' => '/data-pokok/ketenagakerjaan', - 'tableSelector' => '[data-testid="datatable-ketenagakerjaan"]', - 'tableTestId' => 'datatable-ketenagakerjaan', - 'chartSelector' => '#barChart', - 'chartSelector2' => '#donutChart', - 'hasFilterTahun' => false, - 'hasFilterStatus' => false, - 'hasCetakButton' => true, - 'cetakTestId' => 'bt-cetak', - 'hasExcelButton' => true, - 'excelTestId' => 'bt-excel', - 'hasDetailButton' => false, - 'hasCollapse' => false, - ], - 'adat' => [ - 'path' => '/data-presisi/adat', - 'tableSelector' => '#adat', - 'chartSelector' => '#pie1', - 'hasFilterTahun' => true, - 'hasFilterStatus' => true, - 'hasCetakButton' => true, - 'hasExcelButton' => true, - 'hasDetailButton' => true, - 'hasCollapse' => false, - ], - 'agama' => [ - 'path' => '/data-pokok/agama', - 'tableSelector' => '#agama', - 'chartSelector' => '#pie1', - 'hasFilterTahun' => true, - 'hasFilterStatus' => true, - 'hasCetakButton' => true, - 'hasExcelButton' => true, - 'hasDetailButton' => true, - 'hasCollapse' => false, - ], - 'seni-budaya' => [ - 'path' => '/data-presisi/seni-budaya', - 'tableSelector' => '#table-seni-budaya', - 'chartSelector' => '#barChart', - 'hasFilterTahun' => true, - 'hasFilterStatus' => true, - 'hasCetakButton' => true, - 'hasExcelButton' => true, - 'hasDetailButton' => true, - 'hasCollapse' => false, - ], -]; - -foreach ($dataPokokPages as $key => $config) { - $path = $config['path']; - $tableSelector = $config['tableSelector']; - - it("opens the {$key} page", function () use ($path) { - $page = SessionState::loginAndNavigate($this->user, $path) - ->assertPathIs($path); - - ScreenshotHelper::saveIfEnabled($page, "{$key}-page"); - }); - - if ($config['chartSelector']) { - $chartSelector = $config['chartSelector']; - $chartSelector2 = $config['chartSelector2'] ?? null; - - it("renders chart on {$key}", function () use ($path, $chartSelector, $chartSelector2) { - $page = SessionState::loginAndNavigate($this->user, $path) - ->assertPathIs($path); - - $page->assertScript( - "new Promise((resolve) => { - const check = () => { - const chart1 = document.querySelector('{$chartSelector}'); - const ready1 = chart1 && (chart1.getContext ? chart1.getContext && chart1.width > 0 : chart1.offsetWidth > 0); - " . ($chartSelector2 ? " - const chart2 = document.querySelector('{$chartSelector2}'); - const ready2 = chart2 && (chart2.getContext ? chart2.getContext && chart2.width > 0 : chart2.offsetWidth > 0); - if (ready1 && ready2) { resolve(true); } else { setTimeout(check, 500); } - " : " - if (ready1) { resolve(true); } else { setTimeout(check, 500); } - ") . " - }; - check(); - })", - true - ); - - ScreenshotHelper::saveIfEnabled($page, "{$key}-chart"); - }); - } - - if ($config['hasFilterTahun']) { - it("displays filter tahun on {$key}", function () use ($path) { - $page = SessionState::loginAndNavigate($this->user, $path) - ->assertVisible('#filter-tahun'); - - ScreenshotHelper::saveIfEnabled($page, "{$key}-filter-tahun"); - }); - } - - if ($config['hasFilterStatus']) { - it("displays filter status on {$key}", function () use ($path) { - $page = SessionState::loginAndNavigate($this->user, $path) - ->assertVisible('#filter-status-kelengkapan'); - - ScreenshotHelper::saveIfEnabled($page, "{$key}-filter-status"); - }); - } - - if ($config['hasCetakButton']) { - $cetakTestId = $config['cetakTestId'] ?? null; - - it("displays cetak button on {$key}", function () use ($path, $cetakTestId) { - $page = SessionState::loginAndNavigate($this->user, $path); - - if ($cetakTestId) { - $page->assertVisible("[data-testid=\"{$cetakTestId}\"]"); - } else { - $page->assertScript( - "new Promise((resolve) => { - const check = () => { - const btn = document.querySelector('[data-testid=\"bt-cetak\"]') || document.querySelector('#cetak') || document.querySelector('button[data-print-url]'); - resolve(!!btn); - }; - check(); - })", - true - ); - } - - ScreenshotHelper::saveIfEnabled($page, "{$key}-cetak-button"); - }); - } - - if ($config['hasExcelButton']) { - $excelTestId = $config['excelTestId'] ?? null; - - it("displays excel button on {$key}", function () use ($path, $excelTestId) { - $page = SessionState::loginAndNavigate($this->user, $path); - - if ($excelTestId) { - $page->assertVisible("[data-testid=\"{$excelTestId}\"]"); - } else { - $page->assertScript( - "new Promise((resolve) => { - const check = () => { - const btn = document.querySelector('[data-testid=\"bt-excel\"]') || document.querySelector('#export-excel') || document.querySelector('#download-excel') || document.querySelector('button[data-download-url]'); - resolve(!!btn); - }; - check(); - })", - true - ); - } - - ScreenshotHelper::saveIfEnabled($page, "{$key}-excel-button"); - }); - } - - it("displays datatable on {$key}", function () use ($path, $tableSelector) { - $page = SessionState::loginAndNavigate($this->user, $path) - ->assertPathIs($path); - - $page->assertScript( - "new Promise((resolve) => { - const check = () => { - const table = document.querySelector('{$tableSelector}'); - if (table) { resolve(true); } else { setTimeout(check, 500); } - }; - check(); - })", - true - ); - - ScreenshotHelper::saveIfEnabled($page, "{$key}-datatable"); - }); - - it("has at least 1 data row on {$key}", function () use ($path, $tableSelector) { - $page = SessionState::loginAndNavigate($this->user, $path) - ->assertPathIs($path); - - $page->assertScript( - "new Promise((resolve) => { - const check = () => { - const table = document.querySelector('{$tableSelector}'); - if (table) { - const rows = table.querySelectorAll('tbody tr'); - if (rows.length > 0 && !rows[0].classList.contains('dataTables_empty')) { - resolve(true); - } else { - setTimeout(check, 500); - } - } else { - setTimeout(check, 500); - } - }; - check(); - })", - true - ); - - ScreenshotHelper::saveIfEnabled($page, "{$key}-data-rows"); - }); - - if ($config['hasDetailButton']) { - it("has detail button in data on {$key}", function () use ($path, $tableSelector) { - $page = SessionState::loginAndNavigate($this->user, $path) - ->assertPathIs($path); - - $page->assertScript( - "new Promise((resolve) => { - const check = () => { - const table = document.querySelector('{$tableSelector}'); - if (table) { - const rows = table.querySelectorAll('tbody tr'); - if (rows.length > 0 && !rows[0].classList.contains('dataTables_empty')) { - const detailBtn = rows[0].querySelector('[data-button=\"Detail\"]') || rows[0].querySelector('a[href*=\"detail\"]'); - resolve(!!detailBtn); - } else { - setTimeout(check, 500); - } - } else { - setTimeout(check, 500); - } - }; - check(); - })", - true - ); - - ScreenshotHelper::saveIfEnabled($page, "{$key}-detail-button"); - }); - } - - it("has no javascript errors on {$key}", function () use ($path) { - $page = SessionState::loginAndNavigate($this->user, $path) - ->assertPathIs($path) - ->assertNoJavaScriptErrors(); - - ScreenshotHelper::saveIfEnabled($page, "{$key}-no-errors"); - }); -} diff --git a/tests/Browser/SmokeInfrastrukturTest.php b/tests/Browser/SmokeInfrastrukturTest.php index 2d9e8bbc..8222ca89 100644 --- a/tests/Browser/SmokeInfrastrukturTest.php +++ b/tests/Browser/SmokeInfrastrukturTest.php @@ -39,8 +39,8 @@ $page->assertScript( 'new Promise((resolve) => { const check = () => { - const kondisi = document.querySelector(\'[data-testid="chart-transportasi"]\'); - const sanitasi = document.querySelector(\'[data-testid="chart-sanitasi"]\'); + const kondisi = document.querySelector(\'[data-testid=\"chart-transportasi\"]\'); + const sanitasi = document.querySelector(\'[data-testid=\"chart-sanitasi\"]\'); const kondisiReady = kondisi && kondisi.getContext && kondisi.width > 0; const sanitasiReady = sanitasi && sanitasi.getContext && sanitasi.width > 0; if (kondisiReady && sanitasiReady) { @@ -59,7 +59,7 @@ it('displays filter tahun', function () { $page = SessionState::loginAndNavigate($this->user, '/data-pokok/infrastruktur') - ->assertVisible('#filter-tahun'); + ->assertVisible('@filter-tahun'); ScreenshotHelper::saveIfEnabled($page, 'infrastruktur-filter-tahun'); }); @@ -69,7 +69,7 @@ ->assertScript( "new Promise((resolve) => { const check = () => { - const btn = document.querySelector('[data-testid=\"bt-cetak\"]') || document.querySelector('#cetak') || document.querySelector('button[data-print-url]'); + const btn = document.querySelector('[data-testid=\"btn-cetak\"]') || document.querySelector('button[data-print-url]'); resolve(!!btn); }; check(); @@ -85,7 +85,7 @@ ->assertScript( "new Promise((resolve) => { const check = () => { - const btn = document.querySelector('[data-testid=\"bt-excel\"]') || document.querySelector('#export-excel') || document.querySelector('#download-excel') || document.querySelector('button[data-download-url]'); + const btn = document.querySelector('[data-testid=\"btn-export-excel\"]') || document.querySelector('button[data-download-url]'); resolve(!!btn); }; check(); @@ -98,18 +98,8 @@ it('displays datatable', function () { $page = SessionState::loginAndNavigate($this->user, '/data-pokok/infrastruktur') - ->assertPathIs('/data-pokok/infrastruktur'); - - $page->assertScript( - 'new Promise((resolve) => { - const check = () => { - const table = document.querySelector(\'[data-testid="datatable-infrastruktur"]\'); - if (table) { resolve(true); } else { setTimeout(check, 500); } - }; - check(); - })', - true - ); + ->assertPathIs('/data-pokok/infrastruktur') + ->assertVisible('@datatable-infrastruktur'); ScreenshotHelper::saveIfEnabled($page, 'infrastruktur-datatable'); }); @@ -121,7 +111,7 @@ $page->assertScript( 'new Promise((resolve) => { const check = () => { - const table = document.querySelector(\'[data-testid="datatable-infrastruktur"]\'); + const table = document.querySelector(\'[data-testid=\"datatable-infrastruktur\"]\'); if (table) { const rows = table.querySelectorAll(\'tbody tr\'); if (rows.length > 0 && !rows[0].classList.contains(\'dataTables_empty\')) { diff --git a/tests/Browser/SmokeJaminanSosialTest.php b/tests/Browser/SmokeJaminanSosialTest.php index 3b80db77..38c216b4 100644 --- a/tests/Browser/SmokeJaminanSosialTest.php +++ b/tests/Browser/SmokeJaminanSosialTest.php @@ -46,9 +46,9 @@ $page->assertScript( 'new Promise((resolve) => { const check = () => { - const pie1 = document.querySelector(\'[data-testid="chart-pie-bantuan"]\'); - const pie2 = document.querySelector(\'[data-testid="chart-pie-mental"]\'); - const pie4 = document.querySelector(\'[data-testid="chart-pie-penanganan"]\'); + const pie1 = document.querySelector(\'[data-testid=\"chart-pie-bantuan\"]\'); + const pie2 = document.querySelector(\'[data-testid=\"chart-pie-mental\"]\'); + const pie4 = document.querySelector(\'[data-testid=\"chart-pie-penanganan\"]\'); const ready1 = pie1 && pie1.offsetWidth > 0; const ready2 = pie2 && pie2.offsetWidth > 0; const ready4 = pie4 && pie4.offsetWidth > 0; @@ -68,64 +68,36 @@ it('displays filter tahun', function () { $page = SessionState::loginAndNavigate($this->user, '/data-pokok/jaminan-sosial') - ->assertVisible('#filter-tahun'); + ->assertVisible('@filter-tahun'); ScreenshotHelper::saveIfEnabled($page, 'jaminan-sosial-filter-tahun'); }); it('displays filter status', function () { $page = SessionState::loginAndNavigate($this->user, '/data-pokok/jaminan-sosial') - ->assertVisible('#filter-status-kelengkapan'); + ->assertVisible('@filter-status-kelengkapan'); ScreenshotHelper::saveIfEnabled($page, 'jaminan-sosial-filter-status'); }); it('displays cetak button', function () { $page = SessionState::loginAndNavigate($this->user, '/data-pokok/jaminan-sosial') - ->assertScript( - "new Promise((resolve) => { - const check = () => { - const btn = document.querySelector('[data-testid=\"bt-cetak\"]') || document.querySelector('#cetak') || document.querySelector('button[data-print-url]'); - resolve(!!btn); - }; - check(); - })", - true - ); + ->assertVisible('@btn-cetak'); ScreenshotHelper::saveIfEnabled($page, 'jaminan-sosial-cetak-button'); }); it('displays excel button', function () { $page = SessionState::loginAndNavigate($this->user, '/data-pokok/jaminan-sosial') - ->assertScript( - "new Promise((resolve) => { - const check = () => { - const btn = document.querySelector('[data-testid=\"bt-excel\"]') || document.querySelector('#export-excel') || document.querySelector('#download-excel') || document.querySelector('button[data-download-url]'); - resolve(!!btn); - }; - check(); - })", - true - ); + ->assertVisible('@btn-export-excel'); ScreenshotHelper::saveIfEnabled($page, 'jaminan-sosial-excel-button'); }); it('displays datatable', function () { $page = SessionState::loginAndNavigate($this->user, '/data-pokok/jaminan-sosial') - ->assertPathIs('/data-pokok/jaminan-sosial'); - - $page->assertScript( - 'new Promise((resolve) => { - const check = () => { - const table = document.querySelector(\'[data-testid="datatable-jaminan-sosial"]\'); - if (table) { resolve(true); } else { setTimeout(check, 500); } - }; - check(); - })', - true - ); + ->assertPathIs('/data-pokok/jaminan-sosial') + ->assertVisible('@datatable-jaminan-sosial'); ScreenshotHelper::saveIfEnabled($page, 'jaminan-sosial-datatable'); }); @@ -137,7 +109,7 @@ $page->assertScript( 'new Promise((resolve) => { const check = () => { - const table = document.querySelector(\'[data-testid="datatable-jaminan-sosial"]\'); + const table = document.querySelector(\'[data-testid=\"datatable-jaminan-sosial\"]\'); if (table) { const rows = table.querySelectorAll(\'tbody tr\'); if (rows.length > 0 && !rows[0].classList.contains(\'dataTables_empty\')) { @@ -164,7 +136,7 @@ $page->assertScript( 'new Promise((resolve) => { const check = () => { - const table = document.querySelector(\'[data-testid="datatable-jaminan-sosial"]\'); + const table = document.querySelector(\'[data-testid=\"datatable-jaminan-sosial\"]\'); if (table) { const rows = table.querySelectorAll(\'tbody tr\'); if (rows.length > 0 && !rows[0].classList.contains(\'dataTables_empty\')) { diff --git a/tests/Browser/SmokeKelembagaanTest.php b/tests/Browser/SmokeKelembagaanTest.php index 914b09af..75afe0b7 100644 --- a/tests/Browser/SmokeKelembagaanTest.php +++ b/tests/Browser/SmokeKelembagaanTest.php @@ -21,21 +21,21 @@ it('displays filter button', function () { $page = SessionState::loginAndNavigate($this->user, '/lembaga') - ->assertVisible('[data-testid="bt-toggle-filter"]'); + ->assertVisible('@bt-toggle-filter'); ScreenshotHelper::saveIfEnabled($page, 'kelembagaan-filter-button'); }); it('displays cetak button', function () { $page = SessionState::loginAndNavigate($this->user, '/lembaga') - ->assertVisible('[data-testid="bt-cetak"]'); + ->assertVisible('@bt-cetak'); ScreenshotHelper::saveIfEnabled($page, 'kelembagaan-cetak-button'); }); it('displays excel button', function () { $page = SessionState::loginAndNavigate($this->user, '/lembaga') - ->assertVisible('[data-testid="bt-excel"]'); + ->assertVisible('@bt-excel'); ScreenshotHelper::saveIfEnabled($page, 'kelembagaan-excel-button'); }); @@ -43,7 +43,7 @@ it('displays datatable with data rows', function () { $page = SessionState::loginAndNavigate($this->user, '/lembaga') ->assertPathIs('/lembaga') - ->assertVisible('[data-testid="datatable-lembaga"]'); + ->assertVisible('@datatable-lembaga'); $page->assertScript( "new Promise((resolve) => { diff --git a/tests/Browser/SmokeKesehatanTest.php b/tests/Browser/SmokeKesehatanTest.php index de3ce04c..cbfd943f 100644 --- a/tests/Browser/SmokeKesehatanTest.php +++ b/tests/Browser/SmokeKesehatanTest.php @@ -21,7 +21,7 @@ it('displays statistik golongan darah', function () { $page = SessionState::loginAndNavigate($this->user, '/data-pokok/kesehatan') ->assertSee('Statistik Golongan Darah') - ->assertVisible('#donutChart'); + ->assertVisible('@chart-donut'); ScreenshotHelper::saveIfEnabled($page, 'kesehatan-donut-chart'); }); @@ -29,7 +29,7 @@ it('displays statistik status gizi balita', function () { $page = SessionState::loginAndNavigate($this->user, '/data-pokok/kesehatan') ->assertSee('Statistik Status Gizi Balita') - ->assertVisible('#barChart'); + ->assertVisible('@chart-bar'); ScreenshotHelper::saveIfEnabled($page, 'kesehatan-bar-chart'); }); @@ -41,8 +41,8 @@ $page->assertScript( "new Promise((resolve) => { const check = () => { - const donut = document.querySelector('#donutChart'); - const bar = document.querySelector('#barChart'); + const donut = document.querySelector('[data-testid=\"chart-donut\"]'); + const bar = document.querySelector('[data-testid=\"chart-bar\"]'); const donutReady = donut && donut.getContext && donut.width > 0; const barReady = bar && bar.getContext && bar.width > 0; if (donutReady && barReady) { @@ -61,14 +61,14 @@ it('displays cetak button', function () { $page = SessionState::loginAndNavigate($this->user, '/data-pokok/kesehatan') - ->assertVisible('[data-testid="bt-cetak"]'); + ->assertVisible('@btn-cetak'); ScreenshotHelper::saveIfEnabled($page, 'kesehatan-cetak-button'); }); it('displays excel button', function () { $page = SessionState::loginAndNavigate($this->user, '/data-pokok/kesehatan') - ->assertVisible('[data-testid="bt-excel"]'); + ->assertVisible('@btn-export-excel'); ScreenshotHelper::saveIfEnabled($page, 'kesehatan-excel-button'); }); @@ -76,12 +76,12 @@ it('displays datatable with data rows', function () { $page = SessionState::loginAndNavigate($this->user, '/data-pokok/kesehatan') ->assertPathIs('/data-pokok/kesehatan') - ->assertVisible('[data-testid="datatable-kesehatan"]'); + ->assertVisible('@datatable-kesehatan'); $page->assertScript( "new Promise((resolve) => { const check = () => { - const rows = document.querySelectorAll('#kesehatan tbody tr'); + const rows = document.querySelectorAll('[data-testid=\"datatable-kesehatan\"] tbody tr'); if (rows.length > 0 && !rows[0].classList.contains('dataTables_empty')) { resolve(true); } else { diff --git a/tests/Browser/SmokeKetenagakerjaanTest.php b/tests/Browser/SmokeKetenagakerjaanTest.php index de4fb3c3..40d5ab94 100644 --- a/tests/Browser/SmokeKetenagakerjaanTest.php +++ b/tests/Browser/SmokeKetenagakerjaanTest.php @@ -23,7 +23,7 @@ it('displays statistik jumlah penghasilan', function () { $page = SessionState::loginAndNavigate($this->user, '/data-pokok/ketenagakerjaan') ->assertSee('Statistik Jumlah Penghasilan') - ->assertVisible('#barChart'); + ->assertVisible('canvas'); ScreenshotHelper::saveIfEnabled($page, 'ketenagakerjaan-bar-chart'); }); @@ -31,7 +31,7 @@ it('displays statistik pelatihan', function () { $page = SessionState::loginAndNavigate($this->user, '/data-pokok/ketenagakerjaan') ->assertSee('Statistik Pelatihan') - ->assertVisible('#donutChart'); + ->assertVisible('canvas'); ScreenshotHelper::saveIfEnabled($page, 'ketenagakerjaan-donut-chart'); }); @@ -43,8 +43,8 @@ $page->assertScript( "new Promise((resolve) => { const check = () => { - const donut = document.querySelector('#donutChart'); - const bar = document.querySelector('#barChart'); + const donut = document.querySelector('canvas'); + const bar = document.querySelectorAll('canvas')[1]; const donutReady = donut && donut.getContext && donut.width > 0; const barReady = bar && bar.getContext && bar.width > 0; if (donutReady && barReady) { @@ -63,14 +63,14 @@ it('displays cetak button', function () { $page = SessionState::loginAndNavigate($this->user, '/data-pokok/ketenagakerjaan') - ->assertVisible('[data-testid="bt-cetak"]'); + ->assertVisible('@btn-cetak'); ScreenshotHelper::saveIfEnabled($page, 'ketenagakerjaan-cetak-button'); }); it('displays excel button', function () { $page = SessionState::loginAndNavigate($this->user, '/data-pokok/ketenagakerjaan') - ->assertVisible('[data-testid="bt-excel"]'); + ->assertVisible('@btn-export-excel'); ScreenshotHelper::saveIfEnabled($page, 'ketenagakerjaan-excel-button'); }); @@ -78,12 +78,12 @@ it('displays datatable with data rows', function () { $page = SessionState::loginAndNavigate($this->user, '/data-pokok/ketenagakerjaan') ->assertPathIs('/data-pokok/ketenagakerjaan') - ->assertVisible('[data-testid="datatable-ketenagakerjaan"]'); + ->assertVisible('@datatable-ketenagakerjaan'); $page->assertScript( "new Promise((resolve) => { const check = () => { - const rows = document.querySelectorAll('#ketenagakerjaan tbody tr'); + const rows = document.querySelectorAll('[data-testid=\"datatable-ketenagakerjaan\"] tbody tr'); if (rows.length > 0 && !rows[0].classList.contains('dataTables_empty')) { resolve(true); } else { diff --git a/tests/Browser/SmokeLaporanBulananTest.php b/tests/Browser/SmokeLaporanBulananTest.php index d3870964..f5061d5d 100644 --- a/tests/Browser/SmokeLaporanBulananTest.php +++ b/tests/Browser/SmokeLaporanBulananTest.php @@ -127,7 +127,7 @@ it('displays excel button', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/laporan-bulanan') - ->assertVisible('@bt-excel'); + ->assertVisible('@btn-export-excel'); ScreenshotHelper::saveIfEnabled($page, 'laporan-bulanan-excel-button'); }); @@ -168,18 +168,8 @@ }); it('displays datatable', function () { - $page = SessionState::loginAndNavigate($this->user, '/statistik/laporan-bulanan'); - - $page->assertScript( - "new Promise((resolve) => { - const check = () => { - const table = document.querySelector('[data-testid=\"datatable-laporan-bulanan\"]'); - if (table) { resolve(true); } else { setTimeout(check, 500); } - }; - check(); - })", - true - ); + $page = SessionState::loginAndNavigate($this->user, '/statistik/laporan-bulanan') + ->assertVisible('@datatable-laporan-bulanan'); ScreenshotHelper::saveIfEnabled($page, 'laporan-bulanan-datatable'); }); diff --git a/tests/Browser/SmokeLaporanPengisianPerDesaTest.php b/tests/Browser/SmokeLaporanPengisianPerDesaTest.php index ab05021e..bf9d2fab 100644 --- a/tests/Browser/SmokeLaporanPengisianPerDesaTest.php +++ b/tests/Browser/SmokeLaporanPengisianPerDesaTest.php @@ -20,32 +20,22 @@ it('displays cetak button', function () { $page = SessionState::loginAndNavigate($this->user, '/data-presisi/laporan/perdesa') - ->assertVisible('[data-testid="btn-cetak"]'); + ->assertVisible('@btn-cetak'); ScreenshotHelper::saveIfEnabled($page, 'laporan-pengisian-perdesa-cetak-button'); }); it('displays excel button', function () { $page = SessionState::loginAndNavigate($this->user, '/data-presisi/laporan/perdesa') - ->assertVisible('[data-testid="btn-export-excel"]'); + ->assertVisible('@btn-export-excel'); ScreenshotHelper::saveIfEnabled($page, 'laporan-pengisian-perdesa-excel-button'); }); it('displays datatable', function () { $page = SessionState::loginAndNavigate($this->user, '/data-presisi/laporan/perdesa') - ->assertPathIs('/data-presisi/laporan/perdesa'); - - $page->assertScript( - 'new Promise((resolve) => { - const check = () => { - const table = document.querySelector(\'[data-testid="datatable-laporan-perdesa"]\'); - if (table) { resolve(true); } else { setTimeout(check, 500); } - }; - check(); - })', - true - ); + ->assertPathIs('/data-presisi/laporan/perdesa') + ->assertVisible('@datatable-laporan-perdesa'); ScreenshotHelper::saveIfEnabled($page, 'laporan-pengisian-perdesa-datatable'); }); diff --git a/tests/Browser/SmokeLaporanPengisianTest.php b/tests/Browser/SmokeLaporanPengisianTest.php index 3db97b1d..07221e68 100644 --- a/tests/Browser/SmokeLaporanPengisianTest.php +++ b/tests/Browser/SmokeLaporanPengisianTest.php @@ -20,39 +20,29 @@ it('displays filter kategori', function () { $page = SessionState::loginAndNavigate($this->user, '/data-presisi/laporan') - ->assertVisible('[data-testid="filter-status"]'); + ->assertVisible('@filter-status'); ScreenshotHelper::saveIfEnabled($page, 'laporan-pengisian-filter-kategori'); }); it('displays cetak button', function () { $page = SessionState::loginAndNavigate($this->user, '/data-presisi/laporan') - ->assertVisible('[data-testid="btn-cetak"]'); + ->assertVisible('@btn-cetak'); ScreenshotHelper::saveIfEnabled($page, 'laporan-pengisian-cetak-button'); }); it('displays excel button', function () { $page = SessionState::loginAndNavigate($this->user, '/data-presisi/laporan') - ->assertVisible('[data-testid="btn-export-excel"]'); + ->assertVisible('@btn-export-excel'); ScreenshotHelper::saveIfEnabled($page, 'laporan-pengisian-excel-button'); }); it('displays datatable', function () { $page = SessionState::loginAndNavigate($this->user, '/data-presisi/laporan') - ->assertPathIs('/data-presisi/laporan'); - - $page->assertScript( - 'new Promise((resolve) => { - const check = () => { - const table = document.querySelector(\'[data-testid="datatable-laporan"]\'); - if (table) { resolve(true); } else { setTimeout(check, 500); } - }; - check(); - })', - true - ); + ->assertPathIs('/data-presisi/laporan') + ->assertVisible('@datatable-laporan'); ScreenshotHelper::saveIfEnabled($page, 'laporan-pengisian-datatable'); }); diff --git a/tests/Browser/SmokePariwisataTest.php b/tests/Browser/SmokePariwisataTest.php index b5d0a4f8..7c963128 100644 --- a/tests/Browser/SmokePariwisataTest.php +++ b/tests/Browser/SmokePariwisataTest.php @@ -39,8 +39,8 @@ $page->assertScript( 'new Promise((resolve) => { const check = () => { - const bar = document.querySelector(\'[data-testid="chart-bar-penginapan"]\'); - const donut = document.querySelector(\'[data-testid="chart-donut-pemanfaatan"]\'); + const bar = document.querySelector(\'[data-testid=\"chart-bar-penginapan\"]\'); + const donut = document.querySelector(\'[data-testid=\"chart-donut-pemanfaatan\"]\'); const barReady = bar && bar.getContext && bar.width > 0; const donutReady = donut && donut.getContext && donut.width > 0; if (barReady && donutReady) { @@ -59,64 +59,36 @@ it('displays filter tahun', function () { $page = SessionState::loginAndNavigate($this->user, '/data-pokok/pariwisata') - ->assertVisible('#filter-tahun'); + ->assertVisible('@filter-tahun'); ScreenshotHelper::saveIfEnabled($page, 'pariwisata-filter-tahun'); }); it('displays filter kategori', function () { $page = SessionState::loginAndNavigate($this->user, '/data-pokok/pariwisata') - ->assertVisible('#filter-kategori-wisata'); + ->assertVisible('@filter-kategori-wisata'); ScreenshotHelper::saveIfEnabled($page, 'pariwisata-filter-kategori'); }); it('displays cetak button', function () { $page = SessionState::loginAndNavigate($this->user, '/data-pokok/pariwisata') - ->assertScript( - "new Promise((resolve) => { - const check = () => { - const btn = document.querySelector('[data-testid=\"bt-cetak\"]') || document.querySelector('#cetak') || document.querySelector('button[data-print-url]'); - resolve(!!btn); - }; - check(); - })", - true - ); + ->assertVisible('@btn-cetak'); ScreenshotHelper::saveIfEnabled($page, 'pariwisata-cetak-button'); }); it('displays excel button', function () { $page = SessionState::loginAndNavigate($this->user, '/data-pokok/pariwisata') - ->assertScript( - "new Promise((resolve) => { - const check = () => { - const btn = document.querySelector('[data-testid=\"bt-excel\"]') || document.querySelector('#export-excel') || document.querySelector('#download-excel') || document.querySelector('button[data-download-url]'); - resolve(!!btn); - }; - check(); - })", - true - ); + ->assertVisible('@btn-export-excel'); ScreenshotHelper::saveIfEnabled($page, 'pariwisata-excel-button'); }); it('displays datatable', function () { $page = SessionState::loginAndNavigate($this->user, '/data-pokok/pariwisata') - ->assertPathIs('/data-pokok/pariwisata'); - - $page->assertScript( - 'new Promise((resolve) => { - const check = () => { - const table = document.querySelector(\'[data-testid="datatable-pariwisata"]\'); - if (table) { resolve(true); } else { setTimeout(check, 500); } - }; - check(); - })', - true - ); + ->assertPathIs('/data-pokok/pariwisata') + ->assertVisible('@datatable-pariwisata'); ScreenshotHelper::saveIfEnabled($page, 'pariwisata-datatable'); }); @@ -128,7 +100,7 @@ $page->assertScript( 'new Promise((resolve) => { const check = () => { - const table = document.querySelector(\'[data-testid="datatable-pariwisata"]\'); + const table = document.querySelector(\'[data-testid=\"datatable-pariwisata\"]\'); if (table) { const rows = table.querySelectorAll(\'tbody tr\'); if (rows.length > 0 && !rows[0].classList.contains(\'dataTables_empty\')) { diff --git a/tests/Browser/SmokePendidikanTest.php b/tests/Browser/SmokePendidikanTest.php index c1fe6175..20839dd3 100644 --- a/tests/Browser/SmokePendidikanTest.php +++ b/tests/Browser/SmokePendidikanTest.php @@ -21,7 +21,7 @@ it('displays statistik partisipasi sekolah', function () { $page = SessionState::loginAndNavigate($this->user, '/data-pokok/pendidikan') ->assertSee('Statistik Partisipasi Sekolah') - ->assertVisible('#donutChart'); + ->assertVisible('canvas'); ScreenshotHelper::saveIfEnabled($page, 'pendidikan-donut-chart'); }); @@ -29,7 +29,7 @@ it('displays statistik ijazah tertinggi', function () { $page = SessionState::loginAndNavigate($this->user, '/data-pokok/pendidikan') ->assertSee('Statistik Ijazah Tertinggi') - ->assertVisible('#barChart'); + ->assertVisible('canvas'); ScreenshotHelper::saveIfEnabled($page, 'pendidikan-bar-chart'); }); @@ -41,8 +41,8 @@ $page->assertScript( "new Promise((resolve) => { const check = () => { - const donut = document.querySelector('#donutChart'); - const bar = document.querySelector('#barChart'); + const donut = document.querySelector('canvas'); + const bar = document.querySelectorAll('canvas')[1]; const donutReady = donut && donut.getContext && donut.width > 0; const barReady = bar && bar.getContext && bar.width > 0; if (donutReady && barReady) { @@ -61,14 +61,14 @@ it('displays cetak button', function () { $page = SessionState::loginAndNavigate($this->user, '/data-pokok/pendidikan') - ->assertVisible('[data-testid="bt-cetak"]'); + ->assertVisible('@btn-cetak'); ScreenshotHelper::saveIfEnabled($page, 'pendidikan-cetak-button'); }); it('displays excel button', function () { $page = SessionState::loginAndNavigate($this->user, '/data-pokok/pendidikan') - ->assertVisible('[data-testid="bt-excel"]'); + ->assertVisible('@btn-export-excel'); ScreenshotHelper::saveIfEnabled($page, 'pendidikan-excel-button'); }); @@ -76,12 +76,12 @@ it('displays datatable with data rows', function () { $page = SessionState::loginAndNavigate($this->user, '/data-pokok/pendidikan') ->assertPathIs('/data-pokok/pendidikan') - ->assertVisible('[data-testid="datatable-pendidikan"]'); + ->assertVisible('@datatable-pendidikan'); $page->assertScript( "new Promise((resolve) => { const check = () => { - const rows = document.querySelectorAll('#pendidikan tbody tr'); + const rows = document.querySelectorAll('[data-testid=\"datatable-pendidikan\"] tbody tr'); if (rows.length > 0 && !rows[0].classList.contains('dataTables_empty')) { resolve(true); } else { diff --git a/tests/Browser/SmokePendudukTest.php b/tests/Browser/SmokePendudukTest.php index 93ec90ac..e6d1cbcd 100644 --- a/tests/Browser/SmokePendudukTest.php +++ b/tests/Browser/SmokePendudukTest.php @@ -21,21 +21,21 @@ it('displays filter button', function () { $page = SessionState::loginAndNavigate($this->user, '/penduduk') - ->assertVisible('[data-testid="bt-toggle-filter"]'); + ->assertVisible('@bt-toggle-filter'); ScreenshotHelper::saveIfEnabled($page, 'penduduk-filter-button'); }); it('displays cetak button', function () { $page = SessionState::loginAndNavigate($this->user, '/penduduk') - ->assertVisible('[data-testid="bt-cetak"]'); + ->assertVisible('@bt-cetak'); ScreenshotHelper::saveIfEnabled($page, 'penduduk-cetak-button'); }); it('displays excel button', function () { $page = SessionState::loginAndNavigate($this->user, '/penduduk') - ->assertVisible('[data-testid="bt-excel"]'); + ->assertVisible('@bt-excel'); ScreenshotHelper::saveIfEnabled($page, 'penduduk-excel-button'); }); @@ -43,7 +43,7 @@ it('displays datatable with data rows', function () { $page = SessionState::loginAndNavigate($this->user, '/penduduk') ->assertPathIs('/penduduk') - ->assertVisible('[data-testid="datatable-penduduk"]'); + ->assertVisible('@datatable-penduduk'); $page->assertScript( "new Promise((resolve) => { diff --git a/tests/Browser/SmokePenerimaBantuanTest.php b/tests/Browser/SmokePenerimaBantuanTest.php index 8c8cb633..a07a0e1b 100644 --- a/tests/Browser/SmokePenerimaBantuanTest.php +++ b/tests/Browser/SmokePenerimaBantuanTest.php @@ -21,21 +21,21 @@ it('displays filter button', function () { $page = SessionState::loginAndNavigate($this->user, '/bantuan') - ->assertVisible('[data-testid="bt-toggle-filter"]'); + ->assertVisible('@bt-toggle-filter'); ScreenshotHelper::saveIfEnabled($page, 'bantuan-filter-button'); }); it('displays cetak button', function () { $page = SessionState::loginAndNavigate($this->user, '/bantuan') - ->assertVisible('[data-testid="bt-cetak"]'); + ->assertVisible('@bt-cetak'); ScreenshotHelper::saveIfEnabled($page, 'bantuan-cetak-button'); }); it('displays excel button', function () { $page = SessionState::loginAndNavigate($this->user, '/bantuan') - ->assertVisible('[data-testid="bt-excel"]'); + ->assertVisible('@bt-excel'); ScreenshotHelper::saveIfEnabled($page, 'bantuan-excel-button'); }); @@ -43,7 +43,7 @@ it('displays datatable with data rows', function () { $page = SessionState::loginAndNavigate($this->user, '/bantuan') ->assertPathIs('/bantuan') - ->assertVisible('[data-testid="datatable-bantuan"]'); + ->assertVisible('@datatable-bantuan'); $page->assertScript( "new Promise((resolve) => { diff --git a/tests/Browser/SmokeStatistikAdatTest.php b/tests/Browser/SmokeStatistikAdatTest.php new file mode 100644 index 00000000..5abd4d6e --- /dev/null +++ b/tests/Browser/SmokeStatistikAdatTest.php @@ -0,0 +1,185 @@ +user = SessionState::loginAdminUser(); +}); + +afterEach(function () { + SessionState::clear(); +}); + +$path = '/data-presisi/statistik/adat'; + +it('opens the statistik adat page', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path) + ->assertSee('Adat'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-adat-page'); +}); + +it('displays kategori statistik list', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@daftar-statistik'); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const items = document.querySelectorAll(\'[data-testid="daftar-statistik"] a\'); + if (items.length > 0) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-adat-kategori-list'); +}); + +it('displays filter tahun', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@filter-tahun'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-adat-filter-tahun'); +}); + +it('displays excel button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-export-excel'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-adat-excel-button'); +}); + +it('displays grafik button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-toggle-grafik'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-adat-grafik-button'); +}); + +it('displays chart button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-toggle-pie'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-adat-chart-button'); +}); + +it('displays datatable', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@datatable-statistik'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-adat-datatable'); +}); + +it('datatable displays data', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const table = document.querySelector(\'[data-testid="datatable-statistik"]\'); + if (table) { + const rows = table.querySelectorAll(\'tbody tr\'); + if (rows.length > 0 && !rows[0].classList.contains(\'dataTables_empty\')) { + resolve(true); + } else { + setTimeout(check, 500); + } + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-adat-datatable-data'); +}); + +it('clicks a kategori statistik', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@daftar-statistik'); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const items = document.querySelectorAll(\'[data-testid="daftar-statistik"] a\'); + if (items.length > 0) { + items[0].click(); + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + $page->wait(2000); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-adat-kategori-clicked'); +}); + +it('renders bar chart', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const bar = document.querySelector(\'[data-testid="chart-bar"]\'); + const ready = bar && bar.getContext && bar.width > 0; + if (ready) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-adat-bar-chart'); +}); + +it('renders pie chart', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const donut = document.querySelector(\'[data-testid="chart-donut"]\'); + const ready = donut && donut.getContext && donut.width > 0; + if (ready) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-adat-pie-chart'); +}); + +it('has no javascript errors', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path) + ->assertNoJavaScriptErrors(); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-adat-no-errors'); +}); diff --git a/tests/Browser/SmokeStatistikAktivitasKeagamaanTest.php b/tests/Browser/SmokeStatistikAktivitasKeagamaanTest.php new file mode 100644 index 00000000..1a2c8ed3 --- /dev/null +++ b/tests/Browser/SmokeStatistikAktivitasKeagamaanTest.php @@ -0,0 +1,185 @@ +user = SessionState::loginAdminUser(); +}); + +afterEach(function () { + SessionState::clear(); +}); + +$path = '/data-presisi/statistik/aktivitas-keagamaan'; + +it('opens the statistik aktivitas keagamaan page', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path) + ->assertSee('Aktivitas Keagamaan'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-aktivitas-keagamaan-page'); +}); + +it('displays kategori statistik list', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@daftar-statistik'); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const items = document.querySelectorAll(\'[data-testid="daftar-statistik"] a\'); + if (items.length > 0) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-aktivitas-keagamaan-kategori-list'); +}); + +it('displays filter tahun', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@filter-tahun'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-aktivitas-keagamaan-filter-tahun'); +}); + +it('displays excel button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-export-excel'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-aktivitas-keagamaan-excel-button'); +}); + +it('displays grafik button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-toggle-grafik'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-aktivitas-keagamaan-grafik-button'); +}); + +it('displays chart button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-toggle-pie'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-aktivitas-keagamaan-chart-button'); +}); + +it('displays datatable', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@datatable-statistik'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-aktivitas-keagamaan-datatable'); +}); + +it('datatable displays data', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const table = document.querySelector(\'[data-testid="datatable-statistik"]\'); + if (table) { + const rows = table.querySelectorAll(\'tbody tr\'); + if (rows.length > 0 && !rows[0].classList.contains(\'dataTables_empty\')) { + resolve(true); + } else { + setTimeout(check, 500); + } + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-aktivitas-keagamaan-datatable-data'); +}); + +it('clicks a kategori statistik', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@daftar-statistik'); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const items = document.querySelectorAll(\'[data-testid="daftar-statistik"] a\'); + if (items.length > 0) { + items[0].click(); + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + $page->wait(2000); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-aktivitas-keagamaan-kategori-clicked'); +}); + +it('renders bar chart', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const bar = document.querySelector(\'[data-testid="chart-bar"]\'); + const ready = bar && bar.getContext && bar.width > 0; + if (ready) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-aktivitas-keagamaan-bar-chart'); +}); + +it('renders pie chart', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const donut = document.querySelector(\'[data-testid="chart-donut"]\'); + const ready = donut && donut.getContext && donut.width > 0; + if (ready) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-aktivitas-keagamaan-pie-chart'); +}); + +it('has no javascript errors', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path) + ->assertNoJavaScriptErrors(); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-aktivitas-keagamaan-no-errors'); +}); diff --git a/tests/Browser/SmokeStatistikBantuanTest.php b/tests/Browser/SmokeStatistikBantuanTest.php index 2f13c2e1..e0fdcf39 100644 --- a/tests/Browser/SmokeStatistikBantuanTest.php +++ b/tests/Browser/SmokeStatistikBantuanTest.php @@ -30,7 +30,7 @@ $page->assertScript( "new Promise((resolve) => { const check = () => { - const items = document.querySelectorAll('#daftar-statistik .pilih-kategori'); + const items = document.querySelectorAll('[data-testid=\"daftar-statistik\"] .pilih-kategori'); if (items.length > 0) { resolve(true); } else { setTimeout(check, 500); } }; check(); @@ -41,7 +41,7 @@ foreach ($kategoriBantuan as $nama) { $escaped = addslashes($nama); $page->assertScript( - "Array.from(document.querySelectorAll('#daftar-statistik .pilih-kategori a')).some(a => a.textContent.trim().includes('{$escaped}'))", + "Array.from(document.querySelectorAll('[data-testid=\"daftar-statistik\"] .pilih-kategori a')).some(a => a.textContent.trim().includes('{$escaped}'))", true ); } @@ -51,28 +51,28 @@ it('displays cetak button', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/bantuan') - ->assertVisible('@bt-cetak'); + ->assertVisible('@btn-cetak'); ScreenshotHelper::saveIfEnabled($page, 'statistik-bantuan-cetak-button'); }); it('displays excel button', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/bantuan') - ->assertVisible('@bt-excel'); + ->assertVisible('@btn-export-excel'); ScreenshotHelper::saveIfEnabled($page, 'statistik-bantuan-excel-button'); }); it('displays grafik button', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/bantuan') - ->assertVisible('@bt-grafik'); + ->assertVisible('@btn-toggle-grafik'); ScreenshotHelper::saveIfEnabled($page, 'statistik-bantuan-grafik-button'); }); it('displays chart button', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/bantuan') - ->assertVisible('@bt-chart'); + ->assertVisible('@btn-toggle-pie'); ScreenshotHelper::saveIfEnabled($page, 'statistik-bantuan-chart-button'); }); @@ -84,7 +84,7 @@ $page->assertScript( "new Promise((resolve) => { const check = () => { - const rows = document.querySelectorAll('#tabel-data tbody tr'); + const rows = document.querySelectorAll('[data-testid=\"datatable-statistik\"] tbody tr'); if (rows.length > 0 && !rows[0].classList.contains('dataTables_empty')) { resolve(true); } else { @@ -106,7 +106,7 @@ $page->assertScript( "new Promise((resolve) => { const check = () => { - const items = document.querySelectorAll('#daftar-statistik .pilih-kategori'); + const items = document.querySelectorAll('[data-testid=\"daftar-statistik\"] .pilih-kategori'); if (items.length > 0) { resolve(true); } else { setTimeout(check, 500); } }; check(); @@ -117,7 +117,7 @@ $page->assertScript( "new Promise((resolve) => { const check = () => { - const rows = document.querySelectorAll('#tabel-data tbody tr'); + const rows = document.querySelectorAll('[data-testid=\"datatable-statistik\"] tbody tr'); if (rows.length > 0 && !rows[0].classList.contains('dataTables_empty')) { resolve(true); } else { @@ -134,15 +134,15 @@ it('accesses grafik and renders successfully', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/bantuan') - ->assertVisible('@bt-grafik'); + ->assertVisible('@btn-toggle-grafik'); - $page->click('@bt-grafik'); + $page->click('@btn-toggle-grafik'); $page->assertScript( "new Promise((resolve) => { const check = () => { const container = document.querySelector('#grafik-statistik'); - const canvas = document.querySelector('#barChart'); + const canvas = document.querySelector('[data-testid=\"chart-bar\"]'); if (container && canvas && canvas.getContext && canvas.width > 0) { resolve(true); } else { @@ -159,15 +159,15 @@ it('accesses chart and renders successfully', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/bantuan') - ->assertVisible('@bt-chart'); + ->assertVisible('@btn-toggle-pie'); - $page->click('@bt-chart'); + $page->click('@btn-toggle-pie'); $page->assertScript( "new Promise((resolve) => { const check = () => { const container = document.querySelector('#pie-statistik'); - const canvas = document.querySelector('#donutChart'); + const canvas = document.querySelector('[data-testid=\"chart-donut\"]'); if (container && canvas && canvas.getContext && canvas.width > 0) { resolve(true); } else { diff --git a/tests/Browser/SmokeStatistikJaminanSosialTest.php b/tests/Browser/SmokeStatistikJaminanSosialTest.php new file mode 100644 index 00000000..6199f5f2 --- /dev/null +++ b/tests/Browser/SmokeStatistikJaminanSosialTest.php @@ -0,0 +1,185 @@ +user = SessionState::loginAdminUser(); +}); + +afterEach(function () { + SessionState::clear(); +}); + +$path = '/data-presisi/statistik/jaminan-sosial'; + +it('opens the statistik jaminan sosial page', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path) + ->assertSee('Jaminan Sosial'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-jaminan-sosial-page'); +}); + +it('displays kategori statistik list', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@daftar-statistik'); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const items = document.querySelectorAll(\'[data-testid="daftar-statistik"] a\'); + if (items.length > 0) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-jaminan-sosial-kategori-list'); +}); + +it('displays filter tahun', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@filter-tahun'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-jaminan-sosial-filter-tahun'); +}); + +it('displays excel button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-export-excel'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-jaminan-sosial-excel-button'); +}); + +it('displays grafik button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-toggle-grafik'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-jaminan-sosial-grafik-button'); +}); + +it('displays chart button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-toggle-pie'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-jaminan-sosial-chart-button'); +}); + +it('displays datatable', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@datatable-statistik'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-jaminan-sosial-datatable'); +}); + +it('datatable displays data', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const table = document.querySelector(\'[data-testid="datatable-statistik"]\'); + if (table) { + const rows = table.querySelectorAll(\'tbody tr\'); + if (rows.length > 0 && !rows[0].classList.contains(\'dataTables_empty\')) { + resolve(true); + } else { + setTimeout(check, 500); + } + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-jaminan-sosial-datatable-data'); +}); + +it('clicks a kategori statistik', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@daftar-statistik'); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const items = document.querySelectorAll(\'[data-testid="daftar-statistik"] a\'); + if (items.length > 0) { + items[0].click(); + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + $page->wait(2000); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-jaminan-sosial-kategori-clicked'); +}); + +it('renders bar chart', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const bar = document.querySelector(\'[data-testid="chart-bar"]\'); + const ready = bar && bar.getContext && bar.width > 0; + if (ready) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-jaminan-sosial-bar-chart'); +}); + +it('renders pie chart', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const donut = document.querySelector(\'[data-testid="chart-donut"]\'); + const ready = donut && donut.getContext && donut.width > 0; + if (ready) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-jaminan-sosial-pie-chart'); +}); + +it('has no javascript errors', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path) + ->assertNoJavaScriptErrors(); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-jaminan-sosial-no-errors'); +}); diff --git a/tests/Browser/SmokeStatistikKeluargaTest.php b/tests/Browser/SmokeStatistikKeluargaTest.php index f98c49f6..b1c0055f 100644 --- a/tests/Browser/SmokeStatistikKeluargaTest.php +++ b/tests/Browser/SmokeStatistikKeluargaTest.php @@ -30,7 +30,7 @@ $page->assertScript( "new Promise((resolve) => { const check = () => { - const items = document.querySelectorAll('#daftar-statistik .pilih-kategori'); + const items = document.querySelectorAll('[data-testid=\"daftar-statistik\"] .pilih-kategori'); if (items.length > 0) { resolve(true); } else { setTimeout(check, 500); } }; check(); @@ -41,7 +41,7 @@ foreach ($kategoriKeluarga as $nama) { $escaped = addslashes($nama); $page->assertScript( - "Array.from(document.querySelectorAll('#daftar-statistik .pilih-kategori a')).some(a => a.textContent.trim().includes('{$escaped}'))", + "Array.from(document.querySelectorAll('[data-testid=\"daftar-statistik\"] .pilih-kategori a')).some(a => a.textContent.trim().includes('{$escaped}'))", true ); } @@ -51,28 +51,28 @@ it('displays cetak button', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/keluarga') - ->assertVisible('@bt-cetak'); + ->assertVisible('@btn-cetak'); ScreenshotHelper::saveIfEnabled($page, 'statistik-keluarga-cetak-button'); }); it('displays excel button', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/keluarga') - ->assertVisible('@bt-excel'); + ->assertVisible('@btn-export-excel'); ScreenshotHelper::saveIfEnabled($page, 'statistik-keluarga-excel-button'); }); it('displays grafik button', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/keluarga') - ->assertVisible('@bt-grafik'); + ->assertVisible('@btn-toggle-grafik'); ScreenshotHelper::saveIfEnabled($page, 'statistik-keluarga-grafik-button'); }); it('displays chart button', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/keluarga') - ->assertVisible('@bt-chart'); + ->assertVisible('@btn-toggle-pie'); ScreenshotHelper::saveIfEnabled($page, 'statistik-keluarga-chart-button'); }); @@ -84,7 +84,7 @@ $page->assertScript( "new Promise((resolve) => { const check = () => { - const rows = document.querySelectorAll('#tabel-data tbody tr'); + const rows = document.querySelectorAll('[data-testid=\"datatable-statistik\"] tbody tr'); if (rows.length > 0 && !rows[0].classList.contains('dataTables_empty')) { resolve(true); } else { @@ -106,7 +106,7 @@ $page->assertScript( "new Promise((resolve) => { const check = () => { - const items = document.querySelectorAll('#daftar-statistik .pilih-kategori'); + const items = document.querySelectorAll('[data-testid=\"daftar-statistik\"] .pilih-kategori'); if (items.length > 0) { resolve(true); } else { setTimeout(check, 500); } }; check(); @@ -117,7 +117,7 @@ $page->assertScript( "new Promise((resolve) => { const check = () => { - const rows = document.querySelectorAll('#tabel-data tbody tr'); + const rows = document.querySelectorAll('[data-testid=\"datatable-statistik\"] tbody tr'); if (rows.length > 0 && !rows[0].classList.contains('dataTables_empty')) { resolve(true); } else { @@ -134,15 +134,15 @@ it('accesses grafik and renders successfully', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/keluarga') - ->assertVisible('@bt-grafik'); + ->assertVisible('@btn-toggle-grafik'); - $page->click('@bt-grafik'); + $page->click('@btn-toggle-grafik'); $page->assertScript( "new Promise((resolve) => { const check = () => { const container = document.querySelector('#grafik-statistik'); - const canvas = document.querySelector('#barChart'); + const canvas = document.querySelector('[data-testid=\"chart-bar\"]'); if (container && canvas && canvas.getContext && canvas.width > 0) { resolve(true); } else { @@ -159,15 +159,15 @@ it('accesses chart and renders successfully', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/keluarga') - ->assertVisible('@bt-chart'); + ->assertVisible('@btn-toggle-pie'); - $page->click('@bt-chart'); + $page->click('@btn-toggle-pie'); $page->assertScript( "new Promise((resolve) => { const check = () => { const container = document.querySelector('#pie-statistik'); - const canvas = document.querySelector('#donutChart'); + const canvas = document.querySelector('[data-testid=\"chart-donut\"]'); if (container && canvas && canvas.getContext && canvas.width > 0) { resolve(true); } else { diff --git a/tests/Browser/SmokeStatistikKesehatanTest.php b/tests/Browser/SmokeStatistikKesehatanTest.php new file mode 100644 index 00000000..97222dcb --- /dev/null +++ b/tests/Browser/SmokeStatistikKesehatanTest.php @@ -0,0 +1,185 @@ +user = SessionState::loginAdminUser(); +}); + +afterEach(function () { + SessionState::clear(); +}); + +$path = '/data-presisi/statistik/kesehatan'; + +it('opens the statistik kesehatan page', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path) + ->assertSee('Kesehatan'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-kesehatan-page'); +}); + +it('displays kategori statistik list', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@daftar-statistik'); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const items = document.querySelectorAll(\'[data-testid="daftar-statistik"] a\'); + if (items.length > 0) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-kesehatan-kategori-list'); +}); + +it('displays filter tahun', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@filter-tahun'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-kesehatan-filter-tahun'); +}); + +it('displays excel button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-export-excel'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-kesehatan-excel-button'); +}); + +it('displays grafik button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-toggle-grafik'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-kesehatan-grafik-button'); +}); + +it('displays chart button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-toggle-pie'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-kesehatan-chart-button'); +}); + +it('displays datatable', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@datatable-statistik'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-kesehatan-datatable'); +}); + +it('datatable displays data', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const table = document.querySelector(\'[data-testid="datatable-statistik"]\'); + if (table) { + const rows = table.querySelectorAll(\'tbody tr\'); + if (rows.length > 0 && !rows[0].classList.contains(\'dataTables_empty\')) { + resolve(true); + } else { + setTimeout(check, 500); + } + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-kesehatan-datatable-data'); +}); + +it('clicks a kategori statistik', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@daftar-statistik'); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const items = document.querySelectorAll(\'[data-testid="daftar-statistik"] a\'); + if (items.length > 0) { + items[0].click(); + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + $page->wait(2000); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-kesehatan-kategori-clicked'); +}); + +it('renders bar chart', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const bar = document.querySelector(\'[data-testid="chart-bar"]\'); + const ready = bar && bar.getContext && bar.width > 0; + if (ready) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-kesehatan-bar-chart'); +}); + +it('renders pie chart', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const donut = document.querySelector(\'[data-testid="chart-donut"]\'); + const ready = donut && donut.getContext && donut.width > 0; + if (ready) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-kesehatan-pie-chart'); +}); + +it('has no javascript errors', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path) + ->assertNoJavaScriptErrors(); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-kesehatan-no-errors'); +}); diff --git a/tests/Browser/SmokeStatistikKetenagakerjaanTest.php b/tests/Browser/SmokeStatistikKetenagakerjaanTest.php new file mode 100644 index 00000000..6c6f8269 --- /dev/null +++ b/tests/Browser/SmokeStatistikKetenagakerjaanTest.php @@ -0,0 +1,185 @@ +user = SessionState::loginAdminUser(); +}); + +afterEach(function () { + SessionState::clear(); +}); + +$path = '/data-presisi/statistik/ketenagakerjaan'; + +it('opens the statistik ketenagakerjaan page', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path) + ->assertSee('Ketenagakerjaan'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-ketenagakerjaan-page'); +}); + +it('displays kategori statistik list', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@daftar-statistik'); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const items = document.querySelectorAll(\'[data-testid="daftar-statistik"] a\'); + if (items.length > 0) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-ketenagakerjaan-kategori-list'); +}); + +it('displays filter tahun', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@filter-tahun'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-ketenagakerjaan-filter-tahun'); +}); + +it('displays excel button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-export-excel'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-ketenagakerjaan-excel-button'); +}); + +it('displays grafik button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-toggle-grafik'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-ketenagakerjaan-grafik-button'); +}); + +it('displays chart button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-toggle-pie'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-ketenagakerjaan-chart-button'); +}); + +it('displays datatable', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@datatable-statistik'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-ketenagakerjaan-datatable'); +}); + +it('datatable displays data', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const table = document.querySelector(\'[data-testid="datatable-statistik"]\'); + if (table) { + const rows = table.querySelectorAll(\'tbody tr\'); + if (rows.length > 0 && !rows[0].classList.contains(\'dataTables_empty\')) { + resolve(true); + } else { + setTimeout(check, 500); + } + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-ketenagakerjaan-datatable-data'); +}); + +it('clicks a kategori statistik', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@daftar-statistik'); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const items = document.querySelectorAll(\'[data-testid="daftar-statistik"] a\'); + if (items.length > 0) { + items[0].click(); + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + $page->wait(2000); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-ketenagakerjaan-kategori-clicked'); +}); + +it('renders bar chart', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const bar = document.querySelector(\'[data-testid="chart-bar"]\'); + const ready = bar && bar.getContext && bar.width > 0; + if (ready) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-ketenagakerjaan-bar-chart'); +}); + +it('renders pie chart', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const donut = document.querySelector(\'[data-testid="chart-donut"]\'); + const ready = donut && donut.getContext && donut.width > 0; + if (ready) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-ketenagakerjaan-pie-chart'); +}); + +it('has no javascript errors', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path) + ->assertNoJavaScriptErrors(); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-ketenagakerjaan-no-errors'); +}); diff --git a/tests/Browser/SmokeStatistikPanganTest.php b/tests/Browser/SmokeStatistikPanganTest.php new file mode 100644 index 00000000..d9965551 --- /dev/null +++ b/tests/Browser/SmokeStatistikPanganTest.php @@ -0,0 +1,185 @@ +user = SessionState::loginAdminUser(); +}); + +afterEach(function () { + SessionState::clear(); +}); + +$path = '/data-presisi/statistik/pangan'; + +it('opens the statistik pangan page', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path) + ->assertSee('Pangan'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-pangan-page'); +}); + +it('displays kategori statistik list', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@daftar-statistik'); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const items = document.querySelectorAll(\'[data-testid="daftar-statistik"] a\'); + if (items.length > 0) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-pangan-kategori-list'); +}); + +it('displays filter tahun', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@filter-tahun'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-pangan-filter-tahun'); +}); + +it('displays excel button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-export-excel'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-pangan-excel-button'); +}); + +it('displays grafik button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-toggle-grafik'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-pangan-grafik-button'); +}); + +it('displays chart button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-toggle-pie'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-pangan-chart-button'); +}); + +it('displays datatable', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@datatable-statistik'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-pangan-datatable'); +}); + +it('datatable displays data', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const table = document.querySelector(\'[data-testid="datatable-statistik"]\'); + if (table) { + const rows = table.querySelectorAll(\'tbody tr\'); + if (rows.length > 0 && !rows[0].classList.contains(\'dataTables_empty\')) { + resolve(true); + } else { + setTimeout(check, 500); + } + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-pangan-datatable-data'); +}); + +it('clicks a kategori statistik', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@daftar-statistik'); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const items = document.querySelectorAll(\'[data-testid="daftar-statistik"] a\'); + if (items.length > 0) { + items[0].click(); + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + $page->wait(2000); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-pangan-kategori-clicked'); +}); + +it('renders bar chart', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const bar = document.querySelector(\'[data-testid="chart-bar"]\'); + const ready = bar && bar.getContext && bar.width > 0; + if (ready) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-pangan-bar-chart'); +}); + +it('renders pie chart', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const donut = document.querySelector(\'[data-testid="chart-donut"]\'); + const ready = donut && donut.getContext && donut.width > 0; + if (ready) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-pangan-pie-chart'); +}); + +it('has no javascript errors', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path) + ->assertNoJavaScriptErrors(); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-pangan-no-errors'); +}); diff --git a/tests/Browser/SmokeStatistikPapanTest.php b/tests/Browser/SmokeStatistikPapanTest.php new file mode 100644 index 00000000..527490d4 --- /dev/null +++ b/tests/Browser/SmokeStatistikPapanTest.php @@ -0,0 +1,185 @@ +user = SessionState::loginAdminUser(); +}); + +afterEach(function () { + SessionState::clear(); +}); + +$path = '/data-presisi/statistik/papan'; + +it('opens the statistik papan page', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path) + ->assertSee('Papan'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-papan-page'); +}); + +it('displays kategori statistik list', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@daftar-statistik'); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const items = document.querySelectorAll(\'[data-testid="daftar-statistik"] a\'); + if (items.length > 0) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-papan-kategori-list'); +}); + +it('displays filter tahun', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@filter-tahun'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-papan-filter-tahun'); +}); + +it('displays excel button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-export-excel'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-papan-excel-button'); +}); + +it('displays grafik button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-toggle-grafik'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-papan-grafik-button'); +}); + +it('displays chart button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-toggle-pie'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-papan-chart-button'); +}); + +it('displays datatable', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@datatable-statistik'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-papan-datatable'); +}); + +it('datatable displays data', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const table = document.querySelector(\'[data-testid="datatable-statistik"]\'); + if (table) { + const rows = table.querySelectorAll(\'tbody tr\'); + if (rows.length > 0 && !rows[0].classList.contains(\'dataTables_empty\')) { + resolve(true); + } else { + setTimeout(check, 500); + } + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-papan-datatable-data'); +}); + +it('clicks a kategori statistik', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@daftar-statistik'); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const items = document.querySelectorAll(\'[data-testid="daftar-statistik"] a\'); + if (items.length > 0) { + items[0].click(); + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + $page->wait(2000); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-papan-kategori-clicked'); +}); + +it('renders bar chart', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const bar = document.querySelector(\'[data-testid="chart-bar"]\'); + const ready = bar && bar.getContext && bar.width > 0; + if (ready) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-papan-bar-chart'); +}); + +it('renders pie chart', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const donut = document.querySelector(\'[data-testid="chart-donut"]\'); + const ready = donut && donut.getContext && donut.width > 0; + if (ready) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-papan-pie-chart'); +}); + +it('has no javascript errors', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path) + ->assertNoJavaScriptErrors(); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-papan-no-errors'); +}); diff --git a/tests/Browser/SmokeStatistikPendidikanTest.php b/tests/Browser/SmokeStatistikPendidikanTest.php new file mode 100644 index 00000000..daf89655 --- /dev/null +++ b/tests/Browser/SmokeStatistikPendidikanTest.php @@ -0,0 +1,185 @@ +user = SessionState::loginAdminUser(); +}); + +afterEach(function () { + SessionState::clear(); +}); + +$path = '/data-presisi/statistik/pendidikan'; + +it('opens the statistik pendidikan page', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path) + ->assertSee('Pendidikan'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-pendidikan-page'); +}); + +it('displays kategori statistik list', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@daftar-statistik'); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const items = document.querySelectorAll(\'[data-testid="daftar-statistik"] a\'); + if (items.length > 0) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-pendidikan-kategori-list'); +}); + +it('displays filter tahun', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@filter-tahun'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-pendidikan-filter-tahun'); +}); + +it('displays excel button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-export-excel'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-pendidikan-excel-button'); +}); + +it('displays grafik button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-toggle-grafik'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-pendidikan-grafik-button'); +}); + +it('displays chart button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-toggle-pie'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-pendidikan-chart-button'); +}); + +it('displays datatable', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@datatable-statistik'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-pendidikan-datatable'); +}); + +it('datatable displays data', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const table = document.querySelector(\'[data-testid="datatable-statistik"]\'); + if (table) { + const rows = table.querySelectorAll(\'tbody tr\'); + if (rows.length > 0 && !rows[0].classList.contains(\'dataTables_empty\')) { + resolve(true); + } else { + setTimeout(check, 500); + } + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-pendidikan-datatable-data'); +}); + +it('clicks a kategori statistik', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@daftar-statistik'); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const items = document.querySelectorAll(\'[data-testid="daftar-statistik"] a\'); + if (items.length > 0) { + items[0].click(); + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + $page->wait(2000); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-pendidikan-kategori-clicked'); +}); + +it('renders bar chart', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const bar = document.querySelector(\'[data-testid="chart-bar"]\'); + const ready = bar && bar.getContext && bar.width > 0; + if (ready) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-pendidikan-bar-chart'); +}); + +it('renders pie chart', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const donut = document.querySelector(\'[data-testid="chart-donut"]\'); + const ready = donut && donut.getContext && donut.width > 0; + if (ready) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-pendidikan-pie-chart'); +}); + +it('has no javascript errors', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path) + ->assertNoJavaScriptErrors(); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-pendidikan-no-errors'); +}); diff --git a/tests/Browser/SmokeStatistikPendudukTest.php b/tests/Browser/SmokeStatistikPendudukTest.php index c646a67b..eecf3368 100644 --- a/tests/Browser/SmokeStatistikPendudukTest.php +++ b/tests/Browser/SmokeStatistikPendudukTest.php @@ -30,7 +30,7 @@ $page->assertScript( "new Promise((resolve) => { const check = () => { - const items = document.querySelectorAll('#daftar-statistik .pilih-kategori'); + const items = document.querySelectorAll('[data-testid=\"daftar-statistik\"] .pilih-kategori'); if (items.length > 0) { resolve(true); } else { setTimeout(check, 500); } }; check(); @@ -41,7 +41,7 @@ foreach ($kategoriPenduduk as $nama) { $escaped = addslashes($nama); $page->assertScript( - "Array.from(document.querySelectorAll('#daftar-statistik .pilih-kategori a')).some(a => a.textContent.trim().includes('{$escaped}'))", + "Array.from(document.querySelectorAll('[data-testid=\"daftar-statistik\"] .pilih-kategori a')).some(a => a.textContent.trim().includes('{$escaped}'))", true ); } @@ -51,28 +51,28 @@ it('displays cetak button', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/penduduk') - ->assertVisible('@bt-cetak'); + ->assertVisible('@btn-cetak'); ScreenshotHelper::saveIfEnabled($page, 'statistik-penduduk-cetak-button'); }); it('displays excel button', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/penduduk') - ->assertVisible('@bt-excel'); + ->assertVisible('@btn-export-excel'); ScreenshotHelper::saveIfEnabled($page, 'statistik-penduduk-excel-button'); }); it('displays grafik button', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/penduduk') - ->assertVisible('@bt-grafik'); + ->assertVisible('@btn-toggle-grafik'); ScreenshotHelper::saveIfEnabled($page, 'statistik-penduduk-grafik-button'); }); it('displays chart button', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/penduduk') - ->assertVisible('@bt-chart'); + ->assertVisible('@btn-toggle-pie'); ScreenshotHelper::saveIfEnabled($page, 'statistik-penduduk-chart-button'); }); @@ -84,7 +84,7 @@ $page->assertScript( "new Promise((resolve) => { const check = () => { - const rows = document.querySelectorAll('#tabel-data tbody tr'); + const rows = document.querySelectorAll('[data-testid=\"datatable-statistik\"] tbody tr'); if (rows.length > 0 && !rows[0].classList.contains('dataTables_empty')) { resolve(true); } else { @@ -106,7 +106,7 @@ $page->assertScript( "new Promise((resolve) => { const check = () => { - const items = document.querySelectorAll('#daftar-statistik .pilih-kategori'); + const items = document.querySelectorAll('[data-testid=\"daftar-statistik\"] .pilih-kategori'); if (items.length > 0) { resolve(true); } else { setTimeout(check, 500); } }; check(); @@ -117,7 +117,7 @@ $page->assertScript( "new Promise((resolve) => { const check = () => { - const rows = document.querySelectorAll('#tabel-data tbody tr'); + const rows = document.querySelectorAll('[data-testid=\"datatable-statistik\"] tbody tr'); if (rows.length > 0 && !rows[0].classList.contains('dataTables_empty')) { resolve(true); } else { @@ -134,16 +134,15 @@ it('accesses grafik and renders successfully', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/penduduk') - ->assertVisible('@bt-grafik'); + ->assertVisible('@btn-toggle-grafik'); - $page->click('@bt-grafik'); + $page->click('@btn-toggle-grafik'); $page->assertScript( "new Promise((resolve) => { const check = () => { - const container = document.querySelector('#grafik-statistik'); - const canvas = document.querySelector('#barChart'); - if (container && canvas && canvas.getContext && canvas.width > 0) { + const canvas = document.querySelector('[data-testid=\"chart-bar\"]'); + if (canvas && canvas.getContext && canvas.width > 0) { resolve(true); } else { setTimeout(check, 500); @@ -159,16 +158,15 @@ it('accesses chart and renders successfully', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/penduduk') - ->assertVisible('@bt-chart'); + ->assertVisible('@btn-toggle-pie'); - $page->click('@bt-chart'); + $page->click('@btn-toggle-pie'); $page->assertScript( "new Promise((resolve) => { const check = () => { - const container = document.querySelector('#pie-statistik'); - const canvas = document.querySelector('#donutChart'); - if (container && canvas && canvas.getContext && canvas.width > 0) { + const canvas = document.querySelector('[data-testid=\"chart-donut\"]'); + if (canvas && canvas.getContext && canvas.width > 0) { resolve(true); } else { setTimeout(check, 500); diff --git a/tests/Browser/SmokeStatistikRtmTest.php b/tests/Browser/SmokeStatistikRtmTest.php index 23cc51a9..c58a857d 100644 --- a/tests/Browser/SmokeStatistikRtmTest.php +++ b/tests/Browser/SmokeStatistikRtmTest.php @@ -30,7 +30,7 @@ $page->assertScript( "new Promise((resolve) => { const check = () => { - const items = document.querySelectorAll('#daftar-statistik .pilih-kategori'); + const items = document.querySelectorAll('[data-testid=\"daftar-statistik\"] .pilih-kategori'); if (items.length > 0) { resolve(true); } else { setTimeout(check, 500); } }; check(); @@ -41,7 +41,7 @@ foreach ($kategoriRtm as $nama) { $escaped = addslashes($nama); $page->assertScript( - "Array.from(document.querySelectorAll('#daftar-statistik .pilih-kategori a')).some(a => a.textContent.trim().includes('{$escaped}'))", + "Array.from(document.querySelectorAll('[data-testid=\"daftar-statistik\"] .pilih-kategori a')).some(a => a.textContent.trim().includes('{$escaped}'))", true ); } @@ -51,28 +51,28 @@ it('displays cetak button', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/rtm') - ->assertVisible('@bt-cetak'); + ->assertVisible('@btn-cetak'); ScreenshotHelper::saveIfEnabled($page, 'statistik-rtm-cetak-button'); }); it('displays excel button', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/rtm') - ->assertVisible('@bt-excel'); + ->assertVisible('@btn-export-excel'); ScreenshotHelper::saveIfEnabled($page, 'statistik-rtm-excel-button'); }); it('displays grafik button', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/rtm') - ->assertVisible('@bt-grafik'); + ->assertVisible('@btn-toggle-grafik'); ScreenshotHelper::saveIfEnabled($page, 'statistik-rtm-grafik-button'); }); it('displays chart button', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/rtm') - ->assertVisible('@bt-chart'); + ->assertVisible('@btn-toggle-pie'); ScreenshotHelper::saveIfEnabled($page, 'statistik-rtm-chart-button'); }); @@ -84,7 +84,7 @@ $page->assertScript( "new Promise((resolve) => { const check = () => { - const rows = document.querySelectorAll('#tabel-data tbody tr'); + const rows = document.querySelectorAll('[data-testid=\"datatable-statistik\"] tbody tr'); if (rows.length > 0 && !rows[0].classList.contains('dataTables_empty')) { resolve(true); } else { @@ -106,7 +106,7 @@ $page->assertScript( "new Promise((resolve) => { const check = () => { - const items = document.querySelectorAll('#daftar-statistik .pilih-kategori'); + const items = document.querySelectorAll('[data-testid=\"daftar-statistik\"] .pilih-kategori'); if (items.length > 0) { resolve(true); } else { setTimeout(check, 500); } }; check(); @@ -117,7 +117,7 @@ $page->assertScript( "new Promise((resolve) => { const check = () => { - const rows = document.querySelectorAll('#tabel-data tbody tr'); + const rows = document.querySelectorAll('[data-testid=\"datatable-statistik\"] tbody tr'); if (rows.length > 0 && !rows[0].classList.contains('dataTables_empty')) { resolve(true); } else { @@ -134,15 +134,15 @@ it('accesses grafik and renders successfully', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/rtm') - ->assertVisible('@bt-grafik'); + ->assertVisible('@btn-toggle-grafik'); - $page->click('@bt-grafik'); + $page->click('@btn-toggle-grafik'); $page->assertScript( "new Promise((resolve) => { const check = () => { const container = document.querySelector('#grafik-statistik'); - const canvas = document.querySelector('#barChart'); + const canvas = document.querySelector('[data-testid=\"chart-bar\"]'); if (container && canvas && canvas.getContext && canvas.width > 0) { resolve(true); } else { @@ -159,15 +159,15 @@ it('accesses chart and renders successfully', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/rtm') - ->assertVisible('@bt-chart'); + ->assertVisible('@btn-toggle-pie'); - $page->click('@bt-chart'); + $page->click('@btn-toggle-pie'); $page->assertScript( "new Promise((resolve) => { const check = () => { const container = document.querySelector('#pie-statistik'); - const canvas = document.querySelector('#donutChart'); + const canvas = document.querySelector('[data-testid=\"chart-donut\"]'); if (container && canvas && canvas.getContext && canvas.width > 0) { resolve(true); } else { diff --git a/tests/Browser/SmokeStatistikSandangTest.php b/tests/Browser/SmokeStatistikSandangTest.php new file mode 100644 index 00000000..50361edf --- /dev/null +++ b/tests/Browser/SmokeStatistikSandangTest.php @@ -0,0 +1,185 @@ +user = SessionState::loginAdminUser(); +}); + +afterEach(function () { + SessionState::clear(); +}); + +$path = '/data-presisi/statistik/sandang'; + +it('opens the statistik sandang page', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path) + ->assertSee('Sandang'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-sandang-page'); +}); + +it('displays kategori statistik list', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@daftar-statistik'); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const items = document.querySelectorAll(\'[data-testid="daftar-statistik"] a\'); + if (items.length > 0) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-sandang-kategori-list'); +}); + +it('displays filter tahun', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@filter-tahun'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-sandang-filter-tahun'); +}); + +it('displays excel button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-export-excel'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-sandang-excel-button'); +}); + +it('displays grafik button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-toggle-grafik'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-sandang-grafik-button'); +}); + +it('displays chart button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-toggle-pie'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-sandang-chart-button'); +}); + +it('displays datatable', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@datatable-statistik'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-sandang-datatable'); +}); + +it('datatable displays data', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const table = document.querySelector(\'[data-testid="datatable-statistik"]\'); + if (table) { + const rows = table.querySelectorAll(\'tbody tr\'); + if (rows.length > 0 && !rows[0].classList.contains(\'dataTables_empty\')) { + resolve(true); + } else { + setTimeout(check, 500); + } + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-sandang-datatable-data'); +}); + +it('clicks a kategori statistik', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@daftar-statistik'); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const items = document.querySelectorAll(\'[data-testid="daftar-statistik"] a\'); + if (items.length > 0) { + items[0].click(); + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + $page->wait(2000); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-sandang-kategori-clicked'); +}); + +it('renders bar chart', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const bar = document.querySelector(\'[data-testid="chart-bar"]\'); + const ready = bar && bar.getContext && bar.width > 0; + if (ready) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-sandang-bar-chart'); +}); + +it('renders pie chart', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const donut = document.querySelector(\'[data-testid="chart-donut"]\'); + const ready = donut && donut.getContext && donut.width > 0; + if (ready) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-sandang-pie-chart'); +}); + +it('has no javascript errors', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path) + ->assertNoJavaScriptErrors(); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-sandang-no-errors'); +}); diff --git a/tests/Browser/SmokeStatistikSeniBudayaTest.php b/tests/Browser/SmokeStatistikSeniBudayaTest.php new file mode 100644 index 00000000..0f12402a --- /dev/null +++ b/tests/Browser/SmokeStatistikSeniBudayaTest.php @@ -0,0 +1,185 @@ +user = SessionState::loginAdminUser(); +}); + +afterEach(function () { + SessionState::clear(); +}); + +$path = '/data-presisi/statistik/senibudaya'; + +it('opens the statistik seni budaya page', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path) + ->assertSee('Seni Budaya'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-seni-budaya-page'); +}); + +it('displays kategori statistik list', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@daftar-statistik'); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const items = document.querySelectorAll(\'[data-testid="daftar-statistik"] a\'); + if (items.length > 0) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-seni-budaya-kategori-list'); +}); + +it('displays filter tahun', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@filter-tahun'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-seni-budaya-filter-tahun'); +}); + +it('displays excel button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-export-excel'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-seni-budaya-excel-button'); +}); + +it('displays grafik button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-toggle-grafik'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-seni-budaya-grafik-button'); +}); + +it('displays chart button', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@btn-toggle-pie'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-seni-budaya-chart-button'); +}); + +it('displays datatable', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@datatable-statistik'); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-seni-budaya-datatable'); +}); + +it('datatable displays data', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const table = document.querySelector(\'[data-testid="datatable-statistik"]\'); + if (table) { + const rows = table.querySelectorAll(\'tbody tr\'); + if (rows.length > 0 && !rows[0].classList.contains(\'dataTables_empty\')) { + resolve(true); + } else { + setTimeout(check, 500); + } + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-seni-budaya-datatable-data'); +}); + +it('clicks a kategori statistik', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertVisible('@daftar-statistik'); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const items = document.querySelectorAll(\'[data-testid="daftar-statistik"] a\'); + if (items.length > 0) { + items[0].click(); + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + $page->wait(2000); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-seni-budaya-kategori-clicked'); +}); + +it('renders bar chart', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const bar = document.querySelector(\'[data-testid="chart-bar"]\'); + const ready = bar && bar.getContext && bar.width > 0; + if (ready) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-seni-budaya-bar-chart'); +}); + +it('renders pie chart', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path); + + $page->assertScript( + 'new Promise((resolve) => { + const check = () => { + const donut = document.querySelector(\'[data-testid="chart-donut"]\'); + const ready = donut && donut.getContext && donut.width > 0; + if (ready) { + resolve(true); + } else { + setTimeout(check, 500); + } + }; + check(); + })', + true + ); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-seni-budaya-pie-chart'); +}); + +it('has no javascript errors', function () use ($path) { + $page = SessionState::loginAndNavigate($this->user, $path) + ->assertPathIs($path) + ->assertNoJavaScriptErrors(); + + ScreenshotHelper::saveIfEnabled($page, 'statistik-seni-budaya-no-errors'); +}); diff --git a/tests/Browser/SmokeStatistikSubmenusTest.php b/tests/Browser/SmokeStatistikSubmenusTest.php deleted file mode 100644 index 77078e94..00000000 --- a/tests/Browser/SmokeStatistikSubmenusTest.php +++ /dev/null @@ -1,225 +0,0 @@ -user = SessionState::loginAdminUser(); -}); - -afterEach(function () { - SessionState::clear(); -}); - -$statistikPages = [ - 'pangan' => [ - 'path' => '/data-presisi/statistik/pangan', - 'judul' => 'Pangan', - 'kategoriUrl' => 'pangan/kategori-statistik', - ], - 'sandang' => [ - 'path' => '/data-presisi/statistik/sandang', - 'judul' => 'Sandang', - 'kategoriUrl' => 'sandang/kategori-statistik', - ], - 'papan' => [ - 'path' => '/data-presisi/statistik/papan', - 'judul' => 'Papan', - 'kategoriUrl' => 'papan/kategori-statistik', - ], - 'seni-budaya' => [ - 'path' => '/data-presisi/statistik/senibudaya', - 'judul' => 'Seni Budaya', - 'kategoriUrl' => 'seni-budaya/kategori-statistik', - ], - 'pendidikan' => [ - 'path' => '/data-presisi/statistik/pendidikan', - 'judul' => 'Pendidikan', - 'kategoriUrl' => 'pendidikan/kategori-statistik', - ], - 'kesehatan' => [ - 'path' => '/data-presisi/statistik/kesehatan', - 'judul' => 'Kesehatan', - 'kategoriUrl' => 'kesehatan/kategori-statistik', - ], - 'jaminan-sosial' => [ - 'path' => '/data-presisi/statistik/jaminan-sosial', - 'judul' => 'Jaminan Sosial', - 'kategoriUrl' => 'jaminan-sosial/kategori-statistik', - ], - 'aktivitas-keagamaan' => [ - 'path' => '/data-presisi/statistik/aktivitas-keagamaan', - 'judul' => 'Aktivitas Keagamaan', - 'kategoriUrl' => 'agama/kategori-statistik', - ], - 'ketenagakerjaan' => [ - 'path' => '/data-presisi/statistik/ketenagakerjaan', - 'judul' => 'Ketenagakerjaan', - 'kategoriUrl' => 'ketenagakerjaan/kategori-statistik', - ], - 'adat' => [ - 'path' => '/data-presisi/statistik/adat', - 'judul' => 'Adat', - 'kategoriUrl' => 'adat/kategori-statistik', - ], -]; - -foreach ($statistikPages as $key => $config) { - $path = $config['path']; - $judul = $config['judul']; - - it("opens the statistik {$judul} page", function () use ($path, $judul) { - $page = SessionState::loginAndNavigate($this->user, $path) - ->assertPathIs($path) - ->assertSee($judul); - - ScreenshotHelper::saveIfEnabled($page, "statistik-{$judul}-page"); - }); - - it("displays kategori statistik list on {$judul}", function () use ($path) { - $page = SessionState::loginAndNavigate($this->user, $path) - ->assertVisible('[data-testid="daftar-statistik"]'); - - $page->assertScript( - 'new Promise((resolve) => { - const check = () => { - const items = document.querySelectorAll(\'[data-testid="daftar-statistik"] a\'); - if (items.length > 0) { - resolve(true); - } else { - setTimeout(check, 500); - } - }; - check(); - })', - true - ); - - ScreenshotHelper::saveIfEnabled($page, "statistik-{$judul}-kategori-list"); - }); - - it("displays filter tahun on {$judul}", function () use ($path) { - $page = SessionState::loginAndNavigate($this->user, $path) - ->assertVisible('[data-testid="filter-tahun"]'); - - ScreenshotHelper::saveIfEnabled($page, "statistik-{$judul}-filter-tahun"); - }); - - it("displays excel button on {$judul}", function () use ($path) { - $page = SessionState::loginAndNavigate($this->user, $path) - ->assertVisible('[data-testid="btn-export-excel"]'); - - ScreenshotHelper::saveIfEnabled($page, "statistik-{$judul}-excel-button"); - }); - - it("displays grafik button on {$judul}", function () use ($path) { - $page = SessionState::loginAndNavigate($this->user, $path) - ->assertVisible('[data-testid="btn-toggle-grafik"]'); - - ScreenshotHelper::saveIfEnabled($page, "statistik-{$judul}-grafik-button"); - }); - - it("displays chart button on {$judul}", function () use ($path) { - $page = SessionState::loginAndNavigate($this->user, $path) - ->assertVisible('[data-testid="btn-toggle-pie"]'); - - ScreenshotHelper::saveIfEnabled($page, "statistik-{$judul}-chart-button"); - }); - - it("displays datatable on {$judul}", function () use ($path) { - $page = SessionState::loginAndNavigate($this->user, $path) - ->assertVisible('[data-testid="datatable-statistik"]'); - - $page->assertScript( - 'new Promise((resolve) => { - const check = () => { - const table = document.querySelector(\'[data-testid="datatable-statistik"]\'); - if (table) { resolve(true); } else { setTimeout(check, 500); } - }; - check(); - })', - true - ); - - ScreenshotHelper::saveIfEnabled($page, "statistik-{$judul}-datatable"); - }); - - it("clicks a kategori statistik on {$judul}", function () use ($path) { - $page = SessionState::loginAndNavigate($this->user, $path) - ->assertVisible('[data-testid="daftar-statistik"]'); - - $page->assertScript( - 'new Promise((resolve) => { - const check = () => { - const items = document.querySelectorAll(\'[data-testid="daftar-statistik"] a\'); - if (items.length > 0) { - items[0].click(); - resolve(true); - } else { - setTimeout(check, 500); - } - }; - check(); - })', - true - ); - - $page->wait(2000); - - ScreenshotHelper::saveIfEnabled($page, "statistik-{$judul}-kategori-clicked"); - }); - - it("renders bar chart on {$judul}", function () use ($path) { - $page = SessionState::loginAndNavigate($this->user, $path) - ->assertPathIs($path); - - $page->assertScript( - 'new Promise((resolve) => { - const check = () => { - const bar = document.querySelector(\'[data-testid="chart-bar"]\'); - const ready = bar && bar.getContext && bar.width > 0; - if (ready) { - resolve(true); - } else { - setTimeout(check, 500); - } - }; - check(); - })', - true - ); - - ScreenshotHelper::saveIfEnabled($page, "statistik-{$judul}-bar-chart"); - }); - - it("renders pie chart on {$judul}", function () use ($path) { - $page = SessionState::loginAndNavigate($this->user, $path) - ->assertPathIs($path); - - $page->assertScript( - 'new Promise((resolve) => { - const check = () => { - const donut = document.querySelector(\'[data-testid="chart-donut"]\'); - const ready = donut && donut.getContext && donut.width > 0; - if (ready) { - resolve(true); - } else { - setTimeout(check, 500); - } - }; - check(); - })', - true - ); - - ScreenshotHelper::saveIfEnabled($page, "statistik-{$judul}-pie-chart"); - }); - - it("has no javascript errors on {$judul}", function () use ($path) { - $page = SessionState::loginAndNavigate($this->user, $path) - ->assertPathIs($path) - ->assertNoJavaScriptErrors(); - - ScreenshotHelper::saveIfEnabled($page, "statistik-{$judul}-no-errors"); - }); -} From 331d1efa87e8508cb451a439abc469f55aa54f77 Mon Sep 17 00:00:00 2001 From: Ahmad Afandi Date: Mon, 15 Jun 2026 15:49:01 +0700 Subject: [PATCH 42/43] kembalikan --- tests/Browser/SmokeLaporanBulananTest.php | 16 +++++++-- tests/Browser/SmokeStatistikBantuanTest.php | 30 ++++++++-------- tests/Browser/SmokeStatistikKeluargaTest.php | 30 ++++++++-------- tests/Browser/SmokeStatistikPendudukTest.php | 36 +++++++++++--------- tests/Browser/SmokeStatistikRtmTest.php | 30 ++++++++-------- 5 files changed, 77 insertions(+), 65 deletions(-) diff --git a/tests/Browser/SmokeLaporanBulananTest.php b/tests/Browser/SmokeLaporanBulananTest.php index f5061d5d..d3870964 100644 --- a/tests/Browser/SmokeLaporanBulananTest.php +++ b/tests/Browser/SmokeLaporanBulananTest.php @@ -127,7 +127,7 @@ it('displays excel button', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/laporan-bulanan') - ->assertVisible('@btn-export-excel'); + ->assertVisible('@bt-excel'); ScreenshotHelper::saveIfEnabled($page, 'laporan-bulanan-excel-button'); }); @@ -168,8 +168,18 @@ }); it('displays datatable', function () { - $page = SessionState::loginAndNavigate($this->user, '/statistik/laporan-bulanan') - ->assertVisible('@datatable-laporan-bulanan'); + $page = SessionState::loginAndNavigate($this->user, '/statistik/laporan-bulanan'); + + $page->assertScript( + "new Promise((resolve) => { + const check = () => { + const table = document.querySelector('[data-testid=\"datatable-laporan-bulanan\"]'); + if (table) { resolve(true); } else { setTimeout(check, 500); } + }; + check(); + })", + true + ); ScreenshotHelper::saveIfEnabled($page, 'laporan-bulanan-datatable'); }); diff --git a/tests/Browser/SmokeStatistikBantuanTest.php b/tests/Browser/SmokeStatistikBantuanTest.php index e0fdcf39..2f13c2e1 100644 --- a/tests/Browser/SmokeStatistikBantuanTest.php +++ b/tests/Browser/SmokeStatistikBantuanTest.php @@ -30,7 +30,7 @@ $page->assertScript( "new Promise((resolve) => { const check = () => { - const items = document.querySelectorAll('[data-testid=\"daftar-statistik\"] .pilih-kategori'); + const items = document.querySelectorAll('#daftar-statistik .pilih-kategori'); if (items.length > 0) { resolve(true); } else { setTimeout(check, 500); } }; check(); @@ -41,7 +41,7 @@ foreach ($kategoriBantuan as $nama) { $escaped = addslashes($nama); $page->assertScript( - "Array.from(document.querySelectorAll('[data-testid=\"daftar-statistik\"] .pilih-kategori a')).some(a => a.textContent.trim().includes('{$escaped}'))", + "Array.from(document.querySelectorAll('#daftar-statistik .pilih-kategori a')).some(a => a.textContent.trim().includes('{$escaped}'))", true ); } @@ -51,28 +51,28 @@ it('displays cetak button', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/bantuan') - ->assertVisible('@btn-cetak'); + ->assertVisible('@bt-cetak'); ScreenshotHelper::saveIfEnabled($page, 'statistik-bantuan-cetak-button'); }); it('displays excel button', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/bantuan') - ->assertVisible('@btn-export-excel'); + ->assertVisible('@bt-excel'); ScreenshotHelper::saveIfEnabled($page, 'statistik-bantuan-excel-button'); }); it('displays grafik button', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/bantuan') - ->assertVisible('@btn-toggle-grafik'); + ->assertVisible('@bt-grafik'); ScreenshotHelper::saveIfEnabled($page, 'statistik-bantuan-grafik-button'); }); it('displays chart button', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/bantuan') - ->assertVisible('@btn-toggle-pie'); + ->assertVisible('@bt-chart'); ScreenshotHelper::saveIfEnabled($page, 'statistik-bantuan-chart-button'); }); @@ -84,7 +84,7 @@ $page->assertScript( "new Promise((resolve) => { const check = () => { - const rows = document.querySelectorAll('[data-testid=\"datatable-statistik\"] tbody tr'); + const rows = document.querySelectorAll('#tabel-data tbody tr'); if (rows.length > 0 && !rows[0].classList.contains('dataTables_empty')) { resolve(true); } else { @@ -106,7 +106,7 @@ $page->assertScript( "new Promise((resolve) => { const check = () => { - const items = document.querySelectorAll('[data-testid=\"daftar-statistik\"] .pilih-kategori'); + const items = document.querySelectorAll('#daftar-statistik .pilih-kategori'); if (items.length > 0) { resolve(true); } else { setTimeout(check, 500); } }; check(); @@ -117,7 +117,7 @@ $page->assertScript( "new Promise((resolve) => { const check = () => { - const rows = document.querySelectorAll('[data-testid=\"datatable-statistik\"] tbody tr'); + const rows = document.querySelectorAll('#tabel-data tbody tr'); if (rows.length > 0 && !rows[0].classList.contains('dataTables_empty')) { resolve(true); } else { @@ -134,15 +134,15 @@ it('accesses grafik and renders successfully', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/bantuan') - ->assertVisible('@btn-toggle-grafik'); + ->assertVisible('@bt-grafik'); - $page->click('@btn-toggle-grafik'); + $page->click('@bt-grafik'); $page->assertScript( "new Promise((resolve) => { const check = () => { const container = document.querySelector('#grafik-statistik'); - const canvas = document.querySelector('[data-testid=\"chart-bar\"]'); + const canvas = document.querySelector('#barChart'); if (container && canvas && canvas.getContext && canvas.width > 0) { resolve(true); } else { @@ -159,15 +159,15 @@ it('accesses chart and renders successfully', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/bantuan') - ->assertVisible('@btn-toggle-pie'); + ->assertVisible('@bt-chart'); - $page->click('@btn-toggle-pie'); + $page->click('@bt-chart'); $page->assertScript( "new Promise((resolve) => { const check = () => { const container = document.querySelector('#pie-statistik'); - const canvas = document.querySelector('[data-testid=\"chart-donut\"]'); + const canvas = document.querySelector('#donutChart'); if (container && canvas && canvas.getContext && canvas.width > 0) { resolve(true); } else { diff --git a/tests/Browser/SmokeStatistikKeluargaTest.php b/tests/Browser/SmokeStatistikKeluargaTest.php index b1c0055f..f98c49f6 100644 --- a/tests/Browser/SmokeStatistikKeluargaTest.php +++ b/tests/Browser/SmokeStatistikKeluargaTest.php @@ -30,7 +30,7 @@ $page->assertScript( "new Promise((resolve) => { const check = () => { - const items = document.querySelectorAll('[data-testid=\"daftar-statistik\"] .pilih-kategori'); + const items = document.querySelectorAll('#daftar-statistik .pilih-kategori'); if (items.length > 0) { resolve(true); } else { setTimeout(check, 500); } }; check(); @@ -41,7 +41,7 @@ foreach ($kategoriKeluarga as $nama) { $escaped = addslashes($nama); $page->assertScript( - "Array.from(document.querySelectorAll('[data-testid=\"daftar-statistik\"] .pilih-kategori a')).some(a => a.textContent.trim().includes('{$escaped}'))", + "Array.from(document.querySelectorAll('#daftar-statistik .pilih-kategori a')).some(a => a.textContent.trim().includes('{$escaped}'))", true ); } @@ -51,28 +51,28 @@ it('displays cetak button', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/keluarga') - ->assertVisible('@btn-cetak'); + ->assertVisible('@bt-cetak'); ScreenshotHelper::saveIfEnabled($page, 'statistik-keluarga-cetak-button'); }); it('displays excel button', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/keluarga') - ->assertVisible('@btn-export-excel'); + ->assertVisible('@bt-excel'); ScreenshotHelper::saveIfEnabled($page, 'statistik-keluarga-excel-button'); }); it('displays grafik button', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/keluarga') - ->assertVisible('@btn-toggle-grafik'); + ->assertVisible('@bt-grafik'); ScreenshotHelper::saveIfEnabled($page, 'statistik-keluarga-grafik-button'); }); it('displays chart button', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/keluarga') - ->assertVisible('@btn-toggle-pie'); + ->assertVisible('@bt-chart'); ScreenshotHelper::saveIfEnabled($page, 'statistik-keluarga-chart-button'); }); @@ -84,7 +84,7 @@ $page->assertScript( "new Promise((resolve) => { const check = () => { - const rows = document.querySelectorAll('[data-testid=\"datatable-statistik\"] tbody tr'); + const rows = document.querySelectorAll('#tabel-data tbody tr'); if (rows.length > 0 && !rows[0].classList.contains('dataTables_empty')) { resolve(true); } else { @@ -106,7 +106,7 @@ $page->assertScript( "new Promise((resolve) => { const check = () => { - const items = document.querySelectorAll('[data-testid=\"daftar-statistik\"] .pilih-kategori'); + const items = document.querySelectorAll('#daftar-statistik .pilih-kategori'); if (items.length > 0) { resolve(true); } else { setTimeout(check, 500); } }; check(); @@ -117,7 +117,7 @@ $page->assertScript( "new Promise((resolve) => { const check = () => { - const rows = document.querySelectorAll('[data-testid=\"datatable-statistik\"] tbody tr'); + const rows = document.querySelectorAll('#tabel-data tbody tr'); if (rows.length > 0 && !rows[0].classList.contains('dataTables_empty')) { resolve(true); } else { @@ -134,15 +134,15 @@ it('accesses grafik and renders successfully', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/keluarga') - ->assertVisible('@btn-toggle-grafik'); + ->assertVisible('@bt-grafik'); - $page->click('@btn-toggle-grafik'); + $page->click('@bt-grafik'); $page->assertScript( "new Promise((resolve) => { const check = () => { const container = document.querySelector('#grafik-statistik'); - const canvas = document.querySelector('[data-testid=\"chart-bar\"]'); + const canvas = document.querySelector('#barChart'); if (container && canvas && canvas.getContext && canvas.width > 0) { resolve(true); } else { @@ -159,15 +159,15 @@ it('accesses chart and renders successfully', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/keluarga') - ->assertVisible('@btn-toggle-pie'); + ->assertVisible('@bt-chart'); - $page->click('@btn-toggle-pie'); + $page->click('@bt-chart'); $page->assertScript( "new Promise((resolve) => { const check = () => { const container = document.querySelector('#pie-statistik'); - const canvas = document.querySelector('[data-testid=\"chart-donut\"]'); + const canvas = document.querySelector('#donutChart'); if (container && canvas && canvas.getContext && canvas.width > 0) { resolve(true); } else { diff --git a/tests/Browser/SmokeStatistikPendudukTest.php b/tests/Browser/SmokeStatistikPendudukTest.php index eecf3368..c646a67b 100644 --- a/tests/Browser/SmokeStatistikPendudukTest.php +++ b/tests/Browser/SmokeStatistikPendudukTest.php @@ -30,7 +30,7 @@ $page->assertScript( "new Promise((resolve) => { const check = () => { - const items = document.querySelectorAll('[data-testid=\"daftar-statistik\"] .pilih-kategori'); + const items = document.querySelectorAll('#daftar-statistik .pilih-kategori'); if (items.length > 0) { resolve(true); } else { setTimeout(check, 500); } }; check(); @@ -41,7 +41,7 @@ foreach ($kategoriPenduduk as $nama) { $escaped = addslashes($nama); $page->assertScript( - "Array.from(document.querySelectorAll('[data-testid=\"daftar-statistik\"] .pilih-kategori a')).some(a => a.textContent.trim().includes('{$escaped}'))", + "Array.from(document.querySelectorAll('#daftar-statistik .pilih-kategori a')).some(a => a.textContent.trim().includes('{$escaped}'))", true ); } @@ -51,28 +51,28 @@ it('displays cetak button', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/penduduk') - ->assertVisible('@btn-cetak'); + ->assertVisible('@bt-cetak'); ScreenshotHelper::saveIfEnabled($page, 'statistik-penduduk-cetak-button'); }); it('displays excel button', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/penduduk') - ->assertVisible('@btn-export-excel'); + ->assertVisible('@bt-excel'); ScreenshotHelper::saveIfEnabled($page, 'statistik-penduduk-excel-button'); }); it('displays grafik button', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/penduduk') - ->assertVisible('@btn-toggle-grafik'); + ->assertVisible('@bt-grafik'); ScreenshotHelper::saveIfEnabled($page, 'statistik-penduduk-grafik-button'); }); it('displays chart button', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/penduduk') - ->assertVisible('@btn-toggle-pie'); + ->assertVisible('@bt-chart'); ScreenshotHelper::saveIfEnabled($page, 'statistik-penduduk-chart-button'); }); @@ -84,7 +84,7 @@ $page->assertScript( "new Promise((resolve) => { const check = () => { - const rows = document.querySelectorAll('[data-testid=\"datatable-statistik\"] tbody tr'); + const rows = document.querySelectorAll('#tabel-data tbody tr'); if (rows.length > 0 && !rows[0].classList.contains('dataTables_empty')) { resolve(true); } else { @@ -106,7 +106,7 @@ $page->assertScript( "new Promise((resolve) => { const check = () => { - const items = document.querySelectorAll('[data-testid=\"daftar-statistik\"] .pilih-kategori'); + const items = document.querySelectorAll('#daftar-statistik .pilih-kategori'); if (items.length > 0) { resolve(true); } else { setTimeout(check, 500); } }; check(); @@ -117,7 +117,7 @@ $page->assertScript( "new Promise((resolve) => { const check = () => { - const rows = document.querySelectorAll('[data-testid=\"datatable-statistik\"] tbody tr'); + const rows = document.querySelectorAll('#tabel-data tbody tr'); if (rows.length > 0 && !rows[0].classList.contains('dataTables_empty')) { resolve(true); } else { @@ -134,15 +134,16 @@ it('accesses grafik and renders successfully', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/penduduk') - ->assertVisible('@btn-toggle-grafik'); + ->assertVisible('@bt-grafik'); - $page->click('@btn-toggle-grafik'); + $page->click('@bt-grafik'); $page->assertScript( "new Promise((resolve) => { const check = () => { - const canvas = document.querySelector('[data-testid=\"chart-bar\"]'); - if (canvas && canvas.getContext && canvas.width > 0) { + const container = document.querySelector('#grafik-statistik'); + const canvas = document.querySelector('#barChart'); + if (container && canvas && canvas.getContext && canvas.width > 0) { resolve(true); } else { setTimeout(check, 500); @@ -158,15 +159,16 @@ it('accesses chart and renders successfully', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/penduduk') - ->assertVisible('@btn-toggle-pie'); + ->assertVisible('@bt-chart'); - $page->click('@btn-toggle-pie'); + $page->click('@bt-chart'); $page->assertScript( "new Promise((resolve) => { const check = () => { - const canvas = document.querySelector('[data-testid=\"chart-donut\"]'); - if (canvas && canvas.getContext && canvas.width > 0) { + const container = document.querySelector('#pie-statistik'); + const canvas = document.querySelector('#donutChart'); + if (container && canvas && canvas.getContext && canvas.width > 0) { resolve(true); } else { setTimeout(check, 500); diff --git a/tests/Browser/SmokeStatistikRtmTest.php b/tests/Browser/SmokeStatistikRtmTest.php index c58a857d..23cc51a9 100644 --- a/tests/Browser/SmokeStatistikRtmTest.php +++ b/tests/Browser/SmokeStatistikRtmTest.php @@ -30,7 +30,7 @@ $page->assertScript( "new Promise((resolve) => { const check = () => { - const items = document.querySelectorAll('[data-testid=\"daftar-statistik\"] .pilih-kategori'); + const items = document.querySelectorAll('#daftar-statistik .pilih-kategori'); if (items.length > 0) { resolve(true); } else { setTimeout(check, 500); } }; check(); @@ -41,7 +41,7 @@ foreach ($kategoriRtm as $nama) { $escaped = addslashes($nama); $page->assertScript( - "Array.from(document.querySelectorAll('[data-testid=\"daftar-statistik\"] .pilih-kategori a')).some(a => a.textContent.trim().includes('{$escaped}'))", + "Array.from(document.querySelectorAll('#daftar-statistik .pilih-kategori a')).some(a => a.textContent.trim().includes('{$escaped}'))", true ); } @@ -51,28 +51,28 @@ it('displays cetak button', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/rtm') - ->assertVisible('@btn-cetak'); + ->assertVisible('@bt-cetak'); ScreenshotHelper::saveIfEnabled($page, 'statistik-rtm-cetak-button'); }); it('displays excel button', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/rtm') - ->assertVisible('@btn-export-excel'); + ->assertVisible('@bt-excel'); ScreenshotHelper::saveIfEnabled($page, 'statistik-rtm-excel-button'); }); it('displays grafik button', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/rtm') - ->assertVisible('@btn-toggle-grafik'); + ->assertVisible('@bt-grafik'); ScreenshotHelper::saveIfEnabled($page, 'statistik-rtm-grafik-button'); }); it('displays chart button', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/rtm') - ->assertVisible('@btn-toggle-pie'); + ->assertVisible('@bt-chart'); ScreenshotHelper::saveIfEnabled($page, 'statistik-rtm-chart-button'); }); @@ -84,7 +84,7 @@ $page->assertScript( "new Promise((resolve) => { const check = () => { - const rows = document.querySelectorAll('[data-testid=\"datatable-statistik\"] tbody tr'); + const rows = document.querySelectorAll('#tabel-data tbody tr'); if (rows.length > 0 && !rows[0].classList.contains('dataTables_empty')) { resolve(true); } else { @@ -106,7 +106,7 @@ $page->assertScript( "new Promise((resolve) => { const check = () => { - const items = document.querySelectorAll('[data-testid=\"daftar-statistik\"] .pilih-kategori'); + const items = document.querySelectorAll('#daftar-statistik .pilih-kategori'); if (items.length > 0) { resolve(true); } else { setTimeout(check, 500); } }; check(); @@ -117,7 +117,7 @@ $page->assertScript( "new Promise((resolve) => { const check = () => { - const rows = document.querySelectorAll('[data-testid=\"datatable-statistik\"] tbody tr'); + const rows = document.querySelectorAll('#tabel-data tbody tr'); if (rows.length > 0 && !rows[0].classList.contains('dataTables_empty')) { resolve(true); } else { @@ -134,15 +134,15 @@ it('accesses grafik and renders successfully', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/rtm') - ->assertVisible('@btn-toggle-grafik'); + ->assertVisible('@bt-grafik'); - $page->click('@btn-toggle-grafik'); + $page->click('@bt-grafik'); $page->assertScript( "new Promise((resolve) => { const check = () => { const container = document.querySelector('#grafik-statistik'); - const canvas = document.querySelector('[data-testid=\"chart-bar\"]'); + const canvas = document.querySelector('#barChart'); if (container && canvas && canvas.getContext && canvas.width > 0) { resolve(true); } else { @@ -159,15 +159,15 @@ it('accesses chart and renders successfully', function () { $page = SessionState::loginAndNavigate($this->user, '/statistik/rtm') - ->assertVisible('@btn-toggle-pie'); + ->assertVisible('@bt-chart'); - $page->click('@btn-toggle-pie'); + $page->click('@bt-chart'); $page->assertScript( "new Promise((resolve) => { const check = () => { const container = document.querySelector('#pie-statistik'); - const canvas = document.querySelector('[data-testid=\"chart-donut\"]'); + const canvas = document.querySelector('#donutChart'); if (container && canvas && canvas.getContext && canvas.width > 0) { resolve(true); } else { From c37c904cd128dfb27f853a7e44504b4b17341401 Mon Sep 17 00:00:00 2001 From: Ahmad Afandi Date: Wed, 17 Jun 2026 12:03:53 +0700 Subject: [PATCH 43/43] smoke test data presisi --- .../filter-kategori-wisata.blade.php | 2 +- .../data_presisi/kesehatan/index.blade.php | 5 +- .../ketenagakerjaan/chart.blade.php | 4 +- .../ketenagakerjaan/index.blade.php | 11 +-- .../data_presisi/pendidikan/chart.blade.php | 4 +- .../data_presisi/pendidikan/index.blade.php | 11 +-- .../data_presisi/seni_budaya/chart.blade.php | 4 +- .../data_presisi/seni_budaya/index.blade.php | 3 + .../data_pokok/infrastruktur/index.blade.php | 2 +- .../data_pokok/jaminan_sosial/index.blade.php | 8 +-- .../data_pokok/kesehatan/chart.blade.php | 4 +- .../data_pokok/kesehatan/index.blade.php | 4 +- .../ketenagakerjaan/chart.blade.php | 4 +- .../ketenagakerjaan/index.blade.php | 4 +- .../data_pokok/pariwisata/chart.blade.php | 4 +- .../data_pokok/pariwisata/index.blade.php | 2 +- .../data_pokok/pendidikan/chart.blade.php | 4 +- .../data_pokok/pendidikan/index.blade.php | 4 +- resources/views/layouts/index.blade.php | 7 ++ tests/Browser/MswSetup.php | 15 ++-- ...hp => SmokeDataPokokInfrastrukturTest.php} | 26 +++---- ...hp => SmokeDataPokokJaminanSosialTest.php} | 32 ++++----- ...t.php => SmokeDataPokokPariwisataTest.php} | 28 ++++---- ...tTest.php => SmokeDataPresisiAdatTest.php} | 20 +++--- ....php => SmokeDataPresisiKesehatanTest.php} | 16 ++--- ...> SmokeDataPresisiKetenagakerjaanTest.php} | 16 ++--- ...ataPresisiLaporanPengisianPerDesaTest.php} | 12 ++-- ... SmokeDataPresisiLaporanPengisianTest.php} | 14 ++-- ...est.php => SmokeDataPresisiPanganTest.php} | 20 +++--- ...php => SmokeDataPresisiPendidikanTest.php} | 16 ++--- ...php => SmokeDataPresisiSeniBudayaTest.php} | 20 +++--- ... => SmokeDataPresisiStatistikAdatTest.php} | 2 - ...resisiStatistikAktivitasKeagamaanTest.php} | 2 - ...DataPresisiStatistikJaminanSosialTest.php} | 2 - ...mokeDataPresisiStatistikKesehatanTest.php} | 2 - ...taPresisiStatistikKetenagakerjaanTest.php} | 2 - ...> SmokeDataPresisiStatistikPanganTest.php} | 4 +- ...=> SmokeDataPresisiStatistikPapanTest.php} | 2 - ...okeDataPresisiStatistikPendidikanTest.php} | 2 - ... SmokeDataPresisiStatistikSandangTest.php} | 2 - ...okeDataPresisiStatistikSeniBudayaTest.php} | 2 - tests/Browser/SmokeKetenagakerjaanTest.php | 12 ++-- tests/Browser/SmokePendidikanTest.php | 8 +-- tests/Browser/fixtures/adat.json | 2 +- tests/Browser/fixtures/agama.json | 25 +++++-- tests/Browser/fixtures/bantuan.json | 2 +- tests/Browser/fixtures/infrastruktur.json | 69 +++++++++++++------ tests/Browser/fixtures/jaminan-sosial.json | 52 +++++++++++--- .../fixtures/kategori-statistik-adat.json | 8 ++- .../fixtures/kategori-statistik-agama.json | 8 ++- .../kategori-statistik-jaminan-sosial.json | 9 ++- .../kategori-statistik-kesehatan.json | 8 ++- .../kategori-statistik-ketenagakerjaan.json | 8 ++- .../fixtures/kategori-statistik-pangan.json | 8 ++- .../fixtures/kategori-statistik-papan.json | 8 ++- .../kategori-statistik-pendidikan.json | 8 ++- .../fixtures/kategori-statistik-sandang.json | 8 ++- .../kategori-statistik-seni-budaya.json | 8 ++- tests/Browser/fixtures/kesehatan-presisi.json | 40 +++++++++++ tests/Browser/fixtures/kesehatan.json | 2 +- .../fixtures/ketenagakerjaan-presisi.json | 36 ++++++++++ tests/Browser/fixtures/ketenagakerjaan.json | 2 +- tests/Browser/fixtures/pangan.json | 38 ++++++++-- tests/Browser/fixtures/papan.json | 2 +- tests/Browser/fixtures/pariwisata.json | 14 ++-- .../Browser/fixtures/pendidikan-presisi.json | 36 ++++++++++ tests/Browser/fixtures/pendidikan.json | 2 +- tests/Browser/fixtures/penduduk-data.json | 4 +- tests/Browser/fixtures/sandang.json | 32 +++++++-- tests/Browser/fixtures/seni-budaya.json | 26 +++++-- 70 files changed, 558 insertions(+), 275 deletions(-) rename tests/Browser/{SmokeInfrastrukturTest.php => SmokeDataPokokInfrastrukturTest.php} (80%) rename tests/Browser/{SmokeJaminanSosialTest.php => SmokeDataPokokJaminanSosialTest.php} (79%) rename tests/Browser/{SmokePariwisataTest.php => SmokeDataPokokPariwisataTest.php} (77%) rename tests/Browser/{SmokeDataPokokAdatTest.php => SmokeDataPresisiAdatTest.php} (83%) rename tests/Browser/{SmokeDataPokokKesehatanTest.php => SmokeDataPresisiKesehatanTest.php} (81%) rename tests/Browser/{SmokeDataPokokKetenagakerjaanTest.php => SmokeDataPresisiKetenagakerjaanTest.php} (80%) rename tests/Browser/{SmokeLaporanPengisianPerDesaTest.php => SmokeDataPresisiLaporanPengisianPerDesaTest.php} (78%) rename tests/Browser/{SmokeLaporanPengisianTest.php => SmokeDataPresisiLaporanPengisianTest.php} (78%) rename tests/Browser/{SmokeDataPokokPanganTest.php => SmokeDataPresisiPanganTest.php} (82%) rename tests/Browser/{SmokeDataPokokPendidikanTest.php => SmokeDataPresisiPendidikanTest.php} (81%) rename tests/Browser/{SmokeDataPokokSeniBudayaTest.php => SmokeDataPresisiSeniBudayaTest.php} (82%) rename tests/Browser/{SmokeStatistikAdatTest.php => SmokeDataPresisiStatistikAdatTest.php} (99%) rename tests/Browser/{SmokeStatistikAktivitasKeagamaanTest.php => SmokeDataPresisiStatistikAktivitasKeagamaanTest.php} (99%) rename tests/Browser/{SmokeStatistikJaminanSosialTest.php => SmokeDataPresisiStatistikJaminanSosialTest.php} (99%) rename tests/Browser/{SmokeStatistikKesehatanTest.php => SmokeDataPresisiStatistikKesehatanTest.php} (99%) rename tests/Browser/{SmokeStatistikKetenagakerjaanTest.php => SmokeDataPresisiStatistikKetenagakerjaanTest.php} (99%) rename tests/Browser/{SmokeStatistikPanganTest.php => SmokeDataPresisiStatistikPanganTest.php} (99%) rename tests/Browser/{SmokeStatistikPapanTest.php => SmokeDataPresisiStatistikPapanTest.php} (99%) rename tests/Browser/{SmokeStatistikPendidikanTest.php => SmokeDataPresisiStatistikPendidikanTest.php} (99%) rename tests/Browser/{SmokeStatistikSandangTest.php => SmokeDataPresisiStatistikSandangTest.php} (99%) rename tests/Browser/{SmokeStatistikSeniBudayaTest.php => SmokeDataPresisiStatistikSeniBudayaTest.php} (99%) create mode 100644 tests/Browser/fixtures/kesehatan-presisi.json create mode 100644 tests/Browser/fixtures/ketenagakerjaan-presisi.json create mode 100644 tests/Browser/fixtures/pendidikan-presisi.json diff --git a/resources/views/components/filter-kategori-wisata.blade.php b/resources/views/components/filter-kategori-wisata.blade.php index 9e65a022..ee09f76f 100644 --- a/resources/views/components/filter-kategori-wisata.blade.php +++ b/resources/views/components/filter-kategori-wisata.blade.php @@ -1,5 +1,5 @@
- diff --git a/resources/views/data_pokok/data_presisi/kesehatan/index.blade.php b/resources/views/data_pokok/data_presisi/kesehatan/index.blade.php index 92e4fa6a..dad4354c 100644 --- a/resources/views/data_pokok/data_presisi/kesehatan/index.blade.php +++ b/resources/views/data_pokok/data_presisi/kesehatan/index.blade.php @@ -23,6 +23,9 @@
+
+ +
@@ -38,7 +41,7 @@
-
+
diff --git a/resources/views/data_pokok/data_presisi/ketenagakerjaan/chart.blade.php b/resources/views/data_pokok/data_presisi/ketenagakerjaan/chart.blade.php index 35c061b7..1963ce36 100644 --- a/resources/views/data_pokok/data_presisi/ketenagakerjaan/chart.blade.php +++ b/resources/views/data_pokok/data_presisi/ketenagakerjaan/chart.blade.php @@ -4,10 +4,10 @@ function grafikPie() { $('#barChart').remove(); $('#donutChart').remove(); $('#grafik').append( - '' + '' ); $('#pie').append( - '' + '' ); // Data untuk bar chart tampilChart('bar', 'barChart', generateChartData(data_grafik, 'tempat_kerja')); diff --git a/resources/views/data_pokok/data_presisi/ketenagakerjaan/index.blade.php b/resources/views/data_pokok/data_presisi/ketenagakerjaan/index.blade.php index 4d79fbdb..46d08e67 100644 --- a/resources/views/data_pokok/data_presisi/ketenagakerjaan/index.blade.php +++ b/resources/views/data_pokok/data_presisi/ketenagakerjaan/index.blade.php @@ -21,7 +21,10 @@
- + +
+
+
@@ -31,14 +34,14 @@
- +
- +
-
Aksi
+
diff --git a/resources/views/data_pokok/data_presisi/pendidikan/chart.blade.php b/resources/views/data_pokok/data_presisi/pendidikan/chart.blade.php index cb3fef16..5fdf23dc 100644 --- a/resources/views/data_pokok/data_presisi/pendidikan/chart.blade.php +++ b/resources/views/data_pokok/data_presisi/pendidikan/chart.blade.php @@ -4,10 +4,10 @@ function grafikPie() { $('#barChart').remove(); $('#donutChart').remove(); $('#grafik').append( - '' + '' ); $('#pie').append( - '' + '' ); // Data untuk bar chart tampilChart('bar', 'barChart', generateChartData(data_grafik, 'jenis_pendidikan_kesetaraan_yg_diikuti')); diff --git a/resources/views/data_pokok/data_presisi/pendidikan/index.blade.php b/resources/views/data_pokok/data_presisi/pendidikan/index.blade.php index b4d77b3f..ed5a9db9 100644 --- a/resources/views/data_pokok/data_presisi/pendidikan/index.blade.php +++ b/resources/views/data_pokok/data_presisi/pendidikan/index.blade.php @@ -21,7 +21,10 @@
- + +
+
+
@@ -31,14 +34,14 @@
- +
- +
-
Aksi
+
diff --git a/resources/views/data_pokok/data_presisi/seni_budaya/chart.blade.php b/resources/views/data_pokok/data_presisi/seni_budaya/chart.blade.php index 5705ac84..3e2f269e 100644 --- a/resources/views/data_pokok/data_presisi/seni_budaya/chart.blade.php +++ b/resources/views/data_pokok/data_presisi/seni_budaya/chart.blade.php @@ -4,10 +4,10 @@ function grafikPie() { $('#barChart').remove(); $('#donutChart').remove(); $('#grafik').append( - '' + '' ); $('#pie').append( - '' + '' ); // Data untuk bar chart tampilChart('bar', 'barChart', generateChartData(data_grafik, 'jenis_seni_value')); diff --git a/resources/views/data_pokok/data_presisi/seni_budaya/index.blade.php b/resources/views/data_pokok/data_presisi/seni_budaya/index.blade.php index ae7a589b..22df9d2f 100644 --- a/resources/views/data_pokok/data_presisi/seni_budaya/index.blade.php +++ b/resources/views/data_pokok/data_presisi/seni_budaya/index.blade.php @@ -23,6 +23,9 @@
+
+ +
diff --git a/resources/views/data_pokok/infrastruktur/index.blade.php b/resources/views/data_pokok/infrastruktur/index.blade.php index 98794347..87fc0665 100644 --- a/resources/views/data_pokok/infrastruktur/index.blade.php +++ b/resources/views/data_pokok/infrastruktur/index.blade.php @@ -48,7 +48,7 @@
-
Aksi
+
diff --git a/resources/views/data_pokok/jaminan_sosial/index.blade.php b/resources/views/data_pokok/jaminan_sosial/index.blade.php index 03e050c8..56dc1903 100644 --- a/resources/views/data_pokok/jaminan_sosial/index.blade.php +++ b/resources/views/data_pokok/jaminan_sosial/index.blade.php @@ -15,7 +15,7 @@
- Jenis Bantuan + Statistik Jenis Bantuan
@@ -27,7 +27,7 @@
- Jenis Gangguan Mental + Statistik Jenis Gangguan Mental
@@ -39,7 +39,7 @@
- Jenis Gangguan Mental + Statistik Jenis Penanganan
@@ -70,7 +70,7 @@
-
Kategori
+
diff --git a/resources/views/data_pokok/kesehatan/chart.blade.php b/resources/views/data_pokok/kesehatan/chart.blade.php index 88648fba..7b812161 100644 --- a/resources/views/data_pokok/kesehatan/chart.blade.php +++ b/resources/views/data_pokok/kesehatan/chart.blade.php @@ -4,10 +4,10 @@ function grafik() { $('#barChart').remove(); $('#donutChart').remove(); $('#grafik').append( - '' + '' ); $('#pie').append( - '' + '' ); // Data untuk bar chart tampilChart('bar', 'barChart', generateChartData(data_grafik, 'status_gizi')); diff --git a/resources/views/data_pokok/kesehatan/index.blade.php b/resources/views/data_pokok/kesehatan/index.blade.php index 6251721d..d6dca9f6 100644 --- a/resources/views/data_pokok/kesehatan/index.blade.php +++ b/resources/views/data_pokok/kesehatan/index.blade.php @@ -17,7 +17,7 @@
- +

@@ -30,7 +30,7 @@
- +

diff --git a/resources/views/data_pokok/ketenagakerjaan/chart.blade.php b/resources/views/data_pokok/ketenagakerjaan/chart.blade.php index 7493ba28..92cd2255 100644 --- a/resources/views/data_pokok/ketenagakerjaan/chart.blade.php +++ b/resources/views/data_pokok/ketenagakerjaan/chart.blade.php @@ -4,10 +4,10 @@ function grafikPie() { $('#barChart').remove(); $('#donutChart').remove(); $('#grafik').append( - '' + '' ); $('#pie').append( - '' + '' ); // Data untuk bar chart diff --git a/resources/views/data_pokok/ketenagakerjaan/index.blade.php b/resources/views/data_pokok/ketenagakerjaan/index.blade.php index e3344d33..0c88ae48 100644 --- a/resources/views/data_pokok/ketenagakerjaan/index.blade.php +++ b/resources/views/data_pokok/ketenagakerjaan/index.blade.php @@ -17,7 +17,7 @@
- +

@@ -30,7 +30,7 @@
- +

diff --git a/resources/views/data_pokok/pariwisata/chart.blade.php b/resources/views/data_pokok/pariwisata/chart.blade.php index 58df6826..ea407fdd 100644 --- a/resources/views/data_pokok/pariwisata/chart.blade.php +++ b/resources/views/data_pokok/pariwisata/chart.blade.php @@ -18,10 +18,10 @@ function grafikPie() { $('#barChart').remove(); $('#donutChart').remove(); $('#grafik').append( - '' + '' ); $('#pie').append( - '' + '' ); var grafik = modifikasiDataGrafik(data_grafik); var pie = modifikasiDataPie(data_grafik, 'tingkat_pemanfaatan'); diff --git a/resources/views/data_pokok/pariwisata/index.blade.php b/resources/views/data_pokok/pariwisata/index.blade.php index e1d88860..82938091 100644 --- a/resources/views/data_pokok/pariwisata/index.blade.php +++ b/resources/views/data_pokok/pariwisata/index.blade.php @@ -60,7 +60,7 @@
-
Aksi
+
diff --git a/resources/views/data_pokok/pendidikan/chart.blade.php b/resources/views/data_pokok/pendidikan/chart.blade.php index 08773043..1ce47571 100644 --- a/resources/views/data_pokok/pendidikan/chart.blade.php +++ b/resources/views/data_pokok/pendidikan/chart.blade.php @@ -4,10 +4,10 @@ function grafikPie() { $('#barChart').remove(); $('#donutChart').remove(); $('#grafik').append( - '' + '' ); $('#pie').append( - '' + '' ); // Data untuk bar chart diff --git a/resources/views/data_pokok/pendidikan/index.blade.php b/resources/views/data_pokok/pendidikan/index.blade.php index 7115db27..300b7156 100644 --- a/resources/views/data_pokok/pendidikan/index.blade.php +++ b/resources/views/data_pokok/pendidikan/index.blade.php @@ -17,7 +17,7 @@
- +

@@ -30,7 +30,7 @@
- +

diff --git a/resources/views/layouts/index.blade.php b/resources/views/layouts/index.blade.php index b42a9e1d..b3ee0c05 100644 --- a/resources/views/layouts/index.blade.php +++ b/resources/views/layouts/index.blade.php @@ -12,6 +12,13 @@ @push('js')
ID