From baddcfd906559cd8c5a5abf0840fbc2677375456 Mon Sep 17 00:00:00 2001 From: qwerty287 <80460567+qwerty287@users.noreply.github.com> Date: Sun, 25 Dec 2022 10:28:43 +0100 Subject: [PATCH] Create admin during installation, allow multiple admins (#1602) --- .../Pipes/Checks/AdminUserExistsCheck.php | 8 +- app/Actions/Settings/SetLogin.php | 37 ------ app/Actions/User/Notify.php | 3 +- app/Actions/User/ResetAdmin.php | 33 ------ app/Console/Commands/CreateUser.php | 69 ++++++++++++ app/Console/Commands/ResetAdmin.php | 46 +------- app/Console/Commands/UpdateUser.php | 64 +++++++++++ app/DTO/Rights/GlobalRightsDTO.php | 15 --- app/DTO/Rights/RootAlbumRightsDTO.php | 8 -- app/DTO/Rights/SettingsRightsDTO.php | 8 -- app/DTO/Rights/UserManagementRightsDTO.php | 8 -- app/DTO/Rights/UserRightsDTO.php | 8 -- .../AdminUserAlreadySetException.php | 13 +++ app/Exceptions/AdminUserRequiredException.php | 13 +++ app/Exceptions/Handler.php | 2 + .../Handlers/AdminSetterHandler.php | 69 ++++++++++++ .../Administration/DiagnosticsController.php | 3 +- .../Administration/SettingsController.php | 31 ----- .../Administration/UpdateController.php | 8 -- .../Administration/UsersController.php | 5 + .../Install/SetUpAdminController.php | 73 ++++++++++++ app/Http/Controllers/SessionController.php | 16 +-- .../WebAuthn/WebAuthnLoginController.php | 28 ++--- .../WebAuthn/WebAuthnRegisterController.php | 6 +- app/Http/Kernel.php | 3 + app/Http/Middleware/AdminUserStatus.php | 69 ++++++++++++ app/Http/Middleware/Checks/HasAdminUser.php | 29 +++++ app/Http/Redirections/ToAdminSetter.php | 27 +++++ .../Requests/Install/SetUpAdminRequest.php | 46 ++++++++ .../Requests/Legacy/SetAdminLoginRequest.php | 31 ----- .../Photo/SetPhotoDescriptionRequest.php | 2 +- app/Http/Requests/Settings/MigrateRequest.php | 4 +- app/Legacy/AdminAuthentication.php | 47 +------- app/Models/User.php | 28 +++-- app/Pipelines/AssertionValidator.php | 64 ----------- .../Pipes/CheckCredentialIsForUser.php | 84 -------------- app/Policies/SettingsPolicy.php | 3 +- .../2022_12_10_183251_increment_user_i_ds.php | 71 ++++++++++++ phpstan.neon | 1 + phpunit.xml | 3 + public/Lychee-front | 2 +- public/dist/frontend.css | 2 +- public/dist/frontend.js | 106 +----------------- public/installer/assets/css/style.css | 1 + resources/views/install/env.blade.php | 39 ++++--- resources/views/install/migrate.blade.php | 13 ++- resources/views/install/permissions.blade.php | 86 +++++++------- .../views/install/requirements.blade.php | 70 ++++++------ resources/views/install/setup-admin.blade.php | 39 +++++++ .../views/install/setup-success.blade.php | 14 +++ resources/views/install/view.blade.php | 104 +++++++++-------- routes/api.php | 8 +- routes/web-install.php | 9 ++ tests/Boot.php | 44 ++++++++ tests/Feature/AlbumTest.php | 44 ++++---- tests/Feature/ApiTokenTest.php | 10 +- tests/Feature/Base/BasePhotoTest.php | 2 +- tests/Feature/CommandsTest.php | 4 - tests/Feature/DiagnosticsTest.php | 2 +- tests/Feature/GeoDataTest.php | 4 +- tests/Feature/InstallTest.php | 56 ++++++++- tests/Feature/LegacyTest.php | 2 +- tests/Feature/LogsTest.php | 8 +- tests/Feature/NotificationTest.php | 6 +- tests/Feature/PhotosDownloadTest.php | 6 +- tests/Feature/PhotosOperationsTest.php | 6 +- tests/Feature/RSSTest.php | 2 +- tests/Feature/SettingsTest.php | 6 +- tests/Feature/SharingBasicTest.php | 14 ++- tests/Feature/Traits/RequiresEmptyUsers.php | 4 +- tests/Feature/UpdateTest.php | 6 +- tests/Feature/UsersTest.php | 55 +++------ tests/Feature/WebAuthTest.php | 6 +- 73 files changed, 1017 insertions(+), 849 deletions(-) delete mode 100644 app/Actions/Settings/SetLogin.php delete mode 100644 app/Actions/User/ResetAdmin.php create mode 100644 app/Console/Commands/CreateUser.php create mode 100644 app/Console/Commands/UpdateUser.php create mode 100644 app/Exceptions/AdminUserAlreadySetException.php create mode 100644 app/Exceptions/AdminUserRequiredException.php create mode 100644 app/Exceptions/Handlers/AdminSetterHandler.php create mode 100644 app/Http/Controllers/Install/SetUpAdminController.php create mode 100644 app/Http/Middleware/AdminUserStatus.php create mode 100644 app/Http/Middleware/Checks/HasAdminUser.php create mode 100644 app/Http/Redirections/ToAdminSetter.php create mode 100644 app/Http/Requests/Install/SetUpAdminRequest.php delete mode 100644 app/Http/Requests/Legacy/SetAdminLoginRequest.php delete mode 100644 app/Pipelines/AssertionValidator.php delete mode 100644 app/Pipelines/Pipes/CheckCredentialIsForUser.php create mode 100644 database/migrations/2022_12_10_183251_increment_user_i_ds.php create mode 100644 resources/views/install/setup-admin.blade.php create mode 100644 resources/views/install/setup-success.blade.php create mode 100644 tests/Boot.php diff --git a/app/Actions/Diagnostics/Pipes/Checks/AdminUserExistsCheck.php b/app/Actions/Diagnostics/Pipes/Checks/AdminUserExistsCheck.php index 901f25e9833..1c4a5589bcd 100644 --- a/app/Actions/Diagnostics/Pipes/Checks/AdminUserExistsCheck.php +++ b/app/Actions/Diagnostics/Pipes/Checks/AdminUserExistsCheck.php @@ -9,11 +9,11 @@ class AdminUserExistsCheck implements DiagnosticPipe { public function handle(array &$data, \Closure $next): array { - $admin = User::query()->find(0); - if ($admin === null) { - $data[] = 'Error: User Admin not found in database. Please run: "php artisan lychee:reset_admin"'; + $numberOfAdmin = User::query()->where('may_administrate', '=', true)->count(); + if ($numberOfAdmin === 0) { + $data[] = 'Error: User Admin not found in database. Please run: "php lychee:create_user {username} {password}"'; } return $next($data); } -} +} \ No newline at end of file diff --git a/app/Actions/Settings/SetLogin.php b/app/Actions/Settings/SetLogin.php deleted file mode 100644 index 5ab07155b0f..00000000000 --- a/app/Actions/Settings/SetLogin.php +++ /dev/null @@ -1,37 +0,0 @@ -findOrFail(0); - $adminUser->username = $username; - $adminUser->password = Hash::make($password); - $adminUser->may_edit_own_settings = true; - $adminUser->may_upload = true; - $adminUser->may_administrate = true; - $adminUser->save(); - - return $adminUser; - } -} diff --git a/app/Actions/User/Notify.php b/app/Actions/User/Notify.php index e1eee62c58d..06a69590639 100644 --- a/app/Actions/User/Notify.php +++ b/app/Actions/User/Notify.php @@ -10,7 +10,6 @@ use App\Notifications\PhotoAdded; use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Database\MultipleRecordsFoundException; -use Illuminate\Support\Collection; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Notification; @@ -35,7 +34,7 @@ public function do(Photo $photo): void } // Admin user is always notified - $users = new Collection([User::find(0)]); + $users = User::query()->where('may_administrate', '=', true)->get(); $album = $photo->album; if ($album !== null) { diff --git a/app/Actions/User/ResetAdmin.php b/app/Actions/User/ResetAdmin.php deleted file mode 100644 index d112a40430d..00000000000 --- a/app/Actions/User/ResetAdmin.php +++ /dev/null @@ -1,33 +0,0 @@ -findOrNew(0); - $user->incrementing = false; // disable auto-generation of ID - $user->id = 0; - $user->username = ''; - $user->password = ''; - $user->may_edit_own_settings = true; - $user->may_administrate = true; - $user->may_upload = true; - $user->save(); - } -} diff --git a/app/Console/Commands/CreateUser.php b/app/Console/Commands/CreateUser.php new file mode 100644 index 00000000000..100dca8cc33 --- /dev/null +++ b/app/Console/Commands/CreateUser.php @@ -0,0 +1,69 @@ +create = $create; + } + + /** + * Execute the console command. + * + * @return int + * + * @throws ExternalLycheeException + */ + public function handle(): int + { + $username = strval($this->argument('username')); + $password = strval($this->argument('password')); + + $count = User::query()->count(); + + $mayAdministrate = $count < 1 || $this->option('may-administrate') === true; + $mayEditOwnSettings = $mayAdministrate || $this->option('may-edit-own-settings') === true; + $mayUpload = $mayAdministrate || $this->option('may-upload') === true; + + $user = $this->create->do($username, $password, $mayUpload, $mayEditOwnSettings); + $user->may_administrate = $mayAdministrate; + $user->save(); + + $this->line(sprintf('Successfully created%s user %s ', $mayAdministrate ? ' admin' : '', $username)); + + return 0; + } +} diff --git a/app/Console/Commands/ResetAdmin.php b/app/Console/Commands/ResetAdmin.php index 9494e36da22..74f61b069f3 100644 --- a/app/Console/Commands/ResetAdmin.php +++ b/app/Console/Commands/ResetAdmin.php @@ -2,21 +2,10 @@ namespace App\Console\Commands; -use App\Actions\User\ResetAdmin as UserResetAdmin; -use App\Console\Commands\Utilities\Colorize; -use App\Contracts\Exceptions\ExternalLycheeException; -use App\Exceptions\Internal\QueryBuilderException; -use App\Exceptions\UnexpectedException; use Illuminate\Console\Command; -use Symfony\Component\Console\Exception\ExceptionInterface as SymfonyConsoleException; class ResetAdmin extends Command { - /** - * Add color to the command line output. - */ - private Colorize $col; - /** * The name and signature of the console command. * @@ -29,44 +18,17 @@ class ResetAdmin extends Command * * @var string */ - protected $description = 'Reset Login and Password of the admin user.'; - - /** - * Access to the reset admin function. - * - * @var UserResetAdmin - */ - protected UserResetAdmin $userResetAdmin; - - /** - * Create a new command instance. - * - * @throws SymfonyConsoleException - */ - public function __construct(Colorize $colorize, UserResetAdmin $userResetAdmin) - { - parent::__construct(); - $this->col = $colorize; - $this->userResetAdmin = $userResetAdmin; - } + protected $description = 'Reset Login and Password of the admin user (deprecated).'; /** * Execute the console command. * * @return int - * - * @throws ExternalLycheeException */ public function handle(): int { - try { - $this->userResetAdmin->do(); - - $this->line($this->col->yellow('Admin username and password reset.')); + $this->line('Command deprecated. Instead, use php artisan lychee:update_user or lychee:create_user {username} {password} --may_administrate.'); - return 0; - } catch (QueryBuilderException $e) { - throw new UnexpectedException($e); - } + return 1; } -} +} \ No newline at end of file diff --git a/app/Console/Commands/UpdateUser.php b/app/Console/Commands/UpdateUser.php new file mode 100644 index 00000000000..844de6f442d --- /dev/null +++ b/app/Console/Commands/UpdateUser.php @@ -0,0 +1,64 @@ +argument('username')); + + /** @var User|null $user */ + $user = User::query()->where('username', '=', $username)->first(); + + if ($user === null) { + $this->error('user not found'); + + return 1; + } + + $password = strval($this->argument('password')); + + if ($password !== '') { + $user->password = Hash::make($password); + $user->save(); + + $this->line('Successfully updated user ' . $username); + + return 0; + } + + $this->error('wrong password'); + + return 1; + } +} diff --git a/app/DTO/Rights/GlobalRightsDTO.php b/app/DTO/Rights/GlobalRightsDTO.php index c63e3bd4c3b..018a169777a 100644 --- a/app/DTO/Rights/GlobalRightsDTO.php +++ b/app/DTO/Rights/GlobalRightsDTO.php @@ -31,19 +31,4 @@ public static function ofCurrentUser(): self user: UserRightsDTO::ofCurrentUser(), ); } - - /** - * Create from no admin registerd. - * - * @return self - */ - public static function ofUnregisteredAdmin(): self - { - return new self( - root_album: RootAlbumRightsDTO::ofUnregisteredAdmin(), - settings: SettingsRightsDTO::ofUnregisteredAdmin(), - user_management: UserManagementRightsDTO::ofUnregisteredAdmin(), - user: UserRightsDTO::ofUnregisteredAdmin(), - ); - } } \ No newline at end of file diff --git a/app/DTO/Rights/RootAlbumRightsDTO.php b/app/DTO/Rights/RootAlbumRightsDTO.php index ec18682e08b..500a644f641 100644 --- a/app/DTO/Rights/RootAlbumRightsDTO.php +++ b/app/DTO/Rights/RootAlbumRightsDTO.php @@ -32,12 +32,4 @@ public static function ofCurrentUser(): self can_import_from_server: Gate::check(AlbumPolicy::CAN_IMPORT_FROM_SERVER, [AbstractAlbum::class]), ); } - - /** - * @return self - */ - public static function ofUnregisteredAdmin(): self - { - return new self(true, true, true); - } } \ No newline at end of file diff --git a/app/DTO/Rights/SettingsRightsDTO.php b/app/DTO/Rights/SettingsRightsDTO.php index 44bf15680c5..406ba1cf2e7 100644 --- a/app/DTO/Rights/SettingsRightsDTO.php +++ b/app/DTO/Rights/SettingsRightsDTO.php @@ -37,12 +37,4 @@ public static function ofCurrentUser(): self can_update: Gate::check(SettingsPolicy::CAN_UPDATE, [Configs::class]), ); } - - /** - * @return self - */ - public static function ofUnregisteredAdmin(): self - { - return new self(true, true, true, true, true); - } } \ No newline at end of file diff --git a/app/DTO/Rights/UserManagementRightsDTO.php b/app/DTO/Rights/UserManagementRightsDTO.php index c14521787d3..6c56d88e90e 100644 --- a/app/DTO/Rights/UserManagementRightsDTO.php +++ b/app/DTO/Rights/UserManagementRightsDTO.php @@ -34,12 +34,4 @@ public static function ofCurrentUser(): self can_delete: Gate::check(UserPolicy::CAN_CREATE_OR_EDIT_OR_DELETE, [User::class]) ); } - - /** - * @return self - */ - public static function ofUnregisteredAdmin(): self - { - return new self(true, true, true, true); - } } diff --git a/app/DTO/Rights/UserRightsDTO.php b/app/DTO/Rights/UserRightsDTO.php index 4e10ae9cccd..072d455c9b3 100644 --- a/app/DTO/Rights/UserRightsDTO.php +++ b/app/DTO/Rights/UserRightsDTO.php @@ -30,12 +30,4 @@ public static function ofCurrentUser(): self can_use_2fa: Gate::check(UserPolicy::CAN_USE_2FA, [User::class]), ); } - - /** - * @return self - */ - public static function ofUnregisteredAdmin(): self - { - return new self(true, true); - } } diff --git a/app/Exceptions/AdminUserAlreadySetException.php b/app/Exceptions/AdminUserAlreadySetException.php new file mode 100644 index 00000000000..71cd7f5a1dd --- /dev/null +++ b/app/Exceptions/AdminUserAlreadySetException.php @@ -0,0 +1,13 @@ +toAdminSetter = true; + + return true; + } + if ($e instanceof AdminUserAlreadySetException) { + $this->toAdminSetter = false; + + return true; + } + } while ($e = $e->getPrevious()); + + return false; + } + + /** + * {@inheritDoc} + */ + public function renderHttpException(SymfonyResponse $defaultResponse, HttpException $e): SymfonyResponse + { + try { + if ($this->toAdminSetter) { + $redirectResponse = ToAdminSetter::go(); + $contentType = $defaultResponse->headers->get('Content-Type'); + if ($contentType !== null && $contentType !== '') { + $redirectResponse->headers->set('Content-Type', $contentType); + $content = $defaultResponse->getContent(); + $redirectResponse->setContent($content !== false ? $content : null); + } + + return $redirectResponse; + } else { + return $defaultResponse; + } + } catch (\Throwable) { + return $defaultResponse; + } + } +} diff --git a/app/Http/Controllers/Administration/DiagnosticsController.php b/app/Http/Controllers/Administration/DiagnosticsController.php index 5986cb24055..48d4c97c109 100644 --- a/app/Http/Controllers/Administration/DiagnosticsController.php +++ b/app/Http/Controllers/Administration/DiagnosticsController.php @@ -11,7 +11,6 @@ use App\DTO\DiagnosticInfo; use App\Exceptions\Internal\FrameworkException; use App\Exceptions\ModelDBException; -use App\Legacy\AdminAuthentication; use App\Models\Configs; use App\Policies\SettingsPolicy; use Carbon\Exceptions\InvalidTimeZoneException; @@ -29,7 +28,7 @@ class DiagnosticsController extends Controller */ private function isAuthorized(): bool { - return Gate::check(SettingsPolicy::CAN_SEE_DIAGNOSTICS, Configs::class) || AdminAuthentication::isAdminNotRegistered(); + return Gate::check(SettingsPolicy::CAN_SEE_DIAGNOSTICS, Configs::class); } /** diff --git a/app/Http/Controllers/Administration/SettingsController.php b/app/Http/Controllers/Administration/SettingsController.php index b0dc77f9e4d..041148a51b5 100644 --- a/app/Http/Controllers/Administration/SettingsController.php +++ b/app/Http/Controllers/Administration/SettingsController.php @@ -2,12 +2,9 @@ namespace App\Http\Controllers\Administration; -use App\Actions\Settings\SetLogin; -use App\Contracts\Exceptions\LycheeException; use App\Exceptions\InsufficientFilesystemPermissions; use App\Exceptions\Internal\InvalidConfigOption; use App\Exceptions\Internal\QueryBuilderException; -use App\Http\Requests\Legacy\SetAdminLoginRequest; use App\Http\Requests\Settings\GetSetAllSettingsRequest; use App\Http\Requests\Settings\SetCSSSettingRequest; use App\Http\Requests\Settings\SetDefaultLicenseSettingRequest; @@ -29,40 +26,12 @@ use App\Models\Configs; use App\Models\User; use Illuminate\Database\Eloquent\Collection; -use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Routing\Controller; -use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Storage; use Symfony\Component\HttpFoundation\Exception\BadRequestException; class SettingsController extends Controller { - /** - * Set the Login information for the admin user (id = 0) - * when the latter is not initialized. - * - * @param SetAdminLoginRequest $request - * @param SetLogin $setLogin - * - * @return User - * - * @throws LycheeException - * @throws ModelNotFoundException - */ - public function setLogin(SetAdminLoginRequest $request, SetLogin $setLogin): User - { - $adminUser = $setLogin->do( - $request->username(), - $request->password() - ); - // Update the session with the new credentials of the user. - // Otherwise, the session is out-of-sync and falsely assumes the user - // to be unauthenticated upon the next request. - Auth::login($adminUser); - - return $adminUser; - } - /** * Define the default sorting type. * diff --git a/app/Http/Controllers/Administration/UpdateController.php b/app/Http/Controllers/Administration/UpdateController.php index 88b5b6ea80b..2462464717b 100644 --- a/app/Http/Controllers/Administration/UpdateController.php +++ b/app/Http/Controllers/Administration/UpdateController.php @@ -8,12 +8,9 @@ use App\Exceptions\VersionControlException; use App\Http\Requests\Settings\MigrateRequest; use App\Http\Requests\Settings\UpdateRequest; -use App\Legacy\AdminAuthentication; use App\Metadata\Versions\GitHubVersion; use Illuminate\Http\Response; use Illuminate\Routing\Controller; -use Illuminate\Support\Facades\Auth; -use Illuminate\Support\Facades\Session; use Illuminate\View\View; /** @@ -139,11 +136,6 @@ public function migrate(MigrateRequest $request): View|Response $output = []; $output = $this->applyUpdate->run(); - if (AdminAuthentication::isAdminNotRegistered()) { - Auth::logout(); - Session::flush(); - } - return view('update.results', ['code' => '200', 'message' => 'Migration results', 'output' => $output]); } } diff --git a/app/Http/Controllers/Administration/UsersController.php b/app/Http/Controllers/Administration/UsersController.php index 1d402e9c4ec..feef7395dda 100644 --- a/app/Http/Controllers/Administration/UsersController.php +++ b/app/Http/Controllers/Administration/UsersController.php @@ -9,6 +9,7 @@ use App\Exceptions\InvalidPropertyException; use App\Exceptions\ModelDBException; use App\Exceptions\UnauthenticatedException; +use App\Exceptions\UnauthorizedException; use App\Http\Requests\Users\AddUserRequest; use App\Http\Requests\Users\DeleteUserRequest; use App\Http\Requests\Users\ListUsersRequest; @@ -17,6 +18,7 @@ use Carbon\Exceptions\InvalidFormatException; use Illuminate\Routing\Controller; use Illuminate\Support\Collection; +use Illuminate\Support\Facades\Auth; class UsersController extends Controller { @@ -71,6 +73,9 @@ public function save(SetUserSettingsRequest $request, Save $save): void */ public function delete(DeleteUserRequest $request): void { + if ($request->user2()->id === Auth::id()) { + throw new UnauthorizedException('You are not allowed to delete yourself'); + } $request->user2()->delete(); } diff --git a/app/Http/Controllers/Install/SetUpAdminController.php b/app/Http/Controllers/Install/SetUpAdminController.php new file mode 100644 index 00000000000..8076915d1a8 --- /dev/null +++ b/app/Http/Controllers/Install/SetUpAdminController.php @@ -0,0 +1,73 @@ + 'Lychee-installer', + 'step' => 5, + ]); + } + + /** + * Set up the admin user. + * Called on POST request. + * + * @return View + * + * @throws FrameworkException + */ + public function create(SetUpAdminRequest $request): View + { + $error = ''; + try { + $user = new User(); + $user->may_upload = true; + $user->may_edit_own_settings = true; + $user->may_administrate = true; + $user->username = $request->username(); + $user->password = Hash::make($request->password()); + $user->save(); + } catch (\Throwable $e) { + $error = $e->getMessage(); + $error .= '
' . $e->getPrevious()->getMessage(); + } + + try { + if ($error === '') { + return view('install.setup-success', [ + 'title' => 'Lychee-setup-admin', + 'step' => 5, + ]); + } + + return view('install.setup-admin', [ + 'title' => 'Lychee-setup-admin', + 'step' => 5, + 'error' => $error, + ]); + } catch (BindingResolutionException $e) { + throw new FrameworkException('Laravel\'s view component', $e); + } + } +} diff --git a/app/Http/Controllers/SessionController.php b/app/Http/Controllers/SessionController.php index eb6541dc6c1..ed8e4ddc1c3 100644 --- a/app/Http/Controllers/SessionController.php +++ b/app/Http/Controllers/SessionController.php @@ -65,18 +65,10 @@ public function init(): array // Return settings $return = []; - if (AdminAuthentication::loginAsAdminIfNotRegistered()) { - // TODO: Remove this legacy stuff after creating the admin user has become part of the installation routine. - // If the session is unauthenticated ('user' === null), but grants admin rights nonetheless, - // the front-end shows the dialog to create an admin account. - $return['user'] = null; - $return['rights'] = GlobalRightsDTO::ofUnregisteredAdmin(); - } else { - /** @var User|null $user */ - $user = Auth::user(); - $return['user'] = $user?->toArray(); - $return['rights'] = GlobalRightsDTO::ofCurrentUser(); - } + /** @var User|null $user */ + $user = Auth::user(); + $return['user'] = $user?->toArray(); + $return['rights'] = GlobalRightsDTO::ofCurrentUser(); // Load configuration settings acc. to authentication status if (Gate::check(SettingsPolicy::CAN_EDIT, [Configs::class])) { diff --git a/app/Http/Controllers/WebAuthn/WebAuthnLoginController.php b/app/Http/Controllers/WebAuthn/WebAuthnLoginController.php index 6d1808fcfbd..e5200049121 100644 --- a/app/Http/Controllers/WebAuthn/WebAuthnLoginController.php +++ b/app/Http/Controllers/WebAuthn/WebAuthnLoginController.php @@ -2,14 +2,12 @@ namespace App\Http\Controllers\WebAuthn; -use App\Exceptions\UnauthenticatedException; -use App\Models\User; -use App\Pipelines\AssertionValidator; +use Illuminate\Contracts\Container\BindingResolutionException; use Illuminate\Contracts\Support\Responsable; -use Illuminate\Support\Facades\Auth; -use Laragear\WebAuthn\Assertion\Validator\AssertionValidation; use Laragear\WebAuthn\Http\Requests\AssertedRequest; use Laragear\WebAuthn\Http\Requests\AssertionRequest; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Exception\HttpException; class WebAuthnLoginController { @@ -18,7 +16,9 @@ class WebAuthnLoginController * * @param AssertionRequest $request * - * @return \Illuminate\Contracts\Support\Responsable + * @return Responsable + * + * @throws BindingResolutionException */ public function options(AssertionRequest $request): Responsable { @@ -32,19 +32,11 @@ public function options(AssertionRequest $request): Responsable * * @return void */ - public function login(AssertedRequest $request, AssertionValidator $assertion): void + public function login(AssertedRequest $request): void { - $credential = $assertion - ->send(new AssertionValidation($request, User::findOrFail(0))) - ->thenReturn() - ->credential; - - if ($credential === null) { - throw new UnauthenticatedException('Invalid credentials'); + $user = $request->login(); + if ($user === null) { + throw new HttpException(Response::HTTP_UNPROCESSABLE_ENTITY); } - - /** @var \Illuminate\Contracts\Auth\Authenticatable $authenticatable */ - $authenticatable = $credential->authenticatable; - Auth::login($authenticatable); } } diff --git a/app/Http/Controllers/WebAuthn/WebAuthnRegisterController.php b/app/Http/Controllers/WebAuthn/WebAuthnRegisterController.php index c2bb1ebd77d..1ef23567250 100644 --- a/app/Http/Controllers/WebAuthn/WebAuthnRegisterController.php +++ b/app/Http/Controllers/WebAuthn/WebAuthnRegisterController.php @@ -13,9 +13,9 @@ class WebAuthnRegisterController /** * Returns a challenge to be verified by the user device. * - * @param \Laragear\WebAuthn\Http\Requests\AttestationRequest $request + * @param AttestationRequest $request * - * @return \Illuminate\Contracts\Support\Responsable + * @return Responsable */ public function options(AttestationRequest $request): Responsable { @@ -29,7 +29,7 @@ public function options(AttestationRequest $request): Responsable /** * Registers a device for further WebAuthn authentication. * - * @param \Laragear\WebAuthn\Http\Requests\AttestedRequest $request + * @param AttestedRequest $request * * @return void */ diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 65ba3d70519..725e6827007 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -33,6 +33,7 @@ class Kernel extends HttpKernel protected $middlewareGroups = [ 'web' => [ 'installation:complete', + 'admin_user:set', 'accept_content_type:html', \Illuminate\Cookie\Middleware\EncryptCookies::class, \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, @@ -61,6 +62,7 @@ class Kernel extends HttpKernel 'api' => [ 'installation:complete', + 'admin_user:set', 'accept_content_type:json', 'content_type:json', \Illuminate\Cookie\Middleware\EncryptCookies::class, @@ -82,6 +84,7 @@ class Kernel extends HttpKernel */ protected $routeMiddleware = [ 'installation' => \App\Http\Middleware\InstallationStatus::class, + 'admin_user' => \App\Http\Middleware\AdminUserStatus::class, 'migration' => \App\Http\Middleware\MigrationStatus::class, 'local_storage' => \App\Http\Middleware\LocalStorageOnly::class, 'content_type' => \App\Http\Middleware\ContentType::class, diff --git a/app/Http/Middleware/AdminUserStatus.php b/app/Http/Middleware/AdminUserStatus.php new file mode 100644 index 00000000000..c29a94b5db6 --- /dev/null +++ b/app/Http/Middleware/AdminUserStatus.php @@ -0,0 +1,69 @@ +hasAdminUser = $hasAdminUser; + } + + /** + * Handles an incoming request. + * + * @param Request $request the incoming request to serve + * @param \Closure $next the next operation to be applied to the + * request + * @param string $requiredStatus the required installation status; either + * {@link self::SET} or + * {@link self::UNSET} + * + * @return mixed + * + * @throws LycheeException + */ + public function handle(Request $request, \Closure $next, string $requiredStatus): mixed + { + if ($requiredStatus === self::SET) { + if ($this->hasAdminUser->assert()) { + return $next($request); + } else { + throw new AdminUserRequiredException(); + } + } elseif ($requiredStatus === self::UNSET) { + if ($this->hasAdminUser->assert()) { + throw new AdminUserAlreadySetException(); + } else { + return $next($request); + } + } else { + throw new LycheeInvalidArgumentException('$requiredStatus must either be "' . self::SET . '" or "' . self::UNSET . '"'); + } + } +} diff --git a/app/Http/Middleware/Checks/HasAdminUser.php b/app/Http/Middleware/Checks/HasAdminUser.php new file mode 100644 index 00000000000..23218e1ce2d --- /dev/null +++ b/app/Http/Middleware/Checks/HasAdminUser.php @@ -0,0 +1,29 @@ +where('may_administrate', '=', true)->count() > 0; + } catch (QueryException $e) { + throw $e; + } catch (BindingResolutionException|NotFoundExceptionInterface|ContainerExceptionInterface $e) { + throw new FrameworkException('Laravel\'s container component', $e); + } + } +} \ No newline at end of file diff --git a/app/Http/Redirections/ToAdminSetter.php b/app/Http/Redirections/ToAdminSetter.php new file mode 100644 index 00000000000..b532dce5247 --- /dev/null +++ b/app/Http/Redirections/ToAdminSetter.php @@ -0,0 +1,27 @@ + 'no-cache, must-revalidate', + ]); + } +} diff --git a/app/Http/Requests/Install/SetUpAdminRequest.php b/app/Http/Requests/Install/SetUpAdminRequest.php new file mode 100644 index 00000000000..ec1c5ef2734 --- /dev/null +++ b/app/Http/Requests/Install/SetUpAdminRequest.php @@ -0,0 +1,46 @@ + ['required', new UsernameRule()], + RequestAttribute::PASSWORD_ATTRIBUTE => ['required', 'confirmed', new PasswordRule(false)], + ]; + } + + /** + * This Request is only available if the application is not installed yet. + * Thus, there's no authorization check here. + * + * @return bool + */ + public function authorize(): bool + { + return true; + } + + public function passedValidation() + { + $values = $this->validated(); + $this->username = $values[RequestAttribute::USERNAME_ATTRIBUTE]; + $this->password = $values[RequestAttribute::PASSWORD_ATTRIBUTE]; + } +} diff --git a/app/Http/Requests/Legacy/SetAdminLoginRequest.php b/app/Http/Requests/Legacy/SetAdminLoginRequest.php deleted file mode 100644 index 29b900ca099..00000000000 --- a/app/Http/Requests/Legacy/SetAdminLoginRequest.php +++ /dev/null @@ -1,31 +0,0 @@ - ['required', new RandomIDRule(false)], - RequestAttribute::DESCRIPTION_ATTRIBUTE => ['required', new DescriptionRule()], + RequestAttribute::DESCRIPTION_ATTRIBUTE => ['present', new DescriptionRule()], ]; } diff --git a/app/Http/Requests/Settings/MigrateRequest.php b/app/Http/Requests/Settings/MigrateRequest.php index cd6f024790b..2c969a5c9d1 100644 --- a/app/Http/Requests/Settings/MigrateRequest.php +++ b/app/Http/Requests/Settings/MigrateRequest.php @@ -37,10 +37,8 @@ public function authorize(): bool // 3. Attempt to login as an admin user using the legacy method: hash(username) + hash(password). // 4. Try to login the normal way. // - // TODO: Step 2 will become unnecessary once admin registration has become part of the installation routine; after that the case that no admin is registered cannot occur anymore - // TODO: Step 3 will become unnecessary once the admin user of any existing installation has at least logged in once and the admin user has therewith migrated to use a non-hashed user name + // TODO: Step 2 will become unnecessary once the admin user of any existing installation has at least logged in once and the admin user has therewith migrated to use a non-hashed user name $isLoggedIn = Auth::check(); - $isLoggedIn = $isLoggedIn || AdminAuthentication::loginAsAdminIfNotRegistered(); $isLoggedIn = $isLoggedIn || AdminAuthentication::loginAsAdmin($this->username(), $this->password(), $this->ip()); $isLoggedIn = $isLoggedIn || Auth::attempt(['username' => $this->username(), 'password' => $this->password()]); diff --git a/app/Legacy/AdminAuthentication.php b/app/Legacy/AdminAuthentication.php index c8fcf08f7b7..0b8b57b311e 100644 --- a/app/Legacy/AdminAuthentication.php +++ b/app/Legacy/AdminAuthentication.php @@ -2,8 +2,6 @@ namespace App\Legacy; -use App\Actions\User\ResetAdmin; -use App\Exceptions\ModelDBException; use App\Models\Logs; use App\Models\User; use Illuminate\Support\Facades\Auth; @@ -27,7 +25,7 @@ class AdminAuthentication public static function loginAsAdmin(string $username, string $password, string $ip): bool { /** @var User|null $adminUser */ - $adminUser = User::query()->find(0); + $adminUser = User::query()->find(1); // Admin User exists, so we check against it. if ($adminUser !== null && Hash::check($username, $adminUser->username) && Hash::check($password, $adminUser->password)) { @@ -43,47 +41,4 @@ public static function loginAsAdmin(string $username, string $password, string $ return false; } - - /** - * Checks whether the admin is unconfigured. - * The method is not side-effect free. - * If the admin user happens to not exist at all, the method creates an unconfigured admin. - * - * @return bool - * - * @throws ModelDBException - */ - public static function isAdminNotRegistered(): bool - { - /** @var User|null $adminUser */ - $adminUser = User::query()->find(0); - if ($adminUser !== null) { - return $adminUser->password === '' || $adminUser->username === ''; - } - (new ResetAdmin())->do(); - - return true; - } - - /** - * TODO: Once the admin user registration is moved to the installation phase this method can finally be removed. - * - * Login as admin temporarily when unconfigured. - * - * @return bool true if successful - * - * @throws ModelDBException - */ - public static function loginAsAdminIfNotRegistered(): bool - { - if (self::isAdminNotRegistered()) { - /** @var User|null $adminUser */ - $adminUser = User::query()->find(0); - Auth::login($adminUser); - - return true; - } - - return false; - } } \ No newline at end of file diff --git a/app/Models/User.php b/app/Models/User.php index 422745e1fdb..c8180bffd58 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -18,6 +18,7 @@ use Illuminate\Support\Carbon; use Illuminate\Support\Facades\Auth; use Laragear\WebAuthn\Contracts\WebAuthnAuthenticatable; +use Laragear\WebAuthn\Models\WebAuthnCredential; use Laragear\WebAuthn\WebAuthnAuthentication; /** @@ -169,22 +170,31 @@ public function getNameAttribute(): string */ public function delete(): bool { - $now = Carbon::now(); - $newOwnerID = Auth::id() ?? throw new UnauthenticatedException(); - /** @var HasMany[] $ownershipRelations */ $ownershipRelations = [$this->photos(), $this->albums()]; + $hasAny = false; foreach ($ownershipRelations as $relation) { - // We must also update the `updated_at` column of the related - // models in case clients have cached these models. - $relation->update([ - $relation->getForeignKeyName() => $newOwnerID, - $relation->getRelated()->getUpdatedAtColumn() => $relation->getRelated()->fromDateTime($now), - ]); + $hasAny = $hasAny || $relation->count() > 0; + } + + if ($hasAny) { + // only try update relations if there are any to allow deleting users from migrations (relations are moved before deleting) + $now = Carbon::now(); + $newOwnerID = Auth::id() ?? throw new UnauthenticatedException(); + + foreach ($ownershipRelations as $relation) { + // We must also update the `updated_at` column of the related + // models in case clients have cached these models. + $relation->update([ + $relation->getForeignKeyName() => $newOwnerID, + $relation->getRelated()->getUpdatedAtColumn() => $relation->getRelated()->fromDateTime($now), + ]); + } } $this->shared()->delete(); + WebAuthnCredential::where('authenticatable_id', '=', $this->id)->delete(); return $this->parentDelete(); } diff --git a/app/Pipelines/AssertionValidator.php b/app/Pipelines/AssertionValidator.php deleted file mode 100644 index 4c732467e61..00000000000 --- a/app/Pipelines/AssertionValidator.php +++ /dev/null @@ -1,64 +0,0 @@ -user !== null) { - $this->validateUser($validation); - - if ($validation->request->json('response.userHandle') !== null) { - $this->validateId($validation); - } - } else { - $this->validateId($validation); - } - - return $next($validation); - } - - /** - * Validate the user owns the Credential if it already exists in the validation procedure. - * - * @param \Laragear\WebAuthn\Assertion\Validator\AssertionValidation $validation - * - * @return void - */ - protected function validateUser(AssertionValidation $validation): void - { - // @phpstan-ignore-next-line - if ($validation->user->id !== $validation->credential?->authenticatable?->id) { - throw AssertionException::make('User is not owner of the stored credential.'); - } - } - - /** - * Validate the user ID of the response. - * - * @param \Laragear\WebAuthn\Assertion\Validator\AssertionValidation $validation - * - * @return void - */ - protected function validateId(AssertionValidation $validation): void - { - $handle = $validation->request->json('response.userHandle'); - - if ($handle === null || !\hash_equals(Uuid::fromString($validation->credential->user_id)->getHex()->toString(), $handle)) { - throw AssertionException::make('User ID is not owner of the stored credential.'); - } - } -} diff --git a/app/Policies/SettingsPolicy.php b/app/Policies/SettingsPolicy.php index 91e2051af0b..22d53cc06db 100644 --- a/app/Policies/SettingsPolicy.php +++ b/app/Policies/SettingsPolicy.php @@ -2,7 +2,6 @@ namespace App\Policies; -use App\Legacy\AdminAuthentication; use App\Models\User; /** @@ -39,7 +38,7 @@ public function canEdit(User $user): bool */ public function canSeeLogs(?User $user): bool { - return AdminAuthentication::isAdminNotRegistered(); + return $user?->may_administrate === true; } /** diff --git a/database/migrations/2022_12_10_183251_increment_user_i_ds.php b/database/migrations/2022_12_10_183251_increment_user_i_ds.php new file mode 100644 index 00000000000..ae5cd694e4c --- /dev/null +++ b/database/migrations/2022_12_10_183251_increment_user_i_ds.php @@ -0,0 +1,71 @@ +find(0); + if ($admin !== null && ($admin->username === '' || $admin->password === '')) { + // The admin user (id 0) has never set a username and password, so we remove it. + // This should only happen on a completely new installation where the admin user is created by the + // MigrateAdminUser migration and the user has never logged in. + DB::table('users')->where('id', '=', 0)->delete(); + } + /** @var App\Models\User $user */ + foreach (DB::table('users')->orderByDesc('id')->get() as $user) { + $oldID = $user->id; + $newID = $oldID + 1; + DB::table('users')->where('id', '=', $oldID)->update(['id' => $newID]); + // update other columns referencing user ID + DB::table('base_albums')->where('owner_id', '=', $oldID)->update(['owner_id' => $newID]); + DB::table('photos')->where('owner_id', '=', $oldID)->update(['owner_id' => $newID]); + DB::table('user_base_album')->where('user_id', '=', $oldID)->update(['user_id' => $newID]); + DB::table('webauthn_credentials')->where('authenticatable_id', '=', $oldID)->update(['authenticatable_id' => $newID]); + DB::table('users')->delete($oldID); + } + if (Schema::connection(null)->getConnection()->getDriverName() === 'pgsql' && DB::table('users')->count() > 0) { + // when using PostgreSQL, the new IDs are not updated after incrementing. Thus, we need to reset the index to the greatest ID + 1 + // the sequence is called `users_id_seq1` + /** @var App\Models\User $lastUser */ + $lastUser = DB::table('users')->orderByDesc('id')->first(); + DB::statement('ALTER SEQUENCE users_id_seq1 RESTART WITH ' . strval($lastUser->id + 1)); + } + Schema::enableForeignKeyConstraints(); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down(): void + { + Schema::disableForeignKeyConstraints(); + /** @var App\Models\User $user */ + foreach (User::query()->orderBy('id')->get() as $user) { + $oldID = $user->id; + $newID = $oldID - 1; + $user->id = $newID; + $user->incrementing = false; + $user->save(); + // update other columns referencing user ID + DB::table('base_albums')->where('owner_id', '=', $oldID)->update(['owner_id' => $newID]); + DB::table('photos')->where('owner_id', '=', $oldID)->update(['owner_id' => $newID]); + DB::table('user_base_album')->where('user_id', '=', $oldID)->update(['user_id' => $newID]); + DB::table('webauthn_credentials')->where('authenticatable_id', '=', $oldID)->update(['authenticatable_id' => $newID]); + DB::table('users')->delete($oldID); + } + Schema::enableForeignKeyConstraints(); + } +}; diff --git a/phpstan.neon b/phpstan.neon index 2f43b945074..6448ddc1014 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -34,6 +34,7 @@ parameters: - '#Dynamic call to static method (Illuminate\\Database\\Query\\Builder|Illuminate\\Database\\Eloquent\\(Builder|Relations\\.*)|App\\Models\\Extensions\\FixedQueryBuilder)(<.*>)?::without\(\)#' - '#Dynamic call to static method (Illuminate\\Database\\Query\\Builder|Illuminate\\Database\\Eloquent\\(Builder|Relations\\.*)|App\\Models\\Extensions\\FixedQueryBuilder)(<.*>)?::count\(\).#' - '#Dynamic call to static method (Illuminate\\Database\\Query\\Builder|Illuminate\\Database\\Eloquent\\(Builder|Relations\\.*)|App\\Models\\Extensions\\FixedQueryBuilder)(<.*>)?::update\(\).#' + - '#Dynamic call to static method App\\Models\\Extensions\\FixedQueryBuilder<.*>::orderByDesc\(\).#' - '#Call to an undefined method Illuminate\\Database\\Eloquent\\.*::update\(\)#' - '#Call to an undefined method Illuminate\\Database\\Eloquent\\.*::with(Only)?\(\)#' - '#Call to private method latest\(\) of parent class Illuminate\\Database\\Eloquent\\Relations\\HasMany#' diff --git a/phpunit.xml b/phpunit.xml index 702f33d502b..8d28b721021 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -25,6 +25,9 @@ ./tests/Feature + + + diff --git a/public/Lychee-front b/public/Lychee-front index 133271f7213..e5b1b6708ef 160000 --- a/public/Lychee-front +++ b/public/Lychee-front @@ -1 +1 @@ -Subproject commit 133271f7213e551dde3490f0f6bc10340d1a9a60 +Subproject commit e5b1b6708ef46d06dc68d2f1250a6c94081432df diff --git a/public/dist/frontend.css b/public/dist/frontend.css index d029999aad7..d0599279e7d 100644 --- a/public/dist/frontend.css +++ b/public/dist/frontend.css @@ -1 +1 @@ -@charset "UTF-8";@-webkit-keyframes basicModal__fadeIn{0%{opacity:0}100%{opacity:1}}@keyframes basicModal__fadeIn{0%{opacity:0}100%{opacity:1}}@-webkit-keyframes basicModal__fadeOut{0%{opacity:1}100%{opacity:0}}@keyframes basicModal__fadeOut{0%{opacity:1}100%{opacity:0}}@-webkit-keyframes basicModal__moveUpFade{0%{-webkit-transform:translateY(80px);transform:translateY(80px)}100%{-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes basicModal__moveUpFade{0%{-webkit-transform:translateY(80px);transform:translateY(80px)}100%{-webkit-transform:translateY(0);transform:translateY(0)}}@-webkit-keyframes basicModal__shake{0%,100%{-webkit-transform:translateX(0);transform:translateX(0)}20%,60%{-webkit-transform:translateX(-10px);transform:translateX(-10px)}40%,80%{-webkit-transform:translateX(10px);transform:translateX(10px)}}@keyframes basicModal__shake{0%,100%{-webkit-transform:translateX(0);transform:translateX(0)}20%,60%{-webkit-transform:translateX(-10px);transform:translateX(-10px)}40%,80%{-webkit-transform:translateX(10px);transform:translateX(10px)}}.basicModalContainer{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;position:fixed;width:100%;height:100%;top:0;left:0;background-color:rgba(0,0,0,.4);z-index:1000;-webkit-box-sizing:border-box;box-sizing:border-box}.basicModalContainer *,.basicModalContainer :after,.basicModalContainer :before{-webkit-box-sizing:border-box;box-sizing:border-box}.basicModalContainer--fadeIn{-webkit-animation:.3s cubic-bezier(.51,.92,.24,1.15) basicModal__fadeIn;animation:.3s cubic-bezier(.51,.92,.24,1.15) basicModal__fadeIn}.basicModalContainer--fadeOut{-webkit-animation:.3s cubic-bezier(.51,.92,.24,1.15) basicModal__fadeOut;animation:.3s cubic-bezier(.51,.92,.24,1.15) basicModal__fadeOut}.basicModalContainer--fadeIn .basicModal--fadeIn{-webkit-animation:.3s cubic-bezier(.51,.92,.24,1.15) basicModal__moveUpFade;animation:.3s cubic-bezier(.51,.92,.24,1.15) basicModal__moveUpFade}.basicModalContainer--fadeIn .basicModal--shake{-webkit-animation:.3s cubic-bezier(.51,.92,.24,1.15) basicModal__shake;animation:.3s cubic-bezier(.51,.92,.24,1.15) basicModal__shake}.basicModal{position:relative;width:500px;background-color:#fff;border-radius:5px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.2);box-shadow:0 1px 2px rgba(0,0,0,.2)}.basicModal__content{padding:7%;max-height:70vh;overflow:auto;-webkit-overflow-scrolling:touch}.basicModal__buttons{display:-webkit-box;display:-ms-flexbox;display:flex;width:100%;-webkit-box-shadow:0 -1px 0 rgba(0,0,0,.1);box-shadow:0 -1px 0 rgba(0,0,0,.1)}.basicModal__button{display:inline-block;width:100%;font-weight:700;text-align:center;-webkit-transition:background-color .2s;-o-transition:background-color .2s;transition:background-color .2s;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;user-select:none}.basicModal__button:hover{background-color:rgba(0,0,0,.02)}.basicModal__button#basicModal__cancel{-ms-flex-negative:2;flex-shrink:2}.basicModal__button#basicModal__action{-ms-flex-negative:1;flex-shrink:1;-webkit-box-shadow:inset 1px 0 0 rgba(0,0,0,.1);box-shadow:inset 1px 0 0 rgba(0,0,0,.1)}.basicModal__button#basicModal__action:first-child{-webkit-box-shadow:none;box-shadow:none}.basicModal__button:first-child{border-radius:0 0 0 5px}.basicModal__button:last-child{border-radius:0 0 5px}.basicModal__small{max-width:340px;text-align:center}.basicModal__small .basicModal__content{padding:10% 5%}.basicModal__xclose#basicModal__cancel{position:absolute;top:-8px;right:-8px;margin:0;padding:0;width:40px;height:40px;background-color:#fff;border-radius:100%;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.2);box-shadow:0 1px 2px rgba(0,0,0,.2)}.basicModal__xclose#basicModal__cancel:after{content:"";position:absolute;left:-3px;top:8px;width:35px;height:34px;background:#fff}.basicModal__xclose#basicModal__cancel svg{position:relative;width:20px;height:39px;fill:#888;z-index:1;-webkit-transition:fill .2s;-o-transition:fill .2s;transition:fill .2s}.basicModal__xclose#basicModal__cancel:after:hover svg,.basicModal__xclose#basicModal__cancel:hover svg{fill:#2875ed}.basicModal__xclose#basicModal__cancel:active svg,.basicModal__xclose#basicModal__cancel:after:active svg{fill:#1364e3}.basicContextContainer{position:fixed;width:100%;height:100%;top:0;left:0;z-index:1000;-webkit-tap-highlight-color:transparent}.basicContext{position:absolute;opacity:0;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;user-select:none;-webkit-box-sizing:border-box;box-sizing:border-box;-webkit-animation:.3s cubic-bezier(.51,.92,.24,1.2) basicContext__popIn;animation:.3s cubic-bezier(.51,.92,.24,1.2) basicContext__popIn}.basicContext *{-webkit-box-sizing:border-box;box-sizing:border-box}.basicContext__item{cursor:pointer}.basicContext__item--separator{float:left;width:100%;cursor:default}.basicContext__data{min-width:140px;text-align:left;white-space:nowrap}.basicContext__icon{display:inline-block}.basicContext--scrollable{height:100%;-webkit-overflow-scrolling:touch;overflow-x:hidden;overflow-y:auto}.basicContext--scrollable .basicContext__data{min-width:160px}@-webkit-keyframes basicContext__popIn{0%{-webkit-transform:scale(0);transform:scale(0)}to{-webkit-transform:scale(1);transform:scale(1)}}@keyframes basicContext__popIn{0%{-webkit-transform:scale(0);transform:scale(0)}to{-webkit-transform:scale(1);transform:scale(1)}}a,abbr,acronym,address,applet,article,aside,audio,b,big,blockquote,body,canvas,caption,center,cite,code,dd,del,details,dfn,div,dl,dt,em,embed,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,header,hgroup,html,i,iframe,img,ins,kbd,label,legend,li,mark,menu,nav,object,ol,output,p,pre,q,ruby,s,samp,section,small,span,strike,strong,sub,summary,sup,table,tbody,td,tfoot,th,thead,time,tr,tt,u,ul,var,video{margin:0;padding:0;border:0;font:inherit;font-size:100%;vertical-align:baseline}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}body{line-height:1;background-color:#1d1d1d;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;-webkit-font-smoothing:antialiased;-moz-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:after,blockquote:before,q:after,q:before{content:"";content:none}table{border-collapse:collapse;border-spacing:0}em,i{font-style:italic}b,strong{font-weight:700}*{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:color .3s,opacity .3s ease-out,-webkit-transform .3s ease-out,-webkit-box-shadow .3s;transition:color .3s,opacity .3s ease-out,transform .3s ease-out,box-shadow .3s,-webkit-transform .3s ease-out,-webkit-box-shadow .3s;-o-transition:color .3s,opacity .3s ease-out,transform .3s ease-out,box-shadow .3s}body,html{width:100%;height:100%;position:relative;overflow:clip}body.mode-frame div#container,body.mode-none div#container{display:none}input,textarea{-webkit-user-select:text!important;-moz-user-select:text!important;-ms-user-select:text!important;user-select:text!important}.svgsprite{display:none}.iconic{width:100%;height:100%}#upload{display:none}.fadeIn{-webkit-animation-name:fadeIn;animation-name:fadeIn;-webkit-animation-duration:.3s;animation-duration:.3s;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards;-webkit-animation-timing-function:cubic-bezier(.51,.92,.24,1);animation-timing-function:cubic-bezier(.51,.92,.24,1)}.fadeOut{-webkit-animation-name:fadeOut;animation-name:fadeOut;-webkit-animation-duration:.3s;animation-duration:.3s;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards;-webkit-animation-timing-function:cubic-bezier(.51,.92,.24,1);animation-timing-function:cubic-bezier(.51,.92,.24,1)}@-webkit-keyframes fadeIn{0%{opacity:0}100%{opacity:1}}@keyframes fadeIn{0%{opacity:0}100%{opacity:1}}@-webkit-keyframes fadeOut{0%{opacity:1}100%{opacity:0}}@keyframes fadeOut{0%{opacity:1}100%{opacity:0}}@-webkit-keyframes moveBackground{0%{background-position-x:0}100%{background-position-x:-100px}}@keyframes moveBackground{0%{background-position-x:0}100%{background-position-x:-100px}}@-webkit-keyframes zoomIn{0%{opacity:0;-webkit-transform:scale(.8);transform:scale(.8)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@keyframes zoomIn{0%{opacity:0;-webkit-transform:scale(.8);transform:scale(.8)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@-webkit-keyframes zoomOut{0%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}100%{opacity:0;-webkit-transform:scale(.8);transform:scale(.8)}}@keyframes zoomOut{0%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}100%{opacity:0;-webkit-transform:scale(.8);transform:scale(.8)}}@-webkit-keyframes pulse{0%,100%{opacity:1}50%{opacity:.3}}@keyframes pulse{0%,100%{opacity:1}50%{opacity:.3}}body.mode-frame #lychee_application_container,body.mode-none #lychee_application_container{display:none}.hflex-container,.hflex-item-rigid,.hflex-item-stretch,.vflex-container,.vflex-item-rigid,.vflex-item-stretch{position:relative;overflow:clip}.hflex-container,.vflex-container{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-line-pack:stretch;align-content:stretch;gap:0 0}.vflex-container{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.hflex-container{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.hflex-item-stretch,.vflex-item-stretch{-webkit-box-flex:1;-ms-flex:auto;flex:auto}.hflex-item-stretch{width:0;height:100%}.vflex-item-stretch{width:100%;height:0}.hflex-item-rigid,.vflex-item-rigid{-webkit-box-flex:0;-ms-flex:none;flex:none}.hflex-item-rigid{width:auto;height:100%}.vflex-item-rigid{width:100%;height:auto}.overlay-container{position:absolute;display:none;top:0;left:0;width:100%;height:100%;background-color:#000;-webkit-transition:background-color .3s;-o-transition:background-color .3s;transition:background-color .3s}.overlay-container.full{cursor:none}.overlay-container.active{display:unset}#lychee_view_content{height:auto;-webkit-box-flex:1;-ms-flex:1 0 auto;flex:1 0 auto;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-line-pack:start;align-content:flex-start;padding-bottom:16px;-webkit-overflow-scrolling:touch}#lychee_view_content.contentZoomIn .album,#lychee_view_content.contentZoomIn .photo{-webkit-animation-name:zoomIn;animation-name:zoomIn}#lychee_view_content.contentZoomIn .divider{-webkit-animation-name:fadeIn;animation-name:fadeIn}#lychee_view_content.contentZoomOut .album,#lychee_view_content.contentZoomOut .photo{-webkit-animation-name:zoomOut;animation-name:zoomOut}#lychee_view_content.contentZoomOut .divider{-webkit-animation-name:fadeOut;animation-name:fadeOut}.album,.photo{position:relative;width:202px;height:202px;margin:30px 0 0 30px;cursor:default;-webkit-animation-duration:.2s;animation-duration:.2s;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards;-webkit-animation-timing-function:cubic-bezier(.51,.92,.24,1);animation-timing-function:cubic-bezier(.51,.92,.24,1)}.album .thumbimg,.photo .thumbimg{position:absolute;width:200px;height:200px;background:#222;color:#222;-webkit-box-shadow:0 2px 5px rgba(0,0,0,.5);box-shadow:0 2px 5px rgba(0,0,0,.5);border:1px solid rgba(255,255,255,.5);-webkit-transition:opacity .3s ease-out,border-color .3s ease-out,-webkit-transform .3s ease-out;transition:opacity .3s ease-out,transform .3s ease-out,border-color .3s ease-out,-webkit-transform .3s ease-out;-o-transition:opacity .3s ease-out,transform .3s ease-out,border-color .3s ease-out}.album .thumbimg>img,.photo .thumbimg>img{width:100%;height:100%}.album.active .thumbimg,.album:focus .thumbimg,.photo.active .thumbimg,.photo:focus .thumbimg{border-color:#2293ec}.album:active .thumbimg,.photo:active .thumbimg{-webkit-transition:none;-o-transition:none;transition:none;border-color:#0f6ab2}.album.selected img,.photo.selected img{outline:#2293ec solid 1px}.album .video::before,.photo .video::before{content:"";position:absolute;display:block;height:100%;width:100%;background:url(../img/play-icon.png) 46% 50% no-repeat;-webkit-transition:.3s;-o-transition:.3s;transition:.3s;will-change:opacity,height}.album .video:focus::before,.photo .video:focus::before{opacity:.75}.album .livephoto::before,.photo .livephoto::before{content:"";position:absolute;display:block;height:100%;width:100%;background:url(../img/live-photo-icon.png) 2% 2% no-repeat;-webkit-transition:.3s;-o-transition:.3s;transition:.3s;will-change:opacity,height}.album .livephoto:focus::before,.photo .livephoto:focus::before{opacity:.75}.album .thumbimg:first-child,.album .thumbimg:nth-child(2){-webkit-transform:rotate(0) translateY(0) translateX(0);-ms-transform:rotate(0) translateY(0) translateX(0);transform:rotate(0) translateY(0) translateX(0);opacity:0}.album:focus .thumbimg:nth-child(1),.album:focus .thumbimg:nth-child(2){opacity:1;will-change:transform}.album:focus .thumbimg:nth-child(1){-webkit-transform:rotate(-2deg) translateY(10px) translateX(-12px);-ms-transform:rotate(-2deg) translateY(10px) translateX(-12px);transform:rotate(-2deg) translateY(10px) translateX(-12px)}.album:focus .thumbimg:nth-child(2){-webkit-transform:rotate(5deg) translateY(-8px) translateX(12px);-ms-transform:rotate(5deg) translateY(-8px) translateX(12px);transform:rotate(5deg) translateY(-8px) translateX(12px)}.blurred span{overflow:hidden}.blurred img{-webkit-filter:blur(5px);filter:blur(5px)}.album .overlay,.photo .overlay{position:absolute;margin:0 1px;width:200px;background:-webkit-gradient(linear,left top,left bottom,from(rgba(0,0,0,0)),to(rgba(0,0,0,.6)));background:-o-linear-gradient(top,rgba(0,0,0,0),rgba(0,0,0,.6));background:linear-gradient(to bottom,rgba(0,0,0,0),rgba(0,0,0,.6));bottom:1px}.album .thumbimg[data-overlay=false]+.overlay{background:0 0}.photo .overlay{opacity:0}.photo.active .overlay,.photo:focus .overlay{opacity:1}.album .overlay h1,.photo .overlay h1{min-height:19px;width:180px;margin:12px 0 5px 15px;color:#fff;text-shadow:0 1px 3px rgba(0,0,0,.4);font-size:16px;font-weight:700;overflow:hidden;white-space:nowrap;-o-text-overflow:ellipsis;text-overflow:ellipsis}.album .overlay a,.photo .overlay a{display:block;margin:0 0 12px 15px;font-size:11px;color:#ccc;text-shadow:0 1px 3px rgba(0,0,0,.4)}.album .overlay a .iconic,.photo .overlay a .iconic{fill:#ccc;margin:0 5px 0 0;width:8px;height:8px}.album .thumbimg[data-overlay=false]+.overlay a,.album .thumbimg[data-overlay=false]+.overlay h1{text-shadow:none}.album .badges,.photo .badges{position:relative;margin:-1px 0 0 6px}.album .subalbum_badge{position:absolute;right:0;top:0}.album .badge,.photo .badge{display:none;margin:0 0 0 6px;padding:12px 8px 6px;width:18px;background:#d92c34;-webkit-box-shadow:0 0 2px rgba(0,0,0,.6);box-shadow:0 0 2px rgba(0,0,0,.6);border-radius:0 0 5px 5px;border:1px solid #fff;border-top:none;color:#fff;text-align:center;text-shadow:0 1px 0 rgba(0,0,0,.4);opacity:.9}.album .badge--visible,.photo .badge--visible{display:inline-block}.album .badge--not--hidden,.photo .badge--not--hidden{background:#0a0}.album .badge--hidden,.photo .badge--hidden{background:#f90}.album .badge--cover,.photo .badge--cover{display:inline-block;background:#f90}.album .badge--star,.photo .badge--star{display:inline-block;background:#fc0}.album .badge--nsfw,.photo .badge--nsfw{display:inline-block;background:#ff82ee}.album .badge--list,.photo .badge--list{background:#2293ec}.album .badge--tag,.photo .badge--tag{display:inline-block;background:#0a0}.album .badge .iconic,.photo .badge .iconic{fill:#fff;width:16px;height:16px}.album .badge--folder,.photo .badge--folder{display:inline-block;-webkit-box-shadow:none;box-shadow:none;background:0 0;border:none}.album .badge--folder .iconic,.photo .badge--folder .iconic{width:12px;height:12px}.divider{margin:50px 0 0;padding:10px 0 0;width:100%;opacity:0;border-top:1px solid rgba(255,255,255,.02);-webkit-box-shadow:0 -1px 0 rgba(0,0,0,.2);box-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-animation-duration:.2s;animation-duration:.2s;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards;-webkit-animation-timing-function:cubic-bezier(.51,.92,.24,1);animation-timing-function:cubic-bezier(.51,.92,.24,1)}.divider:first-child{margin-top:10px;border-top:0;-webkit-box-shadow:none;box-shadow:none}.divider h1{margin:0 0 0 30px;color:rgba(255,255,255,.6);font-size:14px;font-weight:700}@media only screen and (min-width:320px) and (max-width:567px){.album,.photo{--size:calc((100vw - 3px) / 3);width:calc(var(--size) - 3px);height:calc(var(--size) - 3px);margin:3px 0 0 3px}.album .thumbimg,.photo .thumbimg{width:calc(var(--size) - 5px);height:calc(var(--size) - 5px)}.album .overlay,.photo .overlay{width:calc(var(--size) - 5px)}.album .overlay h1,.photo .overlay h1{min-height:14px;width:calc(var(--size) - 19px);margin:8px 0 2px 6px;font-size:12px}.album .overlay a,.photo .overlay a{display:none}.album .badge,.photo .badge{padding:4px 3px 3px;width:12px}.album .badge .iconic,.photo .badge .iconic{width:12px;height:12px}.album .badge--folder .iconic,.photo .badge--folder .iconic{width:8px;height:8px}.divider{margin:20px 0 0}.divider:first-child{margin-top:0}.divider h1{margin:0 0 6px 8px;font-size:12px}}@media only screen and (min-width:568px) and (max-width:639px){.album,.photo{--size:calc((100vw - 3px) / 4);width:calc(var(--size) - 3px);height:calc(var(--size) - 3px);margin:3px 0 0 3px}.album .thumbimg,.photo .thumbimg{width:calc(var(--size) - 5px);height:calc(var(--size) - 5px)}.album .overlay,.photo .overlay{width:calc(var(--size) - 5px)}.album .overlay h1,.photo .overlay h1{min-height:14px;width:calc(var(--size) - 19px);margin:8px 0 2px 6px;font-size:12px}.album .overlay a,.photo .overlay a{display:none}.album .badge,.photo .badge{padding:4px 3px 3px;width:14px}.album .badge .iconic,.photo .badge .iconic{width:14px;height:14px}.album .badge--folder .iconic,.photo .badge--folder .iconic{width:9px;height:9px}.divider{margin:24px 0 0}.divider:first-child{margin-top:0}.divider h1{margin:0 0 6px 10px}}@media only screen and (min-width:640px) and (max-width:768px){.album,.photo{--size:calc((100vw - 5px) / 5);width:calc(var(--size) - 5px);height:calc(var(--size) - 5px);margin:5px 0 0 5px}.album .thumbimg,.photo .thumbimg{width:calc(var(--size) - 7px);height:calc(var(--size) - 7px)}.album .overlay,.photo .overlay{width:calc(var(--size) - 7px)}.album .overlay h1,.photo .overlay h1{min-height:14px;width:calc(var(--size) - 21px);margin:10px 0 3px 8px;font-size:12px}.album .overlay a,.photo .overlay a{display:none}.album .badge,.photo .badge{padding:6px 4px 4px;width:16px}.album .badge .iconic,.photo .badge .iconic{width:16px;height:16px}.album .badge--folder .iconic,.photo .badge--folder .iconic{width:10px;height:10px}.divider{margin:28px 0 0}.divider:first-child{margin-top:0}.divider h1{margin:0 0 6px 10px}}.no_content{position:absolute;top:50%;left:50%;padding-top:20px;color:rgba(255,255,255,.35);text-align:center;-webkit-transform:translateX(-50%) translateY(-50%);-ms-transform:translateX(-50%) translateY(-50%);transform:translateX(-50%) translateY(-50%)}.no_content .iconic{fill:rgba(255,255,255,.3);margin:0 0 10px;width:50px;height:50px}.no_content p{font-size:16px;font-weight:700}body.mode-gallery #lychee_frame_container,body.mode-none #lychee_frame_container,body.mode-view #lychee_frame_container{display:none}#lychee_frame_bg_canvas{width:100%;height:100%;position:absolute}#lychee_frame_bg_image{position:absolute;display:none}#lychee_frame_noise_layer{position:absolute;top:0;left:0;width:100%;height:100%;background-image:url(../img/noise.png);background-repeat:repeat;background-position:44px 44px}#lychee_frame_image_container{width:100%;height:100%;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-ms-flex-line-pack:center;align-content:center}#lychee_frame_image_container img{height:95%;width:95%;-o-object-fit:contain;object-fit:contain;-webkit-filter:drop-shadow(0 0 1px rgba(0, 0, 0, .3)) drop-shadow(0 0 10px rgba(0, 0, 0, .3));filter:drop-shadow(0 0 1px rgba(0, 0, 0, .3)) drop-shadow(0 0 10px rgba(0, 0, 0, .3))}#lychee_frame_shutter{position:absolute;width:100%;height:100%;top:0;left:0;padding:0;margin:0;background-color:#1d1d1d;opacity:1;-webkit-transition:opacity 1s ease-in-out;-o-transition:opacity 1s ease-in-out;transition:opacity 1s ease-in-out}#lychee_frame_shutter.opened{opacity:0}#lychee_left_menu_container{width:0;background-color:#111;padding-top:16px;-webkit-transition:width .5s;-o-transition:width .5s;transition:width .5s;height:100%;z-index:998}#lychee_left_menu,#lychee_left_menu_container.visible{width:250px}#lychee_left_menu a{padding:8px 8px 8px 32px;text-decoration:none;font-size:18px;color:#818181;display:block;cursor:pointer}#lychee_left_menu a.linkMenu{white-space:nowrap}#lychee_left_menu .iconic{display:inline-block;margin:0 10px 0 1px;width:15px;height:14px;fill:#818181}#lychee_left_menu .iconic.ionicons{margin:0 8px -2px 0;width:18px;height:18px}@media only screen and (max-width:567px),only screen and (max-width:640px) and (orientation:portrait){#lychee_left_menu,#lychee_left_menu_container{position:absolute;left:0}#lychee_left_menu_container.visible{width:100%}}@media (hover:hover){.album:hover .thumbimg,.photo:hover .thumbimg{border-color:#2293ec}.album .livephoto:hover::before,.album .video:hover::before,.photo .livephoto:hover::before,.photo .video:hover::before{opacity:.75}.album:hover .thumbimg:nth-child(1),.album:hover .thumbimg:nth-child(2),.album__dragover .thumbimg:nth-child(1),.album__dragover .thumbimg:nth-child(2){opacity:1;will-change:transform}.album:hover .thumbimg:nth-child(1),.album__dragover .thumbimg:nth-child(1){-webkit-transform:rotate(-2deg) translateY(10px) translateX(-12px);-ms-transform:rotate(-2deg) translateY(10px) translateX(-12px);transform:rotate(-2deg) translateY(10px) translateX(-12px)}.album:hover .thumbimg:nth-child(2),.album__dragover .thumbimg:nth-child(2){-webkit-transform:rotate(5deg) translateY(-8px) translateX(12px);-ms-transform:rotate(5deg) translateY(-8px) translateX(12px);transform:rotate(5deg) translateY(-8px) translateX(12px)}.photo:hover .overlay{opacity:1}#lychee_left_menu a:hover{color:#f1f1f1}}.basicContext{padding:5px 0 6px;background:-webkit-gradient(linear,left top,left bottom,from(#333),to(#252525));background:-o-linear-gradient(top,#333,#252525);background:linear-gradient(to bottom,#333,#252525);-webkit-box-shadow:0 1px 4px rgba(0,0,0,.2),inset 0 1px 0 rgba(255,255,255,.05);box-shadow:0 1px 4px rgba(0,0,0,.2),inset 0 1px 0 rgba(255,255,255,.05);border-radius:5px;border:1px solid rgba(0,0,0,.7);border-bottom:1px solid rgba(0,0,0,.8);-webkit-transition:none;-o-transition:none;transition:none}.basicContext__item{margin-bottom:2px;font-size:14px;color:#ccc}.basicContext__item--separator{margin:4px 0;height:2px;background:rgba(0,0,0,.2);border-bottom:1px solid rgba(255,255,255,.06)}.basicContext__item--disabled{cursor:default;opacity:.5}.basicContext__item:last-child{margin-bottom:0}.basicContext__data{min-width:auto;padding:6px 25px 7px 12px;-webkit-transition:none;-o-transition:none;transition:none;cursor:default}@media (hover:none) and (pointer:coarse){.basicContext__data{padding:12px 25px 12px 12px}}.basicContext__item:not(.basicContext__item--disabled):active .basicContext__data{background:-webkit-gradient(linear,left top,left bottom,from(#1178ca),to(#0f6ab2));background:-o-linear-gradient(top,#1178ca,#0f6ab2);background:linear-gradient(to bottom,#1178ca,#0f6ab2)}.basicContext__icon{margin-right:10px;width:12px;text-align:center}@media (hover:hover){.basicContext__item:not(.basicContext__item--disabled):hover .basicContext__data{background:-webkit-gradient(linear,left top,left bottom,from(#2293ec),to(#1386e1));background:-o-linear-gradient(top,#2293ec,#1386e1);background:linear-gradient(to bottom,#2293ec,#1386e1)}.basicContext__item:hover{color:#fff;-webkit-transition:.3s;-o-transition:.3s;transition:.3s;-webkit-transform:scale(1.05);-ms-transform:scale(1.05);transform:scale(1.05)}.basicContext__item:hover .iconic{fill:#fff}.basicContext__item--noHover:hover .basicContext__data{background:0 0!important}}.basicContext__data .cover{position:absolute;background-color:#222;border-radius:2px;-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.5);box-shadow:0 0 0 1px rgba(0,0,0,.5)}.basicContext__data .title{display:inline-block;margin:0 0 3px 26px}.basicContext__data .iconic{display:inline-block;margin:0 10px 0 1px;width:11px;height:10px;fill:#fff}.basicContext__data .iconic.active{fill:#f90}.basicContext__data .iconic.ionicons{margin:0 8px -2px 0;width:14px;height:14px}.basicContext__data input#link{margin:-2px 0;padding:5px 7px 6px;width:100%;background:#333;color:#fff;-webkit-box-shadow:0 1px 0 rgba(255,255,255,.05);box-shadow:0 1px 0 rgba(255,255,255,.05);border:1px solid rgba(0,0,0,.4);border-radius:3px;outline:0}.basicContext__item--noHover .basicContext__data{padding-right:12px}div.basicModalContainer{background-color:rgba(0,0,0,.85);z-index:999}div.basicModalContainer--error{-webkit-transform:translateY(40px);-ms-transform:translateY(40px);transform:translateY(40px)}div.basicModal{background:-webkit-gradient(linear,left top,left bottom,from(#444),to(#333));background:-o-linear-gradient(top,#444,#333);background:linear-gradient(to bottom,#444,#333);-webkit-box-shadow:0 1px 4px rgba(0,0,0,.2),inset 0 1px 0 rgba(255,255,255,.05);box-shadow:0 1px 4px rgba(0,0,0,.2),inset 0 1px 0 rgba(255,255,255,.05);font-size:14px;line-height:17px}div.basicModal--error{-webkit-transform:translateY(-40px);-ms-transform:translateY(-40px);transform:translateY(-40px)}div.basicModal__buttons{-webkit-box-shadow:none;box-shadow:none}.basicModal__button{padding:13px 0 15px;background:0 0;color:#999;border-top:1px solid rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.02);box-shadow:inset 0 1px 0 rgba(255,255,255,.02);cursor:default}.basicModal__button--busy,.basicModal__button:active{-webkit-transition:none;-o-transition:none;transition:none;background:rgba(0,0,0,.1);cursor:wait}.basicModal__button#basicModal__action{color:#2293ec;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.02),inset 1px 0 0 rgba(0,0,0,.2);box-shadow:inset 0 1px 0 rgba(255,255,255,.02),inset 1px 0 0 rgba(0,0,0,.2)}.basicModal__button#basicModal__action.red,.basicModal__button#basicModal__cancel.red{color:#d92c34}.basicModal__button.hidden{display:none}div.basicModal__content{padding:36px;color:#ececec;text-align:left}div.basicModal__content>*{display:block;width:100%;margin:24px 0;padding:0}div.basicModal__content>.force-first-child,div.basicModal__content>:first-child{margin-top:0}div.basicModal__content>.force-last-child,div.basicModal__content>:last-child{margin-bottom:0}div.basicModal__content .disabled{color:#999}div.basicModal__content b{font-weight:700;color:#fff}div.basicModal__content a{color:inherit;text-decoration:none;border-bottom:1px dashed #ececec}div.basicModal__content a.button{display:inline-block;margin:0 6px;padding:3px 12px;color:#2293ec;text-align:center;border-radius:5px;border:none;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.02),inset 1px 0 0 rgba(0,0,0,.2);box-shadow:inset 0 1px 0 rgba(255,255,255,.02),inset 1px 0 0 rgba(0,0,0,.2)}div.basicModal__content a.button .iconic{fill:#2293ec}div.basicModal__content>hr{border:none;border-top:1px solid rgba(0,0,0,.3)}#lychee_toolbar_container{-webkit-transition:height .3s ease-out;-o-transition:height .3s ease-out;transition:height .3s ease-out}#lychee_toolbar_container.hidden{height:0}#lychee_toolbar_container,.toolbar{height:49px}.toolbar{background:-webkit-gradient(linear,left top,left bottom,from(#222),to(#1a1a1a));background:-o-linear-gradient(top,#222,#1a1a1a);background:linear-gradient(to bottom,#222,#1a1a1a);border-bottom:1px solid #0f0f0f;display:none;-webkit-box-align:center;-ms-flex-align:center;align-items:center;position:relative;-webkit-box-sizing:border-box;box-sizing:border-box;width:100%}.toolbar.visible{display:-webkit-box;display:-ms-flexbox;display:flex}#lychee_toolbar_config .toolbar .button .iconic{-webkit-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg)}#lychee_toolbar_config .toolbar .header__title{padding-right:80px}.toolbar .header__title{width:100%;padding:16px 0;color:#fff;font-size:16px;font-weight:700;text-align:center;cursor:default;overflow:hidden;white-space:nowrap;-o-text-overflow:ellipsis;text-overflow:ellipsis;-webkit-transition:margin-left .5s;-o-transition:margin-left .5s;transition:margin-left .5s}.toolbar .header__title .iconic{display:none;margin:0 0 0 5px;width:10px;height:10px;fill:rgba(255,255,255,.5);-webkit-transition:fill .2s ease-out;-o-transition:fill .2s ease-out;transition:fill .2s ease-out}.toolbar .header__title:active .iconic{-webkit-transition:none;-o-transition:none;transition:none;fill:rgba(255,255,255,.8)}.toolbar .header__title--editable .iconic{display:inline-block}.toolbar .button{-ms-flex-negative:0;flex-shrink:0;padding:16px 8px;height:15px}.toolbar .button .iconic{width:15px;height:15px;fill:rgba(255,255,255,.5);-webkit-transition:fill .2s ease-out;-o-transition:fill .2s ease-out;transition:fill .2s ease-out}.toolbar .button:active .iconic{-webkit-transition:none;-o-transition:none;transition:none;fill:rgba(255,255,255,.8)}.toolbar .button--star.active .iconic{fill:#f0ef77}.toolbar .button--eye.active .iconic{fill:#d92c34}.toolbar .button--eye.active--not-hidden .iconic{fill:#0a0}.toolbar .button--eye.active--hidden .iconic{fill:#f90}.toolbar .button--share .iconic.ionicons{margin:-2px 0;width:18px;height:18px}.toolbar .button--nsfw.active .iconic{fill:#ff82ee}.toolbar .button--info.active .iconic{fill:#2293ec}.toolbar #button_back,.toolbar #button_back_home,.toolbar #button_close_config,.toolbar #button_settings,.toolbar #button_signin{padding:16px 12px 16px 18px}.toolbar .button_add{padding:16px 18px 16px 12px}.toolbar .header__divider{-ms-flex-negative:0;flex-shrink:0;width:14px}.toolbar .header__search__field{position:relative}.toolbar input[type=text].header__search{-ms-flex-negative:0;flex-shrink:0;width:80px;margin:0;padding:5px 12px 6px;background-color:#1d1d1d;color:#fff;border:1px solid rgba(0,0,0,.9);-webkit-box-shadow:0 1px 0 rgba(255,255,255,.04);box-shadow:0 1px 0 rgba(255,255,255,.04);outline:0;border-radius:50px;opacity:.6;-webkit-transition:opacity .3s ease-out,width .2s ease-out,-webkit-box-shadow .3s ease-out;transition:opacity .3s ease-out,box-shadow .3s ease-out,width .2s ease-out,-webkit-box-shadow .3s ease-out;-o-transition:opacity .3s ease-out,box-shadow .3s ease-out,width .2s ease-out}.toolbar input[type=text].header__search:focus{width:140px;border-color:#2293ec;-webkit-box-shadow:0 1px 0 rgba(255,255,255,0);box-shadow:0 1px 0 rgba(255,255,255,0);opacity:1}.toolbar input[type=text].header__search:focus~.header__clear{opacity:1}.toolbar input[type=text].header__search::-ms-clear{display:none}.toolbar .header__clear{position:absolute;top:50%;-ms-transform:translateY(-50%);-webkit-transform:translateY(-50%);transform:translateY(-50%);right:8px;padding:0;color:rgba(255,255,255,.5);font-size:24px;opacity:0;-webkit-transition:color .2s ease-out;-o-transition:color .2s ease-out;transition:color .2s ease-out;cursor:default}.toolbar .header__clear_nomap{right:60px}.toolbar .header__hostedwith{-ms-flex-negative:0;flex-shrink:0;padding:5px 10px;margin:11px 0;color:#888;font-size:13px;border-radius:100px;cursor:default}@media only screen and (max-width:640px){#button_move,#button_move_album,#button_nsfw_album,#button_trash,#button_trash_album,#button_visibility,#button_visibility_album{display:none!important}}@media only screen and (max-width:640px) and (max-width:567px){#button_rotate_ccwise,#button_rotate_cwise{display:none!important}.header__divider{width:0}}#imageview #image,#imageview #livephoto{position:absolute;top:30px;right:30px;bottom:30px;left:30px;margin:auto;max-width:calc(100% - 60px);max-height:calc(100% - 60px);width:auto;height:auto;-webkit-transition:top .3s,right .3s,bottom .3s,left .3s,max-width .3s,max-height .3s;-o-transition:top .3s,right .3s,bottom .3s,left .3s,max-width .3s,max-height .3s;transition:top .3s,right .3s,bottom .3s,left .3s,max-width .3s,max-height .3s;-webkit-animation-name:zoomIn;animation-name:zoomIn;-webkit-animation-duration:.3s;animation-duration:.3s;-webkit-animation-timing-function:cubic-bezier(.51,.92,.24,1.15);animation-timing-function:cubic-bezier(.51,.92,.24,1.15);background-size:contain;background-position:center;background-repeat:no-repeat}#imageview.full #image,#imageview.full #livephoto{top:0;right:0;bottom:0;left:0;max-width:100%;max-height:100%}#imageview #image_overlay{position:absolute;bottom:30px;left:30px;color:#fff;text-shadow:1px 1px 2px #000;z-index:3}#imageview #image_overlay h1{font-size:28px;font-weight:500;-webkit-transition:visibility .3s linear,opacity .3s linear;-o-transition:visibility .3s linear,opacity .3s linear;transition:visibility .3s linear,opacity .3s linear}#imageview #image_overlay p{margin-top:5px;font-size:20px;line-height:24px}#imageview #image_overlay a .iconic{fill:#fff;margin:0 5px 0 0;width:14px;height:14px}#imageview .arrow_wrapper{position:absolute;width:15%;height:calc(100% - 60px);top:60px}#imageview .arrow_wrapper--previous{left:0}#imageview .arrow_wrapper--next{right:0}#imageview .arrow_wrapper a{position:absolute;top:50%;margin:-19px 0 0;padding:8px 12px;width:16px;height:22px;background-size:100% 100%;border:1px solid rgba(255,255,255,.8);opacity:.6;z-index:2;-webkit-transition:opacity .2s ease-out,-webkit-transform .2s ease-out;transition:transform .2s ease-out,opacity .2s ease-out,-webkit-transform .2s ease-out;-o-transition:transform .2s ease-out,opacity .2s ease-out;will-change:transform}#imageview .arrow_wrapper a#previous{left:-1px;-webkit-transform:translateX(-100%);-ms-transform:translateX(-100%);transform:translateX(-100%)}#imageview .arrow_wrapper a#next{right:-1px;-webkit-transform:translateX(100%);-ms-transform:translateX(100%);transform:translateX(100%)}#imageview .arrow_wrapper .iconic{fill:rgba(255,255,255,.8)}#imageview video{z-index:1}@media (hover:hover){.basicModal__button:hover{background:rgba(255,255,255,.02)}div.basicModal__content a.button:hover{color:#fff;background:#2293ec}.toolbar .button:hover .iconic,.toolbar .header__title:hover .iconic,div.basicModal__content a.button:hover .iconic{fill:#fff}.toolbar .header__clear:hover{color:#fff}.toolbar .header__hostedwith:hover{background-color:rgba(0,0,0,.3)}#imageview .arrow_wrapper:hover a#next,#imageview .arrow_wrapper:hover a#previous{-webkit-transform:translateX(0);-ms-transform:translateX(0);transform:translateX(0)}#imageview .arrow_wrapper a:hover{opacity:1}}@media only screen and (max-width:567px),only screen and (max-width:640px) and (orientation:portrait){#imageview #image,#imageview #livephoto{top:0;right:0;bottom:0;left:0;max-width:100%;max-height:100%}#imageview #image_overlay h1{font-size:14px}#imageview #image_overlay p{margin-top:2px;font-size:11px;line-height:13px}#imageview #image_overlay a .iconic{width:9px;height:9px}}@media only screen and (min-width:568px) and (max-width:768px),only screen and (min-width:568px) and (max-width:640px) and (orientation:landscape){#imageview #image,#imageview #livephoto{top:0;right:0;bottom:0;left:0;max-width:100%;max-height:100%}#imageview #image_overlay h1{font-size:18px}#imageview #image_overlay p{margin-top:4px;font-size:14px;line-height:16px}#imageview #image_overlay a .iconic{width:12px;height:12px}}.leaflet-marker-photo img{width:100%;height:100%}.image-leaflet-popup{width:100%}.leaflet-popup-content div{pointer-events:none;position:absolute;bottom:19px;left:22px;right:22px;padding-bottom:10px;background:-webkit-gradient(linear,left top,left bottom,from(rgba(0,0,0,0)),to(rgba(0,0,0,.6)));background:-o-linear-gradient(top,rgba(0,0,0,0),rgba(0,0,0,.6));background:linear-gradient(to bottom,rgba(0,0,0,0),rgba(0,0,0,.6))}.leaflet-popup-content h1{top:0;position:relative;margin:12px 0 5px 15px;font-size:16px;font-weight:700;text-shadow:0 1px 3px rgba(255,255,255,.4);color:#fff;white-space:nowrap;-o-text-overflow:ellipsis;text-overflow:ellipsis}.leaflet-popup-content span{margin-left:12px}.leaflet-popup-content svg{fill:#fff;vertical-align:middle}.leaflet-popup-content p{display:inline;font-size:11px;color:#fff}.leaflet-popup-content .iconic{width:20px;height:15px}#lychee_sidebar_container{width:0;-webkit-transition:width .3s cubic-bezier(.51,.92,.24,1);-o-transition:width .3s cubic-bezier(.51,.92,.24,1);transition:width .3s cubic-bezier(.51,.92,.24,1)}#lychee_sidebar,#lychee_sidebar_container.active{width:350px}#lychee_sidebar{height:100%;background-color:rgba(25,25,25,.98);border-left:1px solid rgba(0,0,0,.2)}#lychee_sidebar_header{height:49px;background:-webkit-gradient(linear,left top,left bottom,from(rgba(255,255,255,.02)),to(rgba(0,0,0,0)));background:-o-linear-gradient(top,rgba(255,255,255,.02),rgba(0,0,0,0));background:linear-gradient(to bottom,rgba(255,255,255,.02),rgba(0,0,0,0));border-top:1px solid #2293ec}#lychee_sidebar_header h1{margin:15px 0;color:#fff;font-size:16px;font-weight:700;text-align:center;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}#lychee_sidebar_content{overflow:clip auto;-webkit-overflow-scrolling:touch}#lychee_sidebar_content .sidebar__divider{padding:12px 0 8px;width:100%;border-top:1px solid rgba(255,255,255,.02);-webkit-box-shadow:0 -1px 0 rgba(0,0,0,.2);box-shadow:0 -1px 0 rgba(0,0,0,.2)}#lychee_sidebar_content .sidebar__divider:first-child{border-top:0;-webkit-box-shadow:none;box-shadow:none}#lychee_sidebar_content .sidebar__divider h1{margin:0 0 0 20px;color:rgba(255,255,255,.6);font-size:14px;font-weight:700;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}#lychee_sidebar_content .edit{display:inline-block;margin-left:3px;width:10px}#lychee_sidebar_content .edit .iconic{width:10px;height:10px;fill:rgba(255,255,255,.5);-webkit-transition:fill .2s ease-out;-o-transition:fill .2s ease-out;transition:fill .2s ease-out}#lychee_sidebar_content .edit:active .iconic{-webkit-transition:none;-o-transition:none;transition:none;fill:rgba(255,255,255,.8)}#lychee_sidebar_content table{margin:10px 0 15px 20px;width:calc(100% - 20px)}#lychee_sidebar_content table tr td{padding:5px 0;color:#fff;font-size:14px;line-height:19px;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}#lychee_sidebar_content table tr td:first-child{width:110px}#lychee_sidebar_content table tr td:last-child{padding-right:10px}#lychee_sidebar_content table tr td span{-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}#lychee_sidebar_content #tags{width:calc(100% - 40px);margin:16px 20px 12px;color:#fff;display:inline-block}#lychee_sidebar_content #tags>div{display:inline-block}#lychee_sidebar_content #tags .empty{font-size:14px;margin:0 2px 8px 0;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}#lychee_sidebar_content #tags .edit{margin-top:6px}#lychee_sidebar_content #tags .empty .edit{margin-top:0}#lychee_sidebar_content #tags .tag{cursor:default;display:inline-block;padding:6px 10px;margin:0 6px 8px 0;background-color:rgba(0,0,0,.5);border-radius:100px;font-size:12px;-webkit-transition:background-color .2s;-o-transition:background-color .2s;transition:background-color .2s;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}#lychee_sidebar_content #tags .tag span{display:inline-block;padding:0;margin:0 0 -2px;width:0;overflow:hidden;-webkit-transform:scale(0);-ms-transform:scale(0);transform:scale(0);-webkit-transition:width .2s,margin .2s,fill .2s ease-out,-webkit-transform .2s;transition:width .2s,margin .2s,transform .2s,fill .2s ease-out,-webkit-transform .2s;-o-transition:width .2s,margin .2s,transform .2s,fill .2s ease-out}#lychee_sidebar_content #tags .tag span .iconic{fill:#d92c34;width:8px;height:8px}#lychee_sidebar_content #tags .tag span:active .iconic{-webkit-transition:none;-o-transition:none;transition:none;fill:#b22027}#lychee_sidebar_content #leaflet_map_single_photo{margin:10px 0 0 20px;height:180px;width:calc(100% - 40px)}#lychee_sidebar_content .attr_location.search{cursor:pointer}@media only screen and (max-width:567px),only screen and (max-width:640px) and (orientation:portrait){#lychee_sidebar_container{position:absolute;right:0}#lychee_sidebar{background-color:rgba(0,0,0,.6)}#lychee_sidebar,#lychee_sidebar_container.active{width:240px}#lychee_sidebar_header{height:22px}#lychee_sidebar_header h1{margin:6px 0;font-size:13px}#lychee_sidebar_content{padding-bottom:10px}#lychee_sidebar_content .sidebar__divider{padding:6px 0 2px}#lychee_sidebar_content .sidebar__divider h1{margin:0 0 0 10px;font-size:12px}#lychee_sidebar_content #tags,#lychee_sidebar_content table{margin:4px 0 6px 10px;width:calc(100% - 16px)}#lychee_sidebar_content table tr td{padding:2px 0;font-size:11px;line-height:12px}#lychee_sidebar_content table tr td:first-child{width:80px}#lychee_sidebar_content #tags .empty{margin:0;font-size:11px}}@media only screen and (min-width:568px) and (max-width:768px),only screen and (min-width:568px) and (max-width:640px) and (orientation:landscape){#lychee_sidebar,#lychee_sidebar_container.active{width:280px}#lychee_sidebar_header{height:28px}#lychee_sidebar_header h1{margin:8px 0;font-size:15px}#lychee_sidebar_content{padding-bottom:10px}#lychee_sidebar_content .sidebar__divider{padding:8px 0 4px}#lychee_sidebar_content .sidebar__divider h1{margin:0 0 0 10px;font-size:13px}#lychee_sidebar_content #tags,#lychee_sidebar_content table{margin:4px 0 6px 10px;width:calc(100% - 16px)}#lychee_sidebar_content table tr td{padding:2px 0;font-size:12px;line-height:13px}#lychee_sidebar_content table tr td:first-child{width:90px}#lychee_sidebar_content #tags .empty{margin:0;font-size:12px}}#lychee_loading{height:0;-webkit-transition:height .3s;-o-transition:height .3s;transition:height .3s;background-size:100px 3px;background-repeat:repeat-x;-webkit-animation-name:moveBackground;animation-name:moveBackground;-webkit-animation-duration:.3s;animation-duration:.3s;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite;-webkit-animation-timing-function:linear;animation-timing-function:linear}#lychee_loading.loading{height:3px;background-image:-webkit-gradient(linear,left top,right top,from(#153674),color-stop(47%,#153674),color-stop(53%,#2651ae),to(#2651ae));background-image:-o-linear-gradient(left,#153674 0,#153674 47%,#2651ae 53%,#2651ae 100%);background-image:linear-gradient(to right,#153674 0,#153674 47%,#2651ae 53%,#2651ae 100%)}#lychee_loading.error{height:40px;background-color:#2f0d0e;background-image:-webkit-gradient(linear,left top,right top,from(#451317),color-stop(47%,#451317),color-stop(53%,#aa3039),to(#aa3039));background-image:-o-linear-gradient(left,#451317 0,#451317 47%,#aa3039 53%,#aa3039 100%);background-image:linear-gradient(to right,#451317 0,#451317 47%,#aa3039 53%,#aa3039 100%)}#lychee_loading.success{height:40px;background-color:#070;background-image:-webkit-gradient(linear,left top,right top,from(#070),color-stop(47%,#090),color-stop(53%,#0a0),to(#0c0));background-image:-o-linear-gradient(left,#070 0,#090 47%,#0a0 53%,#0c0 100%);background-image:linear-gradient(to right,#070 0,#090 47%,#0a0 53%,#0c0 100%)}#lychee_loading h1{margin:13px 13px 0;color:#ddd;font-size:14px;font-weight:700;text-shadow:0 1px 0 #000;text-transform:capitalize}#lychee_loading h1 span{margin-left:10px;font-weight:400;text-transform:none}div.select,input,output,select,textarea{display:inline-block;position:relative}div.select>select{display:block;width:100%}div.select,input,output,select,select option,textarea{color:#fff;background-color:#2c2c2c;margin:0;font-size:inherit;line-height:inherit;padding:0;border:none;-webkit-box-shadow:none;box-shadow:none;outline:0}input[type=password],input[type=text],select{padding-top:3px;padding-bottom:3px}input[type=password],input[type=text]{padding-left:2px;padding-right:2px;background-color:transparent;border-bottom:1px solid #222;border-radius:0;-webkit-box-shadow:0 1px 0 rgba(255,255,255,.05);box-shadow:0 1px 0 rgba(255,255,255,.05)}input[type=password]:focus,input[type=text]:focus{border-bottom-color:#2293ec}input[type=password].error,input[type=text].error{border-bottom-color:#d92c34}input[type=checkbox]{top:2px;height:16px;width:16px;-webkit-appearance:none;-moz-appearance:none;appearance:none;color:#2293ec;border:none;border-radius:3px;-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.7);box-shadow:0 0 0 1px rgba(0,0,0,.7)}input[type=checkbox]::before{content:"✔";position:absolute;text-align:center;font-size:16px;line-height:16px;top:0;bottom:0;left:0;right:0;width:auto;height:auto;visibility:hidden}input[type=checkbox]:checked::before{visibility:visible}input[type=checkbox].slider{top:5px;height:22px;width:42px;border:1px solid rgba(0,0,0,.2);-webkit-box-shadow:0 1px 0 rgba(255,255,255,.02);box-shadow:0 1px 0 rgba(255,255,255,.02);border-radius:11px;background:#2c2c2c}input[type=checkbox].slider::before{content:"";background-color:#2293ec;height:14px;width:14px;left:3px;top:3px;border:none;border-radius:7px;visibility:visible}input[type=checkbox].slider:checked{background-color:#2293ec}input[type=checkbox].slider:checked::before{left:auto;right:3px;background-color:#fff}div.select{font-size:12px;background:#2c2c2c;border-radius:3px;border:1px solid rgba(0,0,0,.2);-webkit-box-shadow:0 1px 0 rgba(255,255,255,.02);box-shadow:0 1px 0 rgba(255,255,255,.02)}div.select::after{position:absolute;content:"≡";right:8px;top:3px;color:#2293ec;font-size:16px;font-weight:700;pointer-events:none}select{padding-left:8px;padding-right:8px;-webkit-appearance:none;-moz-appearance:none;appearance:none;background:0 0}select option{padding:2px 0;-webkit-transition:none;-o-transition:none;transition:none}form div.input-group{position:relative;margin:18px 0}form div.input-group:first-child{margin-top:0}form div.input-group:last-child{margin-bottom:0}form div.input-group.hidden{display:none}form div.input-group label{font-weight:700}form div.input-group p{display:block;margin:6px 0;font-size:13px;line-height:16px}form div.input-group p:last-child{margin-bottom:0}form div.input-group.stacked>label{display:block;margin-bottom:6px}form div.input-group.stacked>label>input[type=password],form div.input-group.stacked>label>input[type=text]{margin-top:12px}form div.input-group.stacked>div.select,form div.input-group.stacked>input,form div.input-group.stacked>output,form div.input-group.stacked>textarea{width:100%;display:block}form div.input-group.compact{padding-left:120px}form div.input-group.compact>label{display:block;position:absolute;margin:0;left:0;width:108px;height:auto;top:3px;bottom:0;overflow-y:hidden}form div.input-group.compact>div.select,form div.input-group.compact>input,form div.input-group.compact>output,form div.input-group.compact>textarea{display:block;width:100%}form div.input-group.compact-inverse{padding-left:36px}form div.input-group.compact-inverse label{display:block}form div.input-group.compact-inverse>div.select,form div.input-group.compact-inverse>input,form div.input-group.compact-inverse>output,form div.input-group.compact-inverse>textarea{display:block;position:absolute;width:16px;height:16px;top:2px;left:0}form div.input-group.compact-no-indent>label{display:inline}form div.input-group.compact-no-indent>div.select,form div.input-group.compact-no-indent>input,form div.input-group.compact-no-indent>output,form div.input-group.compact-no-indent>textarea{display:inline-block;margin-left:.3em;margin-right:.3em}div.basicModal.about-dialog div.basicModal__content h1{font-size:120%;font-weight:700;text-align:center;color:#fff}div.basicModal.about-dialog div.basicModal__content h2{font-weight:700;color:#fff}div.basicModal.about-dialog div.basicModal__content p.update-status.up-to-date-git,div.basicModal.about-dialog div.basicModal__content p.update-status.up-to-date-release{display:none}div.basicModal.about-dialog div.basicModal__content p.about-desc{line-height:1.4em}div.basicModal.downloads div.basicModal__content a.button{display:block;margin:12px 0;padding:12px;font-weight:700;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.02);box-shadow:inset 0 1px 0 rgba(255,255,255,.02);border:1px solid rgba(0,0,0,.2)}div.basicModal.downloads div.basicModal__content a.button .iconic{width:12px;height:12px;margin-right:12px}div.basicModal.qr-code{width:300px}div.basicModal.qr-code div.basicModal__content{padding:12px}.basicModal.import div.basicModal__content{padding:12px 8px}.basicModal.import div.basicModal__content h1{margin-bottom:12px;color:#fff;font-size:16px;line-height:19px;font-weight:700;text-align:center}.basicModal.import div.basicModal__content ol{margin-top:12px;height:300px;background-color:#2c2c2c;overflow:hidden;overflow-y:auto;border-radius:3px;-webkit-box-shadow:inset 0 0 3px rgba(0,0,0,.4);box-shadow:inset 0 0 3px rgba(0,0,0,.4)}.basicModal.import div.basicModal__content ol li{float:left;padding:8px 0;width:100%;background-color:rgba(255,255,255,.02)}.basicModal.import div.basicModal__content ol li:nth-child(2n){background-color:rgba(255,255,255,0)}.basicModal.import div.basicModal__content ol li h2{float:left;padding:5px 10px;width:70%;color:#fff;font-size:14px;white-space:nowrap;overflow:hidden}.basicModal.import div.basicModal__content ol li p.status{float:left;padding:5px 10px;width:30%;color:#999;font-size:14px;text-align:right;-webkit-animation-name:pulse;animation-name:pulse;-webkit-animation-duration:2s;animation-duration:2s;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite}.basicModal.import div.basicModal__content ol li p.status.error,.basicModal.import div.basicModal__content ol li p.status.success,.basicModal.import div.basicModal__content ol li p.status.warning{-webkit-animation:none;animation:none}.basicModal.import div.basicModal__content ol li p.status.error{color:#d92c34}.basicModal.import div.basicModal__content ol li p.status.warning{color:#fc0}.basicModal.import div.basicModal__content ol li p.status.success{color:#0a0}.basicModal.import div.basicModal__content ol li p.notice{float:left;padding:2px 10px 5px;width:100%;color:#999;font-size:12px;overflow:hidden;line-height:16px}.basicModal.import div.basicModal__content ol li p.notice:empty{display:none}div.basicModal.login div.basicModal__content a.button#signInKeyLess{position:absolute;display:block;color:#999;top:8px;left:8px;width:30px;height:30px;margin:0;padding:5px;cursor:pointer;-webkit-box-shadow:inset 1px 1px 0 rgba(255,255,255,.02);box-shadow:inset 1px 1px 0 rgba(255,255,255,.02);border:1px solid rgba(0,0,0,.2)}div.basicModal.login div.basicModal__content a.button#signInKeyLess .iconic{width:100%;height:100%;fill:#999}div.basicModal.login div.basicModal__content p.version{font-size:12px;text-align:right}div.basicModal.login div.basicModal__content p.version span.update-status.up-to-date-git,div.basicModal.login div.basicModal__content p.version span.update-status.up-to-date-release{display:none}@media (hover:hover){#lychee_sidebar .edit:hover .iconic{fill:#fff}#lychee_sidebar #tags .tag:hover{background-color:rgba(0,0,0,.3)}#lychee_sidebar #tags .tag:hover.search{cursor:pointer}#lychee_sidebar #tags .tag:hover span{width:9px;margin:0 0 -2px 5px;-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}#lychee_sidebar #tags .tag span:hover .iconic{fill:#e1575e}div.basicModal.login div.basicModal__content a.button#signInKeyLess:hover{color:#fff;background:inherit}div.basicModal.login div.basicModal__content a.button#signInKeyLess:hover .iconic{fill:#fff}}form.photo-links div.input-group{padding-right:30px}form.photo-links div.input-group a.button{display:block;position:absolute;margin:0;padding:4px;right:0;bottom:0;width:26px;height:26px;cursor:pointer;-webkit-box-shadow:inset 1px 1px 0 rgba(255,255,255,.02);box-shadow:inset 1px 1px 0 rgba(255,255,255,.02);border:1px solid rgba(0,0,0,.2)}form.photo-links div.input-group a.button .iconic{width:100%;height:100%}form.token div.input-group{padding-right:82px}form.token div.input-group input.disabled,form.token div.input-group input[disabled]{color:#999}form.token div.input-group div.button-group{display:block;position:absolute;margin:0;padding:0;right:0;bottom:0;width:78px}form.token div.input-group div.button-group a.button{display:block;float:right;margin:0;padding:4px;bottom:4px;width:26px;height:26px;cursor:pointer;-webkit-box-shadow:inset 1px 1px 0 rgba(255,255,255,.02);box-shadow:inset 1px 1px 0 rgba(255,255,255,.02);border:1px solid rgba(0,0,0,.2)}form.token div.input-group div.button-group a.button .iconic{width:100%;height:100%}#sensitive_warning{background:rgba(100,0,0,.95);text-align:center;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;color:#fff}#sensitive_warning.active{display:-webkit-box;display:-ms-flexbox;display:flex}#sensitive_warning h1{font-size:36px;font-weight:700;border-bottom:2px solid #fff;margin-bottom:15px}#sensitive_warning p{font-size:20px;max-width:40%;margin-top:15px}.settings_view{width:90%;max-width:700px;margin-left:auto;margin-right:auto}.settings_view input.text{padding:9px 2px;width:calc(50% - 4px);background-color:transparent;color:#fff;border:none;border-bottom:1px solid #222;border-radius:0;-webkit-box-shadow:0 1px 0 rgba(255,255,255,.05);box-shadow:0 1px 0 rgba(255,255,255,.05);outline:0}.settings_view input.text:focus{border-bottom-color:#2293ec}.settings_view input.text .error{border-bottom-color:#d92c34}.settings_view .basicModal__button{color:#2293ec;display:inline-block;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.02),inset 1px 0 0 rgba(0,0,0,.2);box-shadow:inset 0 1px 0 rgba(255,255,255,.02),inset 1px 0 0 rgba(0,0,0,.2);border-radius:5px}.settings_view .basicModal__button_MORE,.settings_view .basicModal__button_SAVE{color:#b22027;border-radius:5px}.settings_view>div{font-size:14px;width:100%;padding:12px 0}.settings_view>div p{margin:0 0 5%;width:100%;color:#ccc;line-height:16px}.settings_view>div p a{color:rgba(255,255,255,.9);text-decoration:none;border-bottom:1px dashed #888}.settings_view>div p:last-of-type{margin:0}.settings_view>div input.text{width:100%}.settings_view>div textarea{padding:9px;width:calc(100% - 18px);height:100px;background-color:transparent;color:#fff;border:1px solid #666;border-radius:0;-webkit-box-shadow:0 1px 0 rgba(255,255,255,.05);box-shadow:0 1px 0 rgba(255,255,255,.05);outline:0;resize:vertical}.settings_view>div textarea:focus{border-color:#2293ec}.settings_view>div .choice{padding:0 30px 15px;width:100%;color:#fff}.settings_view>div .choice:last-child{padding-bottom:40px}.settings_view>div .choice label{float:left;color:#fff;font-size:14px;font-weight:700}.settings_view>div .choice label input{position:absolute;margin:0;opacity:0}.settings_view>div .choice label .checkbox{float:left;display:block;width:16px;height:16px;background:rgba(0,0,0,.5);border-radius:3px;-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.7);box-shadow:0 0 0 1px rgba(0,0,0,.7)}.settings_view>div .choice label .checkbox .iconic{-webkit-box-sizing:border-box;box-sizing:border-box;fill:#2293ec;padding:2px;opacity:0;-ms-transform:scale(0);-webkit-transform:scale(0);transform:scale(0);-webkit-transition:opacity .2s cubic-bezier(.51,.92,.24,1),-webkit-transform .2s cubic-bezier(.51,.92,.24,1);transition:opacity .2s cubic-bezier(.51,.92,.24,1),transform .2s cubic-bezier(.51,.92,.24,1),-webkit-transform .2s cubic-bezier(.51,.92,.24,1);-o-transition:opacity .2s cubic-bezier(.51,.92,.24,1),transform .2s cubic-bezier(.51,.92,.24,1)}.settings_view>div .select{position:relative;margin:1px 5px;padding:0;width:110px;color:#fff;border-radius:3px;border:1px solid rgba(0,0,0,.2);-webkit-box-shadow:0 1px 0 rgba(255,255,255,.02);box-shadow:0 1px 0 rgba(255,255,255,.02);font-size:11px;line-height:16px;overflow:hidden;outline:0;vertical-align:middle;background:rgba(0,0,0,.3);display:inline-block}.settings_view>div .select select{margin:0;padding:4px 8px;width:120%;color:#fff;font-size:11px;line-height:16px;border:0;outline:0;-webkit-box-shadow:none;box-shadow:none;border-radius:0;background-color:transparent;background-image:none;-moz-appearance:none;-webkit-appearance:none;appearance:none}.settings_view>div .select select option{margin:0;padding:0;background:#fff;color:#333;-webkit-transition:none;-o-transition:none;transition:none}.settings_view>div .select select:disabled{color:#000;cursor:not-allowed}.settings_view>div .select::after{position:absolute;content:"≡";right:8px;top:4px;color:#2293ec;font-size:16px;line-height:16px;font-weight:700;pointer-events:none}.settings_view>div .switch{position:relative;display:inline-block;width:42px;height:22px;bottom:-2px;line-height:24px}.settings_view>div .switch input{opacity:0;width:0;height:0}.settings_view>div .slider{position:absolute;cursor:pointer;top:0;left:0;right:0;bottom:0;border:1px solid rgba(0,0,0,.2);-webkit-box-shadow:0 1px 0 rgba(255,255,255,.02);box-shadow:0 1px 0 rgba(255,255,255,.02);background:rgba(0,0,0,.3);-webkit-transition:.4s;-o-transition:.4s;transition:.4s}.settings_view>div .slider:before{position:absolute;content:"";height:14px;width:14px;left:3px;bottom:3px;background-color:#2293ec}.settings_view>div input:checked+.slider{background-color:#2293ec}.settings_view>div input:checked+.slider:before{-ms-transform:translateX(20px);-webkit-transform:translateX(20px);transform:translateX(20px);background-color:#fff}.settings_view>div .slider.round{border-radius:20px}.settings_view>div .slider.round:before{border-radius:50%}.settings_view .setting_category{font-size:20px;width:100%;padding-top:10px;padding-left:4px;border-bottom:1px dotted #222;margin-top:20px;color:#fff;font-weight:700;text-transform:capitalize}.settings_view .setting_line{font-size:14px;width:100%}.settings_view .setting_line:first-child,.settings_view .setting_line:last-child{padding-top:50px}.settings_view .setting_line p{min-width:550px;margin:0;color:#ccc;display:inline-block;width:100%;overflow-wrap:break-word}.settings_view .setting_line p a{color:rgba(255,255,255,.9);text-decoration:none;border-bottom:1px dashed #888}.settings_view .setting_line p:last-of-type{margin:0}.settings_view .setting_line p .warning{margin-bottom:30px;color:#d92c34;font-weight:700;font-size:18px;text-align:justify;line-height:22px}.settings_view .setting_line span.text{display:inline-block;padding:9px 4px;width:calc(50% - 12px);background-color:transparent;color:#fff;border:none}.settings_view .setting_line span.text_icon{width:5%}.settings_view .setting_line span.text_icon .iconic{width:15px;height:14px;margin:0 10px 0 1px;fill:#fff}.settings_view .setting_line input.text{width:calc(50% - 4px)}@media (hover:hover){.settings_view .basicModal__button:hover{background:#2293ec;color:#fff;cursor:pointer}.settings_view .basicModal__button_MORE:hover,.settings_view .basicModal__button_SAVE:hover{background:#b22027;color:#fff}.settings_view input:hover{border-bottom:1px solid #2293ec}}@media (hover:none){#lychee_left_menu a{padding:14px 8px 14px 32px}.settings_view input.text{border-bottom:1px solid #2293ec;margin:6px 0}.settings_view>div{padding:16px 0}.settings_view .basicModal__button{background:#2293ec;color:#fff;max-width:320px;margin-top:20px}.settings_view .basicModal__button_MORE,.settings_view .basicModal__button_SAVE{background:#b22027}}@media only screen and (max-width:567px),only screen and (max-width:640px) and (orientation:portrait){.settings_view{max-width:100%}.settings_view .setting_category{font-size:14px;padding-left:0;margin-bottom:4px}.settings_view .setting_line{font-size:12px}.settings_view .setting_line:first-child{padding-top:20px}.settings_view .setting_line p{min-width:unset;line-height:20px}.settings_view .setting_line p.warning{font-size:14px;line-height:16px;margin-bottom:0}.settings_view .setting_line p input,.settings_view .setting_line p span{padding:0}.settings_view .basicModal__button_SAVE{margin-top:20px}}.users_view{width:90%;max-width:700px;margin-left:auto;margin-right:auto}.users_view_line{font-size:14px;width:100%}.users_view_line:first-child,.users_view_line:last-child{padding-top:50px}.users_view_line p{width:550px;margin:0 0 5%;color:#ccc;display:inline-block}.users_view_line p a{color:rgba(255,255,255,.9);text-decoration:none;border-bottom:1px dashed #888}.users_view_line p.line,.users_view_line p:last-of-type{margin:0}.users_view_line span.text{display:inline-block;padding:9px 6px 9px 0;width:40%;background-color:transparent;color:#fff;border:none}.users_view_line span.text_icon{width:5%;min-width:32px}.users_view_line span.text_icon .iconic{width:15px;height:14px;margin:0 8px;fill:#fff}.users_view_line input.text{padding:9px 6px 9px 0;width:40%;background-color:transparent;color:#fff;border:none;border-bottom:1px solid #222;border-radius:0;-webkit-box-shadow:0 1px 0 rgba(255,255,255,.05);box-shadow:0 1px 0 rgba(255,255,255,.05);outline:0;margin:0 0 10px}.users_view_line input.text:focus{border-bottom-color:#2293ec}.users_view_line input.text.error{border-bottom-color:#d92c34}.users_view_line .choice label input:checked~.checkbox .iconic{opacity:1;-ms-transform:scale(1);-webkit-transform:scale(1);transform:scale(1)}.users_view_line .choice{display:inline-block;width:5%;min-width:32px;color:#fff}.users_view_line .choice input{position:absolute;margin:0;opacity:0}.users_view_line .choice .checkbox{display:inline-block;width:16px;height:16px;margin:10px 8px 0;background:rgba(0,0,0,.5);border-radius:3px;-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.7);box-shadow:0 0 0 1px rgba(0,0,0,.7)}.users_view_line .choice .checkbox .iconic{-webkit-box-sizing:border-box;box-sizing:border-box;fill:#2293ec;padding:2px;opacity:0;-ms-transform:scale(0);-webkit-transform:scale(0);transform:scale(0);-webkit-transition:opacity .2s cubic-bezier(.51,.92,.24,1),-webkit-transform .2s cubic-bezier(.51,.92,.24,1);transition:opacity .2s cubic-bezier(.51,.92,.24,1),transform .2s cubic-bezier(.51,.92,.24,1),-webkit-transform .2s cubic-bezier(.51,.92,.24,1);-o-transition:opacity .2s cubic-bezier(.51,.92,.24,1),transform .2s cubic-bezier(.51,.92,.24,1)}.users_view_line .basicModal__button{display:inline-block;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.02),inset 1px 0 0 rgba(0,0,0,.2);box-shadow:inset 0 1px 0 rgba(255,255,255,.02),inset 1px 0 0 rgba(0,0,0,.2);width:10%;min-width:72px;border-radius:0}.users_view_line .basicModal__button_OK{color:#2293ec;border-radius:5px 0 0 5px;margin-right:-4px}.users_view_line .basicModal__button_DEL{color:#b22027;border-radius:0 5px 5px 0}.users_view_line .basicModal__button_CREATE{width:20%;color:#090;border-radius:5px;min-width:144px}.users_view_line .select{position:relative;margin:1px 5px;padding:0;width:110px;color:#fff;border-radius:3px;border:1px solid rgba(0,0,0,.2);-webkit-box-shadow:0 1px 0 rgba(255,255,255,.02);box-shadow:0 1px 0 rgba(255,255,255,.02);font-size:11px;line-height:16px;overflow:hidden;outline:0;vertical-align:middle;background:rgba(0,0,0,.3);display:inline-block}.users_view_line .select select{margin:0;padding:4px 8px;width:120%;color:#fff;font-size:11px;line-height:16px;border:0;outline:0;-webkit-box-shadow:none;box-shadow:none;border-radius:0;background:0 0;-moz-appearance:none;-webkit-appearance:none;appearance:none}.users_view_line .select select option{margin:0;padding:0;background:#fff;color:#333;-webkit-transition:none;-o-transition:none;transition:none}.users_view_line .select::after{position:absolute;content:"≡";right:8px;top:4px;color:#2293ec;font-size:16px;line-height:16px;font-weight:700;pointer-events:none}@media (hover:hover){.users_view_line .basicModal__button:hover{cursor:pointer;color:#fff}.users_view_line .basicModal__button_OK:hover{background:#2293ec}.users_view_line .basicModal__button_DEL:hover{background:#b22027}.users_view_line .basicModal__button_CREATE:hover{background:#090}.users_view_line input:hover{border-bottom:1px solid #2293ec}}@media (hover:none){.users_view_line .basicModal__button{color:#fff}.users_view_line .basicModal__button_OK{background:#2293ec}.users_view_line .basicModal__button_DEL{background:#b22027}.users_view_line .basicModal__button_CREATE{background:#090}.users_view_line input{border-bottom:1px solid #2293ec}}@media only screen and (max-width:567px),only screen and (max-width:640px) and (orientation:portrait){.users_view{width:100%;max-width:100%;padding:20px}.users_view_line p{width:100%}.users_view_line p .text,.users_view_line p input.text{width:36%;font-size:smaller}.users_view_line .choice{margin-left:-8px;margin-right:3px}}.u2f_view{width:90%;max-width:700px;margin-left:auto;margin-right:auto}.u2f_view_line{font-size:14px;width:100%}.u2f_view_line:first-child,.u2f_view_line:last-child{padding-top:50px}.u2f_view_line p{width:550px;margin:0 0 5%;color:#ccc;display:inline-block}.u2f_view_line p a{color:rgba(255,255,255,.9);text-decoration:none;border-bottom:1px dashed #888}.u2f_view_line p.line,.u2f_view_line p:last-of-type{margin:0}.u2f_view_line p.single{text-align:center}.u2f_view_line span.text{display:inline-block;padding:9px 4px;width:80%;background-color:transparent;color:#fff;border:none}.u2f_view_line span.text_icon{width:5%}.u2f_view_line span.text_icon .iconic{width:15px;height:14px;margin:0 15px 0 1px;fill:#fff}.u2f_view_line .choice label input:checked~.checkbox .iconic{opacity:1;-ms-transform:scale(1);-webkit-transform:scale(1);transform:scale(1)}.u2f_view_line .choice{display:inline-block;width:5%;color:#fff}.u2f_view_line .choice input{position:absolute;margin:0;opacity:0}.u2f_view_line .choice .checkbox{display:inline-block;width:16px;height:16px;margin-top:10px;margin-left:2px;background:rgba(0,0,0,.5);border-radius:3px;-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.7);box-shadow:0 0 0 1px rgba(0,0,0,.7)}.u2f_view_line .choice .checkbox .iconic{-webkit-box-sizing:border-box;box-sizing:border-box;fill:#2293ec;padding:2px;opacity:0;-ms-transform:scale(0);-webkit-transform:scale(0);transform:scale(0);-webkit-transition:opacity .2s cubic-bezier(.51,.92,.24,1),-webkit-transform .2s cubic-bezier(.51,.92,.24,1);transition:opacity .2s cubic-bezier(.51,.92,.24,1),transform .2s cubic-bezier(.51,.92,.24,1),-webkit-transform .2s cubic-bezier(.51,.92,.24,1);-o-transition:opacity .2s cubic-bezier(.51,.92,.24,1),transform .2s cubic-bezier(.51,.92,.24,1)}.u2f_view_line .basicModal__button{display:inline-block;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.02),inset 1px 0 0 rgba(0,0,0,.2);box-shadow:inset 0 1px 0 rgba(255,255,255,.02),inset 1px 0 0 rgba(0,0,0,.2);width:20%;min-width:50px;border-radius:0}.u2f_view_line .basicModal__button_OK{color:#2293ec;border-radius:5px 0 0 5px}.u2f_view_line .basicModal__button_DEL{color:#b22027;border-radius:0 5px 5px 0}.u2f_view_line .basicModal__button_CREATE{width:100%;color:#090;border-radius:5px}.u2f_view_line .select{position:relative;margin:1px 5px;padding:0;width:110px;color:#fff;border-radius:3px;border:1px solid rgba(0,0,0,.2);-webkit-box-shadow:0 1px 0 rgba(255,255,255,.02);box-shadow:0 1px 0 rgba(255,255,255,.02);font-size:11px;line-height:16px;overflow:hidden;outline:0;vertical-align:middle;background:rgba(0,0,0,.3);display:inline-block}.u2f_view_line .select select{margin:0;padding:4px 8px;width:120%;color:#fff;font-size:11px;line-height:16px;border:0;outline:0;-webkit-box-shadow:none;box-shadow:none;border-radius:0;background:0 0;-moz-appearance:none;-webkit-appearance:none;appearance:none}.u2f_view_line .select select option{margin:0;padding:0;background:#fff;color:#333;-webkit-transition:none;-o-transition:none;transition:none}.u2f_view_line .select::after{position:absolute;content:"≡";right:8px;top:4px;color:#2293ec;font-size:16px;line-height:16px;font-weight:700;pointer-events:none}@media (hover:hover){.u2f_view_line .basicModal__button:hover{cursor:pointer}.u2f_view_line .basicModal__button_OK:hover{background:#2293ec;color:#fff}.u2f_view_line .basicModal__button_DEL:hover{background:#b22027;color:#fff}.u2f_view_line .basicModal__button_CREATE:hover{background:#090;color:#fff}.u2f_view_line input:hover{border-bottom:1px solid #2293ec}}@media (hover:none){.u2f_view_line .basicModal__button{color:#fff}.u2f_view_line .basicModal__button_OK{background:#2293ec}.u2f_view_line .basicModal__button_DEL{background:#b22027}.u2f_view_line .basicModal__button_CREATE{background:#090}.u2f_view_line input{border-bottom:1px solid #2293ec}}@media only screen and (max-width:567px),only screen and (max-width:640px) and (orientation:portrait){.u2f_view{width:100%;max-width:100%;padding:20px}.u2f_view_line p{width:100%}.u2f_view_line .basicModal__button_CREATE{width:80%;margin:0 10%}}.logs_diagnostics_view{width:90%;margin-left:auto;margin-right:auto;color:#ccc;font-size:12px;line-height:14px}.logs_diagnostics_view pre{font-family:monospace;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;padding-right:30px}.clear_logs_update{padding-left:30px;margin:20px auto}.clear_logs_update .basicModal__button,.logs_diagnostics_view .basicModal__button{color:#2293ec;display:inline-block;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.02),inset 1px 0 0 rgba(0,0,0,.2);box-shadow:inset 0 1px 0 rgba(255,255,255,.02),inset 1px 0 0 rgba(0,0,0,.2);border-radius:5px}.clear_logs_update .iconic,.logs_diagnostics_view .iconic{display:inline-block;margin:0 10px 0 1px;width:13px;height:12px;fill:#2293ec}.clear_logs_update .button_left,.logs_diagnostics_view .button_left{margin-left:24px;width:400px}@media (hover:none){.clear_logs_update .basicModal__button,.logs_diagnostics_view .basicModal__button{background:#2293ec;color:#fff;max-width:320px;margin-top:20px}.clear_logs_update .iconic,.logs_diagnostics_view .iconic{fill:#fff}}@media only screen and (max-width:567px),only screen and (max-width:640px) and (orientation:portrait){.clear_logs_update,.logs_diagnostics_view{width:100%;max-width:100%;font-size:11px;line-height:12px}.clear_logs_update .basicModal__button,.clear_logs_update .button_left,.logs_diagnostics_view .basicModal__button,.logs_diagnostics_view .button_left{width:80%;margin:0 10%}.logs_diagnostics_view{padding:10px 10px 0 0}.clear_logs_update{padding:10px 10px 0;margin:0}}.sharing_view{width:90%;max-width:700px;margin-left:auto;margin-right:auto;margin-top:20px}.sharing_view .sharing_view_line{width:100%;display:block;clear:left}.sharing_view .col-xs-1,.sharing_view .col-xs-10,.sharing_view .col-xs-11,.sharing_view .col-xs-12,.sharing_view .col-xs-2,.sharing_view .col-xs-3,.sharing_view .col-xs-4,.sharing_view .col-xs-5,.sharing_view .col-xs-6,.sharing_view .col-xs-7,.sharing_view .col-xs-8,.sharing_view .col-xs-9{float:left;position:relative;min-height:1px}.sharing_view .col-xs-2{width:10%;padding-right:3%;padding-left:3%}.sharing_view .col-xs-5{width:42%}.sharing_view .btn-block+.btn-block{margin-top:5px}.sharing_view .btn-block{display:block;width:100%}.sharing_view .btn-default{color:#2293ec;border-color:#2293ec;background:rgba(0,0,0,.5);border-radius:3px;-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.7);box-shadow:0 0 0 1px rgba(0,0,0,.7)}.sharing_view .btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.sharing_view select[multiple],.sharing_view select[size]{height:150px}.sharing_view .form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color .15s ease-in-out,-webkit-box-shadow .15s ease-in-out;-o-transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-box-shadow .15s ease-in-out}.sharing_view .iconic{display:inline-block;width:15px;height:14px;fill:#2293ec}.sharing_view .iconic .iconic.ionicons{margin:0 8px -2px 0;width:18px;height:18px}.sharing_view .blue .iconic{fill:#2293ec}.sharing_view .grey .iconic{fill:#b4b4b4}.sharing_view p{width:100%;color:#ccc;text-align:center;font-size:14px;display:block}.sharing_view p.with{padding:15px 0}.sharing_view span.text{display:inline-block;padding:0 2px;width:40%;background-color:transparent;color:#fff;border:none}.sharing_view span.text:last-of-type{width:5%}.sharing_view span.text .iconic{width:15px;height:14px;margin:0 10px 0 1px;fill:#fff}.sharing_view .basicModal__button{margin-top:10px;color:#2293ec;display:inline-block;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.02),inset 1px 0 0 rgba(0,0,0,.2);box-shadow:inset 0 1px 0 rgba(255,255,255,.02),inset 1px 0 0 rgba(0,0,0,.2);border-radius:5px}.sharing_view .choice label input:checked~.checkbox .iconic{opacity:1;-ms-transform:scale(1);-webkit-transform:scale(1);transform:scale(1)}.sharing_view .choice{display:inline-block;width:5%;margin:0 10px;color:#fff}.sharing_view .choice input{position:absolute;margin:0;opacity:0}.sharing_view .choice .checkbox{display:inline-block;width:16px;height:16px;margin-top:10px;margin-left:2px;background:rgba(0,0,0,.5);border-radius:3px;-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.7);box-shadow:0 0 0 1px rgba(0,0,0,.7)}.sharing_view .choice .checkbox .iconic{-webkit-box-sizing:border-box;box-sizing:border-box;fill:#2293ec;padding:2px;opacity:0;-ms-transform:scale(0);-webkit-transform:scale(0);transform:scale(0);-webkit-transition:opacity .2s cubic-bezier(.51,.92,.24,1),-webkit-transform .2s cubic-bezier(.51,.92,.24,1);transition:opacity .2s cubic-bezier(.51,.92,.24,1),transform .2s cubic-bezier(.51,.92,.24,1),-webkit-transform .2s cubic-bezier(.51,.92,.24,1);-o-transition:opacity .2s cubic-bezier(.51,.92,.24,1),transform .2s cubic-bezier(.51,.92,.24,1)}.sharing_view .select{position:relative;padding:0;color:#fff;border-radius:3px;border:1px solid rgba(0,0,0,.2);-webkit-box-shadow:0 1px 0 rgba(255,255,255,.02);box-shadow:0 1px 0 rgba(255,255,255,.02);font-size:14px;line-height:16px;outline:0;vertical-align:middle;background:rgba(0,0,0,.3);display:inline-block}.sharing_view .borderBlue{border:1px solid #2293ec}@media (hover:none){.sharing_view .basicModal__button{background:#2293ec;color:#fff}.sharing_view input{border-bottom:1px solid #2293ec}}@media only screen and (max-width:567px),only screen and (max-width:640px) and (orientation:portrait){.sharing_view{width:100%;max-width:100%;padding:10px}.sharing_view .select{font-size:12px}.sharing_view .iconic{margin-left:-4px}.sharing_view_line p{width:100%}.sharing_view_line .basicModal__button{width:80%;margin:0 10%}}#multiselect{position:absolute;background-color:rgba(0,94,204,.3);border:1px solid #005ecc;border-radius:3px;z-index:5}.justified-layout,.unjustified-layout{margin:30px;width:100%;position:relative}.justified-layout.laying-out,.unjustified-layout.laying-out{display:none}.justified-layout>.photo{position:absolute;--lychee-default-height:320px;margin:0}.unjustified-layout>.photo{float:left;max-height:240px;margin:5px}.justified-layout>.photo>.thumbimg,.justified-layout>.photo>.thumbimg>img,.unjustified-layout>.photo>.thumbimg,.unjustified-layout>.photo>.thumbimg>img{width:100%;height:100%;border:none;-o-object-fit:cover;object-fit:cover}.justified-layout>.photo>.overlay,.unjustified-layout>.photo>.overlay{width:100%;bottom:0;margin:0}.justified-layout>.photo>.overlay>h1,.unjustified-layout>.photo>.overlay>h1{width:auto;margin-right:15px}@media only screen and (min-width:320px) and (max-width:567px){.justified-layout{margin:8px}.justified-layout .photo{--lychee-default-height:160px}}@media only screen and (min-width:568px) and (max-width:639px){.justified-layout{margin:9px}.justified-layout .photo{--lychee-default-height:200px}}@media only screen and (min-width:640px) and (max-width:768px){.justified-layout{margin:10px}.justified-layout .photo{--lychee-default-height:240px}}#lychee_footer{text-align:center;padding:5px 0;background:#1d1d1d;-webkit-transition:color .3s,opacity .3s ease-out,margin-left .5s,-webkit-transform .3s ease-out,-webkit-box-shadow .3s;transition:color .3s,opacity .3s ease-out,transform .3s ease-out,box-shadow .3s,margin-left .5s,-webkit-transform .3s ease-out,-webkit-box-shadow .3s;-o-transition:color .3s,opacity .3s ease-out,transform .3s ease-out,box-shadow .3s,margin-left .5s}#lychee_footer p{color:#ccc;font-size:.75em;font-weight:400;line-height:26px}#lychee_footer p a,#lychee_footer p a:visited{color:#ccc}#lychee_footer p.home_copyright,#lychee_footer p.hosted_by{text-transform:uppercase}#lychee_footer #home_socials a[href=""],#lychee_footer p:empty,.hide_footer,body.mode-frame div#footer,body.mode-none div#footer{display:none}@font-face{font-family:socials;src:url(fonts/socials.eot?egvu10);src:url(fonts/socials.eot?egvu10#iefix) format("embedded-opentype"),url(fonts/socials.ttf?egvu10) format("truetype"),url(fonts/socials.woff?egvu10) format("woff"),url(fonts/socials.svg?egvu10#socials) format("svg");font-weight:400;font-style:normal}#socials_footer{padding:0;text-align:center;left:0;right:0}.socialicons{display:inline-block;font-size:18px;font-family:socials!important;speak:none;color:#ccc;text-decoration:none;margin:15px 15px 5px;transition:.3s;-webkit-transition:.3s;-moz-transition:.3s;-o-transition:.3s}#twitter:before{content:"\ea96"}#instagram:before{content:"\ea92"}#youtube:before{content:"\ea9d"}#flickr:before{content:"\eaa4"}#facebook:before{content:"\ea91"}@media (hover:hover){.sharing_view .basicModal__button:hover{background:#2293ec;color:#fff;cursor:pointer}.sharing_view input:hover{border-bottom:1px solid #2293ec}.socialicons:hover{color:#b5b5b5;-ms-transform:scale(1.3);transform:scale(1.3);-webkit-transform:scale(1.3)}}@media tv{.basicModal__button:focus{background:#2293ec;color:#fff;cursor:pointer;outline-style:none}.basicModal__button#basicModal__action:focus{color:#fff}.photo:focus{outline:#fff solid 10px}.album:focus{outline-width:0}.toolbar .button:focus{outline-width:0;background-color:#fff}.header__title:focus{outline-width:0;background-color:#fff;color:#000}.toolbar .button:focus .iconic{fill:#000}#imageview{background-color:#000}#imageview #image,#imageview #livephoto{outline-width:0}}#lychee_view_container{position:absolute;top:0;left:0;height:100%;width:100%;overflow:clip auto}.leaflet-image-layer,.leaflet-layer,.leaflet-marker-icon,.leaflet-marker-shadow,.leaflet-pane,.leaflet-pane>canvas,.leaflet-pane>svg,.leaflet-tile,.leaflet-tile-container,.leaflet-zoom-box{position:absolute;left:0;top:0}.leaflet-container{overflow:hidden;-webkit-tap-highlight-color:transparent;background:#ddd;outline-offset:1px;font-family:"Helvetica Neue",Arial,Helvetica,sans-serif;font-size:.75rem;line-height:1.5}.leaflet-marker-icon,.leaflet-marker-shadow,.leaflet-tile{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-user-drag:none}.leaflet-tile::-moz-selection{background:0 0}.leaflet-tile::selection{background:0 0}.leaflet-safari .leaflet-tile{image-rendering:-webkit-optimize-contrast}.leaflet-safari .leaflet-tile-container{width:1600px;height:1600px;-webkit-transform-origin:0 0}.leaflet-marker-icon,.leaflet-marker-shadow{display:block}.leaflet-container .leaflet-overlay-pane svg{max-width:none!important;max-height:none!important}.leaflet-container .leaflet-marker-pane img,.leaflet-container .leaflet-shadow-pane img,.leaflet-container .leaflet-tile,.leaflet-container .leaflet-tile-pane img,.leaflet-container img.leaflet-image-layer{max-width:none!important;max-height:none!important;width:auto;padding:0}.leaflet-container.leaflet-touch-zoom{-ms-touch-action:pan-x pan-y;touch-action:pan-x pan-y}.leaflet-container.leaflet-touch-drag{-ms-touch-action:pinch-zoom;touch-action:none;touch-action:pinch-zoom}.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom{-ms-touch-action:none;touch-action:none}.leaflet-container a{-webkit-tap-highlight-color:rgba(51,181,229,.4);color:#0078a8}.leaflet-tile{-webkit-filter:inherit;filter:inherit;visibility:hidden}.leaflet-tile-loaded{visibility:inherit}.leaflet-zoom-box{width:0;height:0;-webkit-box-sizing:border-box;box-sizing:border-box;z-index:800}.leaflet-overlay-pane svg{-moz-user-select:none}.leaflet-pane{z-index:400}.leaflet-tile-pane{z-index:200}.leaflet-overlay-pane{z-index:400}.leaflet-shadow-pane{z-index:500}.leaflet-marker-pane{z-index:600}.leaflet-tooltip-pane{z-index:650}.leaflet-popup-pane{z-index:700}.leaflet-map-pane canvas{z-index:100}.leaflet-map-pane svg{z-index:200}.leaflet-vml-shape{width:1px;height:1px}.lvml{behavior:url(#default#VML);display:inline-block;position:absolute}.leaflet-control{position:relative;z-index:800;pointer-events:visiblePainted;pointer-events:auto;float:left;clear:both}.leaflet-bottom,.leaflet-top{position:absolute;z-index:1000;pointer-events:none}.leaflet-top{top:0}.leaflet-right{right:0}.leaflet-bottom{bottom:0}.leaflet-left{left:0}.leaflet-right .leaflet-control{float:right;margin-right:10px}.leaflet-top .leaflet-control{margin-top:10px}.leaflet-bottom .leaflet-control{margin-bottom:10px}.leaflet-left .leaflet-control{margin-left:10px}.leaflet-fade-anim .leaflet-popup{opacity:0;-webkit-transition:opacity .2s linear;-o-transition:opacity .2s linear;transition:opacity .2s linear}.leaflet-fade-anim .leaflet-map-pane .leaflet-popup{opacity:1}.leaflet-zoom-animated{-webkit-transform-origin:0 0;-ms-transform-origin:0 0;transform-origin:0 0}svg.leaflet-zoom-animated{will-change:transform}.leaflet-zoom-anim .leaflet-zoom-animated{-webkit-transition:-webkit-transform .25s cubic-bezier(0,0,.25,1);transition:transform .25s cubic-bezier(0,0,.25,1);-o-transition:transform .25s cubic-bezier(0,0,.25,1);transition:transform .25s cubic-bezier(0,0,.25,1),-webkit-transform .25s cubic-bezier(0,0,.25,1)}.leaflet-pan-anim .leaflet-tile,.leaflet-zoom-anim .leaflet-tile{-webkit-transition:none;-o-transition:none;transition:none}.leaflet-zoom-anim .leaflet-zoom-hide{visibility:hidden}.leaflet-interactive{cursor:pointer}.leaflet-grab{cursor:-webkit-grab;cursor:grab}.leaflet-crosshair,.leaflet-crosshair .leaflet-interactive{cursor:crosshair}.leaflet-control,.leaflet-popup-pane{cursor:auto}.leaflet-dragging .leaflet-grab,.leaflet-dragging .leaflet-grab .leaflet-interactive,.leaflet-dragging .leaflet-marker-draggable{cursor:move;cursor:-webkit-grabbing;cursor:grabbing}.leaflet-image-layer,.leaflet-marker-icon,.leaflet-marker-shadow,.leaflet-pane>svg path,.leaflet-tile-container{pointer-events:none}.leaflet-image-layer.leaflet-interactive,.leaflet-marker-icon.leaflet-interactive,.leaflet-pane>svg path.leaflet-interactive,svg.leaflet-image-layer.leaflet-interactive path{pointer-events:visiblePainted;pointer-events:auto}.leaflet-zoom-box{border:2px dotted #38f;background:rgba(255,255,255,.5)}.leaflet-bar{-webkit-box-shadow:0 1px 5px rgba(0,0,0,.65);box-shadow:0 1px 5px rgba(0,0,0,.65);border-radius:4px}.leaflet-bar a{background-color:#fff;border-bottom:1px solid #ccc;width:26px;height:26px;line-height:26px;text-align:center;text-decoration:none;color:#000}.leaflet-bar a,.leaflet-control-layers-toggle{background-position:50% 50%;background-repeat:no-repeat;display:block}.leaflet-bar a:focus,.leaflet-bar a:hover{background-color:#f4f4f4}.leaflet-bar a:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.leaflet-bar a:last-child{border-bottom-left-radius:4px;border-bottom-right-radius:4px;border-bottom:none}.leaflet-bar a.leaflet-disabled{cursor:default;background-color:#f4f4f4;color:#bbb}.leaflet-touch .leaflet-bar a{width:30px;height:30px;line-height:30px}.leaflet-touch .leaflet-bar a:first-child{border-top-left-radius:2px;border-top-right-radius:2px}.leaflet-touch .leaflet-bar a:last-child{border-bottom-left-radius:2px;border-bottom-right-radius:2px}.leaflet-control-zoom-in,.leaflet-control-zoom-out{font:bold 18px "Lucida Console",Monaco,monospace;text-indent:1px}.leaflet-touch .leaflet-control-zoom-in,.leaflet-touch .leaflet-control-zoom-out{font-size:22px}.leaflet-control-layers{-webkit-box-shadow:0 1px 5px rgba(0,0,0,.4);box-shadow:0 1px 5px rgba(0,0,0,.4);background:#fff;border-radius:5px}.leaflet-control-layers-toggle{background-image:url(images/layers.png);width:36px;height:36px}.leaflet-retina .leaflet-control-layers-toggle{background-image:url(images/layers-2x.png);background-size:26px 26px}.leaflet-touch .leaflet-control-layers-toggle{width:44px;height:44px}.leaflet-control-layers .leaflet-control-layers-list,.leaflet-control-layers-expanded .leaflet-control-layers-toggle{display:none}.leaflet-control-layers-expanded .leaflet-control-layers-list{display:block;position:relative}.leaflet-control-layers-expanded{padding:6px 10px 6px 6px;color:#333;background:#fff}.leaflet-control-layers-scrollbar{overflow-y:scroll;overflow-x:hidden;padding-right:5px}.leaflet-control-layers-selector{margin-top:2px;position:relative;top:1px}.leaflet-control-layers label{display:block;font-size:1.08333em}.leaflet-control-layers-separator{height:0;border-top:1px solid #ddd;margin:5px -10px 5px -6px}.leaflet-default-icon-path{background-image:url(images/marker-icon.png)}.leaflet-container .leaflet-control-attribution{background:rgba(255,255,255,.8);margin:0}.leaflet-control-attribution,.leaflet-control-scale-line{padding:0 5px;color:#333;line-height:1.4}.leaflet-control-attribution a{text-decoration:none}.leaflet-control-attribution a:focus,.leaflet-control-attribution a:hover{text-decoration:underline}.leaflet-attribution-flag{display:inline!important;vertical-align:baseline!important;width:1em;height:.6669em}.leaflet-left .leaflet-control-scale{margin-left:5px}.leaflet-bottom .leaflet-control-scale{margin-bottom:5px}.leaflet-control-scale-line{border:2px solid #777;border-top:none;line-height:1.1;padding:2px 5px 1px;white-space:nowrap;overflow:hidden;-webkit-box-sizing:border-box;box-sizing:border-box;background:rgba(255,255,255,.5)}.leaflet-control-scale-line:not(:first-child){border-top:2px solid #777;border-bottom:none;margin-top:-2px}.leaflet-control-scale-line:not(:first-child):not(:last-child){border-bottom:2px solid #777}.leaflet-touch .leaflet-bar,.leaflet-touch .leaflet-control-attribution,.leaflet-touch .leaflet-control-layers{-webkit-box-shadow:none;box-shadow:none}.leaflet-touch .leaflet-bar,.leaflet-touch .leaflet-control-layers{border:2px solid rgba(0,0,0,.2);background-clip:padding-box}.leaflet-popup{position:absolute;text-align:center;margin-bottom:20px}.leaflet-popup-content-wrapper{padding:1px;text-align:left;border-radius:12px}.leaflet-popup-content{margin:13px 24px 13px 20px;line-height:1.3;font-size:1.08333em;min-height:1px}.leaflet-popup-content p{margin:1.3em 0}.leaflet-popup-tip-container{width:40px;height:20px;position:absolute;left:50%;margin-top:-1px;margin-left:-20px;overflow:hidden;pointer-events:none}.leaflet-popup-tip{width:17px;height:17px;padding:1px;margin:-10px auto 0;pointer-events:auto;-webkit-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg)}.leaflet-popup-content-wrapper,.leaflet-popup-tip{background:#fff;color:#333;-webkit-box-shadow:0 3px 14px rgba(0,0,0,.4);box-shadow:0 3px 14px rgba(0,0,0,.4)}.leaflet-container a.leaflet-popup-close-button{position:absolute;top:0;right:0;border:none;text-align:center;width:24px;height:24px;font:16px/24px Tahoma,Verdana,sans-serif;color:#757575;text-decoration:none;background:0 0}.leaflet-container a.leaflet-popup-close-button:focus,.leaflet-container a.leaflet-popup-close-button:hover{color:#585858}.leaflet-popup-scrolled{overflow:auto}.leaflet-oldie .leaflet-popup-content-wrapper{-ms-zoom:1}.leaflet-oldie .leaflet-popup-tip{width:24px;margin:0 auto}.leaflet-oldie .leaflet-control-layers,.leaflet-oldie .leaflet-control-zoom,.leaflet-oldie .leaflet-popup-content-wrapper,.leaflet-oldie .leaflet-popup-tip{border:1px solid #999}.leaflet-div-icon{background:#fff;border:1px solid #666}.leaflet-tooltip{position:absolute;padding:6px;background-color:#fff;border:1px solid #fff;border-radius:3px;color:#222;white-space:nowrap;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;pointer-events:none;-webkit-box-shadow:0 1px 3px rgba(0,0,0,.4);box-shadow:0 1px 3px rgba(0,0,0,.4)}.leaflet-tooltip.leaflet-interactive{cursor:pointer;pointer-events:auto}.leaflet-tooltip-bottom:before,.leaflet-tooltip-left:before,.leaflet-tooltip-right:before,.leaflet-tooltip-top:before{position:absolute;pointer-events:none;border:6px solid transparent;background:0 0;content:""}.leaflet-tooltip-bottom{margin-top:6px}.leaflet-tooltip-top{margin-top:-6px}.leaflet-tooltip-bottom:before,.leaflet-tooltip-top:before{left:50%;margin-left:-6px}.leaflet-tooltip-top:before{bottom:0;margin-bottom:-12px;border-top-color:#fff}.leaflet-tooltip-bottom:before{top:0;margin-top:-12px;margin-left:-6px;border-bottom-color:#fff}.leaflet-tooltip-left{margin-left:-6px}.leaflet-tooltip-right{margin-left:6px}.leaflet-tooltip-left:before,.leaflet-tooltip-right:before{top:50%;margin-top:-6px}.leaflet-tooltip-left:before{right:0;margin-right:-12px;border-left-color:#fff}.leaflet-tooltip-right:before{left:0;margin-left:-12px;border-right-color:#fff}@media print{.leaflet-control{-webkit-print-color-adjust:exact;print-color-adjust:exact}}.leaflet-cluster-anim .leaflet-marker-icon,.leaflet-cluster-anim .leaflet-marker-shadow{-webkit-transition:opacity .3s ease-in,-webkit-transform .3s ease-out;-o-transition:transform .3s ease-out,opacity .3s ease-in;transition:transform .3s ease-out,opacity .3s ease-in,-webkit-transform .3s ease-out}.leaflet-cluster-spider-leg{-webkit-transition:stroke-dashoffset .3s ease-out,stroke-opacity .3s ease-in;-o-transition:stroke-dashoffset .3s ease-out,stroke-opacity .3s ease-in;transition:stroke-dashoffset .3s ease-out,stroke-opacity .3s ease-in}.leaflet-marker-photo{border:2px solid #fff;-webkit-box-shadow:3px 3px 10px #888;box-shadow:3px 3px 10px #888}.leaflet-marker-photo div{width:100%;height:100%;background-size:cover;background-position:center center;background-repeat:no-repeat}.leaflet-marker-photo b{position:absolute;top:-7px;right:-11px;color:#555;background-color:#fff;border-radius:8px;height:12px;min-width:12px;line-height:12px;text-align:center;padding:3px;-webkit-box-shadow:0 3px 14px rgba(0,0,0,.4);box-shadow:0 3px 14px rgba(0,0,0,.4)} \ No newline at end of file +@charset "UTF-8";@-webkit-keyframes basicModal__fadeIn{0%{opacity:0}100%{opacity:1}}@keyframes basicModal__fadeIn{0%{opacity:0}100%{opacity:1}}@-webkit-keyframes basicModal__fadeOut{0%{opacity:1}100%{opacity:0}}@keyframes basicModal__fadeOut{0%{opacity:1}100%{opacity:0}}@-webkit-keyframes basicModal__moveUpFade{0%{-webkit-transform:translateY(80px);transform:translateY(80px)}100%{-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes basicModal__moveUpFade{0%{-webkit-transform:translateY(80px);transform:translateY(80px)}100%{-webkit-transform:translateY(0);transform:translateY(0)}}@-webkit-keyframes basicModal__shake{0%,100%{-webkit-transform:translateX(0);transform:translateX(0)}20%,60%{-webkit-transform:translateX(-10px);transform:translateX(-10px)}40%,80%{-webkit-transform:translateX(10px);transform:translateX(10px)}}@keyframes basicModal__shake{0%,100%{-webkit-transform:translateX(0);transform:translateX(0)}20%,60%{-webkit-transform:translateX(-10px);transform:translateX(-10px)}40%,80%{-webkit-transform:translateX(10px);transform:translateX(10px)}}.basicModalContainer{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;position:fixed;width:100%;height:100%;top:0;left:0;background-color:rgba(0,0,0,.4);z-index:1000;-webkit-box-sizing:border-box;box-sizing:border-box}.basicModalContainer *,.basicModalContainer :after,.basicModalContainer :before{-webkit-box-sizing:border-box;box-sizing:border-box}.basicModalContainer--fadeIn{-webkit-animation:.3s cubic-bezier(.51,.92,.24,1.15) basicModal__fadeIn;animation:.3s cubic-bezier(.51,.92,.24,1.15) basicModal__fadeIn}.basicModalContainer--fadeOut{-webkit-animation:.3s cubic-bezier(.51,.92,.24,1.15) basicModal__fadeOut;animation:.3s cubic-bezier(.51,.92,.24,1.15) basicModal__fadeOut}.basicModalContainer--fadeIn .basicModal--fadeIn{-webkit-animation:.3s cubic-bezier(.51,.92,.24,1.15) basicModal__moveUpFade;animation:.3s cubic-bezier(.51,.92,.24,1.15) basicModal__moveUpFade}.basicModalContainer--fadeIn .basicModal--shake{-webkit-animation:.3s cubic-bezier(.51,.92,.24,1.15) basicModal__shake;animation:.3s cubic-bezier(.51,.92,.24,1.15) basicModal__shake}.basicModal{position:relative;width:500px;background-color:#fff;border-radius:5px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.2);box-shadow:0 1px 2px rgba(0,0,0,.2)}.basicModal__content{padding:7%;max-height:70vh;overflow:auto;-webkit-overflow-scrolling:touch}.basicModal__buttons{display:-webkit-box;display:-ms-flexbox;display:flex;width:100%;-webkit-box-shadow:0 -1px 0 rgba(0,0,0,.1);box-shadow:0 -1px 0 rgba(0,0,0,.1)}.basicModal__button{display:inline-block;width:100%;font-weight:700;text-align:center;-webkit-transition:background-color .2s;-o-transition:background-color .2s;transition:background-color .2s;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;user-select:none}.basicModal__button:hover{background-color:rgba(0,0,0,.02)}.basicModal__button#basicModal__cancel{-ms-flex-negative:2;flex-shrink:2}.basicModal__button#basicModal__action{-ms-flex-negative:1;flex-shrink:1;-webkit-box-shadow:inset 1px 0 0 rgba(0,0,0,.1);box-shadow:inset 1px 0 0 rgba(0,0,0,.1)}.basicModal__button#basicModal__action:first-child{-webkit-box-shadow:none;box-shadow:none}.basicModal__button:first-child{border-radius:0 0 0 5px}.basicModal__button:last-child{border-radius:0 0 5px}.basicModal__small{max-width:340px;text-align:center}.basicModal__small .basicModal__content{padding:10% 5%}.basicModal__xclose#basicModal__cancel{position:absolute;top:-8px;right:-8px;margin:0;padding:0;width:40px;height:40px;background-color:#fff;border-radius:100%;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.2);box-shadow:0 1px 2px rgba(0,0,0,.2)}.basicModal__xclose#basicModal__cancel:after{content:"";position:absolute;left:-3px;top:8px;width:35px;height:34px;background:#fff}.basicModal__xclose#basicModal__cancel svg{position:relative;width:20px;height:39px;fill:#888;z-index:1;-webkit-transition:fill .2s;-o-transition:fill .2s;transition:fill .2s}.basicModal__xclose#basicModal__cancel:after:hover svg,.basicModal__xclose#basicModal__cancel:hover svg{fill:#2875ed}.basicModal__xclose#basicModal__cancel:active svg,.basicModal__xclose#basicModal__cancel:after:active svg{fill:#1364e3}.basicContextContainer{position:fixed;width:100%;height:100%;top:0;left:0;z-index:1000;-webkit-tap-highlight-color:transparent}.basicContext{position:absolute;opacity:0;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;user-select:none;-webkit-box-sizing:border-box;box-sizing:border-box;-webkit-animation:.3s cubic-bezier(.51,.92,.24,1.2) basicContext__popIn;animation:.3s cubic-bezier(.51,.92,.24,1.2) basicContext__popIn}.basicContext *{-webkit-box-sizing:border-box;box-sizing:border-box}.basicContext__item{cursor:pointer}.basicContext__item--separator{float:left;width:100%;cursor:default}.basicContext__data{min-width:140px;text-align:left;white-space:nowrap}.basicContext__icon{display:inline-block}.basicContext--scrollable{height:100%;-webkit-overflow-scrolling:touch;overflow-x:hidden;overflow-y:auto}.basicContext--scrollable .basicContext__data{min-width:160px}@-webkit-keyframes basicContext__popIn{0%{-webkit-transform:scale(0);transform:scale(0)}to{-webkit-transform:scale(1);transform:scale(1)}}@keyframes basicContext__popIn{0%{-webkit-transform:scale(0);transform:scale(0)}to{-webkit-transform:scale(1);transform:scale(1)}}a,abbr,acronym,address,applet,article,aside,audio,b,big,blockquote,body,canvas,caption,center,cite,code,dd,del,details,dfn,div,dl,dt,em,embed,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,header,hgroup,html,i,iframe,img,ins,kbd,label,legend,li,mark,menu,nav,object,ol,output,p,pre,q,ruby,s,samp,section,small,span,strike,strong,sub,summary,sup,table,tbody,td,tfoot,th,thead,time,tr,tt,u,ul,var,video{margin:0;padding:0;border:0;font:inherit;font-size:100%;vertical-align:baseline}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}body{line-height:1;background-color:#1d1d1d;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;-webkit-font-smoothing:antialiased;-moz-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:after,blockquote:before,q:after,q:before{content:"";content:none}table{border-collapse:collapse;border-spacing:0}em,i{font-style:italic}b,strong{font-weight:700}*{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:color .3s,opacity .3s ease-out,-webkit-transform .3s ease-out,-webkit-box-shadow .3s;transition:color .3s,opacity .3s ease-out,transform .3s ease-out,box-shadow .3s,-webkit-transform .3s ease-out,-webkit-box-shadow .3s;-o-transition:color .3s,opacity .3s ease-out,transform .3s ease-out,box-shadow .3s}body,html{width:100%;height:100%;position:relative;overflow:clip}body.mode-frame div#container,body.mode-none div#container{display:none}input,textarea{-webkit-user-select:text!important;-moz-user-select:text!important;-ms-user-select:text!important;user-select:text!important}.svgsprite{display:none}.iconic{width:100%;height:100%}#upload{display:none}.fadeIn{-webkit-animation-name:fadeIn;animation-name:fadeIn;-webkit-animation-duration:.3s;animation-duration:.3s;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards;-webkit-animation-timing-function:cubic-bezier(.51,.92,.24,1);animation-timing-function:cubic-bezier(.51,.92,.24,1)}.fadeOut{-webkit-animation-name:fadeOut;animation-name:fadeOut;-webkit-animation-duration:.3s;animation-duration:.3s;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards;-webkit-animation-timing-function:cubic-bezier(.51,.92,.24,1);animation-timing-function:cubic-bezier(.51,.92,.24,1)}@-webkit-keyframes fadeIn{0%{opacity:0}100%{opacity:1}}@keyframes fadeIn{0%{opacity:0}100%{opacity:1}}@-webkit-keyframes fadeOut{0%{opacity:1}100%{opacity:0}}@keyframes fadeOut{0%{opacity:1}100%{opacity:0}}@-webkit-keyframes moveBackground{0%{background-position-x:0}100%{background-position-x:-100px}}@keyframes moveBackground{0%{background-position-x:0}100%{background-position-x:-100px}}@-webkit-keyframes zoomIn{0%{opacity:0;-webkit-transform:scale(.8);transform:scale(.8)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@keyframes zoomIn{0%{opacity:0;-webkit-transform:scale(.8);transform:scale(.8)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@-webkit-keyframes zoomOut{0%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}100%{opacity:0;-webkit-transform:scale(.8);transform:scale(.8)}}@keyframes zoomOut{0%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}100%{opacity:0;-webkit-transform:scale(.8);transform:scale(.8)}}@-webkit-keyframes pulse{0%,100%{opacity:1}50%{opacity:.3}}@keyframes pulse{0%,100%{opacity:1}50%{opacity:.3}}body.mode-frame #lychee_application_container,body.mode-none #lychee_application_container{display:none}.hflex-container,.hflex-item-rigid,.hflex-item-stretch,.vflex-container,.vflex-item-rigid,.vflex-item-stretch{position:relative;overflow:clip}.hflex-container,.vflex-container{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-line-pack:stretch;align-content:stretch;gap:0 0}.vflex-container{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.hflex-container{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.hflex-item-stretch,.vflex-item-stretch{-webkit-box-flex:1;-ms-flex:auto;flex:auto}.hflex-item-stretch{width:0;height:100%}.vflex-item-stretch{width:100%;height:0}.hflex-item-rigid,.vflex-item-rigid{-webkit-box-flex:0;-ms-flex:none;flex:none}.hflex-item-rigid{width:auto;height:100%}.vflex-item-rigid{width:100%;height:auto}.overlay-container{position:absolute;display:none;top:0;left:0;width:100%;height:100%;background-color:#000;-webkit-transition:background-color .3s;-o-transition:background-color .3s;transition:background-color .3s}.overlay-container.full{cursor:none}.overlay-container.active{display:unset}#lychee_view_content{height:auto;-webkit-box-flex:1;-ms-flex:1 0 auto;flex:1 0 auto;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-line-pack:start;align-content:flex-start;padding-bottom:16px;-webkit-overflow-scrolling:touch}#lychee_view_content.contentZoomIn .album,#lychee_view_content.contentZoomIn .photo{-webkit-animation-name:zoomIn;animation-name:zoomIn}#lychee_view_content.contentZoomIn .divider{-webkit-animation-name:fadeIn;animation-name:fadeIn}#lychee_view_content.contentZoomOut .album,#lychee_view_content.contentZoomOut .photo{-webkit-animation-name:zoomOut;animation-name:zoomOut}#lychee_view_content.contentZoomOut .divider{-webkit-animation-name:fadeOut;animation-name:fadeOut}.album,.photo{position:relative;width:202px;height:202px;margin:30px 0 0 30px;cursor:default;-webkit-animation-duration:.2s;animation-duration:.2s;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards;-webkit-animation-timing-function:cubic-bezier(.51,.92,.24,1);animation-timing-function:cubic-bezier(.51,.92,.24,1)}.album .thumbimg,.photo .thumbimg{position:absolute;width:200px;height:200px;background:#222;color:#222;-webkit-box-shadow:0 2px 5px rgba(0,0,0,.5);box-shadow:0 2px 5px rgba(0,0,0,.5);border:1px solid rgba(255,255,255,.5);-webkit-transition:opacity .3s ease-out,border-color .3s ease-out,-webkit-transform .3s ease-out;transition:opacity .3s ease-out,transform .3s ease-out,border-color .3s ease-out,-webkit-transform .3s ease-out;-o-transition:opacity .3s ease-out,transform .3s ease-out,border-color .3s ease-out}.album .thumbimg>img,.photo .thumbimg>img{width:100%;height:100%}.album.active .thumbimg,.album:focus .thumbimg,.photo.active .thumbimg,.photo:focus .thumbimg{border-color:#2293ec}.album:active .thumbimg,.photo:active .thumbimg{-webkit-transition:none;-o-transition:none;transition:none;border-color:#0f6ab2}.album.selected img,.photo.selected img{outline:#2293ec solid 1px}.album .video::before,.photo .video::before{content:"";position:absolute;display:block;height:100%;width:100%;background:url(../img/play-icon.png) 46% 50% no-repeat;-webkit-transition:.3s;-o-transition:.3s;transition:.3s;will-change:opacity,height}.album .video:focus::before,.photo .video:focus::before{opacity:.75}.album .livephoto::before,.photo .livephoto::before{content:"";position:absolute;display:block;height:100%;width:100%;background:url(../img/live-photo-icon.png) 2% 2% no-repeat;-webkit-transition:.3s;-o-transition:.3s;transition:.3s;will-change:opacity,height}.album .livephoto:focus::before,.photo .livephoto:focus::before{opacity:.75}.album .thumbimg:first-child,.album .thumbimg:nth-child(2){-webkit-transform:rotate(0) translateY(0) translateX(0);-ms-transform:rotate(0) translateY(0) translateX(0);transform:rotate(0) translateY(0) translateX(0);opacity:0}.album:focus .thumbimg:nth-child(1),.album:focus .thumbimg:nth-child(2){opacity:1;will-change:transform}.album:focus .thumbimg:nth-child(1){-webkit-transform:rotate(-2deg) translateY(10px) translateX(-12px);-ms-transform:rotate(-2deg) translateY(10px) translateX(-12px);transform:rotate(-2deg) translateY(10px) translateX(-12px)}.album:focus .thumbimg:nth-child(2){-webkit-transform:rotate(5deg) translateY(-8px) translateX(12px);-ms-transform:rotate(5deg) translateY(-8px) translateX(12px);transform:rotate(5deg) translateY(-8px) translateX(12px)}.blurred span{overflow:hidden}.blurred img{-webkit-filter:blur(5px);filter:blur(5px)}.album .overlay,.photo .overlay{position:absolute;margin:0 1px;width:200px;background:-webkit-gradient(linear,left top,left bottom,from(rgba(0,0,0,0)),to(rgba(0,0,0,.6)));background:-o-linear-gradient(top,rgba(0,0,0,0),rgba(0,0,0,.6));background:linear-gradient(to bottom,rgba(0,0,0,0),rgba(0,0,0,.6));bottom:1px}.album .thumbimg[data-overlay=false]+.overlay{background:0 0}.photo .overlay{opacity:0}.photo.active .overlay,.photo:focus .overlay{opacity:1}.album .overlay h1,.photo .overlay h1{min-height:19px;width:180px;margin:12px 0 5px 15px;color:#fff;text-shadow:0 1px 3px rgba(0,0,0,.4);font-size:16px;font-weight:700;overflow:hidden;white-space:nowrap;-o-text-overflow:ellipsis;text-overflow:ellipsis}.album .overlay a,.photo .overlay a{display:block;margin:0 0 12px 15px;font-size:11px;color:#ccc;text-shadow:0 1px 3px rgba(0,0,0,.4)}.album .overlay a .iconic,.photo .overlay a .iconic{fill:#ccc;margin:0 5px 0 0;width:8px;height:8px}.album .thumbimg[data-overlay=false]+.overlay a,.album .thumbimg[data-overlay=false]+.overlay h1{text-shadow:none}.album .badges,.photo .badges{position:relative;margin:-1px 0 0 6px}.album .subalbum_badge{position:absolute;right:0;top:0}.album .badge,.photo .badge{display:none;margin:0 0 0 6px;padding:12px 8px 6px;width:18px;background:#d92c34;-webkit-box-shadow:0 0 2px rgba(0,0,0,.6);box-shadow:0 0 2px rgba(0,0,0,.6);border-radius:0 0 5px 5px;border:1px solid #fff;border-top:none;color:#fff;text-align:center;text-shadow:0 1px 0 rgba(0,0,0,.4);opacity:.9}.album .badge--visible,.photo .badge--visible{display:inline-block}.album .badge--not--hidden,.photo .badge--not--hidden{background:#0a0}.album .badge--hidden,.photo .badge--hidden{background:#f90}.album .badge--cover,.photo .badge--cover{display:inline-block;background:#f90}.album .badge--star,.photo .badge--star{display:inline-block;background:#fc0}.album .badge--nsfw,.photo .badge--nsfw{display:inline-block;background:#ff82ee}.album .badge--list,.photo .badge--list{background:#2293ec}.album .badge--tag,.photo .badge--tag{display:inline-block;background:#0a0}.album .badge .iconic,.photo .badge .iconic{fill:#fff;width:16px;height:16px}.album .badge--folder,.photo .badge--folder{display:inline-block;-webkit-box-shadow:none;box-shadow:none;background:0 0;border:none}.album .badge--folder .iconic,.photo .badge--folder .iconic{width:12px;height:12px}.divider{margin:50px 0 0;padding:10px 0 0;width:100%;opacity:0;border-top:1px solid rgba(255,255,255,.02);-webkit-box-shadow:0 -1px 0 rgba(0,0,0,.2);box-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-animation-duration:.2s;animation-duration:.2s;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards;-webkit-animation-timing-function:cubic-bezier(.51,.92,.24,1);animation-timing-function:cubic-bezier(.51,.92,.24,1)}.divider:first-child{margin-top:10px;border-top:0;-webkit-box-shadow:none;box-shadow:none}.divider h1{margin:0 0 0 30px;color:rgba(255,255,255,.6);font-size:14px;font-weight:700}@media only screen and (min-width:320px) and (max-width:567px){.album,.photo{--size:calc((100vw - 3px) / 3);width:calc(var(--size) - 3px);height:calc(var(--size) - 3px);margin:3px 0 0 3px}.album .thumbimg,.photo .thumbimg{width:calc(var(--size) - 5px);height:calc(var(--size) - 5px)}.album .overlay,.photo .overlay{width:calc(var(--size) - 5px)}.album .overlay h1,.photo .overlay h1{min-height:14px;width:calc(var(--size) - 19px);margin:8px 0 2px 6px;font-size:12px}.album .overlay a,.photo .overlay a{display:none}.album .badge,.photo .badge{padding:4px 3px 3px;width:12px}.album .badge .iconic,.photo .badge .iconic{width:12px;height:12px}.album .badge--folder .iconic,.photo .badge--folder .iconic{width:8px;height:8px}.divider{margin:20px 0 0}.divider:first-child{margin-top:0}.divider h1{margin:0 0 6px 8px;font-size:12px}}@media only screen and (min-width:568px) and (max-width:639px){.album,.photo{--size:calc((100vw - 3px) / 4);width:calc(var(--size) - 3px);height:calc(var(--size) - 3px);margin:3px 0 0 3px}.album .thumbimg,.photo .thumbimg{width:calc(var(--size) - 5px);height:calc(var(--size) - 5px)}.album .overlay,.photo .overlay{width:calc(var(--size) - 5px)}.album .overlay h1,.photo .overlay h1{min-height:14px;width:calc(var(--size) - 19px);margin:8px 0 2px 6px;font-size:12px}.album .overlay a,.photo .overlay a{display:none}.album .badge,.photo .badge{padding:4px 3px 3px;width:14px}.album .badge .iconic,.photo .badge .iconic{width:14px;height:14px}.album .badge--folder .iconic,.photo .badge--folder .iconic{width:9px;height:9px}.divider{margin:24px 0 0}.divider:first-child{margin-top:0}.divider h1{margin:0 0 6px 10px}}@media only screen and (min-width:640px) and (max-width:768px){.album,.photo{--size:calc((100vw - 5px) / 5);width:calc(var(--size) - 5px);height:calc(var(--size) - 5px);margin:5px 0 0 5px}.album .thumbimg,.photo .thumbimg{width:calc(var(--size) - 7px);height:calc(var(--size) - 7px)}.album .overlay,.photo .overlay{width:calc(var(--size) - 7px)}.album .overlay h1,.photo .overlay h1{min-height:14px;width:calc(var(--size) - 21px);margin:10px 0 3px 8px;font-size:12px}.album .overlay a,.photo .overlay a{display:none}.album .badge,.photo .badge{padding:6px 4px 4px;width:16px}.album .badge .iconic,.photo .badge .iconic{width:16px;height:16px}.album .badge--folder .iconic,.photo .badge--folder .iconic{width:10px;height:10px}.divider{margin:28px 0 0}.divider:first-child{margin-top:0}.divider h1{margin:0 0 6px 10px}}.no_content{position:absolute;top:50%;left:50%;padding-top:20px;color:rgba(255,255,255,.35);text-align:center;-webkit-transform:translateX(-50%) translateY(-50%);-ms-transform:translateX(-50%) translateY(-50%);transform:translateX(-50%) translateY(-50%)}.no_content .iconic{fill:rgba(255,255,255,.3);margin:0 0 10px;width:50px;height:50px}.no_content p{font-size:16px;font-weight:700}body.mode-gallery #lychee_frame_container,body.mode-none #lychee_frame_container,body.mode-view #lychee_frame_container{display:none}#lychee_frame_bg_canvas{width:100%;height:100%;position:absolute}#lychee_frame_bg_image{position:absolute;display:none}#lychee_frame_noise_layer{position:absolute;top:0;left:0;width:100%;height:100%;background-image:url(../img/noise.png);background-repeat:repeat;background-position:44px 44px}#lychee_frame_image_container{width:100%;height:100%;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-ms-flex-line-pack:center;align-content:center}#lychee_frame_image_container img{height:95%;width:95%;-o-object-fit:contain;object-fit:contain;-webkit-filter:drop-shadow(0 0 1px rgba(0, 0, 0, .3)) drop-shadow(0 0 10px rgba(0, 0, 0, .3));filter:drop-shadow(0 0 1px rgba(0, 0, 0, .3)) drop-shadow(0 0 10px rgba(0, 0, 0, .3))}#lychee_frame_shutter{position:absolute;width:100%;height:100%;top:0;left:0;padding:0;margin:0;background-color:#1d1d1d;opacity:1;-webkit-transition:opacity 1s ease-in-out;-o-transition:opacity 1s ease-in-out;transition:opacity 1s ease-in-out}#lychee_frame_shutter.opened{opacity:0}#lychee_left_menu_container{width:0;background-color:#111;padding-top:16px;-webkit-transition:width .5s;-o-transition:width .5s;transition:width .5s;height:100%;z-index:998}#lychee_left_menu,#lychee_left_menu_container.visible{width:250px}#lychee_left_menu a{padding:8px 8px 8px 32px;text-decoration:none;font-size:18px;color:#818181;display:block;cursor:pointer}#lychee_left_menu a.linkMenu{white-space:nowrap}#lychee_left_menu .iconic{display:inline-block;margin:0 10px 0 1px;width:15px;height:14px;fill:#818181}#lychee_left_menu .iconic.ionicons{margin:0 8px -2px 0;width:18px;height:18px}@media only screen and (max-width:567px),only screen and (max-width:640px) and (orientation:portrait){#lychee_left_menu,#lychee_left_menu_container{position:absolute;left:0}#lychee_left_menu_container.visible{width:100%}}@media (hover:hover){.album:hover .thumbimg,.photo:hover .thumbimg{border-color:#2293ec}.album .livephoto:hover::before,.album .video:hover::before,.photo .livephoto:hover::before,.photo .video:hover::before{opacity:.75}.album:hover .thumbimg:nth-child(1),.album:hover .thumbimg:nth-child(2),.album__dragover .thumbimg:nth-child(1),.album__dragover .thumbimg:nth-child(2){opacity:1;will-change:transform}.album:hover .thumbimg:nth-child(1),.album__dragover .thumbimg:nth-child(1){-webkit-transform:rotate(-2deg) translateY(10px) translateX(-12px);-ms-transform:rotate(-2deg) translateY(10px) translateX(-12px);transform:rotate(-2deg) translateY(10px) translateX(-12px)}.album:hover .thumbimg:nth-child(2),.album__dragover .thumbimg:nth-child(2){-webkit-transform:rotate(5deg) translateY(-8px) translateX(12px);-ms-transform:rotate(5deg) translateY(-8px) translateX(12px);transform:rotate(5deg) translateY(-8px) translateX(12px)}.photo:hover .overlay{opacity:1}#lychee_left_menu a:hover{color:#f1f1f1}}.basicContext{padding:5px 0 6px;background:-webkit-gradient(linear,left top,left bottom,from(#333),to(#252525));background:-o-linear-gradient(top,#333,#252525);background:linear-gradient(to bottom,#333,#252525);-webkit-box-shadow:0 1px 4px rgba(0,0,0,.2),inset 0 1px 0 rgba(255,255,255,.05);box-shadow:0 1px 4px rgba(0,0,0,.2),inset 0 1px 0 rgba(255,255,255,.05);border-radius:5px;border:1px solid rgba(0,0,0,.7);border-bottom:1px solid rgba(0,0,0,.8);-webkit-transition:none;-o-transition:none;transition:none}.basicContext__item{margin-bottom:2px;font-size:14px;color:#ccc}.basicContext__item--separator{margin:4px 0;height:2px;background:rgba(0,0,0,.2);border-bottom:1px solid rgba(255,255,255,.06)}.basicContext__item--disabled{cursor:default;opacity:.5}.basicContext__item:last-child{margin-bottom:0}.basicContext__data{min-width:auto;padding:6px 25px 7px 12px;-webkit-transition:none;-o-transition:none;transition:none;cursor:default}@media (hover:none) and (pointer:coarse){.basicContext__data{padding:12px 25px 12px 12px}}.basicContext__item:not(.basicContext__item--disabled):active .basicContext__data{background:-webkit-gradient(linear,left top,left bottom,from(#1178ca),to(#0f6ab2));background:-o-linear-gradient(top,#1178ca,#0f6ab2);background:linear-gradient(to bottom,#1178ca,#0f6ab2)}.basicContext__icon{margin-right:10px;width:12px;text-align:center}@media (hover:hover){.basicContext__item:not(.basicContext__item--disabled):hover .basicContext__data{background:-webkit-gradient(linear,left top,left bottom,from(#2293ec),to(#1386e1));background:-o-linear-gradient(top,#2293ec,#1386e1);background:linear-gradient(to bottom,#2293ec,#1386e1)}.basicContext__item:hover{color:#fff;-webkit-transition:.3s;-o-transition:.3s;transition:.3s;-webkit-transform:scale(1.05);-ms-transform:scale(1.05);transform:scale(1.05)}.basicContext__item:hover .iconic{fill:#fff}.basicContext__item--noHover:hover .basicContext__data{background:0 0!important}}.basicContext__data .cover{position:absolute;background-color:#222;border-radius:2px;-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.5);box-shadow:0 0 0 1px rgba(0,0,0,.5)}.basicContext__data .title{display:inline-block;margin:0 0 3px 26px}.basicContext__data .iconic{display:inline-block;margin:0 10px 0 1px;width:11px;height:10px;fill:#fff}.basicContext__data .iconic.active{fill:#f90}.basicContext__data .iconic.ionicons{margin:0 8px -2px 0;width:14px;height:14px}.basicContext__data input#link{margin:-2px 0;padding:5px 7px 6px;width:100%;background:#333;color:#fff;-webkit-box-shadow:0 1px 0 rgba(255,255,255,.05);box-shadow:0 1px 0 rgba(255,255,255,.05);border:1px solid rgba(0,0,0,.4);border-radius:3px;outline:0}.basicContext__item--noHover .basicContext__data{padding-right:12px}div.basicModalContainer{background-color:rgba(0,0,0,.85);z-index:999}div.basicModalContainer--error{-webkit-transform:translateY(40px);-ms-transform:translateY(40px);transform:translateY(40px)}div.basicModal{background:-webkit-gradient(linear,left top,left bottom,from(#444),to(#333));background:-o-linear-gradient(top,#444,#333);background:linear-gradient(to bottom,#444,#333);-webkit-box-shadow:0 1px 4px rgba(0,0,0,.2),inset 0 1px 0 rgba(255,255,255,.05);box-shadow:0 1px 4px rgba(0,0,0,.2),inset 0 1px 0 rgba(255,255,255,.05);font-size:14px;line-height:17px}div.basicModal--error{-webkit-transform:translateY(-40px);-ms-transform:translateY(-40px);transform:translateY(-40px)}div.basicModal__buttons{-webkit-box-shadow:none;box-shadow:none}.basicModal__button{padding:13px 0 15px;background:0 0;color:#999;border-top:1px solid rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.02);box-shadow:inset 0 1px 0 rgba(255,255,255,.02);cursor:default}.basicModal__button--busy,.basicModal__button:active{-webkit-transition:none;-o-transition:none;transition:none;background:rgba(0,0,0,.1);cursor:wait}.basicModal__button#basicModal__action{color:#2293ec;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.02),inset 1px 0 0 rgba(0,0,0,.2);box-shadow:inset 0 1px 0 rgba(255,255,255,.02),inset 1px 0 0 rgba(0,0,0,.2)}.basicModal__button#basicModal__action.red,.basicModal__button#basicModal__cancel.red{color:#d92c34}.basicModal__button.hidden{display:none}div.basicModal__content{padding:36px;color:#ececec;text-align:left}div.basicModal__content>*{display:block;width:100%;margin:24px 0;padding:0}div.basicModal__content>.force-first-child,div.basicModal__content>:first-child{margin-top:0}div.basicModal__content>.force-last-child,div.basicModal__content>:last-child{margin-bottom:0}div.basicModal__content .disabled{color:#999}div.basicModal__content b{font-weight:700;color:#fff}div.basicModal__content a{color:inherit;text-decoration:none;border-bottom:1px dashed #ececec}div.basicModal__content a.button{display:inline-block;margin:0 6px;padding:3px 12px;color:#2293ec;text-align:center;border-radius:5px;border:none;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.02),inset 1px 0 0 rgba(0,0,0,.2);box-shadow:inset 0 1px 0 rgba(255,255,255,.02),inset 1px 0 0 rgba(0,0,0,.2)}div.basicModal__content a.button .iconic{fill:#2293ec}div.basicModal__content>hr{border:none;border-top:1px solid rgba(0,0,0,.3)}#lychee_toolbar_container{-webkit-transition:height .3s ease-out;-o-transition:height .3s ease-out;transition:height .3s ease-out}#lychee_toolbar_container.hidden{height:0}#lychee_toolbar_container,.toolbar{height:49px}.toolbar{background:-webkit-gradient(linear,left top,left bottom,from(#222),to(#1a1a1a));background:-o-linear-gradient(top,#222,#1a1a1a);background:linear-gradient(to bottom,#222,#1a1a1a);border-bottom:1px solid #0f0f0f;display:none;-webkit-box-align:center;-ms-flex-align:center;align-items:center;position:relative;-webkit-box-sizing:border-box;box-sizing:border-box;width:100%}.toolbar.visible{display:-webkit-box;display:-ms-flexbox;display:flex}#lychee_toolbar_config .toolbar .button .iconic{-webkit-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg)}#lychee_toolbar_config .toolbar .header__title{padding-right:80px}.toolbar .header__title{width:100%;padding:16px 0;color:#fff;font-size:16px;font-weight:700;text-align:center;cursor:default;overflow:hidden;white-space:nowrap;-o-text-overflow:ellipsis;text-overflow:ellipsis;-webkit-transition:margin-left .5s;-o-transition:margin-left .5s;transition:margin-left .5s}.toolbar .header__title .iconic{display:none;margin:0 0 0 5px;width:10px;height:10px;fill:rgba(255,255,255,.5);-webkit-transition:fill .2s ease-out;-o-transition:fill .2s ease-out;transition:fill .2s ease-out}.toolbar .header__title:active .iconic{-webkit-transition:none;-o-transition:none;transition:none;fill:rgba(255,255,255,.8)}.toolbar .header__title--editable .iconic{display:inline-block}.toolbar .button{-ms-flex-negative:0;flex-shrink:0;padding:16px 8px;height:15px}.toolbar .button .iconic{width:15px;height:15px;fill:rgba(255,255,255,.5);-webkit-transition:fill .2s ease-out;-o-transition:fill .2s ease-out;transition:fill .2s ease-out}.toolbar .button:active .iconic{-webkit-transition:none;-o-transition:none;transition:none;fill:rgba(255,255,255,.8)}.toolbar .button--star.active .iconic{fill:#f0ef77}.toolbar .button--eye.active .iconic{fill:#d92c34}.toolbar .button--eye.active--not-hidden .iconic{fill:#0a0}.toolbar .button--eye.active--hidden .iconic{fill:#f90}.toolbar .button--share .iconic.ionicons{margin:-2px 0;width:18px;height:18px}.toolbar .button--nsfw.active .iconic{fill:#ff82ee}.toolbar .button--info.active .iconic{fill:#2293ec}.toolbar #button_back,.toolbar #button_back_home,.toolbar #button_close_config,.toolbar #button_settings,.toolbar #button_signin{padding:16px 12px 16px 18px}.toolbar .button_add{padding:16px 18px 16px 12px}.toolbar .header__divider{-ms-flex-negative:0;flex-shrink:0;width:14px}.toolbar .header__search__field{position:relative}.toolbar input[type=text].header__search{-ms-flex-negative:0;flex-shrink:0;width:80px;margin:0;padding:5px 12px 6px;background-color:#1d1d1d;color:#fff;border:1px solid rgba(0,0,0,.9);-webkit-box-shadow:0 1px 0 rgba(255,255,255,.04);box-shadow:0 1px 0 rgba(255,255,255,.04);outline:0;border-radius:50px;opacity:.6;-webkit-transition:opacity .3s ease-out,width .2s ease-out,-webkit-box-shadow .3s ease-out;transition:opacity .3s ease-out,box-shadow .3s ease-out,width .2s ease-out,-webkit-box-shadow .3s ease-out;-o-transition:opacity .3s ease-out,box-shadow .3s ease-out,width .2s ease-out}.toolbar input[type=text].header__search:focus{width:140px;border-color:#2293ec;-webkit-box-shadow:0 1px 0 rgba(255,255,255,0);box-shadow:0 1px 0 rgba(255,255,255,0);opacity:1}.toolbar input[type=text].header__search:focus~.header__clear{opacity:1}.toolbar input[type=text].header__search::-ms-clear{display:none}.toolbar .header__clear{position:absolute;top:50%;-ms-transform:translateY(-50%);-webkit-transform:translateY(-50%);transform:translateY(-50%);right:8px;padding:0;color:rgba(255,255,255,.5);font-size:24px;opacity:0;-webkit-transition:color .2s ease-out;-o-transition:color .2s ease-out;transition:color .2s ease-out;cursor:default}.toolbar .header__clear_nomap{right:60px}.toolbar .header__hostedwith{-ms-flex-negative:0;flex-shrink:0;padding:5px 10px;margin:11px 0;color:#888;font-size:13px;border-radius:100px;cursor:default}@media only screen and (max-width:640px){#button_move,#button_move_album,#button_nsfw_album,#button_trash,#button_trash_album,#button_visibility,#button_visibility_album{display:none!important}}@media only screen and (max-width:640px) and (max-width:567px){#button_rotate_ccwise,#button_rotate_cwise{display:none!important}.header__divider{width:0}}#imageview #image,#imageview #livephoto{position:absolute;top:30px;right:30px;bottom:30px;left:30px;margin:auto;max-width:calc(100% - 60px);max-height:calc(100% - 60px);width:auto;height:auto;-webkit-transition:top .3s,right .3s,bottom .3s,left .3s,max-width .3s,max-height .3s;-o-transition:top .3s,right .3s,bottom .3s,left .3s,max-width .3s,max-height .3s;transition:top .3s,right .3s,bottom .3s,left .3s,max-width .3s,max-height .3s;-webkit-animation-name:zoomIn;animation-name:zoomIn;-webkit-animation-duration:.3s;animation-duration:.3s;-webkit-animation-timing-function:cubic-bezier(.51,.92,.24,1.15);animation-timing-function:cubic-bezier(.51,.92,.24,1.15);background-size:contain;background-position:center;background-repeat:no-repeat}#imageview.full #image,#imageview.full #livephoto{top:0;right:0;bottom:0;left:0;max-width:100%;max-height:100%}#imageview #image_overlay{position:absolute;bottom:30px;left:30px;color:#fff;text-shadow:1px 1px 2px #000;z-index:3}#imageview #image_overlay h1{font-size:28px;font-weight:500;-webkit-transition:visibility .3s linear,opacity .3s linear;-o-transition:visibility .3s linear,opacity .3s linear;transition:visibility .3s linear,opacity .3s linear}#imageview #image_overlay p{margin-top:5px;font-size:20px;line-height:24px}#imageview #image_overlay a .iconic{fill:#fff;margin:0 5px 0 0;width:14px;height:14px}#imageview .arrow_wrapper{position:absolute;width:15%;height:calc(100% - 60px);top:60px}#imageview .arrow_wrapper--previous{left:0}#imageview .arrow_wrapper--next{right:0}#imageview .arrow_wrapper a{position:absolute;top:50%;margin:-19px 0 0;padding:8px 12px;width:16px;height:22px;background-size:100% 100%;border:1px solid rgba(255,255,255,.8);opacity:.6;z-index:2;-webkit-transition:opacity .2s ease-out,-webkit-transform .2s ease-out;transition:transform .2s ease-out,opacity .2s ease-out,-webkit-transform .2s ease-out;-o-transition:transform .2s ease-out,opacity .2s ease-out;will-change:transform}#imageview .arrow_wrapper a#previous{left:-1px;-webkit-transform:translateX(-100%);-ms-transform:translateX(-100%);transform:translateX(-100%)}#imageview .arrow_wrapper a#next{right:-1px;-webkit-transform:translateX(100%);-ms-transform:translateX(100%);transform:translateX(100%)}#imageview .arrow_wrapper .iconic{fill:rgba(255,255,255,.8)}#imageview video{z-index:1}@media (hover:hover){.basicModal__button:hover{background:rgba(255,255,255,.02)}div.basicModal__content a.button:hover{color:#fff;background:#2293ec}.toolbar .button:hover .iconic,.toolbar .header__title:hover .iconic,div.basicModal__content a.button:hover .iconic{fill:#fff}.toolbar .header__clear:hover{color:#fff}.toolbar .header__hostedwith:hover{background-color:rgba(0,0,0,.3)}#imageview .arrow_wrapper:hover a#next,#imageview .arrow_wrapper:hover a#previous{-webkit-transform:translateX(0);-ms-transform:translateX(0);transform:translateX(0)}#imageview .arrow_wrapper a:hover{opacity:1}}@media only screen and (max-width:567px),only screen and (max-width:640px) and (orientation:portrait){#imageview #image,#imageview #livephoto{top:0;right:0;bottom:0;left:0;max-width:100%;max-height:100%}#imageview #image_overlay h1{font-size:14px}#imageview #image_overlay p{margin-top:2px;font-size:11px;line-height:13px}#imageview #image_overlay a .iconic{width:9px;height:9px}}@media only screen and (min-width:568px) and (max-width:768px),only screen and (min-width:568px) and (max-width:640px) and (orientation:landscape){#imageview #image,#imageview #livephoto{top:0;right:0;bottom:0;left:0;max-width:100%;max-height:100%}#imageview #image_overlay h1{font-size:18px}#imageview #image_overlay p{margin-top:4px;font-size:14px;line-height:16px}#imageview #image_overlay a .iconic{width:12px;height:12px}}.leaflet-marker-photo img{width:100%;height:100%}.image-leaflet-popup{width:100%}.leaflet-popup-content div{pointer-events:none;position:absolute;bottom:19px;left:22px;right:22px;padding-bottom:10px;background:-webkit-gradient(linear,left top,left bottom,from(rgba(0,0,0,0)),to(rgba(0,0,0,.6)));background:-o-linear-gradient(top,rgba(0,0,0,0),rgba(0,0,0,.6));background:linear-gradient(to bottom,rgba(0,0,0,0),rgba(0,0,0,.6))}.leaflet-popup-content h1{top:0;position:relative;margin:12px 0 5px 15px;font-size:16px;font-weight:700;text-shadow:0 1px 3px rgba(255,255,255,.4);color:#fff;white-space:nowrap;-o-text-overflow:ellipsis;text-overflow:ellipsis}.leaflet-popup-content span{margin-left:12px}.leaflet-popup-content svg{fill:#fff;vertical-align:middle}.leaflet-popup-content p{display:inline;font-size:11px;color:#fff}.leaflet-popup-content .iconic{width:20px;height:15px}#lychee_sidebar_container{width:0;-webkit-transition:width .3s cubic-bezier(.51,.92,.24,1);-o-transition:width .3s cubic-bezier(.51,.92,.24,1);transition:width .3s cubic-bezier(.51,.92,.24,1)}#lychee_sidebar,#lychee_sidebar_container.active{width:350px}#lychee_sidebar{height:100%;background-color:rgba(25,25,25,.98);border-left:1px solid rgba(0,0,0,.2)}#lychee_sidebar_header{height:49px;background:-webkit-gradient(linear,left top,left bottom,from(rgba(255,255,255,.02)),to(rgba(0,0,0,0)));background:-o-linear-gradient(top,rgba(255,255,255,.02),rgba(0,0,0,0));background:linear-gradient(to bottom,rgba(255,255,255,.02),rgba(0,0,0,0));border-top:1px solid #2293ec}#lychee_sidebar_header h1{margin:15px 0;color:#fff;font-size:16px;font-weight:700;text-align:center;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}#lychee_sidebar_content{overflow:clip auto;-webkit-overflow-scrolling:touch}#lychee_sidebar_content .sidebar__divider{padding:12px 0 8px;width:100%;border-top:1px solid rgba(255,255,255,.02);-webkit-box-shadow:0 -1px 0 rgba(0,0,0,.2);box-shadow:0 -1px 0 rgba(0,0,0,.2)}#lychee_sidebar_content .sidebar__divider:first-child{border-top:0;-webkit-box-shadow:none;box-shadow:none}#lychee_sidebar_content .sidebar__divider h1{margin:0 0 0 20px;color:rgba(255,255,255,.6);font-size:14px;font-weight:700;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}#lychee_sidebar_content .edit{display:inline-block;margin-left:3px;width:10px}#lychee_sidebar_content .edit .iconic{width:10px;height:10px;fill:rgba(255,255,255,.5);-webkit-transition:fill .2s ease-out;-o-transition:fill .2s ease-out;transition:fill .2s ease-out}#lychee_sidebar_content .edit:active .iconic{-webkit-transition:none;-o-transition:none;transition:none;fill:rgba(255,255,255,.8)}#lychee_sidebar_content table{margin:10px 0 15px 20px;width:calc(100% - 20px)}#lychee_sidebar_content table tr td{padding:5px 0;color:#fff;font-size:14px;line-height:19px;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}#lychee_sidebar_content table tr td:first-child{width:110px}#lychee_sidebar_content table tr td:last-child{padding-right:10px}#lychee_sidebar_content table tr td span{-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}#lychee_sidebar_content #tags{width:calc(100% - 40px);margin:16px 20px 12px;color:#fff;display:inline-block}#lychee_sidebar_content #tags>div{display:inline-block}#lychee_sidebar_content #tags .empty{font-size:14px;margin:0 2px 8px 0;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}#lychee_sidebar_content #tags .edit{margin-top:6px}#lychee_sidebar_content #tags .empty .edit{margin-top:0}#lychee_sidebar_content #tags .tag{cursor:default;display:inline-block;padding:6px 10px;margin:0 6px 8px 0;background-color:rgba(0,0,0,.5);border-radius:100px;font-size:12px;-webkit-transition:background-color .2s;-o-transition:background-color .2s;transition:background-color .2s;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}#lychee_sidebar_content #tags .tag span{display:inline-block;padding:0;margin:0 0 -2px;width:0;overflow:hidden;-webkit-transform:scale(0);-ms-transform:scale(0);transform:scale(0);-webkit-transition:width .2s,margin .2s,fill .2s ease-out,-webkit-transform .2s;transition:width .2s,margin .2s,transform .2s,fill .2s ease-out,-webkit-transform .2s;-o-transition:width .2s,margin .2s,transform .2s,fill .2s ease-out}#lychee_sidebar_content #tags .tag span .iconic{fill:#d92c34;width:8px;height:8px}#lychee_sidebar_content #tags .tag span:active .iconic{-webkit-transition:none;-o-transition:none;transition:none;fill:#b22027}#lychee_sidebar_content #leaflet_map_single_photo{margin:10px 0 0 20px;height:180px;width:calc(100% - 40px)}#lychee_sidebar_content .attr_location.search{cursor:pointer}@media only screen and (max-width:567px),only screen and (max-width:640px) and (orientation:portrait){#lychee_sidebar_container{position:absolute;right:0}#lychee_sidebar{background-color:rgba(0,0,0,.6)}#lychee_sidebar,#lychee_sidebar_container.active{width:240px}#lychee_sidebar_header{height:22px}#lychee_sidebar_header h1{margin:6px 0;font-size:13px}#lychee_sidebar_content{padding-bottom:10px}#lychee_sidebar_content .sidebar__divider{padding:6px 0 2px}#lychee_sidebar_content .sidebar__divider h1{margin:0 0 0 10px;font-size:12px}#lychee_sidebar_content #tags,#lychee_sidebar_content table{margin:4px 0 6px 10px;width:calc(100% - 16px)}#lychee_sidebar_content table tr td{padding:2px 0;font-size:11px;line-height:12px}#lychee_sidebar_content table tr td:first-child{width:80px}#lychee_sidebar_content #tags .empty{margin:0;font-size:11px}}@media only screen and (min-width:568px) and (max-width:768px),only screen and (min-width:568px) and (max-width:640px) and (orientation:landscape){#lychee_sidebar,#lychee_sidebar_container.active{width:280px}#lychee_sidebar_header{height:28px}#lychee_sidebar_header h1{margin:8px 0;font-size:15px}#lychee_sidebar_content{padding-bottom:10px}#lychee_sidebar_content .sidebar__divider{padding:8px 0 4px}#lychee_sidebar_content .sidebar__divider h1{margin:0 0 0 10px;font-size:13px}#lychee_sidebar_content #tags,#lychee_sidebar_content table{margin:4px 0 6px 10px;width:calc(100% - 16px)}#lychee_sidebar_content table tr td{padding:2px 0;font-size:12px;line-height:13px}#lychee_sidebar_content table tr td:first-child{width:90px}#lychee_sidebar_content #tags .empty{margin:0;font-size:12px}}#lychee_loading{height:0;-webkit-transition:height .3s;-o-transition:height .3s;transition:height .3s;background-size:100px 3px;background-repeat:repeat-x;-webkit-animation-name:moveBackground;animation-name:moveBackground;-webkit-animation-duration:.3s;animation-duration:.3s;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite;-webkit-animation-timing-function:linear;animation-timing-function:linear}#lychee_loading.loading{height:3px;background-image:-webkit-gradient(linear,left top,right top,from(#153674),color-stop(47%,#153674),color-stop(53%,#2651ae),to(#2651ae));background-image:-o-linear-gradient(left,#153674 0,#153674 47%,#2651ae 53%,#2651ae 100%);background-image:linear-gradient(to right,#153674 0,#153674 47%,#2651ae 53%,#2651ae 100%)}#lychee_loading.error{height:40px;background-color:#2f0d0e;background-image:-webkit-gradient(linear,left top,right top,from(#451317),color-stop(47%,#451317),color-stop(53%,#aa3039),to(#aa3039));background-image:-o-linear-gradient(left,#451317 0,#451317 47%,#aa3039 53%,#aa3039 100%);background-image:linear-gradient(to right,#451317 0,#451317 47%,#aa3039 53%,#aa3039 100%)}#lychee_loading.success{height:40px;background-color:#070;background-image:-webkit-gradient(linear,left top,right top,from(#070),color-stop(47%,#090),color-stop(53%,#0a0),to(#0c0));background-image:-o-linear-gradient(left,#070 0,#090 47%,#0a0 53%,#0c0 100%);background-image:linear-gradient(to right,#070 0,#090 47%,#0a0 53%,#0c0 100%)}#lychee_loading h1{margin:13px 13px 0;color:#ddd;font-size:14px;font-weight:700;text-shadow:0 1px 0 #000;text-transform:capitalize}#lychee_loading h1 span{margin-left:10px;font-weight:400;text-transform:none}div.select,input,output,select,textarea{display:inline-block;position:relative}div.select>select{display:block;width:100%}div.select,input,output,select,select option,textarea{color:#fff;background-color:#2c2c2c;margin:0;font-size:inherit;line-height:inherit;padding:0;border:none;-webkit-box-shadow:none;box-shadow:none;outline:0}input[type=password],input[type=text],select{padding-top:3px;padding-bottom:3px}input[type=password],input[type=text]{padding-left:2px;padding-right:2px;background-color:transparent;border-bottom:1px solid #222;border-radius:0;-webkit-box-shadow:0 1px 0 rgba(255,255,255,.05);box-shadow:0 1px 0 rgba(255,255,255,.05)}input[type=password]:focus,input[type=text]:focus{border-bottom-color:#2293ec}input[type=password].error,input[type=text].error{border-bottom-color:#d92c34}input[type=checkbox]{top:2px;height:16px;width:16px;-webkit-appearance:none;-moz-appearance:none;appearance:none;color:#2293ec;border:none;border-radius:3px;-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.7);box-shadow:0 0 0 1px rgba(0,0,0,.7)}input[type=checkbox]::before{content:"✔";position:absolute;text-align:center;font-size:16px;line-height:16px;top:0;bottom:0;left:0;right:0;width:auto;height:auto;visibility:hidden}input[type=checkbox]:checked::before{visibility:visible}input[type=checkbox].slider{top:5px;height:22px;width:42px;border:1px solid rgba(0,0,0,.2);-webkit-box-shadow:0 1px 0 rgba(255,255,255,.02);box-shadow:0 1px 0 rgba(255,255,255,.02);border-radius:11px;background:#2c2c2c}input[type=checkbox].slider::before{content:"";background-color:#2293ec;height:14px;width:14px;left:3px;top:3px;border:none;border-radius:7px;visibility:visible}input[type=checkbox].slider:checked{background-color:#2293ec}input[type=checkbox].slider:checked::before{left:auto;right:3px;background-color:#fff}div.select{font-size:12px;background:#2c2c2c;border-radius:3px;border:1px solid rgba(0,0,0,.2);-webkit-box-shadow:0 1px 0 rgba(255,255,255,.02);box-shadow:0 1px 0 rgba(255,255,255,.02)}div.select::after{position:absolute;content:"≡";right:8px;top:3px;color:#2293ec;font-size:16px;font-weight:700;pointer-events:none}select{padding-left:8px;padding-right:8px;-webkit-appearance:none;-moz-appearance:none;appearance:none;background:0 0}select option{padding:2px 0;-webkit-transition:none;-o-transition:none;transition:none}form div.input-group{position:relative;margin:18px 0}form div.input-group:first-child{margin-top:0}form div.input-group:last-child{margin-bottom:0}form div.input-group.hidden{display:none}form div.input-group label{font-weight:700}form div.input-group p{display:block;margin:6px 0;font-size:13px;line-height:16px}form div.input-group p:last-child{margin-bottom:0}form div.input-group.stacked>label{display:block;margin-bottom:6px}form div.input-group.stacked>label>input[type=password],form div.input-group.stacked>label>input[type=text]{margin-top:12px}form div.input-group.stacked>div.select,form div.input-group.stacked>input,form div.input-group.stacked>output,form div.input-group.stacked>textarea{width:100%;display:block}form div.input-group.compact{padding-left:120px}form div.input-group.compact>label{display:block;position:absolute;margin:0;left:0;width:108px;height:auto;top:3px;bottom:0;overflow-y:hidden}form div.input-group.compact>div.select,form div.input-group.compact>input,form div.input-group.compact>output,form div.input-group.compact>textarea{display:block;width:100%}form div.input-group.compact-inverse{padding-left:36px}form div.input-group.compact-inverse label{display:block}form div.input-group.compact-inverse>div.select,form div.input-group.compact-inverse>input,form div.input-group.compact-inverse>output,form div.input-group.compact-inverse>textarea{display:block;position:absolute;width:16px;height:16px;top:2px;left:0}form div.input-group.compact-no-indent>label{display:inline}form div.input-group.compact-no-indent>div.select,form div.input-group.compact-no-indent>input,form div.input-group.compact-no-indent>output,form div.input-group.compact-no-indent>textarea{display:inline-block;margin-left:.3em;margin-right:.3em}div.basicModal.about-dialog div.basicModal__content h1{font-size:120%;font-weight:700;text-align:center;color:#fff}div.basicModal.about-dialog div.basicModal__content h2{font-weight:700;color:#fff}div.basicModal.about-dialog div.basicModal__content p.update-status.up-to-date-git,div.basicModal.about-dialog div.basicModal__content p.update-status.up-to-date-release{display:none}div.basicModal.about-dialog div.basicModal__content p.about-desc{line-height:1.4em}div.basicModal.downloads div.basicModal__content a.button{display:block;margin:12px 0;padding:12px;font-weight:700;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.02);box-shadow:inset 0 1px 0 rgba(255,255,255,.02);border:1px solid rgba(0,0,0,.2)}div.basicModal.downloads div.basicModal__content a.button .iconic{width:12px;height:12px;margin-right:12px}div.basicModal.qr-code{width:300px}div.basicModal.qr-code div.basicModal__content{padding:12px}.basicModal.import div.basicModal__content{padding:12px 8px}.basicModal.import div.basicModal__content h1{margin-bottom:12px;color:#fff;font-size:16px;line-height:19px;font-weight:700;text-align:center}.basicModal.import div.basicModal__content ol{margin-top:12px;height:300px;background-color:#2c2c2c;overflow:hidden;overflow-y:auto;border-radius:3px;-webkit-box-shadow:inset 0 0 3px rgba(0,0,0,.4);box-shadow:inset 0 0 3px rgba(0,0,0,.4)}.basicModal.import div.basicModal__content ol li{float:left;padding:8px 0;width:100%;background-color:rgba(255,255,255,.02)}.basicModal.import div.basicModal__content ol li:nth-child(2n){background-color:rgba(255,255,255,0)}.basicModal.import div.basicModal__content ol li h2{float:left;padding:5px 10px;width:70%;color:#fff;font-size:14px;white-space:nowrap;overflow:hidden}.basicModal.import div.basicModal__content ol li p.status{float:left;padding:5px 10px;width:30%;color:#999;font-size:14px;text-align:right;-webkit-animation-name:pulse;animation-name:pulse;-webkit-animation-duration:2s;animation-duration:2s;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite}.basicModal.import div.basicModal__content ol li p.status.error,.basicModal.import div.basicModal__content ol li p.status.success,.basicModal.import div.basicModal__content ol li p.status.warning{-webkit-animation:none;animation:none}.basicModal.import div.basicModal__content ol li p.status.error{color:#d92c34}.basicModal.import div.basicModal__content ol li p.status.warning{color:#fc0}.basicModal.import div.basicModal__content ol li p.status.success{color:#0a0}.basicModal.import div.basicModal__content ol li p.notice{float:left;padding:2px 10px 5px;width:100%;color:#999;font-size:12px;overflow:hidden;line-height:16px}.basicModal.import div.basicModal__content ol li p.notice:empty{display:none}div.basicModal.login div.basicModal__content a.button#signInKeyLess{position:absolute;display:block;color:#999;top:8px;left:8px;width:30px;height:30px;margin:0;padding:5px;cursor:pointer;-webkit-box-shadow:inset 1px 1px 0 rgba(255,255,255,.02);box-shadow:inset 1px 1px 0 rgba(255,255,255,.02);border:1px solid rgba(0,0,0,.2)}div.basicModal.login div.basicModal__content a.button#signInKeyLess .iconic{width:100%;height:100%;fill:#999}div.basicModal.login div.basicModal__content p.version{font-size:12px;text-align:right}div.basicModal.login div.basicModal__content p.version span.update-status.up-to-date-git,div.basicModal.login div.basicModal__content p.version span.update-status.up-to-date-release{display:none}@media (hover:hover){#lychee_sidebar .edit:hover .iconic{fill:#fff}#lychee_sidebar #tags .tag:hover{background-color:rgba(0,0,0,.3)}#lychee_sidebar #tags .tag:hover.search{cursor:pointer}#lychee_sidebar #tags .tag:hover span{width:9px;margin:0 0 -2px 5px;-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}#lychee_sidebar #tags .tag span:hover .iconic{fill:#e1575e}div.basicModal.login div.basicModal__content a.button#signInKeyLess:hover{color:#fff;background:inherit}div.basicModal.login div.basicModal__content a.button#signInKeyLess:hover .iconic{fill:#fff}}form.photo-links div.input-group{padding-right:30px}form.photo-links div.input-group a.button{display:block;position:absolute;margin:0;padding:4px;right:0;bottom:0;width:26px;height:26px;cursor:pointer;-webkit-box-shadow:inset 1px 1px 0 rgba(255,255,255,.02);box-shadow:inset 1px 1px 0 rgba(255,255,255,.02);border:1px solid rgba(0,0,0,.2)}form.photo-links div.input-group a.button .iconic{width:100%;height:100%}form.token div.input-group{padding-right:82px}form.token div.input-group input.disabled,form.token div.input-group input[disabled]{color:#999}form.token div.input-group div.button-group{display:block;position:absolute;margin:0;padding:0;right:0;bottom:0;width:78px}form.token div.input-group div.button-group a.button{display:block;float:right;margin:0;padding:4px;bottom:4px;width:26px;height:26px;cursor:pointer;-webkit-box-shadow:inset 1px 1px 0 rgba(255,255,255,.02);box-shadow:inset 1px 1px 0 rgba(255,255,255,.02);border:1px solid rgba(0,0,0,.2)}form.token div.input-group div.button-group a.button .iconic{width:100%;height:100%}#sensitive_warning{background:rgba(100,0,0,.95);text-align:center;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;color:#fff}#sensitive_warning.active{display:-webkit-box;display:-ms-flexbox;display:flex}#sensitive_warning h1{font-size:36px;font-weight:700;border-bottom:2px solid #fff;margin-bottom:15px}#sensitive_warning p{font-size:20px;max-width:40%;margin-top:15px}.settings_view{width:90%;max-width:700px;margin-left:auto;margin-right:auto}.settings_view input.text{padding:9px 2px;width:calc(50% - 4px);background-color:transparent;color:#fff;border:none;border-bottom:1px solid #222;border-radius:0;-webkit-box-shadow:0 1px 0 rgba(255,255,255,.05);box-shadow:0 1px 0 rgba(255,255,255,.05);outline:0}.settings_view input.text:focus{border-bottom-color:#2293ec}.settings_view input.text .error{border-bottom-color:#d92c34}.settings_view .basicModal__button{color:#2293ec;display:inline-block;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.02),inset 1px 0 0 rgba(0,0,0,.2);box-shadow:inset 0 1px 0 rgba(255,255,255,.02),inset 1px 0 0 rgba(0,0,0,.2);border-radius:5px}.settings_view .basicModal__button_MORE,.settings_view .basicModal__button_SAVE{color:#b22027;border-radius:5px}.settings_view>div{font-size:14px;width:100%;padding:12px 0}.settings_view>div p{margin:0 0 5%;width:100%;color:#ccc;line-height:16px}.settings_view>div p a{color:rgba(255,255,255,.9);text-decoration:none;border-bottom:1px dashed #888}.settings_view>div p:last-of-type{margin:0}.settings_view>div input.text{width:100%}.settings_view>div textarea{padding:9px;width:calc(100% - 18px);height:100px;background-color:transparent;color:#fff;border:1px solid #666;border-radius:0;-webkit-box-shadow:0 1px 0 rgba(255,255,255,.05);box-shadow:0 1px 0 rgba(255,255,255,.05);outline:0;resize:vertical}.settings_view>div textarea:focus{border-color:#2293ec}.settings_view>div .choice{padding:0 30px 15px;width:100%;color:#fff}.settings_view>div .choice:last-child{padding-bottom:40px}.settings_view>div .choice label{float:left;color:#fff;font-size:14px;font-weight:700}.settings_view>div .choice label input{position:absolute;margin:0;opacity:0}.settings_view>div .choice label .checkbox{float:left;display:block;width:16px;height:16px;background:rgba(0,0,0,.5);border-radius:3px;-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.7);box-shadow:0 0 0 1px rgba(0,0,0,.7)}.settings_view>div .choice label .checkbox .iconic{-webkit-box-sizing:border-box;box-sizing:border-box;fill:#2293ec;padding:2px;opacity:0;-ms-transform:scale(0);-webkit-transform:scale(0);transform:scale(0);-webkit-transition:opacity .2s cubic-bezier(.51,.92,.24,1),-webkit-transform .2s cubic-bezier(.51,.92,.24,1);transition:opacity .2s cubic-bezier(.51,.92,.24,1),transform .2s cubic-bezier(.51,.92,.24,1),-webkit-transform .2s cubic-bezier(.51,.92,.24,1);-o-transition:opacity .2s cubic-bezier(.51,.92,.24,1),transform .2s cubic-bezier(.51,.92,.24,1)}.settings_view>div .select{position:relative;margin:1px 5px;padding:0;width:110px;color:#fff;border-radius:3px;border:1px solid rgba(0,0,0,.2);-webkit-box-shadow:0 1px 0 rgba(255,255,255,.02);box-shadow:0 1px 0 rgba(255,255,255,.02);font-size:11px;line-height:16px;overflow:hidden;outline:0;vertical-align:middle;background:rgba(0,0,0,.3);display:inline-block}.settings_view>div .select select{margin:0;padding:4px 8px;width:120%;color:#fff;font-size:11px;line-height:16px;border:0;outline:0;-webkit-box-shadow:none;box-shadow:none;border-radius:0;background-color:transparent;background-image:none;-moz-appearance:none;-webkit-appearance:none;appearance:none}.settings_view>div .select select option{margin:0;padding:0;background:#fff;color:#333;-webkit-transition:none;-o-transition:none;transition:none}.settings_view>div .select select:disabled{color:#000;cursor:not-allowed}.settings_view>div .select::after{position:absolute;content:"≡";right:8px;top:4px;color:#2293ec;font-size:16px;line-height:16px;font-weight:700;pointer-events:none}.settings_view>div .switch{position:relative;display:inline-block;width:42px;height:22px;bottom:-2px;line-height:24px}.settings_view>div .switch input{opacity:0;width:0;height:0}.settings_view>div .slider{position:absolute;cursor:pointer;top:0;left:0;right:0;bottom:0;border:1px solid rgba(0,0,0,.2);-webkit-box-shadow:0 1px 0 rgba(255,255,255,.02);box-shadow:0 1px 0 rgba(255,255,255,.02);background:rgba(0,0,0,.3);-webkit-transition:.4s;-o-transition:.4s;transition:.4s}.settings_view>div .slider:before{position:absolute;content:"";height:14px;width:14px;left:3px;bottom:3px;background-color:#2293ec}.settings_view>div input:checked+.slider{background-color:#2293ec}.settings_view>div input:checked+.slider:before{-ms-transform:translateX(20px);-webkit-transform:translateX(20px);transform:translateX(20px);background-color:#fff}.settings_view>div .slider.round{border-radius:20px}.settings_view>div .slider.round:before{border-radius:50%}.settings_view .setting_category{font-size:20px;width:100%;padding-top:10px;padding-left:4px;border-bottom:1px dotted #222;margin-top:20px;color:#fff;font-weight:700;text-transform:capitalize}.settings_view .setting_line{font-size:14px;width:100%}.settings_view .setting_line:first-child,.settings_view .setting_line:last-child{padding-top:50px}.settings_view .setting_line p{min-width:550px;margin:0;color:#ccc;display:inline-block;width:100%;overflow-wrap:break-word}.settings_view .setting_line p a{color:rgba(255,255,255,.9);text-decoration:none;border-bottom:1px dashed #888}.settings_view .setting_line p:last-of-type{margin:0}.settings_view .setting_line p .warning{margin-bottom:30px;color:#d92c34;font-weight:700;font-size:18px;text-align:justify;line-height:22px}.settings_view .setting_line span.text{display:inline-block;padding:9px 4px;width:calc(50% - 12px);background-color:transparent;color:#fff;border:none}.settings_view .setting_line span.text_icon{width:5%}.settings_view .setting_line span.text_icon .iconic{width:15px;height:14px;margin:0 10px 0 1px;fill:#fff}.settings_view .setting_line input.text{width:calc(50% - 4px)}@media (hover:hover){.settings_view .basicModal__button:hover{background:#2293ec;color:#fff;cursor:pointer}.settings_view .basicModal__button_MORE:hover,.settings_view .basicModal__button_SAVE:hover{background:#b22027;color:#fff}.settings_view input:hover{border-bottom:1px solid #2293ec}}@media (hover:none){#lychee_left_menu a{padding:14px 8px 14px 32px}.settings_view input.text{border-bottom:1px solid #2293ec;margin:6px 0}.settings_view>div{padding:16px 0}.settings_view .basicModal__button{background:#2293ec;color:#fff;max-width:320px;margin-top:20px}.settings_view .basicModal__button_MORE,.settings_view .basicModal__button_SAVE{background:#b22027}}@media only screen and (max-width:567px),only screen and (max-width:640px) and (orientation:portrait){.settings_view{max-width:100%}.settings_view .setting_category{font-size:14px;padding-left:0;margin-bottom:4px}.settings_view .setting_line{font-size:12px}.settings_view .setting_line:first-child{padding-top:20px}.settings_view .setting_line p{min-width:unset;line-height:20px}.settings_view .setting_line p.warning{font-size:14px;line-height:16px;margin-bottom:0}.settings_view .setting_line p input,.settings_view .setting_line p span{padding:0}.settings_view .basicModal__button_SAVE{margin-top:20px}}.users_view{width:90%;max-width:700px;margin-left:auto;margin-right:auto}.users_view_line{font-size:14px;width:100%}.users_view_line:first-child,.users_view_line:last-child{padding-top:50px}.users_view_line p{width:550px;margin:0 0 5%;color:#ccc;display:inline-block}.users_view_line p a{color:rgba(255,255,255,.9);text-decoration:none;border-bottom:1px dashed #888}.users_view_line p.line,.users_view_line p:last-of-type{margin:0}.users_view_line span.text{display:inline-block;padding:9px 6px 9px 0;width:40%;background-color:transparent;color:#fff;border:none}.users_view_line span.text_icon{width:5%;min-width:32px}.users_view_line span.text_icon .iconic{width:15px;height:14px;margin:0 8px;fill:#fff}.users_view_line input.text{padding:9px 6px 9px 0;width:40%;background-color:transparent;color:#fff;border:none;border-bottom:1px solid #222;border-radius:0;-webkit-box-shadow:0 1px 0 rgba(255,255,255,.05);box-shadow:0 1px 0 rgba(255,255,255,.05);outline:0;margin:0 0 10px}.users_view_line input.text:focus{border-bottom-color:#2293ec}.users_view_line input.text.error{border-bottom-color:#d92c34}.users_view_line .choice label input:checked~.checkbox .iconic{opacity:1;-ms-transform:scale(1);-webkit-transform:scale(1);transform:scale(1)}.users_view_line .choice{display:inline-block;width:5%;min-width:32px;color:#fff}.users_view_line .choice input{position:absolute;margin:0;opacity:0}.users_view_line .choice .checkbox{display:inline-block;width:16px;height:16px;margin:10px 8px 0;background:rgba(0,0,0,.5);border-radius:3px;-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.7);box-shadow:0 0 0 1px rgba(0,0,0,.7)}.users_view_line .choice .checkbox .iconic{-webkit-box-sizing:border-box;box-sizing:border-box;fill:#2293ec;padding:2px;opacity:0;-ms-transform:scale(0);-webkit-transform:scale(0);transform:scale(0);-webkit-transition:opacity .2s cubic-bezier(.51,.92,.24,1),-webkit-transform .2s cubic-bezier(.51,.92,.24,1);transition:opacity .2s cubic-bezier(.51,.92,.24,1),transform .2s cubic-bezier(.51,.92,.24,1),-webkit-transform .2s cubic-bezier(.51,.92,.24,1);-o-transition:opacity .2s cubic-bezier(.51,.92,.24,1),transform .2s cubic-bezier(.51,.92,.24,1)}.users_view_line .basicModal__button{display:inline-block;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.02),inset 1px 0 0 rgba(0,0,0,.2);box-shadow:inset 0 1px 0 rgba(255,255,255,.02),inset 1px 0 0 rgba(0,0,0,.2);width:10%;min-width:72px;border-radius:0}.users_view_line .basicModal__button_OK{color:#2293ec;border-radius:5px 0 0 5px;margin-right:-4px}.users_view_line .basicModal__button_OK_no_DEL{border-radius:5px;min-width:144px;width:20%}.users_view_line .basicModal__button_DEL{color:#b22027;border-radius:0 5px 5px 0}.users_view_line .basicModal__button_CREATE{width:20%;color:#090;border-radius:5px;min-width:144px}.users_view_line .select{position:relative;margin:1px 5px;padding:0;width:110px;color:#fff;border-radius:3px;border:1px solid rgba(0,0,0,.2);-webkit-box-shadow:0 1px 0 rgba(255,255,255,.02);box-shadow:0 1px 0 rgba(255,255,255,.02);font-size:11px;line-height:16px;overflow:hidden;outline:0;vertical-align:middle;background:rgba(0,0,0,.3);display:inline-block}.users_view_line .select select{margin:0;padding:4px 8px;width:120%;color:#fff;font-size:11px;line-height:16px;border:0;outline:0;-webkit-box-shadow:none;box-shadow:none;border-radius:0;background:0 0;-moz-appearance:none;-webkit-appearance:none;appearance:none}.users_view_line .select select option{margin:0;padding:0;background:#fff;color:#333;-webkit-transition:none;-o-transition:none;transition:none}.users_view_line .select::after{position:absolute;content:"≡";right:8px;top:4px;color:#2293ec;font-size:16px;line-height:16px;font-weight:700;pointer-events:none}@media (hover:hover){.users_view_line .basicModal__button:hover{cursor:pointer;color:#fff}.users_view_line .basicModal__button_OK:hover{background:#2293ec}.users_view_line .basicModal__button_DEL:hover{background:#b22027}.users_view_line .basicModal__button_CREATE:hover{background:#090}.users_view_line input:hover{border-bottom:1px solid #2293ec}}@media (hover:none){.users_view_line .basicModal__button{color:#fff}.users_view_line .basicModal__button_OK{background:#2293ec}.users_view_line .basicModal__button_DEL{background:#b22027}.users_view_line .basicModal__button_CREATE{background:#090}.users_view_line input{border-bottom:1px solid #2293ec}}@media only screen and (max-width:567px),only screen and (max-width:640px) and (orientation:portrait){.users_view{width:100%;max-width:100%;padding:20px}.users_view_line p{width:100%}.users_view_line p .text,.users_view_line p input.text{width:36%;font-size:smaller}.users_view_line .choice{margin-left:-8px;margin-right:3px}}.u2f_view{width:90%;max-width:700px;margin-left:auto;margin-right:auto}.u2f_view_line{font-size:14px;width:100%}.u2f_view_line:first-child,.u2f_view_line:last-child{padding-top:50px}.u2f_view_line p{width:550px;margin:0 0 5%;color:#ccc;display:inline-block}.u2f_view_line p a{color:rgba(255,255,255,.9);text-decoration:none;border-bottom:1px dashed #888}.u2f_view_line p.line,.u2f_view_line p:last-of-type{margin:0}.u2f_view_line p.single{text-align:center}.u2f_view_line span.text{display:inline-block;padding:9px 4px;width:80%;background-color:transparent;color:#fff;border:none}.u2f_view_line span.text_icon{width:5%}.u2f_view_line span.text_icon .iconic{width:15px;height:14px;margin:0 15px 0 1px;fill:#fff}.u2f_view_line .choice label input:checked~.checkbox .iconic{opacity:1;-ms-transform:scale(1);-webkit-transform:scale(1);transform:scale(1)}.u2f_view_line .choice{display:inline-block;width:5%;color:#fff}.u2f_view_line .choice input{position:absolute;margin:0;opacity:0}.u2f_view_line .choice .checkbox{display:inline-block;width:16px;height:16px;margin-top:10px;margin-left:2px;background:rgba(0,0,0,.5);border-radius:3px;-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.7);box-shadow:0 0 0 1px rgba(0,0,0,.7)}.u2f_view_line .choice .checkbox .iconic{-webkit-box-sizing:border-box;box-sizing:border-box;fill:#2293ec;padding:2px;opacity:0;-ms-transform:scale(0);-webkit-transform:scale(0);transform:scale(0);-webkit-transition:opacity .2s cubic-bezier(.51,.92,.24,1),-webkit-transform .2s cubic-bezier(.51,.92,.24,1);transition:opacity .2s cubic-bezier(.51,.92,.24,1),transform .2s cubic-bezier(.51,.92,.24,1),-webkit-transform .2s cubic-bezier(.51,.92,.24,1);-o-transition:opacity .2s cubic-bezier(.51,.92,.24,1),transform .2s cubic-bezier(.51,.92,.24,1)}.u2f_view_line .basicModal__button{display:inline-block;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.02),inset 1px 0 0 rgba(0,0,0,.2);box-shadow:inset 0 1px 0 rgba(255,255,255,.02),inset 1px 0 0 rgba(0,0,0,.2);width:20%;min-width:50px;border-radius:0}.u2f_view_line .basicModal__button_OK{color:#2293ec;border-radius:5px 0 0 5px}.u2f_view_line .basicModal__button_DEL{color:#b22027;border-radius:0 5px 5px 0}.u2f_view_line .basicModal__button_CREATE{width:100%;color:#090;border-radius:5px}.u2f_view_line .select{position:relative;margin:1px 5px;padding:0;width:110px;color:#fff;border-radius:3px;border:1px solid rgba(0,0,0,.2);-webkit-box-shadow:0 1px 0 rgba(255,255,255,.02);box-shadow:0 1px 0 rgba(255,255,255,.02);font-size:11px;line-height:16px;overflow:hidden;outline:0;vertical-align:middle;background:rgba(0,0,0,.3);display:inline-block}.u2f_view_line .select select{margin:0;padding:4px 8px;width:120%;color:#fff;font-size:11px;line-height:16px;border:0;outline:0;-webkit-box-shadow:none;box-shadow:none;border-radius:0;background:0 0;-moz-appearance:none;-webkit-appearance:none;appearance:none}.u2f_view_line .select select option{margin:0;padding:0;background:#fff;color:#333;-webkit-transition:none;-o-transition:none;transition:none}.u2f_view_line .select::after{position:absolute;content:"≡";right:8px;top:4px;color:#2293ec;font-size:16px;line-height:16px;font-weight:700;pointer-events:none}@media (hover:hover){.u2f_view_line .basicModal__button:hover{cursor:pointer}.u2f_view_line .basicModal__button_OK:hover{background:#2293ec;color:#fff}.u2f_view_line .basicModal__button_DEL:hover{background:#b22027;color:#fff}.u2f_view_line .basicModal__button_CREATE:hover{background:#090;color:#fff}.u2f_view_line input:hover{border-bottom:1px solid #2293ec}}@media (hover:none){.u2f_view_line .basicModal__button{color:#fff}.u2f_view_line .basicModal__button_OK{background:#2293ec}.u2f_view_line .basicModal__button_DEL{background:#b22027}.u2f_view_line .basicModal__button_CREATE{background:#090}.u2f_view_line input{border-bottom:1px solid #2293ec}}@media only screen and (max-width:567px),only screen and (max-width:640px) and (orientation:portrait){.u2f_view{width:100%;max-width:100%;padding:20px}.u2f_view_line p{width:100%}.u2f_view_line .basicModal__button_CREATE{width:80%;margin:0 10%}}.logs_diagnostics_view{width:90%;margin-left:auto;margin-right:auto;color:#ccc;font-size:12px;line-height:14px}.logs_diagnostics_view pre{font-family:monospace;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;padding-right:30px}.clear_logs_update{padding-left:30px;margin:20px auto}.clear_logs_update .basicModal__button,.logs_diagnostics_view .basicModal__button{color:#2293ec;display:inline-block;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.02),inset 1px 0 0 rgba(0,0,0,.2);box-shadow:inset 0 1px 0 rgba(255,255,255,.02),inset 1px 0 0 rgba(0,0,0,.2);border-radius:5px}.clear_logs_update .iconic,.logs_diagnostics_view .iconic{display:inline-block;margin:0 10px 0 1px;width:13px;height:12px;fill:#2293ec}.clear_logs_update .button_left,.logs_diagnostics_view .button_left{margin-left:24px;width:400px}@media (hover:none){.clear_logs_update .basicModal__button,.logs_diagnostics_view .basicModal__button{background:#2293ec;color:#fff;max-width:320px;margin-top:20px}.clear_logs_update .iconic,.logs_diagnostics_view .iconic{fill:#fff}}@media only screen and (max-width:567px),only screen and (max-width:640px) and (orientation:portrait){.clear_logs_update,.logs_diagnostics_view{width:100%;max-width:100%;font-size:11px;line-height:12px}.clear_logs_update .basicModal__button,.clear_logs_update .button_left,.logs_diagnostics_view .basicModal__button,.logs_diagnostics_view .button_left{width:80%;margin:0 10%}.logs_diagnostics_view{padding:10px 10px 0 0}.clear_logs_update{padding:10px 10px 0;margin:0}}.sharing_view{width:90%;max-width:700px;margin-left:auto;margin-right:auto;margin-top:20px}.sharing_view .sharing_view_line{width:100%;display:block;clear:left}.sharing_view .col-xs-1,.sharing_view .col-xs-10,.sharing_view .col-xs-11,.sharing_view .col-xs-12,.sharing_view .col-xs-2,.sharing_view .col-xs-3,.sharing_view .col-xs-4,.sharing_view .col-xs-5,.sharing_view .col-xs-6,.sharing_view .col-xs-7,.sharing_view .col-xs-8,.sharing_view .col-xs-9{float:left;position:relative;min-height:1px}.sharing_view .col-xs-2{width:10%;padding-right:3%;padding-left:3%}.sharing_view .col-xs-5{width:42%}.sharing_view .btn-block+.btn-block{margin-top:5px}.sharing_view .btn-block{display:block;width:100%}.sharing_view .btn-default{color:#2293ec;border-color:#2293ec;background:rgba(0,0,0,.5);border-radius:3px;-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.7);box-shadow:0 0 0 1px rgba(0,0,0,.7)}.sharing_view .btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.sharing_view select[multiple],.sharing_view select[size]{height:150px}.sharing_view .form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color .15s ease-in-out,-webkit-box-shadow .15s ease-in-out;-o-transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-box-shadow .15s ease-in-out}.sharing_view .iconic{display:inline-block;width:15px;height:14px;fill:#2293ec}.sharing_view .iconic .iconic.ionicons{margin:0 8px -2px 0;width:18px;height:18px}.sharing_view .blue .iconic{fill:#2293ec}.sharing_view .grey .iconic{fill:#b4b4b4}.sharing_view p{width:100%;color:#ccc;text-align:center;font-size:14px;display:block}.sharing_view p.with{padding:15px 0}.sharing_view span.text{display:inline-block;padding:0 2px;width:40%;background-color:transparent;color:#fff;border:none}.sharing_view span.text:last-of-type{width:5%}.sharing_view span.text .iconic{width:15px;height:14px;margin:0 10px 0 1px;fill:#fff}.sharing_view .basicModal__button{margin-top:10px;color:#2293ec;display:inline-block;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.02),inset 1px 0 0 rgba(0,0,0,.2);box-shadow:inset 0 1px 0 rgba(255,255,255,.02),inset 1px 0 0 rgba(0,0,0,.2);border-radius:5px}.sharing_view .choice label input:checked~.checkbox .iconic{opacity:1;-ms-transform:scale(1);-webkit-transform:scale(1);transform:scale(1)}.sharing_view .choice{display:inline-block;width:5%;margin:0 10px;color:#fff}.sharing_view .choice input{position:absolute;margin:0;opacity:0}.sharing_view .choice .checkbox{display:inline-block;width:16px;height:16px;margin-top:10px;margin-left:2px;background:rgba(0,0,0,.5);border-radius:3px;-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.7);box-shadow:0 0 0 1px rgba(0,0,0,.7)}.sharing_view .choice .checkbox .iconic{-webkit-box-sizing:border-box;box-sizing:border-box;fill:#2293ec;padding:2px;opacity:0;-ms-transform:scale(0);-webkit-transform:scale(0);transform:scale(0);-webkit-transition:opacity .2s cubic-bezier(.51,.92,.24,1),-webkit-transform .2s cubic-bezier(.51,.92,.24,1);transition:opacity .2s cubic-bezier(.51,.92,.24,1),transform .2s cubic-bezier(.51,.92,.24,1),-webkit-transform .2s cubic-bezier(.51,.92,.24,1);-o-transition:opacity .2s cubic-bezier(.51,.92,.24,1),transform .2s cubic-bezier(.51,.92,.24,1)}.sharing_view .select{position:relative;padding:0;color:#fff;border-radius:3px;border:1px solid rgba(0,0,0,.2);-webkit-box-shadow:0 1px 0 rgba(255,255,255,.02);box-shadow:0 1px 0 rgba(255,255,255,.02);font-size:14px;line-height:16px;outline:0;vertical-align:middle;background:rgba(0,0,0,.3);display:inline-block}.sharing_view .borderBlue{border:1px solid #2293ec}@media (hover:none){.sharing_view .basicModal__button{background:#2293ec;color:#fff}.sharing_view input{border-bottom:1px solid #2293ec}}@media only screen and (max-width:567px),only screen and (max-width:640px) and (orientation:portrait){.sharing_view{width:100%;max-width:100%;padding:10px}.sharing_view .select{font-size:12px}.sharing_view .iconic{margin-left:-4px}.sharing_view_line p{width:100%}.sharing_view_line .basicModal__button{width:80%;margin:0 10%}}#multiselect{position:absolute;background-color:rgba(0,94,204,.3);border:1px solid #005ecc;border-radius:3px;z-index:5}.justified-layout,.unjustified-layout{margin:30px;width:100%;position:relative}.justified-layout.laying-out,.unjustified-layout.laying-out{display:none}.justified-layout>.photo{position:absolute;--lychee-default-height:320px;margin:0}.unjustified-layout>.photo{float:left;max-height:240px;margin:5px}.justified-layout>.photo>.thumbimg,.justified-layout>.photo>.thumbimg>img,.unjustified-layout>.photo>.thumbimg,.unjustified-layout>.photo>.thumbimg>img{width:100%;height:100%;border:none;-o-object-fit:cover;object-fit:cover}.justified-layout>.photo>.overlay,.unjustified-layout>.photo>.overlay{width:100%;bottom:0;margin:0}.justified-layout>.photo>.overlay>h1,.unjustified-layout>.photo>.overlay>h1{width:auto;margin-right:15px}@media only screen and (min-width:320px) and (max-width:567px){.justified-layout{margin:8px}.justified-layout .photo{--lychee-default-height:160px}}@media only screen and (min-width:568px) and (max-width:639px){.justified-layout{margin:9px}.justified-layout .photo{--lychee-default-height:200px}}@media only screen and (min-width:640px) and (max-width:768px){.justified-layout{margin:10px}.justified-layout .photo{--lychee-default-height:240px}}#lychee_footer{text-align:center;padding:5px 0;background:#1d1d1d;-webkit-transition:color .3s,opacity .3s ease-out,margin-left .5s,-webkit-transform .3s ease-out,-webkit-box-shadow .3s;transition:color .3s,opacity .3s ease-out,transform .3s ease-out,box-shadow .3s,margin-left .5s,-webkit-transform .3s ease-out,-webkit-box-shadow .3s;-o-transition:color .3s,opacity .3s ease-out,transform .3s ease-out,box-shadow .3s,margin-left .5s}#lychee_footer p{color:#ccc;font-size:.75em;font-weight:400;line-height:26px}#lychee_footer p a,#lychee_footer p a:visited{color:#ccc}#lychee_footer p.home_copyright,#lychee_footer p.hosted_by{text-transform:uppercase}#lychee_footer #home_socials a[href=""],#lychee_footer p:empty,.hide_footer,body.mode-frame div#footer,body.mode-none div#footer{display:none}@font-face{font-family:socials;src:url(fonts/socials.eot?egvu10);src:url(fonts/socials.eot?egvu10#iefix) format("embedded-opentype"),url(fonts/socials.ttf?egvu10) format("truetype"),url(fonts/socials.woff?egvu10) format("woff"),url(fonts/socials.svg?egvu10#socials) format("svg");font-weight:400;font-style:normal}#socials_footer{padding:0;text-align:center;left:0;right:0}.socialicons{display:inline-block;font-size:18px;font-family:socials!important;speak:none;color:#ccc;text-decoration:none;margin:15px 15px 5px;transition:.3s;-webkit-transition:.3s;-moz-transition:.3s;-o-transition:.3s}#twitter:before{content:"\ea96"}#instagram:before{content:"\ea92"}#youtube:before{content:"\ea9d"}#flickr:before{content:"\eaa4"}#facebook:before{content:"\ea91"}@media (hover:hover){.sharing_view .basicModal__button:hover{background:#2293ec;color:#fff;cursor:pointer}.sharing_view input:hover{border-bottom:1px solid #2293ec}.socialicons:hover{color:#b5b5b5;-ms-transform:scale(1.3);transform:scale(1.3);-webkit-transform:scale(1.3)}}@media tv{.basicModal__button:focus{background:#2293ec;color:#fff;cursor:pointer;outline-style:none}.basicModal__button#basicModal__action:focus{color:#fff}.photo:focus{outline:#fff solid 10px}.album:focus{outline-width:0}.toolbar .button:focus{outline-width:0;background-color:#fff}.header__title:focus{outline-width:0;background-color:#fff;color:#000}.toolbar .button:focus .iconic{fill:#000}#imageview{background-color:#000}#imageview #image,#imageview #livephoto{outline-width:0}}#lychee_view_container{position:absolute;top:0;left:0;height:100%;width:100%;overflow:clip auto}.leaflet-image-layer,.leaflet-layer,.leaflet-marker-icon,.leaflet-marker-shadow,.leaflet-pane,.leaflet-pane>canvas,.leaflet-pane>svg,.leaflet-tile,.leaflet-tile-container,.leaflet-zoom-box{position:absolute;left:0;top:0}.leaflet-container{overflow:hidden;-webkit-tap-highlight-color:transparent;background:#ddd;outline-offset:1px;font-family:"Helvetica Neue",Arial,Helvetica,sans-serif;font-size:.75rem;line-height:1.5}.leaflet-marker-icon,.leaflet-marker-shadow,.leaflet-tile{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-user-drag:none}.leaflet-tile::-moz-selection{background:0 0}.leaflet-tile::selection{background:0 0}.leaflet-safari .leaflet-tile{image-rendering:-webkit-optimize-contrast}.leaflet-safari .leaflet-tile-container{width:1600px;height:1600px;-webkit-transform-origin:0 0}.leaflet-marker-icon,.leaflet-marker-shadow{display:block}.leaflet-container .leaflet-overlay-pane svg{max-width:none!important;max-height:none!important}.leaflet-container .leaflet-marker-pane img,.leaflet-container .leaflet-shadow-pane img,.leaflet-container .leaflet-tile,.leaflet-container .leaflet-tile-pane img,.leaflet-container img.leaflet-image-layer{max-width:none!important;max-height:none!important;width:auto;padding:0}.leaflet-container.leaflet-touch-zoom{-ms-touch-action:pan-x pan-y;touch-action:pan-x pan-y}.leaflet-container.leaflet-touch-drag{-ms-touch-action:pinch-zoom;touch-action:none;touch-action:pinch-zoom}.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom{-ms-touch-action:none;touch-action:none}.leaflet-container a{-webkit-tap-highlight-color:rgba(51,181,229,.4);color:#0078a8}.leaflet-tile{-webkit-filter:inherit;filter:inherit;visibility:hidden}.leaflet-tile-loaded{visibility:inherit}.leaflet-zoom-box{width:0;height:0;-webkit-box-sizing:border-box;box-sizing:border-box;z-index:800}.leaflet-overlay-pane svg{-moz-user-select:none}.leaflet-pane{z-index:400}.leaflet-tile-pane{z-index:200}.leaflet-overlay-pane{z-index:400}.leaflet-shadow-pane{z-index:500}.leaflet-marker-pane{z-index:600}.leaflet-tooltip-pane{z-index:650}.leaflet-popup-pane{z-index:700}.leaflet-map-pane canvas{z-index:100}.leaflet-map-pane svg{z-index:200}.leaflet-vml-shape{width:1px;height:1px}.lvml{behavior:url(#default#VML);display:inline-block;position:absolute}.leaflet-control{position:relative;z-index:800;pointer-events:visiblePainted;pointer-events:auto;float:left;clear:both}.leaflet-bottom,.leaflet-top{position:absolute;z-index:1000;pointer-events:none}.leaflet-top{top:0}.leaflet-right{right:0}.leaflet-bottom{bottom:0}.leaflet-left{left:0}.leaflet-right .leaflet-control{float:right;margin-right:10px}.leaflet-top .leaflet-control{margin-top:10px}.leaflet-bottom .leaflet-control{margin-bottom:10px}.leaflet-left .leaflet-control{margin-left:10px}.leaflet-fade-anim .leaflet-popup{opacity:0;-webkit-transition:opacity .2s linear;-o-transition:opacity .2s linear;transition:opacity .2s linear}.leaflet-fade-anim .leaflet-map-pane .leaflet-popup{opacity:1}.leaflet-zoom-animated{-webkit-transform-origin:0 0;-ms-transform-origin:0 0;transform-origin:0 0}svg.leaflet-zoom-animated{will-change:transform}.leaflet-zoom-anim .leaflet-zoom-animated{-webkit-transition:-webkit-transform .25s cubic-bezier(0,0,.25,1);transition:transform .25s cubic-bezier(0,0,.25,1);-o-transition:transform .25s cubic-bezier(0,0,.25,1);transition:transform .25s cubic-bezier(0,0,.25,1),-webkit-transform .25s cubic-bezier(0,0,.25,1)}.leaflet-pan-anim .leaflet-tile,.leaflet-zoom-anim .leaflet-tile{-webkit-transition:none;-o-transition:none;transition:none}.leaflet-zoom-anim .leaflet-zoom-hide{visibility:hidden}.leaflet-interactive{cursor:pointer}.leaflet-grab{cursor:-webkit-grab;cursor:grab}.leaflet-crosshair,.leaflet-crosshair .leaflet-interactive{cursor:crosshair}.leaflet-control,.leaflet-popup-pane{cursor:auto}.leaflet-dragging .leaflet-grab,.leaflet-dragging .leaflet-grab .leaflet-interactive,.leaflet-dragging .leaflet-marker-draggable{cursor:move;cursor:-webkit-grabbing;cursor:grabbing}.leaflet-image-layer,.leaflet-marker-icon,.leaflet-marker-shadow,.leaflet-pane>svg path,.leaflet-tile-container{pointer-events:none}.leaflet-image-layer.leaflet-interactive,.leaflet-marker-icon.leaflet-interactive,.leaflet-pane>svg path.leaflet-interactive,svg.leaflet-image-layer.leaflet-interactive path{pointer-events:visiblePainted;pointer-events:auto}.leaflet-zoom-box{border:2px dotted #38f;background:rgba(255,255,255,.5)}.leaflet-bar{-webkit-box-shadow:0 1px 5px rgba(0,0,0,.65);box-shadow:0 1px 5px rgba(0,0,0,.65);border-radius:4px}.leaflet-bar a{background-color:#fff;border-bottom:1px solid #ccc;width:26px;height:26px;line-height:26px;text-align:center;text-decoration:none;color:#000}.leaflet-bar a,.leaflet-control-layers-toggle{background-position:50% 50%;background-repeat:no-repeat;display:block}.leaflet-bar a:focus,.leaflet-bar a:hover{background-color:#f4f4f4}.leaflet-bar a:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.leaflet-bar a:last-child{border-bottom-left-radius:4px;border-bottom-right-radius:4px;border-bottom:none}.leaflet-bar a.leaflet-disabled{cursor:default;background-color:#f4f4f4;color:#bbb}.leaflet-touch .leaflet-bar a{width:30px;height:30px;line-height:30px}.leaflet-touch .leaflet-bar a:first-child{border-top-left-radius:2px;border-top-right-radius:2px}.leaflet-touch .leaflet-bar a:last-child{border-bottom-left-radius:2px;border-bottom-right-radius:2px}.leaflet-control-zoom-in,.leaflet-control-zoom-out{font:bold 18px "Lucida Console",Monaco,monospace;text-indent:1px}.leaflet-touch .leaflet-control-zoom-in,.leaflet-touch .leaflet-control-zoom-out{font-size:22px}.leaflet-control-layers{-webkit-box-shadow:0 1px 5px rgba(0,0,0,.4);box-shadow:0 1px 5px rgba(0,0,0,.4);background:#fff;border-radius:5px}.leaflet-control-layers-toggle{background-image:url(images/layers.png);width:36px;height:36px}.leaflet-retina .leaflet-control-layers-toggle{background-image:url(images/layers-2x.png);background-size:26px 26px}.leaflet-touch .leaflet-control-layers-toggle{width:44px;height:44px}.leaflet-control-layers .leaflet-control-layers-list,.leaflet-control-layers-expanded .leaflet-control-layers-toggle{display:none}.leaflet-control-layers-expanded .leaflet-control-layers-list{display:block;position:relative}.leaflet-control-layers-expanded{padding:6px 10px 6px 6px;color:#333;background:#fff}.leaflet-control-layers-scrollbar{overflow-y:scroll;overflow-x:hidden;padding-right:5px}.leaflet-control-layers-selector{margin-top:2px;position:relative;top:1px}.leaflet-control-layers label{display:block;font-size:1.08333em}.leaflet-control-layers-separator{height:0;border-top:1px solid #ddd;margin:5px -10px 5px -6px}.leaflet-default-icon-path{background-image:url(images/marker-icon.png)}.leaflet-container .leaflet-control-attribution{background:rgba(255,255,255,.8);margin:0}.leaflet-control-attribution,.leaflet-control-scale-line{padding:0 5px;color:#333;line-height:1.4}.leaflet-control-attribution a{text-decoration:none}.leaflet-control-attribution a:focus,.leaflet-control-attribution a:hover{text-decoration:underline}.leaflet-attribution-flag{display:inline!important;vertical-align:baseline!important;width:1em;height:.6669em}.leaflet-left .leaflet-control-scale{margin-left:5px}.leaflet-bottom .leaflet-control-scale{margin-bottom:5px}.leaflet-control-scale-line{border:2px solid #777;border-top:none;line-height:1.1;padding:2px 5px 1px;white-space:nowrap;overflow:hidden;-webkit-box-sizing:border-box;box-sizing:border-box;background:rgba(255,255,255,.5)}.leaflet-control-scale-line:not(:first-child){border-top:2px solid #777;border-bottom:none;margin-top:-2px}.leaflet-control-scale-line:not(:first-child):not(:last-child){border-bottom:2px solid #777}.leaflet-touch .leaflet-bar,.leaflet-touch .leaflet-control-attribution,.leaflet-touch .leaflet-control-layers{-webkit-box-shadow:none;box-shadow:none}.leaflet-touch .leaflet-bar,.leaflet-touch .leaflet-control-layers{border:2px solid rgba(0,0,0,.2);background-clip:padding-box}.leaflet-popup{position:absolute;text-align:center;margin-bottom:20px}.leaflet-popup-content-wrapper{padding:1px;text-align:left;border-radius:12px}.leaflet-popup-content{margin:13px 24px 13px 20px;line-height:1.3;font-size:1.08333em;min-height:1px}.leaflet-popup-content p{margin:1.3em 0}.leaflet-popup-tip-container{width:40px;height:20px;position:absolute;left:50%;margin-top:-1px;margin-left:-20px;overflow:hidden;pointer-events:none}.leaflet-popup-tip{width:17px;height:17px;padding:1px;margin:-10px auto 0;pointer-events:auto;-webkit-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg)}.leaflet-popup-content-wrapper,.leaflet-popup-tip{background:#fff;color:#333;-webkit-box-shadow:0 3px 14px rgba(0,0,0,.4);box-shadow:0 3px 14px rgba(0,0,0,.4)}.leaflet-container a.leaflet-popup-close-button{position:absolute;top:0;right:0;border:none;text-align:center;width:24px;height:24px;font:16px/24px Tahoma,Verdana,sans-serif;color:#757575;text-decoration:none;background:0 0}.leaflet-container a.leaflet-popup-close-button:focus,.leaflet-container a.leaflet-popup-close-button:hover{color:#585858}.leaflet-popup-scrolled{overflow:auto}.leaflet-oldie .leaflet-popup-content-wrapper{-ms-zoom:1}.leaflet-oldie .leaflet-popup-tip{width:24px;margin:0 auto}.leaflet-oldie .leaflet-control-layers,.leaflet-oldie .leaflet-control-zoom,.leaflet-oldie .leaflet-popup-content-wrapper,.leaflet-oldie .leaflet-popup-tip{border:1px solid #999}.leaflet-div-icon{background:#fff;border:1px solid #666}.leaflet-tooltip{position:absolute;padding:6px;background-color:#fff;border:1px solid #fff;border-radius:3px;color:#222;white-space:nowrap;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;pointer-events:none;-webkit-box-shadow:0 1px 3px rgba(0,0,0,.4);box-shadow:0 1px 3px rgba(0,0,0,.4)}.leaflet-tooltip.leaflet-interactive{cursor:pointer;pointer-events:auto}.leaflet-tooltip-bottom:before,.leaflet-tooltip-left:before,.leaflet-tooltip-right:before,.leaflet-tooltip-top:before{position:absolute;pointer-events:none;border:6px solid transparent;background:0 0;content:""}.leaflet-tooltip-bottom{margin-top:6px}.leaflet-tooltip-top{margin-top:-6px}.leaflet-tooltip-bottom:before,.leaflet-tooltip-top:before{left:50%;margin-left:-6px}.leaflet-tooltip-top:before{bottom:0;margin-bottom:-12px;border-top-color:#fff}.leaflet-tooltip-bottom:before{top:0;margin-top:-12px;margin-left:-6px;border-bottom-color:#fff}.leaflet-tooltip-left{margin-left:-6px}.leaflet-tooltip-right{margin-left:6px}.leaflet-tooltip-left:before,.leaflet-tooltip-right:before{top:50%;margin-top:-6px}.leaflet-tooltip-left:before{right:0;margin-right:-12px;border-left-color:#fff}.leaflet-tooltip-right:before{left:0;margin-left:-12px;border-right-color:#fff}@media print{.leaflet-control{-webkit-print-color-adjust:exact;print-color-adjust:exact}}.leaflet-cluster-anim .leaflet-marker-icon,.leaflet-cluster-anim .leaflet-marker-shadow{-webkit-transition:opacity .3s ease-in,-webkit-transform .3s ease-out;-o-transition:transform .3s ease-out,opacity .3s ease-in;transition:transform .3s ease-out,opacity .3s ease-in,-webkit-transform .3s ease-out}.leaflet-cluster-spider-leg{-webkit-transition:stroke-dashoffset .3s ease-out,stroke-opacity .3s ease-in;-o-transition:stroke-dashoffset .3s ease-out,stroke-opacity .3s ease-in;transition:stroke-dashoffset .3s ease-out,stroke-opacity .3s ease-in}.leaflet-marker-photo{border:2px solid #fff;-webkit-box-shadow:3px 3px 10px #888;box-shadow:3px 3px 10px #888}.leaflet-marker-photo div{width:100%;height:100%;background-size:cover;background-position:center center;background-repeat:no-repeat}.leaflet-marker-photo b{position:absolute;top:-7px;right:-11px;color:#555;background-color:#fff;border-radius:8px;height:12px;min-width:12px;line-height:12px;text-align:center;padding:3px;-webkit-box-shadow:0 3px 14px rgba(0,0,0,.4);box-shadow:0 3px 14px rgba(0,0,0,.4)} \ No newline at end of file diff --git a/public/dist/frontend.js b/public/dist/frontend.js index c095346dee4..cc3b0f1c606 100644 --- a/public/dist/frontend.js +++ b/public/dist/frontend.js @@ -998,7 +998,7 @@ var _templateObject = _taggedTemplateLiteral(["$", "", ""], ["$", "", ""]), _templateObject20 = _taggedTemplateLiteral(["$", ""], ["$", ""]), _templateObject21 = _taggedTemplateLiteral(["
", "
"], ["
", "
"]), - _templateObject22 = _taggedTemplateLiteral(["
\n\t\t\t

