diff --git a/.gitattributes b/.gitattributes
index dfe0770..fcb21d3 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,2 +1,11 @@
-# Auto detect text files and perform LF normalization
-* text=auto
+* text=auto eol=lf
+
+*.blade.php diff=html
+*.css diff=css
+*.html diff=html
+*.md diff=markdown
+*.php diff=php
+
+/.github export-ignore
+CHANGELOG.md export-ignore
+.styleci.yml export-ignore
diff --git a/README.md b/README.md
index e431711..6851ea9 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,61 @@
-# RECAJE
+

+
+
+
+
+
+
+
+## About Laravel
+
+Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as:
+
+- [Simple, fast routing engine](https://laravel.com/docs/routing).
+- [Powerful dependency injection container](https://laravel.com/docs/container).
+- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage.
+- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent).
+- Database agnostic [schema migrations](https://laravel.com/docs/migrations).
+- [Robust background job processing](https://laravel.com/docs/queues).
+- [Real-time event broadcasting](https://laravel.com/docs/broadcasting).
+
+Laravel is accessible, powerful, and provides tools required for large, robust applications.
+
+## Learning Laravel
+
+Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework.
+
+You may also try the [Laravel Bootcamp](https://bootcamp.laravel.com), where you will be guided through building a modern Laravel application from scratch.
+
+If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains thousands of video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library.
+
+## Laravel Sponsors
+
+We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the [Laravel Partners program](https://partners.laravel.com).
+
+### Premium Partners
+
+- **[Vehikl](https://vehikl.com/)**
+- **[Tighten Co.](https://tighten.co)**
+- **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)**
+- **[64 Robots](https://64robots.com)**
+- **[Curotec](https://www.curotec.com/services/technologies/laravel/)**
+- **[DevSquad](https://devsquad.com/hire-laravel-developers)**
+- **[Redberry](https://redberry.international/laravel-development/)**
+- **[Active Logic](https://activelogic.com)**
+
+## Contributing
+
+Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).
+
+## Code of Conduct
+
+In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct).
+
+## Security Vulnerabilities
+
+If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed.
+
+## License
+
+The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).
diff --git a/app/Http/Controllers/Admin/CategoryController.php b/app/Http/Controllers/Admin/CategoryController.php
index 178bb93..d6121bc 100644
--- a/app/Http/Controllers/Admin/CategoryController.php
+++ b/app/Http/Controllers/Admin/CategoryController.php
@@ -1,89 +1,89 @@
-get();
- return view('admin.categories.index', compact('categories'));
- }
-
- /**
- * Show the form for creating a new resource.
- */
- public function create()
- {
- return view('admin.categories.create');
- }
-
- /**
- * Store a newly created resource in storage.
- */
- public function store(Request $request)
- {
- $request->validate([
- 'name' => 'required|string|max:255',
- ]);
-
- $category = Category::create([
- 'name' => $request->name,
- ]);
-
- return redirect()->route('admin.subcategories.create', ['category_id' => $category->id])
- ->with('success', 'Kategori berhasil ditambahkan! Silakan tambahkan sub-kategori.');
- }
-
- /**
- * Display the specified resource.
- */
- public function show(Category $category)
- {
- return view('admin.categories.show', compact('category'));
- }
-
- /**
- * Show the form for editing the specified resource.
- */
- public function edit(Category $category)
- {
- return view('admin.categories.edit', compact('category'));
- }
-
- /**
- * Update the specified resource in storage.
- */
- public function update(Request $request, Category $category)
- {
- $request->validate([
- 'name' => 'required|string|max:255',
- ]);
-
- $category->update([
- 'name' => $request->name,
- ]);
-
- return redirect()->route('admin.categories.index')
- ->with('success', 'Kategori berhasil diperbarui!');
- }
-
- /**
- * Remove the specified resource from storage.
- */
- public function destroy(Category $category)
- {
- $category->delete();
-
- return redirect()->route('admin.categories.index')
- ->with('success', 'Kategori berhasil dihapus!');
- }
+get();
+ return view('admin.categories.index', compact('categories'));
+ }
+
+ /**
+ * Show the form for creating a new resource.
+ */
+ public function create()
+ {
+ return view('admin.categories.create');
+ }
+
+ /**
+ * Store a newly created resource in storage.
+ */
+ public function store(Request $request)
+ {
+ $request->validate([
+ 'name' => 'required|string|max:255',
+ ]);
+
+ $category = Category::create([
+ 'name' => $request->name,
+ ]);
+
+ return redirect()->route('admin.subcategories.create', ['category_id' => $category->id])
+ ->with('success', 'Kategori berhasil ditambahkan! Silakan tambahkan sub-kategori.');
+ }
+
+ /**
+ * Display the specified resource.
+ */
+ public function show(Category $category)
+ {
+ return view('admin.categories.show', compact('category'));
+ }
+
+ /**
+ * Show the form for editing the specified resource.
+ */
+ public function edit(Category $category)
+ {
+ return view('admin.categories.edit', compact('category'));
+ }
+
+ /**
+ * Update the specified resource in storage.
+ */
+ public function update(Request $request, Category $category)
+ {
+ $request->validate([
+ 'name' => 'required|string|max:255',
+ ]);
+
+ $category->update([
+ 'name' => $request->name,
+ ]);
+
+ return redirect()->route('admin.categories.index')
+ ->with('success', 'Kategori berhasil diperbarui!');
+ }
+
+ /**
+ * Remove the specified resource from storage.
+ */
+ public function destroy(Category $category)
+ {
+ $category->delete();
+
+ return redirect()->route('admin.categories.index')
+ ->with('success', 'Kategori berhasil dihapus!');
+ }
}
\ No newline at end of file
diff --git a/app/Http/Controllers/Admin/SubcategoryController.php b/app/Http/Controllers/Admin/SubcategoryController.php
index 460b3b7..b9ec140 100644
--- a/app/Http/Controllers/Admin/SubcategoryController.php
+++ b/app/Http/Controllers/Admin/SubcategoryController.php
@@ -1,123 +1,123 @@
-all());
-
- $request->validate([
- 'category_id' => 'required|exists:categories,id',
- 'subcategories' => 'required|array|min:1',
- 'subcategories.*.name' => 'required|string|max:255',
- 'subcategories.*.value' => 'required|integer|min:1|max:5'
- ]);
-
- \Illuminate\Support\Facades\Log::info('Validation passed, processing subcategories');
-
- try {
- DB::beginTransaction();
-
- foreach ($request->subcategories as $subcategory) {
- \Illuminate\Support\Facades\Log::info('Creating subcategory:', $subcategory);
-
- Subcategory::create([
- 'category_id' => $request->category_id,
- 'name' => $subcategory['name'],
- 'value' => $subcategory['value']
- ]);
- }
-
- DB::commit();
- \Illuminate\Support\Facades\Log::info('All subcategories created successfully');
-
- return redirect()->route('admin.subcategories.index')
- ->with('success', 'Sub-kategori berhasil ditambahkan!');
-
- } catch (\Exception $e) {
- DB::rollBack();
- \Illuminate\Support\Facades\Log::error('Error creating subcategories: ' . $e->getMessage());
-
- return back()->with('error', 'Terjadi kesalahan saat menambahkan sub-kategori: ' . $e->getMessage());
- }
- }
-
- /**
- * Display the specified resource.
- */
- public function show(Subcategory $subcategory)
- {
- return view('admin.subcategories.show', compact('subcategory'));
- }
-
- /**
- * Show the form for editing the specified resource.
- */
- public function edit(Subcategory $subcategory)
- {
- $categories = Category::all();
- return view('admin.subcategories.edit', compact('subcategory', 'categories'));
- }
-
- /**
- * Update the specified resource in storage.
- */
- public function update(Request $request, Subcategory $subcategory)
- {
- $request->validate([
- 'category_id' => 'required|exists:categories,id',
- 'name' => 'required|string|max:255',
- ]);
-
- $subcategory->update([
- 'category_id' => $request->category_id,
- 'name' => $request->name,
- ]);
-
- return redirect()->route('admin.subcategories.index')
- ->with('success', 'Sub-kategori berhasil diperbarui!');
- }
-
- /**
- * Remove the specified resource from storage.
- */
- public function destroy(Subcategory $subcategory)
- {
- $subcategory->delete();
-
- return redirect()->route('admin.subcategories.index')
- ->with('success', 'Sub-kategori berhasil dihapus!');
- }
+all());
+
+ $request->validate([
+ 'category_id' => 'required|exists:categories,id',
+ 'subcategories' => 'required|array|min:1',
+ 'subcategories.*.name' => 'required|string|max:255',
+ 'subcategories.*.value' => 'required|integer|min:1|max:5'
+ ]);
+
+ \Illuminate\Support\Facades\Log::info('Validation passed, processing subcategories');
+
+ try {
+ DB::beginTransaction();
+
+ foreach ($request->subcategories as $subcategory) {
+ \Illuminate\Support\Facades\Log::info('Creating subcategory:', $subcategory);
+
+ Subcategory::create([
+ 'category_id' => $request->category_id,
+ 'name' => $subcategory['name'],
+ 'value' => $subcategory['value']
+ ]);
+ }
+
+ DB::commit();
+ \Illuminate\Support\Facades\Log::info('All subcategories created successfully');
+
+ return redirect()->route('admin.subcategories.index')
+ ->with('success', 'Sub-kategori berhasil ditambahkan!');
+
+ } catch (\Exception $e) {
+ DB::rollBack();
+ \Illuminate\Support\Facades\Log::error('Error creating subcategories: ' . $e->getMessage());
+
+ return back()->with('error', 'Terjadi kesalahan saat menambahkan sub-kategori: ' . $e->getMessage());
+ }
+ }
+
+ /**
+ * Display the specified resource.
+ */
+ public function show(Subcategory $subcategory)
+ {
+ return view('admin.subcategories.show', compact('subcategory'));
+ }
+
+ /**
+ * Show the form for editing the specified resource.
+ */
+ public function edit(Subcategory $subcategory)
+ {
+ $categories = Category::all();
+ return view('admin.subcategories.edit', compact('subcategory', 'categories'));
+ }
+
+ /**
+ * Update the specified resource in storage.
+ */
+ public function update(Request $request, Subcategory $subcategory)
+ {
+ $request->validate([
+ 'category_id' => 'required|exists:categories,id',
+ 'name' => 'required|string|max:255',
+ ]);
+
+ $subcategory->update([
+ 'category_id' => $request->category_id,
+ 'name' => $request->name,
+ ]);
+
+ return redirect()->route('admin.subcategories.index')
+ ->with('success', 'Sub-kategori berhasil diperbarui!');
+ }
+
+ /**
+ * Remove the specified resource from storage.
+ */
+ public function destroy(Subcategory $subcategory)
+ {
+ $subcategory->delete();
+
+ return redirect()->route('admin.subcategories.index')
+ ->with('success', 'Sub-kategori berhasil dihapus!');
+ }
}
\ No newline at end of file
diff --git a/app/Http/Controllers/Admin/UserController.php b/app/Http/Controllers/Admin/UserController.php
index 0b92434..a3bb99b 100644
--- a/app/Http/Controllers/Admin/UserController.php
+++ b/app/Http/Controllers/Admin/UserController.php
@@ -1,182 +1,182 @@
-role !== 'admin') {
- return redirect('/')->with('error', 'Anda tidak memiliki akses ke halaman tersebut.');
- }
-
- $users = User::orderBy('id', 'asc')->paginate(10);
-
- // Format nama setiap user
- $users->getCollection()->transform(function ($user) {
- $user->name = ucwords(strtolower($user->name));
- return $user;
- });
-
- return view('admin.users.index', compact('users'));
- }
-
- /**
- * Menampilkan detail pengguna.
- *
- * @param \App\Models\User $user
- * @return \Illuminate\View\View
- */
- public function show(User $user)
- {
- // Cek apakah pengguna adalah admin
- if (Auth::user()->role !== 'admin') {
- return redirect('/')->with('error', 'Anda tidak memiliki akses ke halaman tersebut.');
- }
-
- $user->name = ucwords(strtolower($user->name));
- return view('admin.users.show', compact('user'));
- }
-
- /**
- * Menghapus pengguna.
- *
- * @param \App\Models\User $user
- * @return \Illuminate\Http\RedirectResponse
- */
- public function destroy(User $user)
- {
- // Cek apakah pengguna adalah admin
- if (Auth::user()->role !== 'admin') {
- return redirect('/')->with('error', 'Anda tidak memiliki akses ke halaman tersebut.');
- }
-
- // Tidak bisa menghapus diri sendiri
- if (Auth::id() === $user->id) {
- return back()->with('error', 'Anda tidak dapat menghapus akun Anda sendiri.');
- }
-
- $user->delete();
-
- return redirect()->route('admin.users.index')
- ->with('success', 'Pengguna berhasil dihapus!');
- }
-
- public function create()
- {
- // Cek apakah pengguna adalah admin
- if (Auth::user()->role !== 'admin') {
- return redirect('/')->with('error', 'Anda tidak memiliki akses ke halaman tersebut.');
- }
-
- return view('admin.users.create');
- }
-
- /**
- * Menyimpan user baru.
- *
- * @param \Illuminate\Http\Request $request
- * @return \Illuminate\Http\RedirectResponse
- */
- public function store(Request $request)
- {
- // Cek apakah pengguna adalah admin
- if (Auth::user()->role !== 'admin') {
- return redirect('/')->with('error', 'Anda tidak memiliki akses ke halaman tersebut.');
- }
-
- $request->validate([
- 'name' => 'required|string|max:255',
- 'email' => 'required|string|email|max:255|unique:users',
- 'password' => 'required|string|min:8|confirmed',
- 'role' => 'required|in:admin,user',
- ]);
-
- $user = User::create([
- 'name' => ucwords(strtolower($request->name)),
- 'email' => $request->email,
- 'password' => Hash::make($request->password),
- 'role' => $request->role,
- ]);
-
- return redirect()->route('admin.users.index')
- ->with('success', 'Pengguna berhasil ditambahkan!');
- }
-
- /**
- * Menampilkan form edit user.
- *
- * @param \App\Models\User $user
- * @return \Illuminate\View\View
- */
- public function edit(User $user)
- {
- // Cek apakah pengguna adalah admin
- if (Auth::user()->role !== 'admin') {
- return redirect('/')->with('error', 'Anda tidak memiliki akses ke halaman tersebut.');
- }
-
- $user->name = ucwords(strtolower($user->name));
- return view('admin.users.edit', compact('user'));
- }
-
- /**
- * Memperbarui data user.
- *
- * @param \Illuminate\Http\Request $request
- * @param \App\Models\User $user
- * @return \Illuminate\Http\RedirectResponse
- */
- public function update(Request $request, User $user)
- {
- // Cek apakah pengguna adalah admin
- if (Auth::user()->role !== 'admin') {
- return redirect('/')->with('error', 'Anda tidak memiliki akses ke halaman tersebut.');
- }
-
- $request->validate([
- 'name' => 'required|string|max:255',
- 'email' => 'required|string|email|max:255|unique:users,email,' . $user->id,
- 'password' => 'nullable|string|min:8|confirmed',
- 'role' => 'required|in:admin,user',
- ]);
-
- $data = [
- 'name' => ucwords(strtolower($request->name)),
- 'email' => $request->email,
- 'role' => $request->role,
- ];
-
- // Update password hanya jika diisi
- if ($request->filled('password')) {
- $data['password'] = Hash::make($request->password);
- }
-
- $user->update($data);
-
- return redirect()->route('admin.users.index')
- ->with('success', 'Pengguna berhasil diperbarui!');
- }
+role !== 'admin') {
+ return redirect('/')->with('error', 'Anda tidak memiliki akses ke halaman tersebut.');
+ }
+
+ $users = User::orderBy('id', 'asc')->paginate(10);
+
+ // Format nama setiap user
+ $users->getCollection()->transform(function ($user) {
+ $user->name = ucwords(strtolower($user->name));
+ return $user;
+ });
+
+ return view('admin.users.index', compact('users'));
+ }
+
+ /**
+ * Menampilkan detail pengguna.
+ *
+ * @param \App\Models\User $user
+ * @return \Illuminate\View\View
+ */
+ public function show(User $user)
+ {
+ // Cek apakah pengguna adalah admin
+ if (Auth::user()->role !== 'admin') {
+ return redirect('/')->with('error', 'Anda tidak memiliki akses ke halaman tersebut.');
+ }
+
+ $user->name = ucwords(strtolower($user->name));
+ return view('admin.users.show', compact('user'));
+ }
+
+ /**
+ * Menghapus pengguna.
+ *
+ * @param \App\Models\User $user
+ * @return \Illuminate\Http\RedirectResponse
+ */
+ public function destroy(User $user)
+ {
+ // Cek apakah pengguna adalah admin
+ if (Auth::user()->role !== 'admin') {
+ return redirect('/')->with('error', 'Anda tidak memiliki akses ke halaman tersebut.');
+ }
+
+ // Tidak bisa menghapus diri sendiri
+ if (Auth::id() === $user->id) {
+ return back()->with('error', 'Anda tidak dapat menghapus akun Anda sendiri.');
+ }
+
+ $user->delete();
+
+ return redirect()->route('admin.users.index')
+ ->with('success', 'Pengguna berhasil dihapus!');
+ }
+
+ public function create()
+ {
+ // Cek apakah pengguna adalah admin
+ if (Auth::user()->role !== 'admin') {
+ return redirect('/')->with('error', 'Anda tidak memiliki akses ke halaman tersebut.');
+ }
+
+ return view('admin.users.create');
+ }
+
+ /**
+ * Menyimpan user baru.
+ *
+ * @param \Illuminate\Http\Request $request
+ * @return \Illuminate\Http\RedirectResponse
+ */
+ public function store(Request $request)
+ {
+ // Cek apakah pengguna adalah admin
+ if (Auth::user()->role !== 'admin') {
+ return redirect('/')->with('error', 'Anda tidak memiliki akses ke halaman tersebut.');
+ }
+
+ $request->validate([
+ 'name' => 'required|string|max:255',
+ 'email' => 'required|string|email|max:255|unique:users',
+ 'password' => 'required|string|min:8|confirmed',
+ 'role' => 'required|in:admin,user',
+ ]);
+
+ $user = User::create([
+ 'name' => ucwords(strtolower($request->name)),
+ 'email' => $request->email,
+ 'password' => Hash::make($request->password),
+ 'role' => $request->role,
+ ]);
+
+ return redirect()->route('admin.users.index')
+ ->with('success', 'Pengguna berhasil ditambahkan!');
+ }
+
+ /**
+ * Menampilkan form edit user.
+ *
+ * @param \App\Models\User $user
+ * @return \Illuminate\View\View
+ */
+ public function edit(User $user)
+ {
+ // Cek apakah pengguna adalah admin
+ if (Auth::user()->role !== 'admin') {
+ return redirect('/')->with('error', 'Anda tidak memiliki akses ke halaman tersebut.');
+ }
+
+ $user->name = ucwords(strtolower($user->name));
+ return view('admin.users.edit', compact('user'));
+ }
+
+ /**
+ * Memperbarui data user.
+ *
+ * @param \Illuminate\Http\Request $request
+ * @param \App\Models\User $user
+ * @return \Illuminate\Http\RedirectResponse
+ */
+ public function update(Request $request, User $user)
+ {
+ // Cek apakah pengguna adalah admin
+ if (Auth::user()->role !== 'admin') {
+ return redirect('/')->with('error', 'Anda tidak memiliki akses ke halaman tersebut.');
+ }
+
+ $request->validate([
+ 'name' => 'required|string|max:255',
+ 'email' => 'required|string|email|max:255|unique:users,email,' . $user->id,
+ 'password' => 'nullable|string|min:8|confirmed',
+ 'role' => 'required|in:admin,user',
+ ]);
+
+ $data = [
+ 'name' => ucwords(strtolower($request->name)),
+ 'email' => $request->email,
+ 'role' => $request->role,
+ ];
+
+ // Update password hanya jika diisi
+ if ($request->filled('password')) {
+ $data['password'] = Hash::make($request->password);
+ }
+
+ $user->update($data);
+
+ return redirect()->route('admin.users.index')
+ ->with('success', 'Pengguna berhasil diperbarui!');
+ }
}
\ No newline at end of file
diff --git a/app/Http/Controllers/Auth/ForgotPasswordController.php b/app/Http/Controllers/Auth/ForgotPasswordController.php
index e161bdd..0f03131 100644
--- a/app/Http/Controllers/Auth/ForgotPasswordController.php
+++ b/app/Http/Controllers/Auth/ForgotPasswordController.php
@@ -1,41 +1,41 @@
-validate([
- 'email' => ['required', 'email'],
- ]);
-
- $status = Password::sendResetLink(
- $request->only('email')
- );
-
- return $status === Password::RESET_LINK_SENT
- ? back()->with('status', 'Link reset password telah dikirim ke email Anda!')
- : back()->withErrors(['email' => __($status)]);
- }
+validate([
+ 'email' => ['required', 'email'],
+ ]);
+
+ $status = Password::sendResetLink(
+ $request->only('email')
+ );
+
+ return $status === Password::RESET_LINK_SENT
+ ? back()->with('status', 'Link reset password telah dikirim ke email Anda!')
+ : back()->withErrors(['email' => __($status)]);
+ }
}
\ No newline at end of file
diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php
index 9e1247b..c08b3e7 100644
--- a/app/Http/Controllers/Auth/LoginController.php
+++ b/app/Http/Controllers/Auth/LoginController.php
@@ -1,151 +1,151 @@
-role === 'admin') {
- return route('admin.dashboard');
- }
-
- return '/';
- }
-
- /**
- * Memproses request login
- *
- * @param \Illuminate\Http\Request $request
- * @return \Illuminate\Http\RedirectResponse
- */
- public function login(Request $request)
- {
- try {
- $credentials = $request->validate([
- 'email' => ['required', 'email'],
- 'password' => ['required'],
- ], [
- 'email.required' => 'Email harus diisi',
- 'email.email' => 'Format email tidak valid',
- 'password.required' => 'Password harus diisi',
- ]);
-
- if (Auth::attempt($credentials, $request->boolean('remember'))) {
- $request->session()->regenerate();
-
- if (Auth::user()->role === 'admin') {
- return redirect()->route('admin.dashboard');
- }
-
- return redirect()->intended('/');
- }
-
- return back()
- ->withInput($request->only('email'))
- ->withErrors([
- 'email' => 'Email atau password yang diberikan tidak cocok dengan data kami.',
- ]);
-
- } catch (\Exception $e) {
- return back()
- ->withInput($request->only('email'))
- ->withErrors([
- 'error' => 'Terjadi kesalahan saat mencoba login. Silakan coba lagi.',
- ]);
- }
- }
-
- /**
- * Logout pengguna
- *
- * @param \Illuminate\Http\Request $request
- * @return \Illuminate\Http\RedirectResponse
- */
- public function logout(Request $request)
- {
- Auth::logout();
-
- $request->session()->invalidate();
- $request->session()->regenerateToken();
-
- return redirect('/');
- }
-
- /**
- * Redirect ke halaman login Google.
- *
- * @return \Illuminate\Http\Response
- */
- public function redirectToGoogle()
- {
- return Socialite::driver('google')->redirect();
- }
-
- /**
- * Handle callback dari Google.
- *
- * @return \Illuminate\Http\Response
- */
- public function handleGoogleCallback()
- {
- try {
- $googleUser = Socialite::driver('google')->user();
-
- // Cari user berdasarkan google id, jika tidak ada cari berdasarkan email
- $user = User::where('google_id', $googleUser->id)->first();
-
- if (!$user) {
- // Cek apakah email sudah terdaftar
- $user = User::where('email', $googleUser->email)->first();
-
- if (!$user) {
- // Jika user belum terdaftar, buat user baru
- $user = User::create([
- 'name' => $googleUser->name,
- 'email' => $googleUser->email,
- 'google_id' => $googleUser->id,
- 'password' => Hash::make(rand(1,10000)),
- 'email_verified_at' => now(), // Email otomatis terverifikasi
- ]);
- } else {
- // Update google_id untuk user yang sudah ada
- $user->update([
- 'google_id' => $googleUser->id,
- 'email_verified_at' => now(), // Email otomatis terverifikasi
- ]);
- }
- }
-
- // Login user
- Auth::login($user);
-
- return redirect('/');
-
- } catch (\Exception $e) {
- return redirect('/login')->with('error', 'Terjadi kesalahan saat login dengan Google: ' . $e->getMessage());
- }
- }
+role === 'admin') {
+ return route('admin.dashboard');
+ }
+
+ return '/';
+ }
+
+ /**
+ * Memproses request login
+ *
+ * @param \Illuminate\Http\Request $request
+ * @return \Illuminate\Http\RedirectResponse
+ */
+ public function login(Request $request)
+ {
+ try {
+ $credentials = $request->validate([
+ 'email' => ['required', 'email'],
+ 'password' => ['required'],
+ ], [
+ 'email.required' => 'Email harus diisi',
+ 'email.email' => 'Format email tidak valid',
+ 'password.required' => 'Password harus diisi',
+ ]);
+
+ if (Auth::attempt($credentials, $request->boolean('remember'))) {
+ $request->session()->regenerate();
+
+ if (Auth::user()->role === 'admin') {
+ return redirect()->route('admin.dashboard');
+ }
+
+ return redirect()->intended('/');
+ }
+
+ return back()
+ ->withInput($request->only('email'))
+ ->withErrors([
+ 'email' => 'Email atau password yang diberikan tidak cocok dengan data kami.',
+ ]);
+
+ } catch (\Exception $e) {
+ return back()
+ ->withInput($request->only('email'))
+ ->withErrors([
+ 'error' => 'Terjadi kesalahan saat mencoba login. Silakan coba lagi.',
+ ]);
+ }
+ }
+
+ /**
+ * Logout pengguna
+ *
+ * @param \Illuminate\Http\Request $request
+ * @return \Illuminate\Http\RedirectResponse
+ */
+ public function logout(Request $request)
+ {
+ Auth::logout();
+
+ $request->session()->invalidate();
+ $request->session()->regenerateToken();
+
+ return redirect('/');
+ }
+
+ /**
+ * Redirect ke halaman login Google.
+ *
+ * @return \Illuminate\Http\Response
+ */
+ public function redirectToGoogle()
+ {
+ return Socialite::driver('google')->redirect();
+ }
+
+ /**
+ * Handle callback dari Google.
+ *
+ * @return \Illuminate\Http\Response
+ */
+ public function handleGoogleCallback()
+ {
+ try {
+ $googleUser = Socialite::driver('google')->user();
+
+ // Cari user berdasarkan google id, jika tidak ada cari berdasarkan email
+ $user = User::where('google_id', $googleUser->id)->first();
+
+ if (!$user) {
+ // Cek apakah email sudah terdaftar
+ $user = User::where('email', $googleUser->email)->first();
+
+ if (!$user) {
+ // Jika user belum terdaftar, buat user baru
+ $user = User::create([
+ 'name' => $googleUser->name,
+ 'email' => $googleUser->email,
+ 'google_id' => $googleUser->id,
+ 'password' => Hash::make(rand(1,10000)),
+ 'email_verified_at' => now(), // Email otomatis terverifikasi
+ ]);
+ } else {
+ // Update google_id untuk user yang sudah ada
+ $user->update([
+ 'google_id' => $googleUser->id,
+ 'email_verified_at' => now(), // Email otomatis terverifikasi
+ ]);
+ }
+ }
+
+ // Login user
+ Auth::login($user);
+
+ return redirect('/');
+
+ } catch (\Exception $e) {
+ return redirect('/login')->with('error', 'Terjadi kesalahan saat login dengan Google: ' . $e->getMessage());
+ }
+ }
}
\ No newline at end of file
diff --git a/app/Http/Controllers/Auth/RegisterController.php b/app/Http/Controllers/Auth/RegisterController.php
index f575e42..82907fe 100644
--- a/app/Http/Controllers/Auth/RegisterController.php
+++ b/app/Http/Controllers/Auth/RegisterController.php
@@ -1,51 +1,51 @@
-validate([
- 'name' => ['required', 'string', 'max:255'],
- 'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
- 'password' => ['required', 'string', 'min:8', 'confirmed'],
- 'terms' => ['required'],
- ]);
-
- $user = User::create([
- 'name' => $request->name,
- 'email' => $request->email,
- 'password' => Hash::make($request->password),
- ]);
-
- event(new Registered($user));
-
- Auth::login($user);
-
- return redirect()->route('verification.notice');
- }
+validate([
+ 'name' => ['required', 'string', 'max:255'],
+ 'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
+ 'password' => ['required', 'string', 'min:8', 'confirmed'],
+ 'terms' => ['required'],
+ ]);
+
+ $user = User::create([
+ 'name' => $request->name,
+ 'email' => $request->email,
+ 'password' => Hash::make($request->password),
+ ]);
+
+ event(new Registered($user));
+
+ Auth::login($user);
+
+ return redirect()->route('verification.notice');
+ }
}
\ No newline at end of file
diff --git a/app/Http/Controllers/Auth/ResetPasswordController.php b/app/Http/Controllers/Auth/ResetPasswordController.php
index 7a1fbf9..70f33e0 100644
--- a/app/Http/Controllers/Auth/ResetPasswordController.php
+++ b/app/Http/Controllers/Auth/ResetPasswordController.php
@@ -1,58 +1,58 @@
-with(
- ['token' => $token, 'email' => $request->email]
- );
- }
-
- /**
- * Memproses reset password
- *
- * @param \Illuminate\Http\Request $request
- * @return \Illuminate\Http\RedirectResponse
- */
- public function reset(Request $request)
- {
- $request->validate([
- 'token' => ['required'],
- 'email' => ['required', 'email'],
- 'password' => ['required', 'confirmed', 'min:8'],
- ]);
-
- $status = Password::reset(
- $request->only('email', 'password', 'password_confirmation', 'token'),
- function ($user, $password) {
- $user->forceFill([
- 'password' => Hash::make($password)
- ])->setRememberToken(Str::random(60));
-
- $user->save();
-
- event(new PasswordReset($user));
- }
- );
-
- return $status === Password::PASSWORD_RESET
- ? redirect()->route('login')->with('status', 'Password Anda telah direset!')
- : back()->withErrors(['email' => [__($status)]]);
- }
+with(
+ ['token' => $token, 'email' => $request->email]
+ );
+ }
+
+ /**
+ * Memproses reset password
+ *
+ * @param \Illuminate\Http\Request $request
+ * @return \Illuminate\Http\RedirectResponse
+ */
+ public function reset(Request $request)
+ {
+ $request->validate([
+ 'token' => ['required'],
+ 'email' => ['required', 'email'],
+ 'password' => ['required', 'confirmed', 'min:8'],
+ ]);
+
+ $status = Password::reset(
+ $request->only('email', 'password', 'password_confirmation', 'token'),
+ function ($user, $password) {
+ $user->forceFill([
+ 'password' => Hash::make($password)
+ ])->setRememberToken(Str::random(60));
+
+ $user->save();
+
+ event(new PasswordReset($user));
+ }
+ );
+
+ return $status === Password::PASSWORD_RESET
+ ? redirect()->route('login')->with('status', 'Password Anda telah direset!')
+ : back()->withErrors(['email' => [__($status)]]);
+ }
}
\ No newline at end of file
diff --git a/app/Http/Controllers/CafeController.php b/app/Http/Controllers/CafeController.php
index 388cbb8..b76249c 100644
--- a/app/Http/Controllers/CafeController.php
+++ b/app/Http/Controllers/CafeController.php
@@ -1,91 +1,91 @@
-has('weight')) {
- $currentStep = 3; // Hasil
- } elseif ($request->has('criteria')) {
- $currentStep = 2; // Kriteria
- }
-
- return view('cafe.search-fixed', compact('currentStep'));
- }
-
- /**
- * Tampilkan halaman rekomendasi cafe
- */
- public function recommend()
- {
- return view('cafe.recommend');
- }
-
- /**
- * Tampilkan semua cafe dengan fitur fuzzy search toleran terhadap typo
- */
- public function index(Request $request)
- {
- $query = Cafe::with(['ratings.subcategory', 'ratings.category']);
-
- // Filter berdasarkan pencarian jika ada
- if ($request->has('search') && !empty($request->search)) {
- $searchTerm = $request->search;
-
- // Memecah kata kunci pencarian menjadi beberapa kata
- $keywords = explode(' ', $searchTerm);
-
- $query->where(function($q) use ($searchTerm, $keywords) {
- // Exact match dengan prioritas tertinggi
- $q->where('nama', 'LIKE', "%{$searchTerm}%");
-
- // Pencarian fuzzy untuk masing-masing kata kunci
- foreach ($keywords as $keyword) {
- if (strlen($keyword) >= 3) {
- // Membuat variasi kata kunci untuk toleransi typo
- $q->orWhere('nama', 'LIKE', "%{$keyword}%");
-
- // Tambahkan wildcard di tengah untuk toleransi kesalahan ketik 1 karakter
- for ($i = 0; $i < strlen($keyword) - 1; $i++) {
- $fuzzyWord = substr($keyword, 0, $i) . '_' . substr($keyword, $i + 1);
- $q->orWhere('nama', 'LIKE', "%{$fuzzyWord}%");
- }
- }
- }
-
- // Mencoba juga pencarian dengan operator SOUNDS LIKE jika tersedia
- try {
- $q->orWhereRaw("SOUNDEX(nama) = SOUNDEX(?)", [$searchTerm]);
- } catch (\Exception $e) {
- // SOUNDEX mungkin tidak tersedia, tidak perlu error handling
- }
- });
- }
-
- $cafes = $query->latest()->paginate(12)->withQueryString();
-
- return view('cafe.index', compact('cafes'));
- }
-
- /**
- * Tampilkan detail cafe
- */
- public function show($id)
- {
- $cafe = Cafe::with(['ratings.subcategory', 'ratings.category'])->findOrFail($id);
- return view('cafe.show', compact('cafe'));
- }
+has('weight')) {
+ $currentStep = 3; // Hasil
+ } elseif ($request->has('criteria')) {
+ $currentStep = 2; // Kriteria
+ }
+
+ return view('cafe.search-fixed', compact('currentStep'));
+ }
+
+ /**
+ * Tampilkan halaman rekomendasi cafe
+ */
+ public function recommend()
+ {
+ return view('cafe.recommend');
+ }
+
+ /**
+ * Tampilkan semua cafe dengan fitur fuzzy search toleran terhadap typo
+ */
+ public function index(Request $request)
+ {
+ $query = Cafe::with(['ratings.subcategory', 'ratings.category']);
+
+ // Filter berdasarkan pencarian jika ada
+ if ($request->has('search') && !empty($request->search)) {
+ $searchTerm = $request->search;
+
+ // Memecah kata kunci pencarian menjadi beberapa kata
+ $keywords = explode(' ', $searchTerm);
+
+ $query->where(function($q) use ($searchTerm, $keywords) {
+ // Exact match dengan prioritas tertinggi
+ $q->where('nama', 'LIKE', "%{$searchTerm}%");
+
+ // Pencarian fuzzy untuk masing-masing kata kunci
+ foreach ($keywords as $keyword) {
+ if (strlen($keyword) >= 3) {
+ // Membuat variasi kata kunci untuk toleransi typo
+ $q->orWhere('nama', 'LIKE', "%{$keyword}%");
+
+ // Tambahkan wildcard di tengah untuk toleransi kesalahan ketik 1 karakter
+ for ($i = 0; $i < strlen($keyword) - 1; $i++) {
+ $fuzzyWord = substr($keyword, 0, $i) . '_' . substr($keyword, $i + 1);
+ $q->orWhere('nama', 'LIKE', "%{$fuzzyWord}%");
+ }
+ }
+ }
+
+ // Mencoba juga pencarian dengan operator SOUNDS LIKE jika tersedia
+ try {
+ $q->orWhereRaw("SOUNDEX(nama) = SOUNDEX(?)", [$searchTerm]);
+ } catch (\Exception $e) {
+ // SOUNDEX mungkin tidak tersedia, tidak perlu error handling
+ }
+ });
+ }
+
+ $cafes = $query->latest()->paginate(12)->withQueryString();
+
+ return view('cafe.index', compact('cafes'));
+ }
+
+ /**
+ * Tampilkan detail cafe
+ */
+ public function show($id)
+ {
+ $cafe = Cafe::with(['ratings.subcategory', 'ratings.category'])->findOrFail($id);
+ return view('cafe.show', compact('cafe'));
+ }
}
\ No newline at end of file
diff --git a/app/Http/Controllers/ContactController.php b/app/Http/Controllers/ContactController.php
index d89737b..cf04559 100644
--- a/app/Http/Controllers/ContactController.php
+++ b/app/Http/Controllers/ContactController.php
@@ -1,71 +1,71 @@
-validate([
- 'name' => 'required|string|max:255',
- 'email' => 'required|string|email|max:255',
- 'message' => 'required|string',
- ]);
-
- ContactMessage::create($validated);
-
- return redirect()->back()->with('success', 'Pesan Anda telah terkirim! Terima kasih telah menghubungi kami.');
- }
-
- /**
- * Display a listing of contact messages for admin.
- *
- * @return \Illuminate\Http\Response
- */
- public function index()
- {
- $messages = ContactMessage::orderBy('created_at', 'desc')->paginate(10);
-
- return view('admin.contact-messages.index', compact('messages'));
- }
-
- /**
- * Display the specified contact message.
- *
- * @param \App\Models\ContactMessage $contactMessage
- * @return \Illuminate\Http\Response
- */
- public function show(ContactMessage $contactMessage)
- {
- // Mark as read
- if (!$contactMessage->is_read) {
- $contactMessage->is_read = true;
- $contactMessage->save();
- }
-
- return view('admin.contact-messages.show', compact('contactMessage'));
- }
-
- /**
- * Remove the specified contact message from storage.
- *
- * @param \App\Models\ContactMessage $contactMessage
- * @return \Illuminate\Http\Response
- */
- public function destroy(ContactMessage $contactMessage)
- {
- $contactMessage->delete();
-
- return redirect()->route('admin.contact-messages.index')
- ->with('success', 'Pesan kontak berhasil dihapus.');
- }
+validate([
+ 'name' => 'required|string|max:255',
+ 'email' => 'required|string|email|max:255',
+ 'message' => 'required|string',
+ ]);
+
+ ContactMessage::create($validated);
+
+ return redirect()->back()->with('success', 'Pesan Anda telah terkirim! Terima kasih telah menghubungi kami.');
+ }
+
+ /**
+ * Display a listing of contact messages for admin.
+ *
+ * @return \Illuminate\Http\Response
+ */
+ public function index()
+ {
+ $messages = ContactMessage::orderBy('created_at', 'desc')->paginate(10);
+
+ return view('admin.contact-messages.index', compact('messages'));
+ }
+
+ /**
+ * Display the specified contact message.
+ *
+ * @param \App\Models\ContactMessage $contactMessage
+ * @return \Illuminate\Http\Response
+ */
+ public function show(ContactMessage $contactMessage)
+ {
+ // Mark as read
+ if (!$contactMessage->is_read) {
+ $contactMessage->is_read = true;
+ $contactMessage->save();
+ }
+
+ return view('admin.contact-messages.show', compact('contactMessage'));
+ }
+
+ /**
+ * Remove the specified contact message from storage.
+ *
+ * @param \App\Models\ContactMessage $contactMessage
+ * @return \Illuminate\Http\Response
+ */
+ public function destroy(ContactMessage $contactMessage)
+ {
+ $contactMessage->delete();
+
+ return redirect()->route('admin.contact-messages.index')
+ ->with('success', 'Pesan kontak berhasil dihapus.');
+ }
}
\ No newline at end of file
diff --git a/app/Http/Controllers/FavoriteController.php b/app/Http/Controllers/FavoriteController.php
index 84f496f..76cb201 100644
--- a/app/Http/Controllers/FavoriteController.php
+++ b/app/Http/Controllers/FavoriteController.php
@@ -1,111 +1,111 @@
-json(['success' => false, 'message' => 'ID cafe tidak valid'], 400);
- }
-
- // Cek apakah cafe dengan ID tersebut ada
- $cafe = Cafe::find($cafeId);
- if (!$cafe) {
- Log::error('Cafe not found with ID: ' . $cafeId);
- return response()->json(['success' => false, 'message' => 'Cafe tidak ditemukan'], 404);
- }
-
- $user = Auth::user();
- if (!$user) {
- Log::error('User not authenticated for favorite toggle');
- return response()->json(['success' => false, 'message' => 'Pengguna tidak terautentikasi'], 401);
- }
-
- Log::info('Toggling favorite for user ID: ' . $user->id . ' and cafe ID: ' . $cafeId);
-
- // Pemeriksaan apakah sudah favorit secara langsung menggunakan query
- $existingFavorite = DB::table('favorites')
- ->where('user_id', $user->id)
- ->where('cafe_id', $cafeId)
- ->first();
-
- if ($existingFavorite) {
- // Hapus favorit
- DB::table('favorites')
- ->where('user_id', $user->id)
- ->where('cafe_id', $cafeId)
- ->delete();
-
- $isFavorited = false;
- Log::info('Favorite removed for user ID: ' . $user->id . ' and cafe ID: ' . $cafeId);
- } else {
- // Tambahkan favorit
- DB::table('favorites')->insert([
- 'user_id' => $user->id,
- 'cafe_id' => $cafeId,
- 'created_at' => now(),
- 'updated_at' => now()
- ]);
-
- $isFavorited = true;
- Log::info('Favorite added for user ID: ' . $user->id . ' and cafe ID: ' . $cafeId);
- }
-
- if ($request->wantsJson() || $request->ajax()) {
- return response()->json([
- 'success' => true,
- 'isFavorited' => $isFavorited
- ]);
- }
-
- return back()->with('success', $isFavorited ? 'Cafe ditambahkan ke favorit' : 'Cafe dihapus dari favorit');
- } catch (Exception $e) {
- Log::error('Error toggling favorite: ' . $e->getMessage() . "\n" . $e->getTraceAsString());
-
- if ($request->wantsJson() || $request->ajax()) {
- return response()->json([
- 'success' => false,
- 'message' => 'Terjadi kesalahan: ' . $e->getMessage()
- ], 500);
- }
-
- return back()->with('error', 'Terjadi kesalahan saat mengubah status favorit.');
- }
- }
-
- /**
- * Menampilkan daftar cafe favorit user
- *
- * @return \Illuminate\Http\Response
- */
- public function index()
- {
- $user = Auth::user();
- $favorites = $user->favorites()->with('cafe')->get();
-
- return view('favorites.index', [
- 'favorites' => $favorites
- ]);
- }
+json(['success' => false, 'message' => 'ID cafe tidak valid'], 400);
+ }
+
+ // Cek apakah cafe dengan ID tersebut ada
+ $cafe = Cafe::find($cafeId);
+ if (!$cafe) {
+ Log::error('Cafe not found with ID: ' . $cafeId);
+ return response()->json(['success' => false, 'message' => 'Cafe tidak ditemukan'], 404);
+ }
+
+ $user = Auth::user();
+ if (!$user) {
+ Log::error('User not authenticated for favorite toggle');
+ return response()->json(['success' => false, 'message' => 'Pengguna tidak terautentikasi'], 401);
+ }
+
+ Log::info('Toggling favorite for user ID: ' . $user->id . ' and cafe ID: ' . $cafeId);
+
+ // Pemeriksaan apakah sudah favorit secara langsung menggunakan query
+ $existingFavorite = DB::table('favorites')
+ ->where('user_id', $user->id)
+ ->where('cafe_id', $cafeId)
+ ->first();
+
+ if ($existingFavorite) {
+ // Hapus favorit
+ DB::table('favorites')
+ ->where('user_id', $user->id)
+ ->where('cafe_id', $cafeId)
+ ->delete();
+
+ $isFavorited = false;
+ Log::info('Favorite removed for user ID: ' . $user->id . ' and cafe ID: ' . $cafeId);
+ } else {
+ // Tambahkan favorit
+ DB::table('favorites')->insert([
+ 'user_id' => $user->id,
+ 'cafe_id' => $cafeId,
+ 'created_at' => now(),
+ 'updated_at' => now()
+ ]);
+
+ $isFavorited = true;
+ Log::info('Favorite added for user ID: ' . $user->id . ' and cafe ID: ' . $cafeId);
+ }
+
+ if ($request->wantsJson() || $request->ajax()) {
+ return response()->json([
+ 'success' => true,
+ 'isFavorited' => $isFavorited
+ ]);
+ }
+
+ return back()->with('success', $isFavorited ? 'Cafe ditambahkan ke favorit' : 'Cafe dihapus dari favorit');
+ } catch (Exception $e) {
+ Log::error('Error toggling favorite: ' . $e->getMessage() . "\n" . $e->getTraceAsString());
+
+ if ($request->wantsJson() || $request->ajax()) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Terjadi kesalahan: ' . $e->getMessage()
+ ], 500);
+ }
+
+ return back()->with('error', 'Terjadi kesalahan saat mengubah status favorit.');
+ }
+ }
+
+ /**
+ * Menampilkan daftar cafe favorit user
+ *
+ * @return \Illuminate\Http\Response
+ */
+ public function index()
+ {
+ $user = Auth::user();
+ $favorites = $user->favorites()->with('cafe')->get();
+
+ return view('favorites.index', [
+ 'favorites' => $favorites
+ ]);
+ }
}
\ No newline at end of file
diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php
index 2878da7..3995d45 100644
--- a/app/Http/Controllers/ProfileController.php
+++ b/app/Http/Controllers/ProfileController.php
@@ -1,54 +1,54 @@
-validate([
- 'name' => ['required', 'string', 'max:255'],
- 'email' => ['required', 'string', 'email', 'max:255', 'unique:users,email,'.$user->id],
- ]);
-
- $user->update($validated);
-
- return redirect()->route('profile.index')->with('success', 'Profil berhasil diperbarui!');
- }
-
- /**
- * Update password pengguna
- */
- public function updatePassword(Request $request)
- {
- $validated = $request->validate([
- 'current_password' => ['required', 'current_password'],
- 'password' => ['required', 'confirmed', Password::defaults()],
- ]);
-
- $user = Auth::user();
- $user->password = Hash::make($validated['password']);
- $user->save();
-
- return redirect()->route('profile.index')->with('success', 'Password berhasil diperbarui!');
- }
+validate([
+ 'name' => ['required', 'string', 'max:255'],
+ 'email' => ['required', 'string', 'email', 'max:255', 'unique:users,email,'.$user->id],
+ ]);
+
+ $user->update($validated);
+
+ return redirect()->route('profile.index')->with('success', 'Profil berhasil diperbarui!');
+ }
+
+ /**
+ * Update password pengguna
+ */
+ public function updatePassword(Request $request)
+ {
+ $validated = $request->validate([
+ 'current_password' => ['required', 'current_password'],
+ 'password' => ['required', 'confirmed', Password::defaults()],
+ ]);
+
+ $user = Auth::user();
+ $user->password = Hash::make($validated['password']);
+ $user->save();
+
+ return redirect()->route('profile.index')->with('success', 'Password berhasil diperbarui!');
+ }
}
\ No newline at end of file
diff --git a/app/Http/Controllers/SmartSearchController.php b/app/Http/Controllers/SmartSearchController.php
index ea56efa..92eab52 100644
--- a/app/Http/Controllers/SmartSearchController.php
+++ b/app/Http/Controllers/SmartSearchController.php
@@ -1,199 +1,199 @@
-input('weight', []);
- $criterias = $request->input('criteria', []);
- $lokasi = $request->input('lokasi');
-
- // Get cafes with ratings
- $cafes = Cafe::with(['ratings.subcategory', 'ratings.category'])->get();
-
- // Filter berdasarkan lokasi jika ada
- if ($lokasi) {
- $cafes = $cafes->filter(function($cafe) use ($lokasi) {
- return stripos($cafe->lokasi, $lokasi) !== false;
- });
- }
-
- // Hitung total semua bobot untuk normalisasi
- $totalWeight = array_sum($weights);
- if ($totalWeight == 0) {
- // Jika tidak ada bobot, buat bobot merata
- $categories = Category::all();
- foreach ($categories as $category) {
- $weights[$category->id] = 100 / $categories->count();
- }
- $totalWeight = 100;
- }
-
- // Hitung skor SMART untuk setiap cafe
- $cafeScores = [];
-
- foreach ($cafes as $cafe) {
- $score = 0;
- $debugInfo = [];
-
- $ratings = \Illuminate\Support\Facades\DB::table('cafe_ratings')
- ->join('subcategories', 'cafe_ratings.subcategory_id', '=', 'subcategories.id')
- ->join('categories', 'cafe_ratings.category_id', '=', 'categories.id')
- ->where('cafe_ratings.cafe_id', $cafe->id)
- ->select(
- 'cafe_ratings.category_id',
- 'categories.name as category_name',
- 'cafe_ratings.subcategory_id',
- 'subcategories.name as subcategory_name',
- 'subcategories.value'
- )
- ->get();
-
- foreach ($ratings as $rating) {
- // Jika ada bobot untuk kategori ini
- if (isset($weights[$rating->category_id])) {
- $weight = floatval($weights[$rating->category_id]);
- $normalizedWeight = $totalWeight > 0 ? $weight / $totalWeight : 0;
-
- // Pastikan nilai valid
- $value = max(1, intval($rating->value));
-
- // Bonus nilai jika kriteria dipilih
- if (isset($criterias[$rating->category_id]) &&
- $criterias[$rating->category_id] == $rating->subcategory_id) {
- $value = 5; // Nilai maksimal
- }
-
- // Hitung utilitas - menerapkan konsep yang sama dengan view
- $utilityValue = ($value - 1) / 4; // Normalisasi ke rentang 0-1 (jika nilai antara 1-5)
- $ratingScore = $normalizedWeight * $utilityValue;
- $score += $ratingScore;
-
- // Simpan untuk debug
- $debugInfo[$rating->category_name] = [
- 'bobot' => $weight,
- 'bobot_normal' => $normalizedWeight,
- 'nilai' => $value,
- 'nilai_asli' => $rating->value,
- 'subcategory' => $rating->subcategory_name,
- 'skor_kriteria' => $ratingScore
- ];
- }
- }
-
- $cafeScores[$cafe->id] = [
- 'score' => $score,
- 'debug' => $debugInfo
- ];
- }
-
- // Urutkan cafe berdasarkan skor tertinggi
- uasort($cafeScores, function($a, $b) {
- return $b['score'] <=> $a['score'];
- });
-
- // Ambil id cafe yang sudah diurutkan
- $rankedCafeIds = array_keys($cafeScores);
-
- // Urutkan koleksi cafe sesuai skor
- $rankedCafes = $cafes->sortBy(function($cafe) use ($rankedCafeIds) {
- return array_search($cafe->id, $rankedCafeIds);
- });
-
- // Ambil hanya 10 cafe teratas
- $rankedCafes = $rankedCafes->take(10);
-
- // Persiapkan data hasil untuk disimpan
- $resultsData = [];
- foreach ($rankedCafes as $cafe) {
- if (isset($cafeScores[$cafe->id])) {
- $resultsData[$cafe->id] = [
- 'name' => $cafe->nama,
- 'score' => $cafeScores[$cafe->id]['score'],
- 'details' => $cafeScores[$cafe->id]['debug']
- ];
- }
- }
-
- // Simpan hasil pencarian ke database jika user sudah login
- if (Auth::check()) {
- SearchHistory::create([
- 'user_id' => Auth::id(),
- 'weights' => $weights,
- 'criteria' => $criterias,
- 'location' => $lokasi,
- 'results' => $resultsData
- ]);
- }
-
- return view('cafe.search-fixed', compact('currentStep', 'rankedCafes', 'cafeScores'));
- }
-
- /**
- * Display search history for the authenticated user.
- *
- * @return \Illuminate\View\View
- */
- public function history()
- {
- $histories = SearchHistory::where('user_id', Auth::id())
- ->orderBy('created_at', 'desc')
- ->paginate(10);
-
- return view('cafe.search-history', compact('histories'));
- }
-
- /**
- * Display search history detail.
- *
- * @param int $id
- * @return \Illuminate\View\View
- */
- public function detail($id)
- {
- $history = SearchHistory::where('user_id', Auth::id())
- ->findOrFail($id);
-
- // Retrieve cafes from the stored results
- $cafeIds = array_keys($history->results ?? []);
- $cafes = Cafe::whereIn('id', $cafeIds)->get();
-
- // Map cafes to include their scores from history
- $rankedCafes = $cafes->map(function($cafe) use ($history) {
- $cafe->smart_score = $history->results[$cafe->id]['score'] ?? 0;
- $cafe->smart_details = $history->results[$cafe->id]['details'] ?? [];
- return $cafe;
- })->sortByDesc('smart_score');
-
- $weights = $history->weights;
-
- return view('cafe.search-detail', compact('history', 'rankedCafes', 'weights'));
- }
+input('weight', []);
+ $criterias = $request->input('criteria', []);
+ $lokasi = $request->input('lokasi');
+
+ // Get cafes with ratings
+ $cafes = Cafe::with(['ratings.subcategory', 'ratings.category'])->get();
+
+ // Filter berdasarkan lokasi jika ada
+ if ($lokasi) {
+ $cafes = $cafes->filter(function($cafe) use ($lokasi) {
+ return stripos($cafe->lokasi, $lokasi) !== false;
+ });
+ }
+
+ // Hitung total semua bobot untuk normalisasi
+ $totalWeight = array_sum($weights);
+ if ($totalWeight == 0) {
+ // Jika tidak ada bobot, buat bobot merata
+ $categories = Category::all();
+ foreach ($categories as $category) {
+ $weights[$category->id] = 100 / $categories->count();
+ }
+ $totalWeight = 100;
+ }
+
+ // Hitung skor SMART untuk setiap cafe
+ $cafeScores = [];
+
+ foreach ($cafes as $cafe) {
+ $score = 0;
+ $debugInfo = [];
+
+ $ratings = \Illuminate\Support\Facades\DB::table('cafe_ratings')
+ ->join('subcategories', 'cafe_ratings.subcategory_id', '=', 'subcategories.id')
+ ->join('categories', 'cafe_ratings.category_id', '=', 'categories.id')
+ ->where('cafe_ratings.cafe_id', $cafe->id)
+ ->select(
+ 'cafe_ratings.category_id',
+ 'categories.name as category_name',
+ 'cafe_ratings.subcategory_id',
+ 'subcategories.name as subcategory_name',
+ 'subcategories.value'
+ )
+ ->get();
+
+ foreach ($ratings as $rating) {
+ // Jika ada bobot untuk kategori ini
+ if (isset($weights[$rating->category_id])) {
+ $weight = floatval($weights[$rating->category_id]);
+ $normalizedWeight = $totalWeight > 0 ? $weight / $totalWeight : 0;
+
+ // Pastikan nilai valid
+ $value = max(1, intval($rating->value));
+
+ // Bonus nilai jika kriteria dipilih
+ if (isset($criterias[$rating->category_id]) &&
+ $criterias[$rating->category_id] == $rating->subcategory_id) {
+ $value = 5; // Nilai maksimal
+ }
+
+ // Hitung utilitas - menerapkan konsep yang sama dengan view
+ $utilityValue = ($value - 1) / 4; // Normalisasi ke rentang 0-1 (jika nilai antara 1-5)
+ $ratingScore = $normalizedWeight * $utilityValue;
+ $score += $ratingScore;
+
+ // Simpan untuk debug
+ $debugInfo[$rating->category_name] = [
+ 'bobot' => $weight,
+ 'bobot_normal' => $normalizedWeight,
+ 'nilai' => $value,
+ 'nilai_asli' => $rating->value,
+ 'subcategory' => $rating->subcategory_name,
+ 'skor_kriteria' => $ratingScore
+ ];
+ }
+ }
+
+ $cafeScores[$cafe->id] = [
+ 'score' => $score,
+ 'debug' => $debugInfo
+ ];
+ }
+
+ // Urutkan cafe berdasarkan skor tertinggi
+ uasort($cafeScores, function($a, $b) {
+ return $b['score'] <=> $a['score'];
+ });
+
+ // Ambil id cafe yang sudah diurutkan
+ $rankedCafeIds = array_keys($cafeScores);
+
+ // Urutkan koleksi cafe sesuai skor
+ $rankedCafes = $cafes->sortBy(function($cafe) use ($rankedCafeIds) {
+ return array_search($cafe->id, $rankedCafeIds);
+ });
+
+ // Ambil hanya 10 cafe teratas
+ $rankedCafes = $rankedCafes->take(10);
+
+ // Persiapkan data hasil untuk disimpan
+ $resultsData = [];
+ foreach ($rankedCafes as $cafe) {
+ if (isset($cafeScores[$cafe->id])) {
+ $resultsData[$cafe->id] = [
+ 'name' => $cafe->nama,
+ 'score' => $cafeScores[$cafe->id]['score'],
+ 'details' => $cafeScores[$cafe->id]['debug']
+ ];
+ }
+ }
+
+ // Simpan hasil pencarian ke database jika user sudah login
+ if (Auth::check()) {
+ SearchHistory::create([
+ 'user_id' => Auth::id(),
+ 'weights' => $weights,
+ 'criteria' => $criterias,
+ 'location' => $lokasi,
+ 'results' => $resultsData
+ ]);
+ }
+
+ return view('cafe.search-fixed', compact('currentStep', 'rankedCafes', 'cafeScores'));
+ }
+
+ /**
+ * Display search history for the authenticated user.
+ *
+ * @return \Illuminate\View\View
+ */
+ public function history()
+ {
+ $histories = SearchHistory::where('user_id', Auth::id())
+ ->orderBy('created_at', 'desc')
+ ->paginate(10);
+
+ return view('cafe.search-history', compact('histories'));
+ }
+
+ /**
+ * Display search history detail.
+ *
+ * @param int $id
+ * @return \Illuminate\View\View
+ */
+ public function detail($id)
+ {
+ $history = SearchHistory::where('user_id', Auth::id())
+ ->findOrFail($id);
+
+ // Retrieve cafes from the stored results
+ $cafeIds = array_keys($history->results ?? []);
+ $cafes = Cafe::whereIn('id', $cafeIds)->get();
+
+ // Map cafes to include their scores from history
+ $rankedCafes = $cafes->map(function($cafe) use ($history) {
+ $cafe->smart_score = $history->results[$cafe->id]['score'] ?? 0;
+ $cafe->smart_details = $history->results[$cafe->id]['details'] ?? [];
+ return $cafe;
+ })->sortByDesc('smart_score');
+
+ $weights = $history->weights;
+
+ return view('cafe.search-detail', compact('history', 'rankedCafes', 'weights'));
+ }
}
\ No newline at end of file
diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php
index ef30ff4..da44daa 100644
--- a/app/Http/Kernel.php
+++ b/app/Http/Kernel.php
@@ -1,67 +1,69 @@
-
- */
- protected $middleware = [
- //
- ];
-
- /**
- * The application's route middleware groups.
- *
- * @var array>
- */
- protected $middlewareGroups = [
- 'web' => [
- // Web middleware group
- ],
-
- 'api' => [
- // API middleware group
- ],
- ];
-
- /**
- * The application's route middleware aliases.
- *
- * Aliases may be used instead of class names to assign middleware to routes and groups.
- *
- * @var array
- */
- protected $middlewareAliases = [
- 'auth' => \App\Http\Middleware\Authenticate::class,
- 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
- 'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,
- 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
- 'can' => \Illuminate\Auth\Middleware\Authorize::class,
- 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
- 'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
- 'precognitive' => \Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests::class,
- 'signed' => \App\Http\Middleware\ValidateSignature::class,
- 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
- 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
- 'admin' => \App\Http\Middleware\AdminMiddleware::class,
- ];
-
- /**
- * Register the application's route middleware.
- *
- * These middleware may be assigned to groups or used individually.
- *
- * @var array
- */
- protected $routeMiddleware = [
- //
- ];
+
+ */
+ protected $middleware = [
+ // \App\Http\Middleware\TrustHosts::class,
+ \App\Http\Middleware\TrustProxies::class,
+ \Illuminate\Http\Middleware\HandleCors::class,
+ \App\Http\Middleware\PreventRequestsDuringMaintenance::class,
+ \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
+ \App\Http\Middleware\TrimStrings::class,
+ \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
+ ];
+
+ /**
+ * The application's route middleware groups.
+ *
+ * @var array>
+ */
+ protected $middlewareGroups = [
+ 'web' => [
+ \App\Http\Middleware\EncryptCookies::class,
+ \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
+ \Illuminate\Session\Middleware\StartSession::class,
+ \Illuminate\View\Middleware\ShareErrorsFromSession::class,
+ \App\Http\Middleware\VerifyCsrfToken::class,
+ \Illuminate\Routing\Middleware\SubstituteBindings::class,
+ ],
+
+ 'api' => [
+ // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
+ 'throttle:api',
+ \Illuminate\Routing\Middleware\SubstituteBindings::class,
+ ],
+ ];
+
+ /**
+ * The application's route middleware.
+ *
+ * These middleware may be assigned to groups or used individually.
+ *
+ * @var array
+ */
+ protected $routeMiddleware = [
+ 'auth' => \App\Http\Middleware\Authenticate::class,
+ 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
+ 'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,
+ 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
+ 'can' => \Illuminate\Auth\Middleware\Authorize::class,
+ 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
+ 'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
+ 'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
+ 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
+ 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
+ 'role' => \App\Http\Middleware\CheckRole::class,
+ 'is_admin' => \App\Http\Middleware\AdminMiddleware::class,
+ ];
}
\ No newline at end of file
diff --git a/app/Http/Middleware/AdminMiddleware.php b/app/Http/Middleware/AdminMiddleware.php
index 28d0065..4dc2c27 100644
--- a/app/Http/Middleware/AdminMiddleware.php
+++ b/app/Http/Middleware/AdminMiddleware.php
@@ -1,26 +1,26 @@
-role === 'admin') {
- return $next($request);
- }
-
- return redirect()->route('home')->with('error', 'Anda tidak memiliki akses untuk halaman ini.');
- }
+role === 'admin') {
+ return $next($request);
+ }
+
+ return redirect()->route('home')->with('error', 'Anda tidak memiliki akses untuk halaman ini.');
+ }
}
\ No newline at end of file
diff --git a/app/Http/Middleware/RedirectIfAuthenticated.php b/app/Http/Middleware/RedirectIfAuthenticated.php
index d4ea104..927b8cc 100644
--- a/app/Http/Middleware/RedirectIfAuthenticated.php
+++ b/app/Http/Middleware/RedirectIfAuthenticated.php
@@ -1,38 +1,38 @@
-check()) {
- $user = Auth::guard($guard)->user();
-
- if ($user && $user->role === 'admin') {
- return redirect()->route('admin.dashboard');
- }
-
- return redirect(RouteServiceProvider::HOME);
- }
- }
-
- return $next($request);
- }
+check()) {
+ $user = Auth::guard($guard)->user();
+
+ if ($user && $user->role === 'admin') {
+ return redirect()->route('admin.dashboard');
+ }
+
+ return redirect(RouteServiceProvider::HOME);
+ }
+ }
+
+ return $next($request);
+ }
}
\ No newline at end of file
diff --git a/app/Models/CafeImage.php b/app/Models/CafeImage.php
index 7cbf429..33aa093 100644
--- a/app/Models/CafeImage.php
+++ b/app/Models/CafeImage.php
@@ -1,21 +1,21 @@
-belongsTo(Cafe::class);
- }
+belongsTo(Cafe::class);
+ }
}
\ No newline at end of file
diff --git a/app/Models/CafeRating.php b/app/Models/CafeRating.php
index c027e5b..24b9a11 100644
--- a/app/Models/CafeRating.php
+++ b/app/Models/CafeRating.php
@@ -1,32 +1,32 @@
-belongsTo(Cafe::class);
- }
-
- public function category()
- {
- return $this->belongsTo(Category::class);
- }
-
- public function subcategory()
- {
- return $this->belongsTo(Subcategory::class);
- }
+belongsTo(Cafe::class);
+ }
+
+ public function category()
+ {
+ return $this->belongsTo(Category::class);
+ }
+
+ public function subcategory()
+ {
+ return $this->belongsTo(Subcategory::class);
+ }
}
\ No newline at end of file
diff --git a/app/Models/Category.php b/app/Models/Category.php
index 5402938..f377327 100644
--- a/app/Models/Category.php
+++ b/app/Models/Category.php
@@ -1,50 +1,50 @@
-slug = Str::slug($category->name);
- });
-
- static::updating(function ($category) {
- $category->slug = Str::slug($category->name);
- });
- }
-
- /**
- * Get the subcategories for the category.
- */
- public function subcategories()
- {
- return $this->hasMany(Subcategory::class);
- }
-
- /**
- * Get the cafes for the category.
- */
- public function cafes()
- {
- return $this->hasMany(Cafe::class);
- }
+slug = Str::slug($category->name);
+ });
+
+ static::updating(function ($category) {
+ $category->slug = Str::slug($category->name);
+ });
+ }
+
+ /**
+ * Get the subcategories for the category.
+ */
+ public function subcategories()
+ {
+ return $this->hasMany(Subcategory::class);
+ }
+
+ /**
+ * Get the cafes for the category.
+ */
+ public function cafes()
+ {
+ return $this->hasMany(Cafe::class);
+ }
}
\ No newline at end of file
diff --git a/app/Models/ContactMessage.php b/app/Models/ContactMessage.php
index 6efe869..90de6be 100644
--- a/app/Models/ContactMessage.php
+++ b/app/Models/ContactMessage.php
@@ -1,23 +1,23 @@
-
- */
- protected $fillable = [
- 'name',
- 'email',
- 'message',
- 'is_read',
- ];
+
+ */
+ protected $fillable = [
+ 'name',
+ 'email',
+ 'message',
+ 'is_read',
+ ];
}
\ No newline at end of file
diff --git a/app/Models/Favorite.php b/app/Models/Favorite.php
index e6b1dbb..bf4f061 100644
--- a/app/Models/Favorite.php
+++ b/app/Models/Favorite.php
@@ -1,23 +1,23 @@
-belongsTo(User::class);
- }
-
- public function cafe()
- {
- return $this->belongsTo(Cafe::class);
- }
+belongsTo(User::class);
+ }
+
+ public function cafe()
+ {
+ return $this->belongsTo(Cafe::class);
+ }
}
\ No newline at end of file
diff --git a/app/Models/SearchHistory.php b/app/Models/SearchHistory.php
index ddef41a..fdf389e 100644
--- a/app/Models/SearchHistory.php
+++ b/app/Models/SearchHistory.php
@@ -1,45 +1,45 @@
- 'array',
- 'criteria' => 'array',
- 'results' => 'array',
- 'created_at' => 'datetime',
- 'updated_at' => 'datetime',
- ];
-
- /**
- * Get the user that owns the search history.
- */
- public function user()
- {
- return $this->belongsTo(User::class);
- }
+ 'array',
+ 'criteria' => 'array',
+ 'results' => 'array',
+ 'created_at' => 'datetime',
+ 'updated_at' => 'datetime',
+ ];
+
+ /**
+ * Get the user that owns the search history.
+ */
+ public function user()
+ {
+ return $this->belongsTo(User::class);
+ }
}
\ No newline at end of file
diff --git a/app/Models/Subcategory.php b/app/Models/Subcategory.php
index 135052b..26ce4ef 100644
--- a/app/Models/Subcategory.php
+++ b/app/Models/Subcategory.php
@@ -1,43 +1,43 @@
-slug = Str::slug($subcategory->name);
- });
-
- static::updating(function ($subcategory) {
- $subcategory->slug = Str::slug($subcategory->name);
- });
- }
-
- /**
- * Get the category that owns the subcategory.
- */
- public function category()
- {
- return $this->belongsTo(Category::class);
- }
+slug = Str::slug($subcategory->name);
+ });
+
+ static::updating(function ($subcategory) {
+ $subcategory->slug = Str::slug($subcategory->name);
+ });
+ }
+
+ /**
+ * Get the category that owns the subcategory.
+ */
+ public function category()
+ {
+ return $this->belongsTo(Category::class);
+ }
}
\ No newline at end of file
diff --git a/app/Notifications/CustomVerifyEmail.php b/app/Notifications/CustomVerifyEmail.php
index 4ac217c..7d2e360 100644
--- a/app/Notifications/CustomVerifyEmail.php
+++ b/app/Notifications/CustomVerifyEmail.php
@@ -1,116 +1,116 @@
-verificationUrl($notifiable);
-
- if (static::$toMailCallback) {
- return call_user_func(static::$toMailCallback, $notifiable, $verificationUrl);
- }
-
- return $this->buildMailMessage($verificationUrl);
- }
-
- /**
- * Get the verify email notification mail message for the given URL.
- *
- * @param string $url
- * @return \Illuminate\Notifications\Messages\MailMessage
- */
- protected function buildMailMessage($url)
- {
- return (new MailMessage)
- ->subject('Verifikasi Alamat Email Anda')
- ->greeting('Halo!')
- ->line('Terima kasih telah mendaftar di aplikasi Recaje.')
- ->line('Silakan klik tombol di bawah ini untuk memverifikasi alamat email Anda.')
- ->action('Verifikasi Email', $url)
- ->line('Jika Anda tidak membuat akun, Anda dapat mengabaikan email ini.')
- ->salutation('Salam, Tim Recaje');
- }
-
- /**
- * Get the verification URL for the given notifiable.
- *
- * @param mixed $notifiable
- * @return string
- */
- protected function verificationUrl($notifiable)
- {
- if (static::$createUrlCallback) {
- return call_user_func(static::$createUrlCallback, $notifiable);
- }
-
- return URL::temporarySignedRoute(
- 'verification.verify',
- Carbon::now()->addMinutes(Config::get('auth.verification.expire', 60)),
- [
- 'id' => $notifiable->getKey(),
- 'hash' => sha1($notifiable->getEmailForVerification()),
- ]
- );
- }
-
- /**
- * Set a callback that should be used when creating the email verification URL.
- *
- * @param \Closure $callback
- * @return void
- */
- public static function createUrlUsing($callback)
- {
- static::$createUrlCallback = $callback;
- }
-
- /**
- * Set a callback that should be used when building the notification mail message.
- *
- * @param \Closure $callback
- * @return void
- */
- public static function toMailUsing($callback)
- {
- static::$toMailCallback = $callback;
- }
+verificationUrl($notifiable);
+
+ if (static::$toMailCallback) {
+ return call_user_func(static::$toMailCallback, $notifiable, $verificationUrl);
+ }
+
+ return $this->buildMailMessage($verificationUrl);
+ }
+
+ /**
+ * Get the verify email notification mail message for the given URL.
+ *
+ * @param string $url
+ * @return \Illuminate\Notifications\Messages\MailMessage
+ */
+ protected function buildMailMessage($url)
+ {
+ return (new MailMessage)
+ ->subject('Verifikasi Alamat Email Anda')
+ ->greeting('Halo!')
+ ->line('Terima kasih telah mendaftar di aplikasi Recaje.')
+ ->line('Silakan klik tombol di bawah ini untuk memverifikasi alamat email Anda.')
+ ->action('Verifikasi Email', $url)
+ ->line('Jika Anda tidak membuat akun, Anda dapat mengabaikan email ini.')
+ ->salutation('Salam, Tim Recaje');
+ }
+
+ /**
+ * Get the verification URL for the given notifiable.
+ *
+ * @param mixed $notifiable
+ * @return string
+ */
+ protected function verificationUrl($notifiable)
+ {
+ if (static::$createUrlCallback) {
+ return call_user_func(static::$createUrlCallback, $notifiable);
+ }
+
+ return URL::temporarySignedRoute(
+ 'verification.verify',
+ Carbon::now()->addMinutes(Config::get('auth.verification.expire', 60)),
+ [
+ 'id' => $notifiable->getKey(),
+ 'hash' => sha1($notifiable->getEmailForVerification()),
+ ]
+ );
+ }
+
+ /**
+ * Set a callback that should be used when creating the email verification URL.
+ *
+ * @param \Closure $callback
+ * @return void
+ */
+ public static function createUrlUsing($callback)
+ {
+ static::$createUrlCallback = $callback;
+ }
+
+ /**
+ * Set a callback that should be used when building the notification mail message.
+ *
+ * @param \Closure $callback
+ * @return void
+ */
+ public static function toMailUsing($callback)
+ {
+ static::$toMailCallback = $callback;
+ }
}
\ No newline at end of file
diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php
index ca3a402..62c1b37 100644
--- a/app/Providers/RouteServiceProvider.php
+++ b/app/Providers/RouteServiceProvider.php
@@ -1,49 +1,49 @@
-configureRateLimiting();
-
- $this->routes(function () {
- Route::prefix('api')
- ->middleware('api')
- ->namespace($this->namespace)
- ->group(base_path('routes/api.php'));
-
- Route::middleware('web')
- ->namespace($this->namespace)
- ->group(base_path('routes/web.php'));
- });
- }
-
- /**
- * Configure the rate limiters for the application.
- *
- * @return void
- */
- protected function configureRateLimiting()
- {
- //
- }
+configureRateLimiting();
+
+ $this->routes(function () {
+ Route::prefix('api')
+ ->middleware('api')
+ ->namespace($this->namespace)
+ ->group(base_path('routes/api.php'));
+
+ Route::middleware('web')
+ ->namespace($this->namespace)
+ ->group(base_path('routes/web.php'));
+ });
+ }
+
+ /**
+ * Configure the rate limiters for the application.
+ *
+ * @return void
+ */
+ protected function configureRateLimiting()
+ {
+ //
+ }
}
\ No newline at end of file
diff --git a/config/database.php b/config/database.php
index 5016e9e..8910562 100644
--- a/config/database.php
+++ b/config/database.php
@@ -47,9 +47,9 @@
'url' => env('DB_URL'),
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
- 'database' => env('DB_DATABASE', 'oyiwebid_recaje'),
- 'username' => env('DB_USERNAME', 'oyiwebid_faradina'),
- 'password' => env('DB_PASSWORD', 'Faradina14*'),
+ 'database' => env('DB_DATABASE', 'laravel'),
+ 'username' => env('DB_USERNAME', 'root'),
+ 'password' => env('DB_PASSWORD', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => env('DB_CHARSET', 'utf8mb4'),
'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
diff --git a/database/migrations/2023_05_01_000000_create_contact_messages_table.php b/database/migrations/2023_05_01_000000_create_contact_messages_table.php
index 55693de..329632f 100644
--- a/database/migrations/2023_05_01_000000_create_contact_messages_table.php
+++ b/database/migrations/2023_05_01_000000_create_contact_messages_table.php
@@ -1,35 +1,35 @@
-id();
- $table->string('name');
- $table->string('email');
- $table->text('message');
- $table->boolean('is_read')->default(false);
- $table->timestamps();
- });
- }
-
- /**
- * Reverse the migrations.
- *
- * @return void
- */
- public function down()
- {
- Schema::dropIfExists('contact_messages');
- }
+id();
+ $table->string('name');
+ $table->string('email');
+ $table->text('message');
+ $table->boolean('is_read')->default(false);
+ $table->timestamps();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('contact_messages');
+ }
}
\ No newline at end of file
diff --git a/database/migrations/2023_05_15_add_area_to_cafes_table.php b/database/migrations/2023_05_15_add_area_to_cafes_table.php
index 3e71c6a..dd0474f 100644
--- a/database/migrations/2023_05_15_add_area_to_cafes_table.php
+++ b/database/migrations/2023_05_15_add_area_to_cafes_table.php
@@ -1,30 +1,30 @@
-string('area')->nullable()->after('lokasi');
- });
- }
-
- /**
- * Reverse the migrations.
- */
- public function down(): void
- {
- Schema::table('cafes', function (Blueprint $table) {
- // Menghapus kolom area jika migrasi di-rollback
- $table->dropColumn('area');
- });
- }
+string('area')->nullable()->after('lokasi');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('cafes', function (Blueprint $table) {
+ // Menghapus kolom area jika migrasi di-rollback
+ $table->dropColumn('area');
+ });
+ }
};
\ No newline at end of file
diff --git a/database/migrations/2023_05_20_000000_create_search_histories_table.php b/database/migrations/2023_05_20_000000_create_search_histories_table.php
index 43acdc1..3f0429a 100644
--- a/database/migrations/2023_05_20_000000_create_search_histories_table.php
+++ b/database/migrations/2023_05_20_000000_create_search_histories_table.php
@@ -1,36 +1,36 @@
-id();
- $table->foreignId('user_id')->constrained()->onDelete('cascade');
- $table->json('weights')->nullable(); // Menyimpan bobot kriteria
- $table->json('criteria')->nullable(); // Menyimpan kriteria yang dipilih
- $table->string('location')->nullable(); // Menyimpan lokasi jika ada
- $table->json('results')->nullable(); // Menyimpan hasil cafe dengan skor
- $table->timestamps();
- });
- }
-
- /**
- * Reverse the migrations.
- *
- * @return void
- */
- public function down()
- {
- Schema::dropIfExists('search_histories');
- }
+id();
+ $table->foreignId('user_id')->constrained()->onDelete('cascade');
+ $table->json('weights')->nullable(); // Menyimpan bobot kriteria
+ $table->json('criteria')->nullable(); // Menyimpan kriteria yang dipilih
+ $table->string('location')->nullable(); // Menyimpan lokasi jika ada
+ $table->json('results')->nullable(); // Menyimpan hasil cafe dengan skor
+ $table->timestamps();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('search_histories');
+ }
}
\ No newline at end of file
diff --git a/database/migrations/2024_03_21_create_categories_table.txt b/database/migrations/2024_03_21_create_categories_table.txt
index 55d8b4d..544cbc3 100644
--- a/database/migrations/2024_03_21_create_categories_table.txt
+++ b/database/migrations/2024_03_21_create_categories_table.txt
@@ -1,32 +1,32 @@
-id();
- $table->string('name');
- $table->string('subcategory1')->nullable();
- $table->string('subcategory2')->nullable();
- $table->string('subcategory3')->nullable();
- $table->string('subcategory4')->nullable();
- $table->string('subcategory5')->nullable();
- $table->integer('value1')->nullable();
- $table->integer('value2')->nullable();
- $table->integer('value3')->nullable();
- $table->integer('value4')->nullable();
- $table->integer('value5')->nullable();
- $table->timestamps();
- });
- }
-
- public function down()
- {
- Schema::dropIfExists('categories');
- }
+id();
+ $table->string('name');
+ $table->string('subcategory1')->nullable();
+ $table->string('subcategory2')->nullable();
+ $table->string('subcategory3')->nullable();
+ $table->string('subcategory4')->nullable();
+ $table->string('subcategory5')->nullable();
+ $table->integer('value1')->nullable();
+ $table->integer('value2')->nullable();
+ $table->integer('value3')->nullable();
+ $table->integer('value4')->nullable();
+ $table->integer('value5')->nullable();
+ $table->timestamps();
+ });
+ }
+
+ public function down()
+ {
+ Schema::dropIfExists('categories');
+ }
};
\ No newline at end of file
diff --git a/database/migrations/2025_04_27_000001_create_categories_table.php b/database/migrations/2025_04_27_000001_create_categories_table.php
index 312d098..ecbcc7c 100644
--- a/database/migrations/2025_04_27_000001_create_categories_table.php
+++ b/database/migrations/2025_04_27_000001_create_categories_table.php
@@ -1,30 +1,30 @@
-id();
- $table->string('name');
- $table->string('slug')->unique();
- $table->text('description')->nullable();
- $table->timestamps();
- });
- }
-
- /**
- * Reverse the migrations.
- */
- public function down(): void
- {
- Schema::dropIfExists('categories');
- }
+id();
+ $table->string('name');
+ $table->string('slug')->unique();
+ $table->text('description')->nullable();
+ $table->timestamps();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::dropIfExists('categories');
+ }
};
\ No newline at end of file
diff --git a/database/migrations/2025_04_27_000002_create_subcategories_table.php b/database/migrations/2025_04_27_000002_create_subcategories_table.php
index 868e520..c34ccc1 100644
--- a/database/migrations/2025_04_27_000002_create_subcategories_table.php
+++ b/database/migrations/2025_04_27_000002_create_subcategories_table.php
@@ -1,30 +1,30 @@
-id();
- $table->foreignId('category_id')->constrained()->onDelete('cascade');
- $table->string('name');
- $table->string('slug')->unique();
- $table->timestamps();
- });
- }
-
- /**
- * Reverse the migrations.
- */
- public function down(): void
- {
- Schema::dropIfExists('subcategories');
- }
+id();
+ $table->foreignId('category_id')->constrained()->onDelete('cascade');
+ $table->string('name');
+ $table->string('slug')->unique();
+ $table->timestamps();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::dropIfExists('subcategories');
+ }
};
\ No newline at end of file
diff --git a/database/migrations/2025_04_30_000001_modify_categories_table.txt b/database/migrations/2025_04_30_000001_modify_categories_table.txt
index 35bec6a..460c003 100644
--- a/database/migrations/2025_04_30_000001_modify_categories_table.txt
+++ b/database/migrations/2025_04_30_000001_modify_categories_table.txt
@@ -1,56 +1,56 @@
-string('subcategory1')->nullable();
- $table->string('subcategory2')->nullable();
- $table->string('subcategory3')->nullable();
- $table->string('subcategory4')->nullable();
- $table->string('subcategory5')->nullable();
-
- // Tambahkan kolom untuk nilai subkategori
- $table->integer('value1')->default(1);
- $table->integer('value2')->default(2);
- $table->integer('value3')->default(3);
- $table->integer('value4')->default(4);
- $table->integer('value5')->default(5);
- });
- }
-
- /**
- * Reverse the migrations.
- */
- public function down(): void
- {
- Schema::table('categories', function (Blueprint $table) {
- // Hapus kolom subkategori
- $table->dropColumn([
- 'subcategory1', 'subcategory2', 'subcategory3', 'subcategory4', 'subcategory5',
- 'value1', 'value2', 'value3', 'value4', 'value5'
- ]);
- });
-
- // Buat ulang tabel subcategories
- Schema::create('subcategories', function (Blueprint $table) {
- $table->id();
- $table->foreignId('category_id')->constrained()->onDelete('cascade');
- $table->string('name');
- $table->timestamps();
- });
- }
+string('subcategory1')->nullable();
+ $table->string('subcategory2')->nullable();
+ $table->string('subcategory3')->nullable();
+ $table->string('subcategory4')->nullable();
+ $table->string('subcategory5')->nullable();
+
+ // Tambahkan kolom untuk nilai subkategori
+ $table->integer('value1')->default(1);
+ $table->integer('value2')->default(2);
+ $table->integer('value3')->default(3);
+ $table->integer('value4')->default(4);
+ $table->integer('value5')->default(5);
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('categories', function (Blueprint $table) {
+ // Hapus kolom subkategori
+ $table->dropColumn([
+ 'subcategory1', 'subcategory2', 'subcategory3', 'subcategory4', 'subcategory5',
+ 'value1', 'value2', 'value3', 'value4', 'value5'
+ ]);
+ });
+
+ // Buat ulang tabel subcategories
+ Schema::create('subcategories', function (Blueprint $table) {
+ $table->id();
+ $table->foreignId('category_id')->constrained()->onDelete('cascade');
+ $table->string('name');
+ $table->timestamps();
+ });
+ }
};
\ No newline at end of file
diff --git a/database/migrations/2025_05_01_create_cafe_ratings_table.php b/database/migrations/2025_05_01_create_cafe_ratings_table.php
index f8c9819..95defff 100644
--- a/database/migrations/2025_05_01_create_cafe_ratings_table.php
+++ b/database/migrations/2025_05_01_create_cafe_ratings_table.php
@@ -1,30 +1,30 @@
-id();
- $table->foreignId('cafe_id')->constrained()->onDelete('cascade');
- $table->foreignId('category_id')->constrained()->onDelete('cascade');
- $table->foreignId('subcategory_id')->constrained()->onDelete('cascade');
- $table->timestamps();
- });
- }
-
- /**
- * Reverse the migrations.
- */
- public function down(): void
- {
- Schema::dropIfExists('cafe_ratings');
- }
+id();
+ $table->foreignId('cafe_id')->constrained()->onDelete('cascade');
+ $table->foreignId('category_id')->constrained()->onDelete('cascade');
+ $table->foreignId('subcategory_id')->constrained()->onDelete('cascade');
+ $table->timestamps();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::dropIfExists('cafe_ratings');
+ }
};
\ No newline at end of file
diff --git a/database/migrations/2025_05_10_restructure_cafes_table.php b/database/migrations/2025_05_10_restructure_cafes_table.php
index 6f91290..e2e9c65 100644
--- a/database/migrations/2025_05_10_restructure_cafes_table.php
+++ b/database/migrations/2025_05_10_restructure_cafes_table.php
@@ -1,57 +1,57 @@
-id();
- $table->string('nama');
- $table->string('lokasi');
- $table->string('gambar')->nullable();
- $table->decimal('latitude', 10, 7)->nullable();
- $table->decimal('longitude', 10, 7)->nullable();
- $table->timestamps();
- });
-
- // Langkah 3: Pindahkan data dari cafes ke cafes_temp
- DB::statement('INSERT INTO cafes_temp (id, nama, lokasi, gambar, latitude, longitude, created_at, updated_at)
- SELECT id, nama, lokasi, gambar, latitude, longitude, created_at, updated_at FROM cafes');
-
- // Langkah 4: Drop tabel cafes lama
- Schema::dropIfExists('cafes');
-
- // Langkah 5: Rename cafes_temp menjadi cafes
- Schema::rename('cafes_temp', 'cafes');
-
- // Langkah 6: Buat kembali tabel cafe_ratings dengan struktur baru
- Schema::create('cafe_ratings', function (Blueprint $table) {
- $table->id();
- $table->foreignId('cafe_id')->constrained()->onDelete('cascade');
- $table->foreignId('category_id')->constrained()->onDelete('cascade');
- $table->foreignId('subcategory_id')->constrained()->onDelete('cascade');
- $table->timestamps();
- });
- }
-
- /**
- * Reverse the migrations.
- */
- public function down(): void
- {
- // Tak perlu diimplementasikan karena ini adalah migrasi restrukturisasi
- // Jika ingin rollback, perlu restore manual dari backup
- }
+id();
+ $table->string('nama');
+ $table->string('lokasi');
+ $table->string('gambar')->nullable();
+ $table->decimal('latitude', 10, 7)->nullable();
+ $table->decimal('longitude', 10, 7)->nullable();
+ $table->timestamps();
+ });
+
+ // Langkah 3: Pindahkan data dari cafes ke cafes_temp
+ DB::statement('INSERT INTO cafes_temp (id, nama, lokasi, gambar, latitude, longitude, created_at, updated_at)
+ SELECT id, nama, lokasi, gambar, latitude, longitude, created_at, updated_at FROM cafes');
+
+ // Langkah 4: Drop tabel cafes lama
+ Schema::dropIfExists('cafes');
+
+ // Langkah 5: Rename cafes_temp menjadi cafes
+ Schema::rename('cafes_temp', 'cafes');
+
+ // Langkah 6: Buat kembali tabel cafe_ratings dengan struktur baru
+ Schema::create('cafe_ratings', function (Blueprint $table) {
+ $table->id();
+ $table->foreignId('cafe_id')->constrained()->onDelete('cascade');
+ $table->foreignId('category_id')->constrained()->onDelete('cascade');
+ $table->foreignId('subcategory_id')->constrained()->onDelete('cascade');
+ $table->timestamps();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ // Tak perlu diimplementasikan karena ini adalah migrasi restrukturisasi
+ // Jika ingin rollback, perlu restore manual dari backup
+ }
};
\ No newline at end of file
diff --git a/database/migrations/2025_05_20_create_favorites_table.php b/database/migrations/2025_05_20_create_favorites_table.php
index 6a4ebf1..15fcc79 100644
--- a/database/migrations/2025_05_20_create_favorites_table.php
+++ b/database/migrations/2025_05_20_create_favorites_table.php
@@ -1,32 +1,32 @@
-id();
- $table->foreignId('user_id')->constrained()->onDelete('cascade');
- $table->foreignId('cafe_id')->constrained()->onDelete('cascade');
- $table->timestamps();
-
- // Pastikan kombinasi user_id dan cafe_id unik
- $table->unique(['user_id', 'cafe_id']);
- });
- }
-
- /**
- * Reverse the migrations.
- */
- public function down(): void
- {
- Schema::dropIfExists('favorites');
- }
+id();
+ $table->foreignId('user_id')->constrained()->onDelete('cascade');
+ $table->foreignId('cafe_id')->constrained()->onDelete('cascade');
+ $table->timestamps();
+
+ // Pastikan kombinasi user_id dan cafe_id unik
+ $table->unique(['user_id', 'cafe_id']);
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::dropIfExists('favorites');
+ }
};
\ No newline at end of file
diff --git a/database/migrations/2025_05_22_create_cafe_images_table.php b/database/migrations/2025_05_22_create_cafe_images_table.php
index f0c13b7..4a35b2a 100644
--- a/database/migrations/2025_05_22_create_cafe_images_table.php
+++ b/database/migrations/2025_05_22_create_cafe_images_table.php
@@ -1,23 +1,23 @@
-id();
- $table->foreignId('cafe_id')->constrained()->onDelete('cascade');
- $table->string('image_path');
- $table->timestamps();
- });
- }
-
- public function down()
- {
- Schema::dropIfExists('cafe_images');
- }
+id();
+ $table->foreignId('cafe_id')->constrained()->onDelete('cascade');
+ $table->string('image_path');
+ $table->timestamps();
+ });
+ }
+
+ public function down()
+ {
+ Schema::dropIfExists('cafe_images');
+ }
};
\ No newline at end of file
diff --git a/database/migrations/2025_07_03_000001_add_harga_range_to_cafes_table.php b/database/migrations/2025_07_03_000001_add_harga_range_to_cafes_table.php
index 76398e8..803e40c 100644
--- a/database/migrations/2025_07_03_000001_add_harga_range_to_cafes_table.php
+++ b/database/migrations/2025_07_03_000001_add_harga_range_to_cafes_table.php
@@ -1,29 +1,29 @@
-decimal('harga_termurah', 10, 2)->nullable()->after('area');
- $table->decimal('harga_termahal', 10, 2)->nullable()->after('harga_termurah');
- });
- }
-
- /**
- * Reverse the migrations.
- */
- public function down(): void
- {
- Schema::table('cafes', function (Blueprint $table) {
- $table->dropColumn(['harga_termurah', 'harga_termahal']);
- });
- }
-};
+decimal('harga_termurah', 10, 2)->nullable()->after('area');
+ $table->decimal('harga_termahal', 10, 2)->nullable()->after('harga_termurah');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('cafes', function (Blueprint $table) {
+ $table->dropColumn(['harga_termurah', 'harga_termahal']);
+ });
+ }
+};
diff --git a/database/seeders/CafeAndRatingsSeeder.php b/database/seeders/CafeAndRatingsSeeder.php
index 098b665..5b62340 100644
--- a/database/seeders/CafeAndRatingsSeeder.php
+++ b/database/seeders/CafeAndRatingsSeeder.php
@@ -1,126 +1,126 @@
- 'Poppins', 'lokasi' => null, 'harga' => 3, 'wifi' => 3, 'jam' => 5, 'fotogenik' => 4, 'jarak' => 3, 'fasilitas' => 3, 'area' => 'Indoor, Outdoor'],
- ['nama' => 'Teras JTI', 'lokasi' => null, 'harga' => 4, 'wifi' => 5, 'jam' => 3, 'fotogenik' => 3, 'jarak' => 5, 'fasilitas' => 4, 'area' => 'Indoor, Semi Outdoor, Outdoor'],
- ['nama' => 'Nugas Jember', 'lokasi' => null, 'harga' => 4, 'wifi' => 3, 'jam' => 4, 'fotogenik' => 1, 'jarak' => 4, 'fasilitas' => 1, 'area' => 'Semi Outdoor'],
- ['nama' => 'Kopi Kampus', 'lokasi' => null, 'harga' => 4, 'wifi' => 3, 'jam' => 5, 'fotogenik' => 1, 'jarak' => 3, 'fasilitas' => 3, 'area' => 'Semi Outdoor'],
- ['nama' => 'Eterno', 'lokasi' => null, 'harga' => 2, 'wifi' => 4, 'jam' => 4, 'fotogenik' => 4, 'jarak' => 3, 'fasilitas' => 5, 'area' => 'Indoor, Outdoor'],
- ['nama' => 'Kattappa', 'lokasi' => null, 'harga' => 3, 'wifi' => 3, 'jam' => 4, 'fotogenik' => 4, 'jarak' => 3, 'fasilitas' => 4, 'area' => 'Indoor, Semi Outdoor, Outdoor'],
- ['nama' => 'Fifty-Fifty', 'lokasi' => null, 'harga' => 4, 'wifi' => 3, 'jam' => 4, 'fotogenik' => 2, 'jarak' => 3, 'fasilitas' => 2, 'area' => 'Semi Outdoor'],
- ['nama' => 'Discuss Space & Coffee', 'lokasi' => null, 'harga' => 3, 'wifi' => 4, 'jam' => 4, 'fotogenik' => 3, 'jarak' => 3, 'fasilitas' => 4, 'area' => 'Indoor'],
- ['nama' => 'Subur', 'lokasi' => null, 'harga' => 3, 'wifi' => 4, 'jam' => 5, 'fotogenik' => 3, 'jarak' => 3, 'fasilitas' => 2, 'area' => 'Indoor'],
- ['nama' => 'Nuansa', 'lokasi' => null, 'harga' => 4, 'wifi' => 3, 'jam' => 4, 'fotogenik' => 2, 'jarak' => 3, 'fasilitas' => 5, 'area' => 'Indoor, Semi Outdoor'],
- ['nama' => 'Nol Kilometer', 'lokasi' => null, 'harga' => 5, 'wifi' => 2, 'jam' => 3, 'fotogenik' => 1, 'jarak' => 3, 'fasilitas' => 2, 'area' => 'Semi Outdoor'],
- ['nama' => 'Tanaloka', 'lokasi' => null, 'harga' => 2, 'wifi' => 4, 'jam' => 2, 'fotogenik' => 5, 'jarak' => 3, 'fasilitas' => 5, 'area' => 'Indoor, Semi Outdoor, Outdoor'],
- ['nama' => 'Wafa', 'lokasi' => null, 'harga' => 3, 'wifi' => 4, 'jam' => 4, 'fotogenik' => 3, 'jarak' => 2, 'fasilitas' => 5, 'area' => 'Indoor, Outdoor'],
- ['nama' => '888', 'lokasi' => null, 'harga' => 2, 'wifi' => 5, 'jam' => 4, 'fotogenik' => 3, 'jarak' => 3, 'fasilitas' => 2, 'area' => 'Indoor, Semi Outdoor'],
- ['nama' => 'Sorai', 'lokasi' => null, 'harga' => 3, 'wifi' => 4, 'jam' => 4, 'fotogenik' => 3, 'jarak' => 3, 'fasilitas' => 3, 'area' => 'Indoor, Outdoor'],
- ['nama' => 'Tharuh', 'lokasi' => null, 'harga' => 3, 'wifi' => 2, 'jam' => 5, 'fotogenik' => 3, 'jarak' => 3, 'fasilitas' => 2, 'area' => 'Indoor, Semi Outdoor'],
- ['nama' => 'Contact', 'lokasi' => null, 'harga' => 3, 'wifi' => 2, 'jam' => 5, 'fotogenik' => 3, 'jarak' => 3, 'fasilitas' => 2, 'area' => 'Indoor, Semi Outdoor, Outdoor'],
- ['nama' => 'Cus Cus', 'lokasi' => null, 'harga' => 3, 'wifi' => 3, 'jam' => 4, 'fotogenik' => 4, 'jarak' => 3, 'fasilitas' => 2, 'area' => 'Indoor, Outdoor'],
- ['nama' => 'Grufi', 'lokasi' => null, 'harga' => 2, 'wifi' => 4, 'jam' => 4, 'fotogenik' => 5, 'jarak' => 3, 'fasilitas' => 2, 'area' => 'Indoor'],
- ['nama' => 'Tomoro', 'lokasi' => null, 'harga' => 3, 'wifi' => 4, 'jam' => 4, 'fotogenik' => 3, 'jarak' => 4, 'fasilitas' => 4, 'area' => 'Indoor'],
- ['nama' => 'Fore', 'lokasi' => null, 'harga' => 2, 'wifi' => 3, 'jam' => 4, 'fotogenik' => 3, 'jarak' => 3, 'fasilitas' => 2, 'area' => 'Indoor'],
- ['nama' => 'Fox', 'lokasi' => null, 'harga' => 3, 'wifi' => 3, 'jam' => 4, 'fotogenik' => 3, 'jarak' => 3, 'fasilitas' => 1, 'area' => 'Indoor'],
- ['nama' => 'Perasa', 'lokasi' => null, 'harga' => 2, 'wifi' => 3, 'jam' => 5, 'fotogenik' => 3, 'jarak' => 3, 'fasilitas' => 3, 'area' => 'Indoor'],
- ['nama' => 'Kopi Boss', 'lokasi' => null, 'harga' => 5, 'wifi' => 3, 'jam' => 5, 'fotogenik' => 1, 'jarak' => 3, 'fasilitas' => 3, 'area' => 'Semi Outdoor'],
- ['nama' => 'Navas', 'lokasi' => null, 'harga' => 3, 'wifi' => 4, 'jam' => 5, 'fotogenik' => 2, 'jarak' => 3, 'fasilitas' => 2, 'area' => 'Semi Outdoor'],
- ];
-
- // Get all categories and their subcategories
- $categories = Category::all();
-
- // Mendapatkan kategori berdasarkan namanya untuk memudahkan penugasan subcategory
- $hargaCategory = Category::where('name', 'Harga')->first();
- $wifiCategory = Category::where('name', 'Kecepatan WiFi')->first();
- $jamOperasionalCategory = Category::where('name', 'Jam Operasional')->first();
- $fotogenikCategory = Category::where('name', 'Fotogenik')->first();
- $areaCategory = Category::where('name', 'Area (Outdoor/Indoor)')->first();
- $jarakCategory = Category::where('name', 'Jarak dengan Kampus')->first();
- $fasilitasCategory = Category::where('name', 'Fasilitas')->first();
-
- // Membuat cafe dan ratings
- foreach ($cafes as $cafeData) {
- // Buat cafe dengan data dasar
- $cafe = Cafe::create([
- 'nama' => $cafeData['nama'],
- 'lokasi' => $cafeData['lokasi'],
- 'area' => $cafeData['area'],
- ]);
-
- // Assign ratings berdasarkan data yang sudah ditentukan
- // Harga
- if ($hargaCategory) {
- $this->createRating($cafe->id, $hargaCategory->id, $cafeData['harga']);
- }
-
- // WiFi
- if ($wifiCategory) {
- $this->createRating($cafe->id, $wifiCategory->id, $cafeData['wifi']);
- }
-
- // Jam Operasional
- if ($jamOperasionalCategory) {
- $this->createRating($cafe->id, $jamOperasionalCategory->id, $cafeData['jam']);
- }
-
- // Fotogenik
- if ($fotogenikCategory) {
- $this->createRating($cafe->id, $fotogenikCategory->id, $cafeData['fotogenik']);
- }
-
- // Jarak
- if ($jarakCategory) {
- $this->createRating($cafe->id, $jarakCategory->id, $cafeData['jarak']);
- }
-
- // Fasilitas
- if ($fasilitasCategory) {
- $this->createRating($cafe->id, $fasilitasCategory->id, $cafeData['fasilitas']);
- }
- }
- }
-
- /**
- * Membuat rating berdasarkan category_id dan nilai
- */
- private function createRating($cafeId, $categoryId, $value)
- {
- // Cari subcategory yang sesuai dengan category_id dan value
- $subcategory = Subcategory::where('category_id', $categoryId)
- ->where('value', $value)
- ->first();
-
- if ($subcategory) {
- CafeRating::create([
- 'cafe_id' => $cafeId,
- 'category_id' => $categoryId,
- 'subcategory_id' => $subcategory->id
- ]);
- }
- }
+ 'Poppins', 'lokasi' => null, 'harga' => 3, 'wifi' => 3, 'jam' => 5, 'fotogenik' => 4, 'jarak' => 3, 'fasilitas' => 3, 'area' => 'Indoor, Outdoor'],
+ ['nama' => 'Teras JTI', 'lokasi' => null, 'harga' => 4, 'wifi' => 5, 'jam' => 3, 'fotogenik' => 3, 'jarak' => 5, 'fasilitas' => 4, 'area' => 'Indoor, Semi Outdoor, Outdoor'],
+ ['nama' => 'Nugas Jember', 'lokasi' => null, 'harga' => 4, 'wifi' => 3, 'jam' => 4, 'fotogenik' => 1, 'jarak' => 4, 'fasilitas' => 1, 'area' => 'Semi Outdoor'],
+ ['nama' => 'Kopi Kampus', 'lokasi' => null, 'harga' => 4, 'wifi' => 3, 'jam' => 5, 'fotogenik' => 1, 'jarak' => 3, 'fasilitas' => 3, 'area' => 'Semi Outdoor'],
+ ['nama' => 'Eterno', 'lokasi' => null, 'harga' => 2, 'wifi' => 4, 'jam' => 4, 'fotogenik' => 4, 'jarak' => 3, 'fasilitas' => 5, 'area' => 'Indoor, Outdoor'],
+ ['nama' => 'Kattappa', 'lokasi' => null, 'harga' => 3, 'wifi' => 3, 'jam' => 4, 'fotogenik' => 4, 'jarak' => 3, 'fasilitas' => 4, 'area' => 'Indoor, Semi Outdoor, Outdoor'],
+ ['nama' => 'Fifty-Fifty', 'lokasi' => null, 'harga' => 4, 'wifi' => 3, 'jam' => 4, 'fotogenik' => 2, 'jarak' => 3, 'fasilitas' => 2, 'area' => 'Semi Outdoor'],
+ ['nama' => 'Discuss Space & Coffee', 'lokasi' => null, 'harga' => 3, 'wifi' => 4, 'jam' => 4, 'fotogenik' => 3, 'jarak' => 3, 'fasilitas' => 4, 'area' => 'Indoor'],
+ ['nama' => 'Subur', 'lokasi' => null, 'harga' => 3, 'wifi' => 4, 'jam' => 5, 'fotogenik' => 3, 'jarak' => 3, 'fasilitas' => 2, 'area' => 'Indoor'],
+ ['nama' => 'Nuansa', 'lokasi' => null, 'harga' => 4, 'wifi' => 3, 'jam' => 4, 'fotogenik' => 2, 'jarak' => 3, 'fasilitas' => 5, 'area' => 'Indoor, Semi Outdoor'],
+ ['nama' => 'Nol Kilometer', 'lokasi' => null, 'harga' => 5, 'wifi' => 2, 'jam' => 3, 'fotogenik' => 1, 'jarak' => 3, 'fasilitas' => 2, 'area' => 'Semi Outdoor'],
+ ['nama' => 'Tanaloka', 'lokasi' => null, 'harga' => 2, 'wifi' => 4, 'jam' => 2, 'fotogenik' => 5, 'jarak' => 3, 'fasilitas' => 5, 'area' => 'Indoor, Semi Outdoor, Outdoor'],
+ ['nama' => 'Wafa', 'lokasi' => null, 'harga' => 3, 'wifi' => 4, 'jam' => 4, 'fotogenik' => 3, 'jarak' => 2, 'fasilitas' => 5, 'area' => 'Indoor, Outdoor'],
+ ['nama' => '888', 'lokasi' => null, 'harga' => 2, 'wifi' => 5, 'jam' => 4, 'fotogenik' => 3, 'jarak' => 3, 'fasilitas' => 2, 'area' => 'Indoor, Semi Outdoor'],
+ ['nama' => 'Sorai', 'lokasi' => null, 'harga' => 3, 'wifi' => 4, 'jam' => 4, 'fotogenik' => 3, 'jarak' => 3, 'fasilitas' => 3, 'area' => 'Indoor, Outdoor'],
+ ['nama' => 'Tharuh', 'lokasi' => null, 'harga' => 3, 'wifi' => 2, 'jam' => 5, 'fotogenik' => 3, 'jarak' => 3, 'fasilitas' => 2, 'area' => 'Indoor, Semi Outdoor'],
+ ['nama' => 'Contact', 'lokasi' => null, 'harga' => 3, 'wifi' => 2, 'jam' => 5, 'fotogenik' => 3, 'jarak' => 3, 'fasilitas' => 2, 'area' => 'Indoor, Semi Outdoor, Outdoor'],
+ ['nama' => 'Cus Cus', 'lokasi' => null, 'harga' => 3, 'wifi' => 3, 'jam' => 4, 'fotogenik' => 4, 'jarak' => 3, 'fasilitas' => 2, 'area' => 'Indoor, Outdoor'],
+ ['nama' => 'Grufi', 'lokasi' => null, 'harga' => 2, 'wifi' => 4, 'jam' => 4, 'fotogenik' => 5, 'jarak' => 3, 'fasilitas' => 2, 'area' => 'Indoor'],
+ ['nama' => 'Tomoro', 'lokasi' => null, 'harga' => 3, 'wifi' => 4, 'jam' => 4, 'fotogenik' => 3, 'jarak' => 4, 'fasilitas' => 4, 'area' => 'Indoor'],
+ ['nama' => 'Fore', 'lokasi' => null, 'harga' => 2, 'wifi' => 3, 'jam' => 4, 'fotogenik' => 3, 'jarak' => 3, 'fasilitas' => 2, 'area' => 'Indoor'],
+ ['nama' => 'Fox', 'lokasi' => null, 'harga' => 3, 'wifi' => 3, 'jam' => 4, 'fotogenik' => 3, 'jarak' => 3, 'fasilitas' => 1, 'area' => 'Indoor'],
+ ['nama' => 'Perasa', 'lokasi' => null, 'harga' => 2, 'wifi' => 3, 'jam' => 5, 'fotogenik' => 3, 'jarak' => 3, 'fasilitas' => 3, 'area' => 'Indoor'],
+ ['nama' => 'Kopi Boss', 'lokasi' => null, 'harga' => 5, 'wifi' => 3, 'jam' => 5, 'fotogenik' => 1, 'jarak' => 3, 'fasilitas' => 3, 'area' => 'Semi Outdoor'],
+ ['nama' => 'Navas', 'lokasi' => null, 'harga' => 3, 'wifi' => 4, 'jam' => 5, 'fotogenik' => 2, 'jarak' => 3, 'fasilitas' => 2, 'area' => 'Semi Outdoor'],
+ ];
+
+ // Get all categories and their subcategories
+ $categories = Category::all();
+
+ // Mendapatkan kategori berdasarkan namanya untuk memudahkan penugasan subcategory
+ $hargaCategory = Category::where('name', 'Harga')->first();
+ $wifiCategory = Category::where('name', 'Kecepatan WiFi')->first();
+ $jamOperasionalCategory = Category::where('name', 'Jam Operasional')->first();
+ $fotogenikCategory = Category::where('name', 'Fotogenik')->first();
+ $areaCategory = Category::where('name', 'Area (Outdoor/Indoor)')->first();
+ $jarakCategory = Category::where('name', 'Jarak dengan Kampus')->first();
+ $fasilitasCategory = Category::where('name', 'Fasilitas')->first();
+
+ // Membuat cafe dan ratings
+ foreach ($cafes as $cafeData) {
+ // Buat cafe dengan data dasar
+ $cafe = Cafe::create([
+ 'nama' => $cafeData['nama'],
+ 'lokasi' => $cafeData['lokasi'],
+ 'area' => $cafeData['area'],
+ ]);
+
+ // Assign ratings berdasarkan data yang sudah ditentukan
+ // Harga
+ if ($hargaCategory) {
+ $this->createRating($cafe->id, $hargaCategory->id, $cafeData['harga']);
+ }
+
+ // WiFi
+ if ($wifiCategory) {
+ $this->createRating($cafe->id, $wifiCategory->id, $cafeData['wifi']);
+ }
+
+ // Jam Operasional
+ if ($jamOperasionalCategory) {
+ $this->createRating($cafe->id, $jamOperasionalCategory->id, $cafeData['jam']);
+ }
+
+ // Fotogenik
+ if ($fotogenikCategory) {
+ $this->createRating($cafe->id, $fotogenikCategory->id, $cafeData['fotogenik']);
+ }
+
+ // Jarak
+ if ($jarakCategory) {
+ $this->createRating($cafe->id, $jarakCategory->id, $cafeData['jarak']);
+ }
+
+ // Fasilitas
+ if ($fasilitasCategory) {
+ $this->createRating($cafe->id, $fasilitasCategory->id, $cafeData['fasilitas']);
+ }
+ }
+ }
+
+ /**
+ * Membuat rating berdasarkan category_id dan nilai
+ */
+ private function createRating($cafeId, $categoryId, $value)
+ {
+ // Cari subcategory yang sesuai dengan category_id dan value
+ $subcategory = Subcategory::where('category_id', $categoryId)
+ ->where('value', $value)
+ ->first();
+
+ if ($subcategory) {
+ CafeRating::create([
+ 'cafe_id' => $cafeId,
+ 'category_id' => $categoryId,
+ 'subcategory_id' => $subcategory->id
+ ]);
+ }
+ }
}
\ No newline at end of file
diff --git a/database/seeders/CafePriceSeeder.php b/database/seeders/CafePriceSeeder.php
index 64f5aef..f86afc8 100644
--- a/database/seeders/CafePriceSeeder.php
+++ b/database/seeders/CafePriceSeeder.php
@@ -1,61 +1,61 @@
- ['harga_termurah' => 18000, 'harga_termahal' => 25000], // Poppins
- 2 => ['harga_termurah' => 10000, 'harga_termahal' => 18000], // Teras JTI
- 3 => ['harga_termurah' => 10000, 'harga_termahal' => 20000], // Nugas Jember
- 4 => ['harga_termurah' => 4000, 'harga_termahal' => 18000], // Kopi Kampus
- 5 => ['harga_termurah' => 15000, 'harga_termahal' => 40000], // Eterno
- 7 => ['harga_termurah' => 8000, 'harga_termahal' => 20000], // Fifty-Fifty
- 8 => ['harga_termurah' => 10000, 'harga_termahal' => 35000], // Discuss Space & Coffee
- 9 => ['harga_termurah' => 12000, 'harga_termahal' => 34000], // Subur
- 10 => ['harga_termurah' => 6000, 'harga_termahal' => 15000], // Nuansa
- 11 => ['harga_termurah' => 6000, 'harga_termahal' => 8000], // Nol Kilometer
- 13 => ['harga_termurah' => 6000, 'harga_termahal' => 25000], // Wafa
- 14 => ['harga_termurah' => 15000, 'harga_termahal' => 45000], // 888
- 15 => ['harga_termurah' => 7000, 'harga_termahal' => 25000], // Sorai
- 16 => ['harga_termurah' => 8000, 'harga_termahal' => 23000], // Tharuh
- 17 => ['harga_termurah' => 12000, 'harga_termahal' => 38000], // Contact
- 18 => ['harga_termurah' => 14000, 'harga_termahal' => 20000], // Cus Cus
- 19 => ['harga_termurah' => 18000, 'harga_termahal' => 34000], // Grufi
- 20 => ['harga_termurah' => 15000, 'harga_termahal' => 25000], // Tomoro
- 21 => ['harga_termurah' => 22000, 'harga_termahal' => 40000], // Fore
- 22 => ['harga_termurah' => 1500, 'harga_termahal' => 37000], // Fox
- 23 => ['harga_termurah' => 12000, 'harga_termahal' => 45000], // Perasa
- 24 => ['harga_termurah' => 5000, 'harga_termahal' => 15000], // Kopi Boss
- 25 => ['harga_termurah' => 10000, 'harga_termahal' => 30000], // Navas
- 26 => ['harga_termurah' => 15000, 'harga_termahal' => 35000], // Kattappa (ID 26)
- 28 => ['harga_termurah' => 17000, 'harga_termahal' => 47000], // Tanaloka (ID 28)
- ];
-
- // Update harga untuk setiap cafe
- foreach ($priceData as $cafeId => $prices) {
- $cafe = Cafe::find($cafeId);
-
- if ($cafe) {
- $cafe->update([
- 'harga_termurah' => $prices['harga_termurah'],
- 'harga_termahal' => $prices['harga_termahal']
- ]);
-
- echo "Updated cafe ID {$cafeId} ({$cafe->nama}): {$prices['harga_termurah']} - {$prices['harga_termahal']}\n";
- } else {
- echo "Cafe with ID {$cafeId} not found\n";
- }
- }
- }
-}
+ ['harga_termurah' => 18000, 'harga_termahal' => 25000], // Poppins
+ 2 => ['harga_termurah' => 10000, 'harga_termahal' => 18000], // Teras JTI
+ 3 => ['harga_termurah' => 10000, 'harga_termahal' => 20000], // Nugas Jember
+ 4 => ['harga_termurah' => 4000, 'harga_termahal' => 18000], // Kopi Kampus
+ 5 => ['harga_termurah' => 15000, 'harga_termahal' => 40000], // Eterno
+ 7 => ['harga_termurah' => 8000, 'harga_termahal' => 20000], // Fifty-Fifty
+ 8 => ['harga_termurah' => 10000, 'harga_termahal' => 35000], // Discuss Space & Coffee
+ 9 => ['harga_termurah' => 12000, 'harga_termahal' => 34000], // Subur
+ 10 => ['harga_termurah' => 6000, 'harga_termahal' => 15000], // Nuansa
+ 11 => ['harga_termurah' => 6000, 'harga_termahal' => 8000], // Nol Kilometer
+ 13 => ['harga_termurah' => 6000, 'harga_termahal' => 25000], // Wafa
+ 14 => ['harga_termurah' => 15000, 'harga_termahal' => 45000], // 888
+ 15 => ['harga_termurah' => 7000, 'harga_termahal' => 25000], // Sorai
+ 16 => ['harga_termurah' => 8000, 'harga_termahal' => 23000], // Tharuh
+ 17 => ['harga_termurah' => 12000, 'harga_termahal' => 38000], // Contact
+ 18 => ['harga_termurah' => 14000, 'harga_termahal' => 20000], // Cus Cus
+ 19 => ['harga_termurah' => 18000, 'harga_termahal' => 34000], // Grufi
+ 20 => ['harga_termurah' => 15000, 'harga_termahal' => 25000], // Tomoro
+ 21 => ['harga_termurah' => 22000, 'harga_termahal' => 40000], // Fore
+ 22 => ['harga_termurah' => 1500, 'harga_termahal' => 37000], // Fox
+ 23 => ['harga_termurah' => 12000, 'harga_termahal' => 45000], // Perasa
+ 24 => ['harga_termurah' => 5000, 'harga_termahal' => 15000], // Kopi Boss
+ 25 => ['harga_termurah' => 10000, 'harga_termahal' => 30000], // Navas
+ 26 => ['harga_termurah' => 15000, 'harga_termahal' => 35000], // Kattappa (ID 26)
+ 28 => ['harga_termurah' => 17000, 'harga_termahal' => 47000], // Tanaloka (ID 28)
+ ];
+
+ // Update harga untuk setiap cafe
+ foreach ($priceData as $cafeId => $prices) {
+ $cafe = Cafe::find($cafeId);
+
+ if ($cafe) {
+ $cafe->update([
+ 'harga_termurah' => $prices['harga_termurah'],
+ 'harga_termahal' => $prices['harga_termahal']
+ ]);
+
+ echo "Updated cafe ID {$cafeId} ({$cafe->nama}): {$prices['harga_termurah']} - {$prices['harga_termahal']}\n";
+ } else {
+ echo "Cafe with ID {$cafeId} not found\n";
+ }
+ }
+ }
+}
diff --git a/database/seeders/CategoryAndSubcategorySeeder.php b/database/seeders/CategoryAndSubcategorySeeder.php
index b8ff197..1fde31e 100644
--- a/database/seeders/CategoryAndSubcategorySeeder.php
+++ b/database/seeders/CategoryAndSubcategorySeeder.php
@@ -1,236 +1,236 @@
- 'Harga'
- ]);
-
- // Subkategori Harga
- Subcategory::create([
- 'category_id' => $harga->id,
- 'name' => 'Sangat Murah (<10k/porsi)',
- 'value' => 5
- ]);
- Subcategory::create([
- 'category_id' => $harga->id,
- 'name' => 'Murah (10k–15k)',
- 'value' => 4
- ]);
- Subcategory::create([
- 'category_id' => $harga->id,
- 'name' => 'Sedang (15k–25k)',
- 'value' => 3
- ]);
- Subcategory::create([
- 'category_id' => $harga->id,
- 'name' => 'Agak Mahal (25k–35k)',
- 'value' => 2
- ]);
- Subcategory::create([
- 'category_id' => $harga->id,
- 'name' => 'Mahal (>35k)',
- 'value' => 1
- ]);
-
- // Kategori 2: Kecepatan WiFi
- $wifi = Category::create([
- 'name' => 'Kecepatan WiFi'
- ]);
-
- // Subkategori Kecepatan WiFi
- Subcategory::create([
- 'category_id' => $wifi->id,
- 'name' => 'Sangat Cepat (>50 Mbps)',
- 'value' => 5
- ]);
- Subcategory::create([
- 'category_id' => $wifi->id,
- 'name' => 'Cepat (30–50 Mbps)',
- 'value' => 4
- ]);
- Subcategory::create([
- 'category_id' => $wifi->id,
- 'name' => 'Sedang (15–30 Mbps)',
- 'value' => 3
- ]);
- Subcategory::create([
- 'category_id' => $wifi->id,
- 'name' => 'Lambat (5–15 Mbps)',
- 'value' => 2
- ]);
- Subcategory::create([
- 'category_id' => $wifi->id,
- 'name' => 'Sangat Lambat (<5 Mbps)',
- 'value' => 1
- ]);
-
- // Kategori 3: Jam Operasional
- $jamOperasional = Category::create([
- 'name' => 'Jam Operasional'
- ]);
-
- // Subkategori Jam Operasional
- Subcategory::create([
- 'category_id' => $jamOperasional->id,
- 'name' => '24 Jam',
- 'value' => 5
- ]);
- Subcategory::create([
- 'category_id' => $jamOperasional->id,
- 'name' => '08.00–00.00 (16 jam)',
- 'value' => 4
- ]);
- Subcategory::create([
- 'category_id' => $jamOperasional->id,
- 'name' => '10.00–22.00 (12 jam)',
- 'value' => 3
- ]);
- Subcategory::create([
- 'category_id' => $jamOperasional->id,
- 'name' => '<10 jam operasional',
- 'value' => 2
- ]);
- Subcategory::create([
- 'category_id' => $jamOperasional->id,
- 'name' => 'Tidak konsisten / Tidak tetap',
- 'value' => 1
- ]);
-
- // Kategori 4: Fotogenik
- $fotogenik = Category::create([
- 'name' => 'Fotogenik'
- ]);
-
- // Subkategori Fotogenik
- Subcategory::create([
- 'category_id' => $fotogenik->id,
- 'name' => 'Sangat Fotogenik (Desain unik, banyak spot foto)',
- 'value' => 5
- ]);
- Subcategory::create([
- 'category_id' => $fotogenik->id,
- 'name' => 'Fotogenik (Desain bagus, cukup spot foto)',
- 'value' => 4
- ]);
- Subcategory::create([
- 'category_id' => $fotogenik->id,
- 'name' => 'Cukup Fotogenik (estetis biasa saja)',
- 'value' => 3
- ]);
- Subcategory::create([
- 'category_id' => $fotogenik->id,
- 'name' => 'Kurang Fotogenik',
- 'value' => 2
- ]);
- Subcategory::create([
- 'category_id' => $fotogenik->id,
- 'name' => 'Tidak Fotogenik',
- 'value' => 1
- ]);
-
- // Kategori 5: Area (Luas Tempat)
- $area = Category::create([
- 'name' => 'Area (Outdoor/Indoor)'
- ]);
-
- Subcategory::create([
- 'category_id' => $area->id,
- 'name' => 'Outdoor & Indoor',
- 'value' => 3
- ]);
- Subcategory::create([
- 'category_id' => $area->id,
- 'name' => 'Indoor',
- 'value' => 2
- ]);
- Subcategory::create([
- 'category_id' => $area->id,
- 'name' => 'Outdoor',
- 'value' => 1
- ]);
-
- // Kategori 6: Jarak dengan Kampus
- $jarak = Category::create([
- 'name' => 'Jarak dengan Kampus'
- ]);
-
- // Subkategori Jarak dengan Kampus
- Subcategory::create([
- 'category_id' => $jarak->id,
- 'name' => 'Sangat Dekat (≤1 Km)',
- 'value' => 5
- ]);
- Subcategory::create([
- 'category_id' => $jarak->id,
- 'name' => 'Dekat (1–2 Km)',
- 'value' => 4
- ]);
- Subcategory::create([
- 'category_id' => $jarak->id,
- 'name' => 'Sedang (2–5 Km)',
- 'value' => 3
- ]);
- Subcategory::create([
- 'category_id' => $jarak->id,
- 'name' => 'Jauh (5–10 Km)',
- 'value' => 2
- ]);
- Subcategory::create([
- 'category_id' => $jarak->id,
- 'name' => 'Sangat Jauh (>10 Km)',
- 'value' => 1
- ]);
-
- // Kategori 7: Fasilitas
- $fasilitas = Category::create([
- 'name' => 'Fasilitas'
- ]);
-
- // Subkategori Fasilitas
- Subcategory::create([
- 'category_id' => $fasilitas->id,
- 'name' => 'Sangat Lengkap (5 fasilitas)',
- 'value' => 5
- ]);
- Subcategory::create([
- 'category_id' => $fasilitas->id,
- 'name' => 'Lengkap (4 fasilitas)',
- 'value' => 4
- ]);
- Subcategory::create([
- 'category_id' => $fasilitas->id,
- 'name' => 'Cukup (3 fasilitas)',
- 'value' => 3
- ]);
- Subcategory::create([
- 'category_id' => $fasilitas->id,
- 'name' => 'Kurang (2 fasilitas)',
- 'value' => 2
- ]);
- Subcategory::create([
- 'category_id' => $fasilitas->id,
- 'name' => 'Minim / Tidak Lengkap (0–1 fasilitas)',
- 'value' => 1
- ]);
- }
+ 'Harga'
+ ]);
+
+ // Subkategori Harga
+ Subcategory::create([
+ 'category_id' => $harga->id,
+ 'name' => 'Sangat Murah (<10k/porsi)',
+ 'value' => 5
+ ]);
+ Subcategory::create([
+ 'category_id' => $harga->id,
+ 'name' => 'Murah (10k–15k)',
+ 'value' => 4
+ ]);
+ Subcategory::create([
+ 'category_id' => $harga->id,
+ 'name' => 'Sedang (15k–25k)',
+ 'value' => 3
+ ]);
+ Subcategory::create([
+ 'category_id' => $harga->id,
+ 'name' => 'Agak Mahal (25k–35k)',
+ 'value' => 2
+ ]);
+ Subcategory::create([
+ 'category_id' => $harga->id,
+ 'name' => 'Mahal (>35k)',
+ 'value' => 1
+ ]);
+
+ // Kategori 2: Kecepatan WiFi
+ $wifi = Category::create([
+ 'name' => 'Kecepatan WiFi'
+ ]);
+
+ // Subkategori Kecepatan WiFi
+ Subcategory::create([
+ 'category_id' => $wifi->id,
+ 'name' => 'Sangat Cepat (>50 Mbps)',
+ 'value' => 5
+ ]);
+ Subcategory::create([
+ 'category_id' => $wifi->id,
+ 'name' => 'Cepat (30–50 Mbps)',
+ 'value' => 4
+ ]);
+ Subcategory::create([
+ 'category_id' => $wifi->id,
+ 'name' => 'Sedang (15–30 Mbps)',
+ 'value' => 3
+ ]);
+ Subcategory::create([
+ 'category_id' => $wifi->id,
+ 'name' => 'Lambat (5–15 Mbps)',
+ 'value' => 2
+ ]);
+ Subcategory::create([
+ 'category_id' => $wifi->id,
+ 'name' => 'Sangat Lambat (<5 Mbps)',
+ 'value' => 1
+ ]);
+
+ // Kategori 3: Jam Operasional
+ $jamOperasional = Category::create([
+ 'name' => 'Jam Operasional'
+ ]);
+
+ // Subkategori Jam Operasional
+ Subcategory::create([
+ 'category_id' => $jamOperasional->id,
+ 'name' => '24 Jam',
+ 'value' => 5
+ ]);
+ Subcategory::create([
+ 'category_id' => $jamOperasional->id,
+ 'name' => '08.00–00.00 (16 jam)',
+ 'value' => 4
+ ]);
+ Subcategory::create([
+ 'category_id' => $jamOperasional->id,
+ 'name' => '10.00–22.00 (12 jam)',
+ 'value' => 3
+ ]);
+ Subcategory::create([
+ 'category_id' => $jamOperasional->id,
+ 'name' => '<10 jam operasional',
+ 'value' => 2
+ ]);
+ Subcategory::create([
+ 'category_id' => $jamOperasional->id,
+ 'name' => 'Tidak konsisten / Tidak tetap',
+ 'value' => 1
+ ]);
+
+ // Kategori 4: Fotogenik
+ $fotogenik = Category::create([
+ 'name' => 'Fotogenik'
+ ]);
+
+ // Subkategori Fotogenik
+ Subcategory::create([
+ 'category_id' => $fotogenik->id,
+ 'name' => 'Sangat Fotogenik (Desain unik, banyak spot foto)',
+ 'value' => 5
+ ]);
+ Subcategory::create([
+ 'category_id' => $fotogenik->id,
+ 'name' => 'Fotogenik (Desain bagus, cukup spot foto)',
+ 'value' => 4
+ ]);
+ Subcategory::create([
+ 'category_id' => $fotogenik->id,
+ 'name' => 'Cukup Fotogenik (estetis biasa saja)',
+ 'value' => 3
+ ]);
+ Subcategory::create([
+ 'category_id' => $fotogenik->id,
+ 'name' => 'Kurang Fotogenik',
+ 'value' => 2
+ ]);
+ Subcategory::create([
+ 'category_id' => $fotogenik->id,
+ 'name' => 'Tidak Fotogenik',
+ 'value' => 1
+ ]);
+
+ // Kategori 5: Area (Luas Tempat)
+ $area = Category::create([
+ 'name' => 'Area (Outdoor/Indoor)'
+ ]);
+
+ Subcategory::create([
+ 'category_id' => $area->id,
+ 'name' => 'Outdoor & Indoor',
+ 'value' => 3
+ ]);
+ Subcategory::create([
+ 'category_id' => $area->id,
+ 'name' => 'Indoor',
+ 'value' => 2
+ ]);
+ Subcategory::create([
+ 'category_id' => $area->id,
+ 'name' => 'Outdoor',
+ 'value' => 1
+ ]);
+
+ // Kategori 6: Jarak dengan Kampus
+ $jarak = Category::create([
+ 'name' => 'Jarak dengan Kampus'
+ ]);
+
+ // Subkategori Jarak dengan Kampus
+ Subcategory::create([
+ 'category_id' => $jarak->id,
+ 'name' => 'Sangat Dekat (≤1 Km)',
+ 'value' => 5
+ ]);
+ Subcategory::create([
+ 'category_id' => $jarak->id,
+ 'name' => 'Dekat (1–2 Km)',
+ 'value' => 4
+ ]);
+ Subcategory::create([
+ 'category_id' => $jarak->id,
+ 'name' => 'Sedang (2–5 Km)',
+ 'value' => 3
+ ]);
+ Subcategory::create([
+ 'category_id' => $jarak->id,
+ 'name' => 'Jauh (5–10 Km)',
+ 'value' => 2
+ ]);
+ Subcategory::create([
+ 'category_id' => $jarak->id,
+ 'name' => 'Sangat Jauh (>10 Km)',
+ 'value' => 1
+ ]);
+
+ // Kategori 7: Fasilitas
+ $fasilitas = Category::create([
+ 'name' => 'Fasilitas'
+ ]);
+
+ // Subkategori Fasilitas
+ Subcategory::create([
+ 'category_id' => $fasilitas->id,
+ 'name' => 'Sangat Lengkap (5 fasilitas)',
+ 'value' => 5
+ ]);
+ Subcategory::create([
+ 'category_id' => $fasilitas->id,
+ 'name' => 'Lengkap (4 fasilitas)',
+ 'value' => 4
+ ]);
+ Subcategory::create([
+ 'category_id' => $fasilitas->id,
+ 'name' => 'Cukup (3 fasilitas)',
+ 'value' => 3
+ ]);
+ Subcategory::create([
+ 'category_id' => $fasilitas->id,
+ 'name' => 'Kurang (2 fasilitas)',
+ 'value' => 2
+ ]);
+ Subcategory::create([
+ 'category_id' => $fasilitas->id,
+ 'name' => 'Minim / Tidak Lengkap (0–1 fasilitas)',
+ 'value' => 1
+ ]);
+ }
}
\ No newline at end of file
diff --git a/database/seeders/CategorySeeder.php b/database/seeders/CategorySeeder.php
index 8c37b61..6ad1667 100644
--- a/database/seeders/CategorySeeder.php
+++ b/database/seeders/CategorySeeder.php
@@ -1,87 +1,87 @@
-truncate();
-
- // Data kategori dengan subkategori
- $categories = [
- [
- 'name' => 'Jarak Kampus',
- 'subcategory1' => 'Sangat Dekat',
- 'subcategory2' => 'Dekat',
- 'subcategory3' => 'Sedang',
- 'subcategory4' => 'Jauh',
- 'subcategory5' => 'Sangat Jauh',
- 'value1' => 5,
- 'value2' => 4,
- 'value3' => 3,
- 'value4' => 2,
- 'value5' => 1,
- 'created_at' => now(),
- 'updated_at' => now()
- ],
- [
- 'name' => 'Kisaran Harga',
- 'subcategory1' => 'Sangat Murah',
- 'subcategory2' => 'Murah',
- 'subcategory3' => 'Sedang',
- 'subcategory4' => 'Mahal',
- 'subcategory5' => 'Sangat Mahal',
- 'value1' => 5,
- 'value2' => 4,
- 'value3' => 3,
- 'value4' => 2,
- 'value5' => 1,
- 'created_at' => now(),
- 'updated_at' => now()
- ],
- [
- 'name' => 'Fasilitas',
- 'subcategory1' => 'Sangat Lengkap',
- 'subcategory2' => 'Lengkap',
- 'subcategory3' => 'Cukup',
- 'subcategory4' => 'Kurang',
- 'subcategory5' => 'Sangat Kurang',
- 'value1' => 5,
- 'value2' => 4,
- 'value3' => 3,
- 'value4' => 2,
- 'value5' => 1,
- 'created_at' => now(),
- 'updated_at' => now()
- ],
- [
- 'name' => 'Kecepatan WiFi',
- 'subcategory1' => 'Sangat Cepat',
- 'subcategory2' => 'Cepat',
- 'subcategory3' => 'Sedang',
- 'subcategory4' => 'Lambat',
- 'subcategory5' => 'Sangat Lambat',
- 'value1' => 5,
- 'value2' => 4,
- 'value3' => 3,
- 'value4' => 2,
- 'value5' => 1,
- 'created_at' => now(),
- 'updated_at' => now()
- ]
- ];
-
- // Insert data kategori
- DB::table('categories')->insert($categories);
-
- $this->command->info('Categories seeded successfully!');
- }
+truncate();
+
+ // Data kategori dengan subkategori
+ $categories = [
+ [
+ 'name' => 'Jarak Kampus',
+ 'subcategory1' => 'Sangat Dekat',
+ 'subcategory2' => 'Dekat',
+ 'subcategory3' => 'Sedang',
+ 'subcategory4' => 'Jauh',
+ 'subcategory5' => 'Sangat Jauh',
+ 'value1' => 5,
+ 'value2' => 4,
+ 'value3' => 3,
+ 'value4' => 2,
+ 'value5' => 1,
+ 'created_at' => now(),
+ 'updated_at' => now()
+ ],
+ [
+ 'name' => 'Kisaran Harga',
+ 'subcategory1' => 'Sangat Murah',
+ 'subcategory2' => 'Murah',
+ 'subcategory3' => 'Sedang',
+ 'subcategory4' => 'Mahal',
+ 'subcategory5' => 'Sangat Mahal',
+ 'value1' => 5,
+ 'value2' => 4,
+ 'value3' => 3,
+ 'value4' => 2,
+ 'value5' => 1,
+ 'created_at' => now(),
+ 'updated_at' => now()
+ ],
+ [
+ 'name' => 'Fasilitas',
+ 'subcategory1' => 'Sangat Lengkap',
+ 'subcategory2' => 'Lengkap',
+ 'subcategory3' => 'Cukup',
+ 'subcategory4' => 'Kurang',
+ 'subcategory5' => 'Sangat Kurang',
+ 'value1' => 5,
+ 'value2' => 4,
+ 'value3' => 3,
+ 'value4' => 2,
+ 'value5' => 1,
+ 'created_at' => now(),
+ 'updated_at' => now()
+ ],
+ [
+ 'name' => 'Kecepatan WiFi',
+ 'subcategory1' => 'Sangat Cepat',
+ 'subcategory2' => 'Cepat',
+ 'subcategory3' => 'Sedang',
+ 'subcategory4' => 'Lambat',
+ 'subcategory5' => 'Sangat Lambat',
+ 'value1' => 5,
+ 'value2' => 4,
+ 'value3' => 3,
+ 'value4' => 2,
+ 'value5' => 1,
+ 'created_at' => now(),
+ 'updated_at' => now()
+ ]
+ ];
+
+ // Insert data kategori
+ DB::table('categories')->insert($categories);
+
+ $this->command->info('Categories seeded successfully!');
+ }
}
\ No newline at end of file
diff --git a/database/seeders/SubcategorySeeder.php b/database/seeders/SubcategorySeeder.php
index 6ffd51c..421204c 100644
--- a/database/seeders/SubcategorySeeder.php
+++ b/database/seeders/SubcategorySeeder.php
@@ -1,66 +1,66 @@
-truncate();
-
- // Data subkategori berdasarkan kategori
- $subcategories = [
- 'Jarak Kampus' => [
- 'Sangat Dekat (< 1 km)',
- 'Dekat (1-2 km)',
- 'Sedang (2-3 km)',
- 'Jauh (> 3 km)'
- ],
- 'Kisaran Harga' => [
- 'Sangat Murah (< Rp 10.000)',
- 'Murah (Rp 10.000 - Rp 20.000)',
- 'Sedang (Rp 20.000 - Rp 30.000)',
- 'Mahal (> Rp 30.000)'
- ],
- 'Fasilitas' => [
- 'Sangat Lengkap',
- 'Lengkap',
- 'Cukup',
- 'Minimal'
- ],
- 'Kecepatan WiFi' => [
- 'Sangat Cepat (> 50 Mbps)',
- 'Cepat (30-50 Mbps)',
- 'Sedang (10-30 Mbps)',
- 'Lambat (< 10 Mbps)'
- ]
- ];
-
- // Insert data subkategori
- foreach ($subcategories as $categoryName => $items) {
- $category = Category::where('name', $categoryName)->first();
-
- if ($category) {
- foreach ($items as $subcategoryName) {
- Subcategory::create([
- 'category_id' => $category->id,
- 'name' => $subcategoryName,
- 'created_at' => now(),
- 'updated_at' => now(),
- ]);
- }
- }
- }
-
- $this->command->info('Subcategories seeded successfully!');
- }
+truncate();
+
+ // Data subkategori berdasarkan kategori
+ $subcategories = [
+ 'Jarak Kampus' => [
+ 'Sangat Dekat (< 1 km)',
+ 'Dekat (1-2 km)',
+ 'Sedang (2-3 km)',
+ 'Jauh (> 3 km)'
+ ],
+ 'Kisaran Harga' => [
+ 'Sangat Murah (< Rp 10.000)',
+ 'Murah (Rp 10.000 - Rp 20.000)',
+ 'Sedang (Rp 20.000 - Rp 30.000)',
+ 'Mahal (> Rp 30.000)'
+ ],
+ 'Fasilitas' => [
+ 'Sangat Lengkap',
+ 'Lengkap',
+ 'Cukup',
+ 'Minimal'
+ ],
+ 'Kecepatan WiFi' => [
+ 'Sangat Cepat (> 50 Mbps)',
+ 'Cepat (30-50 Mbps)',
+ 'Sedang (10-30 Mbps)',
+ 'Lambat (< 10 Mbps)'
+ ]
+ ];
+
+ // Insert data subkategori
+ foreach ($subcategories as $categoryName => $items) {
+ $category = Category::where('name', $categoryName)->first();
+
+ if ($category) {
+ foreach ($items as $subcategoryName) {
+ Subcategory::create([
+ 'category_id' => $category->id,
+ 'name' => $subcategoryName,
+ 'created_at' => now(),
+ 'updated_at' => now(),
+ ]);
+ }
+ }
+ }
+
+ $this->command->info('Subcategories seeded successfully!');
+ }
}
\ No newline at end of file
diff --git a/public/.htaccess b/public/.htaccess
new file mode 100644
index 0000000..b574a59
--- /dev/null
+++ b/public/.htaccess
@@ -0,0 +1,25 @@
+
+
+ Options -MultiViews -Indexes
+
+
+ RewriteEngine On
+
+ # Handle Authorization Header
+ RewriteCond %{HTTP:Authorization} .
+ RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
+
+ # Handle X-XSRF-Token Header
+ RewriteCond %{HTTP:x-xsrf-token} .
+ RewriteRule .* - [E=HTTP_X_XSRF_TOKEN:%{HTTP:X-XSRF-Token}]
+
+ # Redirect Trailing Slashes If Not A Folder...
+ RewriteCond %{REQUEST_FILENAME} !-d
+ RewriteCond %{REQUEST_URI} (.+)/$
+ RewriteRule ^ %1 [L,R=301]
+
+ # Send Requests To Front Controller...
+ RewriteCond %{REQUEST_FILENAME} !-d
+ RewriteCond %{REQUEST_FILENAME} !-f
+ RewriteRule ^ index.php [L]
+
diff --git a/public/assets/icons/User Dark.png b/public/assets/icons/User Dark.png
new file mode 100644
index 0000000..b36a294
Binary files /dev/null and b/public/assets/icons/User Dark.png differ
diff --git a/public/assets/icons/User Light.png b/public/assets/icons/User Light.png
new file mode 100644
index 0000000..95b19e9
Binary files /dev/null and b/public/assets/icons/User Light.png differ
diff --git a/public/assets/icons/User Logo.svg b/public/assets/icons/User Logo.svg
new file mode 100644
index 0000000..1d4dde0
--- /dev/null
+++ b/public/assets/icons/User Logo.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/assets/images/About Us.png b/public/assets/images/About Us.png
new file mode 100644
index 0000000..e95b8bd
Binary files /dev/null and b/public/assets/images/About Us.png differ
diff --git a/public/assets/images/Feature 1.webp b/public/assets/images/Feature 1.webp
new file mode 100644
index 0000000..b5c0e8d
Binary files /dev/null and b/public/assets/images/Feature 1.webp differ
diff --git a/public/assets/images/Feature 2.webp b/public/assets/images/Feature 2.webp
new file mode 100644
index 0000000..598ccf9
Binary files /dev/null and b/public/assets/images/Feature 2.webp differ
diff --git a/public/assets/images/Logo.png b/public/assets/images/Logo.png
new file mode 100644
index 0000000..0bb7750
Binary files /dev/null and b/public/assets/images/Logo.png differ
diff --git a/public/assets/images/LogoDark.png b/public/assets/images/LogoDark.png
new file mode 100644
index 0000000..0bb7750
Binary files /dev/null and b/public/assets/images/LogoDark.png differ
diff --git a/public/assets/images/LogoLight.png b/public/assets/images/LogoLight.png
new file mode 100644
index 0000000..cd6ff55
Binary files /dev/null and b/public/assets/images/LogoLight.png differ
diff --git a/public/css/disable-keyboard.css b/public/css/disable-keyboard.css
new file mode 100644
index 0000000..4e4d647
--- /dev/null
+++ b/public/css/disable-keyboard.css
@@ -0,0 +1,9 @@
+/* Ini akan mencegah papan ketikan (keyboard) muncul pada elemen yang bukan input */
+body:not(:focus-within) {
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
\ No newline at end of file
diff --git a/public/favicon.ico b/public/favicon.ico
new file mode 100644
index 0000000..e69de29
diff --git a/public/images/layers-2x.png b/public/images/layers-2x.png
new file mode 100644
index 0000000..200c333
Binary files /dev/null and b/public/images/layers-2x.png differ
diff --git a/public/images/layers.png b/public/images/layers.png
new file mode 100644
index 0000000..1a72e57
Binary files /dev/null and b/public/images/layers.png differ
diff --git a/public/images/leaflet/layers-2x.png b/public/images/leaflet/layers-2x.png
new file mode 100644
index 0000000..200c333
Binary files /dev/null and b/public/images/leaflet/layers-2x.png differ
diff --git a/public/images/leaflet/layers.png b/public/images/leaflet/layers.png
new file mode 100644
index 0000000..1a72e57
Binary files /dev/null and b/public/images/leaflet/layers.png differ
diff --git a/public/images/leaflet/marker-icon-2x.png b/public/images/leaflet/marker-icon-2x.png
new file mode 100644
index 0000000..88f9e50
Binary files /dev/null and b/public/images/leaflet/marker-icon-2x.png differ
diff --git a/public/images/leaflet/marker-icon.png b/public/images/leaflet/marker-icon.png
new file mode 100644
index 0000000..950edf2
Binary files /dev/null and b/public/images/leaflet/marker-icon.png differ
diff --git a/public/images/leaflet/marker-shadow.png b/public/images/leaflet/marker-shadow.png
new file mode 100644
index 0000000..9fd2979
Binary files /dev/null and b/public/images/leaflet/marker-shadow.png differ
diff --git a/public/images/marker-icon-2x.png b/public/images/marker-icon-2x.png
new file mode 100644
index 0000000..88f9e50
Binary files /dev/null and b/public/images/marker-icon-2x.png differ
diff --git a/public/images/marker-icon.png b/public/images/marker-icon.png
new file mode 100644
index 0000000..950edf2
Binary files /dev/null and b/public/images/marker-icon.png differ
diff --git a/public/images/marker-shadow.png b/public/images/marker-shadow.png
new file mode 100644
index 0000000..9fd2979
Binary files /dev/null and b/public/images/marker-shadow.png differ
diff --git a/public/index.php b/public/index.php
new file mode 100644
index 0000000..ee8f07e
--- /dev/null
+++ b/public/index.php
@@ -0,0 +1,20 @@
+handleRequest(Request::capture());
diff --git a/public/js/cafe-search.js b/public/js/cafe-search.js
new file mode 100644
index 0000000..6767b54
--- /dev/null
+++ b/public/js/cafe-search.js
@@ -0,0 +1,252 @@
+// Inisialisasi variabel global
+let hasSearchResults = false;
+let currentStep = 1;
+
+// Global data untuk menyimpan nilai penting setiap kriteria
+const importanceValues = {};
+
+// Label untuk tingkat kepentingan
+const importanceLabels = {
+ 1: "Sangat tidak penting (1)",
+ 2: "Tidak penting (2)",
+ 3: "Kurang penting (3)",
+ 4: "Agak penting (4)",
+ 5: "Sedang (5)",
+ 6: "Cukup penting (6)",
+ 7: "Penting (7)",
+ 8: "Sangat penting (8)",
+ 9: "Sangat penting sekali (9)",
+ 10: "Paling penting (10)"
+};
+
+// Fungsi untuk update nilai slider yang dipanggil langsung dari oninput HTML
+function updateSliderValue(id, value) {
+ // Update nilai penting di object global
+ importanceValues[id] = parseInt(value);
+
+ // Update tampilan label tingkat kepentingan
+ const importanceDisplay = document.getElementById(`importance_value_${id}`);
+ if (importanceDisplay) {
+ importanceDisplay.textContent = importanceLabels[value];
+ }
+
+ // Hitung ulang bobot dan perbarui tampilan
+ calculateAndUpdateWeights();
+}
+
+// Fungsi untuk menghitung bobot berdasarkan tingkat kepentingan
+function calculateAndUpdateWeights() {
+ let totalImportance = 0;
+ Object.values(importanceValues).forEach(value => {
+ totalImportance += value;
+ });
+
+ // Hitung bobot berdasarkan proporsi kepentingan
+ const weights = {};
+ let totalWeight = 0;
+
+ if (totalImportance > 0) {
+ // Hitung berdasarkan proporsi
+ Object.keys(importanceValues).forEach(id => {
+ weights[id] = Math.floor((importanceValues[id] / totalImportance) * 100);
+ totalWeight += weights[id];
+ });
+
+ // Distribusikan sisa persen
+ if (totalWeight < 100) {
+ // Urutkan ID berdasarkan tingkat kepentingan (tertinggi dulu)
+ const sortedIds = Object.keys(importanceValues).sort((a, b) =>
+ importanceValues[b] - importanceValues[a]
+ );
+
+ // Distribusikan sisa ke nilai tertinggi
+ let remainder = 100 - totalWeight;
+ for (let i = 0; i < remainder; i++) {
+ weights[sortedIds[i % sortedIds.length]]++;
+ }
+ }
+ } else {
+ // Distribusi merata jika semua nilai penting adalah 0
+ const equalWeight = Math.floor(100 / Object.keys(importanceValues).length);
+ let remainder = 100 - (equalWeight * Object.keys(importanceValues).length);
+
+ Object.keys(importanceValues).forEach((id, index) => {
+ weights[id] = equalWeight + (index < remainder ? 1 : 0);
+ });
+ }
+
+ // Update tampilan bobot dan input tersembunyi
+ Object.keys(weights).forEach(id => {
+ const weightDisplay = document.getElementById(`weight_display_${id}`);
+ const hiddenInput = document.getElementById(`hidden_weight_${id}`);
+
+ if (weightDisplay) {
+ weightDisplay.textContent = `${weights[id]}%`;
+
+ // Terapkan kode warna berdasarkan nilai bobot
+ if (weights[id] >= 30) {
+ weightDisplay.className = 'text-sm font-medium text-green-600 dark:text-green-400';
+ } else if (weights[id] >= 15) {
+ weightDisplay.className = 'text-sm font-medium text-blue-600 dark:text-blue-400';
+ } else {
+ weightDisplay.className = 'text-sm font-medium text-gray-700 dark:text-gray-300';
+ }
+ }
+
+ if (hiddenInput) {
+ hiddenInput.value = weights[id];
+ }
+ });
+
+ // Update indikator total bobot
+ const totalWeightIndicator = document.getElementById('total_weight_indicator');
+ if (totalWeightIndicator) {
+ const calculatedTotal = Object.values(weights).reduce((sum, w) => sum + w, 0);
+ totalWeightIndicator.textContent = `Total: ${calculatedTotal}%`;
+
+ if (calculatedTotal === 100) {
+ totalWeightIndicator.classList.remove('bg-red-100', 'text-red-800', 'dark:bg-red-900', 'dark:text-red-300');
+ totalWeightIndicator.classList.add('bg-green-100', 'text-green-800', 'dark:bg-green-900', 'dark:text-green-300');
+ } else {
+ totalWeightIndicator.classList.remove('bg-green-100', 'text-green-800', 'dark:bg-green-900', 'dark:text-green-300');
+ totalWeightIndicator.classList.add('bg-red-100', 'text-red-800', 'dark:bg-red-900', 'dark:text-red-300');
+ }
+ }
+
+ return Object.values(weights).reduce((sum, w) => sum + w, 0) === 100;
+}
+
+// Fungsi untuk cek apakah halaman ini hasil dari submit form
+function checkIfSearchResults() {
+ return hasSearchResults;
+}
+
+// Update fungsi showSection untuk memanggil updateStepProgress
+function showSection1() {
+ document.getElementById('section1').style.display = 'block';
+ document.getElementById('section2').style.display = 'none';
+ document.getElementById('section3').style.display = 'none';
+ updateStepperVisually(1);
+ window.scrollTo(0, 0);
+}
+
+function showSection2() {
+ document.getElementById('section1').style.display = 'none';
+ document.getElementById('section2').style.display = 'block';
+ document.getElementById('section3').style.display = 'none';
+ updateStepperVisually(2);
+ window.scrollTo(0, 0);
+}
+
+function showSection3() {
+ document.getElementById('section1').style.display = 'none';
+ document.getElementById('section2').style.display = 'none';
+ document.getElementById('section3').style.display = 'block';
+ updateStepperVisually(3);
+ window.scrollTo(0, 0);
+}
+
+// Fungsi untuk memperbarui tampilan stepper
+function updateStepperVisually(step) {
+ // Ambil semua step-item dari stepper
+ const stepItems = document.querySelectorAll('.step-item');
+
+ stepItems.forEach((item, index) => {
+ const stepNum = index + 1;
+ const circleDiv = item.querySelector('.w-10');
+ const textSpan = item.querySelector('.text-sm');
+
+ if (stepNum <= step) {
+ // Langkah aktif atau sudah dilalui
+ circleDiv.classList.remove('bg-gray-200', 'dark:bg-gray-700', 'text-gray-600', 'dark:text-gray-400');
+ circleDiv.classList.add('bg-blue-600', 'text-white');
+
+ textSpan.classList.remove('text-gray-500', 'dark:text-gray-400');
+ textSpan.classList.add('text-blue-600', 'dark:text-blue-400');
+ } else {
+ // Langkah yang belum aktif
+ circleDiv.classList.remove('bg-blue-600', 'text-white');
+ circleDiv.classList.add('bg-gray-200', 'dark:bg-gray-700', 'text-gray-600', 'dark:text-gray-400');
+
+ textSpan.classList.remove('text-blue-600', 'dark:text-blue-400');
+ textSpan.classList.add('text-gray-500', 'dark:text-gray-400');
+ }
+
+ // Update status aktif di class
+ if (stepNum <= step) {
+ item.classList.add('active');
+ } else {
+ item.classList.remove('active');
+ }
+ });
+}
+
+// Fungsi inisialisasi yang akan dipanggil ketika DOM selesai dimuat
+function initializeApp() {
+ try {
+ console.log('DOM Content Loaded.');
+
+ // Ambil data dari atribut script
+ const scriptTag = document.querySelector('script[src*="cafe-search.js"]');
+ if (scriptTag) {
+ hasSearchResults = scriptTag.getAttribute('data-has-search-results') === 'true';
+ currentStep = parseInt(scriptTag.getAttribute('data-current-step') || '1');
+ }
+
+ // Inisialisasi nilai penting dan bobot dari URL jika ada
+ const sliders = document.querySelectorAll('.importance-slider');
+ sliders.forEach(slider => {
+ const id = slider.dataset.id;
+ const requestWeight = slider.dataset.requestWeight;
+
+ // Jika ada bobot dari request
+ if (requestWeight) {
+ // Cari nilai importance yang sesuai dengan bobot
+ for (let i = 1; i <= 10; i++) {
+ importanceValues[id] = i;
+ calculateAndUpdateWeights();
+ const weight = document.getElementById(`hidden_weight_${id}`).value;
+ if (parseInt(weight) === parseInt(requestWeight)) {
+ slider.value = i;
+ updateSliderValue(id, i);
+ break;
+ }
+ }
+ } else {
+ // Default nilai awal
+ const defaultValue = slider.value;
+ importanceValues[id] = parseInt(defaultValue);
+ }
+ });
+
+ // Update semua bobot
+ calculateAndUpdateWeights();
+
+ // Tampilkan section sesuai langkah
+ if (hasSearchResults) {
+ showSection3();
+ } else if (currentStep === 2) {
+ showSection2();
+ } else {
+ showSection1();
+ }
+
+ // Form submission validation - ensure weights sum to 100%
+ const smartForm = document.getElementById('smartForm');
+ if (smartForm) {
+ smartForm.addEventListener('submit', function(event) {
+ if (!calculateAndUpdateWeights()) {
+ event.preventDefault();
+ alert('Total bobot harus 100%. Silakan sesuaikan tingkat kepentingan.');
+ showSection1();
+ }
+ });
+ }
+
+ } catch (error) {
+ console.error('Terjadi error saat inisialisasi halaman:', error);
+ }
+}
+
+// Inisialisasi saat DOM selesai dimuat
+document.addEventListener('DOMContentLoaded', initializeApp);
\ No newline at end of file
diff --git a/public/js/disable-keyboard.js b/public/js/disable-keyboard.js
new file mode 100644
index 0000000..5893468
--- /dev/null
+++ b/public/js/disable-keyboard.js
@@ -0,0 +1,23 @@
+// Mencegah papan ketikan (keyboard) muncul saat pengguna menyentuh area yang bukan input/textarea
+document.addEventListener('DOMContentLoaded', function() {
+ document.addEventListener('touchstart', function(e) {
+ if (e.target.tagName !== 'INPUT' &&
+ e.target.tagName !== 'TEXTAREA' &&
+ !e.target.isContentEditable) {
+ e.preventDefault();
+ }
+ }, { passive: false });
+
+ // Menambahkan atribut readonly ke elemen date input untuk mencegah keyboard muncul
+ const dateInputs = document.querySelectorAll('input[type="date"]');
+ dateInputs.forEach(input => {
+ input.setAttribute('readonly', 'readonly');
+ // Tambahkan event listener untuk menghapus readonly saat diklik
+ input.addEventListener('mousedown', function() {
+ this.removeAttribute('readonly');
+ });
+ input.addEventListener('blur', function() {
+ this.setAttribute('readonly', 'readonly');
+ });
+ });
+});
\ No newline at end of file
diff --git a/public/js/leaflet-init.js b/public/js/leaflet-init.js
new file mode 100644
index 0000000..9669522
--- /dev/null
+++ b/public/js/leaflet-init.js
@@ -0,0 +1,80 @@
+// Inisialisasi Leaflet Map untuk halaman tambah kafe
+
+document.addEventListener('DOMContentLoaded', function() {
+ // Pastikan elemen peta ada
+ const mapElement = document.getElementById('map');
+ if (!mapElement) {
+ console.error('Elemen peta tidak ditemukan!');
+ return;
+ }
+
+ console.log('Menginisialisasi peta Leaflet...');
+
+ // Koordinat default (Alun-Alun Jember)
+ const defaultLat = -8.1722;
+ const defaultLng = 113.6982;
+
+ try {
+ // Inisialisasi peta
+ const mymap = L.map('map').setView([defaultLat, defaultLng], 13);
+
+ // Tambahkan layer peta
+ L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
+ maxZoom: 19,
+ attribution: '© OpenStreetMap contributors'
+ }).addTo(mymap);
+
+ // Inisialisasi marker
+ let marker;
+
+ // Fungsi untuk menambahkan marker
+ function addMarker(lat, lng) {
+ // Hapus marker sebelumnya jika ada
+ if (marker) {
+ mymap.removeLayer(marker);
+ }
+
+ // Tambahkan marker baru
+ marker = L.marker([lat, lng]).addTo(mymap);
+
+ // Update formulir
+ document.getElementById('latitude').value = lat;
+ document.getElementById('longitude').value = lng;
+ document.getElementById('koordinat').value = lat + ', ' + lng;
+ }
+
+ // Event listener untuk klik pada peta
+ mymap.on('click', function(e) {
+ console.log('Peta diklik pada:', e.latlng);
+ addMarker(e.latlng.lat, e.latlng.lng);
+ });
+
+ // Event listener untuk input koordinat manual
+ const koordinatInput = document.getElementById('koordinat');
+ if (koordinatInput) {
+ koordinatInput.addEventListener('change', function() {
+ const koordinat = this.value.split(',');
+ if (koordinat.length === 2) {
+ const lat = parseFloat(koordinat[0].trim());
+ const lng = parseFloat(koordinat[1].trim());
+ if (!isNaN(lat) && !isNaN(lng)) {
+ console.log('Mengubah view peta ke koordinat:', lat, lng);
+ mymap.setView([lat, lng], 13);
+ addMarker(lat, lng);
+ }
+ }
+ });
+ }
+
+ // Perbaiki ukuran peta
+ setTimeout(function() {
+ mymap.invalidateSize();
+ console.log('Ukuran peta diperbaiki');
+ }, 100);
+
+ console.log('Peta berhasil diinisialisasi');
+ } catch (error) {
+ console.error('Terjadi kesalahan saat menginisialisasi peta:', error);
+ mapElement.innerHTML = 'Terjadi kesalahan saat memuat peta: ' + error.message + '
';
+ }
+});
\ No newline at end of file
diff --git a/public/leaflet-control-geocoder/Control.Geocoder.css b/public/leaflet-control-geocoder/Control.Geocoder.css
new file mode 100644
index 0000000..b335001
--- /dev/null
+++ b/public/leaflet-control-geocoder/Control.Geocoder.css
@@ -0,0 +1 @@
+.leaflet-control-geocoder{border-radius:4px;background:#fff;min-width:26px;min-height:26px}.leaflet-touch .leaflet-control-geocoder{min-width:30px;min-height:30px}.leaflet-control-geocoder a,.leaflet-control-geocoder .leaflet-control-geocoder-icon{border-bottom:none;display:inline-block}.leaflet-control-geocoder .leaflet-control-geocoder-alternatives a{width:inherit;height:inherit;line-height:inherit}.leaflet-control-geocoder a:hover,.leaflet-control-geocoder .leaflet-control-geocoder-icon:hover{border-bottom:none;display:inline-block}.leaflet-control-geocoder-form{display:none;vertical-align:middle}.leaflet-control-geocoder-expanded .leaflet-control-geocoder-form{display:inline-block}.leaflet-control-geocoder-form input{font-size:120%;border:0;background-color:transparent;width:246px}.leaflet-control-geocoder-icon{border-radius:4px;width:26px;height:26px;border:none;background-color:#fff;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12.2 13l3.4 6.6c.6 1.1 2.5-.4 2-1.2l-4-6.2z'/%3E%3Ccircle cx='10.8' cy='8.9' r='3.9' fill='none' stroke='%23000' stroke-width='1.5'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:center;cursor:pointer}.leaflet-touch .leaflet-control-geocoder-icon{width:30px;height:30px}.leaflet-control-geocoder-throbber .leaflet-control-geocoder-icon{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' stroke='%23000' stroke-linecap='round' stroke-width='1.6' viewBox='0 0 24 24'%3E%3Cdefs/%3E%3Cg%3E%3Cpath stroke-opacity='.1' d='M14 8.4l3-5'/%3E%3Cpath stroke-opacity='.2' d='M15.6 10l5-3'/%3E%3Cpath stroke-opacity='.3' d='M16.2 12H22'/%3E%3Cpath stroke-opacity='.4' d='M15.6 14l5 3m-6.5-1.4l2.9 5'/%3E%3Cpath stroke-opacity='.5' d='M12 16.2V22m-2-6.4l-3 5'/%3E%3Cpath stroke-opacity='.6' d='M8.4 14l-5 3'/%3E%3Cpath stroke-opacity='.7' d='M7.8 12H2'/%3E%3Cpath stroke-opacity='.8' d='M8.4 10l-5-3'/%3E%3Cpath stroke-opacity='.9' d='M10 8.4l-3-5'/%3E%3Cpath d='M12 7.8V2'/%3E%3CanimateTransform attributeName='transform' calcMode='discrete' dur='1s' repeatCount='indefinite' type='rotate' values='0 12 12;30 12 12;60 12 12;90 12 12;120 12 12;150 12 12;180 12 12;210 12 12;240 12 12;270 12 12;300 12 12;330 12 12'/%3E%3C/g%3E%3C/svg%3E")}.leaflet-control-geocoder-form-no-error{display:none}.leaflet-control-geocoder-form input:focus{outline:none}.leaflet-control-geocoder-form button{display:none}.leaflet-control-geocoder-error{margin-top:8px;margin-left:8px;display:block;color:#444}.leaflet-control-geocoder-alternatives{display:block;width:272px;list-style:none;padding:0;margin:0}.leaflet-control-geocoder-alternatives-minimized{display:none;height:0}.leaflet-control-geocoder-alternatives li{white-space:nowrap;display:block;overflow:hidden;padding:5px 8px;text-overflow:ellipsis;border-bottom:1px solid #ccc;cursor:pointer}.leaflet-control-geocoder-alternatives li a,.leaflet-control-geocoder-alternatives li a:hover{width:inherit;height:inherit;line-height:inherit;background:inherit;border-radius:inherit;text-align:left}.leaflet-control-geocoder-alternatives li:last-child{border-bottom:none}.leaflet-control-geocoder-alternatives li:hover,.leaflet-control-geocoder-selected{background-color:#f5f5f5}.leaflet-control-geocoder-address-context{color:#666}
diff --git a/public/leaflet-control-geocoder/Control.Geocoder.js b/public/leaflet-control-geocoder/Control.Geocoder.js
new file mode 100644
index 0000000..ba8bcd6
--- /dev/null
+++ b/public/leaflet-control-geocoder/Control.Geocoder.js
@@ -0,0 +1,9 @@
+(function(u,m){typeof exports=="object"&&typeof module<"u"?m(exports,require("leaflet")):typeof define=="function"&&define.amd?define(["exports","leaflet"],m):(u=typeof globalThis<"u"?globalThis:u||self,m(u["leaflet-control-geocoder"]={},u.L))})(this,function(u,m){"use strict";var ut=Object.defineProperty;var ht=(u,m,v)=>m in u?ut(u,m,{enumerable:!0,configurable:!0,writable:!0,value:v}):u[m]=v;var p=(u,m,v)=>ht(u,typeof m!="symbol"?m+"":m,v);function v(a){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(a){for(const n in a)if(n!=="default"){const e=Object.getOwnPropertyDescriptor(a,n);Object.defineProperty(t,n,e.get?e:{enumerable:!0,get:()=>a[n]})}}return t.default=a,Object.freeze(t)}const s=v(m);function h(a,t){return s.Util.extend(t,a.geocodingQueryParams)}function g(a,t){return s.Util.extend(t,a.reverseQueryParams)}const W=/[&<>"'`]/g,z=/[&<>"'`]/,I={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`"};function q(a){return I[a]}function H(a){return a==null?"":a?(a=""+a,z.test(a)?a.replace(W,q):a):a+""}function d(a,t){const n={Accept:"application/json"},e=new URL(a);return Object.entries(t).forEach(([o,i])=>{(Array.isArray(i)?i:[i]).forEach(r=>{e.searchParams.append(o,r)})}),fetch(e.toString(),{headers:n}).then(o=>o.json())}function J(a,t){return a.replace(/\{ *([\w_]+) *\}/g,(n,e)=>{let o=t[e];return o===void 0?o="":typeof o=="function"&&(o=o(t)),H(o)})}class x{constructor(t){p(this,"options",{serviceUrl:"https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer",apiKey:""});s.Util.setOptions(this,t)}async geocode(t){const n=h(this.options,{token:this.options.apiKey,SingleLine:t,outFields:"Addr_Type",forStorage:!1,maxLocations:10,f:"json"}),e=await d(this.options.serviceUrl+"/findAddressCandidates",n),o=[];if(e.candidates&&e.candidates.length)for(let i=0;i<=e.candidates.length-1;i++){const r=e.candidates[i],l=s.latLng(r.location.y,r.location.x),c=s.latLngBounds(s.latLng(r.extent.ymax,r.extent.xmax),s.latLng(r.extent.ymin,r.extent.xmin));o[i]={name:r.address,bbox:c,center:l}}return o}suggest(t){return this.geocode(t)}async reverse(t,n){const e=g(this.options,{location:t.lng+","+t.lat,distance:100,f:"json"}),o=await d(this.options.serviceUrl+"/reverseGeocode",e),i=[];if(o&&!o.error){const r=s.latLng(o.location.y,o.location.x),l=s.latLngBounds(r,r);i.push({name:o.address.Match_addr,center:r,bbox:l})}return i}}function V(a){return new x(a)}class U{constructor(t){p(this,"options",{serviceUrl:"https://dev.virtualearth.net/REST/v1/Locations/"});s.Util.setOptions(this,t)}async geocode(t){const n=h(this.options,{query:t,key:this.options.apiKey}),e=await d(this.options.serviceUrl,n),o=[];if(e.resourceSets.length>0)for(let i=e.resourceSets[0].resources.length-1;i>=0;i--){const r=e.resourceSets[0].resources[i],l=r.bbox;o[i]={name:r.name,bbox:s.latLngBounds([l[0],l[1]],[l[2],l[3]]),center:s.latLng(r.point.coordinates)}}return o}async reverse(t,n){const e=g(this.options,{key:this.options.apiKey}),o=await d(this.options.serviceUrl+t.lat+","+t.lng,e),i=[];for(let r=o.resourceSets[0].resources.length-1;r>=0;r--){const l=o.resourceSets[0].resources[r],c=l.bbox;i[r]={name:l.name,bbox:s.latLngBounds([c[0],c[1]],[c[2],c[3]]),center:s.latLng(l.point.coordinates)}}return i}}function $(a){return new U(a)}class C{constructor(t){p(this,"options",{apiKey:"",serviceUrl:"https://atlas.microsoft.com/search"});if(s.Util.setOptions(this,t),!this.options.apiKey)throw new Error("Azure Maps Geocoder requires an API key.")}async geocode(t){const n={"api-version":"1.0",query:t,"subscription-key":this.options.apiKey},e=this.options.serviceUrl+"/address/json",o=await d(e,n),i=[];if(o.results&&o.results.length>0)for(const r of o.results)i.push({name:r.address.freeformAddress,bbox:s.latLngBounds([r.viewport.topLeftPoint.lat,r.viewport.topLeftPoint.lon],[r.viewport.btmRightPoint.lat,r.viewport.btmRightPoint.lon]),center:s.latLng(r.position.lat,r.position.lon)});return i}async reverse(t,n){const e={"api-version":"1.0",query:t.lat+","+t.lng,"subscription-key":this.options.apiKey},o=this.options.serviceUrl+"/address/reverse/json",i=await d(o,e),r=[];if(i.addresses&&i.addresses.length>0)for(const l of i.addresses)r.push({name:l.address.freeformAddress,bbox:s.latLngBounds([l.viewport.topLeftPoint.lat,l.viewport.topLeftPoint.lon],[l.viewport.btmRightPoint.lat,l.viewport.btmRightPoint.lon]),center:s.latLng(t.lat,t.lng)});return r}}function F(a){return new C(a)}class R{constructor(t){p(this,"options",{serviceUrl:"https://maps.googleapis.com/maps/api/geocode/json"});s.Util.setOptions(this,t)}async geocode(t){const n=h(this.options,{key:this.options.apiKey,address:t}),e=await d(this.options.serviceUrl,n),o=[];if(e.results&&e.results.length)for(let i=0;i<=e.results.length-1;i++){const r=e.results[i],l=s.latLng(r.geometry.location),c=s.latLngBounds(s.latLng(r.geometry.viewport.northeast),s.latLng(r.geometry.viewport.southwest));o[i]={name:r.formatted_address,bbox:c,center:l,properties:r.address_components}}return o}async reverse(t,n){const e=g(this.options,{key:this.options.apiKey,latlng:t.lat+","+t.lng}),o=await d(this.options.serviceUrl,e),i=[];if(o.results&&o.results.length)for(let r=0;r<=o.results.length-1;r++){const l=o.results[r],c=s.latLng(l.geometry.location),f=s.latLngBounds(s.latLng(l.geometry.viewport.northeast),s.latLng(l.geometry.viewport.southwest));i[r]={name:l.formatted_address,bbox:f,center:c,properties:l.address_components}}return i}}function Q(a){return new R(a)}class E{constructor(t){p(this,"options",{serviceUrl:"https://geocoder.api.here.com/6.2/",app_id:"",app_code:"",apiKey:"",maxResults:5});if(s.Util.setOptions(this,t),t!=null&&t.apiKey)throw Error("apiKey is not supported, use app_id/app_code instead!")}geocode(t){const n=h(this.options,{searchtext:t,gen:9,app_id:this.options.app_id,app_code:this.options.app_code,jsonattributes:1,maxresults:this.options.maxResults});return this.getJSON(this.options.serviceUrl+"geocode.json",n)}reverse(t,n){let e=t.lat+","+t.lng;this.options.reverseGeocodeProxRadius&&(e+=","+this.options.reverseGeocodeProxRadius);const o=g(this.options,{prox:e,mode:"retrieveAddresses",app_id:this.options.app_id,app_code:this.options.app_code,gen:9,jsonattributes:1,maxresults:this.options.maxResults});return this.getJSON(this.options.serviceUrl+"reversegeocode.json",o)}async getJSON(t,n){const e=await d(t,n),o=[];if(e.response.view&&e.response.view.length)for(let i=0;i<=e.response.view[0].result.length-1;i++){const r=e.response.view[0].result[i].location,l=s.latLng(r.displayPosition.latitude,r.displayPosition.longitude),c=s.latLngBounds(s.latLng(r.mapView.topLeft.latitude,r.mapView.topLeft.longitude),s.latLng(r.mapView.bottomRight.latitude,r.mapView.bottomRight.longitude));o[i]={name:r.address.label,properties:r.address,bbox:c,center:l}}return o}}class k{constructor(t){p(this,"options",{serviceUrl:"https://geocode.search.hereapi.com/v1",apiKey:"",app_id:"",app_code:"",maxResults:10});s.Util.setOptions(this,t)}geocode(t){const n=h(this.options,{q:t,apiKey:this.options.apiKey,limit:this.options.maxResults});if(!n.at&&!n.in)throw Error("at / in parameters not found. Please define coordinates (at=latitude,longitude) or other (in) in your geocodingQueryParams.");return this.getJSON(this.options.serviceUrl+"/discover",n)}reverse(t,n){const e=g(this.options,{at:t.lat+","+t.lng,limit:this.options.reverseGeocodeProxRadius,apiKey:this.options.apiKey});return this.getJSON(this.options.serviceUrl+"/revgeocode",e)}async getJSON(t,n){const e=await d(t,n),o=[];if(e.items&&e.items.length)for(let i=0;i<=e.items.length-1;i++){const r=e.items[i],l=s.latLng(r.position.lat,r.position.lng);let c;r.mapView?c=s.latLngBounds(s.latLng(r.mapView.south,r.mapView.west),s.latLng(r.mapView.north,r.mapView.east)):c=s.latLngBounds(s.latLng(r.position.lat,r.position.lng),s.latLng(r.position.lat,r.position.lng)),o[i]={name:r.address.label,properties:r.address,bbox:c,center:l}}return o}}function X(a){return a!=null&&a.apiKey?new k(a):new E(a)}function D(a){let t;if(t=a.match(/^([NS])\s*(\d{1,3}(?:\.\d*)?)\W*([EW])\s*(\d{1,3}(?:\.\d*)?)$/))return s.latLng((/N/i.test(t[1])?1:-1)*+t[2],(/E/i.test(t[3])?1:-1)*+t[4]);if(t=a.match(/^(\d{1,3}(?:\.\d*)?)\s*([NS])\W*(\d{1,3}(?:\.\d*)?)\s*([EW])$/))return s.latLng((/N/i.test(t[2])?1:-1)*+t[1],(/E/i.test(t[4])?1:-1)*+t[3]);if(t=a.match(/^([NS])\s*(\d{1,3})°?\s*(\d{1,3}(?:\.\d*)?)?['′]?\W*([EW])\s*(\d{1,3})°?\s*(\d{1,3}(?:\.\d*)?)?['′]?$/))return s.latLng((/N/i.test(t[1])?1:-1)*(+t[2]+ +t[3]/60),(/E/i.test(t[4])?1:-1)*(+t[5]+ +t[6]/60));if(t=a.match(/^(\d{1,3})°?\s*(\d{1,3}(?:\.\d*)?)?['′]?\s*([NS])\W*(\d{1,3})°?\s*(\d{1,3}(?:\.\d*)?)?['′]?\s*([EW])$/))return s.latLng((/N/i.test(t[3])?1:-1)*(+t[1]+ +t[2]/60),(/E/i.test(t[6])?1:-1)*(+t[4]+ +t[5]/60));if(t=a.match(/^([NS])\s*(\d{1,3})°?\s*(\d{1,2})['′]?\s*(\d{1,3}(?:\.\d*)?)?["″]?\W*([EW])\s*(\d{1,3})°?\s*(\d{1,2})['′]?\s*(\d{1,3}(?:\.\d*)?)?["″]?$/))return s.latLng((/N/i.test(t[1])?1:-1)*(+t[2]+ +t[3]/60+ +t[4]/3600),(/E/i.test(t[5])?1:-1)*(+t[6]+ +t[7]/60+ +t[8]/3600));if(t=a.match(/^(\d{1,3})°?\s*(\d{1,2})['′]?\s*(\d{1,3}(?:\.\d*)?)?["″]\s*([NS])\W*(\d{1,3})°?\s*(\d{1,2})['′]?\s*(\d{1,3}(?:\.\d*)?)?["″]?\s*([EW])$/))return s.latLng((/N/i.test(t[4])?1:-1)*(+t[1]+ +t[2]/60+ +t[3]/3600),(/E/i.test(t[8])?1:-1)*(+t[5]+ +t[6]/60+ +t[7]/3600));if(t=a.match(/^\s*([+-]?\d+(?:\.\d*)?)\s*[\s,]\s*([+-]?\d+(?:\.\d*)?)\s*$/))return s.latLng(+t[1],+t[2])}class B{constructor(t){p(this,"options",{next:void 0,sizeInMeters:1e4});s.Util.setOptions(this,t)}async geocode(t){const n=D(t);return n?[{name:t,center:n,bbox:n.toBounds(this.options.sizeInMeters)}]:this.options.next?this.options.next.geocode(t):[]}}function Y(a){return new B(a)}class O{constructor(t){p(this,"options",{serviceUrl:"https://api.mapbox.com/geocoding/v5/mapbox.places/"});s.Util.setOptions(this,t)}_getProperties(t){const n={text:t.text,address:t.address};for(let e=0;e<(t.context||[]).length;e++){const o=t.context[e].id.split(".")[0];n[o]=t.context[e].text,t.context[e].short_code&&(n.countryShortCode=t.context[e].short_code)}return n}async geocode(t){const n=this.options.serviceUrl+encodeURIComponent(t)+".json",e=h(this.options,{access_token:this.options.apiKey});e.proximity!==void 0&&e.proximity.lat!==void 0&&e.proximity.lng!==void 0&&(e.proximity=e.proximity.lng+","+e.proximity.lat);const o=await d(n,e);return this._parseResults(o)}suggest(t){return this.geocode(t)}async reverse(t,n){const e=this.options.serviceUrl+t.lng+","+t.lat+".json",o=g(this.options,{access_token:this.options.apiKey}),i=await d(e,o);return this._parseResults(i)}_parseResults(t){var e;if(!((e=t.features)!=null&&e.length))return[];const n=[];for(let o=0;o<=t.features.length-1;o++){const i=t.features[o],r=s.latLng(i.center.reverse());let l;i.bbox?l=s.latLngBounds(s.latLng(i.bbox.slice(0,2).reverse()),s.latLng(i.bbox.slice(2,4).reverse())):l=s.latLngBounds(r,r),n[o]={name:i.place_name,bbox:l,center:r,properties:this._getProperties(i)}}return n}}function Z(a){return new O(a)}class S{constructor(t){p(this,"options",{serviceUrl:"https://www.mapquestapi.com/geocoding/v1"});s.Util.setOptions(this,t),this.options.apiKey=decodeURIComponent(this.options.apiKey)}_formatName(...t){return t.filter(n=>!!n).join(", ")}async geocode(t){const n=h(this.options,{key:this.options.apiKey,location:t,limit:5,outFormat:"json"}),e=await d(this.options.serviceUrl+"/address",n);return this._parseResults(e)}async reverse(t,n){const e=g(this.options,{key:this.options.apiKey,location:t.lat+","+t.lng,outputFormat:"json"}),o=await d(this.options.serviceUrl+"/reverse",e);return this._parseResults(o)}_parseResults(t){const n=[];if(t.results&&t.results[0].locations)for(let e=t.results[0].locations.length-1;e>=0;e--){const o=t.results[0].locations[e],i=s.latLng(o.latLng);n[e]={name:this._formatName(o.street,o.adminArea4,o.adminArea3,o.adminArea1),bbox:s.latLngBounds(i,i),center:i}}return n}}function tt(a){return new S(a)}class K{constructor(t){p(this,"options",{userId:"",apiKey:"",serviceUrl:"https://neutrinoapi.com/"});s.Util.setOptions(this,t)}async geocode(t){const n=h(this.options,{apiKey:this.options.apiKey,userId:this.options.userId,address:t.split(/\s+/).join(".")}),e=await d(this.options.serviceUrl+"geocode-address",n),o=[];if(e.locations){e.geometry=e.locations[0];const i=s.latLng(e.geometry.latitude,e.geometry.longitude),r=s.latLngBounds(i,i);o[0]={name:e.geometry.address,bbox:r,center:i}}return o}suggest(t){return this.geocode(t)}async reverse(t,n){const e=g(this.options,{apiKey:this.options.apiKey,userId:this.options.userId,latitude:t.lat,longitude:t.lng}),o=await d(this.options.serviceUrl+"geocode-reverse",e),i=[];if(o.status.status==200&&o.found){const r=s.latLng(t.lat,t.lng),l=s.latLngBounds(r,r);i[0]={name:o.address,bbox:l,center:r}}return i}}function et(a){return new K(a)}class y{constructor(t){p(this,"options",{serviceUrl:"https://nominatim.openstreetmap.org/",htmlTemplate(t){const n=t.address;let e;const o=[];return(n.road||n.building)&&o.push("{building} {road} {house_number}"),(n.city||n.town||n.village||n.hamlet)&&(e=o.length>0?"leaflet-control-geocoder-address-detail":"",o.push('{postcode} {city} {town} {village} {hamlet}')),(n.state||n.country)&&(e=o.length>0?"leaflet-control-geocoder-address-context":"",o.push('{state} {country}')),J(o.join("
"),n)}});s.Util.setOptions(this,t||{})}async geocode(t){const n=h(this.options,{q:t,limit:5,format:"json",addressdetails:1}),e=await d(this.options.serviceUrl+"search",n),o=[];for(let i=e.length-1;i>=0;i--){const r=e[i].boundingbox;o[i]={icon:e[i].icon,name:e[i].display_name,html:this.options.htmlTemplate?this.options.htmlTemplate(e[i]):void 0,bbox:s.latLngBounds([+r[0],+r[2]],[+r[1],+r[3]]),center:s.latLng(+e[i].lat,+e[i].lon),properties:e[i]}}return o}async reverse(t,n){const e=g(this.options,{lat:t.lat,lon:t.lng,zoom:Math.round(Math.log(n/256)/Math.log(2)),addressdetails:1,format:"json"}),o=await d(this.options.serviceUrl+"reverse",e),i=[];if(o&&o.lat&&o.lon){const r=s.latLng(+o.lat,+o.lon),l=s.latLngBounds(r,r);i.push({name:o.display_name,html:this.options.htmlTemplate?this.options.htmlTemplate(o):void 0,center:r,bbox:l,properties:o})}return i}}function st(a){return new y(a)}class P{constructor(t){p(this,"options",{});s.Util.setOptions(this,t)}async geocode(t){try{const n=this.options.OpenLocationCode.decode(t);return[{name:t,center:s.latLng(n.latitudeCenter,n.longitudeCenter),bbox:s.latLngBounds(s.latLng(n.latitudeLo,n.longitudeLo),s.latLng(n.latitudeHi,n.longitudeHi))}]}catch(n){return console.warn(n),[]}}async reverse(t,n){try{return[{name:this.options.OpenLocationCode.encode(t.lat,t.lng,this.options.codeLength),center:s.latLng(t.lat,t.lng),bbox:s.latLngBounds(s.latLng(t.lat,t.lng),s.latLng(t.lat,t.lng))}]}catch(e){return console.warn(e),[]}}}function ot(a){return new P(a)}class T{constructor(t){p(this,"options",{serviceUrl:"https://api.opencagedata.com/geocode/v1/json"});s.Util.setOptions(this,t)}async geocode(t){const n=h(this.options,{key:this.options.apiKey,q:t}),e=await d(this.options.serviceUrl,n);return this._parseResults(e)}suggest(t){return this.geocode(t)}async reverse(t,n){const e=g(this.options,{key:this.options.apiKey,q:[t.lat,t.lng].join(",")}),o=await d(this.options.serviceUrl,e);return this._parseResults(o)}_parseResults(t){const n=[];if(t.results&&t.results.length)for(let e=0;e{var e;return(e=t.properties)==null?void 0:e[n]}).filter(n=>!!n).join(", ")}}function pt(a){return new M(a)}class N{constructor(t){p(this,"options",{serviceUrl:"https://api.what3words.com/v2/"});s.Util.setOptions(this,t)}async geocode(t){const n=await d(this.options.serviceUrl+"forward",h(this.options,{key:this.options.apiKey,addr:t.split(/\s+/).join(".")})),e=[];if(n.geometry){const o=s.latLng(n.geometry.lat,n.geometry.lng),i=s.latLngBounds(o,o);e[0]={name:n.words,bbox:i,center:o}}return e}suggest(t){return this.geocode(t)}async reverse(t,n){const e=await d(this.options.serviceUrl+"reverse",g(this.options,{key:this.options.apiKey,coords:[t.lat,t.lng].join(",")})),o=[];if(e.status.status==200){const i=s.latLng(e.geometry.lat,e.geometry.lng),r=s.latLngBounds(i,i);o[0]={name:e.words,bbox:r,center:i}}return o}}function dt(a){return new N(a)}const A=Object.freeze(Object.defineProperty({__proto__:null,ArcGis:x,AzureMaps:C,Bing:U,GeocodeEarth:it,Google:R,HERE:E,HEREv2:k,LatLng:B,MapQuest:S,Mapbox:O,Mapzen:at,Neutrino:K,Nominatim:y,OpenCage:T,OpenLocationCode:P,Openrouteservice:j,Pelias:_,Photon:M,What3Words:N,arcgis:V,azure:F,bing:$,geocodeEarth:rt,geocodingParams:h,google:Q,here:X,latLng:Y,mapQuest:tt,mapbox:Z,mapzen:lt,neutrino:et,nominatim:st,openLocationCode:ot,opencage:nt,openrouteservice:ct,parseLatLng:D,pelias:L,photon:pt,reverseParams:g,what3words:dt},Symbol.toStringTag,{value:"Module"}));class w{constructor(...t){}}s.Util.extend(w.prototype,s.Control.prototype),s.Util.extend(w.prototype,s.Evented.prototype);class b extends w{constructor(n){super(n);p(this,"options",{showUniqueResult:!0,showResultIcons:!1,collapsed:!0,expand:"touch",position:"topright",placeholder:"Search...",errorMessage:"Nothing found.",iconLabel:"Initiate a new search",query:"",queryMinLength:1,suggestMinLength:3,suggestTimeout:250,defaultMarkGeocode:!0});p(this,"_alts");p(this,"_container");p(this,"_errorElement");p(this,"_geocodeMarker");p(this,"_input");p(this,"_lastGeocode");p(this,"_map");p(this,"_preventBlurCollapse");p(this,"_requestCount",0);p(this,"_results");p(this,"_selection");p(this,"_suggestTimeout");s.Util.setOptions(this,n),this.options.geocoder||(this.options.geocoder=new y)}addThrobberClass(){s.DomUtil.addClass(this._container,"leaflet-control-geocoder-throbber")}removeThrobberClass(){s.DomUtil.removeClass(this._container,"leaflet-control-geocoder-throbber")}onAdd(n){var c;const e="leaflet-control-geocoder",o=s.DomUtil.create("div",e+" leaflet-bar"),i=s.DomUtil.create("button",e+"-icon",o),r=s.DomUtil.create("div",e+"-form",o);this._map=n,this._container=o,i.innerHTML=" ",i.type="button",i.setAttribute("aria-label",this.options.iconLabel);const l=this._input=s.DomUtil.create("input","",r);return l.type="search",l.value=this.options.query,l.placeholder=this.options.placeholder,s.DomEvent.disableClickPropagation(l),this._errorElement=s.DomUtil.create("div",e+"-form-no-error",o),this._errorElement.innerHTML=this.options.errorMessage,this._alts=s.DomUtil.create("ul",e+"-alternatives leaflet-control-geocoder-alternatives-minimized",o),s.DomEvent.disableClickPropagation(this._alts),s.DomEvent.addListener(l,"keydown",this._keydown,this),(c=this.options.geocoder)!=null&&c.suggest&&s.DomEvent.addListener(l,"input",this._change,this),s.DomEvent.addListener(l,"blur",()=>{this.options.collapsed&&!this._preventBlurCollapse&&this._collapse(),this._preventBlurCollapse=!1}),this.options.collapsed?this.options.expand==="click"?s.DomEvent.addListener(o,"click",f=>{f.button===0&&f.detail!==2&&this._toggle()}):this.options.expand==="touch"?s.DomEvent.addListener(o,s.Browser.touch?"touchstart mousedown":"mousedown",f=>{this._toggle(),f.preventDefault(),f.stopPropagation()},this):(s.DomEvent.addListener(o,"mouseover",this._expand,this),s.DomEvent.addListener(o,"mouseout",this._collapse,this),this._map.on("movestart",this._collapse,this)):(this._expand(),s.Browser.touch?s.DomEvent.addListener(o,"touchstart",()=>this._geocode()):s.DomEvent.addListener(o,"click",()=>this._geocode())),this.options.defaultMarkGeocode&&this.on("markgeocode",this.markGeocode,this),this.on("startgeocode",this.addThrobberClass,this),this.on("finishgeocode",this.removeThrobberClass,this),this.on("startsuggest",this.addThrobberClass,this),this.on("finishsuggest",this.removeThrobberClass,this),s.DomEvent.disableClickPropagation(o),o}setQuery(n){return this._input.value=n,this}_geocodeResult(n,e){if(!e&&this.options.showUniqueResult&&n.length===1)this._geocodeResultSelected(n[0]);else if(n.length>0){this._alts.innerHTML="",this._results=n,s.DomUtil.removeClass(this._alts,"leaflet-control-geocoder-alternatives-minimized"),s.DomUtil.addClass(this._container,"leaflet-control-geocoder-options-open");for(let o=0;o{this._preventBlurCollapse=!0,s.DomEvent.stop(f),this._geocodeResultSelected(n),s.DomEvent.on(o,"click touchend",()=>{this.options.collapsed?this._collapse():this._clearResults()})};return r&&(r.src=n.icon),o.setAttribute("data-result-index",String(e)),n.html?i.innerHTML=i.innerHTML+n.html:l&&i.appendChild(l),s.DomEvent.addListener(o,"mousedown touchstart",c,this),o}_keydown(n){const e=o=>{this._selection&&(s.DomUtil.removeClass(this._selection,"leaflet-control-geocoder-selected"),this._selection=this._selection[o>0?"nextSibling":"previousSibling"]),this._selection||(this._selection=this._alts[o>0?"firstChild":"lastChild"]),this._selection&&s.DomUtil.addClass(this._selection,"leaflet-control-geocoder-selected")};switch(n.keyCode){case 27:this.options.collapsed?this._collapse():this._clearResults();break;case 38:e(-1);break;case 40:e(1);break;case 13:if(this._selection){const o=parseInt(this._selection.getAttribute("data-result-index"),10);this._geocodeResultSelected(this._results[o]),this._clearResults()}else this._geocode();break;default:return}s.DomEvent.preventDefault(n)}_change(){const n=this._input.value;n!==this._lastGeocode&&(clearTimeout(this._suggestTimeout),n.length>=this.options.suggestMinLength?this._suggestTimeout=setTimeout(()=>this._geocode(!0),this.options.suggestTimeout):this._clearResults())}}function G(a){return new b(a)}/* @preserve
+ * Leaflet Control Geocoder
+ * https://github.com/perliedman/leaflet-control-geocoder
+ *
+ * Copyright (c) 2012 sa3m (https://github.com/sa3m)
+ * Copyright (c) 2018 Per Liedman
+ * All rights reserved.
+ */s.Util.extend(b,A),s.Util.extend(s.Control,{Geocoder:b,geocoder:G}),u.Geocoder=b,u.default=b,u.geocoder=G,u.geocoders=A,Object.defineProperties(u,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
+//# sourceMappingURL=Control.Geocoder.js.map
diff --git a/public/leaflet-control-geocoder/Control.Geocoder.js.map b/public/leaflet-control-geocoder/Control.Geocoder.js.map
new file mode 100644
index 0000000..a3cef4f
--- /dev/null
+++ b/public/leaflet-control-geocoder/Control.Geocoder.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"Control.Geocoder.js","sources":["../src/geocoders/api.ts","../src/util.ts","../src/geocoders/arcgis.ts","../src/geocoders/bing.ts","../src/geocoders/azure.ts","../src/geocoders/google.ts","../src/geocoders/here.ts","../src/geocoders/latlng.ts","../src/geocoders/mapbox.ts","../src/geocoders/mapquest.ts","../src/geocoders/neutrino.ts","../src/geocoders/nominatim.ts","../src/geocoders/open-location-code.ts","../src/geocoders/opencage.ts","../src/geocoders/pelias.ts","../src/geocoders/photon.ts","../src/geocoders/what3words.ts","../src/control.ts","../src/index.ts"],"sourcesContent":["import * as L from 'leaflet';\n\n/**\n * An object that represents a result from a geocoding query\n */\nexport interface GeocodingResult {\n /**\n * Name of found location\n */\n name: string;\n /**\n * The bounds of the location\n */\n bbox: L.LatLngBounds;\n /**\n * The center coordinate of the location\n */\n center: L.LatLng;\n /**\n * URL for icon representing result; optional\n */\n icon?: string;\n /**\n * HTML formatted representation of the name\n */\n html?: string;\n /**\n * Additional properties returned by the geocoder\n */\n properties?: any;\n}\n\n/**\n * An interface implemented to respond to geocoding queries\n */\nexport interface IGeocoder {\n /**\n * Performs a geocoding query and returns the results as promise\n * @param query the query\n */\n geocode(query: string): Promise;\n /**\n * Performs a geocoding query suggestion (this happens while typing) and returns the results as promise\n * @param query the query\n */\n suggest?(query: string): Promise;\n /**\n * Performs a reverse geocoding query and returns the results as promise\n * @param location the coordinate to reverse geocode\n * @param scale the map scale possibly used for reverse geocoding\n */\n reverse?(location: L.LatLngLiteral, scale: number): Promise;\n}\n\nexport interface GeocoderOptions {\n /**\n * URL of the service\n */\n serviceUrl: string;\n /**\n * Additional URL parameters (strings) that will be added to geocoding requests\n */\n geocodingQueryParams?: Record;\n /**\n * Additional URL parameters (strings) that will be added to reverse geocoding requests\n */\n reverseQueryParams?: Record;\n /**\n * API key to use this service\n */\n apiKey?: string;\n}\n\n/**\n * @internal\n */\nexport function geocodingParams(\n options: GeocoderOptions,\n params: Record\n): Record {\n return L.Util.extend(params, options.geocodingQueryParams);\n}\n\n/**\n * @internal\n */\nexport function reverseParams(\n options: GeocoderOptions,\n params: Record\n): Record {\n return L.Util.extend(params, options.reverseQueryParams);\n}\n","// Adapted from handlebars.js\n// https://github.com/wycats/handlebars.js/\n/**\n * @internal\n */\nconst badChars = /[&<>\"'`]/g;\n/**\n * @internal\n */\nconst possible = /[&<>\"'`]/;\n/**\n * @internal\n */\nconst escape: Record = {\n '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": ''',\n '`': '`'\n};\n\n/**\n * @internal\n */\nfunction escapeChar(chr: string) {\n return escape[chr];\n}\n\n/**\n * @internal\n */\nexport function htmlEscape(string?: string): string {\n if (string == null) {\n return '';\n } else if (!string) {\n return string + '';\n }\n\n // Force a string conversion as this will be done by the append regardless and\n // the regex test will do this transparently behind the scenes, causing issues if\n // an object's to string has escaped characters in it.\n string = '' + string;\n\n if (!possible.test(string)) {\n return string;\n }\n return string.replace(badChars, escapeChar);\n}\n\n/**\n * @internal\n */\nexport function getJSON(url: string, params: Record): Promise {\n const headers = { Accept: 'application/json' };\n const request = new URL(url);\n Object.entries(params).forEach(([key, value]) => {\n (Array.isArray(value) ? value : [value]).forEach(v => {\n request.searchParams.append(key, v);\n });\n });\n return fetch(request.toString(), { headers }).then(response => response.json());\n}\n\n/**\n * @internal\n */\nexport function template(str: string, data: Record): string {\n return str.replace(/\\{ *([\\w_]+) *\\}/g, (str, key) => {\n let value = data[key];\n if (value === undefined) {\n value = '';\n } else if (typeof value === 'function') {\n value = value(data);\n }\n return htmlEscape(value);\n });\n}\n","import * as L from 'leaflet';\nimport { getJSON } from '../util';\nimport { IGeocoder, GeocoderOptions, geocodingParams, GeocodingResult, reverseParams } from './api';\n\nexport interface ArcGisOptions extends GeocoderOptions {}\n\n\n\n/**\n * Implementation of the [ArcGIS geocoder](https://developers.arcgis.com/features/geocoding/)\n */\nexport class ArcGis implements IGeocoder {\n options: ArcGisOptions = {\n serviceUrl: 'https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer',\n apiKey: ''\n };\n\n constructor(options?: Partial) {\n L.Util.setOptions(this, options);\n }\n\n async geocode(query: string): Promise {\n const params = geocodingParams(this.options, {\n token: this.options.apiKey,\n SingleLine: query,\n outFields: 'Addr_Type',\n forStorage: false,\n maxLocations: 10,\n f: 'json'\n });\n\n const data = await getJSON(\n this.options.serviceUrl + '/findAddressCandidates',\n params\n );\n const results: GeocodingResult[] = [];\n if (data.candidates && data.candidates.length) {\n for (let i = 0; i <= data.candidates.length - 1; i++) {\n const loc = data.candidates[i];\n const latLng = L.latLng(loc.location.y, loc.location.x);\n const latLngBounds = L.latLngBounds(\n L.latLng(loc.extent.ymax, loc.extent.xmax),\n L.latLng(loc.extent.ymin, loc.extent.xmin)\n );\n results[i] = {\n name: loc.address,\n bbox: latLngBounds,\n center: latLng\n };\n }\n }\n\n return results;\n }\n\n suggest(query: string): Promise {\n return this.geocode(query);\n }\n\n async reverse(location: L.LatLngLiteral, scale: number): Promise {\n const params = reverseParams(this.options, {\n location: location.lng + ',' + location.lat,\n distance: 100,\n f: 'json'\n });\n const data = await getJSON(this.options.serviceUrl + '/reverseGeocode', params);\n const result: GeocodingResult[] = [];\n if (data && !data.error) {\n const center = L.latLng(data.location.y, data.location.x);\n const bbox = L.latLngBounds(center, center);\n result.push({\n name: data.address.Match_addr,\n center: center,\n bbox: bbox\n });\n }\n\n return result;\n }\n}\n\n/**\n * [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link ArcGis}\n * @param options the options\n */\nexport function arcgis(options?: Partial) {\n return new ArcGis(options);\n}\n\n/**\n * @internal\n */\nexport interface ArcGisResponse {\n spatialReference: {\n wkid: number;\n latestWkid: number;\n };\n candidates: Candidate[];\n}\n\ninterface Candidate {\n address: string;\n location: {\n x: number;\n y: number;\n };\n score: number;\n attributes: {\n Addr_Type: string;\n };\n extent: {\n xmin: number;\n ymin: number;\n xmax: number;\n ymax: number;\n };\n}\n\n","import * as L from 'leaflet';\nimport { getJSON } from '../util';\nimport { IGeocoder, GeocoderOptions, geocodingParams, GeocodingResult, reverseParams } from './api';\n\nexport interface BingOptions extends GeocoderOptions {}\n\n/**\n * Implementation of the [Bing Locations API](https://docs.microsoft.com/en-us/bingmaps/rest-services/locations/)\n *\n * Bing Maps for Enterprise is deprecated and will be retired.\n * Free (Basic) account customers can continue to use Bing Maps for Enterprise services until June 30th, 2025.\n * Enterprise account customers can continue to use Bing Maps for Enterprise services until June 30th, 2028.\n */\nexport class Bing implements IGeocoder {\n options: BingOptions = {\n serviceUrl: 'https://dev.virtualearth.net/REST/v1/Locations/'\n };\n\n constructor(options?: Partial) {\n L.Util.setOptions(this, options);\n }\n\n async geocode(query: string): Promise {\n const params = geocodingParams(this.options, {\n query: query,\n key: this.options.apiKey\n });\n const data = await getJSON(this.options.serviceUrl, params);\n const results: GeocodingResult[] = [];\n if (data.resourceSets.length > 0) {\n for (let i = data.resourceSets[0].resources.length - 1; i >= 0; i--) {\n const resource = data.resourceSets[0].resources[i],\n bbox = resource.bbox;\n results[i] = {\n name: resource.name,\n bbox: L.latLngBounds([bbox[0], bbox[1]], [bbox[2], bbox[3]]),\n center: L.latLng(resource.point.coordinates)\n };\n }\n }\n return results;\n }\n\n async reverse(location: L.LatLngLiteral, scale: number): Promise {\n const params = reverseParams(this.options, {\n key: this.options.apiKey\n });\n const data = await getJSON(\n this.options.serviceUrl + location.lat + ',' + location.lng,\n params\n );\n const results: GeocodingResult[] = [];\n for (let i = data.resourceSets[0].resources.length - 1; i >= 0; i--) {\n const resource = data.resourceSets[0].resources[i],\n bbox = resource.bbox;\n results[i] = {\n name: resource.name,\n bbox: L.latLngBounds([bbox[0], bbox[1]], [bbox[2], bbox[3]]),\n center: L.latLng(resource.point.coordinates)\n };\n }\n return results;\n }\n}\n\n/**\n * [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link Bing}\n * @param options the options\n */\nexport function bing(options?: Partial) {\n return new Bing(options);\n}\n","import * as L from 'leaflet';\nimport { getJSON } from '../util';\nimport { GeocodingResult, IGeocoder } from './api';\n\nexport interface AzureMapsOptions {\n apiKey: string; // Azure Maps API Key\n serviceUrl: string; // Optional: Base URL for the Azure Maps API\n}\n\n/**\n * Implementation of [Azure Maps Geocoding](https://www.microsoft.com/en-us/maps/azure/location-services/geocoding)\n *\n * https://learn.microsoft.com/en-us/rest/api/maps/search?view=rest-maps-1.0\n */\nexport class AzureMaps implements IGeocoder {\n private options: AzureMapsOptions = {\n apiKey: '',\n serviceUrl: 'https://atlas.microsoft.com/search'\n };\n\n constructor(options: Partial) {\n L.Util.setOptions(this, options);\n if (!this.options.apiKey) {\n throw new Error('Azure Maps Geocoder requires an API key.');\n }\n }\n\n /**\n * {@inheritdoc}\n * https://learn.microsoft.com/en-us/rest/api/maps/search/get-search-address?view=rest-maps-1.0&tabs=HTTP\n */\n async geocode(query: string): Promise {\n const params = {\n 'api-version': '1.0',\n query,\n 'subscription-key': this.options.apiKey\n };\n const url = this.options.serviceUrl + '/address/json';\n const data = await getJSON(url, params);\n\n const results: GeocodingResult[] = [];\n if (data.results && data.results.length > 0) {\n for (const result of data.results) {\n results.push({\n name: result.address.freeformAddress,\n bbox: L.latLngBounds(\n [result.viewport.topLeftPoint.lat, result.viewport.topLeftPoint.lon],\n [result.viewport.btmRightPoint.lat, result.viewport.btmRightPoint.lon]\n ),\n center: L.latLng(result.position.lat, result.position.lon)\n });\n }\n }\n return results;\n }\n\n /**\n * {@inheritdoc}\n * https://learn.microsoft.com/en-us/rest/api/maps/search/get-search-address-reverse?view=rest-maps-1.0&tabs=HTTP\n */\n async reverse(location: L.LatLngLiteral, scale: number): Promise {\n const params = {\n 'api-version': '1.0',\n query: location.lat + ',' + location.lng,\n 'subscription-key': this.options.apiKey\n };\n const url = this.options.serviceUrl + '/address/reverse/json';\n const data = await getJSON(url, params);\n\n const results: GeocodingResult[] = [];\n if (data.addresses && data.addresses.length > 0) {\n for (const address of data.addresses) {\n results.push({\n name: address.address.freeformAddress,\n bbox: L.latLngBounds(\n [address.viewport.topLeftPoint.lat, address.viewport.topLeftPoint.lon],\n [address.viewport.btmRightPoint.lat, address.viewport.btmRightPoint.lon]\n ),\n center: L.latLng(location.lat, location.lng)\n });\n }\n }\n return results;\n }\n}\n\n/**\n * [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link Azure}\n * @param options the options\n */\nexport function azure(options: AzureMapsOptions) {\n return new AzureMaps(options);\n}\n\n/**\n * @internal\n */\nexport interface AzureMapsResponse {\n summary: Summary;\n results: Result[];\n}\n\ninterface Result {\n type: string;\n id: string;\n score: number;\n address: Address;\n position: Position;\n viewport: Viewport;\n entryPoints: EntryPoint[];\n}\n\ninterface Address {\n streetNumber: string;\n streetName: string;\n municipalitySubdivision: string;\n municipality: string;\n countrySecondarySubdivision: string;\n countryTertiarySubdivision: string;\n countrySubdivisionCode: string;\n postalCode: string;\n extendedPostalCode: string;\n countryCode: string;\n country: string;\n countryCodeISO3: string;\n freeformAddress: string;\n countrySubdivisionName: string;\n}\n\ninterface EntryPoint {\n type: string;\n position: Position;\n}\n\ninterface Position {\n lat: number;\n lon: number;\n}\n\ninterface Viewport {\n topLeftPoint: Position;\n btmRightPoint: Position;\n}\n\ninterface Summary {\n query: string;\n queryType: string;\n queryTime: number;\n numResults: number;\n offset: number;\n totalResults: number;\n fuzzyLevel: number;\n}\n","import * as L from 'leaflet';\nimport { getJSON } from '../util';\nimport { IGeocoder, GeocoderOptions, geocodingParams, GeocodingResult, reverseParams } from './api';\n\n/**\n * Implementation of the [Google Geocoding API](https://developers.google.com/maps/documentation/geocoding/)\n */\nexport interface GoogleOptions extends GeocoderOptions {}\n\nexport class Google implements IGeocoder {\n options: GoogleOptions = {\n serviceUrl: 'https://maps.googleapis.com/maps/api/geocode/json'\n };\n\n constructor(options?: Partial) {\n L.Util.setOptions(this, options);\n }\n\n async geocode(query: string): Promise {\n const params = geocodingParams(this.options, {\n key: this.options.apiKey,\n address: query\n });\n const data = await getJSON(this.options.serviceUrl, params);\n const results: GeocodingResult[] = [];\n if (data.results && data.results.length) {\n for (let i = 0; i <= data.results.length - 1; i++) {\n const loc = data.results[i];\n const latLng = L.latLng(loc.geometry.location);\n const latLngBounds = L.latLngBounds(\n L.latLng(loc.geometry.viewport.northeast),\n L.latLng(loc.geometry.viewport.southwest)\n );\n results[i] = {\n name: loc.formatted_address,\n bbox: latLngBounds,\n center: latLng,\n properties: loc.address_components\n };\n }\n }\n\n return results;\n }\n\n async reverse(location: L.LatLngLiteral, scale: number): Promise {\n const params = reverseParams(this.options, {\n key: this.options.apiKey,\n latlng: location.lat + ',' + location.lng\n });\n const data = await getJSON(this.options.serviceUrl, params);\n const results: GeocodingResult[] = [];\n if (data.results && data.results.length) {\n for (let i = 0; i <= data.results.length - 1; i++) {\n const loc = data.results[i];\n const center = L.latLng(loc.geometry.location);\n const bbox = L.latLngBounds(\n L.latLng(loc.geometry.viewport.northeast),\n L.latLng(loc.geometry.viewport.southwest)\n );\n results[i] = {\n name: loc.formatted_address,\n bbox: bbox,\n center: center,\n properties: loc.address_components\n };\n }\n }\n\n return results;\n }\n}\n\n/**\n * [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link Google}\n * @param options the options\n */\nexport function google(options?: Partial) {\n return new Google(options);\n}\n\n/**\n * @internal\n */\nexport interface GoogleResponse {\n results: Result[];\n status: string;\n}\n\ninterface Result {\n address_components: AddressComponent[];\n formatted_address: string;\n geometry: Geometry;\n place_id: string;\n types: string[];\n}\n\ninterface AddressComponent {\n long_name: string;\n short_name: string;\n types: string[];\n}\n\ninterface Geometry {\n bounds: Bounds;\n location: Location;\n location_type: string;\n viewport: Bounds;\n}\n\ninterface Bounds {\n northeast: Location;\n southwest: Location;\n}\n\ninterface Location {\n lat: number;\n lng: number;\n}\n","import * as L from 'leaflet';\nimport { getJSON } from '../util';\nimport { IGeocoder, GeocoderOptions, geocodingParams, GeocodingResult, reverseParams } from './api';\n\nexport interface HereOptions extends GeocoderOptions {\n /**\n * Use `apiKey` and the new `HEREv2` geocoder\n * @deprecated\n */\n app_id: string;\n /**\n * Use `apiKey` and the new `HEREv2` geocoder\n * @deprecated\n */\n app_code: string;\n reverseGeocodeProxRadius?: any;\n apiKey: string;\n maxResults: number;\n}\n\n/**\n * Implementation of the [HERE Geocoder API](https://developer.here.com/documentation/geocoder/topics/introduction.html)\n */\nexport class HERE implements IGeocoder {\n options: HereOptions = {\n serviceUrl: 'https://geocoder.api.here.com/6.2/',\n app_id: '',\n app_code: '',\n apiKey: '',\n maxResults: 5\n };\n\n constructor(options?: Partial) {\n L.Util.setOptions(this, options);\n if (options?.apiKey) throw Error('apiKey is not supported, use app_id/app_code instead!');\n }\n\n geocode(query: string): Promise {\n const params = geocodingParams(this.options, {\n searchtext: query,\n gen: 9,\n app_id: this.options.app_id,\n app_code: this.options.app_code,\n jsonattributes: 1,\n maxresults: this.options.maxResults\n });\n return this.getJSON(this.options.serviceUrl + 'geocode.json', params);\n }\n\n reverse(location: L.LatLngLiteral, scale: number): Promise {\n let prox = location.lat + ',' + location.lng;\n if (this.options.reverseGeocodeProxRadius) {\n prox += ',' + this.options.reverseGeocodeProxRadius;\n }\n const params = reverseParams(this.options, {\n prox,\n mode: 'retrieveAddresses',\n app_id: this.options.app_id,\n app_code: this.options.app_code,\n gen: 9,\n jsonattributes: 1,\n maxresults: this.options.maxResults\n });\n return this.getJSON(this.options.serviceUrl + 'reversegeocode.json', params);\n }\n\n async getJSON(url: string, params: any): Promise {\n const data = await getJSON(url, params);\n const results: GeocodingResult[] = [];\n\n if (data.response.view && data.response.view.length) {\n for (let i = 0; i <= data.response.view[0].result.length - 1; i++) {\n const loc = data.response.view[0].result[i].location;\n const center = L.latLng(loc.displayPosition.latitude, loc.displayPosition.longitude);\n const bbox = L.latLngBounds(\n L.latLng(loc.mapView.topLeft.latitude, loc.mapView.topLeft.longitude),\n L.latLng(loc.mapView.bottomRight.latitude, loc.mapView.bottomRight.longitude)\n );\n results[i] = {\n name: loc.address.label,\n properties: loc.address,\n bbox: bbox,\n center: center\n };\n }\n }\n return results;\n }\n}\n\n/**\n * Implementation of the new [HERE Geocoder API](https://developer.here.com/documentation/geocoding-search-api/api-reference-swagger.html)\n */\nexport class HEREv2 implements IGeocoder {\n options: HereOptions = {\n serviceUrl: 'https://geocode.search.hereapi.com/v1',\n apiKey: '',\n app_id: '',\n app_code: '',\n maxResults: 10\n };\n\n constructor(options?: Partial) {\n L.Util.setOptions(this, options);\n }\n\n geocode(query: string): Promise {\n const params = geocodingParams(this.options, {\n q: query,\n apiKey: this.options.apiKey,\n limit: this.options.maxResults\n });\n\n if (!params.at && !params.in) {\n throw Error(\n 'at / in parameters not found. Please define coordinates (at=latitude,longitude) or other (in) in your geocodingQueryParams.'\n );\n }\n\n return this.getJSON(this.options.serviceUrl + '/discover', params);\n }\n\n reverse(location: L.LatLngLiteral, scale: number): Promise {\n const params = reverseParams(this.options, {\n at: location.lat + ',' + location.lng,\n limit: this.options.reverseGeocodeProxRadius,\n apiKey: this.options.apiKey\n });\n return this.getJSON(this.options.serviceUrl + '/revgeocode', params);\n }\n\n async getJSON(url: string, params: any): Promise {\n const data = await getJSON(url, params);\n const results: GeocodingResult[] = [];\n\n if (data.items && data.items.length) {\n for (let i = 0; i <= data.items.length - 1; i++) {\n const item = data.items[i];\n const latLng = L.latLng(item.position.lat, item.position.lng);\n let bbox: L.LatLngBounds;\n if (item.mapView) {\n bbox = L.latLngBounds(\n L.latLng(item.mapView.south, item.mapView.west),\n L.latLng(item.mapView.north, item.mapView.east)\n );\n } else {\n // Using only position when not provided\n bbox = L.latLngBounds(\n L.latLng(item.position.lat, item.position.lng),\n L.latLng(item.position.lat, item.position.lng)\n );\n }\n results[i] = {\n name: item.address.label,\n properties: item.address,\n bbox: bbox,\n center: latLng\n };\n }\n }\n return results;\n }\n}\n\n/**\n * [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link HERE}\n * @param options the options\n */\nexport function here(options?: Partial) {\n if (options?.apiKey) {\n return new HEREv2(options);\n } else {\n return new HERE(options);\n }\n}\n\n/**\n * @internal\n */\nexport interface HEREv2Response {\n items: Item[];\n}\n\ninterface Item {\n title: string;\n id: string;\n ontologyId: string;\n resultType: string;\n address: Address;\n mapView?: MapView;\n position: Position;\n access: Position[];\n distance: number;\n categories: Category[];\n references: Reference[];\n foodTypes: Category[];\n contacts: Contact[];\n openingHours: OpeningHour[];\n}\n\ninterface MapView {\n east: number;\n north: number;\n south: number;\n west: number;\n}\n\ninterface Position {\n lat: number;\n lng: number;\n}\n\ninterface Address {\n label: string;\n countryCode: string;\n countryName: string;\n stateCode: string;\n state: string;\n county: string;\n city: string;\n district: string;\n street: string;\n postalCode: string;\n houseNumber: string;\n}\n\ninterface Category {\n id: string;\n name: string;\n primary?: boolean;\n}\n\ninterface Contact {\n phone: Email[];\n fax: Email[];\n www: Email[];\n email: Email[];\n}\n\ninterface Email {\n value: string;\n}\n\ninterface OpeningHour {\n text: string[];\n isOpen: boolean;\n structured: Structured[];\n}\n\ninterface Structured {\n start: string;\n duration: string;\n recurrence: string;\n}\n\ninterface Reference {\n supplier: Supplier;\n id: string;\n}\n\ninterface Supplier {\n id: string;\n}\n","import * as L from 'leaflet';\nimport { IGeocoder, GeocodingResult } from './api';\n\nexport interface LatLngOptions {\n /**\n * The next geocoder to use for non-supported queries\n */\n next?: IGeocoder;\n /**\n * The size in meters used for passing to `LatLng.toBounds`\n */\n sizeInMeters: number;\n}\n\n/**\n * Parses basic latitude/longitude strings such as `'50.06773 14.37742'`, `'N50.06773 W14.37742'`, `'S 50° 04.064 E 014° 22.645'`, or `'S 50° 4′ 03.828″, W 14° 22′ 38.712″'`\n * @param query the latitude/longitude string to parse\n * @returns the parsed latitude/longitude\n */\nexport function parseLatLng(query: string): L.LatLng | undefined {\n let match;\n // regex from https://github.com/openstreetmap/openstreetmap-website/blob/master/app/controllers/geocoder_controller.rb\n if ((match = query.match(/^([NS])\\s*(\\d{1,3}(?:\\.\\d*)?)\\W*([EW])\\s*(\\d{1,3}(?:\\.\\d*)?)$/))) {\n // [NSEW] decimal degrees\n return L.latLng(\n (/N/i.test(match[1]) ? 1 : -1) * +match[2],\n (/E/i.test(match[3]) ? 1 : -1) * +match[4]\n );\n } else if (\n (match = query.match(/^(\\d{1,3}(?:\\.\\d*)?)\\s*([NS])\\W*(\\d{1,3}(?:\\.\\d*)?)\\s*([EW])$/))\n ) {\n // decimal degrees [NSEW]\n return L.latLng(\n (/N/i.test(match[2]) ? 1 : -1) * +match[1],\n (/E/i.test(match[4]) ? 1 : -1) * +match[3]\n );\n } else if (\n (match = query.match(\n /^([NS])\\s*(\\d{1,3})°?\\s*(\\d{1,3}(?:\\.\\d*)?)?['′]?\\W*([EW])\\s*(\\d{1,3})°?\\s*(\\d{1,3}(?:\\.\\d*)?)?['′]?$/\n ))\n ) {\n // [NSEW] degrees, decimal minutes\n return L.latLng(\n (/N/i.test(match[1]) ? 1 : -1) * (+match[2] + +match[3] / 60),\n (/E/i.test(match[4]) ? 1 : -1) * (+match[5] + +match[6] / 60)\n );\n } else if (\n (match = query.match(\n /^(\\d{1,3})°?\\s*(\\d{1,3}(?:\\.\\d*)?)?['′]?\\s*([NS])\\W*(\\d{1,3})°?\\s*(\\d{1,3}(?:\\.\\d*)?)?['′]?\\s*([EW])$/\n ))\n ) {\n // degrees, decimal minutes [NSEW]\n return L.latLng(\n (/N/i.test(match[3]) ? 1 : -1) * (+match[1] + +match[2] / 60),\n (/E/i.test(match[6]) ? 1 : -1) * (+match[4] + +match[5] / 60)\n );\n } else if (\n (match = query.match(\n /^([NS])\\s*(\\d{1,3})°?\\s*(\\d{1,2})['′]?\\s*(\\d{1,3}(?:\\.\\d*)?)?[\"″]?\\W*([EW])\\s*(\\d{1,3})°?\\s*(\\d{1,2})['′]?\\s*(\\d{1,3}(?:\\.\\d*)?)?[\"″]?$/\n ))\n ) {\n // [NSEW] degrees, minutes, decimal seconds\n return L.latLng(\n (/N/i.test(match[1]) ? 1 : -1) * (+match[2] + +match[3] / 60 + +match[4] / 3600),\n (/E/i.test(match[5]) ? 1 : -1) * (+match[6] + +match[7] / 60 + +match[8] / 3600)\n );\n } else if (\n (match = query.match(\n /^(\\d{1,3})°?\\s*(\\d{1,2})['′]?\\s*(\\d{1,3}(?:\\.\\d*)?)?[\"″]\\s*([NS])\\W*(\\d{1,3})°?\\s*(\\d{1,2})['′]?\\s*(\\d{1,3}(?:\\.\\d*)?)?[\"″]?\\s*([EW])$/\n ))\n ) {\n // degrees, minutes, decimal seconds [NSEW]\n return L.latLng(\n (/N/i.test(match[4]) ? 1 : -1) * (+match[1] + +match[2] / 60 + +match[3] / 3600),\n (/E/i.test(match[8]) ? 1 : -1) * (+match[5] + +match[6] / 60 + +match[7] / 3600)\n );\n } else if ((match = query.match(/^\\s*([+-]?\\d+(?:\\.\\d*)?)\\s*[\\s,]\\s*([+-]?\\d+(?:\\.\\d*)?)\\s*$/))) {\n return L.latLng(+match[1], +match[2]);\n }\n}\n\n/**\n * Parses basic latitude/longitude strings such as `'50.06773 14.37742'`, `'N50.06773 W14.37742'`, `'S 50° 04.064 E 014° 22.645'`, or `'S 50° 4′ 03.828″, W 14° 22′ 38.712″'`\n */\nexport class LatLng implements IGeocoder {\n options: LatLngOptions = {\n next: undefined,\n sizeInMeters: 10000\n };\n\n constructor(options?: Partial) {\n L.Util.setOptions(this, options);\n }\n\n async geocode(query: string) {\n const center = parseLatLng(query);\n if (center) {\n const results: GeocodingResult[] = [\n {\n name: query,\n center: center,\n bbox: center.toBounds(this.options.sizeInMeters)\n }\n ];\n return results;\n } else if (this.options.next) {\n return this.options.next.geocode(query);\n } else {\n return [];\n }\n }\n}\n\n/**\n * [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link LatLng}\n * @param options the options\n */\nexport function latLng(options?: Partial) {\n return new LatLng(options);\n}\n","import * as L from 'leaflet';\nimport { getJSON } from '../util';\nimport { IGeocoder, GeocoderOptions, geocodingParams, GeocodingResult, reverseParams } from './api';\n\nexport interface MapboxOptions extends GeocoderOptions {}\n\n/**\n * Implementation of the [Mapbox Geocoding](https://www.mapbox.com/api-documentation/#geocoding)\n */\nexport class Mapbox implements IGeocoder {\n options: MapboxOptions = {\n serviceUrl: 'https://api.mapbox.com/geocoding/v5/mapbox.places/'\n };\n\n constructor(options?: Partial) {\n L.Util.setOptions(this, options);\n }\n\n _getProperties(loc) {\n const properties = {\n text: loc.text,\n address: loc.address\n };\n\n for (let j = 0; j < (loc.context || []).length; j++) {\n const id = loc.context[j].id.split('.')[0];\n properties[id] = loc.context[j].text;\n\n // Get country code when available\n if (loc.context[j].short_code) {\n properties['countryShortCode'] = loc.context[j].short_code;\n }\n }\n return properties;\n }\n\n async geocode(query: string): Promise {\n const url = this.options.serviceUrl + encodeURIComponent(query) + '.json';\n const params: any = geocodingParams(this.options, {\n access_token: this.options.apiKey\n });\n if (\n params.proximity !== undefined &&\n params.proximity.lat !== undefined &&\n params.proximity.lng !== undefined\n ) {\n params.proximity = params.proximity.lng + ',' + params.proximity.lat;\n }\n const data = await getJSON(url, params);\n return this._parseResults(data);\n }\n\n suggest(query: string): Promise {\n return this.geocode(query);\n }\n\n async reverse(location: L.LatLngLiteral, scale: number): Promise {\n const url = this.options.serviceUrl + location.lng + ',' + location.lat + '.json';\n const param = reverseParams(this.options, {\n access_token: this.options.apiKey\n });\n const data = await getJSON(url, param);\n return this._parseResults(data);\n }\n\n private _parseResults(data: MapboxResponse): any[] | GeocodingResult[] {\n if (!data.features?.length) {\n return [];\n }\n const results: GeocodingResult[] = [];\n for (let i = 0; i <= data.features.length - 1; i++) {\n const loc = data.features[i];\n const center = L.latLng(loc.center.reverse() as [number, number]);\n let bbox: L.LatLngBounds;\n if (loc.bbox) {\n bbox = L.latLngBounds(\n L.latLng(loc.bbox.slice(0, 2).reverse() as [number, number]),\n L.latLng(loc.bbox.slice(2, 4).reverse() as [number, number])\n );\n } else {\n bbox = L.latLngBounds(center, center);\n }\n results[i] = {\n name: loc.place_name,\n bbox: bbox,\n center: center,\n properties: this._getProperties(loc)\n };\n }\n\n return results;\n }\n}\n\n/**\n * [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link Mapbox}\n * @param options the options\n */\nexport function mapbox(options?: Partial) {\n return new Mapbox(options);\n}\n\n/**\n * @internal\n */\nexport interface MapboxResponse {\n type: string;\n query: string[];\n features: Feature[];\n attribution: string;\n}\n\ninterface Feature {\n id: string;\n type: string;\n place_type: string[];\n relevance: number;\n properties: Properties;\n text: string;\n place_name: string;\n matching_text: string;\n matching_place_name: string;\n center: [number, number];\n bbox?: [number, number, number, number];\n geometry: Geometry;\n address: string;\n context: Context[];\n}\n\ninterface Context {\n id: string;\n text: string;\n wikidata?: string;\n short_code?: string;\n}\n\ninterface Geometry {\n type: string;\n coordinates: number[];\n interpolated: boolean;\n omitted: boolean;\n}\n\ninterface Properties {}\n","import * as L from 'leaflet';\nimport { getJSON } from '../util';\nimport { IGeocoder, GeocoderOptions, geocodingParams, GeocodingResult, reverseParams } from './api';\n\nexport interface MapQuestOptions extends GeocoderOptions {}\n\n/**\n * Implementation of the [MapQuest Geocoding API](http://developer.mapquest.com/web/products/dev-services/geocoding-ws)\n */\nexport class MapQuest implements IGeocoder {\n options: MapQuestOptions = {\n serviceUrl: 'https://www.mapquestapi.com/geocoding/v1'\n };\n\n constructor(options?: Partial) {\n L.Util.setOptions(this, options);\n // MapQuest seems to provide URI encoded API keys,\n // so to avoid encoding them twice, we decode them here\n this.options.apiKey = decodeURIComponent(this.options.apiKey!);\n }\n\n _formatName(...parts: string[]) {\n return parts.filter(s => !!s).join(', ');\n }\n\n async geocode(query: string): Promise {\n const params = geocodingParams(this.options, {\n key: this.options.apiKey,\n location: query,\n limit: 5,\n outFormat: 'json'\n });\n const data = await getJSON(this.options.serviceUrl + '/address', params);\n return this._parseResults(data);\n }\n\n async reverse(location: L.LatLngLiteral, scale: number): Promise {\n const params = reverseParams(this.options, {\n key: this.options.apiKey,\n location: location.lat + ',' + location.lng,\n outputFormat: 'json'\n });\n const data = await getJSON(this.options.serviceUrl + '/reverse', params);\n return this._parseResults(data);\n }\n\n private _parseResults(data): GeocodingResult[] {\n const results: GeocodingResult[] = [];\n if (data.results && data.results[0].locations) {\n for (let i = data.results[0].locations.length - 1; i >= 0; i--) {\n const loc = data.results[0].locations[i];\n const center = L.latLng(loc.latLng);\n results[i] = {\n name: this._formatName(loc.street, loc.adminArea4, loc.adminArea3, loc.adminArea1),\n bbox: L.latLngBounds(center, center),\n center: center\n };\n }\n }\n return results;\n }\n}\n\n/**\n * [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link MapQuest}\n * @param options the options\n */\nexport function mapQuest(options?: Partial) {\n return new MapQuest(options);\n}\n","import * as L from 'leaflet';\nimport { getJSON } from '../util';\nimport { IGeocoder, GeocoderOptions, geocodingParams, GeocodingResult, reverseParams } from './api';\n\nexport interface NeutrinoOptions extends GeocoderOptions {\n userId: string;\n}\n\n/**\n * Implementation of the [Neutrino API](https://www.neutrinoapi.com/api/geocode-address/)\n */\nexport class Neutrino implements IGeocoder {\n options: NeutrinoOptions = {\n userId: '',\n apiKey: '',\n serviceUrl: 'https://neutrinoapi.com/'\n };\n\n constructor(options?: Partial) {\n L.Util.setOptions(this, options);\n }\n\n // https://www.neutrinoapi.com/api/geocode-address/\n async geocode(query: string): Promise {\n const params = geocodingParams(this.options, {\n apiKey: this.options.apiKey,\n userId: this.options.userId,\n //get three words and make a dot based string\n address: query.split(/\\s+/).join('.')\n });\n const data = await getJSON(this.options.serviceUrl + 'geocode-address', params);\n const results: GeocodingResult[] = [];\n if (data.locations) {\n data.geometry = data.locations[0];\n const center = L.latLng(data.geometry['latitude'], data.geometry['longitude']);\n const bbox = L.latLngBounds(center, center);\n results[0] = {\n name: data.geometry.address,\n bbox: bbox,\n center: center\n };\n }\n\n return results;\n }\n\n suggest(query: string): Promise {\n return this.geocode(query);\n }\n\n // https://www.neutrinoapi.com/api/geocode-reverse/\n async reverse(location: L.LatLngLiteral, scale: number): Promise {\n const params = reverseParams(this.options, {\n apiKey: this.options.apiKey,\n userId: this.options.userId,\n latitude: location.lat,\n longitude: location.lng\n });\n const data = await getJSON(this.options.serviceUrl + 'geocode-reverse', params);\n const results: GeocodingResult[] = [];\n if (data.status.status == 200 && data.found) {\n const center = L.latLng(location.lat, location.lng);\n const bbox = L.latLngBounds(center, center);\n results[0] = {\n name: data.address,\n bbox: bbox,\n center: center\n };\n }\n return results;\n }\n}\n\n/**\n * [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link Neutrino}\n * @param options the options\n */\nexport function neutrino(options?: Partial) {\n return new Neutrino(options);\n}\n","import * as L from 'leaflet';\nimport { template, getJSON } from '../util';\nimport { IGeocoder, GeocoderOptions, geocodingParams, GeocodingResult, reverseParams } from './api';\n\nexport type NominatimResponse = NominatimResult[];\n\nexport interface NominatimResult {\n place_id: number;\n licence: string;\n osm_type: string;\n osm_id: number;\n boundingbox: string[];\n lat: string;\n lon: string;\n display_name: string;\n class?: string;\n type?: string;\n importance?: number;\n icon?: string;\n address: NominatimAddress;\n}\n\nexport interface NominatimAddress {\n building?: string;\n city_district?: string;\n city?: string;\n country_code?: string;\n country?: string;\n county?: string;\n hamlet?: string;\n house_number?: string;\n neighbourhood?: string;\n postcode?: string;\n road?: string;\n state_district?: string;\n state?: string;\n suburb?: string;\n village?: string;\n}\n\nexport interface NominatimOptions extends GeocoderOptions {\n /**\n * Additional URL parameters (strings) that will be added to geocoding requests; can be used to restrict results to a specific country for example, by providing the [`countrycodes`](https://wiki.openstreetmap.org/wiki/Nominatim#Parameters) parameter to Nominatim\n */\n geocodingQueryParams?: Record;\n /**\n * A function that takes an GeocodingResult as argument and returns an HTML formatted string that represents the result. Default function breaks up address in parts from most to least specific, in attempt to increase readability compared to Nominatim's naming\n */\n htmlTemplate: (r: NominatimResult) => string;\n}\n\n/**\n * Implementation of the [Nominatim](https://wiki.openstreetmap.org/wiki/Nominatim) geocoder.\n *\n * This is the default geocoding service used by the control, unless otherwise specified in the options.\n *\n * Unless using your own Nominatim installation, please refer to the [Nominatim usage policy](https://operations.osmfoundation.org/policies/nominatim/).\n */\nexport class Nominatim implements IGeocoder {\n options: NominatimOptions = {\n serviceUrl: 'https://nominatim.openstreetmap.org/',\n htmlTemplate(r: NominatimResult) {\n const address = r.address;\n let className: string;\n const parts: string[] = [];\n if (address.road || address.building) {\n parts.push('{building} {road} {house_number}');\n }\n\n if (address.city || (address as any).town || address.village || address.hamlet) {\n className = parts.length > 0 ? 'leaflet-control-geocoder-address-detail' : '';\n parts.push(\n '{postcode} {city} {town} {village} {hamlet}'\n );\n }\n\n if (address.state || address.country) {\n className = parts.length > 0 ? 'leaflet-control-geocoder-address-context' : '';\n parts.push('{state} {country}');\n }\n\n return template(parts.join('
'), address);\n }\n };\n\n constructor(options?: Partial) {\n L.Util.setOptions(this, options || {});\n }\n\n async geocode(query: string) {\n const params = geocodingParams(this.options, {\n q: query,\n limit: 5,\n format: 'json',\n addressdetails: 1\n });\n const data = await getJSON(this.options.serviceUrl + 'search', params);\n const results: GeocodingResult[] = [];\n for (let i = data.length - 1; i >= 0; i--) {\n const bbox = data[i].boundingbox;\n results[i] = {\n icon: data[i].icon,\n name: data[i].display_name,\n html: this.options.htmlTemplate ? this.options.htmlTemplate(data[i]) : undefined,\n bbox: L.latLngBounds([+bbox[0], +bbox[2]], [+bbox[1], +bbox[3]]),\n center: L.latLng(+data[i].lat, +data[i].lon),\n properties: data[i]\n };\n }\n return results;\n }\n\n async reverse(location: L.LatLngLiteral, scale: number) {\n const params = reverseParams(this.options, {\n lat: location.lat,\n lon: location.lng,\n zoom: Math.round(Math.log(scale / 256) / Math.log(2)),\n addressdetails: 1,\n format: 'json'\n });\n const data = await getJSON(this.options.serviceUrl + 'reverse', params);\n const results: GeocodingResult[] = [];\n if (data && data.lat && data.lon) {\n const center = L.latLng(+data.lat, +data.lon);\n const bbox = L.latLngBounds(center, center);\n results.push({\n name: data.display_name,\n html: this.options.htmlTemplate ? this.options.htmlTemplate(data) : undefined,\n center: center,\n bbox: bbox,\n properties: data\n });\n }\n return results;\n }\n}\n\n/**\n * [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link Nominatim}\n * @param options the options\n */\nexport function nominatim(options?: Partial) {\n return new Nominatim(options);\n}\n","import * as L from 'leaflet';\nimport { IGeocoder, GeocodingResult } from './api';\n\nexport interface OpenLocationCodeOptions {\n OpenLocationCode: OpenLocationCodeApi;\n codeLength?: number;\n}\n\nexport interface OpenLocationCodeApi {\n encode(latitude: number, longitude: number, codeLength?: number): string;\n decode(code: string): CodeArea;\n}\n\nexport interface CodeArea {\n latitudeLo: number;\n longitudeLo: number;\n latitudeHi: number;\n longitudeHi: number;\n latitudeCenter: number;\n longitudeCenter: number;\n codeLength: number;\n}\n\n/**\n * Implementation of the [Plus codes](https://plus.codes/) (formerly OpenLocationCode) (requires [open-location-code](https://www.npmjs.com/package/open-location-code))\n */\nexport class OpenLocationCode implements IGeocoder {\n options = {} as OpenLocationCodeOptions;\n constructor(options?: Partial) {\n L.Util.setOptions(this, options);\n }\n\n async geocode(query: string) {\n try {\n const decoded = this.options.OpenLocationCode.decode(query);\n const result: GeocodingResult = {\n name: query,\n center: L.latLng(decoded.latitudeCenter, decoded.longitudeCenter),\n bbox: L.latLngBounds(\n L.latLng(decoded.latitudeLo, decoded.longitudeLo),\n L.latLng(decoded.latitudeHi, decoded.longitudeHi)\n )\n };\n return [result];\n } catch (e) {\n console.warn(e); // eslint-disable-line no-console\n return [];\n }\n }\n async reverse(location: L.LatLngLiteral, scale: number) {\n try {\n const code = this.options.OpenLocationCode.encode(\n location.lat,\n location.lng,\n this.options.codeLength\n );\n const result = {\n name: code,\n center: L.latLng(location.lat, location.lng),\n bbox: L.latLngBounds(\n L.latLng(location.lat, location.lng),\n L.latLng(location.lat, location.lng)\n )\n };\n return [result];\n } catch (e) {\n console.warn(e); // eslint-disable-line no-console\n return [];\n }\n }\n}\n\n/**\n * [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link OpenLocationCode}\n * @param options the options\n */\nexport function openLocationCode(options?: Partial) {\n return new OpenLocationCode(options);\n}\n","import * as L from 'leaflet';\nimport { getJSON } from '../util';\nimport { IGeocoder, GeocoderOptions, geocodingParams, GeocodingResult, reverseParams } from './api';\n\nexport interface OpenCageOptions extends GeocoderOptions {}\n\n/**\n * Implementation of the [OpenCage Data API](https://opencagedata.com/)\n */\nexport class OpenCage implements IGeocoder {\n options: OpenCageOptions = {\n serviceUrl: 'https://api.opencagedata.com/geocode/v1/json'\n };\n\n constructor(options?: Partial) {\n L.Util.setOptions(this, options);\n }\n\n async geocode(query: string): Promise {\n const params = geocodingParams(this.options, {\n key: this.options.apiKey,\n q: query\n });\n const data = await getJSON(this.options.serviceUrl, params);\n return this._parseResults(data);\n }\n\n suggest(query: string): Promise {\n return this.geocode(query);\n }\n\n async reverse(location: L.LatLngLiteral, scale: number): Promise {\n const params = reverseParams(this.options, {\n key: this.options.apiKey,\n q: [location.lat, location.lng].join(',')\n });\n const data = await getJSON(this.options.serviceUrl, params);\n return this._parseResults(data);\n }\n\n private _parseResults(data): GeocodingResult[] {\n const results: GeocodingResult[] = [];\n if (data.results && data.results.length) {\n for (let i = 0; i < data.results.length; i++) {\n const loc = data.results[i];\n const center = L.latLng(loc.geometry);\n let bbox: L.LatLngBounds;\n if (loc.annotations && loc.annotations.bounds) {\n bbox = L.latLngBounds(\n L.latLng(loc.annotations.bounds.northeast),\n L.latLng(loc.annotations.bounds.southwest)\n );\n } else {\n bbox = L.latLngBounds(center, center);\n }\n results.push({\n name: loc.formatted,\n bbox: bbox,\n center: center\n });\n }\n }\n return results;\n }\n}\n\nexport function opencage(options?: Partial) {\n return new OpenCage(options);\n}\n","import * as L from 'leaflet';\nimport { getJSON } from '../util';\nimport { IGeocoder, GeocoderOptions, geocodingParams, GeocodingResult, reverseParams } from './api';\n\nexport interface PeliasOptions extends GeocoderOptions {}\n\n/**\n * Implementation of the [Pelias](https://pelias.io/), [geocode.earth](https://geocode.earth/) geocoder (formerly Mapzen Search)\n */\nexport class Pelias implements IGeocoder {\n options: PeliasOptions = {\n serviceUrl: 'https://api.geocode.earth/v1'\n };\n\n constructor(options?: Partial) {\n L.Util.setOptions(this, options);\n }\n\n async geocode(query: string): Promise {\n const params = geocodingParams(this.options, {\n api_key: this.options.apiKey,\n text: query\n });\n const data = await getJSON(this.options.serviceUrl + '/search', params);\n return this._parseResults(data, 'bbox');\n }\n\n async suggest(query: string): Promise {\n const params = geocodingParams(this.options, {\n api_key: this.options.apiKey,\n text: query\n });\n const data = await getJSON(this.options.serviceUrl + '/autocomplete', params);\n return this._parseResults(data, 'bbox');\n }\n\n async reverse(location: L.LatLngLiteral, scale: number): Promise {\n const params = reverseParams(this.options, {\n api_key: this.options.apiKey,\n 'point.lat': location.lat,\n 'point.lon': location.lng\n });\n const data = await getJSON(this.options.serviceUrl + '/reverse', params);\n return this._parseResults(data, 'bounds');\n }\n\n _parseResults(data, bboxname): GeocodingResult[] {\n const results: GeocodingResult[] = [];\n L.geoJSON(data, {\n pointToLayer(feature, latlng) {\n return L.circleMarker(latlng);\n },\n onEachFeature(feature, layer: any) {\n const result = {} as GeocodingResult;\n let bbox;\n let center;\n\n if (layer.getBounds) {\n bbox = layer.getBounds();\n center = bbox.getCenter();\n } else if (layer.feature.bbox) {\n center = layer.getLatLng();\n bbox = L.latLngBounds(\n L.GeoJSON.coordsToLatLng(layer.feature.bbox.slice(0, 2)),\n L.GeoJSON.coordsToLatLng(layer.feature.bbox.slice(2, 4))\n );\n } else {\n center = layer.getLatLng();\n bbox = L.latLngBounds(center, center);\n }\n\n result.name = layer.feature.properties.label;\n result.center = center;\n result[bboxname] = bbox;\n result.properties = layer.feature.properties;\n results.push(result);\n }\n });\n return results;\n }\n}\n\n/**\n * [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link Pelias}\n * @param options the options\n */\nexport function pelias(options?: Partial) {\n return new Pelias(options);\n}\n\nexport const GeocodeEarth = Pelias;\nexport const geocodeEarth = pelias;\n\n/**\n * r.i.p.\n * @deprecated\n */\nexport const Mapzen = Pelias;\n/**\n * r.i.p.\n * @deprecated\n */\nexport const mapzen = pelias;\n\n/**\n * Implementation of the [Openrouteservice](https://openrouteservice.org/dev/#/api-docs/geocode) geocoder\n */\nexport class Openrouteservice extends Pelias {\n constructor(options?: Partial) {\n super(\n L.Util.extend(\n {\n serviceUrl: 'https://api.openrouteservice.org/geocode'\n },\n options\n )\n );\n }\n}\n\n/**\n * [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link Openrouteservice}\n * @param options the options\n */\nexport function openrouteservice(options?: Partial) {\n return new Openrouteservice(options);\n}\n\n/**\n * @internal\n */\nexport type PeliasResponse = GeoJSON.FeatureCollection & {\n geocoding: Geocoding;\n};\n\ninterface Properties {\n id: string;\n layer: string;\n source_id: string;\n name: string;\n confidence: number;\n match_type: string;\n accuracy: string;\n country: string;\n country_a: string;\n region: string;\n region_a: string;\n county: string;\n county_a: string;\n localadmin: string;\n locality: string;\n continent: string;\n label: string;\n}\n\ninterface Geocoding {\n version: string;\n attribution: string;\n query: Query;\n warnings: string[];\n engine: Engine;\n}\n\ninterface Engine {\n name: string;\n author: string;\n version: string;\n}\n\ninterface Query {\n}\n","import * as L from 'leaflet';\nimport { getJSON } from '../util';\nimport { IGeocoder, GeocoderOptions, geocodingParams, GeocodingResult, reverseParams } from './api';\n\nexport interface PhotonOptions extends GeocoderOptions {\n reverseUrl: string;\n nameProperties: string[];\n htmlTemplate?: (r: any) => string;\n}\n\n/**\n * Implementation of the [Photon](http://photon.komoot.de/) geocoder\n */\nexport class Photon implements IGeocoder {\n options: PhotonOptions = {\n serviceUrl: 'https://photon.komoot.io/api/',\n reverseUrl: 'https://photon.komoot.io/reverse/',\n nameProperties: ['name', 'street', 'suburb', 'hamlet', 'town', 'city', 'state', 'country']\n };\n\n constructor(options?: Partial) {\n L.Util.setOptions(this, options);\n }\n\n async geocode(query: string): Promise {\n const params = geocodingParams(this.options, { q: query });\n const data = await getJSON(this.options.serviceUrl, params);\n return this._parseResults(data);\n }\n\n suggest(query: string): Promise {\n return this.geocode(query);\n }\n\n async reverse(latLng: L.LatLngLiteral, scale: number): Promise {\n const params = reverseParams(this.options, {\n lat: latLng.lat,\n lon: latLng.lng\n });\n const data = await getJSON(this.options.reverseUrl, params);\n return this._parseResults(data);\n }\n\n _parseResults(data: GeoJSON.FeatureCollection): GeocodingResult[] {\n const results: GeocodingResult[] = [];\n\n if (data && data.features) {\n for (let i = 0; i < data.features.length; i++) {\n const f = data.features[i];\n const c = f.geometry.coordinates;\n const center = L.latLng(c[1], c[0]);\n const extent = f.properties?.extent;\n\n const bbox = extent\n ? L.latLngBounds([extent[1], extent[0]], [extent[3], extent[2]])\n : L.latLngBounds(center, center);\n\n results.push({\n name: this._decodeFeatureName(f),\n html: this.options.htmlTemplate ? this.options.htmlTemplate(f) : undefined,\n center: center,\n bbox: bbox,\n properties: f.properties\n });\n }\n }\n\n return results;\n }\n\n _decodeFeatureName(f: GeoJSON.Feature) {\n return (this.options.nameProperties || [])\n .map(p => f.properties?.[p])\n .filter(v => !!v)\n .join(', ');\n }\n}\n\n/**\n * [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link Photon}\n * @param options the options\n */\nexport function photon(options?: Partial) {\n return new Photon(options);\n}\n\n/**\n * @internal\n */\nexport type PhotonResponse = GeoJSON.FeatureCollection;\n\ninterface PhotonProperties {\n osm_id: number;\n osm_type: string;\n extent?: number[];\n country: string;\n osm_key: string;\n city: string;\n countrycode: string;\n osm_value: string;\n name: string;\n state: string;\n type: string;\n postcode?: string;\n housenumber?: string;\n street?: string;\n district?: string;\n}\n","import * as L from 'leaflet';\nimport { getJSON } from '../util';\nimport { IGeocoder, GeocoderOptions, geocodingParams, GeocodingResult, reverseParams } from './api';\n\nexport interface What3WordsOptions extends GeocoderOptions {}\n\n/**\n * Implementation of the What3Words service\n */\nexport class What3Words implements IGeocoder {\n options: What3WordsOptions = {\n serviceUrl: 'https://api.what3words.com/v2/'\n };\n\n constructor(options: Partial) {\n L.Util.setOptions(this, options);\n }\n\n async geocode(query: string): Promise {\n const data = await getJSON(\n this.options.serviceUrl + 'forward',\n geocodingParams(this.options, {\n key: this.options.apiKey,\n //get three words and make a dot based string\n addr: query.split(/\\s+/).join('.')\n })\n );\n const results: GeocodingResult[] = [];\n if (data.geometry) {\n const latLng = L.latLng(data.geometry['lat'], data.geometry['lng']);\n const latLngBounds = L.latLngBounds(latLng, latLng);\n results[0] = {\n name: data.words,\n bbox: latLngBounds,\n center: latLng\n };\n }\n return results;\n }\n\n suggest(query: string): Promise {\n return this.geocode(query);\n }\n\n async reverse(location: L.LatLngLiteral, scale: number): Promise {\n const data = await getJSON(\n this.options.serviceUrl + 'reverse',\n reverseParams(this.options, {\n key: this.options.apiKey,\n coords: [location.lat, location.lng].join(',')\n })\n );\n const results: GeocodingResult[] = [];\n if (data.status.status == 200) {\n const center = L.latLng(data.geometry['lat'], data.geometry['lng']);\n const bbox = L.latLngBounds(center, center);\n results[0] = {\n name: data.words,\n bbox: bbox,\n center: center\n };\n }\n return results;\n }\n}\n\n/**\n * [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link What3Words}\n * @param options the options\n */\nexport function what3words(options: Partial) {\n return new What3Words(options);\n}\n","import * as L from 'leaflet';\nimport { Nominatim } from './geocoders/index';\nimport { IGeocoder, GeocodingResult } from './geocoders/api';\n\nexport interface GeocoderControlOptions extends L.ControlOptions {\n /**\n * Collapse control unless hovered/clicked\n */\n collapsed: boolean;\n /**\n * How to expand a collapsed control: `touch` or `click` or `hover`\n */\n expand: 'touch' | 'click' | 'hover';\n /**\n * Placeholder text for text input\n */\n placeholder: string;\n /**\n * Message when no result found / geocoding error occurs\n */\n errorMessage: string;\n /**\n * Accessibility label for the search icon used by screen readers\n */\n iconLabel: string;\n /**\n * Object to perform the actual geocoding queries\n */\n geocoder?: IGeocoder;\n /**\n * Immediately show the unique result without prompting for alternatives\n */\n showUniqueResult: boolean;\n /**\n * Show icons for geocoding results (if available); supported by Nominatim\n */\n showResultIcons: boolean;\n /**\n * Minimum number characters before suggest functionality is used (if available from geocoder)\n */\n suggestMinLength: number;\n /**\n * Number of milliseconds after typing stopped before suggest functionality is used (if available from geocoder)\n */\n suggestTimeout: number;\n /**\n * Initial query string for text input\n */\n query: string;\n /**\n * Minimum number of characters in search text before performing a query\n */\n queryMinLength: number;\n /**\n * Whether to mark a geocoding result on the map by default\n */\n defaultMarkGeocode: boolean;\n}\n\n/**\n * Event is fired when selecting a geocode result.\n * By default, the control will center the map on it and place a marker at its location.\n * To remove the control's default handler for marking a result, set {@link GeocoderControlOptions.defaultMarkGeocode} to `false`.\n */\nexport type MarkGeocodeEvent = { geocode: GeocodingResult };\nexport type MarkGeocodeEventHandlerFn = (event: MarkGeocodeEvent) => void;\n\n/**\n * Event is fired before invoking {@link IGeocoder.geocode} (or {@link IGeocoder.suggest}).\n * The event data contains the query string as `input`.\n */\nexport type StartGeocodeEvent = { input: string };\nexport type StartGeocodeEventHandlerFn = (event: StartGeocodeEvent) => void;\n\n/**\n * Event is fired before after receiving results from {@link IGeocoder.geocode} (or {@link IGeocoder.suggest}).\n * The event data contains the query string as `input` and the geocoding `results`.\n */\nexport type FinishGeocodeEvent = { input: string; results: GeocodingResult[] };\nexport type FinishGeocodeEventHandlerFn = (event: FinishGeocodeEvent) => void;\n\ndeclare module 'leaflet' {\n interface Evented {\n on(type: 'markgeocode', fn: MarkGeocodeEventHandlerFn, context?: any): this;\n on(type: 'startgeocode', fn: StartGeocodeEventHandlerFn, context?: any): this;\n on(type: 'startsuggest', fn: StartGeocodeEventHandlerFn, context?: any): this;\n on(type: 'finishsuggest', fn: FinishGeocodeEventHandlerFn, context?: any): this;\n on(type: 'finishgeocode', fn: FinishGeocodeEventHandlerFn, context?: any): this;\n }\n}\n\n/**\n * Leaflet mixins https://leafletjs.com/reference-1.7.1.html#class-includes\n * for TypeScript https://www.typescriptlang.org/docs/handbook/mixins.html\n * @internal\n */\nclass EventedControl {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n constructor(...args: any[]) {\n // empty\n }\n}\n\n/**\n * @internal\n */\ninterface EventedControl extends L.Control, L.Evented {}\nL.Util.extend(EventedControl.prototype, L.Control.prototype);\nL.Util.extend(EventedControl.prototype, L.Evented.prototype);\n\n/**\n * This is the geocoder control. It works like any other [Leaflet control](https://leafletjs.com/reference.html#control), and is added to the map.\n */\nexport class GeocoderControl extends EventedControl {\n options: GeocoderControlOptions = {\n showUniqueResult: true,\n showResultIcons: false,\n collapsed: true,\n expand: 'touch',\n position: 'topright',\n placeholder: 'Search...',\n errorMessage: 'Nothing found.',\n iconLabel: 'Initiate a new search',\n query: '',\n queryMinLength: 1,\n suggestMinLength: 3,\n suggestTimeout: 250,\n defaultMarkGeocode: true\n };\n\n private _alts: HTMLUListElement;\n private _container: HTMLDivElement;\n private _errorElement: HTMLDivElement;\n private _geocodeMarker: L.Marker;\n private _input: HTMLInputElement;\n private _lastGeocode: string;\n private _map: L.Map;\n private _preventBlurCollapse: boolean;\n private _requestCount = 0;\n private _results: any;\n private _selection: any;\n private _suggestTimeout: any;\n\n /**\n * Instantiates a geocoder control (to be invoked using `new`)\n * @param options the options\n */\n constructor(options?: Partial) {\n super(options);\n L.Util.setOptions(this, options);\n if (!this.options.geocoder) {\n this.options.geocoder = new Nominatim();\n }\n }\n\n addThrobberClass() {\n L.DomUtil.addClass(this._container, 'leaflet-control-geocoder-throbber');\n }\n\n removeThrobberClass() {\n L.DomUtil.removeClass(this._container, 'leaflet-control-geocoder-throbber');\n }\n\n /**\n * Returns the container DOM element for the control and add listeners on relevant map events.\n * @param map the map instance\n * @see https://leafletjs.com/reference.html#control-onadd\n */\n onAdd(map: L.Map) {\n const className = 'leaflet-control-geocoder';\n const container = L.DomUtil.create('div', className + ' leaflet-bar') as HTMLDivElement;\n const icon = L.DomUtil.create('button', className + '-icon', container) as HTMLButtonElement;\n const form = L.DomUtil.create('div', className + '-form', container) as HTMLDivElement;\n\n this._map = map;\n this._container = container;\n\n icon.innerHTML = ' ';\n icon.type = 'button';\n icon.setAttribute('aria-label', this.options.iconLabel);\n\n const input = (this._input = L.DomUtil.create('input', '', form) as HTMLInputElement);\n input.type = 'search';\n input.value = this.options.query;\n input.placeholder = this.options.placeholder;\n L.DomEvent.disableClickPropagation(input);\n\n this._errorElement = L.DomUtil.create(\n 'div',\n className + '-form-no-error',\n container\n ) as HTMLDivElement;\n this._errorElement.innerHTML = this.options.errorMessage;\n\n this._alts = L.DomUtil.create(\n 'ul',\n className + '-alternatives leaflet-control-geocoder-alternatives-minimized',\n container\n ) as HTMLUListElement;\n L.DomEvent.disableClickPropagation(this._alts);\n\n L.DomEvent.addListener(input, 'keydown', this._keydown, this);\n if (this.options.geocoder?.suggest) {\n L.DomEvent.addListener(input, 'input', this._change, this);\n }\n L.DomEvent.addListener(input, 'blur', () => {\n if (this.options.collapsed && !this._preventBlurCollapse) {\n this._collapse();\n }\n this._preventBlurCollapse = false;\n });\n\n if (this.options.collapsed) {\n if (this.options.expand === 'click') {\n L.DomEvent.addListener(container, 'click', (e: Event) => {\n if ((e as MouseEvent).button === 0 && (e as MouseEvent).detail !== 2) {\n this._toggle();\n }\n });\n } else if (this.options.expand === 'touch') {\n L.DomEvent.addListener(\n container,\n L.Browser.touch ? 'touchstart mousedown' : 'mousedown',\n (e: Event) => {\n this._toggle();\n e.preventDefault(); // mobile: clicking focuses the icon, so UI expands and immediately collapses\n e.stopPropagation();\n },\n this\n );\n } else {\n L.DomEvent.addListener(container, 'mouseover', this._expand, this);\n L.DomEvent.addListener(container, 'mouseout', this._collapse, this);\n this._map.on('movestart', this._collapse, this);\n }\n } else {\n this._expand();\n if (L.Browser.touch) {\n L.DomEvent.addListener(container, 'touchstart', () => this._geocode());\n } else {\n L.DomEvent.addListener(container, 'click', () => this._geocode());\n }\n }\n\n if (this.options.defaultMarkGeocode) {\n this.on('markgeocode', this.markGeocode, this);\n }\n\n this.on('startgeocode', this.addThrobberClass, this);\n this.on('finishgeocode', this.removeThrobberClass, this);\n this.on('startsuggest', this.addThrobberClass, this);\n this.on('finishsuggest', this.removeThrobberClass, this);\n\n L.DomEvent.disableClickPropagation(container);\n\n return container;\n }\n\n /**\n * Sets the query string on the text input\n * @param string the query string\n */\n setQuery(string: string): this {\n this._input.value = string;\n return this;\n }\n\n private _geocodeResult(results: GeocodingResult[], suggest: boolean) {\n if (!suggest && this.options.showUniqueResult && results.length === 1) {\n this._geocodeResultSelected(results[0]);\n } else if (results.length > 0) {\n this._alts.innerHTML = '';\n this._results = results;\n L.DomUtil.removeClass(this._alts, 'leaflet-control-geocoder-alternatives-minimized');\n L.DomUtil.addClass(this._container, 'leaflet-control-geocoder-options-open');\n for (let i = 0; i < results.length; i++) {\n this._alts.appendChild(this._createAlt(results[i], i));\n }\n } else {\n L.DomUtil.addClass(this._container, 'leaflet-control-geocoder-options-error');\n L.DomUtil.addClass(this._errorElement, 'leaflet-control-geocoder-error');\n }\n }\n\n /**\n * Marks a geocoding result on the map\n * @param result the geocoding result\n */\n markGeocode(event: MarkGeocodeEvent) {\n const result = event.geocode;\n\n this._map.fitBounds(result.bbox);\n\n if (this._geocodeMarker) {\n this._map.removeLayer(this._geocodeMarker);\n }\n\n this._geocodeMarker = new L.Marker(result.center)\n .bindPopup(result.html || result.name)\n .addTo(this._map)\n .openPopup();\n\n return this;\n }\n\n private async _geocode(suggest: boolean = false) {\n const value = this._input.value;\n if (!suggest && value.length < this.options.queryMinLength) {\n return;\n }\n\n const requestCount = ++this._requestCount;\n this._lastGeocode = value;\n if (!suggest) {\n this._clearResults();\n }\n\n const event: StartGeocodeEvent = { input: value };\n this.fire(suggest ? 'startsuggest' : 'startgeocode', event);\n\n const results = suggest\n ? await this.options.geocoder!.suggest!(value)\n : await this.options.geocoder!.geocode(value);\n\n if (requestCount === this._requestCount) {\n const event: FinishGeocodeEvent = { input: value, results };\n this.fire(suggest ? 'finishsuggest' : 'finishgeocode', event);\n this._geocodeResult(results, suggest);\n }\n }\n\n private _geocodeResultSelected(geocode: GeocodingResult) {\n const event: MarkGeocodeEvent = { geocode };\n this.fire('markgeocode', event);\n }\n\n private _toggle() {\n if (L.DomUtil.hasClass(this._container, 'leaflet-control-geocoder-expanded')) {\n this._collapse();\n } else {\n this._expand();\n }\n }\n\n private _expand() {\n L.DomUtil.addClass(this._container, 'leaflet-control-geocoder-expanded');\n this._input.select();\n this.fire('expand');\n }\n\n private _collapse() {\n L.DomUtil.removeClass(this._container, 'leaflet-control-geocoder-expanded');\n L.DomUtil.addClass(this._alts, 'leaflet-control-geocoder-alternatives-minimized');\n L.DomUtil.removeClass(this._errorElement, 'leaflet-control-geocoder-error');\n L.DomUtil.removeClass(this._container, 'leaflet-control-geocoder-options-open');\n L.DomUtil.removeClass(this._container, 'leaflet-control-geocoder-options-error');\n this._input.blur(); // mobile: keyboard shouldn't stay expanded\n this.fire('collapse');\n }\n\n private _clearResults() {\n L.DomUtil.addClass(this._alts, 'leaflet-control-geocoder-alternatives-minimized');\n this._selection = null;\n L.DomUtil.removeClass(this._errorElement, 'leaflet-control-geocoder-error');\n L.DomUtil.removeClass(this._container, 'leaflet-control-geocoder-options-open');\n L.DomUtil.removeClass(this._container, 'leaflet-control-geocoder-options-error');\n }\n\n private _createAlt(result: GeocodingResult, index: number) {\n const li = L.DomUtil.create('li', ''),\n a = L.DomUtil.create('a', '', li),\n icon =\n this.options.showResultIcons && result.icon\n ? (L.DomUtil.create('img', '', a) as HTMLImageElement)\n : null,\n text = result.html ? undefined : document.createTextNode(result.name),\n mouseDownHandler = (e: Event) => {\n // In some browsers, a click will fire on the map if the control is\n // collapsed directly after mousedown. To work around this, we\n // wait until the click is completed, and _then_ collapse the\n // control. Messy, but this is the workaround I could come up with\n // for #142.\n this._preventBlurCollapse = true;\n L.DomEvent.stop(e);\n this._geocodeResultSelected(result);\n L.DomEvent.on(li, 'click touchend', () => {\n if (this.options.collapsed) {\n this._collapse();\n } else {\n this._clearResults();\n }\n });\n };\n\n if (icon) {\n icon.src = result.icon!;\n }\n\n li.setAttribute('data-result-index', String(index));\n\n if (result.html) {\n a.innerHTML = a.innerHTML + result.html;\n } else if (text) {\n a.appendChild(text);\n }\n\n // Use mousedown and not click, since click will fire _after_ blur,\n // causing the control to have collapsed and removed the items\n // before the click can fire.\n L.DomEvent.addListener(li, 'mousedown touchstart', mouseDownHandler, this);\n\n return li;\n }\n\n private _keydown(e: KeyboardEvent) {\n const select = (dir: number) => {\n if (this._selection) {\n L.DomUtil.removeClass(this._selection, 'leaflet-control-geocoder-selected');\n this._selection = this._selection[dir > 0 ? 'nextSibling' : 'previousSibling'];\n }\n if (!this._selection) {\n this._selection = this._alts[dir > 0 ? 'firstChild' : 'lastChild'];\n }\n\n if (this._selection) {\n L.DomUtil.addClass(this._selection, 'leaflet-control-geocoder-selected');\n }\n };\n\n switch (e.keyCode) {\n // Escape\n case 27:\n if (this.options.collapsed) {\n this._collapse();\n } else {\n this._clearResults();\n }\n break;\n // Up\n case 38:\n select(-1);\n break;\n // Up\n case 40:\n select(1);\n break;\n // Enter\n case 13:\n if (this._selection) {\n const index = parseInt(this._selection.getAttribute('data-result-index'), 10);\n this._geocodeResultSelected(this._results[index]);\n this._clearResults();\n } else {\n this._geocode();\n }\n break;\n default:\n return;\n }\n\n L.DomEvent.preventDefault(e);\n }\n\n private _change() {\n const v = this._input.value;\n if (v !== this._lastGeocode) {\n clearTimeout(this._suggestTimeout);\n if (v.length >= this.options.suggestMinLength) {\n this._suggestTimeout = setTimeout(() => this._geocode(true), this.options.suggestTimeout);\n } else {\n this._clearResults();\n }\n }\n }\n}\n\n/**\n * [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link GeocoderControl}\n * @param options the options\n */\nexport function geocoder(options?: Partial) {\n return new GeocoderControl(options);\n}\n","/* @preserve\n * Leaflet Control Geocoder\n * https://github.com/perliedman/leaflet-control-geocoder\n *\n * Copyright (c) 2012 sa3m (https://github.com/sa3m)\n * Copyright (c) 2018 Per Liedman\n * All rights reserved.\n */\nimport * as L from 'leaflet';\nimport { GeocoderControl as Geocoder, geocoder } from './control';\nimport * as geocoders from './geocoders/index';\nimport './style.css';\n\nL.Util.extend(Geocoder, geocoders);\nexport default Geocoder;\nexport { Geocoder, geocoder, geocoders };\n\nL.Util.extend(L.Control, {\n Geocoder: Geocoder,\n geocoder: geocoder\n});\n"],"names":["geocodingParams","options","params","L","reverseParams","badChars","possible","escape","escapeChar","chr","htmlEscape","string","getJSON","url","headers","request","key","value","v","response","template","str","data","ArcGis","__publicField","query","results","loc","latLng","latLngBounds","location","scale","result","center","bbox","arcgis","Bing","resource","i","bing","AzureMaps","address","azure","Google","google","HERE","prox","HEREv2","item","here","parseLatLng","match","LatLng","Mapbox","properties","j","id","param","_a","mapbox","MapQuest","parts","s","mapQuest","Neutrino","neutrino","Nominatim","r","className","nominatim","OpenLocationCode","decoded","e","openLocationCode","OpenCage","opencage","Pelias","bboxname","feature","latlng","layer","pelias","GeocodeEarth","geocodeEarth","Mapzen","mapzen","Openrouteservice","openrouteservice","Photon","f","c","extent","p","photon","What3Words","what3words","EventedControl","args","GeocoderControl","map","container","icon","form","input","suggest","event","requestCount","geocode","index","li","a","text","mouseDownHandler","select","dir","geocoder","L__namespace","Geocoder","geocoders"],"mappings":"wtBA4EgB,SAAAA,EACdC,EACAC,EACyB,CACzB,OAAOC,EAAE,KAAK,OAAOD,EAAQD,EAAQ,oBAAoB,CAC3D,CAKgB,SAAAG,EACdH,EACAC,EACyB,CACzB,OAAOC,EAAE,KAAK,OAAOD,EAAQD,EAAQ,kBAAkB,CACzD,CCtFA,MAAMI,EAAW,YAIXC,EAAW,WAIXC,EAAiC,CACrC,IAAK,QACL,IAAK,OACL,IAAK,OACL,IAAK,SACL,IAAK,SACL,IAAK,QACP,EAKA,SAASC,EAAWC,EAAa,CAC/B,OAAOF,EAAOE,CAAG,CACnB,CAKO,SAASC,EAAWC,EAAyB,CAClD,OAAIA,GAAU,KACL,GACGA,GAOZA,EAAS,GAAKA,EAETL,EAAS,KAAKK,CAAM,EAGlBA,EAAO,QAAQN,EAAUG,CAAU,EAFjCG,GATAA,EAAS,EAYpB,CAKgB,SAAAC,EAAWC,EAAaX,EAA6C,CAC7E,MAAAY,EAAU,CAAE,OAAQ,kBAAmB,EACvCC,EAAU,IAAI,IAAIF,CAAG,EACpB,cAAA,QAAQX,CAAM,EAAE,QAAQ,CAAC,CAACc,EAAKC,CAAK,IAAM,EAC9C,MAAM,QAAQA,CAAK,EAAIA,EAAQ,CAACA,CAAK,GAAG,QAAaC,GAAA,CAC5CH,EAAA,aAAa,OAAOC,EAAKE,CAAC,CAAA,CACnC,CAAA,CACF,EACM,MAAMH,EAAQ,SAAS,EAAG,CAAE,QAAAD,CAAA,CAAS,EAAE,KAAKK,GAAYA,EAAS,KAAA,CAAM,CAChF,CAKgB,SAAAC,EAASC,EAAaC,EAAmC,CACvE,OAAOD,EAAI,QAAQ,oBAAqB,CAACA,EAAKL,IAAQ,CAChD,IAAAC,EAAQK,EAAKN,CAAG,EACpB,OAAIC,IAAU,OACJA,EAAA,GACC,OAAOA,GAAU,aAC1BA,EAAQA,EAAMK,CAAI,GAEbZ,EAAWO,CAAK,CAAA,CACxB,CACH,CClEO,MAAMM,CAA4B,CAMvC,YAAYtB,EAAkC,CAL9CuB,EAAA,eAAyB,CACvB,WAAY,sEACZ,OAAQ,EACV,GAGIrB,EAAA,KAAK,WAAW,KAAMF,CAAO,CAAA,CAGjC,MAAM,QAAQwB,EAA2C,CACjD,MAAAvB,EAASF,EAAgB,KAAK,QAAS,CAC3C,MAAO,KAAK,QAAQ,OACpB,WAAYyB,EACZ,UAAW,YACX,WAAY,GACZ,aAAc,GACd,EAAG,MAAA,CACJ,EAEKH,EAAO,MAAMV,EACjB,KAAK,QAAQ,WAAa,yBAC1BV,CACF,EACMwB,EAA6B,CAAC,EACpC,GAAIJ,EAAK,YAAcA,EAAK,WAAW,OACrC,QAAS,EAAI,EAAG,GAAKA,EAAK,WAAW,OAAS,EAAG,IAAK,CAC9C,MAAAK,EAAML,EAAK,WAAW,CAAC,EACvBM,EAASzB,EAAE,OAAOwB,EAAI,SAAS,EAAGA,EAAI,SAAS,CAAC,EAChDE,EAAe1B,EAAE,aACrBA,EAAE,OAAOwB,EAAI,OAAO,KAAMA,EAAI,OAAO,IAAI,EACzCxB,EAAE,OAAOwB,EAAI,OAAO,KAAMA,EAAI,OAAO,IAAI,CAC3C,EACAD,EAAQ,CAAC,EAAI,CACX,KAAMC,EAAI,QACV,KAAME,EACN,OAAQD,CACV,CAAA,CAIG,OAAAF,CAAA,CAGT,QAAQD,EAA2C,CAC1C,OAAA,KAAK,QAAQA,CAAK,CAAA,CAG3B,MAAM,QAAQK,EAA2BC,EAA2C,CAC5E,MAAA7B,EAASE,EAAc,KAAK,QAAS,CACzC,SAAU0B,EAAS,IAAM,IAAMA,EAAS,IACxC,SAAU,IACV,EAAG,MAAA,CACJ,EACKR,EAAO,MAAMV,EAAa,KAAK,QAAQ,WAAa,kBAAmBV,CAAM,EAC7E8B,EAA4B,CAAC,EAC/B,GAAAV,GAAQ,CAACA,EAAK,MAAO,CACjB,MAAAW,EAAS9B,EAAE,OAAOmB,EAAK,SAAS,EAAGA,EAAK,SAAS,CAAC,EAClDY,EAAO/B,EAAE,aAAa8B,EAAQA,CAAM,EAC1CD,EAAO,KAAK,CACV,KAAMV,EAAK,QAAQ,WACnB,OAAAW,EACA,KAAAC,CAAA,CACD,CAAA,CAGI,OAAAF,CAAA,CAEX,CAMO,SAASG,EAAOlC,EAAkC,CAChD,OAAA,IAAIsB,EAAOtB,CAAO,CAC3B,CC1EO,MAAMmC,CAA0B,CAKrC,YAAYnC,EAAgC,CAJ5CuB,EAAA,eAAuB,CACrB,WAAY,iDACd,GAGIrB,EAAA,KAAK,WAAW,KAAMF,CAAO,CAAA,CAGjC,MAAM,QAAQwB,EAA2C,CACjD,MAAAvB,EAASF,EAAgB,KAAK,QAAS,CAC3C,MAAAyB,EACA,IAAK,KAAK,QAAQ,MAAA,CACnB,EACKH,EAAO,MAAMV,EAAa,KAAK,QAAQ,WAAYV,CAAM,EACzDwB,EAA6B,CAAC,EAChC,GAAAJ,EAAK,aAAa,OAAS,EACpB,QAAA,EAAIA,EAAK,aAAa,CAAC,EAAE,UAAU,OAAS,EAAG,GAAK,EAAG,IAAK,CAC7D,MAAAe,EAAWf,EAAK,aAAa,CAAC,EAAE,UAAU,CAAC,EAC/CY,EAAOG,EAAS,KAClBX,EAAQ,CAAC,EAAI,CACX,KAAMW,EAAS,KACf,KAAMlC,EAAE,aAAa,CAAC+B,EAAK,CAAC,EAAGA,EAAK,CAAC,CAAC,EAAG,CAACA,EAAK,CAAC,EAAGA,EAAK,CAAC,CAAC,CAAC,EAC3D,OAAQ/B,EAAE,OAAOkC,EAAS,MAAM,WAAW,CAC7C,CAAA,CAGG,OAAAX,CAAA,CAGT,MAAM,QAAQI,EAA2BC,EAA2C,CAC5E,MAAA7B,EAASE,EAAc,KAAK,QAAS,CACzC,IAAK,KAAK,QAAQ,MAAA,CACnB,EACKkB,EAAO,MAAMV,EACjB,KAAK,QAAQ,WAAakB,EAAS,IAAM,IAAMA,EAAS,IACxD5B,CACF,EACMwB,EAA6B,CAAC,EAC3B,QAAAY,EAAIhB,EAAK,aAAa,CAAC,EAAE,UAAU,OAAS,EAAGgB,GAAK,EAAGA,IAAK,CAC7D,MAAAD,EAAWf,EAAK,aAAa,CAAC,EAAE,UAAUgB,CAAC,EAC/CJ,EAAOG,EAAS,KAClBX,EAAQY,CAAC,EAAI,CACX,KAAMD,EAAS,KACf,KAAMlC,EAAE,aAAa,CAAC+B,EAAK,CAAC,EAAGA,EAAK,CAAC,CAAC,EAAG,CAACA,EAAK,CAAC,EAAGA,EAAK,CAAC,CAAC,CAAC,EAC3D,OAAQ/B,EAAE,OAAOkC,EAAS,MAAM,WAAW,CAC7C,CAAA,CAEK,OAAAX,CAAA,CAEX,CAMO,SAASa,EAAKtC,EAAgC,CAC5C,OAAA,IAAImC,EAAKnC,CAAO,CACzB,CCzDO,MAAMuC,CAA+B,CAM1C,YAAYvC,EAAoC,CALxCuB,EAAA,eAA4B,CAClC,OAAQ,GACR,WAAY,oCACd,GAIM,GADFrB,EAAA,KAAK,WAAW,KAAMF,CAAO,EAC3B,CAAC,KAAK,QAAQ,OACV,MAAA,IAAI,MAAM,0CAA0C,CAC5D,CAOF,MAAM,QAAQwB,EAA2C,CACvD,MAAMvB,EAAS,CACb,cAAe,MACf,MAAAuB,EACA,mBAAoB,KAAK,QAAQ,MACnC,EACMZ,EAAM,KAAK,QAAQ,WAAa,gBAChCS,EAAO,MAAMV,EAA2BC,EAAKX,CAAM,EAEnDwB,EAA6B,CAAC,EACpC,GAAIJ,EAAK,SAAWA,EAAK,QAAQ,OAAS,EAC7B,UAAAU,KAAUV,EAAK,QACxBI,EAAQ,KAAK,CACX,KAAMM,EAAO,QAAQ,gBACrB,KAAM7B,EAAE,aACN,CAAC6B,EAAO,SAAS,aAAa,IAAKA,EAAO,SAAS,aAAa,GAAG,EACnE,CAACA,EAAO,SAAS,cAAc,IAAKA,EAAO,SAAS,cAAc,GAAG,CACvE,EACA,OAAQ7B,EAAE,OAAO6B,EAAO,SAAS,IAAKA,EAAO,SAAS,GAAG,CAAA,CAC1D,EAGE,OAAAN,CAAA,CAOT,MAAM,QAAQI,EAA2BC,EAA2C,CAClF,MAAM7B,EAAS,CACb,cAAe,MACf,MAAO4B,EAAS,IAAM,IAAMA,EAAS,IACrC,mBAAoB,KAAK,QAAQ,MACnC,EACMjB,EAAM,KAAK,QAAQ,WAAa,wBAChCS,EAAO,MAAMV,EAAaC,EAAKX,CAAM,EAErCwB,EAA6B,CAAC,EACpC,GAAIJ,EAAK,WAAaA,EAAK,UAAU,OAAS,EACjC,UAAAmB,KAAWnB,EAAK,UACzBI,EAAQ,KAAK,CACX,KAAMe,EAAQ,QAAQ,gBACtB,KAAMtC,EAAE,aACN,CAACsC,EAAQ,SAAS,aAAa,IAAKA,EAAQ,SAAS,aAAa,GAAG,EACrE,CAACA,EAAQ,SAAS,cAAc,IAAKA,EAAQ,SAAS,cAAc,GAAG,CACzE,EACA,OAAQtC,EAAE,OAAO2B,EAAS,IAAKA,EAAS,GAAG,CAAA,CAC5C,EAGE,OAAAJ,CAAA,CAEX,CAMO,SAASgB,EAAMzC,EAA2B,CACxC,OAAA,IAAIuC,EAAUvC,CAAO,CAC9B,CCnFO,MAAM0C,CAA4B,CAKvC,YAAY1C,EAAkC,CAJ9CuB,EAAA,eAAyB,CACvB,WAAY,mDACd,GAGIrB,EAAA,KAAK,WAAW,KAAMF,CAAO,CAAA,CAGjC,MAAM,QAAQwB,EAA2C,CACjD,MAAAvB,EAASF,EAAgB,KAAK,QAAS,CAC3C,IAAK,KAAK,QAAQ,OAClB,QAASyB,CAAA,CACV,EACKH,EAAO,MAAMV,EAAwB,KAAK,QAAQ,WAAYV,CAAM,EACpEwB,EAA6B,CAAC,EACpC,GAAIJ,EAAK,SAAWA,EAAK,QAAQ,OAC/B,QAAS,EAAI,EAAG,GAAKA,EAAK,QAAQ,OAAS,EAAG,IAAK,CAC3C,MAAAK,EAAML,EAAK,QAAQ,CAAC,EACpBM,EAASzB,EAAE,OAAOwB,EAAI,SAAS,QAAQ,EACvCE,EAAe1B,EAAE,aACrBA,EAAE,OAAOwB,EAAI,SAAS,SAAS,SAAS,EACxCxB,EAAE,OAAOwB,EAAI,SAAS,SAAS,SAAS,CAC1C,EACAD,EAAQ,CAAC,EAAI,CACX,KAAMC,EAAI,kBACV,KAAME,EACN,OAAQD,EACR,WAAYD,EAAI,kBAClB,CAAA,CAIG,OAAAD,CAAA,CAGT,MAAM,QAAQI,EAA2BC,EAA2C,CAC5E,MAAA7B,EAASE,EAAc,KAAK,QAAS,CACzC,IAAK,KAAK,QAAQ,OAClB,OAAQ0B,EAAS,IAAM,IAAMA,EAAS,GAAA,CACvC,EACKR,EAAO,MAAMV,EAAa,KAAK,QAAQ,WAAYV,CAAM,EACzDwB,EAA6B,CAAC,EACpC,GAAIJ,EAAK,SAAWA,EAAK,QAAQ,OAC/B,QAASgB,EAAI,EAAGA,GAAKhB,EAAK,QAAQ,OAAS,EAAGgB,IAAK,CAC3C,MAAAX,EAAML,EAAK,QAAQgB,CAAC,EACpBL,EAAS9B,EAAE,OAAOwB,EAAI,SAAS,QAAQ,EACvCO,EAAO/B,EAAE,aACbA,EAAE,OAAOwB,EAAI,SAAS,SAAS,SAAS,EACxCxB,EAAE,OAAOwB,EAAI,SAAS,SAAS,SAAS,CAC1C,EACAD,EAAQY,CAAC,EAAI,CACX,KAAMX,EAAI,kBACV,KAAAO,EACA,OAAAD,EACA,WAAYN,EAAI,kBAClB,CAAA,CAIG,OAAAD,CAAA,CAEX,CAMO,SAASkB,EAAO3C,EAAkC,CAChD,OAAA,IAAI0C,EAAO1C,CAAO,CAC3B,CCxDO,MAAM4C,CAA0B,CASrC,YAAY5C,EAAgC,CAR5CuB,EAAA,eAAuB,CACrB,WAAY,qCACZ,OAAQ,GACR,SAAU,GACV,OAAQ,GACR,WAAY,CACd,GAIE,GADErB,EAAA,KAAK,WAAW,KAAMF,CAAO,EAC3BA,GAAA,MAAAA,EAAS,OAAc,MAAA,MAAM,uDAAuD,CAAA,CAG1F,QAAQwB,EAA2C,CAC3C,MAAAvB,EAASF,EAAgB,KAAK,QAAS,CAC3C,WAAYyB,EACZ,IAAK,EACL,OAAQ,KAAK,QAAQ,OACrB,SAAU,KAAK,QAAQ,SACvB,eAAgB,EAChB,WAAY,KAAK,QAAQ,UAAA,CAC1B,EACD,OAAO,KAAK,QAAQ,KAAK,QAAQ,WAAa,eAAgBvB,CAAM,CAAA,CAGtE,QAAQ4B,EAA2BC,EAA2C,CAC5E,IAAIe,EAAOhB,EAAS,IAAM,IAAMA,EAAS,IACrC,KAAK,QAAQ,2BACPgB,GAAA,IAAM,KAAK,QAAQ,0BAEvB,MAAA5C,EAASE,EAAc,KAAK,QAAS,CACzC,KAAA0C,EACA,KAAM,oBACN,OAAQ,KAAK,QAAQ,OACrB,SAAU,KAAK,QAAQ,SACvB,IAAK,EACL,eAAgB,EAChB,WAAY,KAAK,QAAQ,UAAA,CAC1B,EACD,OAAO,KAAK,QAAQ,KAAK,QAAQ,WAAa,sBAAuB5C,CAAM,CAAA,CAG7E,MAAM,QAAQW,EAAaX,EAAyC,CAClE,MAAMoB,EAAO,MAAMV,EAAaC,EAAKX,CAAM,EACrCwB,EAA6B,CAAC,EAEpC,GAAIJ,EAAK,SAAS,MAAQA,EAAK,SAAS,KAAK,OAClC,QAAA,EAAI,EAAG,GAAKA,EAAK,SAAS,KAAK,CAAC,EAAE,OAAO,OAAS,EAAG,IAAK,CAC3D,MAAAK,EAAML,EAAK,SAAS,KAAK,CAAC,EAAE,OAAO,CAAC,EAAE,SACtCW,EAAS9B,EAAE,OAAOwB,EAAI,gBAAgB,SAAUA,EAAI,gBAAgB,SAAS,EAC7EO,EAAO/B,EAAE,aACbA,EAAE,OAAOwB,EAAI,QAAQ,QAAQ,SAAUA,EAAI,QAAQ,QAAQ,SAAS,EACpExB,EAAE,OAAOwB,EAAI,QAAQ,YAAY,SAAUA,EAAI,QAAQ,YAAY,SAAS,CAC9E,EACAD,EAAQ,CAAC,EAAI,CACX,KAAMC,EAAI,QAAQ,MAClB,WAAYA,EAAI,QAChB,KAAAO,EACA,OAAAD,CACF,CAAA,CAGG,OAAAP,CAAA,CAEX,CAKO,MAAMqB,CAA4B,CASvC,YAAY9C,EAAgC,CAR5CuB,EAAA,eAAuB,CACrB,WAAY,wCACZ,OAAQ,GACR,OAAQ,GACR,SAAU,GACV,WAAY,EACd,GAGIrB,EAAA,KAAK,WAAW,KAAMF,CAAO,CAAA,CAGjC,QAAQwB,EAA2C,CAC3C,MAAAvB,EAASF,EAAgB,KAAK,QAAS,CAC3C,EAAGyB,EACH,OAAQ,KAAK,QAAQ,OACrB,MAAO,KAAK,QAAQ,UAAA,CACrB,EAED,GAAI,CAACvB,EAAO,IAAM,CAACA,EAAO,GAClB,MAAA,MACJ,6HACF,EAGF,OAAO,KAAK,QAAQ,KAAK,QAAQ,WAAa,YAAaA,CAAM,CAAA,CAGnE,QAAQ4B,EAA2BC,EAA2C,CACtE,MAAA7B,EAASE,EAAc,KAAK,QAAS,CACzC,GAAI0B,EAAS,IAAM,IAAMA,EAAS,IAClC,MAAO,KAAK,QAAQ,yBACpB,OAAQ,KAAK,QAAQ,MAAA,CACtB,EACD,OAAO,KAAK,QAAQ,KAAK,QAAQ,WAAa,cAAe5B,CAAM,CAAA,CAGrE,MAAM,QAAQW,EAAaX,EAAyC,CAClE,MAAMoB,EAAO,MAAMV,EAAwBC,EAAKX,CAAM,EAChDwB,EAA6B,CAAC,EAEpC,GAAIJ,EAAK,OAASA,EAAK,MAAM,OAC3B,QAAS,EAAI,EAAG,GAAKA,EAAK,MAAM,OAAS,EAAG,IAAK,CACzC,MAAA0B,EAAO1B,EAAK,MAAM,CAAC,EACnBM,EAASzB,EAAE,OAAO6C,EAAK,SAAS,IAAKA,EAAK,SAAS,GAAG,EACxD,IAAAd,EACAc,EAAK,QACPd,EAAO/B,EAAE,aACPA,EAAE,OAAO6C,EAAK,QAAQ,MAAOA,EAAK,QAAQ,IAAI,EAC9C7C,EAAE,OAAO6C,EAAK,QAAQ,MAAOA,EAAK,QAAQ,IAAI,CAChD,EAGAd,EAAO/B,EAAE,aACPA,EAAE,OAAO6C,EAAK,SAAS,IAAKA,EAAK,SAAS,GAAG,EAC7C7C,EAAE,OAAO6C,EAAK,SAAS,IAAKA,EAAK,SAAS,GAAG,CAC/C,EAEFtB,EAAQ,CAAC,EAAI,CACX,KAAMsB,EAAK,QAAQ,MACnB,WAAYA,EAAK,QACjB,KAAAd,EACA,OAAQN,CACV,CAAA,CAGG,OAAAF,CAAA,CAEX,CAMO,SAASuB,EAAKhD,EAAgC,CACnD,OAAIA,GAAA,MAAAA,EAAS,OACJ,IAAI8C,EAAO9C,CAAO,EAElB,IAAI4C,EAAK5C,CAAO,CAE3B,CC3JO,SAASiD,EAAYzB,EAAqC,CAC3D,IAAA0B,EAEJ,GAAKA,EAAQ1B,EAAM,MAAM,+DAA+D,EAEtF,OAAOtB,EAAE,QACN,KAAK,KAAKgD,EAAM,CAAC,CAAC,EAAI,EAAI,IAAM,CAACA,EAAM,CAAC,GACxC,KAAK,KAAKA,EAAM,CAAC,CAAC,EAAI,EAAI,IAAM,CAACA,EAAM,CAAC,CAC3C,EAEC,GAAAA,EAAQ1B,EAAM,MAAM,+DAA+D,EAGpF,OAAOtB,EAAE,QACN,KAAK,KAAKgD,EAAM,CAAC,CAAC,EAAI,EAAI,IAAM,CAACA,EAAM,CAAC,GACxC,KAAK,KAAKA,EAAM,CAAC,CAAC,EAAI,EAAI,IAAM,CAACA,EAAM,CAAC,CAC3C,EACF,GACGA,EAAQ1B,EAAM,MACb,uGAAA,EAIF,OAAOtB,EAAE,QACN,KAAK,KAAKgD,EAAM,CAAC,CAAC,EAAI,EAAI,KAAO,CAACA,EAAM,CAAC,GAAI,CAACA,EAAM,CAAC,EAAI,KACzD,KAAK,KAAKA,EAAM,CAAC,CAAC,EAAI,EAAI,KAAO,CAACA,EAAM,CAAC,GAAI,CAACA,EAAM,CAAC,EAAI,GAC5D,EACF,GACGA,EAAQ1B,EAAM,MACb,uGAAA,EAIF,OAAOtB,EAAE,QACN,KAAK,KAAKgD,EAAM,CAAC,CAAC,EAAI,EAAI,KAAO,CAACA,EAAM,CAAC,GAAI,CAACA,EAAM,CAAC,EAAI,KACzD,KAAK,KAAKA,EAAM,CAAC,CAAC,EAAI,EAAI,KAAO,CAACA,EAAM,CAAC,GAAI,CAACA,EAAM,CAAC,EAAI,GAC5D,EACF,GACGA,EAAQ1B,EAAM,MACb,yIAAA,EAIF,OAAOtB,EAAE,QACN,KAAK,KAAKgD,EAAM,CAAC,CAAC,EAAI,EAAI,KAAO,CAACA,EAAM,CAAC,GAAI,CAACA,EAAM,CAAC,EAAI,IAAK,CAACA,EAAM,CAAC,EAAI,OAC1E,KAAK,KAAKA,EAAM,CAAC,CAAC,EAAI,EAAI,KAAO,CAACA,EAAM,CAAC,GAAI,CAACA,EAAM,CAAC,EAAI,IAAK,CAACA,EAAM,CAAC,EAAI,KAC7E,EACF,GACGA,EAAQ1B,EAAM,MACb,wIAAA,EAIF,OAAOtB,EAAE,QACN,KAAK,KAAKgD,EAAM,CAAC,CAAC,EAAI,EAAI,KAAO,CAACA,EAAM,CAAC,GAAI,CAACA,EAAM,CAAC,EAAI,IAAK,CAACA,EAAM,CAAC,EAAI,OAC1E,KAAK,KAAKA,EAAM,CAAC,CAAC,EAAI,EAAI,KAAO,CAACA,EAAM,CAAC,GAAI,CAACA,EAAM,CAAC,EAAI,IAAK,CAACA,EAAM,CAAC,EAAI,KAC7E,EACU,GAAAA,EAAQ1B,EAAM,MAAM,6DAA6D,EACpF,OAAAtB,EAAE,OAAO,CAACgD,EAAM,CAAC,EAAG,CAACA,EAAM,CAAC,CAAC,CAExC,CAKO,MAAMC,CAA4B,CAMvC,YAAYnD,EAAkC,CAL9CuB,EAAA,eAAyB,CACvB,KAAM,OACN,aAAc,GAChB,GAGIrB,EAAA,KAAK,WAAW,KAAMF,CAAO,CAAA,CAGjC,MAAM,QAAQwB,EAAe,CACrB,MAAAQ,EAASiB,EAAYzB,CAAK,EAChC,OAAIQ,EACiC,CACjC,CACE,KAAMR,EACN,OAAAQ,EACA,KAAMA,EAAO,SAAS,KAAK,QAAQ,YAAY,CAAA,CAEnD,EAES,KAAK,QAAQ,KACf,KAAK,QAAQ,KAAK,QAAQR,CAAK,EAE/B,CAAC,CACV,CAEJ,CAMO,SAASG,EAAO3B,EAAkC,CAChD,OAAA,IAAImD,EAAOnD,CAAO,CAC3B,CC9GO,MAAMoD,CAA4B,CAKvC,YAAYpD,EAAkC,CAJ9CuB,EAAA,eAAyB,CACvB,WAAY,oDACd,GAGIrB,EAAA,KAAK,WAAW,KAAMF,CAAO,CAAA,CAGjC,eAAe0B,EAAK,CAClB,MAAM2B,EAAa,CACjB,KAAM3B,EAAI,KACV,QAASA,EAAI,OACf,EAES,QAAA4B,EAAI,EAAGA,GAAK5B,EAAI,SAAW,CAAC,GAAG,OAAQ4B,IAAK,CAC7C,MAAAC,EAAK7B,EAAI,QAAQ4B,CAAC,EAAE,GAAG,MAAM,GAAG,EAAE,CAAC,EACzCD,EAAWE,CAAE,EAAI7B,EAAI,QAAQ4B,CAAC,EAAE,KAG5B5B,EAAI,QAAQ4B,CAAC,EAAE,aACjBD,EAAW,iBAAsB3B,EAAI,QAAQ4B,CAAC,EAAE,WAClD,CAEK,OAAAD,CAAA,CAGT,MAAM,QAAQ7B,EAA2C,CACvD,MAAMZ,EAAM,KAAK,QAAQ,WAAa,mBAAmBY,CAAK,EAAI,QAC5DvB,EAAcF,EAAgB,KAAK,QAAS,CAChD,aAAc,KAAK,QAAQ,MAAA,CAC5B,EAECE,EAAO,YAAc,QACrBA,EAAO,UAAU,MAAQ,QACzBA,EAAO,UAAU,MAAQ,SAEzBA,EAAO,UAAYA,EAAO,UAAU,IAAM,IAAMA,EAAO,UAAU,KAEnE,MAAMoB,EAAO,MAAMV,EAAwBC,EAAKX,CAAM,EAC/C,OAAA,KAAK,cAAcoB,CAAI,CAAA,CAGhC,QAAQG,EAA2C,CAC1C,OAAA,KAAK,QAAQA,CAAK,CAAA,CAG3B,MAAM,QAAQK,EAA2BC,EAA2C,CAC5E,MAAAlB,EAAM,KAAK,QAAQ,WAAaiB,EAAS,IAAM,IAAMA,EAAS,IAAM,QACpE2B,EAAQrD,EAAc,KAAK,QAAS,CACxC,aAAc,KAAK,QAAQ,MAAA,CAC5B,EACKkB,EAAO,MAAMV,EAAwBC,EAAK4C,CAAK,EAC9C,OAAA,KAAK,cAAcnC,CAAI,CAAA,CAGxB,cAAcA,EAAiD,OACjE,GAAA,GAACoC,EAAApC,EAAK,WAAL,MAAAoC,EAAe,QAClB,MAAO,CAAC,EAEV,MAAMhC,EAA6B,CAAC,EACpC,QAASY,EAAI,EAAGA,GAAKhB,EAAK,SAAS,OAAS,EAAGgB,IAAK,CAC5C,MAAAX,EAAML,EAAK,SAASgB,CAAC,EACrBL,EAAS9B,EAAE,OAAOwB,EAAI,OAAO,SAA6B,EAC5D,IAAAO,EACAP,EAAI,KACNO,EAAO/B,EAAE,aACPA,EAAE,OAAOwB,EAAI,KAAK,MAAM,EAAG,CAAC,EAAE,SAA6B,EAC3DxB,EAAE,OAAOwB,EAAI,KAAK,MAAM,EAAG,CAAC,EAAE,QAA6B,CAAA,CAC7D,EAEOO,EAAA/B,EAAE,aAAa8B,EAAQA,CAAM,EAEtCP,EAAQY,CAAC,EAAI,CACX,KAAMX,EAAI,WACV,KAAAO,EACA,OAAAD,EACA,WAAY,KAAK,eAAeN,CAAG,CACrC,CAAA,CAGK,OAAAD,CAAA,CAEX,CAMO,SAASiC,EAAO1D,EAAkC,CAChD,OAAA,IAAIoD,EAAOpD,CAAO,CAC3B,CC3FO,MAAM2D,CAA8B,CAKzC,YAAY3D,EAAoC,CAJhDuB,EAAA,eAA2B,CACzB,WAAY,0CACd,GAGIrB,EAAA,KAAK,WAAW,KAAMF,CAAO,EAG/B,KAAK,QAAQ,OAAS,mBAAmB,KAAK,QAAQ,MAAO,CAAA,CAG/D,eAAe4D,EAAiB,CACvB,OAAAA,EAAM,OAAYC,GAAA,CAAC,CAACA,CAAC,EAAE,KAAK,IAAI,CAAA,CAGzC,MAAM,QAAQrC,EAA2C,CACjD,MAAAvB,EAASF,EAAgB,KAAK,QAAS,CAC3C,IAAK,KAAK,QAAQ,OAClB,SAAUyB,EACV,MAAO,EACP,UAAW,MAAA,CACZ,EACKH,EAAO,MAAMV,EAAa,KAAK,QAAQ,WAAa,WAAYV,CAAM,EACrE,OAAA,KAAK,cAAcoB,CAAI,CAAA,CAGhC,MAAM,QAAQQ,EAA2BC,EAA2C,CAC5E,MAAA7B,EAASE,EAAc,KAAK,QAAS,CACzC,IAAK,KAAK,QAAQ,OAClB,SAAU0B,EAAS,IAAM,IAAMA,EAAS,IACxC,aAAc,MAAA,CACf,EACKR,EAAO,MAAMV,EAAa,KAAK,QAAQ,WAAa,WAAYV,CAAM,EACrE,OAAA,KAAK,cAAcoB,CAAI,CAAA,CAGxB,cAAcA,EAAyB,CAC7C,MAAMI,EAA6B,CAAC,EACpC,GAAIJ,EAAK,SAAWA,EAAK,QAAQ,CAAC,EAAE,UACzB,QAAAgB,EAAIhB,EAAK,QAAQ,CAAC,EAAE,UAAU,OAAS,EAAGgB,GAAK,EAAGA,IAAK,CAC9D,MAAMX,EAAML,EAAK,QAAQ,CAAC,EAAE,UAAUgB,CAAC,EACjCL,EAAS9B,EAAE,OAAOwB,EAAI,MAAM,EAClCD,EAAQY,CAAC,EAAI,CACX,KAAM,KAAK,YAAYX,EAAI,OAAQA,EAAI,WAAYA,EAAI,WAAYA,EAAI,UAAU,EACjF,KAAMxB,EAAE,aAAa8B,EAAQA,CAAM,EACnC,OAAAA,CACF,CAAA,CAGG,OAAAP,CAAA,CAEX,CAMO,SAASqC,GAAS9D,EAAoC,CACpD,OAAA,IAAI2D,EAAS3D,CAAO,CAC7B,CC1DO,MAAM+D,CAA8B,CAOzC,YAAY/D,EAAoC,CANhDuB,EAAA,eAA2B,CACzB,OAAQ,GACR,OAAQ,GACR,WAAY,0BACd,GAGIrB,EAAA,KAAK,WAAW,KAAMF,CAAO,CAAA,CAIjC,MAAM,QAAQwB,EAA2C,CACjD,MAAAvB,EAASF,EAAgB,KAAK,QAAS,CAC3C,OAAQ,KAAK,QAAQ,OACrB,OAAQ,KAAK,QAAQ,OAErB,QAASyB,EAAM,MAAM,KAAK,EAAE,KAAK,GAAG,CAAA,CACrC,EACKH,EAAO,MAAMV,EAAa,KAAK,QAAQ,WAAa,kBAAmBV,CAAM,EAC7EwB,EAA6B,CAAC,EACpC,GAAIJ,EAAK,UAAW,CACbA,EAAA,SAAWA,EAAK,UAAU,CAAC,EAC1B,MAAAW,EAAS9B,EAAE,OAAOmB,EAAK,SAAS,SAAaA,EAAK,SAAS,SAAY,EACvEY,EAAO/B,EAAE,aAAa8B,EAAQA,CAAM,EAC1CP,EAAQ,CAAC,EAAI,CACX,KAAMJ,EAAK,SAAS,QACpB,KAAAY,EACA,OAAAD,CACF,CAAA,CAGK,OAAAP,CAAA,CAGT,QAAQD,EAA2C,CAC1C,OAAA,KAAK,QAAQA,CAAK,CAAA,CAI3B,MAAM,QAAQK,EAA2BC,EAA2C,CAC5E,MAAA7B,EAASE,EAAc,KAAK,QAAS,CACzC,OAAQ,KAAK,QAAQ,OACrB,OAAQ,KAAK,QAAQ,OACrB,SAAU0B,EAAS,IACnB,UAAWA,EAAS,GAAA,CACrB,EACKR,EAAO,MAAMV,EAAa,KAAK,QAAQ,WAAa,kBAAmBV,CAAM,EAC7EwB,EAA6B,CAAC,EACpC,GAAIJ,EAAK,OAAO,QAAU,KAAOA,EAAK,MAAO,CAC3C,MAAMW,EAAS9B,EAAE,OAAO2B,EAAS,IAAKA,EAAS,GAAG,EAC5CI,EAAO/B,EAAE,aAAa8B,EAAQA,CAAM,EAC1CP,EAAQ,CAAC,EAAI,CACX,KAAMJ,EAAK,QACX,KAAAY,EACA,OAAAD,CACF,CAAA,CAEK,OAAAP,CAAA,CAEX,CAMO,SAASuC,GAAShE,EAAoC,CACpD,OAAA,IAAI+D,EAAS/D,CAAO,CAC7B,CCrBO,MAAMiE,CAA+B,CA2B1C,YAAYjE,EAAqC,CA1BjDuB,EAAA,eAA4B,CAC1B,WAAY,uCACZ,aAAa2C,EAAoB,CAC/B,MAAM1B,EAAU0B,EAAE,QACd,IAAAC,EACJ,MAAMP,EAAkB,CAAC,EACrB,OAAApB,EAAQ,MAAQA,EAAQ,WAC1BoB,EAAM,KAAK,kCAAkC,GAG3CpB,EAAQ,MAASA,EAAgB,MAAQA,EAAQ,SAAWA,EAAQ,UAC1D2B,EAAAP,EAAM,OAAS,EAAI,0CAA4C,GACrEA,EAAA,KACJ,gBAAkBO,EAAY,sDAChC,IAGE3B,EAAQ,OAASA,EAAQ,WACf2B,EAAAP,EAAM,OAAS,EAAI,2CAA6C,GACtEA,EAAA,KAAK,gBAAkBO,EAAY,4BAA4B,GAGhEhD,EAASyC,EAAM,KAAK,OAAO,EAAGpB,CAAO,CAAA,CAEhD,GAGEtC,EAAE,KAAK,WAAW,KAAMF,GAAW,CAAA,CAAE,CAAA,CAGvC,MAAM,QAAQwB,EAAe,CACrB,MAAAvB,EAASF,EAAgB,KAAK,QAAS,CAC3C,EAAGyB,EACH,MAAO,EACP,OAAQ,OACR,eAAgB,CAAA,CACjB,EACKH,EAAO,MAAMV,EAA2B,KAAK,QAAQ,WAAa,SAAUV,CAAM,EAClFwB,EAA6B,CAAC,EACpC,QAAS,EAAIJ,EAAK,OAAS,EAAG,GAAK,EAAG,IAAK,CACnC,MAAAY,EAAOZ,EAAK,CAAC,EAAE,YACrBI,EAAQ,CAAC,EAAI,CACX,KAAMJ,EAAK,CAAC,EAAE,KACd,KAAMA,EAAK,CAAC,EAAE,aACd,KAAM,KAAK,QAAQ,aAAe,KAAK,QAAQ,aAAaA,EAAK,CAAC,CAAC,EAAI,OACvE,KAAMnB,EAAE,aAAa,CAAC,CAAC+B,EAAK,CAAC,EAAG,CAACA,EAAK,CAAC,CAAC,EAAG,CAAC,CAACA,EAAK,CAAC,EAAG,CAACA,EAAK,CAAC,CAAC,CAAC,EAC/D,OAAQ/B,EAAE,OAAO,CAACmB,EAAK,CAAC,EAAE,IAAK,CAACA,EAAK,CAAC,EAAE,GAAG,EAC3C,WAAYA,EAAK,CAAC,CACpB,CAAA,CAEK,OAAAI,CAAA,CAGT,MAAM,QAAQI,EAA2BC,EAAe,CAChD,MAAA7B,EAASE,EAAc,KAAK,QAAS,CACzC,IAAK0B,EAAS,IACd,IAAKA,EAAS,IACd,KAAM,KAAK,MAAM,KAAK,IAAIC,EAAQ,GAAG,EAAI,KAAK,IAAI,CAAC,CAAC,EACpD,eAAgB,EAChB,OAAQ,MAAA,CACT,EACKT,EAAO,MAAMV,EAAyB,KAAK,QAAQ,WAAa,UAAWV,CAAM,EACjFwB,EAA6B,CAAC,EACpC,GAAIJ,GAAQA,EAAK,KAAOA,EAAK,IAAK,CAC1B,MAAAW,EAAS9B,EAAE,OAAO,CAACmB,EAAK,IAAK,CAACA,EAAK,GAAG,EACtCY,EAAO/B,EAAE,aAAa8B,EAAQA,CAAM,EAC1CP,EAAQ,KAAK,CACX,KAAMJ,EAAK,aACX,KAAM,KAAK,QAAQ,aAAe,KAAK,QAAQ,aAAaA,CAAI,EAAI,OACpE,OAAAW,EACA,KAAAC,EACA,WAAYZ,CAAA,CACb,CAAA,CAEI,OAAAI,CAAA,CAEX,CAMO,SAAS2C,GAAUpE,EAAqC,CACtD,OAAA,IAAIiE,EAAUjE,CAAO,CAC9B,CCrHO,MAAMqE,CAAsC,CAEjD,YAAYrE,EAA4C,CADxDuB,EAAA,eAAU,CAAC,GAEPrB,EAAA,KAAK,WAAW,KAAMF,CAAO,CAAA,CAGjC,MAAM,QAAQwB,EAAe,CACvB,GAAA,CACF,MAAM8C,EAAU,KAAK,QAAQ,iBAAiB,OAAO9C,CAAK,EAS1D,MAAO,CARyB,CAC9B,KAAMA,EACN,OAAQtB,EAAE,OAAOoE,EAAQ,eAAgBA,EAAQ,eAAe,EAChE,KAAMpE,EAAE,aACNA,EAAE,OAAOoE,EAAQ,WAAYA,EAAQ,WAAW,EAChDpE,EAAE,OAAOoE,EAAQ,WAAYA,EAAQ,WAAW,CAAA,CAEpD,CACc,QACPC,EAAG,CACV,eAAQ,KAAKA,CAAC,EACP,CAAC,CAAA,CACV,CAEF,MAAM,QAAQ1C,EAA2BC,EAAe,CAClD,GAAA,CAcF,MAAO,CARQ,CACb,KANW,KAAK,QAAQ,iBAAiB,OACzCD,EAAS,IACTA,EAAS,IACT,KAAK,QAAQ,UACf,EAGE,OAAQ3B,EAAE,OAAO2B,EAAS,IAAKA,EAAS,GAAG,EAC3C,KAAM3B,EAAE,aACNA,EAAE,OAAO2B,EAAS,IAAKA,EAAS,GAAG,EACnC3B,EAAE,OAAO2B,EAAS,IAAKA,EAAS,GAAG,CAAA,CAEvC,CACc,QACP,EAAG,CACV,eAAQ,KAAK,CAAC,EACP,CAAC,CAAA,CACV,CAEJ,CAMO,SAAS2C,GAAiBxE,EAA4C,CACpE,OAAA,IAAIqE,EAAiBrE,CAAO,CACrC,CCrEO,MAAMyE,CAA8B,CAKzC,YAAYzE,EAAoC,CAJhDuB,EAAA,eAA2B,CACzB,WAAY,8CACd,GAGIrB,EAAA,KAAK,WAAW,KAAMF,CAAO,CAAA,CAGjC,MAAM,QAAQwB,EAA2C,CACjD,MAAAvB,EAASF,EAAgB,KAAK,QAAS,CAC3C,IAAK,KAAK,QAAQ,OAClB,EAAGyB,CAAA,CACJ,EACKH,EAAO,MAAMV,EAAa,KAAK,QAAQ,WAAYV,CAAM,EACxD,OAAA,KAAK,cAAcoB,CAAI,CAAA,CAGhC,QAAQG,EAA2C,CAC1C,OAAA,KAAK,QAAQA,CAAK,CAAA,CAG3B,MAAM,QAAQK,EAA2BC,EAA2C,CAC5E,MAAA7B,EAASE,EAAc,KAAK,QAAS,CACzC,IAAK,KAAK,QAAQ,OAClB,EAAG,CAAC0B,EAAS,IAAKA,EAAS,GAAG,EAAE,KAAK,GAAG,CAAA,CACzC,EACKR,EAAO,MAAMV,EAAa,KAAK,QAAQ,WAAYV,CAAM,EACxD,OAAA,KAAK,cAAcoB,CAAI,CAAA,CAGxB,cAAcA,EAAyB,CAC7C,MAAMI,EAA6B,CAAC,EACpC,GAAIJ,EAAK,SAAWA,EAAK,QAAQ,OAC/B,QAASgB,EAAI,EAAGA,EAAIhB,EAAK,QAAQ,OAAQgB,IAAK,CACtC,MAAAX,EAAML,EAAK,QAAQgB,CAAC,EACpBL,EAAS9B,EAAE,OAAOwB,EAAI,QAAQ,EAChC,IAAAO,EACAP,EAAI,aAAeA,EAAI,YAAY,OACrCO,EAAO/B,EAAE,aACPA,EAAE,OAAOwB,EAAI,YAAY,OAAO,SAAS,EACzCxB,EAAE,OAAOwB,EAAI,YAAY,OAAO,SAAS,CAC3C,EAEOO,EAAA/B,EAAE,aAAa8B,EAAQA,CAAM,EAEtCP,EAAQ,KAAK,CACX,KAAMC,EAAI,UACV,KAAAO,EACA,OAAAD,CAAA,CACD,CAAA,CAGE,OAAAP,CAAA,CAEX,CAEO,SAASiD,GAAS1E,EAAoC,CACpD,OAAA,IAAIyE,EAASzE,CAAO,CAC7B,CC3DO,MAAM2E,CAA4B,CAKvC,YAAY3E,EAAkC,CAJ9CuB,EAAA,eAAyB,CACvB,WAAY,8BACd,GAGIrB,EAAA,KAAK,WAAW,KAAMF,CAAO,CAAA,CAGjC,MAAM,QAAQwB,EAA2C,CACjD,MAAAvB,EAASF,EAAgB,KAAK,QAAS,CAC3C,QAAS,KAAK,QAAQ,OACtB,KAAMyB,CAAA,CACP,EACKH,EAAO,MAAMV,EAAa,KAAK,QAAQ,WAAa,UAAWV,CAAM,EACpE,OAAA,KAAK,cAAcoB,EAAM,MAAM,CAAA,CAGxC,MAAM,QAAQG,EAA2C,CACjD,MAAAvB,EAASF,EAAgB,KAAK,QAAS,CAC3C,QAAS,KAAK,QAAQ,OACtB,KAAMyB,CAAA,CACP,EACKH,EAAO,MAAMV,EAAa,KAAK,QAAQ,WAAa,gBAAiBV,CAAM,EAC1E,OAAA,KAAK,cAAcoB,EAAM,MAAM,CAAA,CAGxC,MAAM,QAAQQ,EAA2BC,EAA2C,CAC5E,MAAA7B,EAASE,EAAc,KAAK,QAAS,CACzC,QAAS,KAAK,QAAQ,OACtB,YAAa0B,EAAS,IACtB,YAAaA,EAAS,GAAA,CACvB,EACKR,EAAO,MAAMV,EAAa,KAAK,QAAQ,WAAa,WAAYV,CAAM,EACrE,OAAA,KAAK,cAAcoB,EAAM,QAAQ,CAAA,CAG1C,cAAcA,EAAMuD,EAA6B,CAC/C,MAAMnD,EAA6B,CAAC,EACpCvB,OAAAA,EAAE,QAAQmB,EAAM,CACd,aAAawD,EAASC,EAAQ,CACrB,OAAA5E,EAAE,aAAa4E,CAAM,CAC9B,EACA,cAAcD,EAASE,EAAY,CACjC,MAAMhD,EAAS,CAAC,EACZ,IAAAE,EACAD,EAEA+C,EAAM,WACR9C,EAAO8C,EAAM,UAAU,EACvB/C,EAASC,EAAK,UAAU,GACf8C,EAAM,QAAQ,MACvB/C,EAAS+C,EAAM,UAAU,EACzB9C,EAAO/B,EAAE,aACPA,EAAE,QAAQ,eAAe6E,EAAM,QAAQ,KAAK,MAAM,EAAG,CAAC,CAAC,EACvD7E,EAAE,QAAQ,eAAe6E,EAAM,QAAQ,KAAK,MAAM,EAAG,CAAC,CAAC,CACzD,IAEA/C,EAAS+C,EAAM,UAAU,EAClB9C,EAAA/B,EAAE,aAAa8B,EAAQA,CAAM,GAG/BD,EAAA,KAAOgD,EAAM,QAAQ,WAAW,MACvChD,EAAO,OAASC,EAChBD,EAAO6C,CAAQ,EAAI3C,EACZF,EAAA,WAAagD,EAAM,QAAQ,WAClCtD,EAAQ,KAAKM,CAAM,CAAA,CACrB,CACD,EACMN,CAAA,CAEX,CAMO,SAASuD,EAAOhF,EAAkC,CAChD,OAAA,IAAI2E,EAAO3E,CAAO,CAC3B,CAEO,MAAMiF,GAAeN,EACfO,GAAeF,EAMfG,GAASR,EAKTS,GAASJ,EAKf,MAAMK,UAAyBV,CAAO,CAC3C,YAAY3E,EAAkC,CAC5C,MACEE,EAAE,KAAK,OACL,CACE,WAAY,0CACd,EACAF,CAAA,CAEJ,CAAA,CAEJ,CAMO,SAASsF,GAAiBtF,EAAkC,CAC1D,OAAA,IAAIqF,EAAiBrF,CAAO,CACrC,CCjHO,MAAMuF,CAA4B,CAOvC,YAAYvF,EAAkC,CAN9CuB,EAAA,eAAyB,CACvB,WAAY,gCACZ,WAAY,oCACZ,eAAgB,CAAC,OAAQ,SAAU,SAAU,SAAU,OAAQ,OAAQ,QAAS,SAAS,CAC3F,GAGIrB,EAAA,KAAK,WAAW,KAAMF,CAAO,CAAA,CAGjC,MAAM,QAAQwB,EAA2C,CACvD,MAAMvB,EAASF,EAAgB,KAAK,QAAS,CAAE,EAAGyB,EAAO,EACnDH,EAAO,MAAMV,EAAa,KAAK,QAAQ,WAAYV,CAAM,EACxD,OAAA,KAAK,cAAcoB,CAAI,CAAA,CAGhC,QAAQG,EAA2C,CAC1C,OAAA,KAAK,QAAQA,CAAK,CAAA,CAG3B,MAAM,QAAQG,EAAyBG,EAA2C,CAC1E,MAAA7B,EAASE,EAAc,KAAK,QAAS,CACzC,IAAKwB,EAAO,IACZ,IAAKA,EAAO,GAAA,CACb,EACKN,EAAO,MAAMV,EAAa,KAAK,QAAQ,WAAYV,CAAM,EACxD,OAAA,KAAK,cAAcoB,CAAI,CAAA,CAGhC,cAAcA,EAAmE,OAC/E,MAAMI,EAA6B,CAAC,EAEhC,GAAAJ,GAAQA,EAAK,SACf,QAASgB,EAAI,EAAGA,EAAIhB,EAAK,SAAS,OAAQgB,IAAK,CACvC,MAAAmD,EAAInE,EAAK,SAASgB,CAAC,EACnBoD,EAAID,EAAE,SAAS,YACfxD,EAAS9B,EAAE,OAAOuF,EAAE,CAAC,EAAGA,EAAE,CAAC,CAAC,EAC5BC,GAASjC,EAAA+B,EAAE,aAAF,YAAA/B,EAAc,OAEvBxB,EAAOyD,EACTxF,EAAE,aAAa,CAACwF,EAAO,CAAC,EAAGA,EAAO,CAAC,CAAC,EAAG,CAACA,EAAO,CAAC,EAAGA,EAAO,CAAC,CAAC,CAAC,EAC7DxF,EAAE,aAAa8B,EAAQA,CAAM,EAEjCP,EAAQ,KAAK,CACX,KAAM,KAAK,mBAAmB+D,CAAC,EAC/B,KAAM,KAAK,QAAQ,aAAe,KAAK,QAAQ,aAAaA,CAAC,EAAI,OACjE,OAAAxD,EACA,KAAAC,EACA,WAAYuD,EAAE,UAAA,CACf,CAAA,CAIE,OAAA/D,CAAA,CAGT,mBAAmB+D,EAAoB,CACrC,OAAQ,KAAK,QAAQ,gBAAkB,CACpC,GAAA,cAAS,OAAA/B,EAAA+B,EAAE,aAAF,YAAA/B,EAAekC,GAAE,EAC1B,OAAY1E,GAAA,CAAC,CAACA,CAAC,EACf,KAAK,IAAI,CAAA,CAEhB,CAMO,SAAS2E,GAAO5F,EAAkC,CAChD,OAAA,IAAIuF,EAAOvF,CAAO,CAC3B,CC3EO,MAAM6F,CAAgC,CAK3C,YAAY7F,EAAqC,CAJjDuB,EAAA,eAA6B,CAC3B,WAAY,gCACd,GAGIrB,EAAA,KAAK,WAAW,KAAMF,CAAO,CAAA,CAGjC,MAAM,QAAQwB,EAA2C,CACvD,MAAMH,EAAO,MAAMV,EACjB,KAAK,QAAQ,WAAa,UAC1BZ,EAAgB,KAAK,QAAS,CAC5B,IAAK,KAAK,QAAQ,OAElB,KAAMyB,EAAM,MAAM,KAAK,EAAE,KAAK,GAAG,CAClC,CAAA,CACH,EACMC,EAA6B,CAAC,EACpC,GAAIJ,EAAK,SAAU,CACX,MAAAM,EAASzB,EAAE,OAAOmB,EAAK,SAAS,IAAQA,EAAK,SAAS,GAAM,EAC5DO,EAAe1B,EAAE,aAAayB,EAAQA,CAAM,EAClDF,EAAQ,CAAC,EAAI,CACX,KAAMJ,EAAK,MACX,KAAMO,EACN,OAAQD,CACV,CAAA,CAEK,OAAAF,CAAA,CAGT,QAAQD,EAA2C,CAC1C,OAAA,KAAK,QAAQA,CAAK,CAAA,CAG3B,MAAM,QAAQK,EAA2BC,EAA2C,CAClF,MAAMT,EAAO,MAAMV,EACjB,KAAK,QAAQ,WAAa,UAC1BR,EAAc,KAAK,QAAS,CAC1B,IAAK,KAAK,QAAQ,OAClB,OAAQ,CAAC0B,EAAS,IAAKA,EAAS,GAAG,EAAE,KAAK,GAAG,CAC9C,CAAA,CACH,EACMJ,EAA6B,CAAC,EAChC,GAAAJ,EAAK,OAAO,QAAU,IAAK,CACvB,MAAAW,EAAS9B,EAAE,OAAOmB,EAAK,SAAS,IAAQA,EAAK,SAAS,GAAM,EAC5DY,EAAO/B,EAAE,aAAa8B,EAAQA,CAAM,EAC1CP,EAAQ,CAAC,EAAI,CACX,KAAMJ,EAAK,MACX,KAAAY,EACA,OAAAD,CACF,CAAA,CAEK,OAAAP,CAAA,CAEX,CAMO,SAASqE,GAAW9F,EAAqC,CACvD,OAAA,IAAI6F,EAAW7F,CAAO,CAC/B,qjBCwBA,MAAM+F,CAAe,CAEnB,eAAeC,EAAa,CAAA,CAG9B,CAMA9F,EAAE,KAAK,OAAO6F,EAAe,UAAW7F,EAAE,QAAQ,SAAS,EAC3DA,EAAE,KAAK,OAAO6F,EAAe,UAAW7F,EAAE,QAAQ,SAAS,EAKpD,MAAM+F,UAAwBF,CAAe,CAkClD,YAAY/F,EAA2C,CACrD,MAAMA,CAAO,EAlCfuB,EAAA,eAAkC,CAChC,iBAAkB,GAClB,gBAAiB,GACjB,UAAW,GACX,OAAQ,QACR,SAAU,WACV,YAAa,YACb,aAAc,iBACd,UAAW,wBACX,MAAO,GACP,eAAgB,EAChB,iBAAkB,EAClB,eAAgB,IAChB,mBAAoB,EACtB,GAEQA,EAAA,cACAA,EAAA,mBACAA,EAAA,sBACAA,EAAA,uBACAA,EAAA,eACAA,EAAA,qBACAA,EAAA,aACAA,EAAA,6BACAA,EAAA,qBAAgB,GAChBA,EAAA,iBACAA,EAAA,mBACAA,EAAA,wBAQJrB,EAAA,KAAK,WAAW,KAAMF,CAAO,EAC1B,KAAK,QAAQ,WACX,KAAA,QAAQ,SAAW,IAAIiE,EAC9B,CAGF,kBAAmB,CACjB/D,EAAE,QAAQ,SAAS,KAAK,WAAY,mCAAmC,CAAA,CAGzE,qBAAsB,CACpBA,EAAE,QAAQ,YAAY,KAAK,WAAY,mCAAmC,CAAA,CAQ5E,MAAMgG,EAAY,OAChB,MAAM/B,EAAY,2BACZgC,EAAYjG,EAAE,QAAQ,OAAO,MAAOiE,EAAY,cAAc,EAC9DiC,EAAOlG,EAAE,QAAQ,OAAO,SAAUiE,EAAY,QAASgC,CAAS,EAChEE,EAAOnG,EAAE,QAAQ,OAAO,MAAOiE,EAAY,QAASgC,CAAS,EAEnE,KAAK,KAAOD,EACZ,KAAK,WAAaC,EAElBC,EAAK,UAAY,SACjBA,EAAK,KAAO,SACZA,EAAK,aAAa,aAAc,KAAK,QAAQ,SAAS,EAEhD,MAAAE,EAAS,KAAK,OAASpG,EAAE,QAAQ,OAAO,QAAS,GAAImG,CAAI,EAC/D,OAAAC,EAAM,KAAO,SACPA,EAAA,MAAQ,KAAK,QAAQ,MACrBA,EAAA,YAAc,KAAK,QAAQ,YAC/BpG,EAAA,SAAS,wBAAwBoG,CAAK,EAEnC,KAAA,cAAgBpG,EAAE,QAAQ,OAC7B,MACAiE,EAAY,iBACZgC,CACF,EACK,KAAA,cAAc,UAAY,KAAK,QAAQ,aAEvC,KAAA,MAAQjG,EAAE,QAAQ,OACrB,KACAiE,EAAY,gEACZgC,CACF,EACEjG,EAAA,SAAS,wBAAwB,KAAK,KAAK,EAE7CA,EAAE,SAAS,YAAYoG,EAAO,UAAW,KAAK,SAAU,IAAI,GACxD7C,EAAA,KAAK,QAAQ,WAAb,MAAAA,EAAuB,SACzBvD,EAAE,SAAS,YAAYoG,EAAO,QAAS,KAAK,QAAS,IAAI,EAE3DpG,EAAE,SAAS,YAAYoG,EAAO,OAAQ,IAAM,CACtC,KAAK,QAAQ,WAAa,CAAC,KAAK,sBAClC,KAAK,UAAU,EAEjB,KAAK,qBAAuB,EAAA,CAC7B,EAEG,KAAK,QAAQ,UACX,KAAK,QAAQ,SAAW,QAC1BpG,EAAE,SAAS,YAAYiG,EAAW,QAAU5B,GAAa,CAClDA,EAAiB,SAAW,GAAMA,EAAiB,SAAW,GACjE,KAAK,QAAQ,CACf,CACD,EACQ,KAAK,QAAQ,SAAW,QACjCrE,EAAE,SAAS,YACTiG,EACAjG,EAAE,QAAQ,MAAQ,uBAAyB,YAC1CqE,GAAa,CACZ,KAAK,QAAQ,EACbA,EAAE,eAAe,EACjBA,EAAE,gBAAgB,CACpB,EACA,IACF,GAEArE,EAAE,SAAS,YAAYiG,EAAW,YAAa,KAAK,QAAS,IAAI,EACjEjG,EAAE,SAAS,YAAYiG,EAAW,WAAY,KAAK,UAAW,IAAI,EAClE,KAAK,KAAK,GAAG,YAAa,KAAK,UAAW,IAAI,IAGhD,KAAK,QAAQ,EACTjG,EAAE,QAAQ,MACZA,EAAE,SAAS,YAAYiG,EAAW,aAAc,IAAM,KAAK,UAAU,EAErEjG,EAAE,SAAS,YAAYiG,EAAW,QAAS,IAAM,KAAK,UAAU,GAIhE,KAAK,QAAQ,oBACf,KAAK,GAAG,cAAe,KAAK,YAAa,IAAI,EAG/C,KAAK,GAAG,eAAgB,KAAK,iBAAkB,IAAI,EACnD,KAAK,GAAG,gBAAiB,KAAK,oBAAqB,IAAI,EACvD,KAAK,GAAG,eAAgB,KAAK,iBAAkB,IAAI,EACnD,KAAK,GAAG,gBAAiB,KAAK,oBAAqB,IAAI,EAErDjG,EAAA,SAAS,wBAAwBiG,CAAS,EAErCA,CAAA,CAOT,SAASzF,EAAsB,CAC7B,YAAK,OAAO,MAAQA,EACb,IAAA,CAGD,eAAee,EAA4B8E,EAAkB,CACnE,GAAI,CAACA,GAAW,KAAK,QAAQ,kBAAoB9E,EAAQ,SAAW,EAC7D,KAAA,uBAAuBA,EAAQ,CAAC,CAAC,UAC7BA,EAAQ,OAAS,EAAG,CAC7B,KAAK,MAAM,UAAY,GACvB,KAAK,SAAWA,EAChBvB,EAAE,QAAQ,YAAY,KAAK,MAAO,iDAAiD,EACnFA,EAAE,QAAQ,SAAS,KAAK,WAAY,uCAAuC,EAC3E,QAASmC,EAAI,EAAGA,EAAIZ,EAAQ,OAAQY,IAC7B,KAAA,MAAM,YAAY,KAAK,WAAWZ,EAAQY,CAAC,EAAGA,CAAC,CAAC,CACvD,MAEAnC,EAAE,QAAQ,SAAS,KAAK,WAAY,wCAAwC,EAC5EA,EAAE,QAAQ,SAAS,KAAK,cAAe,gCAAgC,CACzE,CAOF,YAAYsG,EAAyB,CACnC,MAAMzE,EAASyE,EAAM,QAEhB,YAAA,KAAK,UAAUzE,EAAO,IAAI,EAE3B,KAAK,gBACF,KAAA,KAAK,YAAY,KAAK,cAAc,EAG3C,KAAK,eAAiB,IAAI7B,EAAE,OAAO6B,EAAO,MAAM,EAC7C,UAAUA,EAAO,MAAQA,EAAO,IAAI,EACpC,MAAM,KAAK,IAAI,EACf,UAAU,EAEN,IAAA,CAGT,MAAc,SAASwE,EAAmB,GAAO,CACzC,MAAAvF,EAAQ,KAAK,OAAO,MAC1B,GAAI,CAACuF,GAAWvF,EAAM,OAAS,KAAK,QAAQ,eAC1C,OAGI,MAAAyF,EAAe,EAAE,KAAK,cAC5B,KAAK,aAAezF,EACfuF,GACH,KAAK,cAAc,EAGf,MAAAC,EAA2B,CAAE,MAAOxF,CAAM,EAChD,KAAK,KAAKuF,EAAU,eAAiB,eAAgBC,CAAK,EAE1D,MAAM/E,EAAU8E,EACZ,MAAM,KAAK,QAAQ,SAAU,QAASvF,CAAK,EAC3C,MAAM,KAAK,QAAQ,SAAU,QAAQA,CAAK,EAE1C,GAAAyF,IAAiB,KAAK,cAAe,CACvC,MAAMD,EAA4B,CAAE,MAAOxF,EAAO,QAAAS,CAAQ,EAC1D,KAAK,KAAK8E,EAAU,gBAAkB,gBAAiBC,CAAK,EACvD,KAAA,eAAe/E,EAAS8E,CAAO,CAAA,CACtC,CAGM,uBAAuBG,EAA0B,CACjD,MAAAF,EAA0B,CAAE,QAAAE,CAAQ,EACrC,KAAA,KAAK,cAAeF,CAAK,CAAA,CAGxB,SAAU,CACZtG,EAAE,QAAQ,SAAS,KAAK,WAAY,mCAAmC,EACzE,KAAK,UAAU,EAEf,KAAK,QAAQ,CACf,CAGM,SAAU,CAChBA,EAAE,QAAQ,SAAS,KAAK,WAAY,mCAAmC,EACvE,KAAK,OAAO,OAAO,EACnB,KAAK,KAAK,QAAQ,CAAA,CAGZ,WAAY,CAClBA,EAAE,QAAQ,YAAY,KAAK,WAAY,mCAAmC,EAC1EA,EAAE,QAAQ,SAAS,KAAK,MAAO,iDAAiD,EAChFA,EAAE,QAAQ,YAAY,KAAK,cAAe,gCAAgC,EAC1EA,EAAE,QAAQ,YAAY,KAAK,WAAY,uCAAuC,EAC9EA,EAAE,QAAQ,YAAY,KAAK,WAAY,wCAAwC,EAC/E,KAAK,OAAO,KAAK,EACjB,KAAK,KAAK,UAAU,CAAA,CAGd,eAAgB,CACtBA,EAAE,QAAQ,SAAS,KAAK,MAAO,iDAAiD,EAChF,KAAK,WAAa,KAClBA,EAAE,QAAQ,YAAY,KAAK,cAAe,gCAAgC,EAC1EA,EAAE,QAAQ,YAAY,KAAK,WAAY,uCAAuC,EAC9EA,EAAE,QAAQ,YAAY,KAAK,WAAY,wCAAwC,CAAA,CAGzE,WAAW6B,EAAyB4E,EAAe,CACzD,MAAMC,EAAK1G,EAAE,QAAQ,OAAO,KAAM,EAAE,EAClC2G,EAAI3G,EAAE,QAAQ,OAAO,IAAK,GAAI0G,CAAE,EAChCR,EACE,KAAK,QAAQ,iBAAmBrE,EAAO,KAClC7B,EAAE,QAAQ,OAAO,MAAO,GAAI2G,CAAC,EAC9B,KACNC,EAAO/E,EAAO,KAAO,OAAY,SAAS,eAAeA,EAAO,IAAI,EACpEgF,EAAoBxC,GAAa,CAM/B,KAAK,qBAAuB,GAC1BrE,EAAA,SAAS,KAAKqE,CAAC,EACjB,KAAK,uBAAuBxC,CAAM,EAClC7B,EAAE,SAAS,GAAG0G,EAAI,iBAAkB,IAAM,CACpC,KAAK,QAAQ,UACf,KAAK,UAAU,EAEf,KAAK,cAAc,CACrB,CACD,CACH,EAEF,OAAIR,IACFA,EAAK,IAAMrE,EAAO,MAGpB6E,EAAG,aAAa,oBAAqB,OAAOD,CAAK,CAAC,EAE9C5E,EAAO,KACP8E,EAAA,UAAYA,EAAE,UAAY9E,EAAO,KAC1B+E,GACTD,EAAE,YAAYC,CAAI,EAMpB5G,EAAE,SAAS,YAAY0G,EAAI,uBAAwBG,EAAkB,IAAI,EAElEH,CAAA,CAGD,SAASrC,EAAkB,CAC3B,MAAAyC,EAAUC,GAAgB,CAC1B,KAAK,aACP/G,EAAE,QAAQ,YAAY,KAAK,WAAY,mCAAmC,EAC1E,KAAK,WAAa,KAAK,WAAW+G,EAAM,EAAI,cAAgB,iBAAiB,GAE1E,KAAK,aACR,KAAK,WAAa,KAAK,MAAMA,EAAM,EAAI,aAAe,WAAW,GAG/D,KAAK,YACP/G,EAAE,QAAQ,SAAS,KAAK,WAAY,mCAAmC,CAE3E,EAEA,OAAQqE,EAAE,QAAS,CAEjB,IAAK,IACC,KAAK,QAAQ,UACf,KAAK,UAAU,EAEf,KAAK,cAAc,EAErB,MAEF,IAAK,IACHyC,EAAO,EAAE,EACT,MAEF,IAAK,IACHA,EAAO,CAAC,EACR,MAEF,IAAK,IACH,GAAI,KAAK,WAAY,CACnB,MAAML,EAAQ,SAAS,KAAK,WAAW,aAAa,mBAAmB,EAAG,EAAE,EAC5E,KAAK,uBAAuB,KAAK,SAASA,CAAK,CAAC,EAChD,KAAK,cAAc,CAAA,MAEnB,KAAK,SAAS,EAEhB,MACF,QACE,MAAA,CAGFzG,EAAA,SAAS,eAAeqE,CAAC,CAAA,CAGrB,SAAU,CACV,MAAAtD,EAAI,KAAK,OAAO,MAClBA,IAAM,KAAK,eACb,aAAa,KAAK,eAAe,EAC7BA,EAAE,QAAU,KAAK,QAAQ,iBACtB,KAAA,gBAAkB,WAAW,IAAM,KAAK,SAAS,EAAI,EAAG,KAAK,QAAQ,cAAc,EAExF,KAAK,cAAc,EAEvB,CAEJ,CAMO,SAASiG,EAASlH,EAA2C,CAC3D,OAAA,IAAIiG,EAAgBjG,CAAO,CACpC,CCleA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAaEmH,EAAA,KAAK,OAAOC,EAAUC,CAAS,EAI/BF,EAAA,KAAK,OAAOjH,EAAE,QAAS,CAAA,SACvBkH,EACA,SAAAF,CACF,CAAC"}
\ No newline at end of file
diff --git a/public/leaflet-control-geocoder/Control.Geocoder.modern.js b/public/leaflet-control-geocoder/Control.Geocoder.modern.js
new file mode 100644
index 0000000..b3ab213
--- /dev/null
+++ b/public/leaflet-control-geocoder/Control.Geocoder.modern.js
@@ -0,0 +1,1133 @@
+var N = Object.defineProperty;
+var M = (a, t, n) => t in a ? N(a, t, { enumerable: !0, configurable: !0, writable: !0, value: n }) : a[t] = n;
+var p = (a, t, n) => M(a, typeof t != "symbol" ? t + "" : t, n);
+import * as s from "leaflet";
+function u(a, t) {
+ return s.Util.extend(t, a.geocodingQueryParams);
+}
+function h(a, t) {
+ return s.Util.extend(t, a.reverseQueryParams);
+}
+const j = /[&<>"'`]/g, A = /[&<>"'`]/, G = {
+ "&": "&",
+ "<": "<",
+ ">": ">",
+ '"': """,
+ "'": "'",
+ "`": "`"
+};
+function W(a) {
+ return G[a];
+}
+function I(a) {
+ return a == null ? "" : a ? (a = "" + a, A.test(a) ? a.replace(j, W) : a) : a + "";
+}
+function d(a, t) {
+ const n = { Accept: "application/json" }, e = new URL(a);
+ return Object.entries(t).forEach(([o, i]) => {
+ (Array.isArray(i) ? i : [i]).forEach((r) => {
+ e.searchParams.append(o, r);
+ });
+ }), fetch(e.toString(), { headers: n }).then((o) => o.json());
+}
+function z(a, t) {
+ return a.replace(/\{ *([\w_]+) *\}/g, (n, e) => {
+ let o = t[e];
+ return o === void 0 ? o = "" : typeof o == "function" && (o = o(t)), I(o);
+ });
+}
+class L {
+ constructor(t) {
+ p(this, "options", {
+ serviceUrl: "https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer",
+ apiKey: ""
+ });
+ s.Util.setOptions(this, t);
+ }
+ async geocode(t) {
+ const n = u(this.options, {
+ token: this.options.apiKey,
+ SingleLine: t,
+ outFields: "Addr_Type",
+ forStorage: !1,
+ maxLocations: 10,
+ f: "json"
+ }), e = await d(
+ this.options.serviceUrl + "/findAddressCandidates",
+ n
+ ), o = [];
+ if (e.candidates && e.candidates.length)
+ for (let i = 0; i <= e.candidates.length - 1; i++) {
+ const r = e.candidates[i], l = s.latLng(r.location.y, r.location.x), c = s.latLngBounds(
+ s.latLng(r.extent.ymax, r.extent.xmax),
+ s.latLng(r.extent.ymin, r.extent.xmin)
+ );
+ o[i] = {
+ name: r.address,
+ bbox: c,
+ center: l
+ };
+ }
+ return o;
+ }
+ suggest(t) {
+ return this.geocode(t);
+ }
+ async reverse(t, n) {
+ const e = h(this.options, {
+ location: t.lng + "," + t.lat,
+ distance: 100,
+ f: "json"
+ }), o = await d(this.options.serviceUrl + "/reverseGeocode", e), i = [];
+ if (o && !o.error) {
+ const r = s.latLng(o.location.y, o.location.x), l = s.latLngBounds(r, r);
+ i.push({
+ name: o.address.Match_addr,
+ center: r,
+ bbox: l
+ });
+ }
+ return i;
+ }
+}
+function q(a) {
+ return new L(a);
+}
+class y {
+ constructor(t) {
+ p(this, "options", {
+ serviceUrl: "https://dev.virtualearth.net/REST/v1/Locations/"
+ });
+ s.Util.setOptions(this, t);
+ }
+ async geocode(t) {
+ const n = u(this.options, {
+ query: t,
+ key: this.options.apiKey
+ }), e = await d(this.options.serviceUrl, n), o = [];
+ if (e.resourceSets.length > 0)
+ for (let i = e.resourceSets[0].resources.length - 1; i >= 0; i--) {
+ const r = e.resourceSets[0].resources[i], l = r.bbox;
+ o[i] = {
+ name: r.name,
+ bbox: s.latLngBounds([l[0], l[1]], [l[2], l[3]]),
+ center: s.latLng(r.point.coordinates)
+ };
+ }
+ return o;
+ }
+ async reverse(t, n) {
+ const e = h(this.options, {
+ key: this.options.apiKey
+ }), o = await d(
+ this.options.serviceUrl + t.lat + "," + t.lng,
+ e
+ ), i = [];
+ for (let r = o.resourceSets[0].resources.length - 1; r >= 0; r--) {
+ const l = o.resourceSets[0].resources[r], c = l.bbox;
+ i[r] = {
+ name: l.name,
+ bbox: s.latLngBounds([c[0], c[1]], [c[2], c[3]]),
+ center: s.latLng(l.point.coordinates)
+ };
+ }
+ return i;
+ }
+}
+function H(a) {
+ return new y(a);
+}
+class x {
+ constructor(t) {
+ p(this, "options", {
+ apiKey: "",
+ serviceUrl: "https://atlas.microsoft.com/search"
+ });
+ if (s.Util.setOptions(this, t), !this.options.apiKey)
+ throw new Error("Azure Maps Geocoder requires an API key.");
+ }
+ /**
+ * {@inheritdoc}
+ * https://learn.microsoft.com/en-us/rest/api/maps/search/get-search-address?view=rest-maps-1.0&tabs=HTTP
+ */
+ async geocode(t) {
+ const n = {
+ "api-version": "1.0",
+ query: t,
+ "subscription-key": this.options.apiKey
+ }, e = this.options.serviceUrl + "/address/json", o = await d(e, n), i = [];
+ if (o.results && o.results.length > 0)
+ for (const r of o.results)
+ i.push({
+ name: r.address.freeformAddress,
+ bbox: s.latLngBounds(
+ [r.viewport.topLeftPoint.lat, r.viewport.topLeftPoint.lon],
+ [r.viewport.btmRightPoint.lat, r.viewport.btmRightPoint.lon]
+ ),
+ center: s.latLng(r.position.lat, r.position.lon)
+ });
+ return i;
+ }
+ /**
+ * {@inheritdoc}
+ * https://learn.microsoft.com/en-us/rest/api/maps/search/get-search-address-reverse?view=rest-maps-1.0&tabs=HTTP
+ */
+ async reverse(t, n) {
+ const e = {
+ "api-version": "1.0",
+ query: t.lat + "," + t.lng,
+ "subscription-key": this.options.apiKey
+ }, o = this.options.serviceUrl + "/address/reverse/json", i = await d(o, e), r = [];
+ if (i.addresses && i.addresses.length > 0)
+ for (const l of i.addresses)
+ r.push({
+ name: l.address.freeformAddress,
+ bbox: s.latLngBounds(
+ [l.viewport.topLeftPoint.lat, l.viewport.topLeftPoint.lon],
+ [l.viewport.btmRightPoint.lat, l.viewport.btmRightPoint.lon]
+ ),
+ center: s.latLng(t.lat, t.lng)
+ });
+ return r;
+ }
+}
+function J(a) {
+ return new x(a);
+}
+class w {
+ constructor(t) {
+ p(this, "options", {
+ serviceUrl: "https://maps.googleapis.com/maps/api/geocode/json"
+ });
+ s.Util.setOptions(this, t);
+ }
+ async geocode(t) {
+ const n = u(this.options, {
+ key: this.options.apiKey,
+ address: t
+ }), e = await d(this.options.serviceUrl, n), o = [];
+ if (e.results && e.results.length)
+ for (let i = 0; i <= e.results.length - 1; i++) {
+ const r = e.results[i], l = s.latLng(r.geometry.location), c = s.latLngBounds(
+ s.latLng(r.geometry.viewport.northeast),
+ s.latLng(r.geometry.viewport.southwest)
+ );
+ o[i] = {
+ name: r.formatted_address,
+ bbox: c,
+ center: l,
+ properties: r.address_components
+ };
+ }
+ return o;
+ }
+ async reverse(t, n) {
+ const e = h(this.options, {
+ key: this.options.apiKey,
+ latlng: t.lat + "," + t.lng
+ }), o = await d(this.options.serviceUrl, e), i = [];
+ if (o.results && o.results.length)
+ for (let r = 0; r <= o.results.length - 1; r++) {
+ const l = o.results[r], c = s.latLng(l.geometry.location), g = s.latLngBounds(
+ s.latLng(l.geometry.viewport.northeast),
+ s.latLng(l.geometry.viewport.southwest)
+ );
+ i[r] = {
+ name: l.formatted_address,
+ bbox: g,
+ center: c,
+ properties: l.address_components
+ };
+ }
+ return i;
+ }
+}
+function V(a) {
+ return new w(a);
+}
+class U {
+ constructor(t) {
+ p(this, "options", {
+ serviceUrl: "https://geocoder.api.here.com/6.2/",
+ app_id: "",
+ app_code: "",
+ apiKey: "",
+ maxResults: 5
+ });
+ if (s.Util.setOptions(this, t), t != null && t.apiKey) throw Error("apiKey is not supported, use app_id/app_code instead!");
+ }
+ geocode(t) {
+ const n = u(this.options, {
+ searchtext: t,
+ gen: 9,
+ app_id: this.options.app_id,
+ app_code: this.options.app_code,
+ jsonattributes: 1,
+ maxresults: this.options.maxResults
+ });
+ return this.getJSON(this.options.serviceUrl + "geocode.json", n);
+ }
+ reverse(t, n) {
+ let e = t.lat + "," + t.lng;
+ this.options.reverseGeocodeProxRadius && (e += "," + this.options.reverseGeocodeProxRadius);
+ const o = h(this.options, {
+ prox: e,
+ mode: "retrieveAddresses",
+ app_id: this.options.app_id,
+ app_code: this.options.app_code,
+ gen: 9,
+ jsonattributes: 1,
+ maxresults: this.options.maxResults
+ });
+ return this.getJSON(this.options.serviceUrl + "reversegeocode.json", o);
+ }
+ async getJSON(t, n) {
+ const e = await d(t, n), o = [];
+ if (e.response.view && e.response.view.length)
+ for (let i = 0; i <= e.response.view[0].result.length - 1; i++) {
+ const r = e.response.view[0].result[i].location, l = s.latLng(r.displayPosition.latitude, r.displayPosition.longitude), c = s.latLngBounds(
+ s.latLng(r.mapView.topLeft.latitude, r.mapView.topLeft.longitude),
+ s.latLng(r.mapView.bottomRight.latitude, r.mapView.bottomRight.longitude)
+ );
+ o[i] = {
+ name: r.address.label,
+ properties: r.address,
+ bbox: c,
+ center: l
+ };
+ }
+ return o;
+ }
+}
+class C {
+ constructor(t) {
+ p(this, "options", {
+ serviceUrl: "https://geocode.search.hereapi.com/v1",
+ apiKey: "",
+ app_id: "",
+ app_code: "",
+ maxResults: 10
+ });
+ s.Util.setOptions(this, t);
+ }
+ geocode(t) {
+ const n = u(this.options, {
+ q: t,
+ apiKey: this.options.apiKey,
+ limit: this.options.maxResults
+ });
+ if (!n.at && !n.in)
+ throw Error(
+ "at / in parameters not found. Please define coordinates (at=latitude,longitude) or other (in) in your geocodingQueryParams."
+ );
+ return this.getJSON(this.options.serviceUrl + "/discover", n);
+ }
+ reverse(t, n) {
+ const e = h(this.options, {
+ at: t.lat + "," + t.lng,
+ limit: this.options.reverseGeocodeProxRadius,
+ apiKey: this.options.apiKey
+ });
+ return this.getJSON(this.options.serviceUrl + "/revgeocode", e);
+ }
+ async getJSON(t, n) {
+ const e = await d(t, n), o = [];
+ if (e.items && e.items.length)
+ for (let i = 0; i <= e.items.length - 1; i++) {
+ const r = e.items[i], l = s.latLng(r.position.lat, r.position.lng);
+ let c;
+ r.mapView ? c = s.latLngBounds(
+ s.latLng(r.mapView.south, r.mapView.west),
+ s.latLng(r.mapView.north, r.mapView.east)
+ ) : c = s.latLngBounds(
+ s.latLng(r.position.lat, r.position.lng),
+ s.latLng(r.position.lat, r.position.lng)
+ ), o[i] = {
+ name: r.address.label,
+ properties: r.address,
+ bbox: c,
+ center: l
+ };
+ }
+ return o;
+ }
+}
+function $(a) {
+ return a != null && a.apiKey ? new C(a) : new U(a);
+}
+function R(a) {
+ let t;
+ if (t = a.match(/^([NS])\s*(\d{1,3}(?:\.\d*)?)\W*([EW])\s*(\d{1,3}(?:\.\d*)?)$/))
+ return s.latLng(
+ (/N/i.test(t[1]) ? 1 : -1) * +t[2],
+ (/E/i.test(t[3]) ? 1 : -1) * +t[4]
+ );
+ if (t = a.match(/^(\d{1,3}(?:\.\d*)?)\s*([NS])\W*(\d{1,3}(?:\.\d*)?)\s*([EW])$/))
+ return s.latLng(
+ (/N/i.test(t[2]) ? 1 : -1) * +t[1],
+ (/E/i.test(t[4]) ? 1 : -1) * +t[3]
+ );
+ if (t = a.match(
+ /^([NS])\s*(\d{1,3})°?\s*(\d{1,3}(?:\.\d*)?)?['′]?\W*([EW])\s*(\d{1,3})°?\s*(\d{1,3}(?:\.\d*)?)?['′]?$/
+ ))
+ return s.latLng(
+ (/N/i.test(t[1]) ? 1 : -1) * (+t[2] + +t[3] / 60),
+ (/E/i.test(t[4]) ? 1 : -1) * (+t[5] + +t[6] / 60)
+ );
+ if (t = a.match(
+ /^(\d{1,3})°?\s*(\d{1,3}(?:\.\d*)?)?['′]?\s*([NS])\W*(\d{1,3})°?\s*(\d{1,3}(?:\.\d*)?)?['′]?\s*([EW])$/
+ ))
+ return s.latLng(
+ (/N/i.test(t[3]) ? 1 : -1) * (+t[1] + +t[2] / 60),
+ (/E/i.test(t[6]) ? 1 : -1) * (+t[4] + +t[5] / 60)
+ );
+ if (t = a.match(
+ /^([NS])\s*(\d{1,3})°?\s*(\d{1,2})['′]?\s*(\d{1,3}(?:\.\d*)?)?["″]?\W*([EW])\s*(\d{1,3})°?\s*(\d{1,2})['′]?\s*(\d{1,3}(?:\.\d*)?)?["″]?$/
+ ))
+ return s.latLng(
+ (/N/i.test(t[1]) ? 1 : -1) * (+t[2] + +t[3] / 60 + +t[4] / 3600),
+ (/E/i.test(t[5]) ? 1 : -1) * (+t[6] + +t[7] / 60 + +t[8] / 3600)
+ );
+ if (t = a.match(
+ /^(\d{1,3})°?\s*(\d{1,2})['′]?\s*(\d{1,3}(?:\.\d*)?)?["″]\s*([NS])\W*(\d{1,3})°?\s*(\d{1,2})['′]?\s*(\d{1,3}(?:\.\d*)?)?["″]?\s*([EW])$/
+ ))
+ return s.latLng(
+ (/N/i.test(t[4]) ? 1 : -1) * (+t[1] + +t[2] / 60 + +t[3] / 3600),
+ (/E/i.test(t[8]) ? 1 : -1) * (+t[5] + +t[6] / 60 + +t[7] / 3600)
+ );
+ if (t = a.match(/^\s*([+-]?\d+(?:\.\d*)?)\s*[\s,]\s*([+-]?\d+(?:\.\d*)?)\s*$/))
+ return s.latLng(+t[1], +t[2]);
+}
+class E {
+ constructor(t) {
+ p(this, "options", {
+ next: void 0,
+ sizeInMeters: 1e4
+ });
+ s.Util.setOptions(this, t);
+ }
+ async geocode(t) {
+ const n = R(t);
+ return n ? [
+ {
+ name: t,
+ center: n,
+ bbox: n.toBounds(this.options.sizeInMeters)
+ }
+ ] : this.options.next ? this.options.next.geocode(t) : [];
+ }
+}
+function F(a) {
+ return new E(a);
+}
+class k {
+ constructor(t) {
+ p(this, "options", {
+ serviceUrl: "https://api.mapbox.com/geocoding/v5/mapbox.places/"
+ });
+ s.Util.setOptions(this, t);
+ }
+ _getProperties(t) {
+ const n = {
+ text: t.text,
+ address: t.address
+ };
+ for (let e = 0; e < (t.context || []).length; e++) {
+ const o = t.context[e].id.split(".")[0];
+ n[o] = t.context[e].text, t.context[e].short_code && (n.countryShortCode = t.context[e].short_code);
+ }
+ return n;
+ }
+ async geocode(t) {
+ const n = this.options.serviceUrl + encodeURIComponent(t) + ".json", e = u(this.options, {
+ access_token: this.options.apiKey
+ });
+ e.proximity !== void 0 && e.proximity.lat !== void 0 && e.proximity.lng !== void 0 && (e.proximity = e.proximity.lng + "," + e.proximity.lat);
+ const o = await d(n, e);
+ return this._parseResults(o);
+ }
+ suggest(t) {
+ return this.geocode(t);
+ }
+ async reverse(t, n) {
+ const e = this.options.serviceUrl + t.lng + "," + t.lat + ".json", o = h(this.options, {
+ access_token: this.options.apiKey
+ }), i = await d(e, o);
+ return this._parseResults(i);
+ }
+ _parseResults(t) {
+ var e;
+ if (!((e = t.features) != null && e.length))
+ return [];
+ const n = [];
+ for (let o = 0; o <= t.features.length - 1; o++) {
+ const i = t.features[o], r = s.latLng(i.center.reverse());
+ let l;
+ i.bbox ? l = s.latLngBounds(
+ s.latLng(i.bbox.slice(0, 2).reverse()),
+ s.latLng(i.bbox.slice(2, 4).reverse())
+ ) : l = s.latLngBounds(r, r), n[o] = {
+ name: i.place_name,
+ bbox: l,
+ center: r,
+ properties: this._getProperties(i)
+ };
+ }
+ return n;
+ }
+}
+function Q(a) {
+ return new k(a);
+}
+class D {
+ constructor(t) {
+ p(this, "options", {
+ serviceUrl: "https://www.mapquestapi.com/geocoding/v1"
+ });
+ s.Util.setOptions(this, t), this.options.apiKey = decodeURIComponent(this.options.apiKey);
+ }
+ _formatName(...t) {
+ return t.filter((n) => !!n).join(", ");
+ }
+ async geocode(t) {
+ const n = u(this.options, {
+ key: this.options.apiKey,
+ location: t,
+ limit: 5,
+ outFormat: "json"
+ }), e = await d(this.options.serviceUrl + "/address", n);
+ return this._parseResults(e);
+ }
+ async reverse(t, n) {
+ const e = h(this.options, {
+ key: this.options.apiKey,
+ location: t.lat + "," + t.lng,
+ outputFormat: "json"
+ }), o = await d(this.options.serviceUrl + "/reverse", e);
+ return this._parseResults(o);
+ }
+ _parseResults(t) {
+ const n = [];
+ if (t.results && t.results[0].locations)
+ for (let e = t.results[0].locations.length - 1; e >= 0; e--) {
+ const o = t.results[0].locations[e], i = s.latLng(o.latLng);
+ n[e] = {
+ name: this._formatName(o.street, o.adminArea4, o.adminArea3, o.adminArea1),
+ bbox: s.latLngBounds(i, i),
+ center: i
+ };
+ }
+ return n;
+ }
+}
+function X(a) {
+ return new D(a);
+}
+class B {
+ constructor(t) {
+ p(this, "options", {
+ userId: "",
+ apiKey: "",
+ serviceUrl: "https://neutrinoapi.com/"
+ });
+ s.Util.setOptions(this, t);
+ }
+ // https://www.neutrinoapi.com/api/geocode-address/
+ async geocode(t) {
+ const n = u(this.options, {
+ apiKey: this.options.apiKey,
+ userId: this.options.userId,
+ //get three words and make a dot based string
+ address: t.split(/\s+/).join(".")
+ }), e = await d(this.options.serviceUrl + "geocode-address", n), o = [];
+ if (e.locations) {
+ e.geometry = e.locations[0];
+ const i = s.latLng(e.geometry.latitude, e.geometry.longitude), r = s.latLngBounds(i, i);
+ o[0] = {
+ name: e.geometry.address,
+ bbox: r,
+ center: i
+ };
+ }
+ return o;
+ }
+ suggest(t) {
+ return this.geocode(t);
+ }
+ // https://www.neutrinoapi.com/api/geocode-reverse/
+ async reverse(t, n) {
+ const e = h(this.options, {
+ apiKey: this.options.apiKey,
+ userId: this.options.userId,
+ latitude: t.lat,
+ longitude: t.lng
+ }), o = await d(this.options.serviceUrl + "geocode-reverse", e), i = [];
+ if (o.status.status == 200 && o.found) {
+ const r = s.latLng(t.lat, t.lng), l = s.latLngBounds(r, r);
+ i[0] = {
+ name: o.address,
+ bbox: l,
+ center: r
+ };
+ }
+ return i;
+ }
+}
+function Y(a) {
+ return new B(a);
+}
+class f {
+ constructor(t) {
+ p(this, "options", {
+ serviceUrl: "https://nominatim.openstreetmap.org/",
+ htmlTemplate(t) {
+ const n = t.address;
+ let e;
+ const o = [];
+ return (n.road || n.building) && o.push("{building} {road} {house_number}"), (n.city || n.town || n.village || n.hamlet) && (e = o.length > 0 ? "leaflet-control-geocoder-address-detail" : "", o.push(
+ '{postcode} {city} {town} {village} {hamlet}'
+ )), (n.state || n.country) && (e = o.length > 0 ? "leaflet-control-geocoder-address-context" : "", o.push('{state} {country}')), z(o.join("
"), n);
+ }
+ });
+ s.Util.setOptions(this, t || {});
+ }
+ async geocode(t) {
+ const n = u(this.options, {
+ q: t,
+ limit: 5,
+ format: "json",
+ addressdetails: 1
+ }), e = await d(this.options.serviceUrl + "search", n), o = [];
+ for (let i = e.length - 1; i >= 0; i--) {
+ const r = e[i].boundingbox;
+ o[i] = {
+ icon: e[i].icon,
+ name: e[i].display_name,
+ html: this.options.htmlTemplate ? this.options.htmlTemplate(e[i]) : void 0,
+ bbox: s.latLngBounds([+r[0], +r[2]], [+r[1], +r[3]]),
+ center: s.latLng(+e[i].lat, +e[i].lon),
+ properties: e[i]
+ };
+ }
+ return o;
+ }
+ async reverse(t, n) {
+ const e = h(this.options, {
+ lat: t.lat,
+ lon: t.lng,
+ zoom: Math.round(Math.log(n / 256) / Math.log(2)),
+ addressdetails: 1,
+ format: "json"
+ }), o = await d(this.options.serviceUrl + "reverse", e), i = [];
+ if (o && o.lat && o.lon) {
+ const r = s.latLng(+o.lat, +o.lon), l = s.latLngBounds(r, r);
+ i.push({
+ name: o.display_name,
+ html: this.options.htmlTemplate ? this.options.htmlTemplate(o) : void 0,
+ center: r,
+ bbox: l,
+ properties: o
+ });
+ }
+ return i;
+ }
+}
+function Z(a) {
+ return new f(a);
+}
+class K {
+ constructor(t) {
+ p(this, "options", {});
+ s.Util.setOptions(this, t);
+ }
+ async geocode(t) {
+ try {
+ const n = this.options.OpenLocationCode.decode(t);
+ return [{
+ name: t,
+ center: s.latLng(n.latitudeCenter, n.longitudeCenter),
+ bbox: s.latLngBounds(
+ s.latLng(n.latitudeLo, n.longitudeLo),
+ s.latLng(n.latitudeHi, n.longitudeHi)
+ )
+ }];
+ } catch (n) {
+ return console.warn(n), [];
+ }
+ }
+ async reverse(t, n) {
+ try {
+ return [{
+ name: this.options.OpenLocationCode.encode(
+ t.lat,
+ t.lng,
+ this.options.codeLength
+ ),
+ center: s.latLng(t.lat, t.lng),
+ bbox: s.latLngBounds(
+ s.latLng(t.lat, t.lng),
+ s.latLng(t.lat, t.lng)
+ )
+ }];
+ } catch (e) {
+ return console.warn(e), [];
+ }
+ }
+}
+function tt(a) {
+ return new K(a);
+}
+class S {
+ constructor(t) {
+ p(this, "options", {
+ serviceUrl: "https://api.opencagedata.com/geocode/v1/json"
+ });
+ s.Util.setOptions(this, t);
+ }
+ async geocode(t) {
+ const n = u(this.options, {
+ key: this.options.apiKey,
+ q: t
+ }), e = await d(this.options.serviceUrl, n);
+ return this._parseResults(e);
+ }
+ suggest(t) {
+ return this.geocode(t);
+ }
+ async reverse(t, n) {
+ const e = h(this.options, {
+ key: this.options.apiKey,
+ q: [t.lat, t.lng].join(",")
+ }), o = await d(this.options.serviceUrl, e);
+ return this._parseResults(o);
+ }
+ _parseResults(t) {
+ const n = [];
+ if (t.results && t.results.length)
+ for (let e = 0; e < t.results.length; e++) {
+ const o = t.results[e], i = s.latLng(o.geometry);
+ let r;
+ o.annotations && o.annotations.bounds ? r = s.latLngBounds(
+ s.latLng(o.annotations.bounds.northeast),
+ s.latLng(o.annotations.bounds.southwest)
+ ) : r = s.latLngBounds(i, i), n.push({
+ name: o.formatted,
+ bbox: r,
+ center: i
+ });
+ }
+ return n;
+ }
+}
+function et(a) {
+ return new S(a);
+}
+class m {
+ constructor(t) {
+ p(this, "options", {
+ serviceUrl: "https://api.geocode.earth/v1"
+ });
+ s.Util.setOptions(this, t);
+ }
+ async geocode(t) {
+ const n = u(this.options, {
+ api_key: this.options.apiKey,
+ text: t
+ }), e = await d(this.options.serviceUrl + "/search", n);
+ return this._parseResults(e, "bbox");
+ }
+ async suggest(t) {
+ const n = u(this.options, {
+ api_key: this.options.apiKey,
+ text: t
+ }), e = await d(this.options.serviceUrl + "/autocomplete", n);
+ return this._parseResults(e, "bbox");
+ }
+ async reverse(t, n) {
+ const e = h(this.options, {
+ api_key: this.options.apiKey,
+ "point.lat": t.lat,
+ "point.lon": t.lng
+ }), o = await d(this.options.serviceUrl + "/reverse", e);
+ return this._parseResults(o, "bounds");
+ }
+ _parseResults(t, n) {
+ const e = [];
+ return s.geoJSON(t, {
+ pointToLayer(o, i) {
+ return s.circleMarker(i);
+ },
+ onEachFeature(o, i) {
+ const r = {};
+ let l, c;
+ i.getBounds ? (l = i.getBounds(), c = l.getCenter()) : i.feature.bbox ? (c = i.getLatLng(), l = s.latLngBounds(
+ s.GeoJSON.coordsToLatLng(i.feature.bbox.slice(0, 2)),
+ s.GeoJSON.coordsToLatLng(i.feature.bbox.slice(2, 4))
+ )) : (c = i.getLatLng(), l = s.latLngBounds(c, c)), r.name = i.feature.properties.label, r.center = c, r[n] = l, r.properties = i.feature.properties, e.push(r);
+ }
+ }), e;
+ }
+}
+function v(a) {
+ return new m(a);
+}
+const st = m, ot = v, nt = m, it = v;
+class O extends m {
+ constructor(t) {
+ super(
+ s.Util.extend(
+ {
+ serviceUrl: "https://api.openrouteservice.org/geocode"
+ },
+ t
+ )
+ );
+ }
+}
+function rt(a) {
+ return new O(a);
+}
+class P {
+ constructor(t) {
+ p(this, "options", {
+ serviceUrl: "https://photon.komoot.io/api/",
+ reverseUrl: "https://photon.komoot.io/reverse/",
+ nameProperties: ["name", "street", "suburb", "hamlet", "town", "city", "state", "country"]
+ });
+ s.Util.setOptions(this, t);
+ }
+ async geocode(t) {
+ const n = u(this.options, { q: t }), e = await d(this.options.serviceUrl, n);
+ return this._parseResults(e);
+ }
+ suggest(t) {
+ return this.geocode(t);
+ }
+ async reverse(t, n) {
+ const e = h(this.options, {
+ lat: t.lat,
+ lon: t.lng
+ }), o = await d(this.options.reverseUrl, e);
+ return this._parseResults(o);
+ }
+ _parseResults(t) {
+ var e;
+ const n = [];
+ if (t && t.features)
+ for (let o = 0; o < t.features.length; o++) {
+ const i = t.features[o], r = i.geometry.coordinates, l = s.latLng(r[1], r[0]), c = (e = i.properties) == null ? void 0 : e.extent, g = c ? s.latLngBounds([c[1], c[0]], [c[3], c[2]]) : s.latLngBounds(l, l);
+ n.push({
+ name: this._decodeFeatureName(i),
+ html: this.options.htmlTemplate ? this.options.htmlTemplate(i) : void 0,
+ center: l,
+ bbox: g,
+ properties: i.properties
+ });
+ }
+ return n;
+ }
+ _decodeFeatureName(t) {
+ return (this.options.nameProperties || []).map((n) => {
+ var e;
+ return (e = t.properties) == null ? void 0 : e[n];
+ }).filter((n) => !!n).join(", ");
+ }
+}
+function at(a) {
+ return new P(a);
+}
+class T {
+ constructor(t) {
+ p(this, "options", {
+ serviceUrl: "https://api.what3words.com/v2/"
+ });
+ s.Util.setOptions(this, t);
+ }
+ async geocode(t) {
+ const n = await d(
+ this.options.serviceUrl + "forward",
+ u(this.options, {
+ key: this.options.apiKey,
+ //get three words and make a dot based string
+ addr: t.split(/\s+/).join(".")
+ })
+ ), e = [];
+ if (n.geometry) {
+ const o = s.latLng(n.geometry.lat, n.geometry.lng), i = s.latLngBounds(o, o);
+ e[0] = {
+ name: n.words,
+ bbox: i,
+ center: o
+ };
+ }
+ return e;
+ }
+ suggest(t) {
+ return this.geocode(t);
+ }
+ async reverse(t, n) {
+ const e = await d(
+ this.options.serviceUrl + "reverse",
+ h(this.options, {
+ key: this.options.apiKey,
+ coords: [t.lat, t.lng].join(",")
+ })
+ ), o = [];
+ if (e.status.status == 200) {
+ const i = s.latLng(e.geometry.lat, e.geometry.lng), r = s.latLngBounds(i, i);
+ o[0] = {
+ name: e.words,
+ bbox: r,
+ center: i
+ };
+ }
+ return o;
+ }
+}
+function lt(a) {
+ return new T(a);
+}
+const ct = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
+ __proto__: null,
+ ArcGis: L,
+ AzureMaps: x,
+ Bing: y,
+ GeocodeEarth: st,
+ Google: w,
+ HERE: U,
+ HEREv2: C,
+ LatLng: E,
+ MapQuest: D,
+ Mapbox: k,
+ Mapzen: nt,
+ Neutrino: B,
+ Nominatim: f,
+ OpenCage: S,
+ OpenLocationCode: K,
+ Openrouteservice: O,
+ Pelias: m,
+ Photon: P,
+ What3Words: T,
+ arcgis: q,
+ azure: J,
+ bing: H,
+ geocodeEarth: ot,
+ geocodingParams: u,
+ google: V,
+ here: $,
+ latLng: F,
+ mapQuest: X,
+ mapbox: Q,
+ mapzen: it,
+ neutrino: Y,
+ nominatim: Z,
+ openLocationCode: tt,
+ opencage: et,
+ openrouteservice: rt,
+ parseLatLng: R,
+ pelias: v,
+ photon: at,
+ reverseParams: h,
+ what3words: lt
+}, Symbol.toStringTag, { value: "Module" }));
+class _ {
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ constructor(...t) {
+ }
+}
+s.Util.extend(_.prototype, s.Control.prototype);
+s.Util.extend(_.prototype, s.Evented.prototype);
+class b extends _ {
+ /**
+ * Instantiates a geocoder control (to be invoked using `new`)
+ * @param options the options
+ */
+ constructor(n) {
+ super(n);
+ p(this, "options", {
+ showUniqueResult: !0,
+ showResultIcons: !1,
+ collapsed: !0,
+ expand: "touch",
+ position: "topright",
+ placeholder: "Search...",
+ errorMessage: "Nothing found.",
+ iconLabel: "Initiate a new search",
+ query: "",
+ queryMinLength: 1,
+ suggestMinLength: 3,
+ suggestTimeout: 250,
+ defaultMarkGeocode: !0
+ });
+ p(this, "_alts");
+ p(this, "_container");
+ p(this, "_errorElement");
+ p(this, "_geocodeMarker");
+ p(this, "_input");
+ p(this, "_lastGeocode");
+ p(this, "_map");
+ p(this, "_preventBlurCollapse");
+ p(this, "_requestCount", 0);
+ p(this, "_results");
+ p(this, "_selection");
+ p(this, "_suggestTimeout");
+ s.Util.setOptions(this, n), this.options.geocoder || (this.options.geocoder = new f());
+ }
+ addThrobberClass() {
+ s.DomUtil.addClass(this._container, "leaflet-control-geocoder-throbber");
+ }
+ removeThrobberClass() {
+ s.DomUtil.removeClass(this._container, "leaflet-control-geocoder-throbber");
+ }
+ /**
+ * Returns the container DOM element for the control and add listeners on relevant map events.
+ * @param map the map instance
+ * @see https://leafletjs.com/reference.html#control-onadd
+ */
+ onAdd(n) {
+ var c;
+ const e = "leaflet-control-geocoder", o = s.DomUtil.create("div", e + " leaflet-bar"), i = s.DomUtil.create("button", e + "-icon", o), r = s.DomUtil.create("div", e + "-form", o);
+ this._map = n, this._container = o, i.innerHTML = " ", i.type = "button", i.setAttribute("aria-label", this.options.iconLabel);
+ const l = this._input = s.DomUtil.create("input", "", r);
+ return l.type = "search", l.value = this.options.query, l.placeholder = this.options.placeholder, s.DomEvent.disableClickPropagation(l), this._errorElement = s.DomUtil.create(
+ "div",
+ e + "-form-no-error",
+ o
+ ), this._errorElement.innerHTML = this.options.errorMessage, this._alts = s.DomUtil.create(
+ "ul",
+ e + "-alternatives leaflet-control-geocoder-alternatives-minimized",
+ o
+ ), s.DomEvent.disableClickPropagation(this._alts), s.DomEvent.addListener(l, "keydown", this._keydown, this), (c = this.options.geocoder) != null && c.suggest && s.DomEvent.addListener(l, "input", this._change, this), s.DomEvent.addListener(l, "blur", () => {
+ this.options.collapsed && !this._preventBlurCollapse && this._collapse(), this._preventBlurCollapse = !1;
+ }), this.options.collapsed ? this.options.expand === "click" ? s.DomEvent.addListener(o, "click", (g) => {
+ g.button === 0 && g.detail !== 2 && this._toggle();
+ }) : this.options.expand === "touch" ? s.DomEvent.addListener(
+ o,
+ s.Browser.touch ? "touchstart mousedown" : "mousedown",
+ (g) => {
+ this._toggle(), g.preventDefault(), g.stopPropagation();
+ },
+ this
+ ) : (s.DomEvent.addListener(o, "mouseover", this._expand, this), s.DomEvent.addListener(o, "mouseout", this._collapse, this), this._map.on("movestart", this._collapse, this)) : (this._expand(), s.Browser.touch ? s.DomEvent.addListener(o, "touchstart", () => this._geocode()) : s.DomEvent.addListener(o, "click", () => this._geocode())), this.options.defaultMarkGeocode && this.on("markgeocode", this.markGeocode, this), this.on("startgeocode", this.addThrobberClass, this), this.on("finishgeocode", this.removeThrobberClass, this), this.on("startsuggest", this.addThrobberClass, this), this.on("finishsuggest", this.removeThrobberClass, this), s.DomEvent.disableClickPropagation(o), o;
+ }
+ /**
+ * Sets the query string on the text input
+ * @param string the query string
+ */
+ setQuery(n) {
+ return this._input.value = n, this;
+ }
+ _geocodeResult(n, e) {
+ if (!e && this.options.showUniqueResult && n.length === 1)
+ this._geocodeResultSelected(n[0]);
+ else if (n.length > 0) {
+ this._alts.innerHTML = "", this._results = n, s.DomUtil.removeClass(this._alts, "leaflet-control-geocoder-alternatives-minimized"), s.DomUtil.addClass(this._container, "leaflet-control-geocoder-options-open");
+ for (let o = 0; o < n.length; o++)
+ this._alts.appendChild(this._createAlt(n[o], o));
+ } else
+ s.DomUtil.addClass(this._container, "leaflet-control-geocoder-options-error"), s.DomUtil.addClass(this._errorElement, "leaflet-control-geocoder-error");
+ }
+ /**
+ * Marks a geocoding result on the map
+ * @param result the geocoding result
+ */
+ markGeocode(n) {
+ const e = n.geocode;
+ return this._map.fitBounds(e.bbox), this._geocodeMarker && this._map.removeLayer(this._geocodeMarker), this._geocodeMarker = new s.Marker(e.center).bindPopup(e.html || e.name).addTo(this._map).openPopup(), this;
+ }
+ async _geocode(n = !1) {
+ const e = this._input.value;
+ if (!n && e.length < this.options.queryMinLength)
+ return;
+ const o = ++this._requestCount;
+ this._lastGeocode = e, n || this._clearResults();
+ const i = { input: e };
+ this.fire(n ? "startsuggest" : "startgeocode", i);
+ const r = n ? await this.options.geocoder.suggest(e) : await this.options.geocoder.geocode(e);
+ if (o === this._requestCount) {
+ const l = { input: e, results: r };
+ this.fire(n ? "finishsuggest" : "finishgeocode", l), this._geocodeResult(r, n);
+ }
+ }
+ _geocodeResultSelected(n) {
+ const e = { geocode: n };
+ this.fire("markgeocode", e);
+ }
+ _toggle() {
+ s.DomUtil.hasClass(this._container, "leaflet-control-geocoder-expanded") ? this._collapse() : this._expand();
+ }
+ _expand() {
+ s.DomUtil.addClass(this._container, "leaflet-control-geocoder-expanded"), this._input.select(), this.fire("expand");
+ }
+ _collapse() {
+ s.DomUtil.removeClass(this._container, "leaflet-control-geocoder-expanded"), s.DomUtil.addClass(this._alts, "leaflet-control-geocoder-alternatives-minimized"), s.DomUtil.removeClass(this._errorElement, "leaflet-control-geocoder-error"), s.DomUtil.removeClass(this._container, "leaflet-control-geocoder-options-open"), s.DomUtil.removeClass(this._container, "leaflet-control-geocoder-options-error"), this._input.blur(), this.fire("collapse");
+ }
+ _clearResults() {
+ s.DomUtil.addClass(this._alts, "leaflet-control-geocoder-alternatives-minimized"), this._selection = null, s.DomUtil.removeClass(this._errorElement, "leaflet-control-geocoder-error"), s.DomUtil.removeClass(this._container, "leaflet-control-geocoder-options-open"), s.DomUtil.removeClass(this._container, "leaflet-control-geocoder-options-error");
+ }
+ _createAlt(n, e) {
+ const o = s.DomUtil.create("li", ""), i = s.DomUtil.create("a", "", o), r = this.options.showResultIcons && n.icon ? s.DomUtil.create("img", "", i) : null, l = n.html ? void 0 : document.createTextNode(n.name), c = (g) => {
+ this._preventBlurCollapse = !0, s.DomEvent.stop(g), this._geocodeResultSelected(n), s.DomEvent.on(o, "click touchend", () => {
+ this.options.collapsed ? this._collapse() : this._clearResults();
+ });
+ };
+ return r && (r.src = n.icon), o.setAttribute("data-result-index", String(e)), n.html ? i.innerHTML = i.innerHTML + n.html : l && i.appendChild(l), s.DomEvent.addListener(o, "mousedown touchstart", c, this), o;
+ }
+ _keydown(n) {
+ const e = (o) => {
+ this._selection && (s.DomUtil.removeClass(this._selection, "leaflet-control-geocoder-selected"), this._selection = this._selection[o > 0 ? "nextSibling" : "previousSibling"]), this._selection || (this._selection = this._alts[o > 0 ? "firstChild" : "lastChild"]), this._selection && s.DomUtil.addClass(this._selection, "leaflet-control-geocoder-selected");
+ };
+ switch (n.keyCode) {
+ // Escape
+ case 27:
+ this.options.collapsed ? this._collapse() : this._clearResults();
+ break;
+ // Up
+ case 38:
+ e(-1);
+ break;
+ // Up
+ case 40:
+ e(1);
+ break;
+ // Enter
+ case 13:
+ if (this._selection) {
+ const o = parseInt(this._selection.getAttribute("data-result-index"), 10);
+ this._geocodeResultSelected(this._results[o]), this._clearResults();
+ } else
+ this._geocode();
+ break;
+ default:
+ return;
+ }
+ s.DomEvent.preventDefault(n);
+ }
+ _change() {
+ const n = this._input.value;
+ n !== this._lastGeocode && (clearTimeout(this._suggestTimeout), n.length >= this.options.suggestMinLength ? this._suggestTimeout = setTimeout(() => this._geocode(!0), this.options.suggestTimeout) : this._clearResults());
+ }
+}
+function pt(a) {
+ return new b(a);
+}
+/* @preserve
+ * Leaflet Control Geocoder
+ * https://github.com/perliedman/leaflet-control-geocoder
+ *
+ * Copyright (c) 2012 sa3m (https://github.com/sa3m)
+ * Copyright (c) 2018 Per Liedman
+ * All rights reserved.
+ */
+s.Util.extend(b, ct);
+s.Util.extend(s.Control, {
+ Geocoder: b,
+ geocoder: pt
+});
+export {
+ b as Geocoder,
+ b as default,
+ pt as geocoder,
+ ct as geocoders
+};
+//# sourceMappingURL=Control.Geocoder.modern.js.map
diff --git a/public/leaflet-control-geocoder/Control.Geocoder.modern.js.map b/public/leaflet-control-geocoder/Control.Geocoder.modern.js.map
new file mode 100644
index 0000000..4f4e39d
--- /dev/null
+++ b/public/leaflet-control-geocoder/Control.Geocoder.modern.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"Control.Geocoder.modern.js","sources":["../src/geocoders/api.ts","../src/util.ts","../src/geocoders/arcgis.ts","../src/geocoders/bing.ts","../src/geocoders/azure.ts","../src/geocoders/google.ts","../src/geocoders/here.ts","../src/geocoders/latlng.ts","../src/geocoders/mapbox.ts","../src/geocoders/mapquest.ts","../src/geocoders/neutrino.ts","../src/geocoders/nominatim.ts","../src/geocoders/open-location-code.ts","../src/geocoders/opencage.ts","../src/geocoders/pelias.ts","../src/geocoders/photon.ts","../src/geocoders/what3words.ts","../src/control.ts","../src/index.ts"],"sourcesContent":["import * as L from 'leaflet';\n\n/**\n * An object that represents a result from a geocoding query\n */\nexport interface GeocodingResult {\n /**\n * Name of found location\n */\n name: string;\n /**\n * The bounds of the location\n */\n bbox: L.LatLngBounds;\n /**\n * The center coordinate of the location\n */\n center: L.LatLng;\n /**\n * URL for icon representing result; optional\n */\n icon?: string;\n /**\n * HTML formatted representation of the name\n */\n html?: string;\n /**\n * Additional properties returned by the geocoder\n */\n properties?: any;\n}\n\n/**\n * An interface implemented to respond to geocoding queries\n */\nexport interface IGeocoder {\n /**\n * Performs a geocoding query and returns the results as promise\n * @param query the query\n */\n geocode(query: string): Promise;\n /**\n * Performs a geocoding query suggestion (this happens while typing) and returns the results as promise\n * @param query the query\n */\n suggest?(query: string): Promise;\n /**\n * Performs a reverse geocoding query and returns the results as promise\n * @param location the coordinate to reverse geocode\n * @param scale the map scale possibly used for reverse geocoding\n */\n reverse?(location: L.LatLngLiteral, scale: number): Promise;\n}\n\nexport interface GeocoderOptions {\n /**\n * URL of the service\n */\n serviceUrl: string;\n /**\n * Additional URL parameters (strings) that will be added to geocoding requests\n */\n geocodingQueryParams?: Record;\n /**\n * Additional URL parameters (strings) that will be added to reverse geocoding requests\n */\n reverseQueryParams?: Record;\n /**\n * API key to use this service\n */\n apiKey?: string;\n}\n\n/**\n * @internal\n */\nexport function geocodingParams(\n options: GeocoderOptions,\n params: Record\n): Record {\n return L.Util.extend(params, options.geocodingQueryParams);\n}\n\n/**\n * @internal\n */\nexport function reverseParams(\n options: GeocoderOptions,\n params: Record\n): Record {\n return L.Util.extend(params, options.reverseQueryParams);\n}\n","// Adapted from handlebars.js\n// https://github.com/wycats/handlebars.js/\n/**\n * @internal\n */\nconst badChars = /[&<>\"'`]/g;\n/**\n * @internal\n */\nconst possible = /[&<>\"'`]/;\n/**\n * @internal\n */\nconst escape: Record = {\n '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": ''',\n '`': '`'\n};\n\n/**\n * @internal\n */\nfunction escapeChar(chr: string) {\n return escape[chr];\n}\n\n/**\n * @internal\n */\nexport function htmlEscape(string?: string): string {\n if (string == null) {\n return '';\n } else if (!string) {\n return string + '';\n }\n\n // Force a string conversion as this will be done by the append regardless and\n // the regex test will do this transparently behind the scenes, causing issues if\n // an object's to string has escaped characters in it.\n string = '' + string;\n\n if (!possible.test(string)) {\n return string;\n }\n return string.replace(badChars, escapeChar);\n}\n\n/**\n * @internal\n */\nexport function getJSON(url: string, params: Record): Promise {\n const headers = { Accept: 'application/json' };\n const request = new URL(url);\n Object.entries(params).forEach(([key, value]) => {\n (Array.isArray(value) ? value : [value]).forEach(v => {\n request.searchParams.append(key, v);\n });\n });\n return fetch(request.toString(), { headers }).then(response => response.json());\n}\n\n/**\n * @internal\n */\nexport function template(str: string, data: Record): string {\n return str.replace(/\\{ *([\\w_]+) *\\}/g, (str, key) => {\n let value = data[key];\n if (value === undefined) {\n value = '';\n } else if (typeof value === 'function') {\n value = value(data);\n }\n return htmlEscape(value);\n });\n}\n","import * as L from 'leaflet';\nimport { getJSON } from '../util';\nimport { IGeocoder, GeocoderOptions, geocodingParams, GeocodingResult, reverseParams } from './api';\n\nexport interface ArcGisOptions extends GeocoderOptions {}\n\n\n\n/**\n * Implementation of the [ArcGIS geocoder](https://developers.arcgis.com/features/geocoding/)\n */\nexport class ArcGis implements IGeocoder {\n options: ArcGisOptions = {\n serviceUrl: 'https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer',\n apiKey: ''\n };\n\n constructor(options?: Partial) {\n L.Util.setOptions(this, options);\n }\n\n async geocode(query: string): Promise {\n const params = geocodingParams(this.options, {\n token: this.options.apiKey,\n SingleLine: query,\n outFields: 'Addr_Type',\n forStorage: false,\n maxLocations: 10,\n f: 'json'\n });\n\n const data = await getJSON(\n this.options.serviceUrl + '/findAddressCandidates',\n params\n );\n const results: GeocodingResult[] = [];\n if (data.candidates && data.candidates.length) {\n for (let i = 0; i <= data.candidates.length - 1; i++) {\n const loc = data.candidates[i];\n const latLng = L.latLng(loc.location.y, loc.location.x);\n const latLngBounds = L.latLngBounds(\n L.latLng(loc.extent.ymax, loc.extent.xmax),\n L.latLng(loc.extent.ymin, loc.extent.xmin)\n );\n results[i] = {\n name: loc.address,\n bbox: latLngBounds,\n center: latLng\n };\n }\n }\n\n return results;\n }\n\n suggest(query: string): Promise {\n return this.geocode(query);\n }\n\n async reverse(location: L.LatLngLiteral, scale: number): Promise {\n const params = reverseParams(this.options, {\n location: location.lng + ',' + location.lat,\n distance: 100,\n f: 'json'\n });\n const data = await getJSON(this.options.serviceUrl + '/reverseGeocode', params);\n const result: GeocodingResult[] = [];\n if (data && !data.error) {\n const center = L.latLng(data.location.y, data.location.x);\n const bbox = L.latLngBounds(center, center);\n result.push({\n name: data.address.Match_addr,\n center: center,\n bbox: bbox\n });\n }\n\n return result;\n }\n}\n\n/**\n * [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link ArcGis}\n * @param options the options\n */\nexport function arcgis(options?: Partial) {\n return new ArcGis(options);\n}\n\n/**\n * @internal\n */\nexport interface ArcGisResponse {\n spatialReference: {\n wkid: number;\n latestWkid: number;\n };\n candidates: Candidate[];\n}\n\ninterface Candidate {\n address: string;\n location: {\n x: number;\n y: number;\n };\n score: number;\n attributes: {\n Addr_Type: string;\n };\n extent: {\n xmin: number;\n ymin: number;\n xmax: number;\n ymax: number;\n };\n}\n\n","import * as L from 'leaflet';\nimport { getJSON } from '../util';\nimport { IGeocoder, GeocoderOptions, geocodingParams, GeocodingResult, reverseParams } from './api';\n\nexport interface BingOptions extends GeocoderOptions {}\n\n/**\n * Implementation of the [Bing Locations API](https://docs.microsoft.com/en-us/bingmaps/rest-services/locations/)\n *\n * Bing Maps for Enterprise is deprecated and will be retired.\n * Free (Basic) account customers can continue to use Bing Maps for Enterprise services until June 30th, 2025.\n * Enterprise account customers can continue to use Bing Maps for Enterprise services until June 30th, 2028.\n */\nexport class Bing implements IGeocoder {\n options: BingOptions = {\n serviceUrl: 'https://dev.virtualearth.net/REST/v1/Locations/'\n };\n\n constructor(options?: Partial) {\n L.Util.setOptions(this, options);\n }\n\n async geocode(query: string): Promise {\n const params = geocodingParams(this.options, {\n query: query,\n key: this.options.apiKey\n });\n const data = await getJSON(this.options.serviceUrl, params);\n const results: GeocodingResult[] = [];\n if (data.resourceSets.length > 0) {\n for (let i = data.resourceSets[0].resources.length - 1; i >= 0; i--) {\n const resource = data.resourceSets[0].resources[i],\n bbox = resource.bbox;\n results[i] = {\n name: resource.name,\n bbox: L.latLngBounds([bbox[0], bbox[1]], [bbox[2], bbox[3]]),\n center: L.latLng(resource.point.coordinates)\n };\n }\n }\n return results;\n }\n\n async reverse(location: L.LatLngLiteral, scale: number): Promise {\n const params = reverseParams(this.options, {\n key: this.options.apiKey\n });\n const data = await getJSON(\n this.options.serviceUrl + location.lat + ',' + location.lng,\n params\n );\n const results: GeocodingResult[] = [];\n for (let i = data.resourceSets[0].resources.length - 1; i >= 0; i--) {\n const resource = data.resourceSets[0].resources[i],\n bbox = resource.bbox;\n results[i] = {\n name: resource.name,\n bbox: L.latLngBounds([bbox[0], bbox[1]], [bbox[2], bbox[3]]),\n center: L.latLng(resource.point.coordinates)\n };\n }\n return results;\n }\n}\n\n/**\n * [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link Bing}\n * @param options the options\n */\nexport function bing(options?: Partial) {\n return new Bing(options);\n}\n","import * as L from 'leaflet';\nimport { getJSON } from '../util';\nimport { GeocodingResult, IGeocoder } from './api';\n\nexport interface AzureMapsOptions {\n apiKey: string; // Azure Maps API Key\n serviceUrl: string; // Optional: Base URL for the Azure Maps API\n}\n\n/**\n * Implementation of [Azure Maps Geocoding](https://www.microsoft.com/en-us/maps/azure/location-services/geocoding)\n *\n * https://learn.microsoft.com/en-us/rest/api/maps/search?view=rest-maps-1.0\n */\nexport class AzureMaps implements IGeocoder {\n private options: AzureMapsOptions = {\n apiKey: '',\n serviceUrl: 'https://atlas.microsoft.com/search'\n };\n\n constructor(options: Partial) {\n L.Util.setOptions(this, options);\n if (!this.options.apiKey) {\n throw new Error('Azure Maps Geocoder requires an API key.');\n }\n }\n\n /**\n * {@inheritdoc}\n * https://learn.microsoft.com/en-us/rest/api/maps/search/get-search-address?view=rest-maps-1.0&tabs=HTTP\n */\n async geocode(query: string): Promise {\n const params = {\n 'api-version': '1.0',\n query,\n 'subscription-key': this.options.apiKey\n };\n const url = this.options.serviceUrl + '/address/json';\n const data = await getJSON(url, params);\n\n const results: GeocodingResult[] = [];\n if (data.results && data.results.length > 0) {\n for (const result of data.results) {\n results.push({\n name: result.address.freeformAddress,\n bbox: L.latLngBounds(\n [result.viewport.topLeftPoint.lat, result.viewport.topLeftPoint.lon],\n [result.viewport.btmRightPoint.lat, result.viewport.btmRightPoint.lon]\n ),\n center: L.latLng(result.position.lat, result.position.lon)\n });\n }\n }\n return results;\n }\n\n /**\n * {@inheritdoc}\n * https://learn.microsoft.com/en-us/rest/api/maps/search/get-search-address-reverse?view=rest-maps-1.0&tabs=HTTP\n */\n async reverse(location: L.LatLngLiteral, scale: number): Promise {\n const params = {\n 'api-version': '1.0',\n query: location.lat + ',' + location.lng,\n 'subscription-key': this.options.apiKey\n };\n const url = this.options.serviceUrl + '/address/reverse/json';\n const data = await getJSON(url, params);\n\n const results: GeocodingResult[] = [];\n if (data.addresses && data.addresses.length > 0) {\n for (const address of data.addresses) {\n results.push({\n name: address.address.freeformAddress,\n bbox: L.latLngBounds(\n [address.viewport.topLeftPoint.lat, address.viewport.topLeftPoint.lon],\n [address.viewport.btmRightPoint.lat, address.viewport.btmRightPoint.lon]\n ),\n center: L.latLng(location.lat, location.lng)\n });\n }\n }\n return results;\n }\n}\n\n/**\n * [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link Azure}\n * @param options the options\n */\nexport function azure(options: AzureMapsOptions) {\n return new AzureMaps(options);\n}\n\n/**\n * @internal\n */\nexport interface AzureMapsResponse {\n summary: Summary;\n results: Result[];\n}\n\ninterface Result {\n type: string;\n id: string;\n score: number;\n address: Address;\n position: Position;\n viewport: Viewport;\n entryPoints: EntryPoint[];\n}\n\ninterface Address {\n streetNumber: string;\n streetName: string;\n municipalitySubdivision: string;\n municipality: string;\n countrySecondarySubdivision: string;\n countryTertiarySubdivision: string;\n countrySubdivisionCode: string;\n postalCode: string;\n extendedPostalCode: string;\n countryCode: string;\n country: string;\n countryCodeISO3: string;\n freeformAddress: string;\n countrySubdivisionName: string;\n}\n\ninterface EntryPoint {\n type: string;\n position: Position;\n}\n\ninterface Position {\n lat: number;\n lon: number;\n}\n\ninterface Viewport {\n topLeftPoint: Position;\n btmRightPoint: Position;\n}\n\ninterface Summary {\n query: string;\n queryType: string;\n queryTime: number;\n numResults: number;\n offset: number;\n totalResults: number;\n fuzzyLevel: number;\n}\n","import * as L from 'leaflet';\nimport { getJSON } from '../util';\nimport { IGeocoder, GeocoderOptions, geocodingParams, GeocodingResult, reverseParams } from './api';\n\n/**\n * Implementation of the [Google Geocoding API](https://developers.google.com/maps/documentation/geocoding/)\n */\nexport interface GoogleOptions extends GeocoderOptions {}\n\nexport class Google implements IGeocoder {\n options: GoogleOptions = {\n serviceUrl: 'https://maps.googleapis.com/maps/api/geocode/json'\n };\n\n constructor(options?: Partial) {\n L.Util.setOptions(this, options);\n }\n\n async geocode(query: string): Promise {\n const params = geocodingParams(this.options, {\n key: this.options.apiKey,\n address: query\n });\n const data = await getJSON(this.options.serviceUrl, params);\n const results: GeocodingResult[] = [];\n if (data.results && data.results.length) {\n for (let i = 0; i <= data.results.length - 1; i++) {\n const loc = data.results[i];\n const latLng = L.latLng(loc.geometry.location);\n const latLngBounds = L.latLngBounds(\n L.latLng(loc.geometry.viewport.northeast),\n L.latLng(loc.geometry.viewport.southwest)\n );\n results[i] = {\n name: loc.formatted_address,\n bbox: latLngBounds,\n center: latLng,\n properties: loc.address_components\n };\n }\n }\n\n return results;\n }\n\n async reverse(location: L.LatLngLiteral, scale: number): Promise {\n const params = reverseParams(this.options, {\n key: this.options.apiKey,\n latlng: location.lat + ',' + location.lng\n });\n const data = await getJSON(this.options.serviceUrl, params);\n const results: GeocodingResult[] = [];\n if (data.results && data.results.length) {\n for (let i = 0; i <= data.results.length - 1; i++) {\n const loc = data.results[i];\n const center = L.latLng(loc.geometry.location);\n const bbox = L.latLngBounds(\n L.latLng(loc.geometry.viewport.northeast),\n L.latLng(loc.geometry.viewport.southwest)\n );\n results[i] = {\n name: loc.formatted_address,\n bbox: bbox,\n center: center,\n properties: loc.address_components\n };\n }\n }\n\n return results;\n }\n}\n\n/**\n * [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link Google}\n * @param options the options\n */\nexport function google(options?: Partial) {\n return new Google(options);\n}\n\n/**\n * @internal\n */\nexport interface GoogleResponse {\n results: Result[];\n status: string;\n}\n\ninterface Result {\n address_components: AddressComponent[];\n formatted_address: string;\n geometry: Geometry;\n place_id: string;\n types: string[];\n}\n\ninterface AddressComponent {\n long_name: string;\n short_name: string;\n types: string[];\n}\n\ninterface Geometry {\n bounds: Bounds;\n location: Location;\n location_type: string;\n viewport: Bounds;\n}\n\ninterface Bounds {\n northeast: Location;\n southwest: Location;\n}\n\ninterface Location {\n lat: number;\n lng: number;\n}\n","import * as L from 'leaflet';\nimport { getJSON } from '../util';\nimport { IGeocoder, GeocoderOptions, geocodingParams, GeocodingResult, reverseParams } from './api';\n\nexport interface HereOptions extends GeocoderOptions {\n /**\n * Use `apiKey` and the new `HEREv2` geocoder\n * @deprecated\n */\n app_id: string;\n /**\n * Use `apiKey` and the new `HEREv2` geocoder\n * @deprecated\n */\n app_code: string;\n reverseGeocodeProxRadius?: any;\n apiKey: string;\n maxResults: number;\n}\n\n/**\n * Implementation of the [HERE Geocoder API](https://developer.here.com/documentation/geocoder/topics/introduction.html)\n */\nexport class HERE implements IGeocoder {\n options: HereOptions = {\n serviceUrl: 'https://geocoder.api.here.com/6.2/',\n app_id: '',\n app_code: '',\n apiKey: '',\n maxResults: 5\n };\n\n constructor(options?: Partial) {\n L.Util.setOptions(this, options);\n if (options?.apiKey) throw Error('apiKey is not supported, use app_id/app_code instead!');\n }\n\n geocode(query: string): Promise {\n const params = geocodingParams(this.options, {\n searchtext: query,\n gen: 9,\n app_id: this.options.app_id,\n app_code: this.options.app_code,\n jsonattributes: 1,\n maxresults: this.options.maxResults\n });\n return this.getJSON(this.options.serviceUrl + 'geocode.json', params);\n }\n\n reverse(location: L.LatLngLiteral, scale: number): Promise {\n let prox = location.lat + ',' + location.lng;\n if (this.options.reverseGeocodeProxRadius) {\n prox += ',' + this.options.reverseGeocodeProxRadius;\n }\n const params = reverseParams(this.options, {\n prox,\n mode: 'retrieveAddresses',\n app_id: this.options.app_id,\n app_code: this.options.app_code,\n gen: 9,\n jsonattributes: 1,\n maxresults: this.options.maxResults\n });\n return this.getJSON(this.options.serviceUrl + 'reversegeocode.json', params);\n }\n\n async getJSON(url: string, params: any): Promise {\n const data = await getJSON(url, params);\n const results: GeocodingResult[] = [];\n\n if (data.response.view && data.response.view.length) {\n for (let i = 0; i <= data.response.view[0].result.length - 1; i++) {\n const loc = data.response.view[0].result[i].location;\n const center = L.latLng(loc.displayPosition.latitude, loc.displayPosition.longitude);\n const bbox = L.latLngBounds(\n L.latLng(loc.mapView.topLeft.latitude, loc.mapView.topLeft.longitude),\n L.latLng(loc.mapView.bottomRight.latitude, loc.mapView.bottomRight.longitude)\n );\n results[i] = {\n name: loc.address.label,\n properties: loc.address,\n bbox: bbox,\n center: center\n };\n }\n }\n return results;\n }\n}\n\n/**\n * Implementation of the new [HERE Geocoder API](https://developer.here.com/documentation/geocoding-search-api/api-reference-swagger.html)\n */\nexport class HEREv2 implements IGeocoder {\n options: HereOptions = {\n serviceUrl: 'https://geocode.search.hereapi.com/v1',\n apiKey: '',\n app_id: '',\n app_code: '',\n maxResults: 10\n };\n\n constructor(options?: Partial) {\n L.Util.setOptions(this, options);\n }\n\n geocode(query: string): Promise {\n const params = geocodingParams(this.options, {\n q: query,\n apiKey: this.options.apiKey,\n limit: this.options.maxResults\n });\n\n if (!params.at && !params.in) {\n throw Error(\n 'at / in parameters not found. Please define coordinates (at=latitude,longitude) or other (in) in your geocodingQueryParams.'\n );\n }\n\n return this.getJSON(this.options.serviceUrl + '/discover', params);\n }\n\n reverse(location: L.LatLngLiteral, scale: number): Promise {\n const params = reverseParams(this.options, {\n at: location.lat + ',' + location.lng,\n limit: this.options.reverseGeocodeProxRadius,\n apiKey: this.options.apiKey\n });\n return this.getJSON(this.options.serviceUrl + '/revgeocode', params);\n }\n\n async getJSON(url: string, params: any): Promise {\n const data = await getJSON(url, params);\n const results: GeocodingResult[] = [];\n\n if (data.items && data.items.length) {\n for (let i = 0; i <= data.items.length - 1; i++) {\n const item = data.items[i];\n const latLng = L.latLng(item.position.lat, item.position.lng);\n let bbox: L.LatLngBounds;\n if (item.mapView) {\n bbox = L.latLngBounds(\n L.latLng(item.mapView.south, item.mapView.west),\n L.latLng(item.mapView.north, item.mapView.east)\n );\n } else {\n // Using only position when not provided\n bbox = L.latLngBounds(\n L.latLng(item.position.lat, item.position.lng),\n L.latLng(item.position.lat, item.position.lng)\n );\n }\n results[i] = {\n name: item.address.label,\n properties: item.address,\n bbox: bbox,\n center: latLng\n };\n }\n }\n return results;\n }\n}\n\n/**\n * [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link HERE}\n * @param options the options\n */\nexport function here(options?: Partial) {\n if (options?.apiKey) {\n return new HEREv2(options);\n } else {\n return new HERE(options);\n }\n}\n\n/**\n * @internal\n */\nexport interface HEREv2Response {\n items: Item[];\n}\n\ninterface Item {\n title: string;\n id: string;\n ontologyId: string;\n resultType: string;\n address: Address;\n mapView?: MapView;\n position: Position;\n access: Position[];\n distance: number;\n categories: Category[];\n references: Reference[];\n foodTypes: Category[];\n contacts: Contact[];\n openingHours: OpeningHour[];\n}\n\ninterface MapView {\n east: number;\n north: number;\n south: number;\n west: number;\n}\n\ninterface Position {\n lat: number;\n lng: number;\n}\n\ninterface Address {\n label: string;\n countryCode: string;\n countryName: string;\n stateCode: string;\n state: string;\n county: string;\n city: string;\n district: string;\n street: string;\n postalCode: string;\n houseNumber: string;\n}\n\ninterface Category {\n id: string;\n name: string;\n primary?: boolean;\n}\n\ninterface Contact {\n phone: Email[];\n fax: Email[];\n www: Email[];\n email: Email[];\n}\n\ninterface Email {\n value: string;\n}\n\ninterface OpeningHour {\n text: string[];\n isOpen: boolean;\n structured: Structured[];\n}\n\ninterface Structured {\n start: string;\n duration: string;\n recurrence: string;\n}\n\ninterface Reference {\n supplier: Supplier;\n id: string;\n}\n\ninterface Supplier {\n id: string;\n}\n","import * as L from 'leaflet';\nimport { IGeocoder, GeocodingResult } from './api';\n\nexport interface LatLngOptions {\n /**\n * The next geocoder to use for non-supported queries\n */\n next?: IGeocoder;\n /**\n * The size in meters used for passing to `LatLng.toBounds`\n */\n sizeInMeters: number;\n}\n\n/**\n * Parses basic latitude/longitude strings such as `'50.06773 14.37742'`, `'N50.06773 W14.37742'`, `'S 50° 04.064 E 014° 22.645'`, or `'S 50° 4′ 03.828″, W 14° 22′ 38.712″'`\n * @param query the latitude/longitude string to parse\n * @returns the parsed latitude/longitude\n */\nexport function parseLatLng(query: string): L.LatLng | undefined {\n let match;\n // regex from https://github.com/openstreetmap/openstreetmap-website/blob/master/app/controllers/geocoder_controller.rb\n if ((match = query.match(/^([NS])\\s*(\\d{1,3}(?:\\.\\d*)?)\\W*([EW])\\s*(\\d{1,3}(?:\\.\\d*)?)$/))) {\n // [NSEW] decimal degrees\n return L.latLng(\n (/N/i.test(match[1]) ? 1 : -1) * +match[2],\n (/E/i.test(match[3]) ? 1 : -1) * +match[4]\n );\n } else if (\n (match = query.match(/^(\\d{1,3}(?:\\.\\d*)?)\\s*([NS])\\W*(\\d{1,3}(?:\\.\\d*)?)\\s*([EW])$/))\n ) {\n // decimal degrees [NSEW]\n return L.latLng(\n (/N/i.test(match[2]) ? 1 : -1) * +match[1],\n (/E/i.test(match[4]) ? 1 : -1) * +match[3]\n );\n } else if (\n (match = query.match(\n /^([NS])\\s*(\\d{1,3})°?\\s*(\\d{1,3}(?:\\.\\d*)?)?['′]?\\W*([EW])\\s*(\\d{1,3})°?\\s*(\\d{1,3}(?:\\.\\d*)?)?['′]?$/\n ))\n ) {\n // [NSEW] degrees, decimal minutes\n return L.latLng(\n (/N/i.test(match[1]) ? 1 : -1) * (+match[2] + +match[3] / 60),\n (/E/i.test(match[4]) ? 1 : -1) * (+match[5] + +match[6] / 60)\n );\n } else if (\n (match = query.match(\n /^(\\d{1,3})°?\\s*(\\d{1,3}(?:\\.\\d*)?)?['′]?\\s*([NS])\\W*(\\d{1,3})°?\\s*(\\d{1,3}(?:\\.\\d*)?)?['′]?\\s*([EW])$/\n ))\n ) {\n // degrees, decimal minutes [NSEW]\n return L.latLng(\n (/N/i.test(match[3]) ? 1 : -1) * (+match[1] + +match[2] / 60),\n (/E/i.test(match[6]) ? 1 : -1) * (+match[4] + +match[5] / 60)\n );\n } else if (\n (match = query.match(\n /^([NS])\\s*(\\d{1,3})°?\\s*(\\d{1,2})['′]?\\s*(\\d{1,3}(?:\\.\\d*)?)?[\"″]?\\W*([EW])\\s*(\\d{1,3})°?\\s*(\\d{1,2})['′]?\\s*(\\d{1,3}(?:\\.\\d*)?)?[\"″]?$/\n ))\n ) {\n // [NSEW] degrees, minutes, decimal seconds\n return L.latLng(\n (/N/i.test(match[1]) ? 1 : -1) * (+match[2] + +match[3] / 60 + +match[4] / 3600),\n (/E/i.test(match[5]) ? 1 : -1) * (+match[6] + +match[7] / 60 + +match[8] / 3600)\n );\n } else if (\n (match = query.match(\n /^(\\d{1,3})°?\\s*(\\d{1,2})['′]?\\s*(\\d{1,3}(?:\\.\\d*)?)?[\"″]\\s*([NS])\\W*(\\d{1,3})°?\\s*(\\d{1,2})['′]?\\s*(\\d{1,3}(?:\\.\\d*)?)?[\"″]?\\s*([EW])$/\n ))\n ) {\n // degrees, minutes, decimal seconds [NSEW]\n return L.latLng(\n (/N/i.test(match[4]) ? 1 : -1) * (+match[1] + +match[2] / 60 + +match[3] / 3600),\n (/E/i.test(match[8]) ? 1 : -1) * (+match[5] + +match[6] / 60 + +match[7] / 3600)\n );\n } else if ((match = query.match(/^\\s*([+-]?\\d+(?:\\.\\d*)?)\\s*[\\s,]\\s*([+-]?\\d+(?:\\.\\d*)?)\\s*$/))) {\n return L.latLng(+match[1], +match[2]);\n }\n}\n\n/**\n * Parses basic latitude/longitude strings such as `'50.06773 14.37742'`, `'N50.06773 W14.37742'`, `'S 50° 04.064 E 014° 22.645'`, or `'S 50° 4′ 03.828″, W 14° 22′ 38.712″'`\n */\nexport class LatLng implements IGeocoder {\n options: LatLngOptions = {\n next: undefined,\n sizeInMeters: 10000\n };\n\n constructor(options?: Partial) {\n L.Util.setOptions(this, options);\n }\n\n async geocode(query: string) {\n const center = parseLatLng(query);\n if (center) {\n const results: GeocodingResult[] = [\n {\n name: query,\n center: center,\n bbox: center.toBounds(this.options.sizeInMeters)\n }\n ];\n return results;\n } else if (this.options.next) {\n return this.options.next.geocode(query);\n } else {\n return [];\n }\n }\n}\n\n/**\n * [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link LatLng}\n * @param options the options\n */\nexport function latLng(options?: Partial) {\n return new LatLng(options);\n}\n","import * as L from 'leaflet';\nimport { getJSON } from '../util';\nimport { IGeocoder, GeocoderOptions, geocodingParams, GeocodingResult, reverseParams } from './api';\n\nexport interface MapboxOptions extends GeocoderOptions {}\n\n/**\n * Implementation of the [Mapbox Geocoding](https://www.mapbox.com/api-documentation/#geocoding)\n */\nexport class Mapbox implements IGeocoder {\n options: MapboxOptions = {\n serviceUrl: 'https://api.mapbox.com/geocoding/v5/mapbox.places/'\n };\n\n constructor(options?: Partial) {\n L.Util.setOptions(this, options);\n }\n\n _getProperties(loc) {\n const properties = {\n text: loc.text,\n address: loc.address\n };\n\n for (let j = 0; j < (loc.context || []).length; j++) {\n const id = loc.context[j].id.split('.')[0];\n properties[id] = loc.context[j].text;\n\n // Get country code when available\n if (loc.context[j].short_code) {\n properties['countryShortCode'] = loc.context[j].short_code;\n }\n }\n return properties;\n }\n\n async geocode(query: string): Promise {\n const url = this.options.serviceUrl + encodeURIComponent(query) + '.json';\n const params: any = geocodingParams(this.options, {\n access_token: this.options.apiKey\n });\n if (\n params.proximity !== undefined &&\n params.proximity.lat !== undefined &&\n params.proximity.lng !== undefined\n ) {\n params.proximity = params.proximity.lng + ',' + params.proximity.lat;\n }\n const data = await getJSON(url, params);\n return this._parseResults(data);\n }\n\n suggest(query: string): Promise {\n return this.geocode(query);\n }\n\n async reverse(location: L.LatLngLiteral, scale: number): Promise {\n const url = this.options.serviceUrl + location.lng + ',' + location.lat + '.json';\n const param = reverseParams(this.options, {\n access_token: this.options.apiKey\n });\n const data = await getJSON(url, param);\n return this._parseResults(data);\n }\n\n private _parseResults(data: MapboxResponse): any[] | GeocodingResult[] {\n if (!data.features?.length) {\n return [];\n }\n const results: GeocodingResult[] = [];\n for (let i = 0; i <= data.features.length - 1; i++) {\n const loc = data.features[i];\n const center = L.latLng(loc.center.reverse() as [number, number]);\n let bbox: L.LatLngBounds;\n if (loc.bbox) {\n bbox = L.latLngBounds(\n L.latLng(loc.bbox.slice(0, 2).reverse() as [number, number]),\n L.latLng(loc.bbox.slice(2, 4).reverse() as [number, number])\n );\n } else {\n bbox = L.latLngBounds(center, center);\n }\n results[i] = {\n name: loc.place_name,\n bbox: bbox,\n center: center,\n properties: this._getProperties(loc)\n };\n }\n\n return results;\n }\n}\n\n/**\n * [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link Mapbox}\n * @param options the options\n */\nexport function mapbox(options?: Partial) {\n return new Mapbox(options);\n}\n\n/**\n * @internal\n */\nexport interface MapboxResponse {\n type: string;\n query: string[];\n features: Feature[];\n attribution: string;\n}\n\ninterface Feature {\n id: string;\n type: string;\n place_type: string[];\n relevance: number;\n properties: Properties;\n text: string;\n place_name: string;\n matching_text: string;\n matching_place_name: string;\n center: [number, number];\n bbox?: [number, number, number, number];\n geometry: Geometry;\n address: string;\n context: Context[];\n}\n\ninterface Context {\n id: string;\n text: string;\n wikidata?: string;\n short_code?: string;\n}\n\ninterface Geometry {\n type: string;\n coordinates: number[];\n interpolated: boolean;\n omitted: boolean;\n}\n\ninterface Properties {}\n","import * as L from 'leaflet';\nimport { getJSON } from '../util';\nimport { IGeocoder, GeocoderOptions, geocodingParams, GeocodingResult, reverseParams } from './api';\n\nexport interface MapQuestOptions extends GeocoderOptions {}\n\n/**\n * Implementation of the [MapQuest Geocoding API](http://developer.mapquest.com/web/products/dev-services/geocoding-ws)\n */\nexport class MapQuest implements IGeocoder {\n options: MapQuestOptions = {\n serviceUrl: 'https://www.mapquestapi.com/geocoding/v1'\n };\n\n constructor(options?: Partial) {\n L.Util.setOptions(this, options);\n // MapQuest seems to provide URI encoded API keys,\n // so to avoid encoding them twice, we decode them here\n this.options.apiKey = decodeURIComponent(this.options.apiKey!);\n }\n\n _formatName(...parts: string[]) {\n return parts.filter(s => !!s).join(', ');\n }\n\n async geocode(query: string): Promise {\n const params = geocodingParams(this.options, {\n key: this.options.apiKey,\n location: query,\n limit: 5,\n outFormat: 'json'\n });\n const data = await getJSON(this.options.serviceUrl + '/address', params);\n return this._parseResults(data);\n }\n\n async reverse(location: L.LatLngLiteral, scale: number): Promise {\n const params = reverseParams(this.options, {\n key: this.options.apiKey,\n location: location.lat + ',' + location.lng,\n outputFormat: 'json'\n });\n const data = await getJSON(this.options.serviceUrl + '/reverse', params);\n return this._parseResults(data);\n }\n\n private _parseResults(data): GeocodingResult[] {\n const results: GeocodingResult[] = [];\n if (data.results && data.results[0].locations) {\n for (let i = data.results[0].locations.length - 1; i >= 0; i--) {\n const loc = data.results[0].locations[i];\n const center = L.latLng(loc.latLng);\n results[i] = {\n name: this._formatName(loc.street, loc.adminArea4, loc.adminArea3, loc.adminArea1),\n bbox: L.latLngBounds(center, center),\n center: center\n };\n }\n }\n return results;\n }\n}\n\n/**\n * [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link MapQuest}\n * @param options the options\n */\nexport function mapQuest(options?: Partial) {\n return new MapQuest(options);\n}\n","import * as L from 'leaflet';\nimport { getJSON } from '../util';\nimport { IGeocoder, GeocoderOptions, geocodingParams, GeocodingResult, reverseParams } from './api';\n\nexport interface NeutrinoOptions extends GeocoderOptions {\n userId: string;\n}\n\n/**\n * Implementation of the [Neutrino API](https://www.neutrinoapi.com/api/geocode-address/)\n */\nexport class Neutrino implements IGeocoder {\n options: NeutrinoOptions = {\n userId: '',\n apiKey: '',\n serviceUrl: 'https://neutrinoapi.com/'\n };\n\n constructor(options?: Partial) {\n L.Util.setOptions(this, options);\n }\n\n // https://www.neutrinoapi.com/api/geocode-address/\n async geocode(query: string): Promise {\n const params = geocodingParams(this.options, {\n apiKey: this.options.apiKey,\n userId: this.options.userId,\n //get three words and make a dot based string\n address: query.split(/\\s+/).join('.')\n });\n const data = await getJSON(this.options.serviceUrl + 'geocode-address', params);\n const results: GeocodingResult[] = [];\n if (data.locations) {\n data.geometry = data.locations[0];\n const center = L.latLng(data.geometry['latitude'], data.geometry['longitude']);\n const bbox = L.latLngBounds(center, center);\n results[0] = {\n name: data.geometry.address,\n bbox: bbox,\n center: center\n };\n }\n\n return results;\n }\n\n suggest(query: string): Promise {\n return this.geocode(query);\n }\n\n // https://www.neutrinoapi.com/api/geocode-reverse/\n async reverse(location: L.LatLngLiteral, scale: number): Promise {\n const params = reverseParams(this.options, {\n apiKey: this.options.apiKey,\n userId: this.options.userId,\n latitude: location.lat,\n longitude: location.lng\n });\n const data = await getJSON(this.options.serviceUrl + 'geocode-reverse', params);\n const results: GeocodingResult[] = [];\n if (data.status.status == 200 && data.found) {\n const center = L.latLng(location.lat, location.lng);\n const bbox = L.latLngBounds(center, center);\n results[0] = {\n name: data.address,\n bbox: bbox,\n center: center\n };\n }\n return results;\n }\n}\n\n/**\n * [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link Neutrino}\n * @param options the options\n */\nexport function neutrino(options?: Partial) {\n return new Neutrino(options);\n}\n","import * as L from 'leaflet';\nimport { template, getJSON } from '../util';\nimport { IGeocoder, GeocoderOptions, geocodingParams, GeocodingResult, reverseParams } from './api';\n\nexport type NominatimResponse = NominatimResult[];\n\nexport interface NominatimResult {\n place_id: number;\n licence: string;\n osm_type: string;\n osm_id: number;\n boundingbox: string[];\n lat: string;\n lon: string;\n display_name: string;\n class?: string;\n type?: string;\n importance?: number;\n icon?: string;\n address: NominatimAddress;\n}\n\nexport interface NominatimAddress {\n building?: string;\n city_district?: string;\n city?: string;\n country_code?: string;\n country?: string;\n county?: string;\n hamlet?: string;\n house_number?: string;\n neighbourhood?: string;\n postcode?: string;\n road?: string;\n state_district?: string;\n state?: string;\n suburb?: string;\n village?: string;\n}\n\nexport interface NominatimOptions extends GeocoderOptions {\n /**\n * Additional URL parameters (strings) that will be added to geocoding requests; can be used to restrict results to a specific country for example, by providing the [`countrycodes`](https://wiki.openstreetmap.org/wiki/Nominatim#Parameters) parameter to Nominatim\n */\n geocodingQueryParams?: Record;\n /**\n * A function that takes an GeocodingResult as argument and returns an HTML formatted string that represents the result. Default function breaks up address in parts from most to least specific, in attempt to increase readability compared to Nominatim's naming\n */\n htmlTemplate: (r: NominatimResult) => string;\n}\n\n/**\n * Implementation of the [Nominatim](https://wiki.openstreetmap.org/wiki/Nominatim) geocoder.\n *\n * This is the default geocoding service used by the control, unless otherwise specified in the options.\n *\n * Unless using your own Nominatim installation, please refer to the [Nominatim usage policy](https://operations.osmfoundation.org/policies/nominatim/).\n */\nexport class Nominatim implements IGeocoder {\n options: NominatimOptions = {\n serviceUrl: 'https://nominatim.openstreetmap.org/',\n htmlTemplate(r: NominatimResult) {\n const address = r.address;\n let className: string;\n const parts: string[] = [];\n if (address.road || address.building) {\n parts.push('{building} {road} {house_number}');\n }\n\n if (address.city || (address as any).town || address.village || address.hamlet) {\n className = parts.length > 0 ? 'leaflet-control-geocoder-address-detail' : '';\n parts.push(\n '{postcode} {city} {town} {village} {hamlet}'\n );\n }\n\n if (address.state || address.country) {\n className = parts.length > 0 ? 'leaflet-control-geocoder-address-context' : '';\n parts.push('{state} {country}');\n }\n\n return template(parts.join('
'), address);\n }\n };\n\n constructor(options?: Partial) {\n L.Util.setOptions(this, options || {});\n }\n\n async geocode(query: string) {\n const params = geocodingParams(this.options, {\n q: query,\n limit: 5,\n format: 'json',\n addressdetails: 1\n });\n const data = await getJSON(this.options.serviceUrl + 'search', params);\n const results: GeocodingResult[] = [];\n for (let i = data.length - 1; i >= 0; i--) {\n const bbox = data[i].boundingbox;\n results[i] = {\n icon: data[i].icon,\n name: data[i].display_name,\n html: this.options.htmlTemplate ? this.options.htmlTemplate(data[i]) : undefined,\n bbox: L.latLngBounds([+bbox[0], +bbox[2]], [+bbox[1], +bbox[3]]),\n center: L.latLng(+data[i].lat, +data[i].lon),\n properties: data[i]\n };\n }\n return results;\n }\n\n async reverse(location: L.LatLngLiteral, scale: number) {\n const params = reverseParams(this.options, {\n lat: location.lat,\n lon: location.lng,\n zoom: Math.round(Math.log(scale / 256) / Math.log(2)),\n addressdetails: 1,\n format: 'json'\n });\n const data = await getJSON(this.options.serviceUrl + 'reverse', params);\n const results: GeocodingResult[] = [];\n if (data && data.lat && data.lon) {\n const center = L.latLng(+data.lat, +data.lon);\n const bbox = L.latLngBounds(center, center);\n results.push({\n name: data.display_name,\n html: this.options.htmlTemplate ? this.options.htmlTemplate(data) : undefined,\n center: center,\n bbox: bbox,\n properties: data\n });\n }\n return results;\n }\n}\n\n/**\n * [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link Nominatim}\n * @param options the options\n */\nexport function nominatim(options?: Partial) {\n return new Nominatim(options);\n}\n","import * as L from 'leaflet';\nimport { IGeocoder, GeocodingResult } from './api';\n\nexport interface OpenLocationCodeOptions {\n OpenLocationCode: OpenLocationCodeApi;\n codeLength?: number;\n}\n\nexport interface OpenLocationCodeApi {\n encode(latitude: number, longitude: number, codeLength?: number): string;\n decode(code: string): CodeArea;\n}\n\nexport interface CodeArea {\n latitudeLo: number;\n longitudeLo: number;\n latitudeHi: number;\n longitudeHi: number;\n latitudeCenter: number;\n longitudeCenter: number;\n codeLength: number;\n}\n\n/**\n * Implementation of the [Plus codes](https://plus.codes/) (formerly OpenLocationCode) (requires [open-location-code](https://www.npmjs.com/package/open-location-code))\n */\nexport class OpenLocationCode implements IGeocoder {\n options = {} as OpenLocationCodeOptions;\n constructor(options?: Partial) {\n L.Util.setOptions(this, options);\n }\n\n async geocode(query: string) {\n try {\n const decoded = this.options.OpenLocationCode.decode(query);\n const result: GeocodingResult = {\n name: query,\n center: L.latLng(decoded.latitudeCenter, decoded.longitudeCenter),\n bbox: L.latLngBounds(\n L.latLng(decoded.latitudeLo, decoded.longitudeLo),\n L.latLng(decoded.latitudeHi, decoded.longitudeHi)\n )\n };\n return [result];\n } catch (e) {\n console.warn(e); // eslint-disable-line no-console\n return [];\n }\n }\n async reverse(location: L.LatLngLiteral, scale: number) {\n try {\n const code = this.options.OpenLocationCode.encode(\n location.lat,\n location.lng,\n this.options.codeLength\n );\n const result = {\n name: code,\n center: L.latLng(location.lat, location.lng),\n bbox: L.latLngBounds(\n L.latLng(location.lat, location.lng),\n L.latLng(location.lat, location.lng)\n )\n };\n return [result];\n } catch (e) {\n console.warn(e); // eslint-disable-line no-console\n return [];\n }\n }\n}\n\n/**\n * [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link OpenLocationCode}\n * @param options the options\n */\nexport function openLocationCode(options?: Partial) {\n return new OpenLocationCode(options);\n}\n","import * as L from 'leaflet';\nimport { getJSON } from '../util';\nimport { IGeocoder, GeocoderOptions, geocodingParams, GeocodingResult, reverseParams } from './api';\n\nexport interface OpenCageOptions extends GeocoderOptions {}\n\n/**\n * Implementation of the [OpenCage Data API](https://opencagedata.com/)\n */\nexport class OpenCage implements IGeocoder {\n options: OpenCageOptions = {\n serviceUrl: 'https://api.opencagedata.com/geocode/v1/json'\n };\n\n constructor(options?: Partial) {\n L.Util.setOptions(this, options);\n }\n\n async geocode(query: string): Promise {\n const params = geocodingParams(this.options, {\n key: this.options.apiKey,\n q: query\n });\n const data = await getJSON(this.options.serviceUrl, params);\n return this._parseResults(data);\n }\n\n suggest(query: string): Promise {\n return this.geocode(query);\n }\n\n async reverse(location: L.LatLngLiteral, scale: number): Promise {\n const params = reverseParams(this.options, {\n key: this.options.apiKey,\n q: [location.lat, location.lng].join(',')\n });\n const data = await getJSON(this.options.serviceUrl, params);\n return this._parseResults(data);\n }\n\n private _parseResults(data): GeocodingResult[] {\n const results: GeocodingResult[] = [];\n if (data.results && data.results.length) {\n for (let i = 0; i < data.results.length; i++) {\n const loc = data.results[i];\n const center = L.latLng(loc.geometry);\n let bbox: L.LatLngBounds;\n if (loc.annotations && loc.annotations.bounds) {\n bbox = L.latLngBounds(\n L.latLng(loc.annotations.bounds.northeast),\n L.latLng(loc.annotations.bounds.southwest)\n );\n } else {\n bbox = L.latLngBounds(center, center);\n }\n results.push({\n name: loc.formatted,\n bbox: bbox,\n center: center\n });\n }\n }\n return results;\n }\n}\n\nexport function opencage(options?: Partial) {\n return new OpenCage(options);\n}\n","import * as L from 'leaflet';\nimport { getJSON } from '../util';\nimport { IGeocoder, GeocoderOptions, geocodingParams, GeocodingResult, reverseParams } from './api';\n\nexport interface PeliasOptions extends GeocoderOptions {}\n\n/**\n * Implementation of the [Pelias](https://pelias.io/), [geocode.earth](https://geocode.earth/) geocoder (formerly Mapzen Search)\n */\nexport class Pelias implements IGeocoder {\n options: PeliasOptions = {\n serviceUrl: 'https://api.geocode.earth/v1'\n };\n\n constructor(options?: Partial) {\n L.Util.setOptions(this, options);\n }\n\n async geocode(query: string): Promise {\n const params = geocodingParams(this.options, {\n api_key: this.options.apiKey,\n text: query\n });\n const data = await getJSON(this.options.serviceUrl + '/search', params);\n return this._parseResults(data, 'bbox');\n }\n\n async suggest(query: string): Promise {\n const params = geocodingParams(this.options, {\n api_key: this.options.apiKey,\n text: query\n });\n const data = await getJSON(this.options.serviceUrl + '/autocomplete', params);\n return this._parseResults(data, 'bbox');\n }\n\n async reverse(location: L.LatLngLiteral, scale: number): Promise {\n const params = reverseParams(this.options, {\n api_key: this.options.apiKey,\n 'point.lat': location.lat,\n 'point.lon': location.lng\n });\n const data = await getJSON(this.options.serviceUrl + '/reverse', params);\n return this._parseResults(data, 'bounds');\n }\n\n _parseResults(data, bboxname): GeocodingResult[] {\n const results: GeocodingResult[] = [];\n L.geoJSON(data, {\n pointToLayer(feature, latlng) {\n return L.circleMarker(latlng);\n },\n onEachFeature(feature, layer: any) {\n const result = {} as GeocodingResult;\n let bbox;\n let center;\n\n if (layer.getBounds) {\n bbox = layer.getBounds();\n center = bbox.getCenter();\n } else if (layer.feature.bbox) {\n center = layer.getLatLng();\n bbox = L.latLngBounds(\n L.GeoJSON.coordsToLatLng(layer.feature.bbox.slice(0, 2)),\n L.GeoJSON.coordsToLatLng(layer.feature.bbox.slice(2, 4))\n );\n } else {\n center = layer.getLatLng();\n bbox = L.latLngBounds(center, center);\n }\n\n result.name = layer.feature.properties.label;\n result.center = center;\n result[bboxname] = bbox;\n result.properties = layer.feature.properties;\n results.push(result);\n }\n });\n return results;\n }\n}\n\n/**\n * [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link Pelias}\n * @param options the options\n */\nexport function pelias(options?: Partial) {\n return new Pelias(options);\n}\n\nexport const GeocodeEarth = Pelias;\nexport const geocodeEarth = pelias;\n\n/**\n * r.i.p.\n * @deprecated\n */\nexport const Mapzen = Pelias;\n/**\n * r.i.p.\n * @deprecated\n */\nexport const mapzen = pelias;\n\n/**\n * Implementation of the [Openrouteservice](https://openrouteservice.org/dev/#/api-docs/geocode) geocoder\n */\nexport class Openrouteservice extends Pelias {\n constructor(options?: Partial) {\n super(\n L.Util.extend(\n {\n serviceUrl: 'https://api.openrouteservice.org/geocode'\n },\n options\n )\n );\n }\n}\n\n/**\n * [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link Openrouteservice}\n * @param options the options\n */\nexport function openrouteservice(options?: Partial) {\n return new Openrouteservice(options);\n}\n\n/**\n * @internal\n */\nexport type PeliasResponse = GeoJSON.FeatureCollection & {\n geocoding: Geocoding;\n};\n\ninterface Properties {\n id: string;\n layer: string;\n source_id: string;\n name: string;\n confidence: number;\n match_type: string;\n accuracy: string;\n country: string;\n country_a: string;\n region: string;\n region_a: string;\n county: string;\n county_a: string;\n localadmin: string;\n locality: string;\n continent: string;\n label: string;\n}\n\ninterface Geocoding {\n version: string;\n attribution: string;\n query: Query;\n warnings: string[];\n engine: Engine;\n}\n\ninterface Engine {\n name: string;\n author: string;\n version: string;\n}\n\ninterface Query {\n}\n","import * as L from 'leaflet';\nimport { getJSON } from '../util';\nimport { IGeocoder, GeocoderOptions, geocodingParams, GeocodingResult, reverseParams } from './api';\n\nexport interface PhotonOptions extends GeocoderOptions {\n reverseUrl: string;\n nameProperties: string[];\n htmlTemplate?: (r: any) => string;\n}\n\n/**\n * Implementation of the [Photon](http://photon.komoot.de/) geocoder\n */\nexport class Photon implements IGeocoder {\n options: PhotonOptions = {\n serviceUrl: 'https://photon.komoot.io/api/',\n reverseUrl: 'https://photon.komoot.io/reverse/',\n nameProperties: ['name', 'street', 'suburb', 'hamlet', 'town', 'city', 'state', 'country']\n };\n\n constructor(options?: Partial) {\n L.Util.setOptions(this, options);\n }\n\n async geocode(query: string): Promise {\n const params = geocodingParams(this.options, { q: query });\n const data = await getJSON(this.options.serviceUrl, params);\n return this._parseResults(data);\n }\n\n suggest(query: string): Promise {\n return this.geocode(query);\n }\n\n async reverse(latLng: L.LatLngLiteral, scale: number): Promise {\n const params = reverseParams(this.options, {\n lat: latLng.lat,\n lon: latLng.lng\n });\n const data = await getJSON(this.options.reverseUrl, params);\n return this._parseResults(data);\n }\n\n _parseResults(data: GeoJSON.FeatureCollection): GeocodingResult[] {\n const results: GeocodingResult[] = [];\n\n if (data && data.features) {\n for (let i = 0; i < data.features.length; i++) {\n const f = data.features[i];\n const c = f.geometry.coordinates;\n const center = L.latLng(c[1], c[0]);\n const extent = f.properties?.extent;\n\n const bbox = extent\n ? L.latLngBounds([extent[1], extent[0]], [extent[3], extent[2]])\n : L.latLngBounds(center, center);\n\n results.push({\n name: this._decodeFeatureName(f),\n html: this.options.htmlTemplate ? this.options.htmlTemplate(f) : undefined,\n center: center,\n bbox: bbox,\n properties: f.properties\n });\n }\n }\n\n return results;\n }\n\n _decodeFeatureName(f: GeoJSON.Feature) {\n return (this.options.nameProperties || [])\n .map(p => f.properties?.[p])\n .filter(v => !!v)\n .join(', ');\n }\n}\n\n/**\n * [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link Photon}\n * @param options the options\n */\nexport function photon(options?: Partial) {\n return new Photon(options);\n}\n\n/**\n * @internal\n */\nexport type PhotonResponse = GeoJSON.FeatureCollection;\n\ninterface PhotonProperties {\n osm_id: number;\n osm_type: string;\n extent?: number[];\n country: string;\n osm_key: string;\n city: string;\n countrycode: string;\n osm_value: string;\n name: string;\n state: string;\n type: string;\n postcode?: string;\n housenumber?: string;\n street?: string;\n district?: string;\n}\n","import * as L from 'leaflet';\nimport { getJSON } from '../util';\nimport { IGeocoder, GeocoderOptions, geocodingParams, GeocodingResult, reverseParams } from './api';\n\nexport interface What3WordsOptions extends GeocoderOptions {}\n\n/**\n * Implementation of the What3Words service\n */\nexport class What3Words implements IGeocoder {\n options: What3WordsOptions = {\n serviceUrl: 'https://api.what3words.com/v2/'\n };\n\n constructor(options: Partial) {\n L.Util.setOptions(this, options);\n }\n\n async geocode(query: string): Promise {\n const data = await getJSON(\n this.options.serviceUrl + 'forward',\n geocodingParams(this.options, {\n key: this.options.apiKey,\n //get three words and make a dot based string\n addr: query.split(/\\s+/).join('.')\n })\n );\n const results: GeocodingResult[] = [];\n if (data.geometry) {\n const latLng = L.latLng(data.geometry['lat'], data.geometry['lng']);\n const latLngBounds = L.latLngBounds(latLng, latLng);\n results[0] = {\n name: data.words,\n bbox: latLngBounds,\n center: latLng\n };\n }\n return results;\n }\n\n suggest(query: string): Promise {\n return this.geocode(query);\n }\n\n async reverse(location: L.LatLngLiteral, scale: number): Promise {\n const data = await getJSON(\n this.options.serviceUrl + 'reverse',\n reverseParams(this.options, {\n key: this.options.apiKey,\n coords: [location.lat, location.lng].join(',')\n })\n );\n const results: GeocodingResult[] = [];\n if (data.status.status == 200) {\n const center = L.latLng(data.geometry['lat'], data.geometry['lng']);\n const bbox = L.latLngBounds(center, center);\n results[0] = {\n name: data.words,\n bbox: bbox,\n center: center\n };\n }\n return results;\n }\n}\n\n/**\n * [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link What3Words}\n * @param options the options\n */\nexport function what3words(options: Partial) {\n return new What3Words(options);\n}\n","import * as L from 'leaflet';\nimport { Nominatim } from './geocoders/index';\nimport { IGeocoder, GeocodingResult } from './geocoders/api';\n\nexport interface GeocoderControlOptions extends L.ControlOptions {\n /**\n * Collapse control unless hovered/clicked\n */\n collapsed: boolean;\n /**\n * How to expand a collapsed control: `touch` or `click` or `hover`\n */\n expand: 'touch' | 'click' | 'hover';\n /**\n * Placeholder text for text input\n */\n placeholder: string;\n /**\n * Message when no result found / geocoding error occurs\n */\n errorMessage: string;\n /**\n * Accessibility label for the search icon used by screen readers\n */\n iconLabel: string;\n /**\n * Object to perform the actual geocoding queries\n */\n geocoder?: IGeocoder;\n /**\n * Immediately show the unique result without prompting for alternatives\n */\n showUniqueResult: boolean;\n /**\n * Show icons for geocoding results (if available); supported by Nominatim\n */\n showResultIcons: boolean;\n /**\n * Minimum number characters before suggest functionality is used (if available from geocoder)\n */\n suggestMinLength: number;\n /**\n * Number of milliseconds after typing stopped before suggest functionality is used (if available from geocoder)\n */\n suggestTimeout: number;\n /**\n * Initial query string for text input\n */\n query: string;\n /**\n * Minimum number of characters in search text before performing a query\n */\n queryMinLength: number;\n /**\n * Whether to mark a geocoding result on the map by default\n */\n defaultMarkGeocode: boolean;\n}\n\n/**\n * Event is fired when selecting a geocode result.\n * By default, the control will center the map on it and place a marker at its location.\n * To remove the control's default handler for marking a result, set {@link GeocoderControlOptions.defaultMarkGeocode} to `false`.\n */\nexport type MarkGeocodeEvent = { geocode: GeocodingResult };\nexport type MarkGeocodeEventHandlerFn = (event: MarkGeocodeEvent) => void;\n\n/**\n * Event is fired before invoking {@link IGeocoder.geocode} (or {@link IGeocoder.suggest}).\n * The event data contains the query string as `input`.\n */\nexport type StartGeocodeEvent = { input: string };\nexport type StartGeocodeEventHandlerFn = (event: StartGeocodeEvent) => void;\n\n/**\n * Event is fired before after receiving results from {@link IGeocoder.geocode} (or {@link IGeocoder.suggest}).\n * The event data contains the query string as `input` and the geocoding `results`.\n */\nexport type FinishGeocodeEvent = { input: string; results: GeocodingResult[] };\nexport type FinishGeocodeEventHandlerFn = (event: FinishGeocodeEvent) => void;\n\ndeclare module 'leaflet' {\n interface Evented {\n on(type: 'markgeocode', fn: MarkGeocodeEventHandlerFn, context?: any): this;\n on(type: 'startgeocode', fn: StartGeocodeEventHandlerFn, context?: any): this;\n on(type: 'startsuggest', fn: StartGeocodeEventHandlerFn, context?: any): this;\n on(type: 'finishsuggest', fn: FinishGeocodeEventHandlerFn, context?: any): this;\n on(type: 'finishgeocode', fn: FinishGeocodeEventHandlerFn, context?: any): this;\n }\n}\n\n/**\n * Leaflet mixins https://leafletjs.com/reference-1.7.1.html#class-includes\n * for TypeScript https://www.typescriptlang.org/docs/handbook/mixins.html\n * @internal\n */\nclass EventedControl {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n constructor(...args: any[]) {\n // empty\n }\n}\n\n/**\n * @internal\n */\ninterface EventedControl extends L.Control, L.Evented {}\nL.Util.extend(EventedControl.prototype, L.Control.prototype);\nL.Util.extend(EventedControl.prototype, L.Evented.prototype);\n\n/**\n * This is the geocoder control. It works like any other [Leaflet control](https://leafletjs.com/reference.html#control), and is added to the map.\n */\nexport class GeocoderControl extends EventedControl {\n options: GeocoderControlOptions = {\n showUniqueResult: true,\n showResultIcons: false,\n collapsed: true,\n expand: 'touch',\n position: 'topright',\n placeholder: 'Search...',\n errorMessage: 'Nothing found.',\n iconLabel: 'Initiate a new search',\n query: '',\n queryMinLength: 1,\n suggestMinLength: 3,\n suggestTimeout: 250,\n defaultMarkGeocode: true\n };\n\n private _alts: HTMLUListElement;\n private _container: HTMLDivElement;\n private _errorElement: HTMLDivElement;\n private _geocodeMarker: L.Marker;\n private _input: HTMLInputElement;\n private _lastGeocode: string;\n private _map: L.Map;\n private _preventBlurCollapse: boolean;\n private _requestCount = 0;\n private _results: any;\n private _selection: any;\n private _suggestTimeout: any;\n\n /**\n * Instantiates a geocoder control (to be invoked using `new`)\n * @param options the options\n */\n constructor(options?: Partial) {\n super(options);\n L.Util.setOptions(this, options);\n if (!this.options.geocoder) {\n this.options.geocoder = new Nominatim();\n }\n }\n\n addThrobberClass() {\n L.DomUtil.addClass(this._container, 'leaflet-control-geocoder-throbber');\n }\n\n removeThrobberClass() {\n L.DomUtil.removeClass(this._container, 'leaflet-control-geocoder-throbber');\n }\n\n /**\n * Returns the container DOM element for the control and add listeners on relevant map events.\n * @param map the map instance\n * @see https://leafletjs.com/reference.html#control-onadd\n */\n onAdd(map: L.Map) {\n const className = 'leaflet-control-geocoder';\n const container = L.DomUtil.create('div', className + ' leaflet-bar') as HTMLDivElement;\n const icon = L.DomUtil.create('button', className + '-icon', container) as HTMLButtonElement;\n const form = L.DomUtil.create('div', className + '-form', container) as HTMLDivElement;\n\n this._map = map;\n this._container = container;\n\n icon.innerHTML = ' ';\n icon.type = 'button';\n icon.setAttribute('aria-label', this.options.iconLabel);\n\n const input = (this._input = L.DomUtil.create('input', '', form) as HTMLInputElement);\n input.type = 'search';\n input.value = this.options.query;\n input.placeholder = this.options.placeholder;\n L.DomEvent.disableClickPropagation(input);\n\n this._errorElement = L.DomUtil.create(\n 'div',\n className + '-form-no-error',\n container\n ) as HTMLDivElement;\n this._errorElement.innerHTML = this.options.errorMessage;\n\n this._alts = L.DomUtil.create(\n 'ul',\n className + '-alternatives leaflet-control-geocoder-alternatives-minimized',\n container\n ) as HTMLUListElement;\n L.DomEvent.disableClickPropagation(this._alts);\n\n L.DomEvent.addListener(input, 'keydown', this._keydown, this);\n if (this.options.geocoder?.suggest) {\n L.DomEvent.addListener(input, 'input', this._change, this);\n }\n L.DomEvent.addListener(input, 'blur', () => {\n if (this.options.collapsed && !this._preventBlurCollapse) {\n this._collapse();\n }\n this._preventBlurCollapse = false;\n });\n\n if (this.options.collapsed) {\n if (this.options.expand === 'click') {\n L.DomEvent.addListener(container, 'click', (e: Event) => {\n if ((e as MouseEvent).button === 0 && (e as MouseEvent).detail !== 2) {\n this._toggle();\n }\n });\n } else if (this.options.expand === 'touch') {\n L.DomEvent.addListener(\n container,\n L.Browser.touch ? 'touchstart mousedown' : 'mousedown',\n (e: Event) => {\n this._toggle();\n e.preventDefault(); // mobile: clicking focuses the icon, so UI expands and immediately collapses\n e.stopPropagation();\n },\n this\n );\n } else {\n L.DomEvent.addListener(container, 'mouseover', this._expand, this);\n L.DomEvent.addListener(container, 'mouseout', this._collapse, this);\n this._map.on('movestart', this._collapse, this);\n }\n } else {\n this._expand();\n if (L.Browser.touch) {\n L.DomEvent.addListener(container, 'touchstart', () => this._geocode());\n } else {\n L.DomEvent.addListener(container, 'click', () => this._geocode());\n }\n }\n\n if (this.options.defaultMarkGeocode) {\n this.on('markgeocode', this.markGeocode, this);\n }\n\n this.on('startgeocode', this.addThrobberClass, this);\n this.on('finishgeocode', this.removeThrobberClass, this);\n this.on('startsuggest', this.addThrobberClass, this);\n this.on('finishsuggest', this.removeThrobberClass, this);\n\n L.DomEvent.disableClickPropagation(container);\n\n return container;\n }\n\n /**\n * Sets the query string on the text input\n * @param string the query string\n */\n setQuery(string: string): this {\n this._input.value = string;\n return this;\n }\n\n private _geocodeResult(results: GeocodingResult[], suggest: boolean) {\n if (!suggest && this.options.showUniqueResult && results.length === 1) {\n this._geocodeResultSelected(results[0]);\n } else if (results.length > 0) {\n this._alts.innerHTML = '';\n this._results = results;\n L.DomUtil.removeClass(this._alts, 'leaflet-control-geocoder-alternatives-minimized');\n L.DomUtil.addClass(this._container, 'leaflet-control-geocoder-options-open');\n for (let i = 0; i < results.length; i++) {\n this._alts.appendChild(this._createAlt(results[i], i));\n }\n } else {\n L.DomUtil.addClass(this._container, 'leaflet-control-geocoder-options-error');\n L.DomUtil.addClass(this._errorElement, 'leaflet-control-geocoder-error');\n }\n }\n\n /**\n * Marks a geocoding result on the map\n * @param result the geocoding result\n */\n markGeocode(event: MarkGeocodeEvent) {\n const result = event.geocode;\n\n this._map.fitBounds(result.bbox);\n\n if (this._geocodeMarker) {\n this._map.removeLayer(this._geocodeMarker);\n }\n\n this._geocodeMarker = new L.Marker(result.center)\n .bindPopup(result.html || result.name)\n .addTo(this._map)\n .openPopup();\n\n return this;\n }\n\n private async _geocode(suggest: boolean = false) {\n const value = this._input.value;\n if (!suggest && value.length < this.options.queryMinLength) {\n return;\n }\n\n const requestCount = ++this._requestCount;\n this._lastGeocode = value;\n if (!suggest) {\n this._clearResults();\n }\n\n const event: StartGeocodeEvent = { input: value };\n this.fire(suggest ? 'startsuggest' : 'startgeocode', event);\n\n const results = suggest\n ? await this.options.geocoder!.suggest!(value)\n : await this.options.geocoder!.geocode(value);\n\n if (requestCount === this._requestCount) {\n const event: FinishGeocodeEvent = { input: value, results };\n this.fire(suggest ? 'finishsuggest' : 'finishgeocode', event);\n this._geocodeResult(results, suggest);\n }\n }\n\n private _geocodeResultSelected(geocode: GeocodingResult) {\n const event: MarkGeocodeEvent = { geocode };\n this.fire('markgeocode', event);\n }\n\n private _toggle() {\n if (L.DomUtil.hasClass(this._container, 'leaflet-control-geocoder-expanded')) {\n this._collapse();\n } else {\n this._expand();\n }\n }\n\n private _expand() {\n L.DomUtil.addClass(this._container, 'leaflet-control-geocoder-expanded');\n this._input.select();\n this.fire('expand');\n }\n\n private _collapse() {\n L.DomUtil.removeClass(this._container, 'leaflet-control-geocoder-expanded');\n L.DomUtil.addClass(this._alts, 'leaflet-control-geocoder-alternatives-minimized');\n L.DomUtil.removeClass(this._errorElement, 'leaflet-control-geocoder-error');\n L.DomUtil.removeClass(this._container, 'leaflet-control-geocoder-options-open');\n L.DomUtil.removeClass(this._container, 'leaflet-control-geocoder-options-error');\n this._input.blur(); // mobile: keyboard shouldn't stay expanded\n this.fire('collapse');\n }\n\n private _clearResults() {\n L.DomUtil.addClass(this._alts, 'leaflet-control-geocoder-alternatives-minimized');\n this._selection = null;\n L.DomUtil.removeClass(this._errorElement, 'leaflet-control-geocoder-error');\n L.DomUtil.removeClass(this._container, 'leaflet-control-geocoder-options-open');\n L.DomUtil.removeClass(this._container, 'leaflet-control-geocoder-options-error');\n }\n\n private _createAlt(result: GeocodingResult, index: number) {\n const li = L.DomUtil.create('li', ''),\n a = L.DomUtil.create('a', '', li),\n icon =\n this.options.showResultIcons && result.icon\n ? (L.DomUtil.create('img', '', a) as HTMLImageElement)\n : null,\n text = result.html ? undefined : document.createTextNode(result.name),\n mouseDownHandler = (e: Event) => {\n // In some browsers, a click will fire on the map if the control is\n // collapsed directly after mousedown. To work around this, we\n // wait until the click is completed, and _then_ collapse the\n // control. Messy, but this is the workaround I could come up with\n // for #142.\n this._preventBlurCollapse = true;\n L.DomEvent.stop(e);\n this._geocodeResultSelected(result);\n L.DomEvent.on(li, 'click touchend', () => {\n if (this.options.collapsed) {\n this._collapse();\n } else {\n this._clearResults();\n }\n });\n };\n\n if (icon) {\n icon.src = result.icon!;\n }\n\n li.setAttribute('data-result-index', String(index));\n\n if (result.html) {\n a.innerHTML = a.innerHTML + result.html;\n } else if (text) {\n a.appendChild(text);\n }\n\n // Use mousedown and not click, since click will fire _after_ blur,\n // causing the control to have collapsed and removed the items\n // before the click can fire.\n L.DomEvent.addListener(li, 'mousedown touchstart', mouseDownHandler, this);\n\n return li;\n }\n\n private _keydown(e: KeyboardEvent) {\n const select = (dir: number) => {\n if (this._selection) {\n L.DomUtil.removeClass(this._selection, 'leaflet-control-geocoder-selected');\n this._selection = this._selection[dir > 0 ? 'nextSibling' : 'previousSibling'];\n }\n if (!this._selection) {\n this._selection = this._alts[dir > 0 ? 'firstChild' : 'lastChild'];\n }\n\n if (this._selection) {\n L.DomUtil.addClass(this._selection, 'leaflet-control-geocoder-selected');\n }\n };\n\n switch (e.keyCode) {\n // Escape\n case 27:\n if (this.options.collapsed) {\n this._collapse();\n } else {\n this._clearResults();\n }\n break;\n // Up\n case 38:\n select(-1);\n break;\n // Up\n case 40:\n select(1);\n break;\n // Enter\n case 13:\n if (this._selection) {\n const index = parseInt(this._selection.getAttribute('data-result-index'), 10);\n this._geocodeResultSelected(this._results[index]);\n this._clearResults();\n } else {\n this._geocode();\n }\n break;\n default:\n return;\n }\n\n L.DomEvent.preventDefault(e);\n }\n\n private _change() {\n const v = this._input.value;\n if (v !== this._lastGeocode) {\n clearTimeout(this._suggestTimeout);\n if (v.length >= this.options.suggestMinLength) {\n this._suggestTimeout = setTimeout(() => this._geocode(true), this.options.suggestTimeout);\n } else {\n this._clearResults();\n }\n }\n }\n}\n\n/**\n * [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link GeocoderControl}\n * @param options the options\n */\nexport function geocoder(options?: Partial) {\n return new GeocoderControl(options);\n}\n","/* @preserve\n * Leaflet Control Geocoder\n * https://github.com/perliedman/leaflet-control-geocoder\n *\n * Copyright (c) 2012 sa3m (https://github.com/sa3m)\n * Copyright (c) 2018 Per Liedman\n * All rights reserved.\n */\nimport * as L from 'leaflet';\nimport { GeocoderControl as Geocoder, geocoder } from './control';\nimport * as geocoders from './geocoders/index';\nimport './style.css';\n\nL.Util.extend(Geocoder, geocoders);\nexport default Geocoder;\nexport { Geocoder, geocoder, geocoders };\n\nL.Util.extend(L.Control, {\n Geocoder: Geocoder,\n geocoder: geocoder\n});\n"],"names":["geocodingParams","options","params","L","reverseParams","badChars","possible","escape","escapeChar","chr","htmlEscape","string","getJSON","url","headers","request","key","value","v","response","template","str","data","ArcGis","__publicField","query","results","loc","latLng","latLngBounds","location","scale","result","center","bbox","arcgis","Bing","resource","i","bing","AzureMaps","address","azure","Google","google","HERE","prox","HEREv2","item","here","parseLatLng","match","LatLng","Mapbox","properties","j","id","param","_a","mapbox","MapQuest","parts","s","mapQuest","Neutrino","neutrino","Nominatim","r","className","nominatim","OpenLocationCode","decoded","e","openLocationCode","OpenCage","opencage","Pelias","bboxname","feature","latlng","layer","pelias","GeocodeEarth","geocodeEarth","Mapzen","mapzen","Openrouteservice","openrouteservice","Photon","f","c","extent","p","photon","What3Words","what3words","EventedControl","args","GeocoderControl","map","container","icon","form","input","suggest","event","requestCount","geocode","index","li","a","text","mouseDownHandler","select","dir","geocoder","Geocoder","geocoders"],"mappings":"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"}
\ No newline at end of file
diff --git a/public/leaflet-control-geocoder/control.d.ts b/public/leaflet-control-geocoder/control.d.ts
new file mode 100644
index 0000000..1ad9cd7
--- /dev/null
+++ b/public/leaflet-control-geocoder/control.d.ts
@@ -0,0 +1,161 @@
+import * as L from 'leaflet';
+import { IGeocoder, GeocodingResult } from './geocoders/api';
+export interface GeocoderControlOptions extends L.ControlOptions {
+ /**
+ * Collapse control unless hovered/clicked
+ */
+ collapsed: boolean;
+ /**
+ * How to expand a collapsed control: `touch` or `click` or `hover`
+ */
+ expand: 'touch' | 'click' | 'hover';
+ /**
+ * Placeholder text for text input
+ */
+ placeholder: string;
+ /**
+ * Message when no result found / geocoding error occurs
+ */
+ errorMessage: string;
+ /**
+ * Accessibility label for the search icon used by screen readers
+ */
+ iconLabel: string;
+ /**
+ * Object to perform the actual geocoding queries
+ */
+ geocoder?: IGeocoder;
+ /**
+ * Immediately show the unique result without prompting for alternatives
+ */
+ showUniqueResult: boolean;
+ /**
+ * Show icons for geocoding results (if available); supported by Nominatim
+ */
+ showResultIcons: boolean;
+ /**
+ * Minimum number characters before suggest functionality is used (if available from geocoder)
+ */
+ suggestMinLength: number;
+ /**
+ * Number of milliseconds after typing stopped before suggest functionality is used (if available from geocoder)
+ */
+ suggestTimeout: number;
+ /**
+ * Initial query string for text input
+ */
+ query: string;
+ /**
+ * Minimum number of characters in search text before performing a query
+ */
+ queryMinLength: number;
+ /**
+ * Whether to mark a geocoding result on the map by default
+ */
+ defaultMarkGeocode: boolean;
+}
+/**
+ * Event is fired when selecting a geocode result.
+ * By default, the control will center the map on it and place a marker at its location.
+ * To remove the control's default handler for marking a result, set {@link GeocoderControlOptions.defaultMarkGeocode} to `false`.
+ */
+export type MarkGeocodeEvent = {
+ geocode: GeocodingResult;
+};
+export type MarkGeocodeEventHandlerFn = (event: MarkGeocodeEvent) => void;
+/**
+ * Event is fired before invoking {@link IGeocoder.geocode} (or {@link IGeocoder.suggest}).
+ * The event data contains the query string as `input`.
+ */
+export type StartGeocodeEvent = {
+ input: string;
+};
+export type StartGeocodeEventHandlerFn = (event: StartGeocodeEvent) => void;
+/**
+ * Event is fired before after receiving results from {@link IGeocoder.geocode} (or {@link IGeocoder.suggest}).
+ * The event data contains the query string as `input` and the geocoding `results`.
+ */
+export type FinishGeocodeEvent = {
+ input: string;
+ results: GeocodingResult[];
+};
+export type FinishGeocodeEventHandlerFn = (event: FinishGeocodeEvent) => void;
+declare module 'leaflet' {
+ interface Evented {
+ on(type: 'markgeocode', fn: MarkGeocodeEventHandlerFn, context?: any): this;
+ on(type: 'startgeocode', fn: StartGeocodeEventHandlerFn, context?: any): this;
+ on(type: 'startsuggest', fn: StartGeocodeEventHandlerFn, context?: any): this;
+ on(type: 'finishsuggest', fn: FinishGeocodeEventHandlerFn, context?: any): this;
+ on(type: 'finishgeocode', fn: FinishGeocodeEventHandlerFn, context?: any): this;
+ }
+}
+/**
+ * Leaflet mixins https://leafletjs.com/reference-1.7.1.html#class-includes
+ * for TypeScript https://www.typescriptlang.org/docs/handbook/mixins.html
+ * @internal
+ */
+declare class EventedControl {
+ constructor(...args: any[]);
+}
+/**
+ * @internal
+ */
+interface EventedControl extends L.Control, L.Evented {
+}
+/**
+ * This is the geocoder control. It works like any other [Leaflet control](https://leafletjs.com/reference.html#control), and is added to the map.
+ */
+export declare class GeocoderControl extends EventedControl {
+ options: GeocoderControlOptions;
+ private _alts;
+ private _container;
+ private _errorElement;
+ private _geocodeMarker;
+ private _input;
+ private _lastGeocode;
+ private _map;
+ private _preventBlurCollapse;
+ private _requestCount;
+ private _results;
+ private _selection;
+ private _suggestTimeout;
+ /**
+ * Instantiates a geocoder control (to be invoked using `new`)
+ * @param options the options
+ */
+ constructor(options?: Partial);
+ addThrobberClass(): void;
+ removeThrobberClass(): void;
+ /**
+ * Returns the container DOM element for the control and add listeners on relevant map events.
+ * @param map the map instance
+ * @see https://leafletjs.com/reference.html#control-onadd
+ */
+ onAdd(map: L.Map): HTMLDivElement;
+ /**
+ * Sets the query string on the text input
+ * @param string the query string
+ */
+ setQuery(string: string): this;
+ private _geocodeResult;
+ /**
+ * Marks a geocoding result on the map
+ * @param result the geocoding result
+ */
+ markGeocode(event: MarkGeocodeEvent): this;
+ private _geocode;
+ private _geocodeResultSelected;
+ private _toggle;
+ private _expand;
+ private _collapse;
+ private _clearResults;
+ private _createAlt;
+ private _keydown;
+ private _change;
+}
+/**
+ * [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link GeocoderControl}
+ * @param options the options
+ */
+export declare function geocoder(options?: Partial): GeocoderControl;
+export {};
diff --git a/public/leaflet-control-geocoder/geocoders/api.d.ts b/public/leaflet-control-geocoder/geocoders/api.d.ts
new file mode 100644
index 0000000..a3ef87c
--- /dev/null
+++ b/public/leaflet-control-geocoder/geocoders/api.d.ts
@@ -0,0 +1,77 @@
+import * as L from 'leaflet';
+/**
+ * An object that represents a result from a geocoding query
+ */
+export interface GeocodingResult {
+ /**
+ * Name of found location
+ */
+ name: string;
+ /**
+ * The bounds of the location
+ */
+ bbox: L.LatLngBounds;
+ /**
+ * The center coordinate of the location
+ */
+ center: L.LatLng;
+ /**
+ * URL for icon representing result; optional
+ */
+ icon?: string;
+ /**
+ * HTML formatted representation of the name
+ */
+ html?: string;
+ /**
+ * Additional properties returned by the geocoder
+ */
+ properties?: any;
+}
+/**
+ * An interface implemented to respond to geocoding queries
+ */
+export interface IGeocoder {
+ /**
+ * Performs a geocoding query and returns the results as promise
+ * @param query the query
+ */
+ geocode(query: string): Promise;
+ /**
+ * Performs a geocoding query suggestion (this happens while typing) and returns the results as promise
+ * @param query the query
+ */
+ suggest?(query: string): Promise;
+ /**
+ * Performs a reverse geocoding query and returns the results as promise
+ * @param location the coordinate to reverse geocode
+ * @param scale the map scale possibly used for reverse geocoding
+ */
+ reverse?(location: L.LatLngLiteral, scale: number): Promise;
+}
+export interface GeocoderOptions {
+ /**
+ * URL of the service
+ */
+ serviceUrl: string;
+ /**
+ * Additional URL parameters (strings) that will be added to geocoding requests
+ */
+ geocodingQueryParams?: Record;
+ /**
+ * Additional URL parameters (strings) that will be added to reverse geocoding requests
+ */
+ reverseQueryParams?: Record;
+ /**
+ * API key to use this service
+ */
+ apiKey?: string;
+}
+/**
+ * @internal
+ */
+export declare function geocodingParams(options: GeocoderOptions, params: Record): Record;
+/**
+ * @internal
+ */
+export declare function reverseParams(options: GeocoderOptions, params: Record): Record;
diff --git a/public/leaflet-control-geocoder/geocoders/arcgis.d.ts b/public/leaflet-control-geocoder/geocoders/arcgis.d.ts
new file mode 100644
index 0000000..23f5106
--- /dev/null
+++ b/public/leaflet-control-geocoder/geocoders/arcgis.d.ts
@@ -0,0 +1,47 @@
+import * as L from 'leaflet';
+import { IGeocoder, GeocoderOptions, GeocodingResult } from './api';
+export interface ArcGisOptions extends GeocoderOptions {
+}
+/**
+ * Implementation of the [ArcGIS geocoder](https://developers.arcgis.com/features/geocoding/)
+ */
+export declare class ArcGis implements IGeocoder {
+ options: ArcGisOptions;
+ constructor(options?: Partial);
+ geocode(query: string): Promise;
+ suggest(query: string): Promise;
+ reverse(location: L.LatLngLiteral, scale: number): Promise;
+}
+/**
+ * [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link ArcGis}
+ * @param options the options
+ */
+export declare function arcgis(options?: Partial): ArcGis;
+/**
+ * @internal
+ */
+export interface ArcGisResponse {
+ spatialReference: {
+ wkid: number;
+ latestWkid: number;
+ };
+ candidates: Candidate[];
+}
+interface Candidate {
+ address: string;
+ location: {
+ x: number;
+ y: number;
+ };
+ score: number;
+ attributes: {
+ Addr_Type: string;
+ };
+ extent: {
+ xmin: number;
+ ymin: number;
+ xmax: number;
+ ymax: number;
+ };
+}
+export {};
diff --git a/public/leaflet-control-geocoder/geocoders/azure.d.ts b/public/leaflet-control-geocoder/geocoders/azure.d.ts
new file mode 100644
index 0000000..498095b
--- /dev/null
+++ b/public/leaflet-control-geocoder/geocoders/azure.d.ts
@@ -0,0 +1,84 @@
+import * as L from 'leaflet';
+import { GeocodingResult, IGeocoder } from './api';
+export interface AzureMapsOptions {
+ apiKey: string;
+ serviceUrl: string;
+}
+/**
+ * Implementation of [Azure Maps Geocoding](https://www.microsoft.com/en-us/maps/azure/location-services/geocoding)
+ *
+ * https://learn.microsoft.com/en-us/rest/api/maps/search?view=rest-maps-1.0
+ */
+export declare class AzureMaps implements IGeocoder {
+ private options;
+ constructor(options: Partial);
+ /**
+ * {@inheritdoc}
+ * https://learn.microsoft.com/en-us/rest/api/maps/search/get-search-address?view=rest-maps-1.0&tabs=HTTP
+ */
+ geocode(query: string): Promise;
+ /**
+ * {@inheritdoc}
+ * https://learn.microsoft.com/en-us/rest/api/maps/search/get-search-address-reverse?view=rest-maps-1.0&tabs=HTTP
+ */
+ reverse(location: L.LatLngLiteral, scale: number): Promise