From 5a9f1f10175c7748ed29d76dedfaf268d4721eb3 Mon Sep 17 00:00:00 2001 From: xiaomlove <1939737565@qq.com> Date: Sun, 29 Dec 2024 22:16:41 +0800 Subject: [PATCH] improve admin user profile + image hosting --- app/Auth/NexusWebUserProvider.php | 4 + app/Console/Commands/Test.php | 4 +- .../SettingResource/Pages/EditSetting.php | 89 +- .../Resources/User/ExamUserResource.php | 2 +- .../Resources/User/HitAndRunResource.php | 2 +- app/Filament/Resources/User/UserResource.php | 210 +- .../User/UserResource/Pages/UserProfile.php | 108 +- app/Http/Controllers/Controller.php | 7 + app/Models/Claim.php | 10 +- app/Models/HitAndRun.php | 2 +- app/Models/User.php | 5 + app/Providers/Filament/AppPanelProvider.php | 45 +- app/Providers/GoogleDriveAdapter.php | 11 - composer.json | 3 +- composer.lock | 6 +- config/livewire.php | 215 +- ...202156_add_driver_to_attachments_table.php | 28 + include/constants.php | 4 +- include/functions.php | 37 +- include/globalfunctions.php | 8 + nexus/Attachment/Drivers/Chevereto.php | 55 + nexus/Attachment/Drivers/Local.php | 22 + nexus/Attachment/Drivers/Lsky.php | 64 + nexus/Attachment/Storage.php | 65 + nexus/Database/NexusDB.php | 1 - public/attachment.php | 359 +- public/userdetails.php | 2 +- public/vendor/livewire/livewire.esm.js | 10926 ++++++++++++++++ public/vendor/livewire/livewire.esm.js.map | 7 + public/vendor/livewire/livewire.js | 10032 +++++++++++++- public/vendor/livewire/livewire.min.js | 103 + public/vendor/livewire/livewire.min.js.map | 7 + public/vendor/livewire/manifest.json | 3 +- resources/lang/en/label.php | 9 + resources/lang/zh_CN/label.php | 9 + resources/lang/zh_TW/label.php | 9 + .../pages/edit-hit-and-run.blade.php | 2 +- .../filament/widgets/account-info.blade.php | 27 +- .../filament/widgets/stat-table.blade.php | 62 +- routes/third-party.php | 2 +- 40 files changed, 22110 insertions(+), 456 deletions(-) delete mode 100644 app/Providers/GoogleDriveAdapter.php create mode 100644 database/migrations/2024_12_29_202156_add_driver_to_attachments_table.php create mode 100644 nexus/Attachment/Drivers/Chevereto.php create mode 100644 nexus/Attachment/Drivers/Local.php create mode 100644 nexus/Attachment/Drivers/Lsky.php create mode 100644 nexus/Attachment/Storage.php create mode 100644 public/vendor/livewire/livewire.esm.js create mode 100644 public/vendor/livewire/livewire.esm.js.map create mode 100644 public/vendor/livewire/livewire.min.js create mode 100644 public/vendor/livewire/livewire.min.js.map diff --git a/app/Auth/NexusWebUserProvider.php b/app/Auth/NexusWebUserProvider.php index a2a4f9f8..203c3e71 100644 --- a/app/Auth/NexusWebUserProvider.php +++ b/app/Auth/NexusWebUserProvider.php @@ -92,4 +92,8 @@ class NexusWebUserProvider implements UserProvider return true; } + public function rehashPasswordIfRequired(Authenticatable $user, #[\SensitiveParameter] array $credentials, bool $force = false) + { + // TODO: Implement rehashPasswordIfRequired() method. + } } diff --git a/app/Console/Commands/Test.php b/app/Console/Commands/Test.php index 5b84e5fa..edaf3b25 100644 --- a/app/Console/Commands/Test.php +++ b/app/Console/Commands/Test.php @@ -102,7 +102,9 @@ class Test extends Command public function handle() { $str = "1.abc.de"; - $res = explode(".", $str, 2); + $ext = "png"; + $str = "202404/20240403215909f58f38ddd968a0e8a4bdd30690a9e92e.png"; + $res = substr($str, 0,-1*strlen($ext)-1); dd($res); } diff --git a/app/Filament/Resources/System/SettingResource/Pages/EditSetting.php b/app/Filament/Resources/System/SettingResource/Pages/EditSetting.php index 19189288..cd9a120b 100644 --- a/app/Filament/Resources/System/SettingResource/Pages/EditSetting.php +++ b/app/Filament/Resources/System/SettingResource/Pages/EditSetting.php @@ -7,27 +7,21 @@ use App\Filament\Resources\System\SettingResource; use App\Models\HitAndRun; use App\Models\SearchBox; use App\Models\Setting; -use App\Models\Tag; use App\Models\User; -use App\Repositories\MeiliSearchRepository; use Filament\Facades\Filament; -use Filament\Forms\ComponentContainer; -use Filament\Forms\Concerns\InteractsWithForms; -use Filament\Resources\Pages\Concerns\InteractsWithRecord; -use Filament\Resources\Pages\EditRecord; use Filament\Resources\Pages\Page; use Filament\Forms; -use Illuminate\Support\Facades\DB; -use Nexus\Database\NexusDB; class EditSetting extends Page implements Forms\Contracts\HasForms { - use InteractsWithForms, OptionsTrait; + use Forms\Concerns\InteractsWithForms, OptionsTrait; protected static string $resource = SettingResource::class; protected static string $view = 'filament.resources.system.setting-resource.pages.edit-hit-and-run'; + public ?array $data = []; + public function getTitle(): string { return __('label.setting.nav_text'); @@ -39,12 +33,21 @@ class EditSetting extends Page implements Forms\Contracts\HasForms $this->fillForm(); } + public function form(Forms\Form $form): Forms\Form + { + return $form + ->schema($this->getFormSchema()) + ->statePath('data'); + } + private function fillForm() { - $settings = Setting::get(); + $settings = Setting::getFromDb(); $this->form->fill($settings); } + + protected function getFormSchema(): array { return [ @@ -81,7 +84,7 @@ class EditSetting extends Page implements Forms\Contracts\HasForms Setting::query()->upsert($data, ['name'], ['value']); do_action("nexus_setting_update"); clear_setting_cache(); - Filament::notify('success', __('filament::resources/pages/edit-record.messages.saved'), true); + send_admin_success_notification(); } private function getTabs(): array @@ -125,6 +128,13 @@ class EditSetting extends Page implements Forms\Contracts\HasForms ->columns(2) ; + $id = "image_hosting"; + $tabs[] = Forms\Components\Tabs\Tab::make(__("label.setting.$id.tab_header")) + ->id($id) + ->schema($this->getTabImageHostingSchema($id)) + ->columns(2) + ; + $tabs[] = Forms\Components\Tabs\Tab::make(__('label.setting.system.tab_header')) ->id('system') ->schema([ @@ -184,7 +194,7 @@ class EditSetting extends Page implements Forms\Contracts\HasForms return apply_filter("hit_and_run_setting_schema", $default); } - private function getTabMeilisearchSchema($id) + private function getTabMeilisearchSchema($id): array { $schema = []; @@ -215,4 +225,59 @@ class EditSetting extends Page implements Forms\Contracts\HasForms return $schema; } + private function getTabImageHostingSchema($id): array + { + $schema = []; + $name = "$id.driver"; + $schema[] = Forms\Components\Radio::make($name) + ->options(['local' => 'local', 'chevereto' => 'chevereto', 'lsky' => 'lsky']) + ->inline(true) + ->label(__("label.setting.$name")) + ->helperText(__("label.setting.{$name}_help")) + ->columnSpanFull() + ; + + //chevereto + $driverName = "chevereto"; + $driverId = sprintf("%s_%s", $id, $driverName); + $driverSchemas = []; + $field = "upload_api_endpoint"; + $driverSchemas[] = Forms\Components\TextInput::make("$driverId.$field") + ->label(__("label.setting.$id.$field")) + ; + $field = "upload_token"; + $driverSchemas[] = Forms\Components\TextInput::make("$driverId.$field") + ->label(__("label.setting.$id.$field")) + ; + $field = "base_url"; + $driverSchemas[] = Forms\Components\TextInput::make("$driverId.$field") + ->label(__("label.setting.$id.$field")) + ; + + $driverSection = Forms\Components\Section::make($driverName)->schema($driverSchemas); + $schema[] = $driverSection; + + //lsky + $driverName = "lsky"; + $driverId = sprintf("%s_%s", $id, $driverName); + $driverSchemas = []; + $field = "upload_api_endpoint"; + $driverSchemas[] = Forms\Components\TextInput::make("$driverId.$field") + ->label(__("label.setting.$id.$field")) + ; + $field = "upload_token"; + $driverSchemas[] = Forms\Components\TextInput::make("$driverId.$field") + ->label(__("label.setting.$id.$field")) + ; + $field = "base_url"; + $driverSchemas[] = Forms\Components\TextInput::make("$driverId.$field") + ->label(__("label.setting.$id.$field")) + ; + $driverSection = Forms\Components\Section::make($driverName)->schema($driverSchemas); + $schema[] = $driverSection; + + + return $schema; + } + } diff --git a/app/Filament/Resources/User/ExamUserResource.php b/app/Filament/Resources/User/ExamUserResource.php index 5fbe8f45..5e71af94 100644 --- a/app/Filament/Resources/User/ExamUserResource.php +++ b/app/Filament/Resources/User/ExamUserResource.php @@ -100,7 +100,7 @@ class ExamUserResource extends Resource ->actions([ Tables\Actions\ViewAction::make(), ]) - ->prependBulkActions([ + ->groupedBulkActions([ Tables\Actions\BulkAction::make('Avoid')->action(function (Collection $records) { $idArr = $records->pluck('id')->toArray(); $rep = new ExamRepository(); diff --git a/app/Filament/Resources/User/HitAndRunResource.php b/app/Filament/Resources/User/HitAndRunResource.php index b942070f..ae0103df 100644 --- a/app/Filament/Resources/User/HitAndRunResource.php +++ b/app/Filament/Resources/User/HitAndRunResource.php @@ -75,7 +75,7 @@ class HitAndRunResource extends Resource ->actions([ Tables\Actions\ViewAction::make(), ]) - ->prependBulkActions([ + ->groupedBulkActions([ Tables\Actions\BulkAction::make('Pardon')->action(function (Collection $records) { $idArr = $records->pluck('id')->toArray(); $rep = new HitAndRunRepository(); diff --git a/app/Filament/Resources/User/UserResource.php b/app/Filament/Resources/User/UserResource.php index 62c9e7e9..0cda61e8 100644 --- a/app/Filament/Resources/User/UserResource.php +++ b/app/Filament/Resources/User/UserResource.php @@ -7,12 +7,16 @@ use App\Filament\Resources\User\UserResource\Pages; use App\Filament\Resources\User\UserResource\RelationManagers; use App\Models\User; use App\Repositories\UserRepository; +use Filament\Actions\Action; use Filament\Forms; use Filament\Forms\Components\Grid; use Filament\Forms\Form; use Filament\Resources\Resource; use Filament\Tables\Table; use Filament\Tables; +use Filament\Infolists; +use Filament\Infolists\Infolist; +use Filament\Infolists\Components; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\SoftDeletingScope; @@ -32,6 +36,16 @@ class UserResource extends Resource protected static ?int $navigationSort = 1; + private static $rep; + + private static function getRep(): UserRepository + { + if (!self::$rep) { + self::$rep = new UserRepository(); + } + return self::$rep; + } + public static function getNavigationLabel(): string { return __('admin.sidebar.users_list'); @@ -102,6 +116,198 @@ class UserResource extends Resource ->bulkActions(self::getBulkActions()); } + public static function infolist(Infolist $infolist): Infolist + { + return $infolist + ->schema([ + Components\Grid::make(2)->schema([ + Components\Group::make([ + Infolists\Components\TextEntry::make('id')->label("UID"), + Infolists\Components\TextEntry::make('username') + ->label(__("label.user.username")) + ->formatStateUsing(fn ($record) => get_username($record->id, false, true, true, true)) + ->html() + , + Infolists\Components\TextEntry::make('email') + ->label(__("label.email")) + , + Infolists\Components\TextEntry::make('passkey'), + Infolists\Components\TextEntry::make('added')->label(__("label.added")), + Infolists\Components\TextEntry::make('last_access')->label(__("label.last_access")), + Infolists\Components\TextEntry::make('inviter.username')->label(__("label.user.invite_by")), + Infolists\Components\TextEntry::make('parked')->label(__("label.user.parked")), + Infolists\Components\TextEntry::make('offer_allowed_count')->label(__("label.user.offer_allowed_count")), + Infolists\Components\TextEntry::make('seed_points')->label(__("label.user.seed_points")), + Infolists\Components\TextEntry::make('uploadedText')->label(__("label.uploaded")), + Infolists\Components\TextEntry::make('downloadedText')->label(__("label.downloaded")), + Infolists\Components\TextEntry::make('seedbonus')->label(__("label.user.seedbonus")), + ])->columns(2), + Components\Group::make([ + Infolists\Components\TextEntry::make('status') + ->label(__('label.user.status')) + ->badge() + ->colors(['success' => User::STATUS_CONFIRMED, 'warning' => User::STATUS_PENDING]) + ->hintAction(self::buildActionConfirm()) + , + + Infolists\Components\TextEntry::make('classText') + ->label(__("label.user.class")) + ->hintAction(self::buildActionChangeClass()) + , + + Infolists\Components\TextEntry::make('enabled') + ->label(__("label.user.enabled")) + ->badge() + ->colors(['success' => 'yes', 'warning' => 'no']) + ->hintAction(self::buildActionEnableDisable()) + , + Infolists\Components\TextEntry::make('downloadpos') + ->label(__("label.user.downloadpos")) + ->badge() + ->colors(['success' => 'yes', 'warning' => 'no']) + ->hintAction(self::buildActionChangeDownloadPos()) + , + Infolists\Components\TextEntry::make('twoFactorAuthenticationStatus') + ->label(__("label.user.two_step_authentication")) + ->badge() + ->colors(['success' => 'yes', 'warning' => 'no']) + ->hintAction(self::buildActionCancelTwoStepAuthentication()) + , + ]) + ]), + ]); + } + + private static function buildActionChangeClass(): Infolists\Components\Actions\Action + { + return Infolists\Components\Actions\Action::make("changeClass") + ->label(__('label.change')) + ->button() + ->visible(fn (User $record): bool => (Auth::user()->class > $record->class)) + ->form([ + Forms\Components\Select::make('class') + ->options(User::listClass(User::CLASS_PEASANT, Auth::user()->class - 1)) + ->default(fn (User $record) => $record->class) + ->label(__('user.labels.class')) + ->required() + ->reactive() + , + Forms\Components\Radio::make('vip_added') + ->options(self::getYesNoOptions('yes', 'no')) + ->default(fn (User $record) => $record->vip_added) + ->label(__('user.labels.vip_added')) + ->helperText(__('user.labels.vip_added_help')) + ->hidden(fn (\Filament\Forms\Get $get) => $get('class') != User::CLASS_VIP) + , + Forms\Components\DateTimePicker::make('vip_until') + ->default(fn (User $record) => $record->vip_until) + ->label(__('user.labels.vip_until')) + ->helperText(__('user.labels.vip_until_help')) + ->hidden(fn (\Filament\Forms\Get $get) => $get('class') != User::CLASS_VIP) + , + Forms\Components\TextInput::make('reason') + ->label(__('admin.resources.user.actions.enable_disable_reason')) + ->placeholder(__('admin.resources.user.actions.enable_disable_reason_placeholder')) + , + ]) + ->action(function (User $record, array $data) { + $userRep = self::getRep(); + try { + $userRep->changeClass(Auth::user(), $record, $data['class'], $data['reason'], $data); + send_admin_success_notification(); + } catch (\Exception $exception) { + send_admin_fail_notification($exception->getMessage()); + } + }); + } + + private static function buildActionConfirm(): Infolists\Components\Actions\Action + { + return Infolists\Components\Actions\Action::make(__('admin.resources.user.actions.confirm_btn')) + ->modalHeading(__('admin.resources.user.actions.confirm_btn')) + ->requiresConfirmation() + ->visible(fn (User $record): bool => (Auth::user()->class > $record->class)) + ->button() + ->color('success') + ->visible(fn ($record) => $record->status == User::STATUS_PENDING) + ->action(function (User $record) { + if (Auth::user()->class <= $record->class) { + send_admin_fail_notification("No Permission!"); + return; + } + $record->status = User::STATUS_CONFIRMED; + $record->info= null; + $record->save(); + send_admin_success_notification(); + }); + } + + private static function buildActionEnableDisable(): Infolists\Components\Actions\Action + { + return Infolists\Components\Actions\Action::make("changeClass") + ->label(fn (User $record) => $record->enabled == 'yes' ? __('admin.resources.user.actions.disable_modal_btn') : __('admin.resources.user.actions.enable_modal_btn')) + ->modalHeading(fn (User $record) => $record->enabled == 'yes' ? __('admin.resources.user.actions.disable_modal_title') : __('admin.resources.user.actions.enable_modal_title')) + ->button() + ->visible(fn (User $record): bool => (Auth::user()->class > $record->class)) + ->form([ + Forms\Components\TextInput::make('reason')->label(__('admin.resources.user.actions.enable_disable_reason'))->placeholder(__('admin.resources.user.actions.enable_disable_reason_placeholder')), + Forms\Components\Hidden::make('action')->default(fn (User $record) => $record->enabled == 'yes' ? 'disable' : 'enable'), + Forms\Components\Hidden::make('uid')->default(fn (User $record) => $record->id), + ]) + ->action(function (User $record, array $data) { + $userRep = self::getRep(); + try { + if ($data['action'] == 'enable') { + $userRep->enableUser(Auth::user(), $data['uid'], $data['reason']); + } elseif ($data['action'] == 'disable') { + $userRep->disableUser(Auth::user(), $data['uid'], $data['reason']); + } + send_admin_success_notification(); + } catch (\Exception $exception) { + send_admin_fail_notification($exception->getMessage()); + } + }); + } + + private static function buildActionChangeDownloadPos(): Infolists\Components\Actions\Action + { + return Infolists\Components\Actions\Action::make("changeDownloadPos") + ->label(fn (User $record) => $record->downloadpos == 'yes' ? __('admin.resources.user.actions.disable_download_privileges_btn') : __('admin.resources.user.actions.enable_download_privileges_btn')) + ->button() + ->requiresConfirmation() + ->visible(fn (User $record): bool => (Auth::user()->class > $record->class)) + ->action(function (User $record) { + $userRep = self::getRep(); + try { + $userRep->updateDownloadPrivileges(Auth::user(), $record->id, $record->downloadpos == 'yes' ? 'no' : 'yes'); + send_admin_success_notification(); + } catch (\Exception $exception) { + send_admin_fail_notification($exception->getMessage()); + } + }); + + } + + private static function buildActionCancelTwoStepAuthentication(): Infolists\Components\Actions\Action + { + return Infolists\Components\Actions\Action::make("twoStepAuthentication") + ->label(__('admin.resources.user.actions.disable_two_step_authentication')) + ->button() + ->visible(fn (User $record) => $record->two_step_secret != "") + ->modalHeading(__('admin.resources.user.actions.disable_two_step_authentication')) + ->requiresConfirmation() + ->action(function (user $record) { + $userRep = self::getRep(); + try { + $userRep->removeTwoStepAuthentication(Auth::user(), $record->id); + send_admin_success_notification(); + } catch (\Exception $exception) { + send_admin_fail_notification($exception->getMessage()); + } + }); + + } + public static function getRelations(): array { return [ @@ -123,13 +329,13 @@ class UserResource extends Resource public static function getBulkActions(): array { $actions = []; - if (Auth::user()->class >= User::CLASS_SYSOP) { + if (filament()->auth()->user()->class >= User::CLASS_SYSOP) { $actions[] = Tables\Actions\BulkAction::make('confirm') ->label(__('admin.resources.user.actions.confirm_bulk')) ->requiresConfirmation() ->deselectRecordsAfterCompletion() ->action(function (Collection $records) { - $rep = new UserRepository(); + $rep = self::getRep(); $rep->confirmUser($records->pluck('id')->toArray()); }); } diff --git a/app/Filament/Resources/User/UserResource/Pages/UserProfile.php b/app/Filament/Resources/User/UserResource/Pages/UserProfile.php index 2cbe6ab4..c632f404 100644 --- a/app/Filament/Resources/User/UserResource/Pages/UserProfile.php +++ b/app/Filament/Resources/User/UserResource/Pages/UserProfile.php @@ -13,10 +13,11 @@ use App\Repositories\ExamRepository; use App\Repositories\MedalRepository; use App\Repositories\UserRepository; use Carbon\Carbon; +use Filament\Notifications\Notification; use Filament\Resources\Pages\Concerns\HasRelationManagers; use Filament\Resources\Pages\Concerns\InteractsWithRecord; use Filament\Resources\Pages\Page; -use Filament\Pages\Actions; +use Filament\Actions; use Filament\Forms; use Filament\Resources\Pages\ViewRecord; use Illuminate\Support\Facades\Auth; @@ -32,13 +33,7 @@ class UserProfile extends ViewRecord protected static string $resource = UserResource::class; - protected static string $view = 'filament.resources.user.user-resource.pages.user-profile'; - - const EVENT_RECORD_UPDATED = 'recordUpdated'; - - protected $listeners = [ - self::EVENT_RECORD_UPDATED => 'updateRecord' - ]; +// protected static string $view = 'filament.resources.user.user-resource.pages.user-profile'; private function getRep(): UserRepository { @@ -48,11 +43,6 @@ class UserProfile extends ViewRecord return self::$rep; } - public function updateRecord($id) - { - $this->record = $this->resolveRecord($id); - } - protected function getHeaderActions(): array { $actions = []; @@ -61,18 +51,18 @@ class UserProfile extends ViewRecord $actions[] = $this->buildGrantMedalAction(); $actions[] = $this->buildAssignExamAction(); $actions[] = $this->buildChangeBonusEtcAction(); - if ($this->record->two_step_secret) { - $actions[] = $this->buildDisableTwoStepAuthenticationAction(); - } - if ($this->record->status == User::STATUS_PENDING) { - $actions[] = $this->buildConfirmAction(); - } +// if ($this->record->two_step_secret) { +// $actions[] = $this->buildDisableTwoStepAuthenticationAction(); +// } +// if ($this->record->status == User::STATUS_PENDING) { +// $actions[] = $this->buildConfirmAction(); +// } $actions[] = $this->buildResetPasswordAction(); - $actions[] = $this->buildEnableDisableAction(); - $actions[] = $this->buildEnableDisableDownloadPrivilegesAction(); - if (user_can('user-change-class')) { - $actions[] = $this->buildChangeClassAction(); - } +// $actions[] = $this->buildEnableDisableAction(); +// $actions[] = $this->buildEnableDisableDownloadPrivilegesAction(); +// if (user_can('user-change-class')) { +// $actions[] = $this->buildChangeClassAction(); +// } if (user_can('user-delete')) { $actions[] = $this->buildDeleteAction(); } @@ -101,10 +91,9 @@ class UserProfile extends ViewRecord } elseif ($data['action'] == 'disable') { $userRep->disableUser(Auth::user(), $data['uid'], $data['reason']); } - $this->notify('success', 'Success!'); - $this->emitSelf(self::EVENT_RECORD_UPDATED, $data['uid']); + $this->sendSuccessNotification(); } catch (\Exception $exception) { - $this->notify('danger', $exception->getMessage()); + $this->sendFailNotification($exception->getMessage()); } }); } @@ -118,10 +107,9 @@ class UserProfile extends ViewRecord $userRep = $this->getRep(); try { $userRep->removeTwoStepAuthentication(Auth::user(), $this->record->id); - $this->notify('success', 'Success!'); - $this->emitSelf(self::EVENT_RECORD_UPDATED, $this->record->id); + $this->sendSuccessNotification(); } catch (\Exception $exception) { - $this->notify('danger', $exception->getMessage()); + $this->sendFailNotification($exception->getMessage()); } }); } @@ -175,10 +163,9 @@ class UserProfile extends ViewRecord } else { $userRep->incrementDecrement(Auth::user(), $this->record->id, $data['action'], $data['field'], $data['value'], $data['reason']); } - $this->notify('success', 'Success!'); - $this->emitSelf(self::EVENT_RECORD_UPDATED, $this->record->id); + $this->sendSuccessNotification(); } catch (\Exception $exception) { - $this->notify('danger', $exception->getMessage()); + $this->sendFailNotification($exception->getMessage()); } }); } @@ -198,10 +185,9 @@ class UserProfile extends ViewRecord $userRep = $this->getRep(); try { $userRep->resetPassword($this->record->id, $data['password'], $data['password_confirmation']); - $this->notify('success', 'Success!'); - $this->emitSelf(self::EVENT_RECORD_UPDATED, $this->record->id); + $this->sendSuccessNotification(); } catch (\Exception $exception) { - $this->notify('danger', $exception->getMessage()); + $this->sendFailNotification($exception->getMessage()); } }); } @@ -223,10 +209,9 @@ class UserProfile extends ViewRecord $examRep = new ExamRepository(); try { $examRep->assignToUser($this->record->id, $data['exam_id'], $data['begin'], $data['end']); - $this->notify('success', 'Success!'); - $this->emitSelf(self::EVENT_RECORD_UPDATED, $this->record->id); + $this->sendSuccessNotification(); } catch (\Exception $exception) { - $this->notify('danger', $exception->getMessage()); + $this->sendFailNotification($exception->getMessage()); } }); } @@ -251,10 +236,9 @@ class UserProfile extends ViewRecord $medalRep = new MedalRepository(); try { $medalRep->grantToUser($this->record->id, $data['medal_id'], $data['duration']); - $this->notify('success', 'Success!'); - $this->emitSelf(self::EVENT_RECORD_UPDATED, $this->record->id); + $this->sendSuccessNotification(); } catch (\Exception $exception) { - $this->notify('danger', $exception->getMessage()); + $this->sendFailNotification($exception->getMessage()); } }); } @@ -272,8 +256,7 @@ class UserProfile extends ViewRecord $this->record->status = User::STATUS_CONFIRMED; $this->record->info= null; $this->record->save(); - $this->notify('success', 'Success!'); - $this->emitSelf(self::EVENT_RECORD_UPDATED, $this->record->id); + $this->sendSuccessNotification(); }); } @@ -287,10 +270,9 @@ class UserProfile extends ViewRecord $userRep = $this->getRep(); try { $userRep->updateDownloadPrivileges(Auth::user(), $this->record->id, $this->record->downloadpos == 'yes' ? 'no' : 'yes'); - $this->notify('success', 'Success!'); - $this->emitSelf(self::EVENT_RECORD_UPDATED, $this->record->id); + $this->sendSuccessNotification(); } catch (\Exception $exception) { - $this->notify('danger', $exception->getMessage()); + $this->sendFailNotification($exception->getMessage()); } }); } @@ -311,15 +293,14 @@ class UserProfile extends ViewRecord $rep = $this->getRep(); try { $rep->addMeta($this->record, $data, $data); - $this->notify('success', 'Success!'); - $this->emitSelf(self::EVENT_RECORD_UPDATED, $this->record->id); + $this->sendSuccessNotification(); } catch (\Exception $exception) { - $this->notify('danger', $exception->getMessage()); + $this->sendFailNotification($exception->getMessage()); } }); } - private function buildDeleteAction(): Actions\Action + private function buildDeleteAction(): Actions\DeleteAction { return Actions\DeleteAction::make()->using(function () { $this->getRep()->destroy($this->record->id); @@ -345,7 +326,7 @@ class UserProfile extends ViewRecord $props = []; foreach ($metaList as $metaKey => $metas) { $meta = $metas->first(); - $text = sprintf('[%s]', $meta->metaKeyText, ); + $text = sprintf('[%s]', $meta->metaKeyText); if ($meta->meta_key == UserMeta::META_KEY_PERSONALIZED_USERNAME) { $text .= sprintf('(%s)', $meta->getDeadlineText()); } @@ -397,11 +378,28 @@ class UserProfile extends ViewRecord $userRep = $this->getRep(); try { $userRep->changeClass(Auth::user(), $this->record, $data['class'], $data['reason'], $data); - $this->notify('success', 'Success!'); - $this->emitSelf(self::EVENT_RECORD_UPDATED, $this->record->id); + $this->sendSuccessNotification(); } catch (\Exception $exception) { - $this->notify('danger', $exception->getMessage()); + $this->sendFailNotification($exception->getMessage()); } }); } + + private function sendSuccessNotification(string $msg = ""): void + { + Notification::make() + ->success() + ->title($msg ?: "Success!") + ->send() + ; + } + + private function sendFailNotification(string $msg = ""): void + { + Notification::make() + ->danger() + ->title($msg ?: "Fail!") + ->send() + ; + } } diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index 2929a0be..2ff696a8 100644 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -11,6 +11,13 @@ use Illuminate\Routing\Controller as BaseController; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Str; + +/** + * @OA\Info( + * title="NexusPHP API", + * version="1.0" + * ) + */ class Controller extends BaseController { use AuthorizesRequests, DispatchesJobs, ValidatesRequests; diff --git a/app/Models/Claim.php b/app/Models/Claim.php index 3aa02186..31e81a51 100644 --- a/app/Models/Claim.php +++ b/app/Models/Claim.php @@ -66,27 +66,27 @@ class Claim extends NexusModel return Setting::get('torrent.claim_enabled', 'no') == 'yes'; } - public static function getConfigTorrentTTL() + public static function getConfigTorrentTTL(): int { return Setting::get('torrent.claim_torrent_ttl', self::TORRENT_TTL); } - public static function getConfigUserUpLimit() + public static function getConfigUserUpLimit(): int { return Setting::get('torrent.claim_torrent_user_counts_up_limit', self::USER_UP_LIMIT); } - public static function getConfigTorrentUpLimit() + public static function getConfigTorrentUpLimit(): int { return Setting::get('torrent.claim_user_torrent_counts_up_limit', self::TORRENT_UP_LIMIT); } - public static function getConfigRemoveDeductBonus() + public static function getConfigRemoveDeductBonus(): int { return Setting::get('torrent.claim_remove_deduct_user_bonus', self::REMOVE_DEDUCT); } - public static function getConfigGiveUpDeductBonus() + public static function getConfigGiveUpDeductBonus(): int { return Setting::get('torrent.claim_give_up_deduct_user_bonus', self::GIVE_UP_DEDUCT); } diff --git a/app/Models/HitAndRun.php b/app/Models/HitAndRun.php index 0fb43ba8..c253adae 100644 --- a/app/Models/HitAndRun.php +++ b/app/Models/HitAndRun.php @@ -73,7 +73,7 @@ class HitAndRun extends NexusModel return '---'; } $inspectTime = HitAndRun::getConfig('inspect_time', $searchBoxId); - $diffInSeconds = Carbon::now()->diffInSeconds($this->created_at->addHours($inspectTime)); + $diffInSeconds = Carbon::now()->diffInSeconds($this->created_at->addHours(intval($inspectTime))); return mkprettytime($diffInSeconds); } diff --git a/app/Models/User.php b/app/Models/User.php index 1a407062..edf73c9a 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -333,6 +333,11 @@ class User extends Authenticatable implements FilamentUser, HasName ); } + protected function getTwoFactorAuthenticationStatusAttribute(): string + { + return $this->two_step_secret != "" ? "yes" : "no"; + } + public static function getMinSeedPoints($class) { $setting = Setting::get("account.{$class}_min_seed_points"); diff --git a/app/Providers/Filament/AppPanelProvider.php b/app/Providers/Filament/AppPanelProvider.php index dee92891..1e6da45b 100644 --- a/app/Providers/Filament/AppPanelProvider.php +++ b/app/Providers/Filament/AppPanelProvider.php @@ -17,7 +17,11 @@ use Illuminate\Cookie\Middleware\EncryptCookies; use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken; use Illuminate\Routing\Middleware\SubstituteBindings; use Illuminate\Session\Middleware\StartSession; +use Illuminate\Support\Facades\Route; use Illuminate\View\Middleware\ShareErrorsFromSession; +use Filament\Tables\Enums\FiltersLayout; +use Filament\Tables\Table; +use Livewire\Livewire; class AppPanelProvider extends PanelProvider { @@ -26,6 +30,11 @@ class AppPanelProvider extends PanelProvider return $panel ->default() ->id('app') + ->homeUrl("/") + ->sidebarWidth("15rem") + ->topbar(true) + ->sidebarCollapsibleOnDesktop(true) + ->authGuard("nexus-web") ->path('nexusphp') ->login() ->colors([ @@ -34,26 +43,48 @@ class AppPanelProvider extends PanelProvider ->discoverResources(in: app_path('Filament/Resources'), for: 'App\\Filament\\Resources') ->discoverPages(in: app_path('Filament/Pages'), for: 'App\\Filament\\Pages') ->pages([ - Pages\Dashboard::class, +// Pages\Dashboard::class, + \App\Filament\Pages\Dashboard::class, ]) ->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\\Filament\\Widgets') ->widgets([ - Widgets\AccountWidget::class, - Widgets\FilamentInfoWidget::class, +// Widgets\AccountWidget::class, +// Widgets\FilamentInfoWidget::class, ]) ->middleware([ - EncryptCookies::class, +// EncryptCookies::class, + \App\Http\Middleware\EncryptCookies::class, AddQueuedCookiesToResponse::class, StartSession::class, - AuthenticateSession::class, +// AuthenticateSession::class, ShareErrorsFromSession::class, VerifyCsrfToken::class, SubstituteBindings::class, DisableBladeIconComponents::class, DispatchServingFilamentEvent::class, + \App\Http\Middleware\Locale::class, ]) ->authMiddleware([ - Authenticate::class, + \App\Http\Middleware\Filament::class, ]); } -} \ No newline at end of file + + public function boot() + { + Table::configureUsing(function (Table $table): void { + $table + ->filtersLayout(FiltersLayout::AboveContent) + ->paginationPageOptions([10, 25, 50, 100]) + ; + }); + } + + public function register(): void + { + parent::register(); // TODO: Change the autogenerated stub + Livewire::setUpdateRoute(function ($handle) { + return Route::post('/livewire/update', $handle)->middleware('filament'); + }); + } + +} diff --git a/app/Providers/GoogleDriveAdapter.php b/app/Providers/GoogleDriveAdapter.php deleted file mode 100644 index 18b9ab3c..00000000 --- a/app/Providers/GoogleDriveAdapter.php +++ /dev/null @@ -1,11 +0,0 @@ -service; - } -} diff --git a/composer.json b/composer.json index 9e5efb63..a1917595 100644 --- a/composer.json +++ b/composer.json @@ -24,6 +24,7 @@ "ext-curl": "*", "ext-gd": "*", "ext-gmp": "*", + "ext-intl": "*", "ext-json": "*", "ext-mbstring": "*", "ext-mysqli": "*", @@ -31,7 +32,7 @@ "ext-redis": "*", "ext-xml": "*", "ext-zend-opcache": "*", - "doctrine/dbal": "^3.1", + "ext-zip": "*", "elasticsearch/elasticsearch": "^7.16", "filament/filament": "^3.2", "flowframe/laravel-trend": "^0.3", diff --git a/composer.lock b/composer.lock index ff9c7c7c..58cb2d90 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": "e1e1ee1af8a78722edeedb53201ff4f3", + "content-hash": "11ad77956fab261081967fcfef0872f7", "packages": [ { "name": "anourvalar/eloquent-serialize", @@ -13513,13 +13513,15 @@ "ext-curl": "*", "ext-gd": "*", "ext-gmp": "*", + "ext-intl": "*", "ext-json": "*", "ext-mbstring": "*", "ext-mysqli": "*", "ext-pcntl": "*", "ext-redis": "*", "ext-xml": "*", - "ext-zend-opcache": "*" + "ext-zend-opcache": "*", + "ext-zip": "*" }, "platform-dev": [], "plugin-api-version": "2.6.0" diff --git a/config/livewire.php b/config/livewire.php index d108a622..0d2ba89b 100644 --- a/config/livewire.php +++ b/config/livewire.php @@ -3,157 +3,158 @@ return [ /* - |-------------------------------------------------------------------------- + |--------------------------------------------------------------------------- | Class Namespace - |-------------------------------------------------------------------------- + |--------------------------------------------------------------------------- | - | This value sets the root namespace for Livewire component classes in - | your application. This value affects component auto-discovery and - | any Livewire file helper commands, like `artisan make:livewire`. - | - | After changing this item, run: `php artisan livewire:discover`. + | This value sets the root class namespace for Livewire component classes in + | your application. This value will change where component auto-discovery + | finds components. It's also referenced by the file creation commands. | */ - 'class_namespace' => 'App\\Http\\Livewire', + 'class_namespace' => 'App\\Livewire', /* - |-------------------------------------------------------------------------- + |--------------------------------------------------------------------------- | View Path - |-------------------------------------------------------------------------- + |--------------------------------------------------------------------------- | - | This value sets the path for Livewire component views. This affects - | file manipulation helper commands like `artisan make:livewire`. + | This value is used to specify where Livewire component Blade templates are + | stored when running file creation commands like `artisan make:livewire`. + | It is also used if you choose to omit a component's render() method. | */ 'view_path' => resource_path('views/livewire'), /* - |-------------------------------------------------------------------------- + |--------------------------------------------------------------------------- | Layout - |-------------------------------------------------------------------------- - | The default layout view that will be used when rendering a component via - | Route::get('/some-endpoint', SomeComponent::class);. In this case the - | the view returned by SomeComponent will be wrapped in "layouts.app" + |--------------------------------------------------------------------------- + | The view that will be used as the layout when rendering a single component + | as an entire page via `Route::get('/post/create', CreatePost::class);`. + | In this case, the view returned by CreatePost will render into $slot. | */ - 'layout' => 'layouts.app', + 'layout' => 'components.layouts.app', /* - |-------------------------------------------------------------------------- - | Livewire Assets URL - |-------------------------------------------------------------------------- - | - | This value sets the path to Livewire JavaScript assets, for cases where - | your app's domain root is not the correct path. By default, Livewire - | will load its JavaScript assets from the app's "relative root". - | - | Examples: "/assets", "myurl.com/app". + |--------------------------------------------------------------------------- + | Lazy Loading Placeholder + |--------------------------------------------------------------------------- + | Livewire allows you to lazy load components that would otherwise slow down + | the initial page load. Every component can have a custom placeholder or + | you can define the default placeholder view for all components below. | */ - 'asset_url' => null, + 'lazy_placeholder' => null, /* - |-------------------------------------------------------------------------- - | Livewire App URL - |-------------------------------------------------------------------------- - | - | This value should be used if livewire assets are served from CDN. - | Livewire will communicate with an app through this url. - | - | Examples: "https://my-app.com", "myurl.com/app". - | - */ - - 'app_url' => null, - - /* - |-------------------------------------------------------------------------- - | Livewire Endpoint Middleware Group - |-------------------------------------------------------------------------- - | - | This value sets the middleware group that will be applied to the main - | Livewire "message" endpoint (the endpoint that gets hit everytime - | a Livewire component updates). It is set to "web" by default. - | - */ - -// 'middleware_group' => 'web', - 'middleware_group' => 'filament', - - /* - |-------------------------------------------------------------------------- - | Livewire Temporary File Uploads Endpoint Configuration - |-------------------------------------------------------------------------- + |--------------------------------------------------------------------------- + | Temporary File Uploads + |--------------------------------------------------------------------------- | | Livewire handles file uploads by storing uploads in a temporary directory - | before the file is validated and stored permanently. All file uploads - | are directed to a global endpoint for temporary storage. The config - | items below are used for customizing the way the endpoint works. + | before the file is stored permanently. All file uploads are directed to + | a global endpoint for temporary storage. You may configure this below: | */ 'temporary_file_upload' => [ - 'disk' => null, // Example: 'local', 's3' Default: 'default' - 'rules' => null, // Example: ['file', 'mimes:png,jpg'] Default: ['required', 'file', 'max:12288'] (12MB) - 'directory' => null, // Example: 'tmp' Default 'livewire-tmp' - 'middleware' => null, // Example: 'throttle:5,1' Default: 'throttle:60,1' - 'preview_mimes' => [ // Supported file types for temporary pre-signed file URLs. + 'disk' => null, // Example: 'local', 's3' | Default: 'default' + 'rules' => null, // Example: ['file', 'mimes:png,jpg'] | Default: ['required', 'file', 'max:12288'] (12MB) + 'directory' => null, // Example: 'tmp' | Default: 'livewire-tmp' + 'middleware' => null, // Example: 'throttle:5,1' | Default: 'throttle:60,1' + 'preview_mimes' => [ // Supported file types for temporary pre-signed file URLs... 'png', 'gif', 'bmp', 'svg', 'wav', 'mp4', 'mov', 'avi', 'wmv', 'mp3', 'm4a', 'jpg', 'jpeg', 'mpga', 'webp', 'wma', ], - 'max_upload_time' => 5, // Max duration (in minutes) before an upload gets invalidated. + 'max_upload_time' => 5, // Max duration (in minutes) before an upload is invalidated... + 'cleanup' => true, // Should cleanup temporary uploads older than 24 hrs... ], /* - |-------------------------------------------------------------------------- - | Manifest File Path - |-------------------------------------------------------------------------- - | - | This value sets the path to the Livewire manifest file. - | The default should work for most cases (which is - | "/bootstrap/cache/livewire-components.php"), but for specific - | cases like when hosting on Laravel Vapor, it could be set to a different value. - | - | Example: for Laravel Vapor, it would be "/tmp/storage/bootstrap/cache/livewire-components.php". - | - */ - - 'manifest_path' => null, - - /* - |-------------------------------------------------------------------------- - | Back Button Cache - |-------------------------------------------------------------------------- - | - | This value determines whether the back button cache will be used on pages - | that contain Livewire. By disabling back button cache, it ensures that - | the back button shows the correct state of components, instead of - | potentially stale, cached data. - | - | Setting it to "false" (default) will disable back button cache. - | - */ - - 'back_button_cache' => false, - - /* - |-------------------------------------------------------------------------- + |--------------------------------------------------------------------------- | Render On Redirect - |-------------------------------------------------------------------------- + |--------------------------------------------------------------------------- | - | This value determines whether Livewire will render before it's redirected - | or not. Setting it to "false" (default) will mean the render method is - | skipped when redirecting. And "true" will mean the render method is - | run before redirecting. Browsers bfcache can store a potentially - | stale view if render is skipped on redirect. + | This value determines if Livewire will run a component's `render()` method + | after a redirect has been triggered using something like `redirect(...)` + | Setting this to true will render the view once more before redirecting | */ 'render_on_redirect' => false, + /* + |--------------------------------------------------------------------------- + | Eloquent Model Binding + |--------------------------------------------------------------------------- + | + | Previous versions of Livewire supported binding directly to eloquent model + | properties using wire:model by default. However, this behavior has been + | deemed too "magical" and has therefore been put under a feature flag. + | + */ + + 'legacy_model_binding' => false, + + /* + |--------------------------------------------------------------------------- + | Auto-inject Frontend Assets + |--------------------------------------------------------------------------- + | + | By default, Livewire automatically injects its JavaScript and CSS into the + | and of pages containing Livewire components. By disabling + | this behavior, you need to use @livewireStyles and @livewireScripts. + | + */ + + 'inject_assets' => true, + + /* + |--------------------------------------------------------------------------- + | Navigate (SPA mode) + |--------------------------------------------------------------------------- + | + | By adding `wire:navigate` to links in your Livewire application, Livewire + | will prevent the default link handling and instead request those pages + | via AJAX, creating an SPA-like effect. Configure this behavior here. + | + */ + + 'navigate' => [ + 'show_progress_bar' => true, + 'progress_bar_color' => '#2299dd', + ], + + /* + |--------------------------------------------------------------------------- + | HTML Morph Markers + |--------------------------------------------------------------------------- + | + | Livewire intelligently "morphs" existing HTML into the newly rendered HTML + | after each update. To make this process more reliable, Livewire injects + | "markers" into the rendered Blade surrounding @if, @class & @foreach. + | + */ + + 'inject_morph_markers' => true, + + /* + |--------------------------------------------------------------------------- + | Pagination Theme + |--------------------------------------------------------------------------- + | + | When enabling Livewire's pagination feature by using the `WithPagination` + | trait, Livewire will use Tailwind templates to render pagination views + | on the page. If you want Bootstrap CSS, you can specify: "bootstrap" + | + */ + + 'pagination_theme' => 'tailwind', ]; diff --git a/database/migrations/2024_12_29_202156_add_driver_to_attachments_table.php b/database/migrations/2024_12_29_202156_add_driver_to_attachments_table.php new file mode 100644 index 00000000..0b8c5c4b --- /dev/null +++ b/database/migrations/2024_12_29_202156_add_driver_to_attachments_table.php @@ -0,0 +1,28 @@ +string('driver')->default("local"); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('attachments', function (Blueprint $table) { + $table->dropColumn('driver'); + }); + } +}; diff --git a/include/constants.php b/include/constants.php index dd1c2173..4647384e 100644 --- a/include/constants.php +++ b/include/constants.php @@ -1,6 +1,6 @@ getImageUrl($row['location']); + do_log(sprintf("driver: %s, location: %s, url: %s", $row['driver'], $row['location'], $url)); if($imageresizer == true) - $onclick = " onclick=\"Previewurl('".$httpdirectory_attachment."/".$row['location']."')\""; + $onclick = " onclick=\"Previewurl('".$url."')\""; else $onclick = ""; - $return = "\"".htmlspecialchars($row['filename'])."\"".$lang_functions['text_size'].": ".mksize($row['filesize'])."
".gettime($row['added']))."', 'styleClass', 'attach', 'x', findPosition(this)[0], 'y', findPosition(this)[1]-58);\" />"; + $return = "\"".htmlspecialchars($row['filename'])."\"".$lang_functions['text_size'].": ".mksize($row['filesize'])."
".gettime($row['added']))."', 'styleClass', 'attach', 'x', findPosition(this)[0], 'y', findPosition(this)[1]-58);\" />"; } else $return = ""; } @@ -345,13 +348,6 @@ function format_comment($text, $strip_html = true, $xssclean = false, $newtab = $replaceXhtmlTagArray = array("", "", "", ""); $s = preg_replace($originalBbTagArray, $replaceXhtmlTagArray, $s); - if ($enableattach_attachment == 'yes' && $imagenum != 1){ - $limit = 20; -// $s = preg_replace("/\[attach\]([0-9a-zA-z][0-9a-zA-z]*)\[\/attach\]/ies", "print_attachment('\\1', ".($enableimage ? 1 : 0).", ".($imageresizer ? 1 : 0).")", $s, $limit); - $s = preg_replace_callback("/\[attach\]([0-9a-zA-z][0-9a-zA-z]*)\[\/attach\]/is", function ($matches) use ($enableimage, $imageresizer) { - return print_attachment($matches[1], ".($enableimage ? 1 : 0).", ".($imageresizer ? 1 : 0)."); - }, $s, $limit); - } if ($enableimage) { // $s = preg_replace("/\[img\]([^\<\r\n\"']+?)\[\/img\]/ei", "formatImg('\\1',".$imageresizer.",".$image_max_width.",".$image_max_height.")", $s, $imagenum, $imgReplaceCount); @@ -451,6 +447,14 @@ function format_comment($text, $strip_html = true, $xssclean = false, $newtab = }, $s); } + if ($enableattach_attachment == 'yes' && $imagenum != 1){ + $limit = 20; +// $s = preg_replace("/\[attach\]([0-9a-zA-z][0-9a-zA-z]*)\[\/attach\]/ies", "print_attachment('\\1', ".($enableimage ? 1 : 0).", ".($imageresizer ? 1 : 0).")", $s, $limit); + $s = preg_replace_callback("/\[attach\]([0-9a-zA-z][0-9a-zA-z]*)\[\/attach\]/is", function ($matches) use ($enableimage, $imageresizer) { + return print_attachment($matches[1], ".($enableimage ? 1 : 0).", ".($imageresizer ? 1 : 0)."); + }, $s, $limit); + } + reset($tempCode); $j = $i = 0; while(count($tempCode) || $j > 5) { @@ -5626,7 +5630,8 @@ function format_description($description) if ($attachments->isNotEmpty()) { $description = preg_replace_callback($pattern, function ($matches) use ($attachments) { $item = $attachments->get($matches[2]); - $url = attachmentUrl($item->location); + $url = \Nexus\Attachment\Storage::getDriver($item->driver)->getImageUrl($item->location); + do_log(sprintf("location: %s, driver: %s, url: %s", $item->location, $item->driver, $url)); return str_replace($matches[2], $url, $matches[1]); }, $description); } diff --git a/include/globalfunctions.php b/include/globalfunctions.php index 290ab2a8..61046eff 100644 --- a/include/globalfunctions.php +++ b/include/globalfunctions.php @@ -1337,3 +1337,11 @@ function get_user_locale(int $uid): string } return \App\Http\Middleware\Locale::$languageMaps[$result[0]['site_lang_folder']] ?? 'en'; } + +function send_admin_success_notification(string $msg = ""): void { + \Filament\Notifications\Notification::make()->success()->title($msg ?: "Success!")->send(); +} + +function send_admin_fail_notification(string $msg = ""): void { + \Filament\Notifications\Notification::make()->danger()->title($msg ?: "Fail!")->send(); +} diff --git a/nexus/Attachment/Drivers/Chevereto.php b/nexus/Attachment/Drivers/Chevereto.php new file mode 100644 index 00000000..146cdbe5 --- /dev/null +++ b/nexus/Attachment/Drivers/Chevereto.php @@ -0,0 +1,55 @@ +request('POST', $api, [ + 'headers' => [ + 'X-API-Key' => sprintf('%s', $token), + ], + 'multipart' => [ + [ + 'name' => 'source', + 'contents' => Psr7\Utils::tryFopen($filepath, 'r') + ] + ] + ]); + $statusCode = $response->getStatusCode(); + $logPrefix .= ", status code: $statusCode"; + if ($statusCode != 200) { + do_log("$logPrefix, statusCode != 200", "error"); + throw new \Exception("Unable to upload file, status code {$statusCode}"); + } + $stringBody = (string)$response->getBody(); + $logPrefix .= ", body: $stringBody"; + $result = json_decode($stringBody, true); + if (!is_array($result)) { + do_log("$logPrefix, can not parse to array", "error"); + throw new \Exception("Unable to parse response body"); + } + if (!isset($result["image"]["url"])) { + do_log("$logPrefix, no image url", "error"); + throw new \Exception("upload fail: " . ($result["error"]["message"] ?? "")); + } + return $result["image"]["url"]; + } + + function getBaseUrl(): string + { + return get_setting("image_hosting_chevereto.base_url"); + } + + function getDriverName(): string + { + return static::DRIVER_CHEVERETO; + } +} diff --git a/nexus/Attachment/Drivers/Local.php b/nexus/Attachment/Drivers/Local.php new file mode 100644 index 00000000..34a06279 --- /dev/null +++ b/nexus/Attachment/Drivers/Local.php @@ -0,0 +1,22 @@ +request('POST', $api, [ + 'headers' => [ + 'Authorization' => sprintf('Bearer %s', $token), + ], + 'multipart' => [ + [ + 'name' => 'file', + 'contents' => Psr7\Utils::tryFopen($filepath, 'r') + ] + ] + ]); + $statusCode = $response->getStatusCode(); + $logPrefix .= ", status code: $statusCode"; + if ($statusCode != 200) { + do_log("$logPrefix, statusCode != 200", "error"); + throw new \Exception("Unable to upload file, status code {$statusCode}"); + } + $stringBody = (string)$response->getBody(); + $logPrefix .= ", body: $stringBody"; + $result = json_decode($stringBody, true); + if (!is_array($result)) { + do_log("$logPrefix, can not parse to array", "error"); + throw new \Exception("Unable to parse response body"); + } + if (!isset($result["status"])) { + do_log("$logPrefix, no status", "error"); + throw new \Exception("Unable to parse response body, no status"); + } + if ($result["status"] !== true) { + do_log("$logPrefix, status != true", "error"); + throw new \Exception("upload fail: " . $result["message"]); + } + if (!isset($result["data"]["links"]["url"])) { + do_log("$logPrefix, no links url", "error"); + throw new \Exception("upload fail: no links url"); + } + + return $result["data"]["links"]["url"]; + } + + function getBaseUrl(): string + { + return get_setting("image_hosting_lsky.base_url"); + } + + function getDriverName(): string + { + return static::DRIVER_LSKY; + } +} diff --git a/nexus/Attachment/Storage.php b/nexus/Attachment/Storage.php new file mode 100644 index 00000000..ab8d4889 --- /dev/null +++ b/nexus/Attachment/Storage.php @@ -0,0 +1,65 @@ +upload($filepath); + return $this->trimBaseUrl($url); + } + + public function getImageUrl(string $location): string + { + return sprintf('%s/%s', trim($this->getBaseUrl(), '/'), trim($location, '/')); + } + + protected function trimBaseUrl(string $url): string + { + $baseUrl = trim($this->getBaseUrl(), '/') . "/"; + if (str_starts_with($url, $baseUrl)) { + return substr($url, strlen($baseUrl)); + } + return $url; + } + + public static function getDriver(string $name = null): Storage + { + $driver = $name ?: get_setting("image_hosting.driver"); + if (isset(self::$drivers[$driver])) { + return self::$drivers[$driver]; + } + $result = null; + if ($driver == self::DRIVER_CHEVERETO) { + $result = new Chevereto(); + } else if ($driver == self::DRIVER_LSKY) { + $result = new Lsky(); + } else if ($driver == self::DRIVER_LOCAL) { + $result = new Local(); + } + if ($result) { + return self::$drivers[$driver] = $result; + } + throw new \Exception("Unsupported driver: $driver"); + } +} diff --git a/nexus/Database/NexusDB.php b/nexus/Database/NexusDB.php index f709a732..36987961 100644 --- a/nexus/Database/NexusDB.php +++ b/nexus/Database/NexusDB.php @@ -260,7 +260,6 @@ class NexusDB $capsule->bootEloquent(); $connection = self::$eloquentConnection = $capsule->getConnection($connectionName); $connection->enableQueryLog(); - $connection->getDoctrineSchemaManager()->getDatabasePlatform()->registerDoctrineTypeMapping('enum', 'string'); } private static function schema(): \Illuminate\Database\Schema\Builder diff --git a/public/attachment.php b/public/attachment.php index a2290dfb..dd067287 100644 --- a/public/attachment.php +++ b/public/attachment.php @@ -58,183 +58,200 @@ if ($Attach->enable_attachment()) if (in_array($ext, $img_ext)) $isimage = true; else $isimage = false; - if ($savedirectorytype_attachment == 'onedir') - $savepath = ""; - elseif ($savedirectorytype_attachment == 'monthdir') - $savepath = date("Ym")."/"; - elseif ($savedirectorytype_attachment == 'daydir') - $savepath = date("Ymd")."/"; - $filemd5 = md5_file($file['tmp_name']); - $filename = date("YmdHis").$filemd5; - $file_location = make_folder($savedirectory_attachment."/", $savepath) . $filename; - do_log("file_location: $file_location"); - $db_file_location = $savepath.$filename; - $abandonorig = false; - $hasthumb = false; - $width = 0; - if ($isimage) //the uploaded file is a image - { - $maycreatethumb = false; - $stop = false; - $imagesize = getimagesize($file['tmp_name']); - if ($imagesize){ - $height = $imagesize[1]; - $width = $imagesize[0]; - $it = $imagesize[2]; - if ($it != 1 || !$Attach->is_gif_ani($file['tmp_name'])){ //if it is an animation GIF, stop creating thumbnail and adding watermark - if ($thumbnailtype_attachment != 'no') //create thumbnail for big image - { - //determine the size of thumbnail - if ($altsize == 'yes'){ - $targetwidth = $altthumbwidth_attachment; - $targetheight = $altthumbheight_attachment; - } - else - { - $targetwidth = $thumbwidth_attachment; - $targetheight = $thumbheight_attachment; - } - $hscale=$height/$targetheight; - $wscale=$width/$targetwidth; - $scale=($hscale < 1 && $wscale < 1) ? 1 : (( $hscale > $wscale) ? $hscale : $wscale); - $newwidth=floor($width/$scale); - $newheight=floor($height/$scale); - if ($scale != 1){ //thumbnail is needed - if ($it==1) - $orig=@imagecreatefromgif($file["tmp_name"]); - elseif ($it == 2) - $orig=@imagecreatefromjpeg($file["tmp_name"]); - else - $orig=@imagecreatefrompng($file["tmp_name"]); - if ($orig && !$stop) - { - $thumb = imagecreatetruecolor($newwidth, $newheight); - imagecopyresampled($thumb, $orig, 0, 0, 0, 0, $newwidth, $newheight, $width, $height); - if ($thumbnailtype_attachment == 'createthumb'){ - $hasthumb = true; - imagejpeg($thumb, $file_location.".".$ext.".thumb.jpg", $thumbquality_attachment); - } - elseif ($thumbnailtype_attachment == 'resizebigimg'){ - $ext = "jpg"; - $filetype = "image/jpeg"; - $it = 2; - $height = $newheight; - $width = $newwidth; - $maycreatethumb = true; - $abandonorig = true; - } - } - } - } - $watermarkpos = $watermarkpos_attachment; - if ($watermarkpos != 'no' && !$stop) //add watermark to image - { - if ($width > $watermarkwidth_attachment && $height > $watermarkheight_attachment) - { - if ($abandonorig) - { - $resource = $thumb; - } - else - { - $resource=imagecreatetruecolor($width,$height); - if ($it==1) - $resource_p=@imagecreatefromgif($file["tmp_name"]); - elseif ($it==2) - $resource_p=@imagecreatefromjpeg($file["tmp_name"]); - else - $resource_p=@imagecreatefrompng($file["tmp_name"]); - imagecopy($resource, $resource_p, 0, 0, 0, 0, $width, $height); - } - $watermark = imagecreatefrompng('pic/watermark.png'); - $watermark_width = imagesx($watermark); - $watermark_height = imagesy($watermark); - //the position of the watermark - if ($watermarkpos == 'random') - $watermarkpos = mt_rand(1, 9); - switch ($watermarkpos) - { - case 1: { - $wmx = 5; - $wmy = 5; - break; - } - case 2: { - $wmx = ($width-$watermark_width)/2; - $wmy = 5; - break; - } - case 3: { - $wmx = $width-$watermark_width-5; - $wmy = 5; - break; - } - case 4: { - $wmx = 5; - $wmy = ($height-$watermark_height)/2; - break; - } - case 5: { - $wmx = ($width-$watermark_width)/2; - $wmy = ($height-$watermark_height)/2; - break; - } - case 6: { - $wmx = $width-$watermark_width-5; - $wmy = ($height-$watermark_height)/2; - break; - } - case 7: { - $wmx = 5; - $wmy = $height-$watermark_height-5; - break; - } - case 8: { - $wmx = ($width-$watermark_width)/2; - $wmy = $height-$watermark_height-5; - break; - } - case 9: { - $wmx = $width-$watermark_width-5; - $wmy = $height-$watermark_height-5; - break; - } - } + $width = $height = 0; + $imagesize = []; + if ($isimage) { + $imagesize = getimagesize($file['tmp_name']); + $height = $imagesize[1]; + $width = $imagesize[0]; + } + //get driver + $storageDriver = get_setting("image_hosting.driver", "local"); + if ($storageDriver == "local" || !$isimage) { + if ($savedirectorytype_attachment == 'onedir') + $savepath = ""; + elseif ($savedirectorytype_attachment == 'monthdir') + $savepath = date("Ym")."/"; + elseif ($savedirectorytype_attachment == 'daydir') + $savepath = date("Ymd")."/"; + $filemd5 = md5_file($file['tmp_name']); + $filename = date("YmdHis").$filemd5; + $file_location = make_folder($savedirectory_attachment."/", $savepath) . $filename; + do_log("file_location: $file_location"); + $db_file_location = $savepath.$filename; + $abandonorig = false; + $hasthumb = false; + if ($isimage) //the uploaded file is a image + { + $maycreatethumb = false; + $stop = false; + if ($imagesize){ + $it = $imagesize[2]; + if ($it != 1 || !$Attach->is_gif_ani($file['tmp_name'])){ //if it is an animation GIF, stop creating thumbnail and adding watermark + if ($thumbnailtype_attachment != 'no') //create thumbnail for big image + { + //determine the size of thumbnail + if ($altsize == 'yes'){ + $targetwidth = $altthumbwidth_attachment; + $targetheight = $altthumbheight_attachment; + } + else + { + $targetwidth = $thumbwidth_attachment; + $targetheight = $thumbheight_attachment; + } + $hscale=$height/$targetheight; + $wscale=$width/$targetwidth; + $scale=($hscale < 1 && $wscale < 1) ? 1 : (( $hscale > $wscale) ? $hscale : $wscale); + $newwidth=floor($width/$scale); + $newheight=floor($height/$scale); + if ($scale != 1){ //thumbnail is needed + if ($it==1) + $orig=@imagecreatefromgif($file["tmp_name"]); + elseif ($it == 2) + $orig=@imagecreatefromjpeg($file["tmp_name"]); + else + $orig=@imagecreatefrompng($file["tmp_name"]); + if ($orig && !$stop) + { + $thumb = imagecreatetruecolor($newwidth, $newheight); + imagecopyresampled($thumb, $orig, 0, 0, 0, 0, $newwidth, $newheight, $width, $height); + if ($thumbnailtype_attachment == 'createthumb'){ + $hasthumb = true; + imagejpeg($thumb, $file_location.".".$ext.".thumb.jpg", $thumbquality_attachment); + } + elseif ($thumbnailtype_attachment == 'resizebigimg'){ + $ext = "jpg"; + $filetype = "image/jpeg"; + $it = 2; + $height = $newheight; + $width = $newwidth; + $maycreatethumb = true; + $abandonorig = true; + } + } + } + } + $watermarkpos = $watermarkpos_attachment; + if ($watermarkpos != 'no' && !$stop) //add watermark to image + { + if ($width > $watermarkwidth_attachment && $height > $watermarkheight_attachment) + { + if ($abandonorig) + { + $resource = $thumb; + } + else + { + $resource=imagecreatetruecolor($width,$height); + if ($it==1) + $resource_p=@imagecreatefromgif($file["tmp_name"]); + elseif ($it==2) + $resource_p=@imagecreatefromjpeg($file["tmp_name"]); + else + $resource_p=@imagecreatefrompng($file["tmp_name"]); + imagecopy($resource, $resource_p, 0, 0, 0, 0, $width, $height); + } + $watermark = imagecreatefrompng('pic/watermark.png'); + $watermark_width = imagesx($watermark); + $watermark_height = imagesy($watermark); + //the position of the watermark + if ($watermarkpos == 'random') + $watermarkpos = mt_rand(1, 9); + switch ($watermarkpos) + { + case 1: { + $wmx = 5; + $wmy = 5; + break; + } + case 2: { + $wmx = ($width-$watermark_width)/2; + $wmy = 5; + break; + } + case 3: { + $wmx = $width-$watermark_width-5; + $wmy = 5; + break; + } + case 4: { + $wmx = 5; + $wmy = ($height-$watermark_height)/2; + break; + } + case 5: { + $wmx = ($width-$watermark_width)/2; + $wmy = ($height-$watermark_height)/2; + break; + } + case 6: { + $wmx = $width-$watermark_width-5; + $wmy = ($height-$watermark_height)/2; + break; + } + case 7: { + $wmx = 5; + $wmy = $height-$watermark_height-5; + break; + } + case 8: { + $wmx = ($width-$watermark_width)/2; + $wmy = $height-$watermark_height-5; + break; + } + case 9: { + $wmx = $width-$watermark_width-5; + $wmy = $height-$watermark_height-5; + break; + } + } - imagecopy($resource, $watermark, $wmx, $wmy, 0, 0, $watermark_width, $watermark_height); - if ($it==1) - imagegif($resource, $file_location.".".$ext); - elseif ($it==2) - imagejpeg($resource, $file_location.".".$ext, $watermarkquality_attachment); - else - imagepng($resource, $file_location.".".$ext); - $filesize = filesize($file_location.".".$ext); - $maycreatethumb = false; - $abandonorig = true; - } - } - if ($maycreatethumb){ // if no watermark is added, create the thumbnail now for the above resized image. - imagejpeg($thumb, $file_location.".".$ext, $thumbquality_attachment); - $filesize = filesize($file_location.".".$ext); - } - } - } - else $warning = $lang_attachment['text_invalid_image_file']; - } - if (!$abandonorig){ - if(!move_uploaded_file($file["tmp_name"], $file_location.".".$ext)) - $warning = $lang_attachment['text_cannot_move_file']; - } + imagecopy($resource, $watermark, $wmx, $wmy, 0, 0, $watermark_width, $watermark_height); + if ($it==1) + imagegif($resource, $file_location.".".$ext); + elseif ($it==2) + imagejpeg($resource, $file_location.".".$ext, $watermarkquality_attachment); + else + imagepng($resource, $file_location.".".$ext); + $filesize = filesize($file_location.".".$ext); + $maycreatethumb = false; + $abandonorig = true; + } + } + if ($maycreatethumb){ // if no watermark is added, create the thumbnail now for the above resized image. + imagejpeg($thumb, $file_location.".".$ext, $thumbquality_attachment); + $filesize = filesize($file_location.".".$ext); + } + } + } + else $warning = $lang_attachment['text_invalid_image_file']; + } + if (!$abandonorig){ + if(!move_uploaded_file($file["tmp_name"], $file_location.".".$ext)) + $warning = $lang_attachment['text_cannot_move_file']; + } + $url = $httpdirectory_attachment."/".$db_file_location . ".$ext"; + if ($hasthumb) { + $url .= ".thumb.jpg"; + } + } else { + try { + $driver = \Nexus\Attachment\Storage::getDriver(); + $location = $driver->uploadGetLocation($file["tmp_name"]); + $db_file_location = substr($location, 0, -1*strlen($ext)-1); + $url = $driver->getImageUrl($location); + } catch (\Exception $exception) { + do_log("upload failed: " . $exception->getMessage() . $exception->getTraceAsString(), 'error'); + $warning = $exception->getMessage(); + } + } if (!$warning) //insert into database and add code to editor { $dlkey = md5($db_file_location.".".$ext); - sql_query("INSERT INTO attachments (userid, width, added, filename, filetype, filesize, location, dlkey, isimage, thumb) VALUES (".$CURUSER['id'].", ".$width.", ".sqlesc(date("Y-m-d H:i:s")).", ".sqlesc($origfilename).", ".sqlesc($filetype).", ".$filesize.", ".sqlesc($db_file_location.".".$ext).", ".sqlesc($dlkey).", ".($isimage ? 1 : 0).", ".($hasthumb ? 1 : 0).")") or sqlerr(__FILE__, __LINE__); + sql_query("INSERT INTO attachments (userid, width, added, filename, filetype, filesize, location, dlkey, isimage, thumb, driver) VALUES (".$CURUSER['id'].", ".$width.", ".sqlesc(date("Y-m-d H:i:s")).", ".sqlesc($origfilename).", ".sqlesc($filetype).", ".$filesize.", ".sqlesc($db_file_location.".".$ext).", ".sqlesc($dlkey).", ".($isimage ? 1 : 0).", ".($hasthumb ? 1 : 0). "," .sqlesc($storageDriver) . ")") or sqlerr(__FILE__, __LINE__); $count_left--; if (!empty($_REQUEST['callback_func']) && preg_match('/^preview_custom_field_image_\d+$/', $_REQUEST['callback_func'])) { - $url = $httpdirectory_attachment."/".$db_file_location . ".$ext"; - if ($hasthumb) { - $url .= ".thumb.jpg"; - } echo sprintf('', $_REQUEST['callback_func'], $dlkey, $url); } else { echo(""); diff --git a/public/userdetails.php b/public/userdetails.php index 830e2a44..a9a78737 100644 --- a/public/userdetails.php +++ b/public/userdetails.php @@ -120,7 +120,7 @@ if ($CURUSER['id'] == $user['id'] || user_can('cruprfmanage')) %s', $userManageSystemUrl, $lang_functions['text_management_system']); $migratedHelp = sprintf($lang_userdetails['change_field_value_migrated'], $userManageSystemText); if (user_can('prfmanage') && $user["class"] < get_user_class()) { diff --git a/public/vendor/livewire/livewire.esm.js b/public/vendor/livewire/livewire.esm.js new file mode 100644 index 00000000..2d7ef479 --- /dev/null +++ b/public/vendor/livewire/livewire.esm.js @@ -0,0 +1,10926 @@ +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __commonJS = (cb, mod) => function __require() { + return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod)); + +// ../alpine/packages/alpinejs/dist/module.cjs.js +var require_module_cjs = __commonJS({ + "../alpine/packages/alpinejs/dist/module.cjs.js"(exports, module) { + var __create2 = Object.create; + var __defProp2 = Object.defineProperty; + var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor; + var __getOwnPropNames2 = Object.getOwnPropertyNames; + var __getProtoOf2 = Object.getPrototypeOf; + var __hasOwnProp2 = Object.prototype.hasOwnProperty; + var __commonJS2 = (cb, mod) => function __require() { + return mod || (0, cb[__getOwnPropNames2(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; + }; + var __export = (target, all2) => { + for (var name in all2) + __defProp2(target, name, { get: all2[name], enumerable: true }); + }; + var __copyProps2 = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames2(from)) + if (!__hasOwnProp2.call(to, key) && key !== except) + __defProp2(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable }); + } + return to; + }; + var __toESM2 = (mod, isNodeMode, target) => (target = mod != null ? __create2(__getProtoOf2(mod)) : {}, __copyProps2(isNodeMode || !mod || !mod.__esModule ? __defProp2(target, "default", { value: mod, enumerable: true }) : target, mod)); + var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod); + var require_shared_cjs = __commonJS2({ + "node_modules/@vue/shared/dist/shared.cjs.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + function makeMap(str, expectsLowerCase) { + const map = /* @__PURE__ */ Object.create(null); + const list = str.split(","); + for (let i = 0; i < list.length; i++) { + map[list[i]] = true; + } + return expectsLowerCase ? (val) => !!map[val.toLowerCase()] : (val) => !!map[val]; + } + var PatchFlagNames = { + [1]: `TEXT`, + [2]: `CLASS`, + [4]: `STYLE`, + [8]: `PROPS`, + [16]: `FULL_PROPS`, + [32]: `HYDRATE_EVENTS`, + [64]: `STABLE_FRAGMENT`, + [128]: `KEYED_FRAGMENT`, + [256]: `UNKEYED_FRAGMENT`, + [512]: `NEED_PATCH`, + [1024]: `DYNAMIC_SLOTS`, + [2048]: `DEV_ROOT_FRAGMENT`, + [-1]: `HOISTED`, + [-2]: `BAIL` + }; + var slotFlagsText = { + [1]: "STABLE", + [2]: "DYNAMIC", + [3]: "FORWARDED" + }; + var GLOBALS_WHITE_LISTED = "Infinity,undefined,NaN,isFinite,isNaN,parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,BigInt"; + var isGloballyWhitelisted = /* @__PURE__ */ makeMap(GLOBALS_WHITE_LISTED); + var range = 2; + function generateCodeFrame(source, start22 = 0, end = source.length) { + let lines = source.split(/(\r?\n)/); + const newlineSequences = lines.filter((_, idx) => idx % 2 === 1); + lines = lines.filter((_, idx) => idx % 2 === 0); + let count = 0; + const res = []; + for (let i = 0; i < lines.length; i++) { + count += lines[i].length + (newlineSequences[i] && newlineSequences[i].length || 0); + if (count >= start22) { + for (let j = i - range; j <= i + range || end > count; j++) { + if (j < 0 || j >= lines.length) + continue; + const line = j + 1; + res.push(`${line}${" ".repeat(Math.max(3 - String(line).length, 0))}| ${lines[j]}`); + const lineLength = lines[j].length; + const newLineSeqLength = newlineSequences[j] && newlineSequences[j].length || 0; + if (j === i) { + const pad = start22 - (count - (lineLength + newLineSeqLength)); + const length = Math.max(1, end > count ? lineLength - pad : end - start22); + res.push(` | ` + " ".repeat(pad) + "^".repeat(length)); + } else if (j > i) { + if (end > count) { + const length = Math.max(Math.min(end - count, lineLength), 1); + res.push(` | ` + "^".repeat(length)); + } + count += lineLength + newLineSeqLength; + } + } + break; + } + } + return res.join("\n"); + } + var specialBooleanAttrs = `itemscope,allowfullscreen,formnovalidate,ismap,nomodule,novalidate,readonly`; + var isSpecialBooleanAttr = /* @__PURE__ */ makeMap(specialBooleanAttrs); + var isBooleanAttr2 = /* @__PURE__ */ makeMap(specialBooleanAttrs + `,async,autofocus,autoplay,controls,default,defer,disabled,hidden,loop,open,required,reversed,scoped,seamless,checked,muted,multiple,selected`); + var unsafeAttrCharRE = /[>/="'\u0009\u000a\u000c\u0020]/; + var attrValidationCache = {}; + function isSSRSafeAttrName(name) { + if (attrValidationCache.hasOwnProperty(name)) { + return attrValidationCache[name]; + } + const isUnsafe = unsafeAttrCharRE.test(name); + if (isUnsafe) { + console.error(`unsafe attribute name: ${name}`); + } + return attrValidationCache[name] = !isUnsafe; + } + var propsToAttrMap = { + acceptCharset: "accept-charset", + className: "class", + htmlFor: "for", + httpEquiv: "http-equiv" + }; + var isNoUnitNumericStyleProp = /* @__PURE__ */ makeMap(`animation-iteration-count,border-image-outset,border-image-slice,border-image-width,box-flex,box-flex-group,box-ordinal-group,column-count,columns,flex,flex-grow,flex-positive,flex-shrink,flex-negative,flex-order,grid-row,grid-row-end,grid-row-span,grid-row-start,grid-column,grid-column-end,grid-column-span,grid-column-start,font-weight,line-clamp,line-height,opacity,order,orphans,tab-size,widows,z-index,zoom,fill-opacity,flood-opacity,stop-opacity,stroke-dasharray,stroke-dashoffset,stroke-miterlimit,stroke-opacity,stroke-width`); + var isKnownAttr = /* @__PURE__ */ makeMap(`accept,accept-charset,accesskey,action,align,allow,alt,async,autocapitalize,autocomplete,autofocus,autoplay,background,bgcolor,border,buffered,capture,challenge,charset,checked,cite,class,code,codebase,color,cols,colspan,content,contenteditable,contextmenu,controls,coords,crossorigin,csp,data,datetime,decoding,default,defer,dir,dirname,disabled,download,draggable,dropzone,enctype,enterkeyhint,for,form,formaction,formenctype,formmethod,formnovalidate,formtarget,headers,height,hidden,high,href,hreflang,http-equiv,icon,id,importance,integrity,ismap,itemprop,keytype,kind,label,lang,language,loading,list,loop,low,manifest,max,maxlength,minlength,media,min,multiple,muted,name,novalidate,open,optimum,pattern,ping,placeholder,poster,preload,radiogroup,readonly,referrerpolicy,rel,required,reversed,rows,rowspan,sandbox,scope,scoped,selected,shape,size,sizes,slot,span,spellcheck,src,srcdoc,srclang,srcset,start,step,style,summary,tabindex,target,title,translate,type,usemap,value,width,wrap`); + function normalizeStyle(value) { + if (isArray2(value)) { + const res = {}; + for (let i = 0; i < value.length; i++) { + const item = value[i]; + const normalized = normalizeStyle(isString(item) ? parseStringStyle(item) : item); + if (normalized) { + for (const key in normalized) { + res[key] = normalized[key]; + } + } + } + return res; + } else if (isObject2(value)) { + return value; + } + } + var listDelimiterRE = /;(?![^(]*\))/g; + var propertyDelimiterRE = /:(.+)/; + function parseStringStyle(cssText) { + const ret = {}; + cssText.split(listDelimiterRE).forEach((item) => { + if (item) { + const tmp = item.split(propertyDelimiterRE); + tmp.length > 1 && (ret[tmp[0].trim()] = tmp[1].trim()); + } + }); + return ret; + } + function stringifyStyle(styles) { + let ret = ""; + if (!styles) { + return ret; + } + for (const key in styles) { + const value = styles[key]; + const normalizedKey = key.startsWith(`--`) ? key : hyphenate(key); + if (isString(value) || typeof value === "number" && isNoUnitNumericStyleProp(normalizedKey)) { + ret += `${normalizedKey}:${value};`; + } + } + return ret; + } + function normalizeClass(value) { + let res = ""; + if (isString(value)) { + res = value; + } else if (isArray2(value)) { + for (let i = 0; i < value.length; i++) { + const normalized = normalizeClass(value[i]); + if (normalized) { + res += normalized + " "; + } + } + } else if (isObject2(value)) { + for (const name in value) { + if (value[name]) { + res += name + " "; + } + } + } + return res.trim(); + } + var HTML_TAGS = "html,body,base,head,link,meta,style,title,address,article,aside,footer,header,h1,h2,h3,h4,h5,h6,hgroup,nav,section,div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,rtc,ruby,s,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,embed,object,param,source,canvas,script,noscript,del,ins,caption,col,colgroup,table,thead,tbody,td,th,tr,button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,output,progress,select,textarea,details,dialog,menu,summary,template,blockquote,iframe,tfoot"; + var SVG_TAGS = "svg,animate,animateMotion,animateTransform,circle,clipPath,color-profile,defs,desc,discard,ellipse,feBlend,feColorMatrix,feComponentTransfer,feComposite,feConvolveMatrix,feDiffuseLighting,feDisplacementMap,feDistanceLight,feDropShadow,feFlood,feFuncA,feFuncB,feFuncG,feFuncR,feGaussianBlur,feImage,feMerge,feMergeNode,feMorphology,feOffset,fePointLight,feSpecularLighting,feSpotLight,feTile,feTurbulence,filter,foreignObject,g,hatch,hatchpath,image,line,linearGradient,marker,mask,mesh,meshgradient,meshpatch,meshrow,metadata,mpath,path,pattern,polygon,polyline,radialGradient,rect,set,solidcolor,stop,switch,symbol,text,textPath,title,tspan,unknown,use,view"; + var VOID_TAGS = "area,base,br,col,embed,hr,img,input,link,meta,param,source,track,wbr"; + var isHTMLTag = /* @__PURE__ */ makeMap(HTML_TAGS); + var isSVGTag = /* @__PURE__ */ makeMap(SVG_TAGS); + var isVoidTag = /* @__PURE__ */ makeMap(VOID_TAGS); + var escapeRE = /["'&<>]/; + function escapeHtml(string) { + const str = "" + string; + const match = escapeRE.exec(str); + if (!match) { + return str; + } + let html = ""; + let escaped; + let index; + let lastIndex = 0; + for (index = match.index; index < str.length; index++) { + switch (str.charCodeAt(index)) { + case 34: + escaped = """; + break; + case 38: + escaped = "&"; + break; + case 39: + escaped = "'"; + break; + case 60: + escaped = "<"; + break; + case 62: + escaped = ">"; + break; + default: + continue; + } + if (lastIndex !== index) { + html += str.substring(lastIndex, index); + } + lastIndex = index + 1; + html += escaped; + } + return lastIndex !== index ? html + str.substring(lastIndex, index) : html; + } + var commentStripRE = /^-?>||--!>| looseEqual(item, val)); + } + var toDisplayString = (val) => { + return val == null ? "" : isObject2(val) ? JSON.stringify(val, replacer, 2) : String(val); + }; + var replacer = (_key, val) => { + if (isMap(val)) { + return { + [`Map(${val.size})`]: [...val.entries()].reduce((entries, [key, val2]) => { + entries[`${key} =>`] = val2; + return entries; + }, {}) + }; + } else if (isSet(val)) { + return { + [`Set(${val.size})`]: [...val.values()] + }; + } else if (isObject2(val) && !isArray2(val) && !isPlainObject(val)) { + return String(val); + } + return val; + }; + var babelParserDefaultPlugins = [ + "bigInt", + "optionalChaining", + "nullishCoalescingOperator" + ]; + var EMPTY_OBJ = Object.freeze({}); + var EMPTY_ARR = Object.freeze([]); + var NOOP = () => { + }; + var NO = () => false; + var onRE = /^on[^a-z]/; + var isOn = (key) => onRE.test(key); + var isModelListener = (key) => key.startsWith("onUpdate:"); + var extend = Object.assign; + var remove = (arr, el) => { + const i = arr.indexOf(el); + if (i > -1) { + arr.splice(i, 1); + } + }; + var hasOwnProperty = Object.prototype.hasOwnProperty; + var hasOwn = (val, key) => hasOwnProperty.call(val, key); + var isArray2 = Array.isArray; + var isMap = (val) => toTypeString(val) === "[object Map]"; + var isSet = (val) => toTypeString(val) === "[object Set]"; + var isDate = (val) => val instanceof Date; + var isFunction2 = (val) => typeof val === "function"; + var isString = (val) => typeof val === "string"; + var isSymbol = (val) => typeof val === "symbol"; + var isObject2 = (val) => val !== null && typeof val === "object"; + var isPromise = (val) => { + return isObject2(val) && isFunction2(val.then) && isFunction2(val.catch); + }; + var objectToString = Object.prototype.toString; + var toTypeString = (value) => objectToString.call(value); + var toRawType = (value) => { + return toTypeString(value).slice(8, -1); + }; + var isPlainObject = (val) => toTypeString(val) === "[object Object]"; + var isIntegerKey = (key) => isString(key) && key !== "NaN" && key[0] !== "-" && "" + parseInt(key, 10) === key; + var isReservedProp = /* @__PURE__ */ makeMap(",key,ref,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"); + var cacheStringFunction = (fn) => { + const cache = /* @__PURE__ */ Object.create(null); + return (str) => { + const hit = cache[str]; + return hit || (cache[str] = fn(str)); + }; + }; + var camelizeRE = /-(\w)/g; + var camelize = cacheStringFunction((str) => { + return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : ""); + }); + var hyphenateRE = /\B([A-Z])/g; + var hyphenate = cacheStringFunction((str) => str.replace(hyphenateRE, "-$1").toLowerCase()); + var capitalize = cacheStringFunction((str) => str.charAt(0).toUpperCase() + str.slice(1)); + var toHandlerKey = cacheStringFunction((str) => str ? `on${capitalize(str)}` : ``); + var hasChanged = (value, oldValue) => value !== oldValue && (value === value || oldValue === oldValue); + var invokeArrayFns = (fns, arg) => { + for (let i = 0; i < fns.length; i++) { + fns[i](arg); + } + }; + var def = (obj, key, value) => { + Object.defineProperty(obj, key, { + configurable: true, + enumerable: false, + value + }); + }; + var toNumber = (val) => { + const n = parseFloat(val); + return isNaN(n) ? val : n; + }; + var _globalThis; + var getGlobalThis = () => { + return _globalThis || (_globalThis = typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : {}); + }; + exports2.EMPTY_ARR = EMPTY_ARR; + exports2.EMPTY_OBJ = EMPTY_OBJ; + exports2.NO = NO; + exports2.NOOP = NOOP; + exports2.PatchFlagNames = PatchFlagNames; + exports2.babelParserDefaultPlugins = babelParserDefaultPlugins; + exports2.camelize = camelize; + exports2.capitalize = capitalize; + exports2.def = def; + exports2.escapeHtml = escapeHtml; + exports2.escapeHtmlComment = escapeHtmlComment; + exports2.extend = extend; + exports2.generateCodeFrame = generateCodeFrame; + exports2.getGlobalThis = getGlobalThis; + exports2.hasChanged = hasChanged; + exports2.hasOwn = hasOwn; + exports2.hyphenate = hyphenate; + exports2.invokeArrayFns = invokeArrayFns; + exports2.isArray = isArray2; + exports2.isBooleanAttr = isBooleanAttr2; + exports2.isDate = isDate; + exports2.isFunction = isFunction2; + exports2.isGloballyWhitelisted = isGloballyWhitelisted; + exports2.isHTMLTag = isHTMLTag; + exports2.isIntegerKey = isIntegerKey; + exports2.isKnownAttr = isKnownAttr; + exports2.isMap = isMap; + exports2.isModelListener = isModelListener; + exports2.isNoUnitNumericStyleProp = isNoUnitNumericStyleProp; + exports2.isObject = isObject2; + exports2.isOn = isOn; + exports2.isPlainObject = isPlainObject; + exports2.isPromise = isPromise; + exports2.isReservedProp = isReservedProp; + exports2.isSSRSafeAttrName = isSSRSafeAttrName; + exports2.isSVGTag = isSVGTag; + exports2.isSet = isSet; + exports2.isSpecialBooleanAttr = isSpecialBooleanAttr; + exports2.isString = isString; + exports2.isSymbol = isSymbol; + exports2.isVoidTag = isVoidTag; + exports2.looseEqual = looseEqual; + exports2.looseIndexOf = looseIndexOf; + exports2.makeMap = makeMap; + exports2.normalizeClass = normalizeClass; + exports2.normalizeStyle = normalizeStyle; + exports2.objectToString = objectToString; + exports2.parseStringStyle = parseStringStyle; + exports2.propsToAttrMap = propsToAttrMap; + exports2.remove = remove; + exports2.slotFlagsText = slotFlagsText; + exports2.stringifyStyle = stringifyStyle; + exports2.toDisplayString = toDisplayString; + exports2.toHandlerKey = toHandlerKey; + exports2.toNumber = toNumber; + exports2.toRawType = toRawType; + exports2.toTypeString = toTypeString; + } + }); + var require_shared = __commonJS2({ + "node_modules/@vue/shared/index.js"(exports2, module2) { + "use strict"; + if (false) { + module2.exports = null; + } else { + module2.exports = require_shared_cjs(); + } + } + }); + var require_reactivity_cjs = __commonJS2({ + "node_modules/@vue/reactivity/dist/reactivity.cjs.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + var shared = require_shared(); + var targetMap = /* @__PURE__ */ new WeakMap(); + var effectStack = []; + var activeEffect; + var ITERATE_KEY = Symbol("iterate"); + var MAP_KEY_ITERATE_KEY = Symbol("Map key iterate"); + function isEffect(fn) { + return fn && fn._isEffect === true; + } + function effect3(fn, options = shared.EMPTY_OBJ) { + if (isEffect(fn)) { + fn = fn.raw; + } + const effect4 = createReactiveEffect(fn, options); + if (!options.lazy) { + effect4(); + } + return effect4; + } + function stop2(effect4) { + if (effect4.active) { + cleanup(effect4); + if (effect4.options.onStop) { + effect4.options.onStop(); + } + effect4.active = false; + } + } + var uid = 0; + function createReactiveEffect(fn, options) { + const effect4 = function reactiveEffect() { + if (!effect4.active) { + return fn(); + } + if (!effectStack.includes(effect4)) { + cleanup(effect4); + try { + enableTracking(); + effectStack.push(effect4); + activeEffect = effect4; + return fn(); + } finally { + effectStack.pop(); + resetTracking(); + activeEffect = effectStack[effectStack.length - 1]; + } + } + }; + effect4.id = uid++; + effect4.allowRecurse = !!options.allowRecurse; + effect4._isEffect = true; + effect4.active = true; + effect4.raw = fn; + effect4.deps = []; + effect4.options = options; + return effect4; + } + function cleanup(effect4) { + const { deps } = effect4; + if (deps.length) { + for (let i = 0; i < deps.length; i++) { + deps[i].delete(effect4); + } + deps.length = 0; + } + } + var shouldTrack = true; + var trackStack = []; + function pauseTracking() { + trackStack.push(shouldTrack); + shouldTrack = false; + } + function enableTracking() { + trackStack.push(shouldTrack); + shouldTrack = true; + } + function resetTracking() { + const last = trackStack.pop(); + shouldTrack = last === void 0 ? true : last; + } + function track2(target, type, key) { + if (!shouldTrack || activeEffect === void 0) { + return; + } + let depsMap = targetMap.get(target); + if (!depsMap) { + targetMap.set(target, depsMap = /* @__PURE__ */ new Map()); + } + let dep = depsMap.get(key); + if (!dep) { + depsMap.set(key, dep = /* @__PURE__ */ new Set()); + } + if (!dep.has(activeEffect)) { + dep.add(activeEffect); + activeEffect.deps.push(dep); + if (activeEffect.options.onTrack) { + activeEffect.options.onTrack({ + effect: activeEffect, + target, + type, + key + }); + } + } + } + function trigger2(target, type, key, newValue, oldValue, oldTarget) { + const depsMap = targetMap.get(target); + if (!depsMap) { + return; + } + const effects = /* @__PURE__ */ new Set(); + const add2 = (effectsToAdd) => { + if (effectsToAdd) { + effectsToAdd.forEach((effect4) => { + if (effect4 !== activeEffect || effect4.allowRecurse) { + effects.add(effect4); + } + }); + } + }; + if (type === "clear") { + depsMap.forEach(add2); + } else if (key === "length" && shared.isArray(target)) { + depsMap.forEach((dep, key2) => { + if (key2 === "length" || key2 >= newValue) { + add2(dep); + } + }); + } else { + if (key !== void 0) { + add2(depsMap.get(key)); + } + switch (type) { + case "add": + if (!shared.isArray(target)) { + add2(depsMap.get(ITERATE_KEY)); + if (shared.isMap(target)) { + add2(depsMap.get(MAP_KEY_ITERATE_KEY)); + } + } else if (shared.isIntegerKey(key)) { + add2(depsMap.get("length")); + } + break; + case "delete": + if (!shared.isArray(target)) { + add2(depsMap.get(ITERATE_KEY)); + if (shared.isMap(target)) { + add2(depsMap.get(MAP_KEY_ITERATE_KEY)); + } + } + break; + case "set": + if (shared.isMap(target)) { + add2(depsMap.get(ITERATE_KEY)); + } + break; + } + } + const run = (effect4) => { + if (effect4.options.onTrigger) { + effect4.options.onTrigger({ + effect: effect4, + target, + key, + type, + newValue, + oldValue, + oldTarget + }); + } + if (effect4.options.scheduler) { + effect4.options.scheduler(effect4); + } else { + effect4(); + } + }; + effects.forEach(run); + } + var isNonTrackableKeys = /* @__PURE__ */ shared.makeMap(`__proto__,__v_isRef,__isVue`); + var builtInSymbols = new Set(Object.getOwnPropertyNames(Symbol).map((key) => Symbol[key]).filter(shared.isSymbol)); + var get2 = /* @__PURE__ */ createGetter(); + var shallowGet = /* @__PURE__ */ createGetter(false, true); + var readonlyGet = /* @__PURE__ */ createGetter(true); + var shallowReadonlyGet = /* @__PURE__ */ createGetter(true, true); + var arrayInstrumentations = /* @__PURE__ */ createArrayInstrumentations(); + function createArrayInstrumentations() { + const instrumentations = {}; + ["includes", "indexOf", "lastIndexOf"].forEach((key) => { + instrumentations[key] = function(...args) { + const arr = toRaw2(this); + for (let i = 0, l = this.length; i < l; i++) { + track2(arr, "get", i + ""); + } + const res = arr[key](...args); + if (res === -1 || res === false) { + return arr[key](...args.map(toRaw2)); + } else { + return res; + } + }; + }); + ["push", "pop", "shift", "unshift", "splice"].forEach((key) => { + instrumentations[key] = function(...args) { + pauseTracking(); + const res = toRaw2(this)[key].apply(this, args); + resetTracking(); + return res; + }; + }); + return instrumentations; + } + function createGetter(isReadonly2 = false, shallow = false) { + return function get3(target, key, receiver) { + if (key === "__v_isReactive") { + return !isReadonly2; + } else if (key === "__v_isReadonly") { + return isReadonly2; + } else if (key === "__v_raw" && receiver === (isReadonly2 ? shallow ? shallowReadonlyMap : readonlyMap : shallow ? shallowReactiveMap : reactiveMap).get(target)) { + return target; + } + const targetIsArray = shared.isArray(target); + if (!isReadonly2 && targetIsArray && shared.hasOwn(arrayInstrumentations, key)) { + return Reflect.get(arrayInstrumentations, key, receiver); + } + const res = Reflect.get(target, key, receiver); + if (shared.isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) { + return res; + } + if (!isReadonly2) { + track2(target, "get", key); + } + if (shallow) { + return res; + } + if (isRef(res)) { + const shouldUnwrap = !targetIsArray || !shared.isIntegerKey(key); + return shouldUnwrap ? res.value : res; + } + if (shared.isObject(res)) { + return isReadonly2 ? readonly(res) : reactive3(res); + } + return res; + }; + } + var set2 = /* @__PURE__ */ createSetter(); + var shallowSet = /* @__PURE__ */ createSetter(true); + function createSetter(shallow = false) { + return function set3(target, key, value, receiver) { + let oldValue = target[key]; + if (!shallow) { + value = toRaw2(value); + oldValue = toRaw2(oldValue); + if (!shared.isArray(target) && isRef(oldValue) && !isRef(value)) { + oldValue.value = value; + return true; + } + } + const hadKey = shared.isArray(target) && shared.isIntegerKey(key) ? Number(key) < target.length : shared.hasOwn(target, key); + const result = Reflect.set(target, key, value, receiver); + if (target === toRaw2(receiver)) { + if (!hadKey) { + trigger2(target, "add", key, value); + } else if (shared.hasChanged(value, oldValue)) { + trigger2(target, "set", key, value, oldValue); + } + } + return result; + }; + } + function deleteProperty(target, key) { + const hadKey = shared.hasOwn(target, key); + const oldValue = target[key]; + const result = Reflect.deleteProperty(target, key); + if (result && hadKey) { + trigger2(target, "delete", key, void 0, oldValue); + } + return result; + } + function has(target, key) { + const result = Reflect.has(target, key); + if (!shared.isSymbol(key) || !builtInSymbols.has(key)) { + track2(target, "has", key); + } + return result; + } + function ownKeys(target) { + track2(target, "iterate", shared.isArray(target) ? "length" : ITERATE_KEY); + return Reflect.ownKeys(target); + } + var mutableHandlers = { + get: get2, + set: set2, + deleteProperty, + has, + ownKeys + }; + var readonlyHandlers = { + get: readonlyGet, + set(target, key) { + { + console.warn(`Set operation on key "${String(key)}" failed: target is readonly.`, target); + } + return true; + }, + deleteProperty(target, key) { + { + console.warn(`Delete operation on key "${String(key)}" failed: target is readonly.`, target); + } + return true; + } + }; + var shallowReactiveHandlers = /* @__PURE__ */ shared.extend({}, mutableHandlers, { + get: shallowGet, + set: shallowSet + }); + var shallowReadonlyHandlers = /* @__PURE__ */ shared.extend({}, readonlyHandlers, { + get: shallowReadonlyGet + }); + var toReactive = (value) => shared.isObject(value) ? reactive3(value) : value; + var toReadonly = (value) => shared.isObject(value) ? readonly(value) : value; + var toShallow = (value) => value; + var getProto = (v) => Reflect.getPrototypeOf(v); + function get$1(target, key, isReadonly2 = false, isShallow = false) { + target = target["__v_raw"]; + const rawTarget = toRaw2(target); + const rawKey = toRaw2(key); + if (key !== rawKey) { + !isReadonly2 && track2(rawTarget, "get", key); + } + !isReadonly2 && track2(rawTarget, "get", rawKey); + const { has: has2 } = getProto(rawTarget); + const wrap = isShallow ? toShallow : isReadonly2 ? toReadonly : toReactive; + if (has2.call(rawTarget, key)) { + return wrap(target.get(key)); + } else if (has2.call(rawTarget, rawKey)) { + return wrap(target.get(rawKey)); + } else if (target !== rawTarget) { + target.get(key); + } + } + function has$1(key, isReadonly2 = false) { + const target = this["__v_raw"]; + const rawTarget = toRaw2(target); + const rawKey = toRaw2(key); + if (key !== rawKey) { + !isReadonly2 && track2(rawTarget, "has", key); + } + !isReadonly2 && track2(rawTarget, "has", rawKey); + return key === rawKey ? target.has(key) : target.has(key) || target.has(rawKey); + } + function size(target, isReadonly2 = false) { + target = target["__v_raw"]; + !isReadonly2 && track2(toRaw2(target), "iterate", ITERATE_KEY); + return Reflect.get(target, "size", target); + } + function add(value) { + value = toRaw2(value); + const target = toRaw2(this); + const proto = getProto(target); + const hadKey = proto.has.call(target, value); + if (!hadKey) { + target.add(value); + trigger2(target, "add", value, value); + } + return this; + } + function set$1(key, value) { + value = toRaw2(value); + const target = toRaw2(this); + const { has: has2, get: get3 } = getProto(target); + let hadKey = has2.call(target, key); + if (!hadKey) { + key = toRaw2(key); + hadKey = has2.call(target, key); + } else { + checkIdentityKeys(target, has2, key); + } + const oldValue = get3.call(target, key); + target.set(key, value); + if (!hadKey) { + trigger2(target, "add", key, value); + } else if (shared.hasChanged(value, oldValue)) { + trigger2(target, "set", key, value, oldValue); + } + return this; + } + function deleteEntry(key) { + const target = toRaw2(this); + const { has: has2, get: get3 } = getProto(target); + let hadKey = has2.call(target, key); + if (!hadKey) { + key = toRaw2(key); + hadKey = has2.call(target, key); + } else { + checkIdentityKeys(target, has2, key); + } + const oldValue = get3 ? get3.call(target, key) : void 0; + const result = target.delete(key); + if (hadKey) { + trigger2(target, "delete", key, void 0, oldValue); + } + return result; + } + function clear() { + const target = toRaw2(this); + const hadItems = target.size !== 0; + const oldTarget = shared.isMap(target) ? new Map(target) : new Set(target); + const result = target.clear(); + if (hadItems) { + trigger2(target, "clear", void 0, void 0, oldTarget); + } + return result; + } + function createForEach(isReadonly2, isShallow) { + return function forEach(callback, thisArg) { + const observed = this; + const target = observed["__v_raw"]; + const rawTarget = toRaw2(target); + const wrap = isShallow ? toShallow : isReadonly2 ? toReadonly : toReactive; + !isReadonly2 && track2(rawTarget, "iterate", ITERATE_KEY); + return target.forEach((value, key) => { + return callback.call(thisArg, wrap(value), wrap(key), observed); + }); + }; + } + function createIterableMethod(method, isReadonly2, isShallow) { + return function(...args) { + const target = this["__v_raw"]; + const rawTarget = toRaw2(target); + const targetIsMap = shared.isMap(rawTarget); + const isPair = method === "entries" || method === Symbol.iterator && targetIsMap; + const isKeyOnly = method === "keys" && targetIsMap; + const innerIterator = target[method](...args); + const wrap = isShallow ? toShallow : isReadonly2 ? toReadonly : toReactive; + !isReadonly2 && track2(rawTarget, "iterate", isKeyOnly ? MAP_KEY_ITERATE_KEY : ITERATE_KEY); + return { + next() { + const { value, done } = innerIterator.next(); + return done ? { value, done } : { + value: isPair ? [wrap(value[0]), wrap(value[1])] : wrap(value), + done + }; + }, + [Symbol.iterator]() { + return this; + } + }; + }; + } + function createReadonlyMethod(type) { + return function(...args) { + { + const key = args[0] ? `on key "${args[0]}" ` : ``; + console.warn(`${shared.capitalize(type)} operation ${key}failed: target is readonly.`, toRaw2(this)); + } + return type === "delete" ? false : this; + }; + } + function createInstrumentations() { + const mutableInstrumentations2 = { + get(key) { + return get$1(this, key); + }, + get size() { + return size(this); + }, + has: has$1, + add, + set: set$1, + delete: deleteEntry, + clear, + forEach: createForEach(false, false) + }; + const shallowInstrumentations2 = { + get(key) { + return get$1(this, key, false, true); + }, + get size() { + return size(this); + }, + has: has$1, + add, + set: set$1, + delete: deleteEntry, + clear, + forEach: createForEach(false, true) + }; + const readonlyInstrumentations2 = { + get(key) { + return get$1(this, key, true); + }, + get size() { + return size(this, true); + }, + has(key) { + return has$1.call(this, key, true); + }, + add: createReadonlyMethod("add"), + set: createReadonlyMethod("set"), + delete: createReadonlyMethod("delete"), + clear: createReadonlyMethod("clear"), + forEach: createForEach(true, false) + }; + const shallowReadonlyInstrumentations2 = { + get(key) { + return get$1(this, key, true, true); + }, + get size() { + return size(this, true); + }, + has(key) { + return has$1.call(this, key, true); + }, + add: createReadonlyMethod("add"), + set: createReadonlyMethod("set"), + delete: createReadonlyMethod("delete"), + clear: createReadonlyMethod("clear"), + forEach: createForEach(true, true) + }; + const iteratorMethods = ["keys", "values", "entries", Symbol.iterator]; + iteratorMethods.forEach((method) => { + mutableInstrumentations2[method] = createIterableMethod(method, false, false); + readonlyInstrumentations2[method] = createIterableMethod(method, true, false); + shallowInstrumentations2[method] = createIterableMethod(method, false, true); + shallowReadonlyInstrumentations2[method] = createIterableMethod(method, true, true); + }); + return [ + mutableInstrumentations2, + readonlyInstrumentations2, + shallowInstrumentations2, + shallowReadonlyInstrumentations2 + ]; + } + var [mutableInstrumentations, readonlyInstrumentations, shallowInstrumentations, shallowReadonlyInstrumentations] = /* @__PURE__ */ createInstrumentations(); + function createInstrumentationGetter(isReadonly2, shallow) { + const instrumentations = shallow ? isReadonly2 ? shallowReadonlyInstrumentations : shallowInstrumentations : isReadonly2 ? readonlyInstrumentations : mutableInstrumentations; + return (target, key, receiver) => { + if (key === "__v_isReactive") { + return !isReadonly2; + } else if (key === "__v_isReadonly") { + return isReadonly2; + } else if (key === "__v_raw") { + return target; + } + return Reflect.get(shared.hasOwn(instrumentations, key) && key in target ? instrumentations : target, key, receiver); + }; + } + var mutableCollectionHandlers = { + get: /* @__PURE__ */ createInstrumentationGetter(false, false) + }; + var shallowCollectionHandlers = { + get: /* @__PURE__ */ createInstrumentationGetter(false, true) + }; + var readonlyCollectionHandlers = { + get: /* @__PURE__ */ createInstrumentationGetter(true, false) + }; + var shallowReadonlyCollectionHandlers = { + get: /* @__PURE__ */ createInstrumentationGetter(true, true) + }; + function checkIdentityKeys(target, has2, key) { + const rawKey = toRaw2(key); + if (rawKey !== key && has2.call(target, rawKey)) { + const type = shared.toRawType(target); + console.warn(`Reactive ${type} contains both the raw and reactive versions of the same object${type === `Map` ? ` as keys` : ``}, which can lead to inconsistencies. Avoid differentiating between the raw and reactive versions of an object and only use the reactive version if possible.`); + } + } + var reactiveMap = /* @__PURE__ */ new WeakMap(); + var shallowReactiveMap = /* @__PURE__ */ new WeakMap(); + var readonlyMap = /* @__PURE__ */ new WeakMap(); + var shallowReadonlyMap = /* @__PURE__ */ new WeakMap(); + function targetTypeMap(rawType) { + switch (rawType) { + case "Object": + case "Array": + return 1; + case "Map": + case "Set": + case "WeakMap": + case "WeakSet": + return 2; + default: + return 0; + } + } + function getTargetType(value) { + return value["__v_skip"] || !Object.isExtensible(value) ? 0 : targetTypeMap(shared.toRawType(value)); + } + function reactive3(target) { + if (target && target["__v_isReadonly"]) { + return target; + } + return createReactiveObject(target, false, mutableHandlers, mutableCollectionHandlers, reactiveMap); + } + function shallowReactive(target) { + return createReactiveObject(target, false, shallowReactiveHandlers, shallowCollectionHandlers, shallowReactiveMap); + } + function readonly(target) { + return createReactiveObject(target, true, readonlyHandlers, readonlyCollectionHandlers, readonlyMap); + } + function shallowReadonly(target) { + return createReactiveObject(target, true, shallowReadonlyHandlers, shallowReadonlyCollectionHandlers, shallowReadonlyMap); + } + function createReactiveObject(target, isReadonly2, baseHandlers, collectionHandlers, proxyMap) { + if (!shared.isObject(target)) { + { + console.warn(`value cannot be made reactive: ${String(target)}`); + } + return target; + } + if (target["__v_raw"] && !(isReadonly2 && target["__v_isReactive"])) { + return target; + } + const existingProxy = proxyMap.get(target); + if (existingProxy) { + return existingProxy; + } + const targetType = getTargetType(target); + if (targetType === 0) { + return target; + } + const proxy = new Proxy(target, targetType === 2 ? collectionHandlers : baseHandlers); + proxyMap.set(target, proxy); + return proxy; + } + function isReactive2(value) { + if (isReadonly(value)) { + return isReactive2(value["__v_raw"]); + } + return !!(value && value["__v_isReactive"]); + } + function isReadonly(value) { + return !!(value && value["__v_isReadonly"]); + } + function isProxy(value) { + return isReactive2(value) || isReadonly(value); + } + function toRaw2(observed) { + return observed && toRaw2(observed["__v_raw"]) || observed; + } + function markRaw(value) { + shared.def(value, "__v_skip", true); + return value; + } + var convert = (val) => shared.isObject(val) ? reactive3(val) : val; + function isRef(r) { + return Boolean(r && r.__v_isRef === true); + } + function ref(value) { + return createRef(value); + } + function shallowRef(value) { + return createRef(value, true); + } + var RefImpl = class { + constructor(value, _shallow = false) { + this._shallow = _shallow; + this.__v_isRef = true; + this._rawValue = _shallow ? value : toRaw2(value); + this._value = _shallow ? value : convert(value); + } + get value() { + track2(toRaw2(this), "get", "value"); + return this._value; + } + set value(newVal) { + newVal = this._shallow ? newVal : toRaw2(newVal); + if (shared.hasChanged(newVal, this._rawValue)) { + this._rawValue = newVal; + this._value = this._shallow ? newVal : convert(newVal); + trigger2(toRaw2(this), "set", "value", newVal); + } + } + }; + function createRef(rawValue, shallow = false) { + if (isRef(rawValue)) { + return rawValue; + } + return new RefImpl(rawValue, shallow); + } + function triggerRef(ref2) { + trigger2(toRaw2(ref2), "set", "value", ref2.value); + } + function unref(ref2) { + return isRef(ref2) ? ref2.value : ref2; + } + var shallowUnwrapHandlers = { + get: (target, key, receiver) => unref(Reflect.get(target, key, receiver)), + set: (target, key, value, receiver) => { + const oldValue = target[key]; + if (isRef(oldValue) && !isRef(value)) { + oldValue.value = value; + return true; + } else { + return Reflect.set(target, key, value, receiver); + } + } + }; + function proxyRefs(objectWithRefs) { + return isReactive2(objectWithRefs) ? objectWithRefs : new Proxy(objectWithRefs, shallowUnwrapHandlers); + } + var CustomRefImpl = class { + constructor(factory) { + this.__v_isRef = true; + const { get: get3, set: set3 } = factory(() => track2(this, "get", "value"), () => trigger2(this, "set", "value")); + this._get = get3; + this._set = set3; + } + get value() { + return this._get(); + } + set value(newVal) { + this._set(newVal); + } + }; + function customRef(factory) { + return new CustomRefImpl(factory); + } + function toRefs(object) { + if (!isProxy(object)) { + console.warn(`toRefs() expects a reactive object but received a plain one.`); + } + const ret = shared.isArray(object) ? new Array(object.length) : {}; + for (const key in object) { + ret[key] = toRef(object, key); + } + return ret; + } + var ObjectRefImpl = class { + constructor(_object, _key) { + this._object = _object; + this._key = _key; + this.__v_isRef = true; + } + get value() { + return this._object[this._key]; + } + set value(newVal) { + this._object[this._key] = newVal; + } + }; + function toRef(object, key) { + return isRef(object[key]) ? object[key] : new ObjectRefImpl(object, key); + } + var ComputedRefImpl = class { + constructor(getter, _setter, isReadonly2) { + this._setter = _setter; + this._dirty = true; + this.__v_isRef = true; + this.effect = effect3(getter, { + lazy: true, + scheduler: () => { + if (!this._dirty) { + this._dirty = true; + trigger2(toRaw2(this), "set", "value"); + } + } + }); + this["__v_isReadonly"] = isReadonly2; + } + get value() { + const self2 = toRaw2(this); + if (self2._dirty) { + self2._value = this.effect(); + self2._dirty = false; + } + track2(self2, "get", "value"); + return self2._value; + } + set value(newValue) { + this._setter(newValue); + } + }; + function computed(getterOrOptions) { + let getter; + let setter; + if (shared.isFunction(getterOrOptions)) { + getter = getterOrOptions; + setter = () => { + console.warn("Write operation failed: computed value is readonly"); + }; + } else { + getter = getterOrOptions.get; + setter = getterOrOptions.set; + } + return new ComputedRefImpl(getter, setter, shared.isFunction(getterOrOptions) || !getterOrOptions.set); + } + exports2.ITERATE_KEY = ITERATE_KEY; + exports2.computed = computed; + exports2.customRef = customRef; + exports2.effect = effect3; + exports2.enableTracking = enableTracking; + exports2.isProxy = isProxy; + exports2.isReactive = isReactive2; + exports2.isReadonly = isReadonly; + exports2.isRef = isRef; + exports2.markRaw = markRaw; + exports2.pauseTracking = pauseTracking; + exports2.proxyRefs = proxyRefs; + exports2.reactive = reactive3; + exports2.readonly = readonly; + exports2.ref = ref; + exports2.resetTracking = resetTracking; + exports2.shallowReactive = shallowReactive; + exports2.shallowReadonly = shallowReadonly; + exports2.shallowRef = shallowRef; + exports2.stop = stop2; + exports2.toRaw = toRaw2; + exports2.toRef = toRef; + exports2.toRefs = toRefs; + exports2.track = track2; + exports2.trigger = trigger2; + exports2.triggerRef = triggerRef; + exports2.unref = unref; + } + }); + var require_reactivity = __commonJS2({ + "node_modules/@vue/reactivity/index.js"(exports2, module2) { + "use strict"; + if (false) { + module2.exports = null; + } else { + module2.exports = require_reactivity_cjs(); + } + } + }); + var module_exports = {}; + __export(module_exports, { + Alpine: () => src_default, + default: () => module_default + }); + module.exports = __toCommonJS(module_exports); + var flushPending = false; + var flushing = false; + var queue = []; + var lastFlushedIndex = -1; + function scheduler(callback) { + queueJob(callback); + } + function queueJob(job) { + if (!queue.includes(job)) + queue.push(job); + queueFlush(); + } + function dequeueJob(job) { + let index = queue.indexOf(job); + if (index !== -1 && index > lastFlushedIndex) + queue.splice(index, 1); + } + function queueFlush() { + if (!flushing && !flushPending) { + flushPending = true; + queueMicrotask(flushJobs); + } + } + function flushJobs() { + flushPending = false; + flushing = true; + for (let i = 0; i < queue.length; i++) { + queue[i](); + lastFlushedIndex = i; + } + queue.length = 0; + lastFlushedIndex = -1; + flushing = false; + } + var reactive; + var effect; + var release; + var raw; + var shouldSchedule = true; + function disableEffectScheduling(callback) { + shouldSchedule = false; + callback(); + shouldSchedule = true; + } + function setReactivityEngine(engine) { + reactive = engine.reactive; + release = engine.release; + effect = (callback) => engine.effect(callback, { scheduler: (task) => { + if (shouldSchedule) { + scheduler(task); + } else { + task(); + } + } }); + raw = engine.raw; + } + function overrideEffect(override) { + effect = override; + } + function elementBoundEffect(el) { + let cleanup = () => { + }; + let wrappedEffect = (callback) => { + let effectReference = effect(callback); + if (!el._x_effects) { + el._x_effects = /* @__PURE__ */ new Set(); + el._x_runEffects = () => { + el._x_effects.forEach((i) => i()); + }; + } + el._x_effects.add(effectReference); + cleanup = () => { + if (effectReference === void 0) + return; + el._x_effects.delete(effectReference); + release(effectReference); + }; + return effectReference; + }; + return [wrappedEffect, () => { + cleanup(); + }]; + } + function watch(getter, callback) { + let firstTime = true; + let oldValue; + let effectReference = effect(() => { + let value = getter(); + JSON.stringify(value); + if (!firstTime) { + queueMicrotask(() => { + callback(value, oldValue); + oldValue = value; + }); + } else { + oldValue = value; + } + firstTime = false; + }); + return () => release(effectReference); + } + var onAttributeAddeds = []; + var onElRemoveds = []; + var onElAddeds = []; + function onElAdded(callback) { + onElAddeds.push(callback); + } + function onElRemoved(el, callback) { + if (typeof callback === "function") { + if (!el._x_cleanups) + el._x_cleanups = []; + el._x_cleanups.push(callback); + } else { + callback = el; + onElRemoveds.push(callback); + } + } + function onAttributesAdded(callback) { + onAttributeAddeds.push(callback); + } + function onAttributeRemoved(el, name, callback) { + if (!el._x_attributeCleanups) + el._x_attributeCleanups = {}; + if (!el._x_attributeCleanups[name]) + el._x_attributeCleanups[name] = []; + el._x_attributeCleanups[name].push(callback); + } + function cleanupAttributes(el, names) { + if (!el._x_attributeCleanups) + return; + Object.entries(el._x_attributeCleanups).forEach(([name, value]) => { + if (names === void 0 || names.includes(name)) { + value.forEach((i) => i()); + delete el._x_attributeCleanups[name]; + } + }); + } + function cleanupElement(el) { + var _a, _b; + (_a = el._x_effects) == null ? void 0 : _a.forEach(dequeueJob); + while ((_b = el._x_cleanups) == null ? void 0 : _b.length) + el._x_cleanups.pop()(); + } + var observer = new MutationObserver(onMutate); + var currentlyObserving = false; + function startObservingMutations() { + observer.observe(document, { subtree: true, childList: true, attributes: true, attributeOldValue: true }); + currentlyObserving = true; + } + function stopObservingMutations() { + flushObserver(); + observer.disconnect(); + currentlyObserving = false; + } + var queuedMutations = []; + function flushObserver() { + let records = observer.takeRecords(); + queuedMutations.push(() => records.length > 0 && onMutate(records)); + let queueLengthWhenTriggered = queuedMutations.length; + queueMicrotask(() => { + if (queuedMutations.length === queueLengthWhenTriggered) { + while (queuedMutations.length > 0) + queuedMutations.shift()(); + } + }); + } + function mutateDom(callback) { + if (!currentlyObserving) + return callback(); + stopObservingMutations(); + let result = callback(); + startObservingMutations(); + return result; + } + var isCollecting = false; + var deferredMutations = []; + function deferMutations() { + isCollecting = true; + } + function flushAndStopDeferringMutations() { + isCollecting = false; + onMutate(deferredMutations); + deferredMutations = []; + } + function onMutate(mutations) { + if (isCollecting) { + deferredMutations = deferredMutations.concat(mutations); + return; + } + let addedNodes = /* @__PURE__ */ new Set(); + let removedNodes = /* @__PURE__ */ new Set(); + let addedAttributes = /* @__PURE__ */ new Map(); + let removedAttributes = /* @__PURE__ */ new Map(); + for (let i = 0; i < mutations.length; i++) { + if (mutations[i].target._x_ignoreMutationObserver) + continue; + if (mutations[i].type === "childList") { + mutations[i].addedNodes.forEach((node) => node.nodeType === 1 && addedNodes.add(node)); + mutations[i].removedNodes.forEach((node) => node.nodeType === 1 && removedNodes.add(node)); + } + if (mutations[i].type === "attributes") { + let el = mutations[i].target; + let name = mutations[i].attributeName; + let oldValue = mutations[i].oldValue; + let add = () => { + if (!addedAttributes.has(el)) + addedAttributes.set(el, []); + addedAttributes.get(el).push({ name, value: el.getAttribute(name) }); + }; + let remove = () => { + if (!removedAttributes.has(el)) + removedAttributes.set(el, []); + removedAttributes.get(el).push(name); + }; + if (el.hasAttribute(name) && oldValue === null) { + add(); + } else if (el.hasAttribute(name)) { + remove(); + add(); + } else { + remove(); + } + } + } + removedAttributes.forEach((attrs, el) => { + cleanupAttributes(el, attrs); + }); + addedAttributes.forEach((attrs, el) => { + onAttributeAddeds.forEach((i) => i(el, attrs)); + }); + for (let node of removedNodes) { + if (addedNodes.has(node)) + continue; + onElRemoveds.forEach((i) => i(node)); + } + addedNodes.forEach((node) => { + node._x_ignoreSelf = true; + node._x_ignore = true; + }); + for (let node of addedNodes) { + if (removedNodes.has(node)) + continue; + if (!node.isConnected) + continue; + delete node._x_ignoreSelf; + delete node._x_ignore; + onElAddeds.forEach((i) => i(node)); + node._x_ignore = true; + node._x_ignoreSelf = true; + } + addedNodes.forEach((node) => { + delete node._x_ignoreSelf; + delete node._x_ignore; + }); + addedNodes = null; + removedNodes = null; + addedAttributes = null; + removedAttributes = null; + } + function scope(node) { + return mergeProxies(closestDataStack(node)); + } + function addScopeToNode(node, data2, referenceNode) { + node._x_dataStack = [data2, ...closestDataStack(referenceNode || node)]; + return () => { + node._x_dataStack = node._x_dataStack.filter((i) => i !== data2); + }; + } + function closestDataStack(node) { + if (node._x_dataStack) + return node._x_dataStack; + if (typeof ShadowRoot === "function" && node instanceof ShadowRoot) { + return closestDataStack(node.host); + } + if (!node.parentNode) { + return []; + } + return closestDataStack(node.parentNode); + } + function mergeProxies(objects) { + return new Proxy({ objects }, mergeProxyTrap); + } + var mergeProxyTrap = { + ownKeys({ objects }) { + return Array.from(new Set(objects.flatMap((i) => Object.keys(i)))); + }, + has({ objects }, name) { + if (name == Symbol.unscopables) + return false; + return objects.some((obj) => Object.prototype.hasOwnProperty.call(obj, name) || Reflect.has(obj, name)); + }, + get({ objects }, name, thisProxy) { + if (name == "toJSON") + return collapseProxies; + return Reflect.get(objects.find((obj) => Reflect.has(obj, name)) || {}, name, thisProxy); + }, + set({ objects }, name, value, thisProxy) { + const target = objects.find((obj) => Object.prototype.hasOwnProperty.call(obj, name)) || objects[objects.length - 1]; + const descriptor = Object.getOwnPropertyDescriptor(target, name); + if ((descriptor == null ? void 0 : descriptor.set) && (descriptor == null ? void 0 : descriptor.get)) + return descriptor.set.call(thisProxy, value) || true; + return Reflect.set(target, name, value); + } + }; + function collapseProxies() { + let keys = Reflect.ownKeys(this); + return keys.reduce((acc, key) => { + acc[key] = Reflect.get(this, key); + return acc; + }, {}); + } + function initInterceptors(data2) { + let isObject2 = (val) => typeof val === "object" && !Array.isArray(val) && val !== null; + let recurse = (obj, basePath = "") => { + Object.entries(Object.getOwnPropertyDescriptors(obj)).forEach(([key, { value, enumerable }]) => { + if (enumerable === false || value === void 0) + return; + if (typeof value === "object" && value !== null && value.__v_skip) + return; + let path = basePath === "" ? key : `${basePath}.${key}`; + if (typeof value === "object" && value !== null && value._x_interceptor) { + obj[key] = value.initialize(data2, path, key); + } else { + if (isObject2(value) && value !== obj && !(value instanceof Element)) { + recurse(value, path); + } + } + }); + }; + return recurse(data2); + } + function interceptor(callback, mutateObj = () => { + }) { + let obj = { + initialValue: void 0, + _x_interceptor: true, + initialize(data2, path, key) { + return callback(this.initialValue, () => get(data2, path), (value) => set(data2, path, value), path, key); + } + }; + mutateObj(obj); + return (initialValue) => { + if (typeof initialValue === "object" && initialValue !== null && initialValue._x_interceptor) { + let initialize = obj.initialize.bind(obj); + obj.initialize = (data2, path, key) => { + let innerValue = initialValue.initialize(data2, path, key); + obj.initialValue = innerValue; + return initialize(data2, path, key); + }; + } else { + obj.initialValue = initialValue; + } + return obj; + }; + } + function get(obj, path) { + return path.split(".").reduce((carry, segment) => carry[segment], obj); + } + function set(obj, path, value) { + if (typeof path === "string") + path = path.split("."); + if (path.length === 1) + obj[path[0]] = value; + else if (path.length === 0) + throw error; + else { + if (obj[path[0]]) + return set(obj[path[0]], path.slice(1), value); + else { + obj[path[0]] = {}; + return set(obj[path[0]], path.slice(1), value); + } + } + } + var magics = {}; + function magic(name, callback) { + magics[name] = callback; + } + function injectMagics(obj, el) { + let memoizedUtilities = getUtilities(el); + Object.entries(magics).forEach(([name, callback]) => { + Object.defineProperty(obj, `$${name}`, { + get() { + return callback(el, memoizedUtilities); + }, + enumerable: false + }); + }); + return obj; + } + function getUtilities(el) { + let [utilities, cleanup] = getElementBoundUtilities(el); + let utils = { interceptor, ...utilities }; + onElRemoved(el, cleanup); + return utils; + } + function tryCatch(el, expression, callback, ...args) { + try { + return callback(...args); + } catch (e) { + handleError(e, el, expression); + } + } + function handleError(error2, el, expression = void 0) { + error2 = Object.assign(error2 != null ? error2 : { message: "No error message given." }, { el, expression }); + console.warn(`Alpine Expression Error: ${error2.message} + +${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); + setTimeout(() => { + throw error2; + }, 0); + } + var shouldAutoEvaluateFunctions = true; + function dontAutoEvaluateFunctions(callback) { + let cache = shouldAutoEvaluateFunctions; + shouldAutoEvaluateFunctions = false; + let result = callback(); + shouldAutoEvaluateFunctions = cache; + return result; + } + function evaluate(el, expression, extras = {}) { + let result; + evaluateLater(el, expression)((value) => result = value, extras); + return result; + } + function evaluateLater(...args) { + return theEvaluatorFunction(...args); + } + var theEvaluatorFunction = normalEvaluator; + function setEvaluator(newEvaluator) { + theEvaluatorFunction = newEvaluator; + } + function normalEvaluator(el, expression) { + let overriddenMagics = {}; + injectMagics(overriddenMagics, el); + let dataStack = [overriddenMagics, ...closestDataStack(el)]; + let evaluator = typeof expression === "function" ? generateEvaluatorFromFunction(dataStack, expression) : generateEvaluatorFromString(dataStack, expression, el); + return tryCatch.bind(null, el, expression, evaluator); + } + function generateEvaluatorFromFunction(dataStack, func) { + return (receiver = () => { + }, { scope: scope2 = {}, params = [] } = {}) => { + let result = func.apply(mergeProxies([scope2, ...dataStack]), params); + runIfTypeOfFunction(receiver, result); + }; + } + var evaluatorMemo = {}; + function generateFunctionFromString(expression, el) { + if (evaluatorMemo[expression]) { + return evaluatorMemo[expression]; + } + let AsyncFunction = Object.getPrototypeOf(async function() { + }).constructor; + let rightSideSafeExpression = /^[\n\s]*if.*\(.*\)/.test(expression.trim()) || /^(let|const)\s/.test(expression.trim()) ? `(async()=>{ ${expression} })()` : expression; + const safeAsyncFunction = () => { + try { + let func2 = new AsyncFunction(["__self", "scope"], `with (scope) { __self.result = ${rightSideSafeExpression} }; __self.finished = true; return __self.result;`); + Object.defineProperty(func2, "name", { + value: `[Alpine] ${expression}` + }); + return func2; + } catch (error2) { + handleError(error2, el, expression); + return Promise.resolve(); + } + }; + let func = safeAsyncFunction(); + evaluatorMemo[expression] = func; + return func; + } + function generateEvaluatorFromString(dataStack, expression, el) { + let func = generateFunctionFromString(expression, el); + return (receiver = () => { + }, { scope: scope2 = {}, params = [] } = {}) => { + func.result = void 0; + func.finished = false; + let completeScope = mergeProxies([scope2, ...dataStack]); + if (typeof func === "function") { + let promise = func(func, completeScope).catch((error2) => handleError(error2, el, expression)); + if (func.finished) { + runIfTypeOfFunction(receiver, func.result, completeScope, params, el); + func.result = void 0; + } else { + promise.then((result) => { + runIfTypeOfFunction(receiver, result, completeScope, params, el); + }).catch((error2) => handleError(error2, el, expression)).finally(() => func.result = void 0); + } + } + }; + } + function runIfTypeOfFunction(receiver, value, scope2, params, el) { + if (shouldAutoEvaluateFunctions && typeof value === "function") { + let result = value.apply(scope2, params); + if (result instanceof Promise) { + result.then((i) => runIfTypeOfFunction(receiver, i, scope2, params)).catch((error2) => handleError(error2, el, value)); + } else { + receiver(result); + } + } else if (typeof value === "object" && value instanceof Promise) { + value.then((i) => receiver(i)); + } else { + receiver(value); + } + } + var prefixAsString = "x-"; + function prefix(subject = "") { + return prefixAsString + subject; + } + function setPrefix(newPrefix) { + prefixAsString = newPrefix; + } + var directiveHandlers = {}; + function directive2(name, callback) { + directiveHandlers[name] = callback; + return { + before(directive22) { + if (!directiveHandlers[directive22]) { + console.warn(String.raw`Cannot find directive \`${directive22}\`. \`${name}\` will use the default order of execution`); + return; + } + const pos = directiveOrder.indexOf(directive22); + directiveOrder.splice(pos >= 0 ? pos : directiveOrder.indexOf("DEFAULT"), 0, name); + } + }; + } + function directiveExists(name) { + return Object.keys(directiveHandlers).includes(name); + } + function directives(el, attributes, originalAttributeOverride) { + attributes = Array.from(attributes); + if (el._x_virtualDirectives) { + let vAttributes = Object.entries(el._x_virtualDirectives).map(([name, value]) => ({ name, value })); + let staticAttributes = attributesOnly(vAttributes); + vAttributes = vAttributes.map((attribute) => { + if (staticAttributes.find((attr) => attr.name === attribute.name)) { + return { + name: `x-bind:${attribute.name}`, + value: `"${attribute.value}"` + }; + } + return attribute; + }); + attributes = attributes.concat(vAttributes); + } + let transformedAttributeMap = {}; + let directives2 = attributes.map(toTransformedAttributes((newName, oldName) => transformedAttributeMap[newName] = oldName)).filter(outNonAlpineAttributes).map(toParsedDirectives(transformedAttributeMap, originalAttributeOverride)).sort(byPriority); + return directives2.map((directive22) => { + return getDirectiveHandler(el, directive22); + }); + } + function attributesOnly(attributes) { + return Array.from(attributes).map(toTransformedAttributes()).filter((attr) => !outNonAlpineAttributes(attr)); + } + var isDeferringHandlers = false; + var directiveHandlerStacks = /* @__PURE__ */ new Map(); + var currentHandlerStackKey = Symbol(); + function deferHandlingDirectives(callback) { + isDeferringHandlers = true; + let key = Symbol(); + currentHandlerStackKey = key; + directiveHandlerStacks.set(key, []); + let flushHandlers = () => { + while (directiveHandlerStacks.get(key).length) + directiveHandlerStacks.get(key).shift()(); + directiveHandlerStacks.delete(key); + }; + let stopDeferring = () => { + isDeferringHandlers = false; + flushHandlers(); + }; + callback(flushHandlers); + stopDeferring(); + } + function getElementBoundUtilities(el) { + let cleanups2 = []; + let cleanup = (callback) => cleanups2.push(callback); + let [effect3, cleanupEffect] = elementBoundEffect(el); + cleanups2.push(cleanupEffect); + let utilities = { + Alpine: alpine_default, + effect: effect3, + cleanup, + evaluateLater: evaluateLater.bind(evaluateLater, el), + evaluate: evaluate.bind(evaluate, el) + }; + let doCleanup = () => cleanups2.forEach((i) => i()); + return [utilities, doCleanup]; + } + function getDirectiveHandler(el, directive22) { + let noop = () => { + }; + let handler4 = directiveHandlers[directive22.type] || noop; + let [utilities, cleanup] = getElementBoundUtilities(el); + onAttributeRemoved(el, directive22.original, cleanup); + let fullHandler = () => { + if (el._x_ignore || el._x_ignoreSelf) + return; + handler4.inline && handler4.inline(el, directive22, utilities); + handler4 = handler4.bind(handler4, el, directive22, utilities); + isDeferringHandlers ? directiveHandlerStacks.get(currentHandlerStackKey).push(handler4) : handler4(); + }; + fullHandler.runCleanups = cleanup; + return fullHandler; + } + var startingWith = (subject, replacement) => ({ name, value }) => { + if (name.startsWith(subject)) + name = name.replace(subject, replacement); + return { name, value }; + }; + var into = (i) => i; + function toTransformedAttributes(callback = () => { + }) { + return ({ name, value }) => { + let { name: newName, value: newValue } = attributeTransformers.reduce((carry, transform) => { + return transform(carry); + }, { name, value }); + if (newName !== name) + callback(newName, name); + return { name: newName, value: newValue }; + }; + } + var attributeTransformers = []; + function mapAttributes(callback) { + attributeTransformers.push(callback); + } + function outNonAlpineAttributes({ name }) { + return alpineAttributeRegex().test(name); + } + var alpineAttributeRegex = () => new RegExp(`^${prefixAsString}([^:^.]+)\\b`); + function toParsedDirectives(transformedAttributeMap, originalAttributeOverride) { + return ({ name, value }) => { + let typeMatch = name.match(alpineAttributeRegex()); + let valueMatch = name.match(/:([a-zA-Z0-9\-_:]+)/); + let modifiers = name.match(/\.[^.\]]+(?=[^\]]*$)/g) || []; + let original = originalAttributeOverride || transformedAttributeMap[name] || name; + return { + type: typeMatch ? typeMatch[1] : null, + value: valueMatch ? valueMatch[1] : null, + modifiers: modifiers.map((i) => i.replace(".", "")), + expression: value, + original + }; + }; + } + var DEFAULT = "DEFAULT"; + var directiveOrder = [ + "ignore", + "ref", + "data", + "id", + "anchor", + "bind", + "init", + "for", + "model", + "modelable", + "transition", + "show", + "if", + DEFAULT, + "teleport" + ]; + function byPriority(a, b) { + let typeA = directiveOrder.indexOf(a.type) === -1 ? DEFAULT : a.type; + let typeB = directiveOrder.indexOf(b.type) === -1 ? DEFAULT : b.type; + return directiveOrder.indexOf(typeA) - directiveOrder.indexOf(typeB); + } + function dispatch3(el, name, detail = {}) { + el.dispatchEvent(new CustomEvent(name, { + detail, + bubbles: true, + composed: true, + cancelable: true + })); + } + function walk(el, callback) { + if (typeof ShadowRoot === "function" && el instanceof ShadowRoot) { + Array.from(el.children).forEach((el2) => walk(el2, callback)); + return; + } + let skip = false; + callback(el, () => skip = true); + if (skip) + return; + let node = el.firstElementChild; + while (node) { + walk(node, callback, false); + node = node.nextElementSibling; + } + } + function warn(message, ...args) { + console.warn(`Alpine Warning: ${message}`, ...args); + } + var started = false; + function start2() { + if (started) + warn("Alpine has already been initialized on this page. Calling Alpine.start() more than once can cause problems."); + started = true; + if (!document.body) + warn("Unable to initialize. Trying to load Alpine before `` is available. Did you forget to add `defer` in Alpine's `