\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t

\n\t\t\tSave\n\t\t\tDelete\n\t\t
\n\t\t"], ["
\n\t\t\t

\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t

\n\t\t\tSave\n\t\t\tDelete\n\t\t
\n\t\t"]), + _templateObject22 = _taggedTemplateLiteral(["
\n\t\t\t

\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t

\n\t\t\t", "\n\t\t\t", "\n\t\t
\n\t\t"], ["
\n\t\t\t

\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t

\n\t\t\t", "\n\t\t\t", "\n\t\t
\n\t\t"]), _templateObject23 = _taggedTemplateLiteral(["
\n\t\t\t

\n\t\t\t\n\t\t\t", "\n\t\t\t

\n\t\t\tDelete\n\t\t
\n\t\t"], ["
\n\t\t\t

\n\t\t\t\n\t\t\t", "\n\t\t\t

\n\t\t\tDelete\n\t\t
\n\t\t"]), _templateObject24 = _taggedTemplateLiteral(["\n\t\t\t ", "\n\t\t\t \"thumbnail\"\n\t\t\t
$", "
\n\t\t\t "], ["\n\t\t\t ", "\n\t\t\t \"thumbnail\"\n\t\t\t
$", "
\n\t\t\t "]), _templateObject25 = _taggedTemplateLiteral(["$", "", ""], ["$", "", ""]), @@ -3572,7 +3572,7 @@ build.tags = function (tags) { * @returns {string} */ build.user = function (user) { - return lychee.html(_templateObject22, user.id, user.id, user.username, lychee.locale["USERNAME"], lychee.locale["NEW_PASSWORD"], lychee.locale["ALLOW_UPLOADS"], lychee.locale["ALLOW_USER_SELF_EDIT"], user.id, user.id); + return lychee.html(_templateObject22, user.id, user.id, user.username, lychee.locale["USERNAME"], lychee.locale["NEW_PASSWORD"], lychee.locale["ALLOW_UPLOADS"], lychee.locale["ALLOW_USER_SELF_EDIT"], user.id, user.id !== lychee.user.id ? "" : "basicModal__button_OK_no_DEL", lychee.locale["SAVE"], user.id !== lychee.user.id ? "" + lychee.locale["DELETE"] + "" : ""); }; /** @@ -6177,14 +6177,6 @@ lychee.init = function () { leftMenu.build(); leftMenu.bind(); lychee.setMode("logged_in"); - - // Show dialog to create admin account, if no user is - // authenticated but admin rights are granted. - // TODO: Refactor the whole logic, i.e. the initial user should be created as part of the installation routine. - // In particular it is completely insane to build the UI as if the admin user was successfully authenticated. - // This might leak confidential photos to anybody if the DB is filled - // with photos and the admin password reset to `null`. - if (data.user === null && data.rights.user.can_edit) settings.createLogin(); } else { lychee.setMode("public"); } @@ -10404,98 +10396,6 @@ settings.open = function () { view.settings.init(); }; -settings.createLogin = function () { - /** - * @param {XMLHttpRequest} jqXHR the jQuery XMLHttpRequest object, see {@link https://api.jquery.com/jQuery.ajax/#jqXHR}. - * @param {Object} params the original JSON parameters of the request - * @param {?LycheeException} lycheeException the Lychee exception - * @returns {boolean} - */ - var errorHandler = function errorHandler(jqXHR, params, lycheeException) { - basicModal.show({ - body: "

