{"id":1767,"date":"2026-03-06T04:15:24","date_gmt":"2026-03-06T04:15:24","guid":{"rendered":"https:\/\/marineconservation.id\/?page_id=1767"},"modified":"2026-04-09T04:58:05","modified_gmt":"2026-04-09T04:58:05","slug":"e-elasmobranch-indonesia","status":"publish","type":"page","link":"https:\/\/marineconservation.id\/id\/e-elasmobranch-indonesia\/","title":{"rendered":"e-Elasmobranch Indonesia"},"content":{"rendered":"<style>.elementor-1767 .elementor-element.elementor-element-3698d72{--display:flex;--flex-direction:column;--container-widget-width:100%;--container-widget-height:initial;--container-widget-flex-grow:0;--container-widget-align-self:initial;--flex-wrap-mobile:wrap;}<\/style>\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"1767\" class=\"elementor elementor-1767\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-3698d72 e-flex e-con-boxed e-con e-parent\" data-id=\"3698d72\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-31b5c01 elementor-widget elementor-widget-html\" data-id=\"31b5c01\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<div style=\"max-width:1200px; margin:0 auto; font-family:Arial, sans-serif;\">\r\n    <h2 style=\"color:#0284c7;\">\ud83e\udd88 e-Elasmobranch Indonesia<\/h2>\r\n    <div style=\"display:flex; flex-wrap:wrap; gap:20px;\">\r\n        <!-- Kolom Peta Utama (dengan filter di atas) -->\r\n        <div style=\"flex:2; min-width:300px;\">\r\n            <!-- Filter di atas peta -->\r\n            <div style=\"margin-bottom:10px; display:flex; gap:5px; flex-wrap:wrap; align-items:center;\">\r\n                <select id=\"filterProvince\" style=\"padding:5px; border:1px solid #ccc; border-radius:5px; min-width:120px;\">\r\n                    <option value=\"\">Semua Provinsi<\/option>\r\n                <\/select>\r\n                <select id=\"filterDistrict\" style=\"padding:5px; border:1px solid #ccc; border-radius:5px; min-width:120px;\">\r\n                    <option value=\"\">Semua Kab\/Kota<\/option>\r\n                <\/select>\r\n                <input type=\"text\" id=\"searchInput\" placeholder=\"Cari ID, lokasi, surveyor...\" style=\"flex:1; min-width:150px; padding:5px; border:1px solid #ccc; border-radius:5px;\">\r\n                <select id=\"filterSex\" style=\"width:120px; padding:5px; border:1px solid #ccc; border-radius:5px;\">\r\n                    <option value=\"\">Semua Kelamin<\/option>\r\n                    <option value=\"Jantan\">Jantan<\/option>\r\n                    <option value=\"Betina\">Betina<\/option>\r\n                    <option value=\"Tidak tahu\">Tidak tahu<\/option>\r\n                <\/select>\r\n                <input type=\"number\" id=\"filterMinLength\" placeholder=\"Min Panjang (cm)\" step=\"0.1\" style=\"width:120px; padding:5px; border:1px solid #ccc; border-radius:5px;\">\r\n                <button id=\"applyFilter\" style=\"background:#0284c7; color:white; border:none; padding:5px 10px; border-radius:5px; cursor:pointer;\">\ud83d\udd0d<\/button>\r\n            <\/div>\r\n            <div id=\"map\" style=\"height:500px; border-radius:10px; overflow:hidden; box-shadow:0 2px 8px rgba(0,0,0,0.1);\"><\/div>\r\n        <\/div>\r\n\r\n        <!-- Kolom Form (hanya muncul jika login) -->\r\n        <div id=\"formSection\" style=\"flex:1.2; min-width:300px; background:#f9f9f9; padding:20px; border-radius:10px; box-shadow:0 2px 8px rgba(0,0,0,0.1); display:none;\">\r\n            <!-- Form akan diisi oleh JavaScript jika login -->\r\n        <\/div>\r\n        <!-- Info jika belum login -->\r\n        <div id=\"loginPrompt\" style=\"flex:1.2; min-width:300px; background:#f9f9f9; padding:20px; border-radius:10px; box-shadow:0 2px 8px rgba(0,0,0,0.1); text-align:center;\">\r\n            <p>\ud83d\udd12 Anda belum login.<\/p>\r\n            <p>\r\n                <a href=\"#\" id=\"loginLink\" style=\"background:#0284c7; color:white; padding:10px 20px; text-decoration:none; border-radius:5px; display:inline-block; margin-right:10px;\">Login<\/a>\r\n                <a href=\"#\" id=\"registerLink\" style=\"background:#2e7d32; color:white; padding:10px 20px; text-decoration:none; border-radius:5px; display:inline-block;\">Daftar<\/a>\r\n            <\/p>\r\n            <p style=\"font-size:12px; color:#666;\">Peta menampilkan semua data publik. Tabel hanya menampilkan data Anda.<\/p>\r\n        <\/div>\r\n    <\/div>\r\n<\/div>\r\n\r\n<!-- Bagian Tabel dan Download -->\r\n<div style=\"margin-top:30px; background:white; padding:20px; border-radius:10px; box-shadow:0 2px 8px rgba(0,0,0,0.1);\">\r\n    <h3 style=\"margin-top:0; color:#0284c7;\">\ud83d\udccb Data Elasmobranch Tersimpan<\/h3>\r\n    \r\n    <!-- Pilihan Format dan Tombol Download -->\r\n    <div style=\"margin-bottom:15px; display:flex; gap:10px; flex-wrap:wrap; align-items:center;\">\r\n        <select id=\"formatSelect\" style=\"padding:8px; border:1px solid #ccc; border-radius:5px; min-width:100px;\">\r\n            <option value=\"csv\">CSV<\/option>\r\n            <option value=\"xlsx\">XLSX (Excel)<\/option>\r\n            <option value=\"txt\">TXT<\/option>\r\n        <\/select>\r\n        <button id=\"downloadBtn\" style=\"background:#2e7d32; color:white; border:none; padding:8px 15px; border-radius:5px; cursor:pointer;\">\ud83d\udce5 Download (Data Saya)<\/button>\r\n    <\/div>\r\n\r\n    <!-- Tabel Data -->\r\n    <div style=\"overflow-x:auto;\">\r\n        <table id=\"dataTable\" style=\"width:100%; border-collapse:collapse; font-size:14px;\">\r\n            <thead>\r\n                <tr style=\"background:#f1f5f9;\">\r\n                    <th style=\"padding:10px; text-align:left;\">ID Individu<\/th>\r\n                    <th style=\"padding:10px; text-align:left;\">Nama<\/th>\r\n                    <th style=\"padding:10px; text-align:left;\">Spesies<\/th>\r\n                    <th style=\"padding:10px; text-align:left;\">Provinsi<\/th>\r\n                    <th style=\"padding:10px; text-align:left;\">Kab\/Kota<\/th>\r\n                    <th style=\"padding:10px; text-align:left;\">Lat<\/th>\r\n                    <th style=\"padding:10px; text-align:left;\">Lng<\/th>\r\n                    <th style=\"padding:10px; text-align:left;\">Tanggal<\/th>\r\n                    <th style=\"padding:10px; text-align:left;\">Waktu<\/th>\r\n                    <th style=\"padding:10px; text-align:left;\">Foto Dorsal<\/th>\r\n                    <th style=\"padding:10px; text-align:left;\">Foto Kiri<\/th>\r\n                    <th style=\"padding:10px; text-align:left;\">Foto Kanan<\/th>\r\n                    <th style=\"padding:10px; text-align:left;\">Foto Ventral<\/th>\r\n                    <th style=\"padding:10px; text-align:left;\">Foto Skala<\/th>\r\n                    <th style=\"padding:10px; text-align:left;\">Panjang (cm)<\/th>\r\n                    <th style=\"padding:10px; text-align:left;\">Lebar Cakram<\/th>\r\n                    <th style=\"padding:10px; text-align:left;\">Jenis Kelamin<\/th>\r\n                    <th style=\"padding:10px; text-align:left;\">Kedalaman (m)<\/th>\r\n                    <th style=\"padding:10px; text-align:left;\">Behaviour<\/th>\r\n                    <th style=\"padding:10px; text-align:left;\">Kesehatan<\/th>\r\n                    <th style=\"padding:10px; text-align:left;\">Ket. Sakit<\/th>\r\n                    <th style=\"padding:10px; text-align:left;\">Hasil Analisis<\/th>\r\n                    <th style=\"padding:10px; text-align:left;\">Kontributor<\/th>\r\n                    <th style=\"padding:10px; text-align:left;\">Surveyor<\/th>\r\n                    <th style=\"padding:10px; text-align:left;\">Instansi<\/th>\r\n                    <th style=\"padding:10px; text-align:left;\">Aksi<\/th>\r\n                <\/tr>\r\n            <\/thead>\r\n            <tbody id=\"tableBody\">\r\n                <tr><td colspan=\"27\" style=\"text-align:center; padding:20px;\">Memuat data...<\/td><\/tr>\r\n            <\/tbody>\r\n        <\/table>\r\n    <\/div>\r\n<\/div>\r\n\r\n<!-- Leaflet, MarkerCluster, Cropper, SheetJS -->\r\n<link rel=\"stylesheet\" href=\"https:\/\/unpkg.com\/leaflet@1.9.4\/dist\/leaflet.css\" \/>\r\n<link rel=\"stylesheet\" href=\"https:\/\/unpkg.com\/leaflet.markercluster@1.4.1\/dist\/MarkerCluster.css\" \/>\r\n<link rel=\"stylesheet\" href=\"https:\/\/unpkg.com\/leaflet.markercluster@1.4.1\/dist\/MarkerCluster.Default.css\" \/>\r\n<link rel=\"stylesheet\" href=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/cropperjs\/1.5.13\/cropper.min.css\" \/>\r\n<script src=\"https:\/\/code.jquery.com\/jquery-3.6.0.min.js\"><\/script>\r\n<script src=\"https:\/\/unpkg.com\/leaflet@1.9.4\/dist\/leaflet.js\"><\/script>\r\n<script src=\"https:\/\/unpkg.com\/leaflet.markercluster@1.4.1\/dist\/leaflet.markercluster.js\"><\/script>\r\n<script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/cropperjs\/1.5.13\/cropper.min.js\"><\/script>\r\n<script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/xlsx\/0.18.5\/xlsx.full.min.js\"><\/script>\r\n\r\n<style>\r\n    #confirmMatchBtn, #useExistingBtn, #newIndividualBtn {\r\n        z-index: 9999;\r\n        position: relative;\r\n        pointer-events: auto !important;\r\n        margin: 5px;\r\n    }\r\n    .loading {\r\n        opacity: 0.6;\r\n        pointer-events: none;\r\n    }\r\n    #calibrationCanvas {\r\n        border: 2px solid #0284c7;\r\n        background: #f0f0f0;\r\n        width: 100%;\r\n        height: 300px;\r\n        cursor: crosshair;\r\n    }\r\n    .calibration-point {\r\n        position: absolute;\r\n        width: 8px;\r\n        height: 8px;\r\n        background: red;\r\n        border-radius: 50%;\r\n        transform: translate(-50%, -50%);\r\n        pointer-events: none;\r\n    }\r\n    .comparison-container {\r\n        display: flex;\r\n        gap: 20px;\r\n        margin-bottom: 15px;\r\n        flex-wrap: wrap;\r\n        justify-content: center;\r\n    }\r\n    .comparison-item {\r\n        flex: 1;\r\n        min-width: 200px;\r\n        text-align: center;\r\n        background: #f0f8ff;\r\n        padding: 10px;\r\n        border-radius: 8px;\r\n    }\r\n    .comparison-item img {\r\n        max-width: 100%;\r\n        max-height: 200px;\r\n        border: 2px solid #0284c7;\r\n        border-radius: 8px;\r\n        box-shadow: 0 2px 5px rgba(0,0,0,0.1);\r\n    }\r\n<\/style>\r\n\r\n<script>\r\n\/\/ ================== OBJEK INDONESIA REGIONS (LENGKAP) ==================\r\nconst indonesiaRegions = {\r\n    \"Aceh\": [\"Aceh Barat\", \"Aceh Barat Daya\", \"Aceh Besar\", \"Aceh Jaya\", \"Aceh Selatan\", \"Aceh Singkil\", \"Aceh Tamiang\", \"Aceh Tengah\", \"Aceh Tenggara\", \"Aceh Timur\", \"Aceh Utara\", \"Bener Meriah\", \"Bireuen\", \"Gayo Lues\", \"Nagan Raya\", \"Pidie\", \"Pidie Jaya\", \"Simeulue\", \"Kota Banda Aceh\", \"Kota Langsa\", \"Kota Lhokseumawe\", \"Kota Sabang\", \"Kota Subulussalam\"],\r\n    \"Sumatera Utara\": [\"Asahan\", \"Batu Bara\", \"Dairi\", \"Deli Serdang\", \"Humbang Hasundutan\", \"Karo\", \"Labuhanbatu\", \"Labuhanbatu Selatan\", \"Labuhanbatu Utara\", \"Langkat\", \"Mandailing Natal\", \"Nias\", \"Nias Barat\", \"Nias Selatan\", \"Nias Utara\", \"Padang Lawas\", \"Padang Lawas Utara\", \"Pakpak Bharat\", \"Samosir\", \"Serdang Bedagai\", \"Simalungun\", \"Tapanuli Selatan\", \"Tapanuli Tengah\", \"Tapanuli Utara\", \"Toba Samosir\", \"Kota Binjai\", \"Kota Gunungsitoli\", \"Kota Medan\", \"Kota Padangsidimpuan\", \"Kota Pematangsiantar\", \"Kota Sibolga\", \"Kota Tanjungbalai\", \"Kota Tebing Tinggi\"],\r\n    \"Sumatera Barat\": [\"Agam\", \"Dharmasraya\", \"Kepulauan Mentawai\", \"Lima Puluh Kota\", \"Padang Pariaman\", \"Pasaman\", \"Pasaman Barat\", \"Pesisir Selatan\", \"Sijunjung\", \"Solok\", \"Solok Selatan\", \"Tanah Datar\", \"Kota Bukittinggi\", \"Kota Padang\", \"Kota Padang Panjang\", \"Kota Pariaman\", \"Kota Payakumbuh\", \"Kota Sawahlunto\", \"Kota Solok\"],\r\n    \"Riau\": [\"Bengkalis\", \"Indragiri Hilir\", \"Indragiri Hulu\", \"Kampar\", \"Kepulauan Meranti\", \"Kuantan Singingi\", \"Pelalawan\", \"Rokan Hilir\", \"Rokan Hulu\", \"Siak\", \"Kota Dumai\", \"Kota Pekanbaru\"],\r\n    \"Kepulauan Riau\": [\"Bintan\", \"Karimun\", \"Kepulauan Anambas\", \"Lingga\", \"Natuna\", \"Kota Batam\", \"Kota Tanjungpinang\"],\r\n    \"Jambi\": [\"Batanghari\", \"Bungo\", \"Kerinci\", \"Merangin\", \"Muaro Jambi\", \"Sarolangun\", \"Tanjung Jabung Barat\", \"Tanjung Jabung Timur\", \"Tebo\", \"Kota Jambi\", \"Kota Sungai Penuh\"],\r\n    \"Bengkulu\": [\"Bengkulu Selatan\", \"Bengkulu Tengah\", \"Bengkulu Utara\", \"Kaur\", \"Kepahiang\", \"Lebong\", \"Muko Muko\", \"Rejang Lebong\", \"Seluma\", \"Kota Bengkulu\"],\r\n    \"Sumatera Selatan\": [\"Banyuasin\", \"Empat Lawang\", \"Lahat\", \"Muara Enim\", \"Musi Banyuasin\", \"Musi Rawas\", \"Musi Rawas Utara\", \"Ogan Ilir\", \"Ogan Komering Ilir\", \"Ogan Komering Ulu\", \"Ogan Komering Ulu Selatan\", \"Ogan Komering Ulu Timur\", \"Penukal Abab Lematang Ilir\", \"Kota Lubuklinggau\", \"Kota Pagar Alam\", \"Kota Palembang\", \"Kota Prabumulih\"],\r\n    \"Bangka Belitung\": [\"Bangka\", \"Bangka Barat\", \"Bangka Selatan\", \"Bangka Tengah\", \"Belitung\", \"Belitung Timur\", \"Kota Pangkalpinang\"],\r\n    \"Lampung\": [\"Lampung Barat\", \"Lampung Selatan\", \"Lampung Tengah\", \"Lampung Timur\", \"Lampung Utara\", \"Mesuji\", \"Pesawaran\", \"Pesisir Barat\", \"Pringsewu\", \"Tanggamus\", \"Tulang Bawang\", \"Tulang Bawang Barat\", \"Way Kanan\", \"Kota Bandar Lampung\", \"Kota Metro\"],\r\n    \"Banten\": [\"Lebak\", \"Pandeglang\", \"Serang\", \"Tangerang\", \"Kota Cilegon\", \"Kota Serang\", \"Kota Tangerang\", \"Kota Tangerang Selatan\"],\r\n    \"DKI Jakarta\": [\"Kepulauan Seribu\", \"Kota Jakarta Barat\", \"Kota Jakarta Pusat\", \"Kota Jakarta Selatan\", \"Kota Jakarta Timur\", \"Kota Jakarta Utara\"],\r\n    \"Jawa Barat\": [\"Bandung\", \"Bandung Barat\", \"Bekasi\", \"Bogor\", \"Ciamis\", \"Cianjur\", \"Cirebon\", \"Garut\", \"Indramayu\", \"Karawang\", \"Kuningan\", \"Majalengka\", \"Pangandaran\", \"Purwakarta\", \"Subang\", \"Sukabumi\", \"Sumedang\", \"Tasikmalaya\", \"Kota Bandung\", \"Kota Banjar\", \"Kota Bekasi\", \"Kota Bogor\", \"Kota Cimahi\", \"Kota Cirebon\", \"Kota Depok\", \"Kota Sukabumi\", \"Kota Tasikmalaya\"],\r\n    \"Jawa Tengah\": [\"Banjarnegara\", \"Banyumas\", \"Batang\", \"Blora\", \"Boyolali\", \"Brebes\", \"Cilacap\", \"Demak\", \"Grobogan\", \"Jepara\", \"Karanganyar\", \"Kebumen\", \"Kendal\", \"Klaten\", \"Kudus\", \"Magelang\", \"Pati\", \"Pekalongan\", \"Pemalang\", \"Purbalingga\", \"Purworejo\", \"Rembang\", \"Semarang\", \"Sragen\", \"Sukoharjo\", \"Tegal\", \"Temanggung\", \"Wonogiri\", \"Wonosobo\", \"Kota Magelang\", \"Kota Pekalongan\", \"Kota Salatiga\", \"Kota Semarang\", \"Kota Surakarta\", \"Kota Tegal\"],\r\n    \"DI Yogyakarta\": [\"Bantul\", \"Gunungkidul\", \"Kulon Progo\", \"Sleman\", \"Kota Yogyakarta\"],\r\n    \"Jawa Timur\": [\"Bangkalan\", \"Banyuwangi\", \"Blitar\", \"Bojonegoro\", \"Bondowoso\", \"Gresik\", \"Jember\", \"Jombang\", \"Kediri\", \"Lamongan\", \"Lumajang\", \"Madiun\", \"Magetan\", \"Malang\", \"Mojokerto\", \"Nganjuk\", \"Ngawi\", \"Pacitan\", \"Pamekasan\", \"Pasuruan\", \"Ponorogo\", \"Probolinggo\", \"Sampang\", \"Sidoarjo\", \"Situbondo\", \"Sumenep\", \"Trenggalek\", \"Tuban\", \"Tulungagung\", \"Kota Batu\", \"Kota Blitar\", \"Kota Kediri\", \"Kota Madiun\", \"Kota Malang\", \"Kota Mojokerto\", \"Kota Pasuruan\", \"Kota Probolinggo\", \"Kota Surabaya\"],\r\n    \"Bali\": [\"Badung\", \"Bangli\", \"Buleleng\", \"Gianyar\", \"Jembrana\", \"Karangasem\", \"Klungkung\", \"Tabanan\", \"Kota Denpasar\"],\r\n    \"Nusa Tenggara Barat\": [\"Bima\", \"Dompu\", \"Lombok Barat\", \"Lombok Tengah\", \"Lombok Timur\", \"Lombok Utara\", \"Sumbawa\", \"Sumbawa Barat\", \"Kota Bima\", \"Kota Mataram\"],\r\n    \"Nusa Tenggara Timur\": [\"Alor\", \"Belu\", \"Ende\", \"Flores Timur\", \"Kupang\", \"Lembata\", \"Malaka\", \"Manggarai\", \"Manggarai Barat\", \"Manggarai Timur\", \"Nagekeo\", \"Ngada\", \"Rote Ndao\", \"Sabu Raijua\", \"Sikka\", \"Sumba Barat\", \"Sumba Barat Daya\", \"Sumba Tengah\", \"Sumba Timur\", \"Timor Tengah Selatan\", \"Timor Tengah Utara\", \"Kota Kupang\"],\r\n    \"Kalimantan Barat\": [\"Bengkayang\", \"Kapuas Hulu\", \"Kayong Utara\", \"Ketapang\", \"Kubu Raya\", \"Landak\", \"Melawi\", \"Mempawah\", \"Sambas\", \"Sanggau\", \"Sekadau\", \"Sintang\", \"Kota Pontianak\", \"Kota Singkawang\"],\r\n    \"Kalimantan Tengah\": [\"Barito Selatan\", \"Barito Timur\", \"Barito Utara\", \"Gunung Mas\", \"Kapuas\", \"Katingan\", \"Kotawaringin Barat\", \"Kotawaringin Timur\", \"Lamandau\", \"Murung Raya\", \"Pulang Pisau\", \"Sukamara\", \"Seruyan\", \"Kota Palangka Raya\"],\r\n    \"Kalimantan Selatan\": [\"Balangan\", \"Banjar\", \"Barito Kuala\", \"Hulu Sungai Selatan\", \"Hulu Sungai Tengah\", \"Hulu Sungai Utara\", \"Kotabaru\", \"Tabalong\", \"Tanah Bumbu\", \"Tanah Laut\", \"Tapin\", \"Kota Banjarbaru\", \"Kota Banjarmasin\"],\r\n    \"Kalimantan Timur\": [\"Berau\", \"Kutai Barat\", \"Kutai Kartanegara\", \"Kutai Timur\", \"Mahakam Ulu\", \"Paser\", \"Penajam Paser Utara\", \"Kota Balikpapan\", \"Kota Bontang\", \"Kota Samarinda\"],\r\n    \"Kalimantan Utara\": [\"Bulungan\", \"Malinau\", \"Nunukan\", \"Tana Tidung\", \"Kota Tarakan\"],\r\n    \"Sulawesi Utara\": [\"Bolaang Mongondow\", \"Bolaang Mongondow Selatan\", \"Bolaang Mongondow Timur\", \"Bolaang Mongondow Utara\", \"Kepulauan Sangihe\", \"Kepulauan Siau Tagulandang Biaro\", \"Kepulauan Talaud\", \"Minahasa\", \"Minahasa Selatan\", \"Minahasa Tenggara\", \"Minahasa Utara\", \"Kota Bitung\", \"Kota Kotamobagu\", \"Kota Manado\", \"Kota Tomohon\"],\r\n    \"Gorontalo\": [\"Boalemo\", \"Bone Bolango\", \"Gorontalo\", \"Gorontalo Utara\", \"Pohuwato\", \"Kota Gorontalo\"],\r\n    \"Sulawesi Tengah\": [\"Banggai\", \"Banggai Kepulauan\", \"Banggai Laut\", \"Buol\", \"Donggala\", \"Morowali\", \"Morowali Utara\", \"Parigi Moutong\", \"Poso\", \"Sigi\", \"Tojo Una-Una\", \"Toli-Toli\", \"Kota Palu\"],\r\n    \"Sulawesi Barat\": [\"Majene\", \"Mamasa\", \"Mamuju\", \"Mamuju Tengah\", \"Pasangkayu\", \"Polewali Mandar\"],\r\n    \"Sulawesi Selatan\": [\"Bantaeng\", \"Barru\", \"Bone\", \"Bulukumba\", \"Enrekang\", \"Gowa\", \"Jeneponto\", \"Kepulauan Selayar\", \"Luwu\", \"Luwu Timur\", \"Luwu Utara\", \"Maros\", \"Pangkajene dan Kepulauan\", \"Pinrang\", \"Sidenreng Rappang\", \"Sinjai\", \"Soppeng\", \"Takalar\", \"Tana Toraja\", \"Toraja Utara\", \"Wajo\", \"Kota Makassar\", \"Kota Palopo\", \"Kota Parepare\"],\r\n    \"Sulawesi Tenggara\": [\"Bombana\", \"Buton\", \"Buton Selatan\", \"Buton Tengah\", \"Buton Utara\", \"Kolaka\", \"Kolaka Timur\", \"Kolaka Utara\", \"Konawe\", \"Konawe Kepulauan\", \"Konawe Selatan\", \"Konawe Utara\", \"Muna\", \"Muna Barat\", \"Wakatobi\", \"Kota Bau-Bau\", \"Kota Kendari\"],\r\n    \"Maluku\": [\"Buru\", \"Buru Selatan\", \"Kepulauan Aru\", \"Maluku Barat Daya\", \"Maluku Tengah\", \"Maluku Tenggara\", \"Maluku Tenggara Barat\", \"Seram Bagian Barat\", \"Seram Bagian Timur\", \"Kota Ambon\", \"Kota Tual\"],\r\n    \"Maluku Utara\": [\"Halmahera Barat\", \"Halmahera Tengah\", \"Halmahera Timur\", \"Halmahera Selatan\", \"Halmahera Utara\", \"Kepulauan Sula\", \"Pulau Morotai\", \"Pulau Taliabu\", \"Kota Ternate\", \"Kota Tidore Kepulauan\"],\r\n    \"Papua\": [\"Asmat\", \"Biak Numfor\", \"Boven Digoel\", \"Deiyai\", \"Dogiyai\", \"Intan Jaya\", \"Jayapura\", \"Jayawijaya\", \"Keerom\", \"Kepulauan Yapen\", \"Lanny Jaya\", \"Mamberamo Raya\", \"Mamberamo Tengah\", \"Mappi\", \"Merauke\", \"Mimika\", \"Nabire\", \"Nduga\", \"Paniai\", \"Pegunungan Bintang\", \"Puncak\", \"Puncak Jaya\", \"Sarmi\", \"Supiori\", \"Tolikara\", \"Waropen\", \"Yahukimo\", \"Yalimo\", \"Kota Jayapura\"],\r\n    \"Papua Barat\": [\"Fakfak\", \"Kaimana\", \"Manokwari\", \"Manokwari Selatan\", \"Maybrat\", \"Pegunungan Arfak\", \"Raja Ampat\", \"Sorong\", \"Sorong Selatan\", \"Tambrauw\", \"Teluk Bintuni\", \"Teluk Wondama\", \"Kota Sorong\"],\r\n    \"Papua Selatan\": [\"Merauke\", \"Mappi\", \"Asmat\", \"Boven Digoel\"],\r\n    \"Papua Tengah\": [\"Nabire\", \"Puncak Jaya\", \"Paniai\", \"Mimika\", \"Puncak\", \"Dogiyai\", \"Intan Jaya\", \"Deiyai\"],\r\n    \"Papua Pegunungan\": [\"Jayawijaya\", \"Lanny Jaya\", \"Mamberamo Tengah\", \"Nduga\", \"Pegunungan Bintang\", \"Tolikara\", \"Yahukimo\", \"Yalimo\"],\r\n    \"Papua Barat Daya\": [\"Sorong\", \"Sorong Selatan\", \"Raja Ampat\", \"Tambrauw\", \"Maybrat\", \"Kota Sorong\"]\r\n};\r\n\r\nlet currentUser = null;\r\nlet isSuperAdmin = false;\r\nlet allData = [];\r\nlet filteredAllData = [];\r\nlet filteredUserData = [];\r\nconst themeFolder = 'astra'; \/\/ GANTI SESUAI TEMA ANDA\r\n\r\n\/\/ Variabel global untuk menyimpan hasil match\r\nlet currentMatch = null;\r\n\r\n\/\/ ================== DATA UKURAN MATA PER SPESIES ==================\r\nconst eyeSizeMap = {\r\n    \"Aetobatus flagellum\": 3.2,\r\n    \"Aetobatus narinari\": 4.5,\r\n    \"Aetobatus ocellatus\": 4.5,\r\n    \"Carcharhinus albimarginatus\": 4.2,\r\n    \"Carcharhinus altimus\": 4.0,\r\n    \"Carcharhinus amblyrhynchoides\": 3.6,\r\n    \"Carcharhinus amblyrhynchos\": 3.5,\r\n    \"Carcharhinus amboinensis\": 3.8,\r\n    \"Carcharhinus borneensis\": 2.4,\r\n    \"Carcharhinus brachyurus\": 4.2,\r\n    \"Carcharhinus brevipinna\": 3.8,\r\n    \"Carcharhinus cautus\": 2.8,\r\n    \"Carcharhinus cerdale\": 2.8,\r\n    \"Carcharhinus coatesi\": 3.0,\r\n    \"Carcharhinus dussumieri\": 2.2,\r\n    \"Carcharhinus falciformis\": 5.5,\r\n    \"Carcharhinus fitzroyensis\": 3.4,\r\n    \"Carcharhinus galapagensis\": 4.8,\r\n    \"Carcharhinus hemiodon\": 2.5,\r\n    \"Carcharhinus leucas\": 4.2,\r\n    \"Carcharhinus limbatus\": 4.0,\r\n    \"Carcharhinus longimanus\": 5.2,\r\n    \"Carcharhinus melanopterus\": 3.2,\r\n    \"Carcharhinus obscurus\": 4.5,\r\n    \"Carcharhinus perezii\": 4.2,\r\n    \"Carcharhinus plumbeus\": 3.8,\r\n    \"Carcharhinus porosus\": 2.8,\r\n    \"Carcharhinus sealei\": 2.8,\r\n    \"Carcharhinus sorrah\": 3.2,\r\n    \"Carcharhinus tilstoni\": 3.6,\r\n    \"Carcharhinus tjutjot\": 3.5,\r\n    \"Carcharodon carcharias\": 6.5,\r\n    \"Galeocerdo cuvier\": 5.8,\r\n    \"Ginglymostoma cirratum\": 2.5,\r\n    \"Gymnura micrura\": 2.2,\r\n    \"Heterodontus mexicanus\": 2.0,\r\n    \"Heterodontus portusjacksoni\": 2.4,\r\n    \"Heterodontus quoyi\": 2.2,\r\n    \"Hexanchus griseus\": 7.5,\r\n    \"Hypanus americanus\": 2.8,\r\n    \"Hypanus longus\": 3.2,\r\n    \"Hypanus sabinus\": 2.0,\r\n    \"Hypanus say\": 1.8,\r\n    \"Isogomphodon oxyrhynchus\": 1.5,\r\n    \"Mobula alfredi\": 7.0,\r\n    \"Mobula birostris\": 10.0,\r\n    \"Mobula tarapacana\": 6.5,\r\n    \"Mobula thurstoni\": 5.5,\r\n    \"Mustelus canis\": 3.2,\r\n    \"Myliobatis australis\": 3.8,\r\n    \"Myliobatis californica\": 4.0,\r\n    \"Myliobatis freminvillei\": 3.5,\r\n    \"Narcine brasiliensis\": 1.8,\r\n    \"Narcine entemedor\": 1.6,\r\n    \"Nebrius ferrugineus\": 2.8,\r\n    \"Negaprion acutidens\": 3.8,\r\n    \"Negaprion brevirostris\": 3.8,\r\n    \"Pateobatis fai\": 3.5,\r\n    \"Prionace glauca\": 6.5,\r\n    \"Pristis pectinata\": 4.5,\r\n    \"Pristis pristis\": 5.5,\r\n    \"Pseudobatos glaucostigma\": 2.6,\r\n    \"Pseudobatos lentiginosus\": 2.2,\r\n    \"Pseudobatos productus\": 3.0,\r\n    \"Raja undulata\": 2.5,\r\n    \"Rhincodon typus\": 3.0,\r\n    \"Rhinobatos rhinobatos\": 2.2,\r\n    \"Rhinoptera bonasus\": 3.8,\r\n    \"Rhinoptera steindachneri\": 3.5,\r\n    \"Rhizoprionodon acutus\": 2.4,\r\n    \"Rhizoprionodon lalandii\": 2.2,\r\n    \"Rhizoprionodon oligolinx\": 2.4,\r\n    \"Rhizoprionodon porosus\": 2.2,\r\n    \"Rhizoprionodon taylori\": 1.8,\r\n    \"Rhizoprionodon terraenovae\": 2.4,\r\n    \"Rostroraja eglanteria\": 2.5,\r\n    \"Sphyrna lewini\": 4.8,\r\n    \"Sphyrna mokarran\": 5.5,\r\n    \"Sphyrna tiburo\": 2.8,\r\n    \"Sphyrna zygaena\": 5.2,\r\n    \"Squalus acanthias\": 3.2,\r\n    \"Stegostoma tigrinum\": 3.0,\r\n    \"Taeniura lymma\": 2.3,\r\n    \"Taeniurops meyeni\": 4.0,\r\n    \"Tetronarce nobiliana\": 2.5,\r\n    \"Torpedo panthera\": 1.8,\r\n    \"Triaenodon obesus\": 3.4,\r\n    \"Urobatis concentricus\": 1.8,\r\n    \"Urobatis halleri\": 1.4,\r\n    \"Urobatis jamaicensis\": 1.7,\r\n    \"Urobatis maculatus\": 1.7,\r\n    \"Urobatis tumbesensis\": 1.8,\r\n    \"Urogymnus asperrimus\": 4.5,\r\n    \"Urogymnus granulatus\": 5.0,\r\n    \"Zapteryx exasperata\": 2.4,\r\n    \"Zapteryx xyster\": 2.6\r\n};\r\n\r\n\/\/ ================== DAFTAR SPESIES ELASMOBRANCH (97 SPESIES) ==================\r\nconst elasmobranchSpecies = [\r\n    { scientific: \"Aetobatus flagellum\", common: \"Longheaded eagle ray\" },\r\n    { scientific: \"Aetobatus narinari\", common: \"Spotted eagle ray\" },\r\n    { scientific: \"Aetobatus ocellatus\", common: \"Pacific eagle ray\" },\r\n    { scientific: \"Carcharhinus albimarginatus\", common: \"Silvertip shark\" },\r\n    { scientific: \"Carcharhinus altimus\", common: \"Bignose shark\" },\r\n    { scientific: \"Carcharhinus amblyrhynchoides\", common: \"Graceful shark\" },\r\n    { scientific: \"Carcharhinus amblyrhynchos\", common: \"Grey reef shark\" },\r\n    { scientific: \"Carcharhinus amboinensis\", common: \"Pigeye shark\" },\r\n    { scientific: \"Carcharhinus borneensis\", common: \"Borneo shark\" },\r\n    { scientific: \"Carcharhinus brachyurus\", common: \"Bronze whaler shark \/ Copper shark\" },\r\n    { scientific: \"Carcharhinus brevipinna\", common: \"Spinner shark\" },\r\n    { scientific: \"Carcharhinus cautus\", common: \"Nervous shark\" },\r\n    { scientific: \"Carcharhinus cerdale\", common: \"Pacific smalltail shark\" },\r\n    { scientific: \"Carcharhinus coatesi\", common: \"Coates's shark\" },\r\n    { scientific: \"Carcharhinus dussumieri\", common: \"Whitecheek shark\" },\r\n    { scientific: \"Carcharhinus falciformis\", common: \"Silky shark\" },\r\n    { scientific: \"Carcharhinus fitzroyensis\", common: \"Creek whaler\" },\r\n    { scientific: \"Carcharhinus galapagensis\", common: \"Galapagos shark\" },\r\n    { scientific: \"Carcharhinus hemiodon\", common: \"Pondicherry shark\" },\r\n    { scientific: \"Carcharhinus leucas\", common: \"Bull shark\" },\r\n    { scientific: \"Carcharhinus limbatus\", common: \"Blacktip shark\" },\r\n    { scientific: \"Carcharhinus longimanus\", common: \"Oceanic whitetip shark\" },\r\n    { scientific: \"Carcharhinus melanopterus\", common: \"Blacktip reef shark\" },\r\n    { scientific: \"Carcharhinus obscurus\", common: \"Dusky shark\" },\r\n    { scientific: \"Carcharhinus perezii\", common: \"Caribbean reef shark\" },\r\n    { scientific: \"Carcharhinus plumbeus\", common: \"Sandbar shark\" },\r\n    { scientific: \"Carcharhinus porosus\", common: \"Smalltail shark\" },\r\n    { scientific: \"Carcharhinus sealei\", common: \"Blackspot shark\" },\r\n    { scientific: \"Carcharhinus sorrah\", common: \"Spot-tail shark\" },\r\n    { scientific: \"Carcharhinus tilstoni\", common: \"Australian blacktip shark\" },\r\n    { scientific: \"Carcharhinus tjutjot\", common: \"Indonesian whaler shark\" },\r\n    { scientific: \"Carcharodon carcharias\", common: \"Great white shark\" },\r\n    { scientific: \"Galeocerdo cuvier\", common: \"Tiger shark\" },\r\n    { scientific: \"Ginglymostoma cirratum\", common: \"Nurse shark\" },\r\n    { scientific: \"Gymnura micrura\", common: \"Lesser butterfly ray\" },\r\n    { scientific: \"Heterodontus mexicanus\", common: \"Mexican hornshark\" },\r\n    { scientific: \"Heterodontus portusjacksoni\", common: \"Port Jackson shark\" },\r\n    { scientific: \"Heterodontus quoyi\", common: \"Galapagos bullhead shark\" },\r\n    { scientific: \"Hexanchus griseus\", common: \"Bluntnose sixgill shark\" },\r\n    { scientific: \"Hypanus americanus\", common: \"Southern stingray\" },\r\n    { scientific: \"Hypanus longus\", common: \"Longtail stingray\" },\r\n    { scientific: \"Hypanus sabinus\", common: \"Atlantic stingray\" },\r\n    { scientific: \"Hypanus say\", common: \"Bluntnose stingray\" },\r\n    { scientific: \"Isogomphodon oxyrhynchus\", common: \"Daggernose shark\" },\r\n    { scientific: \"Mobula alfredi\", common: \"Reef manta ray\" },\r\n    { scientific: \"Mobula birostris\", common: \"Giant manta ray\" },\r\n    { scientific: \"Mobula tarapacana\", common: \"Chilean devil ray\" },\r\n    { scientific: \"Mobula thurstoni\", common: \"Smoothtail mobula\" },\r\n    { scientific: \"Mustelus canis\", common: \"Dusky smooth-hound\" },\r\n    { scientific: \"Myliobatis australis\", common: \"Australian eagle ray\" },\r\n    { scientific: \"Myliobatis californica\", common: \"Bat ray\" },\r\n    { scientific: \"Myliobatis freminvillei\", common: \"Bullnose eagle ray\" },\r\n    { scientific: \"Narcine brasiliensis\", common: \"Brazilian electric ray\" },\r\n    { scientific: \"Narcine entemedor\", common: \"Cortez electric ray\" },\r\n    { scientific: \"Nebrius ferrugineus\", common: \"Tawny nurse shark\" },\r\n    { scientific: \"Negaprion acutidens\", common: \"Sicklefin lemon shark\" },\r\n    { scientific: \"Negaprion brevirostris\", common: \"Lemon shark\" },\r\n    { scientific: \"Pateobatis fai\", common: \"Pink whipray\" },\r\n    { scientific: \"Prionace glauca\", common: \"Blue shark\" },\r\n    { scientific: \"Pristis pectinata\", common: \"Smalltooth sawfish\" },\r\n    { scientific: \"Pristis pristis\", common: \"Largetooth sawfish\" },\r\n    { scientific: \"Pseudobatos glaucostigma\", common: \"Speckled guitarfish\" },\r\n    { scientific: \"Pseudobatos lentiginosus\", common: \"Atlantic guitarfish\" },\r\n    { scientific: \"Pseudobatos productus\", common: \"Shovelnose guitarfish\" },\r\n    { scientific: \"Raja undulata\", common: \"Undulate ray\" },\r\n    { scientific: \"Rhincodon typus\", common: \"Whale shark\" },\r\n    { scientific: \"Rhinobatos rhinobatos\", common: \"Common guitarfish\" },\r\n    { scientific: \"Rhinoptera bonasus\", common: \"Cownose ray\" },\r\n    { scientific: \"Rhinoptera steindachneri\", common: \"Pacific cownose ray\" },\r\n    { scientific: \"Rhizoprionodon acutus\", common: \"Milk shark\" },\r\n    { scientific: \"Rhizoprionodon lalandii\", common: \"Brazilian sharpnose shark\" },\r\n    { scientific: \"Rhizoprionodon oligolinx\", common: \"Grey sharpnose shark\" },\r\n    { scientific: \"Rhizoprionodon porosus\", common: \"Caribbean sharpnose shark\" },\r\n    { scientific: \"Rhizoprionodon taylori\", common: \"Australian sharpnose shark\" },\r\n    { scientific: \"Rhizoprionodon terraenovae\", common: \"Atlantic sharpnose shark\" },\r\n    { scientific: \"Rostroraja eglanteria\", common: \"Clearnose skate\" },\r\n    { scientific: \"Sphyrna lewini\", common: \"Scalloped hammerhead\" },\r\n    { scientific: \"Sphyrna mokarran\", common: \"Great hammerhead\" },\r\n    { scientific: \"Sphyrna tiburo\", common: \"Bonnethead shark\" },\r\n    { scientific: \"Sphyrna zygaena\", common: \"Smooth hammerhead\" },\r\n    { scientific: \"Squalus acanthias\", common: \"Spiny dogfish\" },\r\n    { scientific: \"Stegostoma tigrinum\", common: \"Zebra shark\" },\r\n    { scientific: \"Taeniura lymma\", common: \"Bluespotted ribbontail ray\" },\r\n    { scientific: \"Taeniurops meyeni\", common: \"Blotched fantail ray\" },\r\n    { scientific: \"Tetronarce nobiliana\", common: \"Atlantic torpedo ray\" },\r\n    { scientific: \"Torpedo panthera\", common: \"Leopard torpedo ray\" },\r\n    { scientific: \"Triaenodon obesus\", common: \"Whitetip reef shark\" },\r\n    { scientific: \"Urobatis concentricus\", common: \"Reticulated round ray\" },\r\n    { scientific: \"Urobatis halleri\", common: \"Round stingray\" },\r\n    { scientific: \"Urobatis jamaicensis\", common: \"Yellow stingray\" },\r\n    { scientific: \"Urobatis maculatus\", common: \"Spotted round ray\" },\r\n    { scientific: \"Urobatis tumbesensis\", common: \"Tumbes round ray\" },\r\n    { scientific: \"Urogymnus asperrimus\", common: \"Porcupine whipray\" },\r\n    { scientific: \"Urogymnus granulatus\", common: \"Mangrove whipray\" },\r\n    { scientific: \"Zapteryx exasperata\", common: \"Banded guitarfish\" },\r\n    { scientific: \"Zapteryx xyster\", common: \"Southern banded guitarfish\" }\r\n];\r\n\r\n\/\/ ================== FUNGSI UNTUK MENDAPATKAN PANDUAN FOTO BERDASARKAN SPESIES ==================\r\nfunction getPhotoGuidance(speciesScientific) {\r\n    if (!speciesScientific) return { label: \"Foto Sirip Punggung\", guidance: \"Pilih spesies untuk melihat panduan foto.\" };\r\n    \r\n    if (speciesScientific.includes(\"Mobula\") || speciesScientific.toLowerCase().includes(\"manta\")) {\r\n        return { label: \"Foto Bagian Perut (Ventral)\", guidance: \"Totol-totol hitam di perut sebagai identifikasi.\" };\r\n    }\r\n    if (speciesScientific === \"Rhincodon typus\") {\r\n        return { label: \"Foto Samping (Lateral)\", guidance: \"Pola totol di belakang insang sebagai identifikasi.\" };\r\n    }\r\n    const rayGenera = [\"Aetobatus\", \"Gymnura\", \"Hypanus\", \"Mobula\", \"Myliobatis\", \"Narcine\", \"Pateobatis\", \"Raja\", \"Rhinobatos\", \"Rhinoptera\", \"Taeniura\", \"Taeniurops\", \"Tetronarce\", \"Torpedo\", \"Urobatis\", \"Urogymnus\", \"Zapteryx\"];\r\n    if (rayGenera.some(genus => speciesScientific.includes(genus))) {\r\n        return { label: \"Foto Atas (Dorsal)\", guidance: \"Pola warna atau totol di punggung sebagai identifikasi.\" };\r\n    }\r\n    return { label: \"Foto Samping (Lateral)\", guidance: \"Lekukan atau takik pada sirip punggung sebagai identifikasi.\" };\r\n}\r\n\r\n\/\/ ================== FUNGSI REVERSE GEOCODING ==================\r\nasync function fillFromCoordinates(lat, lng) {\r\n    const radii = [0, 5000, 10000, 20000, 50000, 100000];\r\n    let provinceFound = false;\r\n    let provinceName = \"\";\r\n    let responseData = null;\r\n\r\n    for (let radius of radii) {\r\n        try {\r\n            const url = `https:\/\/api.bigdatacloud.net\/data\/reverse-geocode-client?latitude=${lat}&longitude=${lng}&localityLanguage=id&radius=${radius}`;\r\n            const response = await fetch(url);\r\n            const data = await response.json();\r\n\r\n            if (!data || !data.localityInfo) continue;\r\n\r\n            data.localityInfo.administrative.forEach(admin => {\r\n                if (admin.adminLevel === 4) {\r\n                    provinceName = admin.name;\r\n                }\r\n            });\r\n\r\n            if (provinceName) {\r\n                responseData = data;\r\n                provinceFound = true;\r\n                break;\r\n            }\r\n        } catch (err) {\r\n            console.error(`Gagal dengan radius ${radius}:`, err);\r\n        }\r\n    }\r\n\r\n    if (!provinceFound) return;\r\n\r\n    const provinceMapping = {\r\n        \"Jakarta\": \"DKI Jakarta\",\r\n        \"Daerah Khusus Ibukota Jakarta\": \"DKI Jakarta\",\r\n        \"Yogyakarta\": \"DI Yogyakarta\",\r\n        \"Daerah Istimewa Yogyakarta\": \"DI Yogyakarta\",\r\n        \"Bali Province\": \"Bali\",\r\n        \"West Java\": \"Jawa Barat\",\r\n        \"Central Java\": \"Jawa Tengah\",\r\n        \"East Java\": \"Jawa Timur\",\r\n        \"West Nusa Tenggara\": \"Nusa Tenggara Barat\",\r\n        \"East Nusa Tenggara\": \"Nusa Tenggara Timur\",\r\n        \"North Sumatra\": \"Sumatera Utara\",\r\n        \"West Sumatra\": \"Sumatera Barat\",\r\n        \"South Sumatra\": \"Sumatera Selatan\",\r\n        \"Bangka Belitung Islands\": \"Bangka Belitung\",\r\n        \"Riau Islands\": \"Kepulauan Riau\",\r\n        \"Lampung\": \"Lampung\"\r\n    };\r\n\r\n    if (provinceMapping[provinceName]) {\r\n        provinceName = provinceMapping[provinceName];\r\n    }\r\n\r\n    const provinceSelect = document.getElementById(\"provinceSelect\");\r\n    const citySelect = document.getElementById(\"districtSelect\");\r\n\r\n    let provinceMatched = false;\r\n    Array.from(provinceSelect.options).forEach(opt => {\r\n        if (opt.text.trim().toLowerCase() === provinceName.trim().toLowerCase()) {\r\n            provinceSelect.value = opt.value;\r\n            provinceMatched = true;\r\n        }\r\n    });\r\n\r\n    if (!provinceMatched) return;\r\n\r\n    provinceSelect.dispatchEvent(new Event('change'));\r\n\r\n    setTimeout(() => {\r\n        const selectedProv = provinceSelect.value;\r\n        if (!selectedProv || !indonesiaRegions[selectedProv]) return;\r\n\r\n        const possibleCities = indonesiaRegions[selectedProv];\r\n        let cityFound = \"\";\r\n\r\n        responseData.localityInfo.administrative.forEach(admin => {\r\n            possibleCities.forEach(city => {\r\n                const cleanApi = admin.name.toLowerCase()\r\n                    .replace(\"kota \", \"\")\r\n                    .replace(\"kabupaten \", \"\")\r\n                    .trim();\r\n                const cleanLocal = city.toLowerCase()\r\n                    .replace(\"kota \", \"\")\r\n                    .replace(\"kabupaten \", \"\")\r\n                    .trim();\r\n                if (cleanApi === cleanLocal) {\r\n                    cityFound = city;\r\n                }\r\n            });\r\n        });\r\n\r\n        if (cityFound) {\r\n            Array.from(citySelect.options).forEach(opt => {\r\n                if (opt.text === cityFound) {\r\n                    citySelect.value = opt.value;\r\n                }\r\n            });\r\n        }\r\n    }, 600);\r\n}\r\n\r\n\/\/ ================== HANDLE SUBMIT (event delegation) ==================\r\nasync function handleElasmobranchFormSubmit(e) {\r\n    e.preventDefault();\r\n    console.log('\ud83d\ude80 Submit triggered via delegation');\r\n\r\n    const form = document.getElementById('elasmobranchForm');\r\n    if (!form) {\r\n        console.error('Form tidak ditemukan');\r\n        return;\r\n    }\r\n\r\n    const submitBtn = document.getElementById('submitBtn');\r\n    const originalText = submitBtn.innerText;\r\n\r\n    submitBtn.disabled = true;\r\n    submitBtn.innerText = 'Menyimpan...';\r\n    submitBtn.classList.add('loading');\r\n\r\n    try {\r\n        const dorsalHidden = document.getElementById('dorsalPhotoData');\r\n        if (!dorsalHidden?.value) {\r\n            throw new Error('Foto sirip punggung wajib diisi. Crop terlebih dahulu.');\r\n        }\r\n\r\n        const species = form.querySelector('select[name=\"species\"]')?.value;\r\n        const surveyDate = form.querySelector('input[name=\"survey_date\"]')?.value;\r\n\r\n        if (!species) {\r\n            throw new Error('Pilih spesies terlebih dahulu.');\r\n        }\r\n        if (!surveyDate) {\r\n            throw new Error('Tanggal survey harus diisi.');\r\n        }\r\n\r\n        const newNameField = document.getElementById('newNameField');\r\n        const newIndividualName = document.getElementById('newIndividualName');\r\n        const individualId = document.getElementById('individualId');\r\n\r\n        if (newNameField && newNameField.style.display === 'block') {\r\n            const newName = newIndividualName.value.trim();\r\n            if (!newName) {\r\n                throw new Error('Harap beri nama untuk individu baru.');\r\n            }\r\n            const existing = allData.find(item => item.individual_name && item.individual_name.toLowerCase() === newName.toLowerCase());\r\n            if (existing) {\r\n                throw new Error(`Nama \"${newName}\" sudah digunakan oleh individu lain (ID: ${existing.individual_id}). Gunakan nama lain.`);\r\n            }\r\n        }\r\n\r\n        const formData = new FormData(form);\r\n\r\n        formData.delete('dorsal_photo');\r\n        formData.append('dorsal_photo_base64', dorsalHidden.value);\r\n\r\n        const leftHidden = document.getElementById('leftPhotoData');\r\n        if (leftHidden?.value) formData.append('left_photo_base64', leftHidden.value);\r\n\r\n        const rightHidden = document.getElementById('rightPhotoData');\r\n        if (rightHidden?.value) formData.append('right_photo_base64', rightHidden.value);\r\n\r\n        const ventralHidden = document.getElementById('ventralPhotoData');\r\n        if (ventralHidden?.value) formData.append('ventral_photo_base64', ventralHidden.value);\r\n\r\n        const scaleHidden = document.getElementById('scalePhotoData');\r\n        if (scaleHidden?.value) formData.append('scale_photo_base64', scaleHidden.value);\r\n\r\n        if (newNameField && newNameField.style.display === 'block') {\r\n            formData.append('individual_name', newIndividualName.value.trim());\r\n            console.log('\u2705 individual_name ditambahkan ke formData:', newIndividualName.value.trim());\r\n        }\r\n\r\n        const controller = new AbortController();\r\n        const timeoutId = setTimeout(() => controller.abort(), 30000);\r\n\r\n        const response = await fetch(`\/wp-content\/themes\/${themeFolder}\/elasmobranch-api\/elasmobranch-save.php`, {\r\n            method: 'POST',\r\n            body: formData,\r\n            signal: controller.signal\r\n        });\r\n        clearTimeout(timeoutId);\r\n\r\n        if (!response.ok) {\r\n            const text = await response.text();\r\n            throw new Error(`HTTP ${response.status}: ${text.substring(0,100)}`);\r\n        }\r\n\r\n        const result = await response.json();\r\n        if (result.success) {\r\n            alert('\u2705 Data berhasil disimpan!');\r\n            window.location.reload();\r\n        } else {\r\n            alert('\u274c Gagal menyimpan: ' + result.message);\r\n        }\r\n    } catch (err) {\r\n        console.error('\ud83d\udd25 Submit error:', err);\r\n        if (err.name === 'AbortError') {\r\n            alert('\u23f1\ufe0f Waktu habis. Server terlalu lama merespons.');\r\n        } else {\r\n            alert('\u274c Error: ' + err.message);\r\n        }\r\n    } finally {\r\n        submitBtn.disabled = false;\r\n        submitBtn.innerText = originalText;\r\n        submitBtn.classList.remove('loading');\r\n    }\r\n}\r\n\r\ndocument.addEventListener('submit', function(e) {\r\n    if (e.target && e.target.id === 'elasmobranchForm') {\r\n        handleElasmobranchFormSubmit(e).catch(err => console.error('Unhandled error in submit:', err));\r\n    }\r\n});\r\n\r\n\/\/ ================== INISIALISASI DOKUMEN ==================\r\ndocument.addEventListener('DOMContentLoaded', function() {\r\n    const currentUrl = encodeURIComponent(window.location.href);\r\n    document.getElementById('loginLink').href = `\/wp-login.php?redirect_to=${currentUrl}`;\r\n    document.getElementById('registerLink').href = `\/wp-login.php?action=register&redirect_to=${currentUrl}`;\r\n\r\n    fetch(`\/wp-content\/themes\/${themeFolder}\/elasmobranch-api\/elasmobranch-ajax.php`)\r\n        .then(response => response.json())\r\n        .then(data => {\r\n            currentUser = data.logged_in ? data.username : null;\r\n            isSuperAdmin = (currentUser === 'ckradiwijaya@gmail.com');\r\n\r\n            const formSection = document.getElementById('formSection');\r\n            const loginPrompt = document.getElementById('loginPrompt');\r\n\r\n            if (data.logged_in) {\r\n                loginPrompt.style.display = 'none';\r\n                formSection.style.display = 'block';\r\n                renderElasmobranchForm(data.username);\r\n            } else {\r\n                loginPrompt.style.display = 'block';\r\n                formSection.style.display = 'none';\r\n            }\r\n        })\r\n        .catch(err => {\r\n            console.error('Gagal cek login', err);\r\n            document.getElementById('loginPrompt').innerHTML = '\u274c Gagal cek login. Pastikan file elasmobranch-ajax.php tersedia.';\r\n        });\r\n\r\n    var map = L.map('map').setView([-2.5, 118], 5);\r\n    L.tileLayer('https:\/\/server.arcgisonline.com\/ArcGIS\/rest\/services\/World_Imagery\/MapServer\/tile\/{z}\/{y}\/{x}', { attribution: 'Tiles &copy; Esri' }).addTo(map);\r\n    var markersCluster = L.markerClusterGroup();\r\n    window.map = map;\r\n    window.markersCluster = markersCluster;\r\n\r\n    function renderElasmobranchForm(username) {\r\n        const formSection = document.getElementById('formSection');\r\n        let speciesOptions = '<option value=\"\">-- Pilih Spesies --<\/option>';\r\n        elasmobranchSpecies.forEach(sp => {\r\n            speciesOptions += `<option value=\"${sp.scientific}\">${sp.common} (${sp.scientific})<\/option>`;\r\n        });\r\n\r\n        formSection.innerHTML = `\r\n            <h3 style=\"margin-top:0;\">\ud83e\udd88 Tambah Data Elasmobranch Baru<\/h3>\r\n            <form id=\"elasmobranchForm\" enctype=\"multipart\/form-data\">\r\n                <div style=\"margin-bottom:12px;\">\r\n                    <label style=\"display:block; font-weight:bold; margin-bottom:5px;\">Nama Lokasi Penemuan<\/label>\r\n                    <input type=\"text\" name=\"location_name\" placeholder=\"Misal: Pelabuhan Muncar\" style=\"width:100%; padding:8px; border:1px solid #ccc; border-radius:5px;\">\r\n                <\/div>\r\n\r\n                <div style=\"margin-bottom:12px;\">\r\n                    <label style=\"display:block; font-weight:bold; margin-bottom:5px;\">Pilih Titik Koordinat (klik peta)<\/label>\r\n                    <div id=\"mini-map\" style=\"height:200px; border-radius:5px; margin-bottom:5px;\"><\/div>\r\n                    <div style=\"display:flex; gap:5px;\">\r\n                        <input type=\"number\" step=\"any\" id=\"lat\" name=\"latitude\" required placeholder=\"Latitude\" style=\"flex:1; padding:8px; border:1px solid #ccc; border-radius:5px;\">\r\n                        <input type=\"number\" step=\"any\" id=\"lng\" name=\"longitude\" required placeholder=\"Longitude\" style=\"flex:1; padding:8px; border:1px solid #ccc; border-radius:5px;\">\r\n                    <\/div>\r\n                <\/div>\r\n\r\n                <div style=\"display:grid; grid-template-columns:1fr 1fr; gap:10px; margin-bottom:10px;\">\r\n                    <div>\r\n                        <select name=\"province\" id=\"provinceSelect\" style=\"width:100%; padding:8px;\">\r\n                            <option value=\"\">Pilih Provinsi<\/option>\r\n                        <\/select>\r\n                    <\/div>\r\n                    <div>\r\n                        <select name=\"district\" id=\"districtSelect\" style=\"width:100%; padding:8px;\">\r\n                            <option value=\"\">Pilih Kab\/Kota<\/option>\r\n                        <\/select>\r\n                    <\/div>\r\n                <\/div>\r\n\r\n                <div style=\"display:grid; grid-template-columns:1fr 1fr; gap:10px; margin-bottom:10px;\">\r\n                    <div><input type=\"date\" name=\"survey_date\" style=\"width:100%; padding:8px;\"><\/div>\r\n                    <div><input type=\"time\" name=\"survey_time\" style=\"width:100%; padding:8px;\"><\/div>\r\n                <\/div>\r\n\r\n                <div style=\"margin-bottom:10px;\">\r\n                    <label style=\"display:block; font-weight:bold; margin-bottom:5px;\">Spesies<\/label>\r\n                    <select name=\"species\" id=\"speciesSelect\" style=\"width:100%; padding:8px;\">\r\n                        ${speciesOptions}\r\n                    <\/select>\r\n                <\/div>\r\n\r\n                <div id=\"photoGuidanceBox\" style=\"margin-bottom:15px; padding:10px; background:#e8f0fe; border-radius:5px; border-left:4px solid #0284c7;\">\r\n                    <strong id=\"photoLabel\">Foto Sirip Punggung<\/strong><br>\r\n                    <span id=\"photoGuidanceText\">Pilih spesies untuk melihat panduan foto.<\/span>\r\n                <\/div>\r\n\r\n                <div style=\"margin-bottom:15px;\">\r\n                    <label style=\"display:block; font-weight:bold; margin-bottom:5px; color:#d32f2f;\" id=\"dorsalPhotoLabel\">Foto Sirip Punggung (wajib) *<\/label>\r\n                    <input type=\"file\" id=\"dorsalPhotoInput\" accept=\"image\/*\" required style=\"width:100%; padding:5px;\">\r\n                    <input type=\"hidden\" name=\"dorsal_photo\" id=\"dorsalPhotoData\">\r\n                <\/div>\r\n\r\n                <div style=\"margin-bottom:15px;\">\r\n                    <label style=\"display:block; font-weight:bold; margin-bottom:5px;\">Foto Sisi Kiri (opsional)<\/label>\r\n                    <input type=\"file\" id=\"leftPhotoInput\" accept=\"image\/*\" style=\"width:100%; padding:5px;\">\r\n                    <input type=\"hidden\" name=\"left_photo\" id=\"leftPhotoData\">\r\n                <\/div>\r\n\r\n                <div style=\"margin-bottom:15px;\">\r\n                    <label style=\"display:block; font-weight:bold; margin-bottom:5px;\">Foto Sisi Kanan (opsional)<\/label>\r\n                    <input type=\"file\" id=\"rightPhotoInput\" accept=\"image\/*\" style=\"width:100%; padding:5px;\">\r\n                    <input type=\"hidden\" name=\"right_photo\" id=\"rightPhotoData\">\r\n                <\/div>\r\n\r\n                <div style=\"margin-bottom:15px;\">\r\n                    <label style=\"display:block; font-weight:bold; margin-bottom:5px;\">Foto Bagian Perut (opsional, untuk pari)<\/label>\r\n                    <input type=\"file\" id=\"ventralPhotoInput\" accept=\"image\/*\" style=\"width:100%; padding:5px;\">\r\n                    <input type=\"hidden\" name=\"ventral_photo\" id=\"ventralPhotoData\">\r\n                <\/div>\r\n\r\n                <div style=\"margin-bottom:15px;\">\r\n                    <label style=\"display:block; font-weight:bold; margin-bottom:5px;\">Foto dengan Skala (opsional, untuk kalibrasi ukuran)<\/label>\r\n                    <input type=\"file\" id=\"scalePhotoInput\" accept=\"image\/*\" style=\"width:100%; padding:5px;\">\r\n                    <input type=\"hidden\" name=\"scale_photo\" id=\"scalePhotoData\">\r\n                    <div style=\"display:grid; grid-template-columns:1fr 1fr; gap:10px; margin-top:10px;\">\r\n                        <div><input type=\"number\" step=\"0.1\" name=\"total_length\" id=\"totalLengthInput\" placeholder=\"Panjang Total (cm)\"><\/div>\r\n                        <div><input type=\"number\" step=\"0.1\" name=\"disc_width\" id=\"discWidthInput\" placeholder=\"Lebar Cakram (cm)\"><\/div>\r\n                    <\/div>\r\n                <\/div>\r\n\r\n                <div style=\"margin-bottom:15px;\">\r\n                    <label>Jenis Kelamin<\/label>\r\n                    <select name=\"sex\" style=\"width:100%; padding:8px;\">\r\n                        <option value=\"\">-- Pilih --<\/option>\r\n                        <option value=\"Jantan\">Jantan<\/option>\r\n                        <option value=\"Betina\">Betina<\/option>\r\n                        <option value=\"Tidak tahu\">Tidak tahu<\/option>\r\n                    <\/select>\r\n                <\/div>\r\n\r\n                <div style=\"margin-bottom:15px;\">\r\n                    <label>Kedalaman (meter)<\/label>\r\n                    <input type=\"number\" step=\"0.1\" name=\"depth\" style=\"width:100%; padding:8px;\">\r\n                <\/div>\r\n\r\n                <div style=\"margin-bottom:15px;\">\r\n                    <label>Behaviour \/ Aktivitas<\/label>\r\n                    <textarea name=\"behaviour\" rows=\"2\" style=\"width:100%; padding:8px;\"><\/textarea>\r\n                <\/div>\r\n\r\n                <div style=\"margin-bottom:15px;\">\r\n                    <label>Kondisi Kesehatan<\/label>\r\n                    <select name=\"health\" id=\"healthSelect\" style=\"width:100%; padding:8px;\">\r\n                        <option value=\"\">-- Pilih --<\/option>\r\n                        <option value=\"Baik\">Baik<\/option>\r\n                        <option value=\"Sakit\">Sakit<\/option>\r\n                        <option value=\"Mati\">Mati<\/option>\r\n                    <\/select>\r\n                    <div id=\"healthNotes\" style=\"display:none; margin-top:10px;\">\r\n                        <textarea name=\"health_notes\" placeholder=\"Keterangan sakit\" rows=\"2\" style=\"width:100%; padding:8px;\"><\/textarea>\r\n                    <\/div>\r\n                <\/div>\r\n\r\n                <input type=\"hidden\" name=\"individual_id\" id=\"individualId\" value=\"\">\r\n\r\n                <div id=\"analysisResult\" style=\"margin-bottom:15px; padding:10px; background:#e6f7ff; border-radius:5px; display:none;\">\r\n                    <p id=\"analysisMessage\"><\/p>\r\n                    <div id=\"matchInfo\" style=\"display:none;\">\r\n                        <p><strong>Individu yang mirip ditemukan:<\/strong><\/p>\r\n                        <div id=\"matchDetails\"><\/div>\r\n                        <div style=\"margin-top:10px;\">\r\n                            <button type=\"button\" id=\"useExistingBtn\" style=\"background:#2e7d32; color:white; border:none; padding:8px 15px; border-radius:5px; cursor:pointer;\">\u2705 Gunakan Individu Ini<\/button>\r\n                            <button type=\"button\" id=\"newIndividualBtn\" style=\"background:#0284c7; color:white; border:none; padding:8px 15px; border-radius:5px; cursor:pointer;\">\ud83c\udd95 Simpan Sebagai Individu Baru<\/button>\r\n                        <\/div>\r\n                    <\/div>\r\n                    <div id=\"newNameField\" style=\"display:none; margin-top:10px;\">\r\n                        <label>Nama untuk individu baru:<\/label>\r\n                        <input type=\"text\" id=\"newIndividualName\" style=\"width:100%; padding:8px; margin-top:5px;\">\r\n                    <\/div>\r\n                <\/div>\r\n\r\n                <button type=\"submit\" id=\"submitBtn\" disabled style=\"background:#0284c7; color:white; border:none; padding:12px; border-radius:5px; cursor:pointer; width:100%; font-size:16px;\">\ud83d\udcbe Simpan Data<\/button>\r\n            <\/form>\r\n            <p style=\"font-size:12px; color:#666; margin-top:15px;\">*Field wajib diisi. Klik foto untuk crop.<\/p>\r\n        `;\r\n\r\n        initRegionDropdowns();\r\n        initMiniMap();\r\n        initElasmobranchForm();\r\n    }\r\n\r\n    function initMiniMap() {\r\n        try {\r\n            var miniMap = L.map('mini-map').setView([-2.5, 118], 5);\r\n            L.tileLayer('https:\/\/server.arcgisonline.com\/ArcGIS\/rest\/services\/World_Imagery\/MapServer\/tile\/{z}\/{y}\/{x}', { attribution: 'Tiles &copy; Esri' }).addTo(miniMap);\r\n            var marker;\r\n\r\n            function updateMarkerFromInput() {\r\n                var lat = parseFloat(document.getElementById('lat').value);\r\n                var lng = parseFloat(document.getElementById('lng').value);\r\n                if (!isNaN(lat) && !isNaN(lng)) {\r\n                    if (marker) miniMap.removeLayer(marker);\r\n                    marker = L.marker([lat, lng]).addTo(miniMap);\r\n                    miniMap.setView([lat, lng], 10);\r\n                    fillFromCoordinates(lat, lng).catch(e => console.error(e));\r\n                } else {\r\n                    if (marker) miniMap.removeLayer(marker);\r\n                    marker = null;\r\n                }\r\n            }\r\n\r\n            miniMap.on('click', function(e) {\r\n                var lat = e.latlng.lat.toFixed(6);\r\n                var lng = e.latlng.lng.toFixed(6);\r\n                document.getElementById('lat').value = lat;\r\n                document.getElementById('lng').value = lng;\r\n                updateMarkerFromInput();\r\n            });\r\n\r\n            document.getElementById('lat').addEventListener('input', updateMarkerFromInput);\r\n            document.getElementById('lng').addEventListener('input', updateMarkerFromInput);\r\n            updateMarkerFromInput();\r\n        } catch (e) {\r\n            console.error('Error di initMiniMap:', e);\r\n        }\r\n    }\r\n\r\n    function initRegionDropdowns() {\r\n        try {\r\n            const provinceSelect = document.getElementById('provinceSelect');\r\n            const districtSelect = document.getElementById('districtSelect');\r\n            if (!provinceSelect || !districtSelect) return;\r\n\r\n            provinceSelect.innerHTML = '<option value=\"\">Pilih Provinsi<\/option>';\r\n            districtSelect.innerHTML = '<option value=\"\">Pilih Kab\/Kota<\/option>';\r\n\r\n            const provinces = Object.keys(indonesiaRegions).sort();\r\n            provinces.forEach(prov => {\r\n                const option = document.createElement('option');\r\n                option.value = prov;\r\n                option.textContent = prov;\r\n                provinceSelect.appendChild(option);\r\n            });\r\n\r\n            provinceSelect.addEventListener('change', function() {\r\n                const selectedProv = this.value;\r\n                districtSelect.innerHTML = '<option value=\"\">Pilih Kab\/Kota<\/option>';\r\n                if (selectedProv && indonesiaRegions[selectedProv]) {\r\n                    const cities = indonesiaRegions[selectedProv].sort();\r\n                    cities.forEach(city => {\r\n                        const option = document.createElement('option');\r\n                        option.value = city;\r\n                        option.textContent = city;\r\n                        districtSelect.appendChild(option);\r\n                    });\r\n                }\r\n            });\r\n        } catch (e) {\r\n            console.error('Error di initRegionDropdowns:', e);\r\n        }\r\n    }\r\n\r\n    function initElasmobranchForm() {\r\n        const healthSelect = document.getElementById('healthSelect');\r\n        const healthNotesDiv = document.getElementById('healthNotes');\r\n        const analysisResult = document.getElementById('analysisResult');\r\n        const analysisMessage = document.getElementById('analysisMessage');\r\n        const matchInfo = document.getElementById('matchInfo');\r\n        const matchDetails = document.getElementById('matchDetails');\r\n        const useExistingBtn = document.getElementById('useExistingBtn');\r\n        const newIndividualBtn = document.getElementById('newIndividualBtn');\r\n        const newNameField = document.getElementById('newNameField');\r\n        const newIndividualName = document.getElementById('newIndividualName');\r\n        const submitBtn = document.getElementById('submitBtn');\r\n        const individualId = document.getElementById('individualId');\r\n        const dorsalInput = document.getElementById('dorsalPhotoInput');\r\n        const dorsalHidden = document.getElementById('dorsalPhotoData');\r\n        const leftInput = document.getElementById('leftPhotoInput');\r\n        const leftHidden = document.getElementById('leftPhotoData');\r\n        const rightInput = document.getElementById('rightPhotoInput');\r\n        const rightHidden = document.getElementById('rightPhotoData');\r\n        const ventralInput = document.getElementById('ventralPhotoInput');\r\n        const ventralHidden = document.getElementById('ventralPhotoData');\r\n        const scaleInput = document.getElementById('scalePhotoInput');\r\n        const scaleHidden = document.getElementById('scalePhotoData');\r\n        const totalLengthInput = document.getElementById('totalLengthInput');\r\n        const discWidthInput = document.getElementById('discWidthInput');\r\n        const speciesSelect = document.getElementById('speciesSelect');\r\n        const photoLabel = document.getElementById('photoLabel');\r\n        const photoGuidanceText = document.getElementById('photoGuidanceText');\r\n        const dorsalPhotoLabel = document.getElementById('dorsalPhotoLabel');\r\n\r\n        let calibrationState = {\r\n            image: null,\r\n            canvas: null,\r\n            ctx: null,\r\n            scale: 0,\r\n            knownLength: 0,\r\n            drawing: false,\r\n            startX: 0,\r\n            startY: 0,\r\n            currentX: 0,\r\n            currentY: 0,\r\n            mode: 'calibrate',\r\n            measuredLength: 0,\r\n            measuredWidth: 0\r\n        };\r\n\r\n        speciesSelect.addEventListener('change', function() {\r\n            const species = this.value;\r\n            const guidance = getPhotoGuidance(species);\r\n            photoLabel.textContent = guidance.label;\r\n            photoGuidanceText.textContent = guidance.guidance;\r\n            dorsalPhotoLabel.textContent = guidance.label + ' (wajib) *';\r\n        });\r\n\r\n        healthSelect.addEventListener('change', function() {\r\n            healthNotesDiv.style.display = (this.value === 'Sakit') ? 'block' : 'none';\r\n        });\r\n\r\n        const modalHTML = `\r\n        <div id=\"cropModal\" style=\"display:none; position:fixed; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,0.8); z-index:10000; justify-content:center; align-items:center; flex-direction:column;\">\r\n            <div style=\"background:#fff; padding:20px; border-radius:10px; width:90%; max-width:800px;\">\r\n                <div style=\"display:flex; justify-content:space-between; align-items:center;\">\r\n                    <h3 style=\"margin:0;\">Crop Foto<\/h3>\r\n                    <button id=\"closeCropModal\" style=\"background:none; border:none; font-size:24px; cursor:pointer;\">&times;<\/button>\r\n                <\/div>\r\n                <div style=\"margin:20px 0;\">\r\n                    <img decoding=\"async\" id=\"cropImage\" src=\"\" style=\"max-width:100%; max-height:70vh;\">\r\n                <\/div>\r\n                <div style=\"display:flex; justify-content:flex-end; gap:10px;\">\r\n                    <button id=\"cropButton\" style=\"background:#0284c7; color:white; border:none; padding:8px 15px; border-radius:5px; cursor:pointer;\">Crop<\/button>\r\n                    <button id=\"cancelCrop\" style=\"background:#6c757d; color:white; border:none; padding:8px 15px; border-radius:5px; cursor:pointer;\">Batal<\/button>\r\n                <\/div>\r\n            <\/div>\r\n        <\/div>\r\n        `;\r\n        document.body.insertAdjacentHTML('beforeend', modalHTML);\r\n\r\n        let cropper;\r\n        let currentInput = null;\r\n        const cropModal = document.getElementById('cropModal');\r\n        const cropImage = document.getElementById('cropImage');\r\n        const cropButton = document.getElementById('cropButton');\r\n        const closeCropModal = document.getElementById('closeCropModal');\r\n        const cancelCrop = document.getElementById('cancelCrop');\r\n\r\n        function showCropModal() { cropModal.style.display = 'flex'; }\r\n        function hideCropModal() {\r\n            cropModal.style.display = 'none';\r\n            if (cropper) { cropper.destroy(); cropper = null; }\r\n        }\r\n\r\n        closeCropModal.addEventListener('click', hideCropModal);\r\n        cancelCrop.addEventListener('click', hideCropModal);\r\n\r\n        function compressImage(file, quality = 0.85) {\r\n            return new Promise((resolve, reject) => {\r\n                const reader = new FileReader();\r\n                reader.onload = function(e) {\r\n                    const img = new Image();\r\n                    img.onload = function() {\r\n                        const canvas = document.createElement('canvas');\r\n                        canvas.width = img.width;\r\n                        canvas.height = img.height;\r\n                        const ctx = canvas.getContext('2d');\r\n                        ctx.drawImage(img, 0, 0);\r\n                        canvas.toBlob(function(blob) {\r\n                            if (!blob) {\r\n                                reject(new Error('Gagal mengompresi gambar'));\r\n                                return;\r\n                            }\r\n                            const compressedFile = new File([blob], file.name, { type: 'image\/jpeg' });\r\n                            resolve(compressedFile);\r\n                        }, 'image\/jpeg', quality);\r\n                    };\r\n                    img.onerror = reject;\r\n                    img.src = e.target.result;\r\n                };\r\n                reader.onerror = reject;\r\n                reader.readAsDataURL(file);\r\n            });\r\n        }\r\n\r\n        async function handleCroppedFile(blob, inputElement, hiddenElement, side) {\r\n            try {\r\n                console.log(`\ud83d\udcf8 Memproses crop untuk ${side}`);\r\n                const file = new File([blob], 'cropped.jpg', { type: 'image\/jpeg' });\r\n                const quality = 0.85;\r\n                const compressedFile = await compressImage(file, quality);\r\n                \r\n                const reader = new FileReader();\r\n                reader.onload = function(e) {\r\n                    hiddenElement.value = e.target.result;\r\n                    console.log(`\u2705 ${side} hidden value length:`, e.target.result.length);\r\n                    \r\n                    if (side === 'dorsal') {\r\n                        analyzePhotos();\r\n                    }\r\n                };\r\n                reader.readAsDataURL(compressedFile);\r\n            } catch (err) {\r\n                console.error(`\u274c Gagal memproses ${side}:`, err);\r\n                alert(`Gagal memproses foto ${side}. Coba lagi.`);\r\n            }\r\n        }\r\n\r\n        dorsalInput.addEventListener('change', function() {\r\n            currentInput = this;\r\n            const file = this.files[0];\r\n            if (file) {\r\n                const reader = new FileReader();\r\n                reader.onload = function(e) {\r\n                    cropImage.src = e.target.result;\r\n                    showCropModal();\r\n                    if (cropper) cropper.destroy();\r\n                    cropper = new Cropper(cropImage, { aspectRatio: NaN, viewMode: 1, autoCropArea: 1 });\r\n                };\r\n                reader.readAsDataURL(file);\r\n            }\r\n        });\r\n\r\n        leftInput.addEventListener('change', function() {\r\n            currentInput = this;\r\n            const file = this.files[0];\r\n            if (file) {\r\n                const reader = new FileReader();\r\n                reader.onload = function(e) {\r\n                    cropImage.src = e.target.result;\r\n                    showCropModal();\r\n                    if (cropper) cropper.destroy();\r\n                    cropper = new Cropper(cropImage, { aspectRatio: NaN, viewMode: 1, autoCropArea: 1 });\r\n                };\r\n                reader.readAsDataURL(file);\r\n            }\r\n        });\r\n\r\n        rightInput.addEventListener('change', function() {\r\n            currentInput = this;\r\n            const file = this.files[0];\r\n            if (file) {\r\n                const reader = new FileReader();\r\n                reader.onload = function(e) {\r\n                    cropImage.src = e.target.result;\r\n                    showCropModal();\r\n                    if (cropper) cropper.destroy();\r\n                    cropper = new Cropper(cropImage, { aspectRatio: NaN, viewMode: 1, autoCropArea: 1 });\r\n                };\r\n                reader.readAsDataURL(file);\r\n            }\r\n        });\r\n\r\n        ventralInput.addEventListener('change', function() {\r\n            currentInput = this;\r\n            const file = this.files[0];\r\n            if (file) {\r\n                const reader = new FileReader();\r\n                reader.onload = function(e) {\r\n                    cropImage.src = e.target.result;\r\n                    showCropModal();\r\n                    if (cropper) cropper.destroy();\r\n                    cropper = new Cropper(cropImage, { aspectRatio: NaN, viewMode: 1, autoCropArea: 1 });\r\n                };\r\n                reader.readAsDataURL(file);\r\n            }\r\n        });\r\n\r\n        cropButton.addEventListener('click', function() {\r\n            if (cropper) {\r\n                const canvas = cropper.getCroppedCanvas();\r\n                canvas.toBlob(function(blob) {\r\n                    hideCropModal();\r\n                    let hidden, side;\r\n                    if (currentInput === dorsalInput) {\r\n                        hidden = dorsalHidden;\r\n                        side = 'dorsal';\r\n                    } else if (currentInput === leftInput) {\r\n                        hidden = leftHidden;\r\n                        side = 'left';\r\n                    } else if (currentInput === rightInput) {\r\n                        hidden = rightHidden;\r\n                        side = 'right';\r\n                    } else if (currentInput === ventralInput) {\r\n                        hidden = ventralHidden;\r\n                        side = 'ventral';\r\n                    } else {\r\n                        hidden = scaleHidden;\r\n                        side = 'scale';\r\n                    }\r\n                    handleCroppedFile(blob, currentInput, hidden, side);\r\n                }, 'image\/jpeg');\r\n            }\r\n        });\r\n\r\n        \/\/ ========== MODAL KALIBRASI YANG DIPERBAIKI ==========\r\n        const calibrationModalHTML = `\r\n        <div id=\"calibrationModal\" style=\"display:none; position:fixed; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,0.9); z-index:10001; justify-content:center; align-items:center;\">\r\n            <div style=\"background:#fff; padding:20px; border-radius:10px; width:95%; max-width:800px; max-height:90vh; overflow-y:auto;\">\r\n                <div style=\"display:flex; justify-content:space-between; align-items:center; margin-bottom:15px;\">\r\n                    <h3 style=\"margin:0;\">\ud83d\udccf Kalibrasi & Pengukuran<\/h3>\r\n                    <button id=\"closeCalibrationModal\" style=\"background:none; border:none; font-size:24px; cursor:pointer;\">&times;<\/button>\r\n                <\/div>\r\n                \r\n                <canvas id=\"calibrationCanvas\" style=\"width:100%; height:300px; border:2px solid #0284c7; background:#f0f0f0; cursor:crosshair;\"><\/canvas>\r\n                \r\n                <div id=\"calibrationStep1\" style=\"margin-top:15px;\">\r\n                    <p><strong>Langkah 1:<\/strong> Tarik garis pada MATA ikan (dua titik) sebagai referensi ukuran.<\/p>\r\n                    <div style=\"display:flex; gap:10px; align-items:center; margin-bottom:10px; flex-wrap:wrap;\">\r\n                        <span>Ukuran mata (cm):<\/span>\r\n                        <input type=\"number\" id=\"eyeSizeInput\" step=\"0.1\" value=\"\" style=\"width:80px; padding:8px; border:1px solid #ccc; border-radius:5px;\" readonly>\r\n                        <button id=\"calibrateBtn\" style=\"background:#0284c7; color:white; border:none; padding:8px 15px; border-radius:5px; cursor:pointer;\">Kalibrasi<\/button>\r\n                    <\/div>\r\n                    <p id=\"calibrationResult\" style=\"margin-top:10px; font-weight:bold; color:#0284c7; min-height:24px;\"><\/p>\r\n                <\/div>\r\n                \r\n                <div id=\"calibrationStep2\" style=\"display:none; margin-top:15px;\">\r\n                    <p><strong>Langkah 2:<\/strong> Ukur panjang total dan lebar cakram.<\/p>\r\n                    <div style=\"display:flex; gap:10px; margin-bottom:15px; flex-wrap:wrap;\">\r\n                        <button id=\"measureLengthBtn\" style=\"background:#2e7d32; color:white; border:none; padding:8px 15px; border-radius:5px; cursor:pointer;\">\ud83d\udccf Ukur Panjang Total<\/button>\r\n                        <button id=\"measureWidthBtn\" style=\"background:#2e7d32; color:white; border:none; padding:8px 15px; border-radius:5px; cursor:pointer;\">\ud83d\udcd0 Ukur Lebar Cakram<\/button>\r\n                    <\/div>\r\n                    <div style=\"display:flex; gap:20px; flex-wrap:wrap;\">\r\n                        <div>\r\n                            <label>Panjang Total (cm):<\/label>\r\n                            <input type=\"number\" id=\"measuredLength\" step=\"0.1\" readonly style=\"width:100px; padding:5px;\">\r\n                        <\/div>\r\n                        <div>\r\n                            <label>Lebar Cakram (cm):<\/label>\r\n                            <input type=\"number\" id=\"measuredWidth\" step=\"0.1\" readonly style=\"width:100px; padding:5px;\">\r\n                        <\/div>\r\n                    <\/div>\r\n                    <div style=\"margin-top:20px; text-align:right;\">\r\n                        <button id=\"applyCalibrationBtn\" style=\"background:#0284c7; color:white; border:none; padding:10px 20px; border-radius:5px; cursor:pointer;\">\u2705 Terapkan ke Form<\/button>\r\n                    <\/div>\r\n                <\/div>\r\n            <\/div>\r\n        <\/div>\r\n        `;\r\n        document.body.insertAdjacentHTML('beforeend', calibrationModalHTML);\r\n\r\n        calibrationState.canvas = document.getElementById('calibrationCanvas');\r\n        if (calibrationState.canvas) {\r\n            calibrationState.ctx = calibrationState.canvas.getContext('2d');\r\n        }\r\n\r\n        function openCalibrationModal(imageFile) {\r\n            const modal = document.getElementById('calibrationModal');\r\n            if (!modal) return;\r\n            \r\n            const reader = new FileReader();\r\n            reader.onload = function(e) {\r\n                const img = new Image();\r\n                img.onload = function() {\r\n                    calibrationState.image = img;\r\n                    const canvas = calibrationState.canvas;\r\n                    const maxHeight = 300;\r\n                    const ratio = img.width \/ img.height;\r\n                    let width = img.width;\r\n                    let height = img.height;\r\n                    if (height > maxHeight) {\r\n                        height = maxHeight;\r\n                        width = height * ratio;\r\n                    }\r\n                    canvas.width = width;\r\n                    canvas.height = height;\r\n                    calibrationState.ctx.drawImage(img, 0, 0, width, height);\r\n                    \r\n                    calibrationState.scale = 0;\r\n                    calibrationState.mode = 'calibrate';\r\n                    calibrationState.measuredLength = 0;\r\n                    calibrationState.measuredWidth = 0;\r\n                    document.getElementById('measuredLength').value = '';\r\n                    document.getElementById('measuredWidth').value = '';\r\n                    document.getElementById('calibrationStep1').style.display = 'block';\r\n                    document.getElementById('calibrationStep2').style.display = 'none';\r\n                    document.getElementById('calibrationResult').innerHTML = '';\r\n                    \r\n                    const eyeSize = eyeSizeMap[speciesSelect.value] || 0;\r\n                    document.getElementById('eyeSizeInput').value = eyeSize;\r\n                };\r\n                img.src = e.target.result;\r\n            };\r\n            reader.readAsDataURL(imageFile);\r\n            \r\n            modal.style.display = 'flex';\r\n        }\r\n\r\n        scaleInput.addEventListener('change', function() {\r\n            const file = this.files[0];\r\n            if (file) {\r\n                if (!speciesSelect.value) {\r\n                    alert('Pilih spesies terlebih dahulu untuk mendapatkan ukuran mata referensi.');\r\n                    this.value = '';\r\n                    return;\r\n                }\r\n                openCalibrationModal(file);\r\n            }\r\n        });\r\n\r\n        calibrationState.canvas.addEventListener('mousedown', function(e) {\r\n            if (!calibrationState.ctx) return;\r\n            const rect = calibrationState.canvas.getBoundingClientRect();\r\n            const scaleX = calibrationState.canvas.width \/ rect.width;\r\n            const scaleY = calibrationState.canvas.height \/ rect.height;\r\n            \r\n            calibrationState.drawing = true;\r\n            calibrationState.startX = (e.clientX - rect.left) * scaleX;\r\n            calibrationState.startY = (e.clientY - rect.top) * scaleY;\r\n            calibrationState.currentX = calibrationState.startX;\r\n            calibrationState.currentY = calibrationState.startY;\r\n        });\r\n\r\n        calibrationState.canvas.addEventListener('mousemove', function(e) {\r\n            if (!calibrationState.drawing || !calibrationState.ctx) return;\r\n            \r\n            const rect = calibrationState.canvas.getBoundingClientRect();\r\n            const scaleX = calibrationState.canvas.width \/ rect.width;\r\n            const scaleY = calibrationState.canvas.height \/ rect.height;\r\n            \r\n            calibrationState.currentX = (e.clientX - rect.left) * scaleX;\r\n            calibrationState.currentY = (e.clientY - rect.top) * scaleY;\r\n            \r\n            const ctx = calibrationState.ctx;\r\n            ctx.clearRect(0, 0, calibrationState.canvas.width, calibrationState.canvas.height);\r\n            ctx.drawImage(calibrationState.image, 0, 0, calibrationState.canvas.width, calibrationState.canvas.height);\r\n            \r\n            ctx.beginPath();\r\n            ctx.strokeStyle = calibrationState.mode === 'calibrate' ? '#0284c7' : '#dc2626';\r\n            ctx.lineWidth = 3;\r\n            ctx.moveTo(calibrationState.startX, calibrationState.startY);\r\n            ctx.lineTo(calibrationState.currentX, calibrationState.currentY);\r\n            ctx.stroke();\r\n            \r\n            const pixelLength = Math.sqrt(\r\n                Math.pow(calibrationState.currentX - calibrationState.startX, 2) +\r\n                Math.pow(calibrationState.currentY - calibrationState.startY, 2)\r\n            );\r\n            \r\n            if (calibrationState.mode === 'calibrate') {\r\n                document.getElementById('calibrationResult').innerHTML = \r\n                    `Panjang: ${pixelLength.toFixed(1)} piksel. Klik \"Kalibrasi\" untuk menyimpan skala.`;\r\n            } else if (calibrationState.mode === 'measureLength' && calibrationState.scale > 0) {\r\n                const lengthCm = pixelLength \/ calibrationState.scale;\r\n                document.getElementById('measuredLength').value = lengthCm.toFixed(1);\r\n            } else if (calibrationState.mode === 'measureWidth' && calibrationState.scale > 0) {\r\n                const widthCm = pixelLength \/ calibrationState.scale;\r\n                document.getElementById('measuredWidth').value = widthCm.toFixed(1);\r\n            }\r\n        });\r\n\r\n        calibrationState.canvas.addEventListener('mouseup', function() {\r\n            if (!calibrationState.drawing) return;\r\n            calibrationState.drawing = false;\r\n            if (calibrationState.mode !== 'calibrate') {\r\n                calibrationState.mode = 'idle';\r\n            }\r\n        });\r\n\r\n        document.getElementById('calibrateBtn').addEventListener('click', function() {\r\n            if (!calibrationState.startX || !calibrationState.currentX) {\r\n                alert('Tarik garis terlebih dahulu pada mata.');\r\n                return;\r\n            }\r\n            const eyeSize = parseFloat(document.getElementById('eyeSizeInput').value);\r\n            if (isNaN(eyeSize) || eyeSize <= 0) {\r\n                alert('Ukuran mata tidak valid.');\r\n                return;\r\n            }\r\n            \r\n            const pixelLength = Math.sqrt(\r\n                Math.pow(calibrationState.currentX - calibrationState.startX, 2) +\r\n                Math.pow(calibrationState.currentY - calibrationState.startY, 2)\r\n            );\r\n            \r\n            if (pixelLength === 0) {\r\n                alert('Garis terlalu pendek.');\r\n                return;\r\n            }\r\n            \r\n            calibrationState.scale = pixelLength \/ eyeSize;\r\n            \r\n            document.getElementById('calibrationResult').innerHTML = \r\n                `Kalibrasi berhasil! 1 cm = ${calibrationState.scale.toFixed(2)} piksel. Sekarang ukur panjang dan lebar.`;\r\n            \r\n            document.getElementById('calibrationStep1').style.display = 'none';\r\n            document.getElementById('calibrationStep2').style.display = 'block';\r\n        });\r\n\r\n        document.getElementById('measureLengthBtn').addEventListener('click', function() {\r\n            if (calibrationState.scale === 0) {\r\n                alert('Lakukan kalibrasi terlebih dahulu.');\r\n                return;\r\n            }\r\n            calibrationState.mode = 'measureLength';\r\n            alert('Tarik garis dari ujung kepala ke ujung ekor (panjang total).');\r\n        });\r\n\r\n        document.getElementById('measureWidthBtn').addEventListener('click', function() {\r\n            if (calibrationState.scale === 0) {\r\n                alert('Lakukan kalibrasi terlebih dahulu.');\r\n                return;\r\n            }\r\n            calibrationState.mode = 'measureWidth';\r\n            alert('Tarik garis dari sisi kiri ke kanan pada bagian terlebar (lebar cakram).');\r\n        });\r\n\r\n        document.getElementById('applyCalibrationBtn').addEventListener('click', function() {\r\n            const measuredLength = document.getElementById('measuredLength').value;\r\n            const measuredWidth = document.getElementById('measuredWidth').value;\r\n            \r\n            if (measuredLength) totalLengthInput.value = measuredLength;\r\n            if (measuredWidth) discWidthInput.value = measuredWidth;\r\n            \r\n            const file = scaleInput.files[0];\r\n            if (file) {\r\n                const reader = new FileReader();\r\n                reader.onload = function(e) {\r\n                    scaleHidden.value = e.target.result;\r\n                };\r\n                reader.readAsDataURL(file);\r\n            }\r\n            \r\n            document.getElementById('calibrationModal').style.display = 'none';\r\n        });\r\n\r\n        document.getElementById('closeCalibrationModal').addEventListener('click', function() {\r\n            document.getElementById('calibrationModal').style.display = 'none';\r\n            scaleInput.value = '';\r\n        });\r\n\r\n        async function analyzePhotos() {\r\n            const form = document.getElementById('elasmobranchForm');\r\n            const formData = new FormData(form);\r\n            formData.delete('dorsal_photo');\r\n            formData.append('dorsal_photo_base64', dorsalHidden.value);\r\n            if (leftHidden?.value) formData.append('left_photo_base64', leftHidden.value);\r\n            if (rightHidden?.value) formData.append('right_photo_base64', rightHidden.value);\r\n            if (ventralHidden?.value) formData.append('ventral_photo_base64', ventralHidden.value);\r\n            if (scaleHidden?.value) formData.append('scale_photo_base64', scaleHidden.value);\r\n            \r\n            \/\/ Tambahkan spesies\r\n            const species = speciesSelect.value;\r\n            formData.append('species', species);\r\n\r\n            analysisResult.style.display = 'block';\r\n            analysisMessage.textContent = '\u23f3 Menganalisis pola sirip punggung...';\r\n\r\n            try {\r\n                const response = await fetch(`\/wp-content\/themes\/${themeFolder}\/elasmobranch-api\/elasmobranch-analyze.php`, {\r\n                    method: 'POST',\r\n                    body: formData\r\n                });\r\n                const result = await response.json();\r\n                currentMatch = result.matchData ? { id: result.matchData.id, name: result.matchData.name, confidence: result.matchData.confidence } : null;\r\n\r\n                if (result.status === 'match') {\r\n                    matchInfo.style.display = 'block';\r\n                    newNameField.style.display = 'none';\r\n                    \r\n                    const uploadedImageUrl = dorsalHidden.value;\r\n                    const dbImageUrl = result.matchData.dorsal_photo_url || '';\r\n                    \r\n                    matchDetails.innerHTML = `\r\n                        <div class=\"comparison-container\">\r\n                            <div class=\"comparison-item\">\r\n                                <strong>Foto Upload<\/strong><br>\r\n                                <img decoding=\"async\" src=\"${uploadedImageUrl}\" alt=\"Upload\">\r\n                            <\/div>\r\n                            <div class=\"comparison-item\">\r\n                                <strong>Foto Database<\/strong><br>\r\n                                ${dbImageUrl ? `<img decoding=\"async\" src=\"${dbImageUrl}\" alt=\"Database\">` : '<p>Foto tidak tersedia<\/p>'}\r\n                            <\/div>\r\n                        <\/div>\r\n                        <p><strong>ID:<\/strong> ${result.matchData.id}<\/p>\r\n                        <p><strong>Nama:<\/strong> ${result.matchData.name || ''}<\/p>\r\n                        <p><strong>Spesies:<\/strong> ${result.matchData.species || ''}<\/p>\r\n                        <p><strong>Kemiripan:<\/strong> ${Math.round(result.matchData.confidence * 100)}%<\/p>\r\n                    `;\r\n                    submitBtn.disabled = true;\r\n                } else if (result.status === 'new') {\r\n                    matchInfo.style.display = 'none';\r\n                    newNameField.style.display = 'block';\r\n                    submitBtn.disabled = false;\r\n                    if (result.feature_data?.temp_id) {\r\n                        let oldTemp = form.querySelector('input[name=\"temp_id\"]');\r\n                        if (oldTemp) oldTemp.remove();\r\n                        let tempInput = document.createElement('input');\r\n                        tempInput.type = 'hidden';\r\n                        tempInput.name = 'temp_id';\r\n                        tempInput.value = result.feature_data.temp_id;\r\n                        form.appendChild(tempInput);\r\n                        console.log('temp_id ditambahkan ke form:', result.feature_data.temp_id);\r\n                    }\r\n                } else {\r\n                    analysisMessage.textContent = '\u274c ' + (result.message || 'Gagal menganalisis. Simpan sebagai individu baru.');\r\n                    submitBtn.disabled = false;\r\n                }\r\n            } catch (err) {\r\n                analysisMessage.textContent = '\u274c Gagal menganalisis. Simpan sebagai individu baru.';\r\n                submitBtn.disabled = false;\r\n                console.error(err);\r\n            }\r\n        }\r\n\r\n        if (useExistingBtn) {\r\n            useExistingBtn.addEventListener('click', function() {\r\n                if (currentMatch) {\r\n                    individualId.value = currentMatch.id;\r\n                    const tempInput = document.querySelector('input[name=\"temp_id\"]');\r\n                    if (tempInput) tempInput.remove();\r\n                    matchInfo.style.display = 'none';\r\n                    newNameField.style.display = 'none';\r\n                    submitBtn.disabled = false;\r\n                    analysisMessage.textContent = `\u2705 Menggunakan individu ${currentMatch.id}`;\r\n                }\r\n            });\r\n        }\r\n\r\n        if (newIndividualBtn) {\r\n            newIndividualBtn.addEventListener('click', function() {\r\n                individualId.value = '';\r\n                matchInfo.style.display = 'none';\r\n                newNameField.style.display = 'block';\r\n                submitBtn.disabled = false;\r\n            });\r\n        }\r\n    }\r\n\r\n    function loadDataAndRender() {\r\n        fetch(`\/wp-content\/themes\/${themeFolder}\/elasmobranch-api\/elasmobranch-get.php`)\r\n            .then(response => response.json())\r\n            .then(data => {\r\n                allData = data;\r\n                const provinces = [...new Set(data.map(item => item.province).filter(p => p))];\r\n                const districts = [...new Set(data.map(item => item.district).filter(d => d))];\r\n                document.getElementById('filterProvince').innerHTML = '<option value=\"\">Semua Provinsi<\/option>' + provinces.sort().map(p => `<option value=\"${p}\">${p}<\/option>`).join('');\r\n                document.getElementById('filterDistrict').innerHTML = '<option value=\"\">Semua Kab\/Kota<\/option>' + districts.sort().map(d => `<option value=\"${d}\">${d}<\/option>`).join('');\r\n                filterData();\r\n            })\r\n            .catch(err => {\r\n                document.getElementById('tableBody').innerHTML = '<tr><td colspan=\"27\" style=\"text-align:center; padding:20px; color:red;\">Gagal memuat data.<\/td><\/tr>';\r\n                console.error(err);\r\n            });\r\n    }\r\n\r\n    function filterData() {\r\n        const province = document.getElementById('filterProvince').value;\r\n        const district = document.getElementById('filterDistrict').value;\r\n        const search = document.getElementById('searchInput').value.toLowerCase();\r\n        const selectedSex = document.getElementById('filterSex').value;\r\n        const minLength = parseFloat(document.getElementById('filterMinLength').value);\r\n\r\n        filteredAllData = allData.filter(item => {\r\n            if (province && item.province !== province) return false;\r\n            if (district && item.district !== district) return false;\r\n            if (search) {\r\n                const rowText = Object.values(item).join(' ').toLowerCase();\r\n                if (!rowText.includes(search)) return false;\r\n            }\r\n            if (selectedSex && item.sex !== selectedSex) return false;\r\n            if (!isNaN(minLength)) {\r\n                const length = parseFloat(item.total_length) || 0;\r\n                if (length < minLength) return false;\r\n            }\r\n            return true;\r\n        });\r\n\r\n        if (isSuperAdmin) {\r\n            filteredUserData = filteredAllData;\r\n        } else if (currentUser) {\r\n            filteredUserData = filteredAllData.filter(item => item.user_login === currentUser);\r\n        } else {\r\n            filteredUserData = [];\r\n        }\r\n\r\n        renderTable(filteredUserData);\r\n        updateMapMarkers(filteredAllData);\r\n    }\r\n\r\n    function renderTable(data) {\r\n        const tbody = document.getElementById('tableBody');\r\n        if (!currentUser) {\r\n            tbody.innerHTML = '<tr><td colspan=\"27\" style=\"text-align:center; padding:20px;\">Silakan login untuk melihat data Anda.<\/td><\/tr>';\r\n            return;\r\n        }\r\n        if (data.length === 0) {\r\n            tbody.innerHTML = '<tr><td colspan=\"27\" style=\"text-align:center; padding:20px;\">Anda belum memiliki data atau tidak ada data sesuai filter.<\/td><\/tr>';\r\n            return;\r\n        }\r\n        let html = '';\r\n        data.forEach(item => {\r\n            const canEdit = (item.user_login === currentUser);\r\n            const actionButtons = canEdit ? `\r\n                <a href=\"\/edit-elasmobranch\/?id=${item.id}\" style=\"background:#0284c7; color:white; padding:4px 8px; border-radius:4px; text-decoration:none; font-size:12px; margin-right:5px;\">Edit<\/a>\r\n                <a href=\"javascript:void(0)\" onclick=\"confirmDelete(${item.id})\" style=\"background:#dc2626; color:white; padding:4px 8px; border-radius:4px; text-decoration:none; font-size:12px;\">Hapus<\/a>\r\n            ` : '';\r\n            html += `<tr>\r\n                <td style=\"padding:8px; border-bottom:1px solid #e2e8f0;\">${item.individual_id || ''}<\/td>\r\n                <td style=\"padding:8px; border-bottom:1px solid #e2e8f0;\">${item.individual_name || ''}<\/td>\r\n                <td style=\"padding:8px; border-bottom:1px solid #e2e8f0;\">${item.species || ''}<\/td>\r\n                <td style=\"padding:8px; border-bottom:1px solid #e2e8f0;\">${item.province || ''}<\/td>\r\n                <td style=\"padding:8px; border-bottom:1px solid #e2e8f0;\">${item.district || ''}<\/td>\r\n                <td style=\"padding:8px; border-bottom:1px solid #e2e8f0;\">${item.latitude || ''}<\/td>\r\n                <td style=\"padding:8px; border-bottom:1px solid #e2e8f0;\">${item.longitude || ''}<\/td>\r\n                <td style=\"padding:8px; border-bottom:1px solid #e2e8f0;\">${item.survey_date || ''}<\/td>\r\n                <td style=\"padding:8px; border-bottom:1px solid #e2e8f0;\">${item.survey_time || ''}<\/td>\r\n                <td style=\"padding:8px; border-bottom:1px solid #e2e8f0;\"><img decoding=\"async\" src=\"${item.dorsal_photo_url}\" style=\"max-width:50px; max-height:50px;\" alt=\"Dorsal\"><\/td>\r\n                <td style=\"padding:8px; border-bottom:1px solid #e2e8f0;\">${item.left_photo_url ? '<img decoding=\"async\" src=\"'+item.left_photo_url+'\" style=\"max-width:50px; max-height:50px;\" alt=\"Kiri\">' : ''}<\/td>\r\n                <td style=\"padding:8px; border-bottom:1px solid #e2e8f0;\">${item.right_photo_url ? '<img decoding=\"async\" src=\"'+item.right_photo_url+'\" style=\"max-width:50px; max-height:50px;\" alt=\"Kanan\">' : ''}<\/td>\r\n                <td style=\"padding:8px; border-bottom:1px solid #e2e8f0;\">${item.ventral_photo_url ? '<img decoding=\"async\" src=\"'+item.ventral_photo_url+'\" style=\"max-width:50px; max-height:50px;\" alt=\"Ventral\">' : ''}<\/td>\r\n                <td style=\"padding:8px; border-bottom:1px solid #e2e8f0;\">${item.scale_photo_url ? '<img decoding=\"async\" src=\"'+item.scale_photo_url+'\" style=\"max-width:50px; max-height:50px;\" alt=\"Skala\">' : ''}<\/td>\r\n                <td style=\"padding:8px; border-bottom:1px solid #e2e8f0;\">${item.total_length || ''}<\/td>\r\n                <td style=\"padding:8px; border-bottom:1px solid #e2e8f0;\">${item.disc_width || ''}<\/td>\r\n                <td style=\"padding:8px; border-bottom:1px solid #e2e8f0;\">${item.sex || ''}<\/td>\r\n                <td style=\"padding:8px; border-bottom:1px solid #e2e8f0;\">${item.depth || ''}<\/td>\r\n                <td style=\"padding:8px; border-bottom:1px solid #e2e8f0;\">${item.behaviour || ''}<\/td>\r\n                <td style=\"padding:8px; border-bottom:1px solid #e2e8f0;\">${item.health || ''}<\/td>\r\n                <td style=\"padding:8px; border-bottom:1px solid #e2e8f0;\">${item.health_notes || ''}<\/td>\r\n                <td style=\"padding:8px; border-bottom:1px solid #e2e8f0;\">${item.analysis_result || ''}<\/td>\r\n                <td style=\"padding:8px; border-bottom:1px solid #e2e8f0;\">${item.contributor || ''}<\/td>\r\n                <td style=\"padding:8px; border-bottom:1px solid #e2e8f0;\">${item.surveyor || ''}<\/td>\r\n                <td style=\"padding:8px; border-bottom:1px solid #e2e8f0;\">${item.institution || ''}<\/td>\r\n                <td style=\"padding:8px; border-bottom:1px solid #e2e8f0;\">${actionButtons}<\/td>\r\n            <\/tr>`;\r\n        });\r\n        tbody.innerHTML = html;\r\n    }\r\n\r\n    window.confirmDelete = function(id) {\r\n        if (confirm('Apakah Anda yakin ingin menghapus data ini?')) {\r\n            fetch(`\/wp-content\/themes\/${themeFolder}\/elasmobranch-api\/elasmobranch-delete.php?id=` + id, { method: 'DELETE' })\r\n                .then(response => response.json())\r\n                .then(result => {\r\n                    if (result.success) {\r\n                        alert('Data berhasil dihapus.');\r\n                        window.location.reload();\r\n                    } else {\r\n                        alert('Gagal menghapus: ' + result.message);\r\n                    }\r\n                })\r\n                .catch(err => alert('Gagal menghapus: ' + err.message));\r\n        }\r\n    };\r\n\r\n    function updateMapMarkers(data) {\r\n        const grouped = {};\r\n        data.forEach(item => {\r\n            const iid = item.individual_id;\r\n            if (!iid) return;\r\n            if (!grouped[iid]) grouped[iid] = [];\r\n            grouped[iid].push(item);\r\n        });\r\n\r\n        Object.keys(grouped).forEach(iid => {\r\n            grouped[iid].sort((a, b) => {\r\n                const dateA = a.survey_date + ' ' + (a.survey_time || '00:00');\r\n                const dateB = b.survey_date + ' ' + (b.survey_time || '00:00');\r\n                return dateA.localeCompare(dateB);\r\n            });\r\n        });\r\n\r\n        markersCluster.clearLayers();\r\n\r\n        Object.keys(grouped).forEach(iid => {\r\n            const points = grouped[iid];\r\n            if (points.length < 2) return;\r\n            \r\n            let hash = 0;\r\n            for (let i = 0; i < iid.length; i++) {\r\n                hash = ((hash << 5) - hash) + iid.charCodeAt(i);\r\n                hash |= 0;\r\n            }\r\n            const hue = Math.abs(hash) % 360;\r\n            const color = `hsl(${hue}, 70%, 50%)`;\r\n            const latlngs = points.map(p => [parseFloat(p.latitude), parseFloat(p.longitude)]);\r\n            const polyline = L.polyline(latlngs, { color: color, weight: 3, opacity: 0.7 });\r\n            markersCluster.addLayer(polyline);\r\n        });\r\n\r\n        Object.keys(grouped).forEach(iid => {\r\n            const points = grouped[iid];\r\n            const baseName = points[0].individual_name || 'Elasmobranch';\r\n            \r\n            points.forEach((item, index) => {\r\n                const lat = parseFloat(item.latitude);\r\n                const lng = parseFloat(item.longitude);\r\n                if (isNaN(lat) || isNaN(lng)) return;\r\n\r\n                const occurrence = index + 1;\r\n                const displayName = occurrence > 1 ? `${baseName} (${occurrence})` : baseName;\r\n\r\n                const marker = L.marker([lat, lng]);\r\n                marker.bindPopup(`\r\n                    <b>${displayName}<\/b><br>\r\n                    <img decoding=\"async\" src=\"${item.dorsal_photo_url}\" style=\"max-width:100px; max-height:100px;\"><br>\r\n                    Spesies: ${item.species || '-'}<br>\r\n                    \ud83c\udd94 ID: ${item.individual_id}<br>\r\n                    \ud83d\udcc5 Tanggal: ${item.survey_date}<br>\r\n                    \ud83d\udccf Panjang: ${item.total_length || '-'} cm<br>\r\n                    \u26a5 Kelamin: ${item.sex || '-'}<br>\r\n                    \ud83d\udcca Kesehatan: ${item.health || '-'}\r\n                `);\r\n                markersCluster.addLayer(marker);\r\n            });\r\n        });\r\n\r\n        if (map.hasLayer(markersCluster)) map.removeLayer(markersCluster);\r\n        map.addLayer(markersCluster);\r\n\r\n        if (data.length > 0) {\r\n            const group = L.featureGroup(data.map(item => L.marker([parseFloat(item.latitude), parseFloat(item.longitude)])));\r\n            map.fitBounds(group.getBounds().pad(0.1));\r\n        } else {\r\n            map.setView([-2.5, 118], 5);\r\n        }\r\n    }\r\n\r\n    document.getElementById('applyFilter').addEventListener('click', filterData);\r\n    document.getElementById('searchInput').addEventListener('keyup', function(e) { if (e.key === 'Enter') filterData(); });\r\n    document.getElementById('filterProvince').addEventListener('change', filterData);\r\n    document.getElementById('filterDistrict').addEventListener('change', filterData);\r\n    document.getElementById('filterSex').addEventListener('change', filterData);\r\n    document.getElementById('filterMinLength').addEventListener('keyup', function(e) { if (e.key === 'Enter') filterData(); });\r\n\r\n    document.getElementById('downloadBtn').addEventListener('click', function() {\r\n        if (!currentUser) { alert('Anda harus login untuk mendownload data.'); return; }\r\n        const password = prompt('Masukkan password untuk mendownload data:');\r\n        if (password !== '17agustus1945') { alert('Password salah!'); return; }\r\n        if (filteredUserData.length === 0) { alert('Tidak ada data sesuai filter.'); return; }\r\n\r\n        const format = document.getElementById('formatSelect').value;\r\n        const headers = ['ID Individu','Nama','Spesies','Provinsi','Kab\/Kota','Lat','Lng','Tanggal','Waktu','Foto Dorsal URL','Foto Kiri URL','Foto Kanan URL','Foto Ventral URL','Foto Skala URL','Panjang (cm)','Lebar Cakram','Jenis Kelamin','Kedalaman','Behaviour','Kesehatan','Keterangan Sakit','Hasil Analisis','Kontributor','Surveyor','Instansi'];\r\n        const rows = filteredUserData.map(item => [\r\n            item.individual_id || '', item.individual_name || '', item.species || '', item.province || '', item.district || '',\r\n            item.latitude || '', item.longitude || '', item.survey_date || '', item.survey_time || '',\r\n            item.dorsal_photo_url || '', item.left_photo_url || '', item.right_photo_url || '', item.ventral_photo_url || '', item.scale_photo_url || '',\r\n            item.total_length || '', item.disc_width || '', item.sex || '', item.depth || '', item.behaviour || '',\r\n            item.health || '', item.health_notes || '', item.analysis_result || '',\r\n            item.contributor || '', item.surveyor || '', item.institution || ''\r\n        ]);\r\n\r\n        if (format === 'csv') {\r\n            let csv = headers.join(',') + '\\n';\r\n            rows.forEach(row => { csv += row.map(val => `\"${val}\"`).join(',') + '\\n'; });\r\n            downloadBlob(csv, 'text\/csv;charset=utf-8;', 'data_elasmobranch.csv');\r\n        } else if (format === 'xlsx') {\r\n            (async () => {\r\n                try {\r\n                    const wsData = [headers, ...rows];\r\n                    const wb = XLSX.utils.book_new();\r\n                    const ws = XLSX.utils.aoa_to_sheet(wsData);\r\n                    XLSX.utils.book_append_sheet(wb, ws, 'Data Elasmobranch');\r\n                    \r\n                    const speciesSummary = {};\r\n                    filteredUserData.forEach(item => {\r\n                        const sp = item.species || 'Tidak diketahui';\r\n                        if (!speciesSummary[sp]) {\r\n                            speciesSummary[sp] = { total_records: 0, unique_individuals: new Set() };\r\n                        }\r\n                        speciesSummary[sp].total_records++;\r\n                        if (item.individual_id) {\r\n                            speciesSummary[sp].unique_individuals.add(item.individual_id);\r\n                        }\r\n                    });\r\n                    \r\n                    const summaryData = [['Spesies', 'Jumlah Individu Unik', 'Total Record']];\r\n                    Object.keys(speciesSummary).sort().forEach(sp => {\r\n                        summaryData.push([\r\n                            sp,\r\n                            speciesSummary[sp].unique_individuals.size,\r\n                            speciesSummary[sp].total_records\r\n                        ]);\r\n                    });\r\n                    \r\n                    const wsSummary = XLSX.utils.aoa_to_sheet(summaryData);\r\n                    XLSX.utils.book_append_sheet(wb, wsSummary, 'Summary Spesies');\r\n                    \r\n                    const resightingMap = {};\r\n                    filteredUserData.forEach(item => {\r\n                        const iid = item.individual_id;\r\n                        if (!iid) return;\r\n                        if (!resightingMap[iid]) {\r\n                            resightingMap[iid] = [];\r\n                        }\r\n                        resightingMap[iid].push(item);\r\n                    });\r\n                    \r\n                    const resightingRows = [\r\n                        ['ID Individu', 'Nama', 'Kemunculan ke', 'Tanggal', 'Waktu', 'Latitude', 'Longitude', 'Lokasi', 'Provinsi', 'Kab\/Kota', 'Foto URL']\r\n                    ];\r\n                    Object.keys(resightingMap).forEach(iid => {\r\n                        const items = resightingMap[iid];\r\n                        if (items.length > 1) {\r\n                            items.sort((a, b) => {\r\n                                const dateA = a.survey_date + ' ' + (a.survey_time || '00:00');\r\n                                const dateB = b.survey_date + ' ' + (b.survey_time || '00:00');\r\n                                return dateA.localeCompare(dateB);\r\n                            });\r\n                            items.forEach((item, index) => {\r\n                                resightingRows.push([\r\n                                    iid,\r\n                                    item.individual_name || '',\r\n                                    index + 1,\r\n                                    item.survey_date || '',\r\n                                    item.survey_time || '',\r\n                                    item.latitude || '',\r\n                                    item.longitude || '',\r\n                                    item.location_name || '',\r\n                                    item.province || '',\r\n                                    item.district || '',\r\n                                    item.dorsal_photo_url || ''\r\n                                ]);\r\n                            });\r\n                        }\r\n                    });\r\n                    \r\n                    const wsResighting = XLSX.utils.aoa_to_sheet(resightingRows);\r\n                    XLSX.utils.book_append_sheet(wb, wsResighting, 'Resighting');\r\n                    \r\n                    const wbout = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });\r\n                    downloadBlob(new Blob([wbout], { type: 'application\/octet-stream' }), 'application\/octet-stream', 'data_elasmobranch.xlsx');\r\n                } catch (e) {\r\n                    console.error('Error saat export XLSX:', e);\r\n                    alert('Terjadi kesalahan saat mengexport data.');\r\n                }\r\n            })();\r\n        } else if (format === 'txt') {\r\n            let txt = headers.join('\\t') + '\\n';\r\n            rows.forEach(row => { txt += row.join('\\t') + '\\n'; });\r\n            downloadBlob(txt, 'text\/plain;charset=utf-8;', 'data_elasmobranch.txt');\r\n        }\r\n    });\r\n\r\n    function downloadBlob(content, mimeType, filename) {\r\n        const blob = new Blob([content], { type: mimeType });\r\n        const url = URL.createObjectURL(blob);\r\n        const a = document.createElement('a');\r\n        a.href = url;\r\n        a.download = filename;\r\n        a.click();\r\n        URL.revokeObjectURL(url);\r\n    }\r\n\r\n    loadDataAndRender();\r\n});\r\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>","protected":false},"excerpt":{"rendered":"<p>\ud83e\udd88 e-Elasmobranch Indonesia Semua Provinsi Semua Kab\/Kota Semua KelaminJantanBetinaTidak tahu \ud83d\udd0d \ud83d\udd12 Anda belum login. Login Daftar Peta menampilkan semua [&hellip;]<\/p>","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"site-sidebar-layout":"no-sidebar","site-content-layout":"","ast-site-content-layout":"full-width-container","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"disabled","ast-breadcrumbs-content":"","ast-featured-img":"disabled","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"class_list":["post-1767","page","type-page","status-publish","hentry"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v22.3 (Yoast SEO v24.7) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>e-Elasmobranch Indonesia - marineconservation.id<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/marineconservation.id\/id\/e-elasmobranch-indonesia\/\" \/>\n<meta property=\"og:locale\" content=\"id_ID\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"e-Elasmobranch Indonesia\" \/>\n<meta property=\"og:description\" content=\"\ud83e\udd88 e-Elasmobranch Indonesia Semua Provinsi Semua Kab\/Kota Semua KelaminJantanBetinaTidak tahu \ud83d\udd0d \ud83d\udd12 Anda belum login. Login Daftar Peta menampilkan semua [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/marineconservation.id\/id\/e-elasmobranch-indonesia\/\" \/>\n<meta property=\"og:site_name\" content=\"marineconservation.id\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/profile.php?id=61572311944591\" \/>\n<meta property=\"article:modified_time\" content=\"2026-04-09T04:58:05+00:00\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Estimasi waktu membaca\" \/>\n\t<meta name=\"twitter:data1\" content=\"1 menit\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/marineconservation.id\/e-elasmobranch-indonesia\/\",\"url\":\"https:\/\/marineconservation.id\/e-elasmobranch-indonesia\/\",\"name\":\"e-Elasmobranch Indonesia - marineconservation.id\",\"isPartOf\":{\"@id\":\"https:\/\/marineconservation.id\/#website\"},\"datePublished\":\"2026-03-06T04:15:24+00:00\",\"dateModified\":\"2026-04-09T04:58:05+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/marineconservation.id\/e-elasmobranch-indonesia\/#breadcrumb\"},\"inLanguage\":\"id\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/marineconservation.id\/e-elasmobranch-indonesia\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/marineconservation.id\/e-elasmobranch-indonesia\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/marineconservation.id\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"e-Elasmobranch Indonesia\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/marineconservation.id\/#website\",\"url\":\"https:\/\/marineconservation.id\/\",\"name\":\"marineconservation.id\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\/\/marineconservation.id\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/marineconservation.id\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"id\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/marineconservation.id\/#organization\",\"name\":\"marineconservation.id\",\"url\":\"https:\/\/marineconservation.id\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"id\",\"@id\":\"https:\/\/marineconservation.id\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/marineconservation.id\/wp-content\/uploads\/2025\/02\/Marine-Conservation-Indonesia.png\",\"contentUrl\":\"https:\/\/marineconservation.id\/wp-content\/uploads\/2025\/02\/Marine-Conservation-Indonesia.png\",\"width\":500,\"height\":500,\"caption\":\"marineconservation.id\"},\"image\":{\"@id\":\"https:\/\/marineconservation.id\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/profile.php?id=61572311944591\",\"https:\/\/www.instagram.com\/marineconservation.id\/\",\"https:\/\/www.youtube.com\/watch?v=nOmjtk-dTg8&list=LL\"]}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"e-Elasmobranch Indonesia - marineconservation.id","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/marineconservation.id\/id\/e-elasmobranch-indonesia\/","og_locale":"id_ID","og_type":"article","og_title":"e-Elasmobranch Indonesia","og_description":"\ud83e\udd88 e-Elasmobranch Indonesia Semua Provinsi Semua Kab\/Kota Semua KelaminJantanBetinaTidak tahu \ud83d\udd0d \ud83d\udd12 Anda belum login. Login Daftar Peta menampilkan semua [&hellip;]","og_url":"https:\/\/marineconservation.id\/id\/e-elasmobranch-indonesia\/","og_site_name":"marineconservation.id","article_publisher":"https:\/\/www.facebook.com\/profile.php?id=61572311944591","article_modified_time":"2026-04-09T04:58:05+00:00","twitter_card":"summary_large_image","twitter_misc":{"Estimasi waktu membaca":"1 menit"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/marineconservation.id\/e-elasmobranch-indonesia\/","url":"https:\/\/marineconservation.id\/e-elasmobranch-indonesia\/","name":"e-Elasmobranch Indonesia - marineconservation.id","isPartOf":{"@id":"https:\/\/marineconservation.id\/#website"},"datePublished":"2026-03-06T04:15:24+00:00","dateModified":"2026-04-09T04:58:05+00:00","breadcrumb":{"@id":"https:\/\/marineconservation.id\/e-elasmobranch-indonesia\/#breadcrumb"},"inLanguage":"id","potentialAction":[{"@type":"ReadAction","target":["https:\/\/marineconservation.id\/e-elasmobranch-indonesia\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/marineconservation.id\/e-elasmobranch-indonesia\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/marineconservation.id\/"},{"@type":"ListItem","position":2,"name":"e-Elasmobranch Indonesia"}]},{"@type":"WebSite","@id":"https:\/\/marineconservation.id\/#website","url":"https:\/\/marineconservation.id\/","name":"marineconservation.id","description":"","publisher":{"@id":"https:\/\/marineconservation.id\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/marineconservation.id\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"id"},{"@type":"Organization","@id":"https:\/\/marineconservation.id\/#organization","name":"marineconservation.id","url":"https:\/\/marineconservation.id\/","logo":{"@type":"ImageObject","inLanguage":"id","@id":"https:\/\/marineconservation.id\/#\/schema\/logo\/image\/","url":"https:\/\/marineconservation.id\/wp-content\/uploads\/2025\/02\/Marine-Conservation-Indonesia.png","contentUrl":"https:\/\/marineconservation.id\/wp-content\/uploads\/2025\/02\/Marine-Conservation-Indonesia.png","width":500,"height":500,"caption":"marineconservation.id"},"image":{"@id":"https:\/\/marineconservation.id\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/profile.php?id=61572311944591","https:\/\/www.instagram.com\/marineconservation.id\/","https:\/\/www.youtube.com\/watch?v=nOmjtk-dTg8&list=LL"]}]}},"_links":{"self":[{"href":"https:\/\/marineconservation.id\/id\/wp-json\/wp\/v2\/pages\/1767","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/marineconservation.id\/id\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/marineconservation.id\/id\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/marineconservation.id\/id\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/marineconservation.id\/id\/wp-json\/wp\/v2\/comments?post=1767"}],"version-history":[{"count":61,"href":"https:\/\/marineconservation.id\/id\/wp-json\/wp\/v2\/pages\/1767\/revisions"}],"predecessor-version":[{"id":2312,"href":"https:\/\/marineconservation.id\/id\/wp-json\/wp\/v2\/pages\/1767\/revisions\/2312"}],"wp:attachment":[{"href":"https:\/\/marineconservation.id\/id\/wp-json\/wp\/v2\/media?parent=1767"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}