", - readyCB: function readyCB(formElement, dialog) { - /** @type {NodeList} */ - var paragraphs = dialog.querySelectorAll("p"); - paragraphs.item(0).textContent = lychee.locale["ERROR_LOGIN"]; - paragraphs.item(1).textContent = lycheeException ? lycheeException.message : ""; - }, - buttons: { - action: { - title: lychee.locale["RETRY"], - fn: function fn() { - return settings.createLogin(); - } - } - } - }); - return true; - }; - - /** - * @param {User} updatedAdminUser - * @returns {void} - */ - var successHandler = function successHandler(updatedAdminUser) { - lychee.user = updatedAdminUser; - }; - - /** - * @param {ModalDialogResult} data - * @returns {void} - */ - var action = function action(data) { - if (!data.username.trim()) { - basicModal.focusError("username"); - return; - } - - if (!data.password.trim()) { - basicModal.focusError("password"); - return; - } - - if (data.password !== data.confirm) { - basicModal.focusError("confirm"); - return; - } - - basicModal.close(); - - var params = { - username: data.username, - password: data.password - }; - - api.post("Settings::setLogin", params, successHandler, null, errorHandler); - }; - - var createLoginDialogBody = "\n\t\t

\n\t\t
\n\t\t\t
\n\t\t\t\t\n\t\t\t
\n\t\t\t
\n\t\t\t\t\n\t\t\t
\n\t\t\t
\n\t\t\t\t\n\t\t\t
\n\t\t
"; - - /** - * @param {ModalDialogFormElements} formElements - * @param {HTMLDivElement} dialog - * @returns {void} - */ - var initDialog = function initDialog(formElements, dialog) { - dialog.querySelector("p").textContent = lychee.locale["LOGIN_TITLE"]; - formElements.username.placeholder = lychee.locale["LOGIN_USERNAME"]; - formElements.password.placeholder = lychee.locale["LOGIN_PASSWORD"]; - formElements.confirm.placeholder = lychee.locale["LOGIN_PASSWORD_CONFIRM"]; - }; - - basicModal.show({ - body: createLoginDialogBody, - readyCB: initDialog, - buttons: { - action: { - title: lychee.locale["LOGIN_CREATE"], - fn: action - } - } - }); -}; - /** * A dictionary of (name,value)-pairs of the form. * @@ -11983,7 +11883,7 @@ u2f.login = function () { login: "/api/WebAuthn::login", loginOptions: "/api/WebAuthn::login/options" }, {}, false).login({ - user_id: 0 // for now it is only available to Admin user via a secret key shortcut. + user_id: 1 // for now it is only available to Admin user via a secret key shortcut. }).then(function () { loadingBar.show("success", lychee.locale["U2F_AUTHENTIFICATION_SUCCESS"]); window.location.reload(); diff --git a/public/installer/assets/css/style.css b/public/installer/assets/css/style.css index ef8d4f2032f..5a8b4914ab7 100644 --- a/public/installer/assets/css/style.css +++ b/public/installer/assets/css/style.css @@ -3981,6 +3981,7 @@ input[type=text] { -webkit-transition: background-color .2s ease, -webkit-box-shadow .2s ease; transition: box-shadow .2s ease, background-color .2s ease, -webkit-box-shadow .2s ease; cursor: pointer; + font-size: 12px; } .button:hover { diff --git a/resources/views/install/env.blade.php b/resources/views/install/env.blade.php index 6a62865d8d4..a6e16f451ca 100644 --- a/resources/views/install/env.blade.php +++ b/resources/views/install/env.blade.php @@ -6,27 +6,26 @@ @section('content')
    -
  • Lychee does not create the database.
  • -
  • 1 - Manually create your database and then enter the sql details bellow.
  • -
  • 2 - If you are migrating from the v3, copy your pictures from
    -      version3/uploads/ to version4/public/uploads/.
  • -
  • 3 - Edit the form below to reflect your desired configuration.
  • +
  • Lychee does not create the database.
  • +
  • 1 - Manually create your database and then enter the sql details bellow.
  • +
  • 2 - If you are migrating from the v3, copy your pictures from
    +      version3/uploads/ to version4/public/uploads/.
  • +
  • 3 - Edit the form below to reflect your desired configuration.
For more details of how those values are used, look in the "config" folder.
-
- -
- -
-
+
+ +
+ +
+
- @if($exists == true) - - @endif + @if($exists == true) + + @endif @endsection \ No newline at end of file diff --git a/resources/views/install/migrate.blade.php b/resources/views/install/migrate.blade.php index a31bd80b311..a7e390a9abb 100644 --- a/resources/views/install/migrate.blade.php +++ b/resources/views/install/migrate.blade.php @@ -12,12 +12,17 @@ @if (empty($errors)) - We did not detect any errors. However if the migration failed, reopen this page. + We did not detect any errors. However if the migration failed, reopen this page. + @else @endif @endsection \ No newline at end of file diff --git a/resources/views/install/permissions.blade.php b/resources/views/install/permissions.blade.php index 4a3cbd4b77c..4a9be300399 100644 --- a/resources/views/install/permissions.blade.php +++ b/resources/views/install/permissions.blade.php @@ -1,53 +1,53 @@ @extends('install.view') @section('perm_step') - active + active @endsection @section('content') - @if ($windows) -

- You are using Windows, we cannot guarantee that the executable permission are properly set.
- All the executable checks have been overridden with true. -
-

- @endif + @if ($windows) +

+ You are using Windows, we cannot guarantee that the executable permission are properly set.
+ All the executable checks have been overridden with true. +
+

+ @endif -
    - @foreach ($permissions as $permission) -
  • - {{ $permission['folder'] }} - @if (count($permission['permission']) < 4) - @for ($i = 0;$i < 4 - count($permission['permission']); $i++) - -   - - @endfor - @endif - @foreach ($permission['permission'] as $perm) - - @if($perm[1] & 1) - - @else - - @endif - {{ $perm[0] }} - @endforeach - @endforeach -
+
    + @foreach ($permissions as $permission) +
  • + {{ $permission['folder'] }} + @if (count($permission['permission']) < 4) + @for ($i = 0;$i < 4 - count($permission['permission']); $i++) + +   + + @endfor + @endif + @foreach ($permission['permission'] as $perm) + + @if($perm[1] & 1) + + @else + + @endif + {{ $perm[0] }} + @endforeach + @endforeach +
- @if (empty($errors)) - - @else - - @endif + @if (empty($errors)) + + @else + + @endif @endsection \ No newline at end of file diff --git a/resources/views/install/requirements.blade.php b/resources/views/install/requirements.blade.php index 98704dd767b..79ba1b827cb 100644 --- a/resources/views/install/requirements.blade.php +++ b/resources/views/install/requirements.blade.php @@ -1,44 +1,44 @@ @extends('install.view') @section('req_step') - active + active @endsection @section('content') - @foreach ( $requirements as $type => $requirement) -
-
    -
  • - {{ ucfirst($type) }} - @if ($type == 'php') - (version {{ $phpSupportInfo['minimum'] }} required) - - {{ $phpSupportInfo['current'] }} - - -
  • - @endif + @foreach ( $requirements as $type => $requirement) +
    +
      +
    • + {{ ucfirst($type) }} + @if ($type == 'php') + (version {{ $phpSupportInfo['minimum'] }} required) + + {{ $phpSupportInfo['current'] }} + + +
    • + @endif - @foreach ($requirement as $extention => $enabled) -
    • - {{ $extention }} - -
    • - @endforeach -
    - @endforeach + @foreach ($requirement as $extention => $enabled) +
  • + {{ $extention }} + +
  • + @endforeach +
+ @endforeach - @if (empty($errors) && $phpSupportInfo['supported']) - - @else - - @endif + @if (empty($errors) && $phpSupportInfo['supported']) + + @else + + @endif @endsection \ No newline at end of file diff --git a/resources/views/install/setup-admin.blade.php b/resources/views/install/setup-admin.blade.php new file mode 100644 index 00000000000..7378a788e09 --- /dev/null +++ b/resources/views/install/setup-admin.blade.php @@ -0,0 +1,39 @@ +@extends('install.view') + +@section('admin_step') +active +@endsection + +@section('content') + @isset($errors) +
+
    + @foreach ($errors->all() as $error) +
  • {{ $error }}
  • + @endforeach +
+
+ @endisset + + @isset($error) +

+ {!! $error !!} +

+ @endisset + + Set up admin account.
+
+ {{-- Create a new user that will be an admin: --}} + + + + +
+ +
+
+@endsection \ No newline at end of file diff --git a/resources/views/install/setup-success.blade.php b/resources/views/install/setup-success.blade.php new file mode 100644 index 00000000000..bf8331f92d4 --- /dev/null +++ b/resources/views/install/setup-success.blade.php @@ -0,0 +1,14 @@ +@extends('install.view') + +@section('admin_step') +active +@endsection + +@section('content') + Admin account has been created. + +@endsection \ No newline at end of file diff --git a/resources/views/install/view.blade.php b/resources/views/install/view.blade.php index 054ca964fbb..a4a2acd7d29 100644 --- a/resources/views/install/view.blade.php +++ b/resources/views/install/view.blade.php @@ -1,60 +1,68 @@ - - - - Lychee Installer - - - - - - -
-
-
-

{{ $title }}

-
-
    -
  • -
  • - @if($step >= 4) - - @else - - @endif + + + + Lychee Installer + + + + + + +
    +
    +
    +

    {{ $title }}

    +
    +
      +
    • +
    • + @if($step >= 5) + + @else + + @endif
    • -
    • +
    • +
    • + @if($step >= 4) + + @else + + @endif +
    • +
    • - @if($step >= 3) - - @else - - @endif + @if($step >= 3) + + @else + + @endif
    • -
    • -
    • - @if($step >= 2) - - @else - - @endif +
    • +
    • + @if($step >= 2) + + @else + + @endif
    • -
    • -
    • - @if($step >= 1) - - @else - - @endif +
    • +
    • + @if($step >= 1) + + @else + + @endif
    • -
    • -
    • +
    • +
    • -
    • -
    -
    +
  • +
+
@if (!empty($errors))

diff --git a/routes/api.php b/routes/api.php index 741a7e7f4e6..702e83b10f3 100644 --- a/routes/api.php +++ b/routes/api.php @@ -127,13 +127,13 @@ */ Route::post('/WebAuthn::list', [WebAuthn\WebAuthnManageController::class, 'list']); Route::post('/WebAuthn::delete', [WebAuthn\WebAuthnManageController::class, 'delete']); -Route::post('/WebAuthn::register/options', [\App\Http\Controllers\WebAuthn\WebAuthnRegisterController::class, 'options']) +Route::post('/WebAuthn::register/options', [WebAuthn\WebAuthnRegisterController::class, 'options']) ->name('webauthn.register.options'); -Route::post('/WebAuthn::register', [\App\Http\Controllers\WebAuthn\WebAuthnRegisterController::class, 'register']) +Route::post('/WebAuthn::register', [WebAuthn\WebAuthnRegisterController::class, 'register']) ->name('webauthn.register'); -Route::post('/WebAuthn::login/options', [\App\Http\Controllers\WebAuthn\WebAuthnLoginController::class, 'options']) +Route::post('/WebAuthn::login/options', [WebAuthn\WebAuthnLoginController::class, 'options']) ->name('webauthn.login.options'); -Route::post('/WebAuthn::login', [\App\Http\Controllers\WebAuthn\WebAuthnLoginController::class, 'login']) +Route::post('/WebAuthn::login', [WebAuthn\WebAuthnLoginController::class, 'login']) ->name('webauthn.login'); /** diff --git a/routes/web-install.php b/routes/web-install.php index 780084f3ff2..9a86e2b19e4 100644 --- a/routes/web-install.php +++ b/routes/web-install.php @@ -28,3 +28,12 @@ Route::get('install/perm', [PermissionsController::class, 'view'])->name('install-perm'); Route::match(['get', 'post'], 'install/env', [EnvController::class, 'view'])->name('install-env'); Route::get('install/migrate', [MigrationController::class, 'view'])->name('install-migrate'); + +Route::post('install/admin', [SetUpAdminController::class, 'create']) + ->withoutMiddleware(['installation:incomplete']) + ->middleware(['admin_user:unset', 'installation:complete']) + ->name('install-admin'); +Route::get('install/admin', [SetUpAdminController::class, 'init']) + ->withoutMiddleware(['installation:incomplete']) + ->middleware(['admin_user:unset', 'installation:complete']) + ->name('install-admin'); diff --git a/tests/Boot.php b/tests/Boot.php new file mode 100644 index 00000000000..cf9403ae17e --- /dev/null +++ b/tests/Boot.php @@ -0,0 +1,44 @@ +createApplication(); + /** @var User|null $admin */ + $admin = User::find(1); + if ($admin === null) { + $admin = new User(); + $admin->incrementing = false; + $admin->id = 1; + $admin->may_upload = true; + $admin->may_edit_own_settings = true; + $admin->may_administrate = true; + $admin->username = 'admin'; + $admin->password = Hash::make('password'); + $admin->save(); + + if (Schema::connection(null)->getConnection()->getDriverName() === 'pgsql' && DB::table('users')->count() > 0) { + // when using PostgreSQL, the next ID value is kept when inserting without incrementing + // which results in errors because trying to insert a user with ID = 1. + // Thus, we need to reset the index to the greatest ID + 1 + /** @var User $lastUser */ + $lastUser = User::query()->orderByDesc('id')->first(); + DB::statement('ALTER SEQUENCE users_id_seq1 RESTART WITH ' . strval($lastUser->id + 1)); + } + } elseif (!$admin->may_administrate) { + $admin->may_administrate = true; + $admin->save(); + } + } +} \ No newline at end of file diff --git a/tests/Feature/AlbumTest.php b/tests/Feature/AlbumTest.php index b72d4fee0c4..c4a974371cd 100644 --- a/tests/Feature/AlbumTest.php +++ b/tests/Feature/AlbumTest.php @@ -90,7 +90,7 @@ public function testAddNotLogged(): void public function testAddReadLogged(): void { - Auth::loginUsingId(0); + Auth::loginUsingId(1); $this->clearCachedSmartAlbums(); $this->albums_tests->get(RecentAlbum::ID); @@ -164,7 +164,7 @@ public function testAddReadLogged(): void $this->albums_tests->unlock($albumID1, 'wrong-password', 403); $this->albums_tests->get($albumID1, 401); - Auth::loginUsingId(0); + Auth::loginUsingId(1); /* * Let's try to delete this album. @@ -228,7 +228,7 @@ public function testMultiDelete(): void // tests. static::assertDatabaseCount('base_albums', 0); - Auth::loginUsingId(0); + Auth::loginUsingId(1); // Create the test layout $albumID1 = $this->albums_tests->add(null, 'Album 1')->offsetGet('id'); @@ -372,7 +372,7 @@ public function testMerge(): void // tests. static::assertDatabaseCount('base_albums', 0); - Auth::loginUsingId(0); + Auth::loginUsingId(1); // Create the test layout $albumID1 = $this->albums_tests->add(null, 'Album 1')->offsetGet('id'); @@ -489,7 +489,7 @@ public function testMerge(): void public function testTrueNegative(): void { - Auth::loginUsingId(0); + Auth::loginUsingId(1); $this->albums_tests->set_description('-1', 'new description', 422); $this->albums_tests->set_description('abcdefghijklmnopqrstuvwx', 'new description', 404); @@ -506,7 +506,7 @@ public function testAlbumTree(): void $albumSortingOrder = Configs::getValueAsString(self::CONFIG_ALBUMS_SORTING_ORDER); try { - Auth::loginUsingId(0); + Auth::loginUsingId(1); Configs::set(self::CONFIG_ALBUMS_SORTING_COL, 'title'); Configs::set(self::CONFIG_ALBUMS_SORTING_ORDER, 'ASC'); @@ -559,7 +559,7 @@ public function testAlbumTree(): void public function testAddAlbumByNonAdminUserWithoutUploadPrivilege(): void { - Auth::loginUsingId(0); + Auth::loginUsingId(1); $userID = $this->users_tests->add('Test user', 'Test password', false)->offsetGet('id'); Auth::logout(); Session::flush(); @@ -569,7 +569,7 @@ public function testAddAlbumByNonAdminUserWithoutUploadPrivilege(): void public function testAddAlbumByNonAdminUserWithUploadPrivilege(): void { - Auth::loginUsingId(0); + Auth::loginUsingId(1); $userID = $this->users_tests->add('Test user', 'Test password')->offsetGet('id'); Auth::logout(); Session::flush(); @@ -579,7 +579,7 @@ public function testAddAlbumByNonAdminUserWithUploadPrivilege(): void public function testEditAlbumByNonOwner(): void { - Auth::loginUsingId(0); + Auth::loginUsingId(1); $userID1 = $this->users_tests->add('Test user 1', 'Test password 1')->offsetGet('id'); $userID2 = $this->users_tests->add('Test user 2', 'Test password 2')->offsetGet('id'); Auth::logout(); @@ -595,7 +595,7 @@ public function testEditAlbumByNonOwner(): void public function testEditAlbumByOwner(): void { - Auth::loginUsingId(0); + Auth::loginUsingId(1); $userID = $this->users_tests->add('Test user', 'Test password 1')->offsetGet('id'); Auth::logout(); Session::flush(); @@ -627,7 +627,7 @@ public function testEditAlbumByOwner(): void public function testDeleteMultipleAlbumsByAnonUser(): void { - Auth::loginUsingId(0); + Auth::loginUsingId(1); $albumID1 = $this->albums_tests->add(null, 'Test Album 1')->offsetGet('id'); $albumID2 = $this->albums_tests->add(null, 'Test Album 2')->offsetGet('id'); Auth::logout(); @@ -637,7 +637,7 @@ public function testDeleteMultipleAlbumsByAnonUser(): void public function testDeleteMultipleAlbumsByNonAdminUserWithoutUploadPrivilege(): void { - Auth::loginUsingId(0); + Auth::loginUsingId(1); $albumID1 = $this->albums_tests->add(null, 'Test Album 1')->offsetGet('id'); $albumID2 = $this->albums_tests->add(null, 'Test Album 2')->offsetGet('id'); $userID = $this->users_tests->add('Test user', 'Test password', false)->offsetGet('id'); @@ -650,7 +650,7 @@ public function testDeleteMultipleAlbumsByNonAdminUserWithoutUploadPrivilege(): public function testDeleteMultipleAlbumsByNonOwner(): void { - Auth::loginUsingId(0); + Auth::loginUsingId(1); $userID1 = $this->users_tests->add('Test user 1', 'Test password 1')->offsetGet('id'); $userID2 = $this->users_tests->add('Test user 2', 'Test password 2')->offsetGet('id'); Auth::logout(); @@ -667,7 +667,7 @@ public function testDeleteMultipleAlbumsByNonOwner(): void public function testDeleteMultipleAlbumsByOwner(): void { - Auth::loginUsingId(0); + Auth::loginUsingId(1); $userID = $this->users_tests->add('Test user 1', 'Test password 1')->offsetGet('id'); Auth::logout(); Session::flush(); @@ -695,7 +695,7 @@ public function testDeleteMultipleAlbumsByOwner(): void */ public function testDeleteNonEmptyTagAlbumWithPhotosFromRegularAlbum(): void { - Auth::loginUsingId(0); + Auth::loginUsingId(1); $regularAlbumID = $this->albums_tests->add(null, 'Regular Album for Delete Test')->offsetGet('id'); $photoID = $this->photos_tests->upload( self::createUploadedFile(self::SAMPLE_FILE_MONGOLIA_IMAGE), $regularAlbumID @@ -723,7 +723,7 @@ public function testDeleteNonEmptyTagAlbumWithPhotosFromRegularAlbum(): void */ public function testSetCoverByNonOwner() { - Auth::loginUsingId(0); + Auth::loginUsingId(1); $userID = $this->users_tests->add('Test user', 'Test password 1')->offsetGet('id'); $albumID = $this->albums_tests->add(null, 'Test Album')->offsetGet('id'); $photoID1 = $this->photos_tests->upload( @@ -748,7 +748,7 @@ public function testSetCoverByNonOwner() */ public function testSetCoverByOwner() { - Auth::loginUsingId(0); + Auth::loginUsingId(1); $albumID = $this->albums_tests->add(null, 'Test Album')->offsetGet('id'); $photoID1 = $this->photos_tests->upload( AbstractTestCase::createUploadedFile(AbstractTestCase::SAMPLE_FILE_NIGHT_IMAGE), @@ -783,7 +783,7 @@ public function testSetCoverByOwner() */ public function testDeleteUnsorted(): void { - Auth::loginUsingId(0); + Auth::loginUsingId(1); $id = $this->photos_tests->upload( AbstractTestCase::createUploadedFile(AbstractTestCase::SAMPLE_FILE_NIGHT_IMAGE) )->offsetGet('id'); @@ -803,7 +803,7 @@ public function testDeleteUnsorted(): void public function testHiddenSmartAlbums(): void { - Auth::loginUsingId(0); + Auth::loginUsingId(1); $this->clearCachedSmartAlbums(); Configs::set('SA_enabled', true); @@ -844,7 +844,7 @@ public function testHiddenSmartAlbums(): void public function testOnThisDayAlbumWhenThereIsPhotoTakenAtCurrentMonthAndDay(): void { - Auth::loginUsingId(0); + Auth::loginUsingId(1); $today = CarbonImmutable::today(); $photoID = $this->photos_tests->upload( AbstractTestCase::createUploadedFile(AbstractTestCase::SAMPLE_FILE_NIGHT_IMAGE) @@ -866,7 +866,7 @@ public function testOnThisDayAlbumWhenThereIsPhotoTakenAtCurrentMonthAndDay(): v public function testOnThisDayAlbumWhenThereIsPhotoCreatedAtCurrentMonthAndDay(): void { - Auth::loginUsingId(0); + Auth::loginUsingId(1); $today = CarbonImmutable::today(); $photoID = $this->photos_tests->upload( AbstractTestCase::createUploadedFile(AbstractTestCase::SAMPLE_FILE_NIGHT_IMAGE) @@ -887,7 +887,7 @@ public function testOnThisDayAlbumWhenThereIsPhotoCreatedAtCurrentMonthAndDay(): public function testOnThisDayAlbumWhenIsEmpty(): void { - Auth::loginUsingId(0); + Auth::loginUsingId(1); $today = CarbonImmutable::today(); $photoID = $this->photos_tests->upload( AbstractTestCase::createUploadedFile(AbstractTestCase::SAMPLE_FILE_NIGHT_IMAGE) diff --git a/tests/Feature/ApiTokenTest.php b/tests/Feature/ApiTokenTest.php index c583b7fe7e8..64e486b06b9 100644 --- a/tests/Feature/ApiTokenTest.php +++ b/tests/Feature/ApiTokenTest.php @@ -49,7 +49,7 @@ public function testAuthenticateWithTokenOnly(): void ]); $this->assertStatus($response, 200); $response->assertSee([ - 'id' => 0, + 'id' => 1, ], false); $this->assertAuthenticated(); @@ -72,7 +72,7 @@ public function testChangeOfTokensWithoutSession(): void ]); $this->assertStatus($response, 200); $response->assertSee([ - 'id' => 0, + 'id' => 1, ], false); // We need to call this to mimic the behaviour of real-world @@ -112,7 +112,7 @@ public function testForbiddenChangeOfTokensInSameSession(): void ]); $this->assertStatus($response, 200); $response->assertSee([ - 'id' => 0, + 'id' => 1, ], false); // We need to call this to mimic the behaviour of real-world @@ -263,7 +263,7 @@ public function testProvideDifferentTokenThanLogin(): void */ protected function resetAdminToken(): string { - Auth::loginUsingId(0); + Auth::loginUsingId(1); $token = $this->users_tests->reset_token()->offsetGet('token'); Auth::logout(); Session::flush(); @@ -279,7 +279,7 @@ protected function resetAdminToken(): string */ protected function createUserWithToken(string $userName, string $password): array { - Auth::loginUsingId(0); + Auth::loginUsingId(1); $id = $this->users_tests->add($userName, $password)->offsetGet('id'); Auth::logout(); Session::flush(); diff --git a/tests/Feature/Base/BasePhotoTest.php b/tests/Feature/Base/BasePhotoTest.php index 2ac90f0ddfb..66351baa2f7 100644 --- a/tests/Feature/Base/BasePhotoTest.php +++ b/tests/Feature/Base/BasePhotoTest.php @@ -38,7 +38,7 @@ public function setUp(): void $this->setUpRequiresExifTool(); $this->setUpRequiresFFMpeg(); $this->setUpRequiresEmptyPhotos(); - Auth::loginUsingId(0); + Auth::loginUsingId(1); } public function tearDown(): void diff --git a/tests/Feature/CommandsTest.php b/tests/Feature/CommandsTest.php index 207b039cf4e..cf5e8819fe2 100644 --- a/tests/Feature/CommandsTest.php +++ b/tests/Feature/CommandsTest.php @@ -30,10 +30,6 @@ public function testCommands(): void ->expectsOutput('No pictures requires EXIF updates.') ->assertExitCode(-1); - $this->artisan('lychee:reset_admin') - ->expectsOutput('Admin username and password reset.') - ->assertExitCode(0); - $this->artisan('lychee:logs') ->assertExitCode(0); diff --git a/tests/Feature/DiagnosticsTest.php b/tests/Feature/DiagnosticsTest.php index 46026250ce5..f69352596ea 100644 --- a/tests/Feature/DiagnosticsTest.php +++ b/tests/Feature/DiagnosticsTest.php @@ -29,7 +29,7 @@ public function testDiagnostics(): void $response = $this->get('/Diagnostics'); $this->assertOk($response); // code 200 something - Auth::loginUsingId(0); + Auth::loginUsingId(1); $response = $this->get('/Diagnostics'); $this->assertOk($response); // code 200 something diff --git a/tests/Feature/GeoDataTest.php b/tests/Feature/GeoDataTest.php index ecfd75f2365..5c69ff6f2ae 100644 --- a/tests/Feature/GeoDataTest.php +++ b/tests/Feature/GeoDataTest.php @@ -43,7 +43,7 @@ public function setUp(): void $this->albums_tests = new AlbumsUnitTest($this); $this->root_album_tests = new RootAlbumUnitTest($this); - Auth::loginUsingId(0); + Auth::loginUsingId(1); $this->setUpRequiresEmptyPhotos(); $this->setUpRequiresEmptyAlbums(); @@ -195,7 +195,7 @@ public function testThumbnailsInsideHiddenAlbum(): void $includeSubAlbums = Configs::getValueAsBool(self::CONFIG_MAP_INCLUDE_SUBALBUMS); try { - Auth::loginUsingId(0); + Auth::loginUsingId(1); Configs::set(self::CONFIG_PUBLIC_RECENT, true); Configs::set(self::CONFIG_PUBLIC_HIDDEN, false); Configs::set(self::CONFIG_PUBLIC_SEARCH, true); diff --git a/tests/Feature/InstallTest.php b/tests/Feature/InstallTest.php index c76c082f504..1cc8c433275 100644 --- a/tests/Feature/InstallTest.php +++ b/tests/Feature/InstallTest.php @@ -15,6 +15,9 @@ use App\Models\Configs; use App\Models\User; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Schema; use function Safe\file_get_contents; use Tests\AbstractTestCase; @@ -31,8 +34,6 @@ public function testInstall(): void /* * Get previous config */ - /** @var User $admin */ - $admin = User::query()->find(0); $prevAppKey = config('app.key'); config(['app.key' => null]); @@ -129,12 +130,33 @@ public function testInstall(): void $this->assertOk($response); $response->assertViewIs('install.migrate'); + $response = $this->get('install/admin'); + $this->assertOk($response); + $response->assertViewIs('install.setup-admin'); + + /** + * set up admin user migration. + */ + $response = $this->post('install/admin', ['username' => 'admin', 'password' => 'password', 'password_confirmation' => 'password']); + $this->assertOk($response); + $response->assertViewIs('install.setup-success'); + + // try to login with newly created admin + $this->assertTrue(Auth::attempt(['username' => 'admin', 'password' => 'password'])); + Auth::logout(); + /** * Re-Installation should be forbidden now. */ $response = $this->get('install/'); $this->assertForbidden($response); + /** + * Setting admin should be forbidden now. + */ + $response = $this->get('install/admin'); + $this->assertForbidden($response); + /** * We now should NOT be redirected. */ @@ -142,8 +164,32 @@ public function testInstall(): void $response = $this->get('/'); $this->assertOk($response); - $admin->save(); - $admin->id = 0; - $admin->save(); + /* + * make sure there's still an admin user with ID 1 + */ + /** @var User|null $admin */ + $admin = User::find(1); + if ($admin === null) { + $admin = new User(); + $admin->incrementing = false; + $admin->id = 1; + $admin->may_upload = true; + $admin->may_edit_own_settings = true; + $admin->may_administrate = true; + $admin->username = 'admin'; + $admin->password = Hash::make('password'); + $admin->save(); + if (Schema::connection(null)->getConnection()->getDriverName() === 'pgsql' && DB::table('users')->count() > 0) { + // when using PostgreSQL, the next ID value is kept when inserting without incrementing + // which results in errors because trying to insert a user with ID = 1. + // Thus, we need to reset the index to the greatest ID + 1 + /** @var User $lastUser */ + $lastUser = User::query()->orderByDesc('id')->first(); + DB::statement('ALTER SEQUENCE users_id_seq1 RESTART WITH ' . strval($lastUser->id + 1)); + } + } elseif (!$admin->may_administrate) { + $admin->may_administrate = true; + $admin->save(); + } } } diff --git a/tests/Feature/LegacyTest.php b/tests/Feature/LegacyTest.php index 0b1f8afdb10..923702d9cfa 100644 --- a/tests/Feature/LegacyTest.php +++ b/tests/Feature/LegacyTest.php @@ -57,7 +57,7 @@ public function tearDown(): void */ public function testLegacyConversion(): void { - Auth::loginUsingId(0); + Auth::loginUsingId(1); $albumID = $this->albums_tests->add(null, 'Test Album')->offsetGet('id'); $photoID = $this->photos_tests->upload( AbstractTestCase::createUploadedFile(AbstractTestCase::SAMPLE_FILE_NIGHT_IMAGE), diff --git a/tests/Feature/LogsTest.php b/tests/Feature/LogsTest.php index 04a316320aa..15424614f70 100644 --- a/tests/Feature/LogsTest.php +++ b/tests/Feature/LogsTest.php @@ -40,7 +40,7 @@ public function testLogs(): void $this->assertUnauthorized($response); // set user as admin - Auth::loginUsingId(0); + Auth::loginUsingId(1); Logs::notice(__METHOD__, __LINE__, 'test'); $response = $this->get('/Logs'); @@ -72,7 +72,7 @@ public function testClearLogs(): void $this->assertUnauthorized($response); // set user as admin - Auth::loginUsingId(0); + Auth::loginUsingId(1); $response = $this->postJson('/api/Logs::clearNoise'); $this->assertNoContent($response); @@ -90,7 +90,7 @@ public function testClearLogs(): void private function initAdmin(): void { - $this->admin = User::find(0); + $this->admin = User::find(1); $this->saveUsername = $this->admin->username; $this->savedPassword = $this->admin->password; $this->admin->username = 'temp'; @@ -100,7 +100,7 @@ private function initAdmin(): void private function revertAdmin(): void { - $this->admin = User::find(0); + $this->admin = User::find(1); $this->admin->username = $this->saveUsername; $this->admin->password = $this->savedPassword; $this->admin->save(); diff --git a/tests/Feature/NotificationTest.php b/tests/Feature/NotificationTest.php index 910723c9a5b..08a5bc3e19c 100644 --- a/tests/Feature/NotificationTest.php +++ b/tests/Feature/NotificationTest.php @@ -60,7 +60,7 @@ public function testNotificationSetting(): void $init_config_value = Configs::getValue('new_photos_notification'); try { - Auth::loginUsingId(0); + Auth::loginUsingId(1); $response = $this->postJson('/api/Settings::setNewPhotosNotification', [ 'new_photos_notification' => '1', @@ -79,7 +79,7 @@ public function testNotificationSetting(): void public function testSetupUserEmail(): void { // add email to admin - Auth::loginUsingId(0); + Auth::loginUsingId(1); $this->users_tests->update_email('test@test.com'); Auth::logout(); @@ -120,7 +120,7 @@ public function testSetAlbumForNotification(): void $init_config_value = Configs::getValue('new_photos_notification'); Configs::set('new_photos_notification', '1'); - Auth::loginUsingId(0); + Auth::loginUsingId(1); $albumID = $this->albums_tests->add(null, 'Album 1')->offsetGet('id'); $photoID = $this->photos_tests->upload( self::createUploadedFile(self::SAMPLE_FILE_MONGOLIA_IMAGE))->offsetGet('id'); diff --git a/tests/Feature/PhotosDownloadTest.php b/tests/Feature/PhotosDownloadTest.php index cf40356e898..34f787f116d 100644 --- a/tests/Feature/PhotosDownloadTest.php +++ b/tests/Feature/PhotosDownloadTest.php @@ -239,7 +239,7 @@ public function testAlbumDownloadWithMultibyteTitle(): void public function testDownloadOfInvisibleUnsortedPhotoByNonOwner(): void { - Auth::loginUsingId(0); + Auth::loginUsingId(1); $userID1 = $this->users_tests->add('Test user 1', 'Test password 1')->offsetGet('id'); $userID2 = $this->users_tests->add('Test user 2', 'Test password 2')->offsetGet('id'); Auth::logout(); @@ -259,7 +259,7 @@ public function testDownloadOfPhotoInSharedDownloadableAlbum(): void $areAlbumsDownloadable = Configs::getValueAsBool(self::CONFIG_DOWNLOADABLE); try { Configs::set(self::CONFIG_DOWNLOADABLE, true); - Auth::loginUsingId(0); + Auth::loginUsingId(1); $userID1 = $this->users_tests->add('Test user 1', 'Test password 1')->offsetGet('id'); $userID2 = $this->users_tests->add('Test user 2', 'Test password 2')->offsetGet('id'); Auth::logout(); @@ -285,7 +285,7 @@ public function testDownloadOfPhotoInSharedNonDownloadableAlbum(): void $areAlbumsDownloadable = Configs::getValueAsBool(self::CONFIG_DOWNLOADABLE); try { Configs::set(self::CONFIG_DOWNLOADABLE, false); - Auth::loginUsingId(0); + Auth::loginUsingId(1); $userID1 = $this->users_tests->add('Test user 1', 'Test password 1')->offsetGet('id'); $userID2 = $this->users_tests->add('Test user 2', 'Test password 2')->offsetGet('id'); Auth::logout(); diff --git a/tests/Feature/PhotosOperationsTest.php b/tests/Feature/PhotosOperationsTest.php index d3a075e2dc6..6abbe33e865 100644 --- a/tests/Feature/PhotosOperationsTest.php +++ b/tests/Feature/PhotosOperationsTest.php @@ -342,7 +342,7 @@ public function testThumbnailsInsideHiddenAlbum(): void $photoSortingOrder = Configs::getValueAsString(self::CONFIG_PHOTOS_SORTING_ORDER); try { - Auth::loginUsingId(0); + Auth::loginUsingId(1); Configs::set(self::CONFIG_PUBLIC_RECENT, true); Configs::set(self::CONFIG_PUBLIC_HIDDEN, false); Configs::set(self::CONFIG_PUBLIC_SEARCH, true); @@ -474,7 +474,7 @@ public function testThumbnailsInsideHiddenAlbum(): void public function testDeleteMultiplePhotosByAnonUser(): void { - Auth::loginUsingId(0); + Auth::loginUsingId(1); $albumID = $this->albums_tests->add(null, 'Test Album')->offsetGet('id'); $photoID1 = $this->photos_tests->upload( self::createUploadedFile(self::SAMPLE_FILE_MONGOLIA_IMAGE), $albumID @@ -490,7 +490,7 @@ public function testDeleteMultiplePhotosByAnonUser(): void public function testDeleteMultiplePhotosByNonOwner(): void { - Auth::loginUsingId(0); + Auth::loginUsingId(1); $userID1 = $this->users_tests->add('Test user 1', 'Test password 1')->offsetGet('id'); $userID2 = $this->users_tests->add('Test user 2', 'Test password 2')->offsetGet('id'); Auth::logout(); diff --git a/tests/Feature/RSSTest.php b/tests/Feature/RSSTest.php index b41acb8a2c4..f8559fb8043 100644 --- a/tests/Feature/RSSTest.php +++ b/tests/Feature/RSSTest.php @@ -77,7 +77,7 @@ public function testRSS1(): void $this->assertOk($response); // log as admin - Auth::loginUsingId(0); + Auth::loginUsingId(1); // create an album $albumID = $this->albums_tests->add(null, 'test_album')->offsetGet('id'); diff --git a/tests/Feature/SettingsTest.php b/tests/Feature/SettingsTest.php index 44aa65ecbe8..2f0a5caa01f 100644 --- a/tests/Feature/SettingsTest.php +++ b/tests/Feature/SettingsTest.php @@ -28,7 +28,7 @@ private function send( int $status = 204, ?string $assertSee = null): void { - Auth::loginUsingId(0); + Auth::loginUsingId(1); $response = $this->postJson('/api' . $url, $params); $this->assertStatus($response, $status); @@ -48,7 +48,7 @@ private function sendKV( ?string $assertSee = null): void { $oldVal = Configs::getValue($key); - Auth::loginUsingId(0); + Auth::loginUsingId(1); $response = $this->postJson('/api' . $url, [$key => $value]); $this->assertStatus($response, $status); @@ -194,7 +194,7 @@ public function testSetCSS(): void // Route::post('/Settings::saveAll', [Administration\SettingsController::class, 'saveAll']); public function testAllSettings(): void { - Auth::loginUsingId(0); + Auth::loginUsingId(1); $response = $this->postJson('/api/Settings::getAll', []); $this->assertStatus($response, 200); diff --git a/tests/Feature/SharingBasicTest.php b/tests/Feature/SharingBasicTest.php index a5f12910e7a..6cb453ae9ca 100644 --- a/tests/Feature/SharingBasicTest.php +++ b/tests/Feature/SharingBasicTest.php @@ -23,7 +23,7 @@ public function testEmptySharingList(): void $response->assertExactJson([ 'shared' => [], 'albums' => [], - 'users' => [], + 'users' => [['id' => 1, 'username' => 'admin']], ]); } @@ -42,7 +42,7 @@ public function testSharingListWithAlbums(): void ['id' => $albumID1, 'title' => self::ALBUM_TITLE_1], ['id' => $albumID2, 'title' => self::ALBUM_TITLE_1 . '/' . self::ALBUM_TITLE_2], ], - 'users' => [], + 'users' => [['id' => 1, 'username' => 'admin']], ]); } @@ -73,10 +73,12 @@ public function testSharingListWithSharedAlbums(): void ['id' => $albumID1, 'title' => self::ALBUM_TITLE_1], ['id' => $albumID2, 'title' => self::ALBUM_TITLE_2], ], - 'users' => [ - ['id' => $userID1, 'username' => self::USER_NAME_1], - ['id' => $userID2, 'username' => self::USER_NAME_2], - ], ]); + + /** @var array $users */ + $users = $response->offsetGet('users'); + self::assertContains(['id' => $userID1, 'username' => self::USER_NAME_1], $users); + self::assertContains(['id' => $userID2, 'username' => self::USER_NAME_2], $users); + self::assertContains(['id' => 1, 'username' => 'admin'], $users); } } \ No newline at end of file diff --git a/tests/Feature/Traits/RequiresEmptyUsers.php b/tests/Feature/Traits/RequiresEmptyUsers.php index 1c383ddb60b..adf7c18f3e0 100644 --- a/tests/Feature/Traits/RequiresEmptyUsers.php +++ b/tests/Feature/Traits/RequiresEmptyUsers.php @@ -24,7 +24,7 @@ protected function setUpRequiresEmptyUsers(): void static::assertEquals( 0, DB::table('users') - ->where('id', '!=', 0) + ->where('may_administrate', '=', false) ->count() ); } @@ -32,6 +32,6 @@ protected function setUpRequiresEmptyUsers(): void protected function tearDownRequiresEmptyUsers(): void { // Clean up remaining stuff from tests - DB::table('users')->where('id', '!=', 0)->delete(); + DB::table('users')->where('may_administrate', '=', false)->delete(); } } diff --git a/tests/Feature/UpdateTest.php b/tests/Feature/UpdateTest.php index 9acb4c163fe..a1d2b3aa9e0 100644 --- a/tests/Feature/UpdateTest.php +++ b/tests/Feature/UpdateTest.php @@ -41,7 +41,7 @@ public function testDoLogged(): void { $gitpull = Configs::getValue('allow_online_git_pull'); - Auth::loginUsingId(0); + Auth::loginUsingId(1); Configs::set('allow_online_git_pull', '0'); $response = $this->postJson('/api/Update::apply'); @@ -89,7 +89,7 @@ public function testApplyMigration(): void { // Prepare for test: we need to make sure there is an admin user registered. /** @var User $adminUser */ - $adminUser = User::findOrFail(0); + $adminUser = User::findOrFail(1); $login = $adminUser->username; $pw = $adminUser->password; $adminUser->username = Hash::make('test_login'); @@ -109,7 +109,7 @@ public function testApplyMigration(): void $this->assertOk($response); // check that Legacy did change the username - $adminUser = User::findOrFail(0); + $adminUser = User::findOrFail(1); $this->assertEquals('test_login', $adminUser->username); // clean up diff --git a/tests/Feature/UsersTest.php b/tests/Feature/UsersTest.php index f7fa13664f5..c79f0741016 100644 --- a/tests/Feature/UsersTest.php +++ b/tests/Feature/UsersTest.php @@ -12,7 +12,6 @@ namespace Tests\Feature; -use App\Legacy\AdminAuthentication; use App\Models\Configs; use App\Models\User; use App\SmartAlbums\OnThisDayAlbum; @@ -34,32 +33,6 @@ class UsersTest extends AbstractTestCase { use InteractWithSmartAlbums; - public function testSetAdminLoginIfAdminUnconfigured(): void - { - /** - * because there is no dependency injection in test cases. - */ - $sessions_test = new SessionUnitTest($this); - - if (!AdminAuthentication::isAdminNotRegistered()) { - static::markTestSkipped('Admin user is registered; test skipped.'); - } - - static::assertTrue(AdminAuthentication::loginAsAdminIfNotRegistered()); - $sessions_test->set_admin('lychee', 'password'); - $sessions_test->logout(); - static::assertFalse(AdminAuthentication::isAdminNotRegistered()); - - $sessions_test->set_admin('lychee', 'password', 403, 'Admin user is already registered'); - - $sessions_test->login('lychee', 'password'); - $sessions_test->logout(); - - $sessions_test->login('foo', 'bar', 401); - $sessions_test->login('lychee', 'bar', 401); - $sessions_test->login('foo', 'password', 401); - } - public function testUsers(): void { $sessions_test = new SessionUnitTest($this); @@ -111,11 +84,12 @@ public function testUsers(): void * 35. update email * 36. get email * 37. update email to blank - * 38. log out + * 38. try to delete yourself (not allowed) + * 39. log out */ // 1 - Auth::loginUsingId(0); + Auth::loginUsingId(1); // 2 $users_test->add( @@ -207,7 +181,7 @@ public function testUsers(): void $sessions_test->logout(); // 15 - Auth::loginUsingId(0); + Auth::loginUsingId(1); // 16 $users_test->save( @@ -277,7 +251,7 @@ public function testUsers(): void $sessions_test->logout(); // 30 - Auth::loginUsingId(0); + Auth::loginUsingId(1); // 31 $users_test->delete($id); @@ -293,7 +267,7 @@ public function testUsers(): void $sessions_test->logout(); // 33 - Auth::loginUsingId(0); + Auth::loginUsingId(1); $configs = Configs::get(); $store_new_photos_notification = $configs['new_photos_notification']; @@ -316,6 +290,9 @@ public function testUsers(): void $users_test->update_email(null); // 38 + $response = $users_test->delete(intval(Auth::id()), 403); + + // 39 $sessions_test->logout(); Configs::set('new_photos_notification', $store_new_photos_notification); } @@ -324,7 +301,7 @@ public function testResetToken(): void { $users_test = new UsersUnitTest($this); - Auth::loginUsingId(0); + Auth::loginUsingId(1); $oldToken = $users_test->reset_token()->offsetGet('token'); $newToken = $users_test->reset_token()->offsetGet('token'); @@ -337,7 +314,7 @@ public function testUnsetToken(): void { $users_test = new UsersUnitTest($this); - Auth::loginUsingId(0); + Auth::loginUsingId(1); $oldToken = $users_test->reset_token()->offsetGet('token'); self::assertNotNull($oldToken); @@ -373,13 +350,13 @@ public function regressionTestAdminAllMighty(): void ], ]); // update Admin user to non valid rights - $admin = User::findOrFail(0); + $admin = User::findOrFail(1); $admin->may_upload = false; $admin->may_edit_own_settings = true; $admin->save(); // Log as admin and check the rights - Auth::loginUsingId(0); + Auth::loginUsingId(1); $response = $sessions_test->init(); $response->assertJsonFragment([ 'rights' => [ @@ -395,7 +372,7 @@ public function regressionTestAdminAllMighty(): void $admin->save(); // Log as admin and verify behaviour - Auth::loginUsingId(0); + Auth::loginUsingId(1); $response = $sessions_test->init(); $response->assertJsonFragment([ 'rights' => [ @@ -415,10 +392,10 @@ public function testGetAuthenticatedUser(): void $users_test->get_user(204); - Auth::loginUsingId(0); + Auth::loginUsingId(1); $users_test->get_user(200, [ - 'id' => 0, + 'id' => 1, ]); } } diff --git a/tests/Feature/WebAuthTest.php b/tests/Feature/WebAuthTest.php index b161ac28d19..cfae6a7b626 100644 --- a/tests/Feature/WebAuthTest.php +++ b/tests/Feature/WebAuthTest.php @@ -30,7 +30,7 @@ class WebAuthTest extends AbstractTestCase */ public function testWebAuthTest(): void { - Auth::loginUsingId(0); + Auth::loginUsingId(1); $response = $this->postJson('/api/WebAuthn::register/options'); $this->assertOk($response); @@ -79,10 +79,10 @@ public function testWebAuthTest(): void 'public_key' => '', ]); /** @var User $user */ - $user = User::query()->find(0); + $user = User::query()->find(1); $user->webAuthnCredentials()->save($key); - Auth::loginUsingId(0); + Auth::loginUsingId(1); $response = $this->postJson('/api/WebAuthn::list'); $this->assertOk($response); // code 200 something