MediaWiki:Common.js: Perbedaan antara revisi
Tidak ada ringkasan suntingan Tanda: Suntingan perangkat seluler Suntingan peramban seluler |
Tidak ada ringkasan suntingan |
||
| (18 revisi perantara oleh pengguna yang sama tidak ditampilkan) | |||
| Baris 1: | Baris 1: | ||
/** | /** | ||
* ==================================================================== | * ==================================================================== | ||
* MIPPEDIA DATA - ADVANCED STRUCTURED DATA SYSTEM (V8. | * MIPPEDIA DATA - ADVANCED STRUCTURED DATA SYSTEM (V8.7 SECURE ULTIMATE) | ||
* Fitur: | * Fitur: ALL ORIGINAL V8 FEATURES KEPT + Global Social Media Box, | ||
* | * Smart Autocomplete/Preview Wikilink, Ultra-Strict Security, Mobile Ready. | ||
* ==================================================================== | * ==================================================================== | ||
*/ | */ | ||
| Baris 19: | Baris 19: | ||
var isLocked = false; | var isLocked = false; | ||
var api = new mw.Api(); | var api = new mw.Api(); | ||
var | var DRAFT_KEY = 'mippedia_data_draft_' + pageName; | ||
// 1. DATA MASTER PLATFORM MEDIA SOSIAL & MUSIK ( | // 1. DATA MASTER PLATFORM MEDIA SOSIAL & MUSIK (FULL ASLI) | ||
var SOCIAL_PLATFORMS = { | var SOCIAL_PLATFORMS = { | ||
applemusic: { name: 'Apple Music', base: 'https://music.apple.com/artist/', color: '#fc3c44', icon: '<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2zm3.32 12.383c-.328.232-.82.418-1.32.418-1.238 0-1.922-.828-1.922-2.191v-4.14l4.312-.868v1.398l-2.91.586v2.805c0 .734.336 1.148.977 1.148.336 0 .617-.094.863-.258v1.102z"/></svg>' }, | applemusic: { name: 'Apple Music', base: 'https://music.apple.com/artist/', color: '#fc3c44', icon: '<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2zm3.32 12.383c-.328.232-.82.418-1.32.418-1.238 0-1.922-.828-1.922-2.191v-4.14l4.312-.868v1.398l-2.91.586v2.805c0 .734.336 1.148.977 1.148.336 0 .617-.094.863-.258v1.102z"/></svg>' }, | ||
| Baris 27: | Baris 27: | ||
soundcloud: { name: 'SoundCloud', base: 'https://soundcloud.com/', color: '#ff5500', icon: '<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M1.175 12.225c-.062 0-.115.053-.115.116v4.636c0 .062.053.115.115.115h.411c.063 0 .116-.053.116-.115v-4.636c0-.063-.053-.116-.116-.116h-.411zm1.312-1.28c-.062 0-.115.052-.115.115v6.52c0 .063.053.115.115.115h.43c.062 0 .115-.052.115-.115v-6.52c0-.063-.053-.115-.115-.115h-.43zm1.33-1.258c-.063 0-.115.052-.115.115v8.322c0 .063.052.115.115.115h.43c.063 0 .115-.052.115-.115V9.802c0-.063-.052-.115-.115-.115h-.43zm1.33-.86c-.063 0-.115.053-.115.116v9.542c0 .063.052.115.115.115h.43c.063 0 .115-.052.115-.115V8.942c0-.063-.052-.116-.115-.116h-.43zm21.43 3.652c-.413 0-.802.08-1.157.228-.312-2.73-2.613-4.858-5.422-4.858-1.08 0-2.083.315-2.928.858-.2-.702-.592-1.332-1.12-1.826-.78-.727-1.812-1.13-2.882-1.13-.815 0-1.6.236-2.272.684v10.822c0 .063.052.115.115.115h15.666c1.666 0 3.018-1.352 3.018-3.018 0-1.666-1.352-3.018-3.018-3.018z"/></svg>' }, | soundcloud: { name: 'SoundCloud', base: 'https://soundcloud.com/', color: '#ff5500', icon: '<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M1.175 12.225c-.062 0-.115.053-.115.116v4.636c0 .062.053.115.115.115h.411c.063 0 .116-.053.116-.115v-4.636c0-.063-.053-.116-.116-.116h-.411zm1.312-1.28c-.062 0-.115.052-.115.115v6.52c0 .063.053.115.115.115h.43c.062 0 .115-.052.115-.115v-6.52c0-.063-.053-.115-.115-.115h-.43zm1.33-1.258c-.063 0-.115.052-.115.115v8.322c0 .063.052.115.115.115h.43c.063 0 .115-.052.115-.115V9.802c0-.063-.052-.115-.115-.115h-.43zm1.33-.86c-.063 0-.115.053-.115.116v9.542c0 .063.052.115.115.115h.43c.063 0 .115-.052.115-.115V8.942c0-.063-.052-.116-.115-.116h-.43zm21.43 3.652c-.413 0-.802.08-1.157.228-.312-2.73-2.613-4.858-5.422-4.858-1.08 0-2.083.315-2.928.858-.2-.702-.592-1.332-1.12-1.826-.78-.727-1.812-1.13-2.882-1.13-.815 0-1.6.236-2.272.684v10.822c0 .063.052.115.115.115h15.666c1.666 0 3.018-1.352 3.018-3.018 0-1.666-1.352-3.018-3.018-3.018z"/></svg>' }, | ||
bandcamp: { name: 'Bandcamp', base: 'https://bandcamp.com/', color: '#1da0c3', icon: '<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M0 18.75l7.437-13.5H24l-7.438 13.5H0z"/></svg>' }, | bandcamp: { name: 'Bandcamp', base: 'https://bandcamp.com/', color: '#1da0c3', icon: '<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M0 18.75l7.437-13.5H24l-7.438 13.5H0z"/></svg>' }, | ||
instagram: { name: 'Instagram', base: 'https://instagram.com/', color: '#e1306c', icon: '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="2" width="20" height="20" rx="5" ry="5"></rect><path d="M16 11.37A4 4 0 1 1 12.63 8 4 4 0 0 1 16 11.37z"></path></svg>' }, | instagram: { name: 'Instagram', base: 'https://instagram.com/', color: '#e1306c', icon: '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="2" width="20" height="20" rx="5" ry="5"></rect><path d="M16 11.37A4 4 0 1 1 12.63 8 4 4 0 0 1 16 11.37z"></path><line x1="17.5" y1="6.5" x2="17.51" y2="6.5"></line></svg>' }, | ||
tiktok: { name: 'TikTok', base: 'https://www.tiktok.com/@', color: '#000000', icon: '<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M19.59 6.69a4.83 4.83 0 0 1-3.77-4.25V2h-3.45v13.67a2.89 2.89 0 1 1-5.2-1.74 2.89 2.89 0 0 1 2.31-2.22V8.2a6.34 6.34 0 0 0-5 6.13 6.34 6.34 0 1 0 10.7-4.52A8.25 8.25 0 0 0 20 10.56V6.69z"/></svg>' }, | tiktok: { name: 'TikTok', base: 'https://www.tiktok.com/@', color: '#000000', icon: '<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M19.59 6.69a4.83 4.83 0 0 1-3.77-4.25V2h-3.45v13.67a2.89 2.89 0 1 1-5.2-1.74 2.89 2.89 0 0 1 2.31-2.22V8.2a6.34 6.34 0 0 0-5 6.13 6.34 6.34 0 1 0 10.7-4.52A8.25 8.25 0 0 0 20 10.56V6.69z"/></svg>' }, | ||
youtube: { name: 'YouTube', base: 'https://youtube.com/@', color: '#ff0000', icon: '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22.54 6.42a2.78 2.78 0 0 0-1.94-2C18.88 4 12 4 12 4s-6.88 0-8.6.46a2.78 2.78 0 0 0-1.94 2A29 29 0 0 0 1 11.75a29 29 0 0 0 .46 5.33A2.78 2.78 0 0 0 3.4 19c1.72.46 8.6.46 8.6.46s6.88 0 8.6-.46a2.78 2.78 0 0 0 1.94-2 29 29 0 0 0 .46-5.25 29 29 0 0 0-.46-5.33z"></path><polygon points="9.75 15.02 15.5 11.75 9.75 8.48 9.75 15.02"></polygon></svg>' }, | youtube: { name: 'YouTube', base: 'https://youtube.com/@', color: '#ff0000', icon: '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22.54 6.42a2.78 2.78 0 0 0-1.94-2C18.88 4 12 4 12 4s-6.88 0-8.6.46a2.78 2.78 0 0 0-1.94 2A29 29 0 0 0 1 11.75a29 29 0 0 0 .46 5.33A2.78 2.78 0 0 0 3.4 19c1.72.46 8.6.46 8.6.46s6.88 0 8.6-.46a2.78 2.78 0 0 0 1.94-2 29 29 0 0 0 .46-5.25 29 29 0 0 0-.46-5.33z"></path><polygon points="9.75 15.02 15.5 11.75 9.75 8.48 9.75 15.02"></polygon></svg>' }, | ||
| Baris 35: | Baris 35: | ||
linkedin: { name: 'LinkedIn', base: 'https://linkedin.com/in/', color: '#0a66c2', icon: '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M16 8a6 6 0 0 1 6 6v7h-4v-7a2 2 0 0 0-2-2 2 2 0 0 0-2 2v7h-4v-7a6 6 0 0 1 6-6z"></path><rect x="2" y="9" width="4" height="12"></rect><circle cx="4" cy="4" r="2"></circle></svg>' }, | linkedin: { name: 'LinkedIn', base: 'https://linkedin.com/in/', color: '#0a66c2', icon: '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M16 8a6 6 0 0 1 6 6v7h-4v-7a2 2 0 0 0-2-2 2 2 0 0 0-2 2v7h-4v-7a6 6 0 0 1 6-6z"></path><rect x="2" y="9" width="4" height="12"></rect><circle cx="4" cy="4" r="2"></circle></svg>' }, | ||
github: { name: 'GitHub', base: 'https://github.com/', color: '#24292e', icon: '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"></path></svg>' }, | github: { name: 'GitHub', base: 'https://github.com/', color: '#24292e', icon: '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"></path></svg>' }, | ||
pinterest: { name: 'Pinterest', base: 'https://pinterest.com/', color: '#e60023', icon: '<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M12 0C5.373 0 0 5.372 0 12c0 5.084 3.163 9.426 7.627 11.174-.105-.949-.2-2.405.042-3.441.218-.937 1.407-5.965 1.407-5.965s-.359-.719-.359-1.782c0-1.668.967-2.914 2.171-2.914 1.023 0 1.518.769 1.518 1.69 0 1.029-.655 2.568-.994 3.995-.283 1.194.599 2.169 1.777 2.169 2.133 0 3.772-2.249 3.772-5.495 0-2.873-2.064-4.882-5.012-4.882-3.414 0-5.418 2.561-5.418 5.207 0 1.031.397 2.138.893 2.738.098.119.112.224.083.345l-.333 1.36c-.053.22-.174.267-.402.161-1.499-.698-2.436-2.889-2.436-4.649 0-3.785 2.75-7.262 7.929-7.262 4.163 0 7.398 2.967 7.398 6.931 0 4.136-2.607 7.464-6.227 7.464-1.216 0-2.359-.631-2.75-1.378l-.748 2.853c-.271 1.043-1.002 2.35-1.492 3.146C9.57 23.812 10.763 24 12 24c6.627 0 12-5.373 12-12 0-6.628-5.373-12-12-12z | pinterest: { name: 'Pinterest', base: 'https://pinterest.com/', color: '#e60023', icon: '<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M12 0C5.373 0 0 5.372 0 12c0 5.084 3.163 9.426 7.627 11.174-.105-.949-.2-2.405.042-3.441.218-.937 1.407-5.965 1.407-5.965s-.359-.719-.359-1.782c0-1.668.967-2.914 2.171-2.914 1.023 0 1.518.769 1.518 1.69 0 1.029-.655 2.568-.994 3.995-.283 1.194.599 2.169 1.777 2.169 2.133 0 3.772-2.249 3.772-5.495 0-2.873-2.064-4.882-5.012-4.882-3.414 0-5.418 2.561-5.418 5.207 0 1.031.397 2.138.893 2.738.098.119.112.224.083.345l-.333 1.36c-.053.22-.174.267-.402.161-1.499-.698-2.436-2.889-2.436-4.649 0-3.785 2.75-7.262 7.929-7.262 4.163 0 7.398 2.967 7.398 6.931 0 4.136-2.607 7.464-6.227 7.464-1.216 0-2.359-.631-2.75-1.378l-.748 2.853c-.271 1.043-1.002 2.35-1.492 3.146C9.57 23.812 10.763 24 12 24c6.627 0 12-5.373 12-12 0-6.628-5.373-12-12-12z"/></svg>' } | ||
}; | }; | ||
// 2. PRESET | // 2. PRESET & MASTER RUJUKAN | ||
var PRESET_LABELS = [ | var PRESET_LABELS = [ | ||
"Nama Lahir", "Tanggal Lahir", "Tempat Lahir", "Pekerjaan", | "Nama Lahir", "Tanggal Lahir", "Tempat Lahir", "Pekerjaan", | ||
"Kebangsaan", "Genre", "Instrumen", "Label Rekaman", | "Kebangsaan", "Genre", "Instrumen", "Label Rekaman", | ||
"Tahun Aktif", "Situs Web Resmi", "Pasangan", "Orang Tua | "Tahun Aktif", "Situs Web Resmi", "Pasangan", "Orang Tua" | ||
]; | ]; | ||
var REF_TYPES = { | var REF_TYPES = { | ||
"Berita": ["Berita Online", "Berita Cetak / Koran", "Wawancara Eksklusif", "Liputan Khusus"], | "Berita": ["Berita Online", "Berita Cetak / Koran", "Wawancara Eksklusif", "Liputan Khusus", "Editorial", "Investigasi Media", "Siaran Radio / TV"], | ||
"Website": ["Situs Resmi", "Portal Informasi", "Database Web", "Forum Diskusi"], | "Website": ["Situs Resmi", "Portal Informasi", "Database Web", "Portal Komunitas", "Forum Diskusi", "Portal Edukasi", "Direktori Web"], | ||
"Buku": ["Buku Cetak", "E-Book", "Bab / Bagian Buku", "Monograf"], | "Buku": ["Buku Cetak", "E-Book", "Bab / Bagian Buku", "Monograf", "Katalog / Biografi Cetak", "Buku Teks Akademik"], | ||
"Jurnal": ["Jurnal Ilmiah Peer-Reviewed", "Paper Konferensi", "Tesis / Disertasi"], | "Jurnal": ["Jurnal Ilmiah Peer-Reviewed", "Paper Konferensi", "Artikel Review", "Pre-print / Draft Penelitian", "Tesis / Disertasi"], | ||
"Siaran Pers": ["Siaran Pers Resmi", "Pengumuman Instansi / BUMN"], | "Siaran Pers": ["Siaran Pers Resmi", "Pengumuman Instansi / BUMN", "Pernyataan Pers Hukrim", "Keterangan Pers Artis/Manajemen"], | ||
"Dokumen PDF": ["Laporan Tahunan / Annual Report", "Dokumen Kebijakan"], | "Dokumen PDF": ["Laporan Tahunan / Annual Report", "Dokumen Kebijakan / Regulasi", "Brosur / Pamflet Resmi", "Naskah Akademik"], | ||
"Studio / Database Musik": ["MusicBrainz", "Discogs", "Genius", "AllMusic", "Spotify For Artists"], | "Studio / Database Musik": ["MusicBrainz", "Discogs", "Genius", "AllMusic", "Spotify For Artists", "Database BPI / RIAA", "Songfacts", "Apple Music Catalogue"], | ||
"Video": ["YouTube Video", "Film Dokumenter", "Wawancara Video"], | "Video": ["YouTube Video", "Film Dokumenter", "Wawancara Video", "Arsip Siaran TV", "Vlog Resmi"], | ||
"Podcast": ["Episode Podcast", "Siniar Audio Resmi"], | "Podcast": ["Episode Podcast", "Siniar Audio Resmi", "Wawancara Audio"], | ||
"Media Sosial": ["Postingan / Tweet", "Video Singkat", "Live Stream"], | "Media Sosial": ["Postingan / Tweet", "Video Singkat (Reels/TikTok/Shorts)", "Live Stream", "Thread Resmi"], | ||
"Ensiklopedia": ["Ensiklopedia Cetak", "Ensiklopedia Online"], | "Ensiklopedia": ["Ensiklopedia Cetak", "Ensiklopedia Online", "Kamus / Glosarium"], | ||
"Arsip": ["Arsip Nasional", "Web Archive (Wayback Machine)"] | "Arsip": ["Arsip Nasional / Sejarah", "Web Archive (Wayback Machine)", "Dokumen Manuskrip"] | ||
}; | }; | ||
// | var VERIFIED_DOMAINS = [ | ||
'kompas.com', 'kompas.id', 'detik.com', 'cnnindonesia.com', 'tribunnews.com', 'kumparan.com', 'tirto.id', | |||
'youtube.com', 'spotify.com', 'apple.com', 'musicbrainz.org', 'billboard.com', 'rollingstone.com', | |||
'bbc.com', 'nytimes.com', 'instagram.com', 'x.com', 'twitter.com', 'facebook.com', 'wikipedia.org', 'imdb.com' | |||
]; | |||
// 3. IKON SISTEM | |||
var svgEdit = '<svg class="md-icon" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path></svg>'; | var svgEdit = '<svg class="md-icon" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path></svg>'; | ||
var svgLock = '<svg class="md-icon" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 10 0v4"></path></svg>'; | var svgLock = '<svg class="md-icon" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 10 0v4"></path></svg>'; | ||
| Baris 73: | Baris 74: | ||
var svgSearch = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>'; | var svgSearch = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>'; | ||
var svgCode = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="16 18 22 12 16 6"></polyline><polyline points="8 6 2 12 8 18"></polyline></svg>'; | var svgCode = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="16 18 22 12 16 6"></polyline><polyline points="8 6 2 12 8 18"></polyline></svg>'; | ||
var | var svgVerified = '<svg width="15" height="15" viewBox="0 0 24 24" fill="#3b82f6" stroke="#ffffff" stroke-width="2"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path><polyline points="22 4 12 14.01 9 11.01"></polyline></svg>'; | ||
var svgHelp = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"></circle><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path><line x1="12" y1="17" x2="12.01" y2="17"></line></svg>'; | |||
var | |||
// | // 4. HELPER UTILITY & ADVANCED SECURITY | ||
function | function sanitizeHTML(str) { | ||
if (typeof str !== 'string') return str; | |||
return str.replace(/[&<>"'`=\/]/g, function(s) { | |||
return { | |||
'&': '&', '<': '<', '>': '>', '"': '"', | |||
"'": ''', '/': '/', '`': '`', '=': '=' | |||
}[s]; | |||
}); | |||
} | |||
function isVerifiedSite(urlStr) { | |||
if(!urlStr) return false; | |||
var domain = extractDomain(urlStr); | |||
return VERIFIED_DOMAINS.some(function(vd) { return domain === vd || domain.endsWith('.' + vd); }); | |||
} | } | ||
function formatIndonesianDate(dateStr) { | function formatIndonesianDate(dateStr) { | ||
if (!dateStr) return ''; | if (!dateStr) return ''; | ||
var parts = dateStr.split('-'); | var parts = dateStr.split('-'); | ||
if (parts.length !== 3) return dateStr; | if (parts.length !== 3) return dateStr; | ||
var months = [' | var months = ['Januari', 'Februari', 'Maret', 'April', 'Mei', 'Juni', 'Juli', 'Agustus', 'September', 'Oktober', 'November', 'Desember']; | ||
var day = parseInt(parts[2], 10); | var day = parseInt(parts[2], 10); | ||
var monthIdx = parseInt(parts[1], 10) - 1; | var monthIdx = parseInt(parts[1], 10) - 1; | ||
var year = parts[0]; | var year = parts[0]; | ||
if (isNaN(day) || monthIdx < 0 || monthIdx > 11 || isNaN(year)) return dateStr; | if (isNaN(day) || monthIdx < 0 || monthIdx > 11 || isNaN(year)) return sanitizeHTML(dateStr); | ||
return day + ' ' + months[monthIdx] + ' ' + year; | return day + ' ' + months[monthIdx] + ' ' + year; | ||
} | } | ||
function extractDomain(urlStr) { | function extractDomain(urlStr) { | ||
try { | try { | ||
| Baris 107: | Baris 109: | ||
} catch(e) { return urlStr.replace(/^https?:\/\/(www\.)?/, '').split('/')[0]; } | } catch(e) { return urlStr.replace(/^https?:\/\/(www\.)?/, '').split('/')[0]; } | ||
} | } | ||
function ensureHttps(urlStr) { | function ensureHttps(urlStr) { | ||
if (!urlStr) return ''; | if (!urlStr) return ''; | ||
urlStr = urlStr.trim(); | urlStr = urlStr.trim(); | ||
if (urlStr && !urlStr.match(/^https?:\/\//i)) | var protoMatch = urlStr.match(/^([a-z0-9]+):/i); | ||
if (protoMatch) { | |||
var badProtos = ['javascript', 'data', 'vbscript', 'file', 'ws', 'wss', 'ftp']; | |||
if (badProtos.indexOf(protoMatch[1].toLowerCase()) !== -1) return ''; | |||
} | |||
return (urlStr && !urlStr.match(/^https?:\/\//i)) ? 'https://' + urlStr : urlStr; | |||
} | } | ||
function getTodayString() { | function getTodayString() { | ||
var now = new Date(); | var now = new Date(); | ||
| Baris 121: | Baris 125: | ||
var d = String(now.getDate()).padStart(2, '0'); | var d = String(now.getDate()).padStart(2, '0'); | ||
return y + '-' + m + '-' + d; | return y + '-' + m + '-' + d; | ||
} | } | ||
// 5. STYLESHEET (TERMASUK LAYOUT MEDIA SOSIAL GLOBAL & AUTOCOMPLETE) | |||
// | |||
var css = ` | var css = ` | ||
#mippedia-data-app { | #mippedia-data-app { | ||
background: | background: #ffffff; border-radius: 16px; box-shadow: 0 4px 24px rgba(0,0,0,0.06); | ||
padding: 20px; margin: 20px 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto | padding: 20px; margin: 20px 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; | ||
border: 1px solid | border: 1px solid #e2e8f0; max-width: 100%; box-sizing: border-box; overflow: hidden; | ||
} | } | ||
.md-header { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 12px; margin-bottom: 16px; padding-bottom: 14px; border-bottom: 1px solid #f1f5f9; } | |||
.md-title-container { display: flex; align-items: center; gap: 8px; flex: 1 1 auto; min-width: 180px; } | .md-title-container { display: flex; align-items: center; gap: 8px; flex: 1 1 auto; min-width: 180px; } | ||
.md-title { font-size: 1.25rem; font-weight: 800; margin: 0; white-space: nowrap | .md-title { font-size: 1.25rem; font-weight: 800; color: #0f172a; margin: 0; white-space: nowrap; } | ||
.md-actions { display: flex; gap: | .md-actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; } | ||
.md-btn-icon { | .md-btn-icon { | ||
background: | background: #f8fafc; border: 1px solid #cbd5e1; cursor: pointer; color: #4f46e5; | ||
padding: 8px | padding: 8px 14px; border-radius: 8px; transition: all 0.2s ease; | ||
display: inline-flex; align-items: center; gap: 6px; font-size: 0. | display: inline-flex; align-items: center; gap: 6px; font-size: 0.85rem; font-weight: 600; white-space: nowrap; | ||
} | } | ||
.md-btn-icon:hover:not(:disabled) { background: | .md-btn-icon:hover:not(:disabled) { background: #f1f5f9; border-color: #94a3b8; transform: translateY(-1px); } | ||
.md-btn-lock { color: #dc2626; background: #fef2f2; border-color: #fca5a5; } | |||
.md-search-box { position: relative; margin-bottom: 16px; width: 100%; box-sizing: border-box; } | |||
.md- | .md-search-input { width: 100%; padding: 8px 12px 8px 34px; border: 1px solid #cbd5e1; border-radius: 8px; font-size: 0.85rem; box-sizing: border-box; background: #f8fafc; transition: 0.2s; } | ||
.md- | .md-search-input:focus { background: #fff; border-color: #4f46e5; outline: none; } | ||
.md- | .md-search-icon { position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: #94a3b8; } | ||
.md- | |||
.md- | /* Kotak Global Sosial Media Terpisah di Atas */ | ||
.md- | .md-global-social-box { background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 12px; padding: 16px; margin-bottom: 16px; box-shadow: 0 2px 8px rgba(0,0,0,0.02); } | ||
.md-social-box-title { font-size: 0.85rem; font-weight: 800; color: #475569; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 12px; border-bottom: 1px dashed #cbd5e1; padding-bottom: 8px; } | |||
.md-social-group { display: flex; flex-wrap: wrap; gap: 8px; } | |||
.md-social-badge { display: inline-flex; align-items: center; gap: 6px; padding: 6px 12px; border-radius: 20px; font-size: 0.8rem; font-weight: 600; color: white !important; text-decoration: none !important; transition: transform 0.2s, opacity 0.2s; box-shadow: 0 2px 4px rgba(0,0,0,0.05); } | |||
.md- | .md-social-badge:hover { transform: translateY(-2px); opacity: 0.92; } | ||
.md- | |||
.md-row-display { display: flex; justify-content: space-between; align-items: flex-start; padding: 16px 12px; border-bottom: 1px solid #f1f5f9; transition: all 0.2s; border-radius: 12px; gap: 12px; margin-bottom: 8px; background: #fff; } | |||
.md-row-display { | .md-row-display:hover { background: #faf5ff; box-shadow: inset 4px 0 0 #6366f1; } | ||
.md-row-display:hover { background: | |||
.md-data-group { display: flex; flex-direction: column; gap: 6px; flex: 1; min-width: 0; } | .md-data-group { display: flex; flex-direction: column; gap: 6px; flex: 1; min-width: 0; } | ||
.md-data-label { font-size: 0.72rem; color: | .md-data-label { font-size: 0.72rem; color: #64748b; font-weight: 800; text-transform: uppercase; letter-spacing: 0.6px; } | ||
.md-data-value-wrap { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; } | .md-data-value-wrap { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; } | ||
.md-data-value { font-size: 1. | .md-data-value { font-size: 1.02rem; color: #0f172a; line-height: 1.4; font-weight: 600; word-break: break-word; } | ||
.md- | |||
.md- | .md-btn-copy { background: none; border: none; color: #94a3b8; cursor: pointer; padding: 2px 4px; border-radius: 4px; transition: 0.2s; display: inline-flex; align-items: center; } | ||
.md-btn-copy:hover { color: #4f46e5; background: #e0e7ff; } | |||
.md-ref-container { margin-top: 10px; width: 100%; box-sizing: border-box; } | |||
.md-ref-container { margin-top: | .md-ref-details { background: #f8fafc; border-left: 3px solid #4f46e5; border-radius: 10px; overflow: hidden; border-top:1px solid #f1f5f9; border-right:1px solid #f1f5f9; border-bottom:1px solid #f1f5f9; transition: 0.3s; } | ||
.md-ref- | .md-ref-details summary { display: flex; padding: 10px 14px; font-size: 0.85rem; font-weight: 700; color: #374151; cursor: pointer; user-select: none; list-style: none; flex-wrap: nowrap; align-items: center; gap: 8px; } | ||
.md-ref-details summary::-webkit-details-marker { display: none; } | |||
.md-ref-details[open] summary { border-bottom: 1px dashed #cbd5e1; background: #e0e7ff; color: #3730a3; } | |||
.md-ref-body { padding: 12px 14px; font-size: 0.85rem; display: flex; flex-direction: column; gap: 6px; } | |||
.md-ref- | |||
.md-ref- | |||
.md- | .md-verified-badge { cursor: pointer; display: inline-flex; align-items: center; justify-content: center; margin-left: 4px; vertical-align: middle; transition: transform 0.2s; flex-shrink: 0; } | ||
.md-verified-badge:hover { transform: scale(1.1); } | |||
.md-btn-visit { background: #f1f5f9; color: #0f172a; padding: 6px 12px; border-radius: 6px; text-decoration: none; font-size: 0.8rem; display: inline-block; width: fit-content; margin-top: 6px; font-weight:700; border:1px solid #cbd5e1; } | |||
.md-btn-visit:hover { background: #e2e8f0; text-decoration:none; } | |||
.md- | |||
.md- | |||
.md-btn- | |||
.md-preset-wrapper { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; } | |||
.md- | .md-preset-pill { background: #f1f5f9; border: 1px solid #cbd5e1; padding: 4px 10px; border-radius: 12px; font-size: 0.75rem; font-weight: 600; color: #475569; cursor: pointer; transition: 0.2s; } | ||
.md- | .md-preset-pill:hover { background: #e0e7ff; color: #3730a3; border-color: #a5b4fc; } | ||
.md- | |||
.md-form-box { background: #ffffff; border-radius: 12px; padding: 18px; margin-top: 16px; border: 1px solid #cbd5e1; box-shadow: 0 4px 16px rgba(0,0,0,0.05); } | |||
.md-form-box { background: | .md-form-title { font-size: 1.05rem; font-weight: 800; color: #0f172a; margin: 0 0 16px 0; border-bottom: 2px solid #e2e8f0; padding-bottom: 8px; } | ||
.md-form-title { font-size: 1. | .md-form-group { margin-bottom: 14px; } | ||
.md-form-group { margin-bottom: 14px | .md-form-group label { display: flex; align-items:center; font-size: 0.82rem; font-weight: 700; margin-bottom: 6px; color: #334155; } | ||
.md-form-group label { display: | |||
.md- | .md-input, .md-select, .md-textarea { width: 100%; padding: 9px 12px; border: 1px solid #cbd5e1; border-radius: 8px; box-sizing: border-box; font-family: inherit; font-size: 0.9rem; transition: 0.2s; background: #fff; } | ||
.md- | .md-input:focus, .md-select:focus, .md-textarea:focus { border-color: #4f46e5; outline: none; box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.15); } | ||
.md-ref-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; background: | /* Autocomplete Styles */ | ||
.md-social-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; background: | .md-autocomplete-wrapper { position: relative; width: 100%; } | ||
.md-autocomplete-list { position: absolute; top: 100%; left: 0; right: 0; background: #fff; border: 1px solid #cbd5e1; border-radius: 0 0 8px 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); z-index: 1000; display: none; max-height: 200px; overflow-y: auto; margin-top: -2px; } | |||
.md-autocomplete-item { padding: 10px 12px; font-size: 0.88rem; color: #0f172a; cursor: pointer; border-bottom: 1px solid #f1f5f9; transition: 0.2s; display: flex; align-items: center; gap: 8px; } | |||
.md-autocomplete-item:last-child { border-bottom: none; } | |||
.md-autocomplete-item:hover { background: #f8fafc; color: #4f46e5; padding-left: 16px; } | |||
.md-ref-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; background: #f8fafc; padding: 12px; border-radius: 8px; border: 1px dashed #cbd5e1; } | |||
.md-social-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; background: #f1f5f9; padding: 12px; border-radius: 8px; border: 1px solid #cbd5e1; max-height: 280px; overflow-y: auto; } | |||
.md-grid-full { grid-column: span 2; } | .md-grid-full { grid-column: span 2; } | ||
.md-form-actions { display: flex; gap: 10px; align-items: center; margin-top: 20px; padding-top: 14px; border-top: 1px solid | .md-cc0-notice { background: #fef2f2; color: #991b1b; padding: 12px; border-radius: 8px; font-size: 0.82rem; line-height: 1.4; border-left: 4px solid #ef4444; margin-top: 15px; margin-bottom: 15px; } | ||
.md-edit-meta { background: #f8fafc; padding: 12px; border-radius: 8px; margin-bottom: 15px; border: 1px solid #e2e8f0; display:flex; flex-direction:column; gap:10px; } | |||
.md-help-icon { color: #64748b; cursor: pointer; margin-left: 6px; display:inline-flex; align-items:center; } | |||
.md-help-icon:hover { color: #4f46e5; } | |||
.md-tooltip-popup { position: absolute; background: #1e293b; color: #fff; padding: 10px 14px; border-radius: 8px; font-size: 0.8rem; width: 260px; max-width: calc(100vw - 30px); z-index: 1000; box-shadow: 0 4px 15px rgba(0,0,0,0.15); display: none; line-height: 1.4; text-align: left; font-weight:normal; } | |||
.md-tooltip-popup::after { content: ''; position: absolute; border-width: 6px; border-style: solid; border-color: #1e293b transparent transparent transparent; top: 100%; left: 15px; } | |||
.md-modal-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.5); z-index: 9999; display: flex; align-items: center; justify-content: center; backdrop-filter: blur(2px); } | |||
.md-modal-box { background: #fff; padding: 24px; border-radius: 12px; max-width: 400px; text-align: center; box-shadow: 0 10px 25px rgba(0,0,0,0.2); animation: mdPopIn 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); } | |||
@keyframes mdPopIn { 0% { transform: scale(0.8); opacity: 0; } 100% { transform: scale(1); opacity: 1; } } | |||
.md-form-actions { display: flex; gap: 10px; align-items: center; margin-top: 20px; padding-top: 14px; border-top: 1px solid #e2e8f0; flex-wrap: wrap; } | |||
.md-btn { padding: 10px 18px; border: none; border-radius: 8px; font-weight: 700; cursor: pointer; color: white; font-size: 0.85rem; transition: 0.2s; display: inline-flex; align-items: center; gap: 6px; justify-content: center; } | .md-btn { padding: 10px 18px; border: none; border-radius: 8px; font-weight: 700; cursor: pointer; color: white; font-size: 0.85rem; transition: 0.2s; display: inline-flex; align-items: center; gap: 6px; justify-content: center; } | ||
.md-btn:hover:not(:disabled) { transform: translateY(- | .md-btn:hover:not(:disabled) { transform: translateY(-1px); } | ||
.md-btn-save { background: linear-gradient(135deg, #4f46e5, #4338ca); flex: 1; min-width: 140px; } | .md-btn-save { background: linear-gradient(135deg, #4f46e5, #4338ca); flex: 1; min-width: 140px; } | ||
.md-btn-cancel { background: #64748b; } | .md-btn-cancel { background: #64748b; } | ||
.md-btn-draft { background: #10b981; } | |||
.md-btn-delete { background: #dc2626; margin-left: auto; } | .md-btn-delete { background: #dc2626; margin-left: auto; } | ||
.md- | .md-spinner { display: none; width: 14px; height: 14px; border: 2px solid rgba(255,255,255,0.4); border-radius: 50%; border-top-color: #fff; animation: md-spin 0.8s linear infinite; } | ||
.is-loading .md-spinner { display: inline-block; } | |||
@keyframes md-spin { to { transform: rotate(360deg); } } | |||
/* Mobile Optimized Layout (Seluler) */ | |||
@media (max-width: 600px) { | @media (max-width: 600px) { | ||
#mippedia-data-app { padding: 14px; margin: | #mippedia-data-app { padding: 14px; margin: 14px 0; border-radius: 12px; } | ||
.md-header { | .md-header { flex-direction: column; align-items: flex-start; gap: 10px; } | ||
.md-actions { width: 100%; justify-content: flex-start; overflow-x: auto; padding-bottom: 5px;} | .md-actions { width: 100%; justify-content: flex-start; overflow-x:auto; padding-bottom:5px; } | ||
.md-btn-icon { padding: 7px | .md-btn-icon { padding: 7px 12px; font-size: 0.8rem; } | ||
.md-ref-grid, .md-social-grid { grid-template-columns: 1fr; } | .md-ref-grid, .md-social-grid { grid-template-columns: 1fr; } | ||
.md-grid-full { grid-column: span 1; } | .md-grid-full { grid-column: span 1; } | ||
.md-form-actions { flex-direction: column; width: 100%; } | .md-form-actions { flex-direction: column; width: 100%; } | ||
.md-btn { width: 100%; margin: 0 !important; } | .md-btn { width: 100%; margin: 0 !important; } | ||
.md-row-display { padding: 12px | .md-row-display { padding: 12px 10px; flex-direction:column; align-items:stretch;} | ||
. | .edit-btn { align-self:flex-end; margin-top:8px; } | ||
.md- | .md-ref-details summary { align-items: flex-start; } | ||
} | } | ||
.md-native-render { display: none; } | .md-native-render { display: none; } | ||
`; | `; | ||
$('<style | $('<style>').text(css).appendTo('head'); | ||
// 6. POPUP, MODAL & TOOLTIPS | |||
function showModal(title, text) { | |||
$('.md-modal-overlay').remove(); | |||
var $overlay = $('<div class="md-modal-overlay"></div>'); | |||
var $box = $('<div class="md-modal-box"><h3 style="margin-top:0; color:#0f172a;">' + sanitizeHTML(title) + '</h3><p style="font-size:0.9rem; color:#475569; margin:15px 0;">' + sanitizeHTML(text) + '</p><button class="md-btn md-btn-save" style="width:100%;">Tutup & Mengerti</button></div>'); | |||
$box.find('button').on('click', function() { $overlay.remove(); }); | |||
$overlay.append($box); | |||
$('body').append($overlay); | |||
} | |||
// 7. PARSER & PENYIMPANAN DATA | function initTooltips() { | ||
$('.md-help-icon').off('click').on('click', function(e) { | |||
$('.md-tooltip-popup').remove(); | |||
e.stopPropagation(); | |||
var $icon = $(this); | |||
var text = sanitizeHTML($icon.data('help')); | |||
var $tt = $('<div class="md-tooltip-popup">' + text + '</div>'); | |||
$('body').append($tt); | |||
var offset = $icon.offset(); | |||
var ttWidth = $tt.outerWidth(); | |||
var winWidth = $(window).width(); | |||
var leftPos = offset.left - 15; | |||
if (leftPos + ttWidth > winWidth - 15) leftPos = winWidth - ttWidth - 15; | |||
if (leftPos < 10) leftPos = 10; | |||
$tt.css({ top: offset.top - $tt.outerHeight() - 10, left: leftPos }).fadeIn(200); | |||
}); | |||
$(document).off('click.mdtt').on('click.mdtt', function() { $('.md-tooltip-popup').fadeOut(200, function(){ $(this).remove(); }); }); | |||
} | |||
// 7. PARSER WIKITEXT & PENYIMPANAN DATA | |||
function loadData() { | function loadData() { | ||
return api.get({ | return api.get({ | ||
| Baris 315: | Baris 299: | ||
function buildWikitextVal(item) { | function buildWikitextVal(item) { | ||
var val = item.val; | var val = item.val; | ||
if (item.linkTarget && item.linkTarget.trim() !== '') return "[[" + item.linkTarget.trim() + "|" + val + "]]"; | if (item.linkTarget && item.linkTarget.trim() !== '') { | ||
return "[[" + item.linkTarget.trim() + "|" + val + "]]"; | |||
} | |||
if (item.linkSplit) { | if (item.linkSplit) { | ||
return val.replace(/([a-zA-Z0-9_À-ÿ\- ]+)/g, function(match) { | return val.replace(/([a-zA-Z0-9_À-ÿ\- ]+)/g, function(match) { | ||
var trimmed = match.trim(); | var trimmed = match.trim(); | ||
if (trimmed.length > 2 && trimmed.toLowerCase() !== 'dan' && trimmed.toLowerCase() !== 'atau') return "[[" + trimmed + "]]"; | if (trimmed.length > 2 && trimmed.toLowerCase() !== 'dan' && trimmed.toLowerCase() !== 'atau') { | ||
return "[[" + trimmed + "]]"; | |||
} | |||
return match; | return match; | ||
}); | }); | ||
} | } | ||
if (item.link) return "[[" + val + "]]"; | if (item.link) { | ||
return "[[" + val + "]]"; | |||
} | |||
return val; | return val; | ||
} | } | ||
| Baris 329: | Baris 319: | ||
function buildWikitextRef(refObj) { | function buildWikitextRef(refObj) { | ||
if (!refObj) return ""; | if (!refObj) return ""; | ||
var title = refObj.title | var title = refObj.title || "Rujukan"; | ||
var text = refObj.url ? "[" + ensureHttps(refObj.url) + " " + title + "]" : "'''" + title + "'''"; | var text = refObj.url ? "[" + ensureHttps(refObj.url) + " " + title + "]" : "'''" + title + "'''"; | ||
var details = []; | var details = []; | ||
| Baris 342: | Baris 332: | ||
} | } | ||
function saveData( | function saveData(actionType, $btnEl, originalContent, customSummary, isMinorEdit) { | ||
if ($btnEl) | if ($btnEl) $btnEl.addClass('is-loading').prop('disabled', true); | ||
var summaryTxt = customSummary ? ('Mippedia Data: ' + customSummary) : ('Mippedia Data V8.7: ' + actionType); | |||
var jsonStr = JSON.stringify({ locked: isLocked, items: dataItems }); | var jsonStr = JSON.stringify({ locked: isLocked, items: dataItems }); | ||
var wt = "<!-- MIPPEDIA_DATA_START | " + jsonStr + " -->\n<div class=\"md-native-render\">\n{| class=\"wikitable\"\n! Data !! Nilai !! Referensi\n"; | var wt = "<!-- MIPPEDIA_DATA_START | " + jsonStr + " -->\n"; | ||
wt += "<div class=\"md-native-render\">\n{| class=\"wikitable\"\n! Data !! Nilai !! Referensi\n"; | |||
dataItems.forEach(function(item) { | dataItems.forEach(function(item) { | ||
wt += "|-\n| " + item.prop + " || " + | var valTxt = buildWikitextVal(item); | ||
var refTxt = buildWikitextRef(item.ref); | |||
wt += "|-\n| " + item.prop + " || " + valTxt + " || " + refTxt + "\n"; | |||
}); | }); | ||
wt += "|}\n</div>\n<!-- MIPPEDIA_DATA_END -->"; | wt += "|}\n</div>\n<!-- MIPPEDIA_DATA_END -->"; | ||
| Baris 355: | Baris 349: | ||
var newContent; | var newContent; | ||
var regex = /<!-- MIPPEDIA_DATA_START \| .*? -->[\s\S]*?<!-- MIPPEDIA_DATA_END -->/; | var regex = /<!-- MIPPEDIA_DATA_START \| .*? -->[\s\S]*?<!-- MIPPEDIA_DATA_END -->/; | ||
if (regex.test(originalContent)) newContent = originalContent.replace(regex, wt); | if (regex.test(originalContent)) { | ||
else newContent = originalContent.trim() + "\n\n" + wt; | newContent = originalContent.replace(regex, wt); | ||
} else { | |||
newContent = originalContent.trim() + "\n\n" + wt; | |||
} | |||
api.postWithToken('csrf', { | api.postWithToken('csrf', { | ||
action: 'edit', title: pageName, text: newContent, | action: 'edit', title: pageName, text: newContent, summary: summaryTxt, minor: isMinorEdit || false | ||
}).done(function() { | }).done(function() { | ||
localStorage.removeItem(DRAFT_KEY); | |||
var nocacheUrl = location.pathname + "?refresh=" + new Date().getTime(); | |||
window.location.href = nocacheUrl; | |||
}).fail(function(err) { | }).fail(function(err) { | ||
if ($btnEl) $btnEl. | if ($btnEl) $btnEl.removeClass('is-loading').prop('disabled', false); | ||
alert("Gagal menyimpan! Error: " + err); | |||
}); | }); | ||
} | } | ||
| Baris 374: | Baris 371: | ||
var $app = $('<div id="mippedia-data-app"></div>'); | var $app = $('<div id="mippedia-data-app"></div>'); | ||
var $header = $('<div class="md-header"></div>'); | var $header = $('<div class="md-header"></div>'); | ||
var $titleContainer = $('<div class="md-title-container"><h3 class="md-title">' + svgEdit + ' Data Terstruktur | var $titleContainer = $('<div class="md-title-container"><h3 class="md-title">' + svgEdit + ' Data Terstruktur</h3></div>'); | ||
var $actions = $('<div class="md-actions"></div>'); | var $actions = $('<div class="md-actions"></div>'); | ||
var $jsonBtn = $('<button class="md-btn-icon" title="Ekspor/Impor JSON">' + svgCode + ' JSON</button>'); | |||
var $ | |||
$jsonBtn.on('click', function() { showJsonModal(rawContent); }); | $jsonBtn.on('click', function() { showJsonModal(rawContent); }); | ||
$actions.append($jsonBtn); | $actions.append($jsonBtn); | ||
if (isSysop) { | if (isSysop) { | ||
var $lockBtn = $('<button class="md-btn-icon">' + (isLocked ? svgUnlock + ' Buka Kunci' : svgLock + ' Kunci') + '</button>'); | var $lockBtn = $('<button class="md-btn-icon ' + (isLocked ? 'md-btn-unlock' : 'md-btn-lock') + '">' + (isLocked ? svgUnlock + ' Buka Kunci' : svgLock + ' Kunci') + '</button>'); | ||
$lockBtn.on('click', function() { | $lockBtn.on('click', function() { | ||
isLocked = !isLocked; | isLocked = !isLocked; | ||
saveData(isLocked ? 'Mengunci data' : 'Membuka kunci data', $(this), rawContent | saveData(isLocked ? 'Mengunci data' : 'Membuka kunci data', $(this), rawContent); | ||
}); | }); | ||
$actions.append($lockBtn); | $actions.append($lockBtn); | ||
| Baris 408: | Baris 389: | ||
if (!isLocked || isSysop) { | if (!isLocked || isSysop) { | ||
var $addBtn = $('<button class="md-btn-icon" | var $addBtn = $('<button class="md-btn-icon" title="Tambah Data Baru">' + svgEdit + ' Tambah</button>'); | ||
$addBtn.on('click', function() { showForm(-1, rawContent); }); | $addBtn.on('click', function() { showForm(-1, rawContent); }); | ||
$actions.append($addBtn); | $actions.append($addBtn); | ||
| Baris 416: | Baris 397: | ||
$app.append($header); | $app.append($header); | ||
// | // Tampilkan kotak pencarian jika data banyak | ||
if (dataItems.length > | if (dataItems.length > 2) { | ||
var $searchBox = $('<div class="md-search-box"><span class="md-search-icon">' + svgSearch + '</span><input type="text" class="md-search-input" placeholder="Cari data terstruktur..."></div>'); | |||
var $searchBox = $('<div class="md-search-box"><span class="md-search-icon">' + svgSearch + '</span><input type="text" class="md-search-input" placeholder="Cari data | $searchBox.find('input').on('keyup input', function() { | ||
var q = $(this).val().toLowerCase(); | |||
$ | $app.find('.md-row-display').each(function() { | ||
$ | var text = $(this).text().toLowerCase(); | ||
$(this).toggle(text.indexOf(q) !== -1); | |||
}); | |||
}); | }); | ||
$app.append($searchBox); | |||
} | |||
$app | |||
// MENGUMPULKAN MEDIA SOSIAL GLOBAL (Memisahkan media sosial ke atas) | |||
var globalSocials = {}; | |||
dataItems.forEach(function(item) { | |||
if (item.isPersonOrg && item.socials) { | |||
Object.assign(globalSocials, item.socials); | |||
} | } | ||
}); | |||
if (Object.keys(globalSocials).length > 0) { | |||
var $topSocials = $('<div class="md-global-social-box"></div>'); | |||
$topSocials.append('<div class="md-social-box-title">Media Sosial & Platform Resmi</div>'); | |||
var $socialGroupGlobal = $('<div class="md-social-group"></div>'); | |||
Object.keys(SOCIAL_PLATFORMS).forEach(function(key) { | |||
var uname = globalSocials[key]; | |||
if (uname) { | |||
var platform = SOCIAL_PLATFORMS[key]; | |||
var fullUrl = platform.base + uname.replace(/^@/, ''); | |||
var $badge = $('<a class="md-social-badge" href="' + sanitizeHTML(fullUrl) + '" target="_blank" rel="noopener noreferrer" style="background:' + platform.color + ';">' + platform.icon + ' ' + sanitizeHTML(platform.name) + '</a>'); | |||
$socialGroupGlobal.append($badge); | |||
} | |||
}); | |||
$topSocials.append($socialGroupGlobal); | |||
$app.append($topSocials); | |||
} | } | ||
var $list = $('<div class="md-list"></div>'); | var $list = $('<div class="md-list"></div>'); | ||
if (dataItems.length === 0) { | if (dataItems.length === 0) { | ||
$list.append('<div style="text-align:center; padding: | $list.append('<div style="color:#64748b; text-align:center; padding: 25px 10px; background:#f8fafc; border-radius:10px; border:1px dashed #cbd5e1; font-size:0.88rem;">Belum ada data terstruktur. Klik <b>Tambah</b> untuk membuat baru.</div>'); | ||
} else { | } else { | ||
dataItems.forEach(function(item, index) { | dataItems.forEach(function(item, index) { | ||
var $row = $('<div class="md-row-display" | var $row = $('<div class="md-row-display"></div>'); | ||
var $info = $('<div class="md-data-group"></div>'); | var $info = $('<div class="md-data-group"></div>'); | ||
$info.append('<span class="md-data-label">' + item.prop + '</span>'); | $info.append('<span class="md-data-label">' + sanitizeHTML(item.prop) + '</span>'); | ||
var displayValHtml = ''; | |||
var rawVal = item.val || ''; | var rawVal = item.val || ''; | ||
if (rawVal.match(/^https?:\/\//i)) { | if (rawVal.match(/^https?:\/\//i)) { | ||
displayValHtml = '<a href="' + rawVal + '" target="_blank">' + extractDomain(rawVal) | displayValHtml = '<a href="' + sanitizeHTML(rawVal) + '" target="_blank" rel="noopener noreferrer" style="color:#2563eb; font-weight:700; text-decoration:none;">' + extractDomain(rawVal) + '</a>'; | ||
} else if (item.linkTarget && item.linkTarget.trim() !== '') { | } else if (item.linkTarget && item.linkTarget.trim() !== '') { | ||
displayValHtml = '<a href="' + mw.util.getUrl(item.linkTarget.trim()) + '">' + rawVal + '</a>'; | displayValHtml = '<a href="' + mw.util.getUrl(item.linkTarget.trim()) + '" style="color:#2563eb; font-weight:700; text-decoration:none;">' + sanitizeHTML(rawVal) + '</a>'; | ||
} else if (item.linkSplit) { | } else if (item.linkSplit) { | ||
var parts = rawVal.split(/([,/\s]+dan\s+|[,/\s]+atau\s+|[,/]+)/gi); | var parts = rawVal.split(/([,/\s]+dan\s+|[,/\s]+atau\s+|[,/]+)/gi); | ||
displayValHtml = parts.map(function(p) { | displayValHtml = parts.map(function(p) { | ||
var trimmed = p.trim(); | var trimmed = p.trim(); | ||
if (trimmed && !p.match(/^[,/\s]+$/) && ! | if (trimmed && !p.match(/^[,/\s]+$/) && trimmed.toLowerCase() !== 'dan' && trimmed.toLowerCase() !== 'atau') { | ||
return '<a href="' + mw.util.getUrl(trimmed) + '">' + trimmed + '</a>'; | return '<a href="' + mw.util.getUrl(trimmed) + '" style="color:#2563eb; font-weight:700; text-decoration:none;">' + sanitizeHTML(trimmed) + '</a>'; | ||
} | } | ||
return p; | return sanitizeHTML(p); | ||
}).join(''); | }).join(''); | ||
} else if (item.link) { | } else if (item.link) { | ||
displayValHtml = '<a href="' + mw.util.getUrl(rawVal) + '">' + rawVal + '</a>'; | displayValHtml = '<a href="' + mw.util.getUrl(rawVal) + '" style="color:#2563eb; font-weight:700; text-decoration:none;">' + sanitizeHTML(rawVal) + '</a>'; | ||
} else { | |||
displayValHtml = sanitizeHTML(rawVal); | |||
} | } | ||
| Baris 482: | Baris 470: | ||
$valWrap.append('<span class="md-data-value">' + displayValHtml + '</span>'); | $valWrap.append('<span class="md-data-value">' + displayValHtml + '</span>'); | ||
var $copyBtn = $('<button class="md-btn- | var $copyBtn = $('<button class="md-btn-copy" title="Salin teks">' + svgCopy + '</button>'); | ||
$copyBtn.on('click', function() { navigator.clipboard.writeText(rawVal); | $copyBtn.on('click', function() { | ||
navigator.clipboard.writeText(rawVal); | |||
$(this).css('color', '#16a34a'); | |||
setTimeout(function(){ $copyBtn.css('color', '#94a3b8'); }, 1200); | |||
}); | |||
$valWrap.append($copyBtn); | $valWrap.append($copyBtn); | ||
$info.append($valWrap); | $info.append($valWrap); | ||
if (item.ref && (item.ref.url || item.ref.title)) { | if (item.ref && (item.ref.url || item.ref.title)) { | ||
var ref = item.ref; | var ref = item.ref; | ||
var | var isVer = isVerifiedSite(ref.url); | ||
var verBadgeHtml = isVer ? '<span class="md-verified-badge" title="Situs Populer Terverifikasi">' + svgVerified + '</span>' : ''; | |||
var safeUrl = ref.url ? ensureHttps(ref.url) : ''; | |||
var | var refTitle = ref.title || "Sumber Rujukan"; | ||
var | |||
var $refContainer = $('<div class="md-ref-container"></div>'); | |||
var $ | var $refDetails = $(` | ||
$ | <details class="md-ref-details"> | ||
<summary> | |||
<div style="color:#4f46e5; display:inline-flex; align-items:center; gap:5px; white-space:nowrap; flex-shrink:0;">${svgRef} REFERENSI:</div> | |||
<div style="color:#374151; display:flex; align-items:center; flex-wrap:wrap; flex:1;"> | |||
<span style="word-break:break-word; line-height:1.3;">${sanitizeHTML(refTitle)}</span>${verBadgeHtml} | |||
</div> | |||
</summary> | |||
<div class="md-ref-body"> | |||
${ref.author ? '<div><span style="color:#64748b; font-weight:700;">Penulis:</span> ' + sanitizeHTML(ref.author) + '</div>' : ''} | |||
${ref.site ? '<div><span style="color:#64748b; font-weight:700;">Situs:</span> ' + sanitizeHTML(ref.site) + '</div>' : ''} | |||
${ref.publisher ? '<div><span style="color:#64748b; font-weight:700;">Penerbit:</span> ' + sanitizeHTML(ref.publisher) + '</div>' : ''} | |||
${ref.date ? '<div><span style="color:#64748b; font-weight:700;">Tanggal Akses:</span> ' + formatIndonesianDate(ref.date) + '</div>' : ''} | |||
${ref.type ? '<div style="margin-top:4px;"><span style="background:#e2e8f0; padding:2px 8px; border-radius:6px; font-weight:700; font-size:0.75rem;">' + sanitizeHTML(ref.type) + (ref.subType ? ' • ' + sanitizeHTML(ref.subType) : '') + '</span></div>' : ''} | |||
${safeUrl ? '<a href="' + sanitizeHTML(safeUrl) + '" target="_blank" rel="noopener noreferrer" class="md-btn-visit">Kunjungi Situs</a>' : ''} | |||
</div> | |||
</details> | |||
`); | |||
$refDetails.find('.md-verified-badge').on('click', function(e) { | |||
e.preventDefault(); e.stopPropagation(); | |||
showModal('Situs Terverifikasi', 'Centang biru menandakan bahwa URL referensi ini valid dan berasal dari entitas, media publikasi besar, portal populer, atau pangkalan data resmi (seperti Kompas, YouTube, Apple Music, Spotify, dll) yang bereputasi kredibel.'); | |||
}); | }); | ||
$refContainer.append($ | $refContainer.append($refDetails); | ||
$info.append($refContainer); | $info.append($refContainer); | ||
} | } | ||
$row | $row.append($info); | ||
if (! | if (isLocked && !isSysop) { | ||
var $ | var $lockNotifyBtn = $('<button class="md-btn-icon edit-btn" style="color:#dc2626; border-color:#fca5a5; background:#fef2f2;">' + svgLock + ' Terkunci</button>'); | ||
var $editIcon = $('<button class="md-btn-icon | $lockNotifyBtn.on('click', function() { | ||
showModal('Halaman Dikunci', 'Halaman Data Terstruktur ini sedang dikunci sementara oleh pengurus untuk melindungi dari vandalisme atau perubahan yang tidak sah. Pengguna biasa atau anonim tidak dapat mengedit saat ini.'); | |||
}); | |||
$row.append($lockNotifyBtn); | |||
} else { | |||
var $editIcon = $('<button class="md-btn-icon edit-btn">' + svgEdit + ' Edit</button>'); | |||
$editIcon.on('click', function() { showForm(index, rawContent); }); | $editIcon.on('click', function() { showForm(index, rawContent); }); | ||
$row.append($editIcon); | |||
} | } | ||
$list.append($row); | $list.append($row); | ||
}); | }); | ||
| Baris 601: | Baris 542: | ||
} | } | ||
// 9. FORM EDIT & DRAFT LOGIC | |||
// 9. FORM EDIT | |||
function showForm(index, rawContent) { | function showForm(index, rawContent) { | ||
var $container = $('#md-form-container') | var $container = $('#md-form-container').empty(); | ||
var isNew = index === -1; | var isNew = index === -1; | ||
var draftData = localStorage.getItem(DRAFT_KEY); | |||
var currentData = isNew ? { prop: '', val: '', link: false, linkTarget: '', linkSplit: false, ref: {}, isPersonOrg: false, socials: {} } : dataItems[index]; | var currentData = isNew ? { prop: '', val: '', link: false, linkTarget: '', linkSplit: false, ref: {}, isPersonOrg: false, socials: {} } : dataItems[index]; | ||
if (isNew && draftData) { | |||
if(confirm("Anda memiliki draft yang belum tersimpan sebelumnya. Ingin melanjutkan draft tersebut?")) { | |||
try { currentData = JSON.parse(draftData); } catch(e) {} | |||
} | |||
} | |||
var currentRef = currentData.ref || {}; | var currentRef = currentData.ref || {}; | ||
var currentSocials = currentData.socials || {}; | var currentSocials = currentData.socials || {}; | ||
var $form = $('<div class="md-form-box"></div>'); | var $form = $('<div class="md-form-box"></div>'); | ||
$form.append('<h4 class="md-form-title">' + (isNew ? 'Tambah Data Baru' : 'Edit Data Terstruktur') + '</h4>'); | |||
var $propGroup = $('<div class="md-form-group"></div>'); | |||
var $propGroup = $('<div class="md-form-group"><label>Label Data ( | $propGroup.append('<label>Label Data <span class="md-help-icon" data-help="Nama bidang data (contoh: Pekerjaan, Sutradara, Tempat Lahir). Klik tombol preset di bawah untuk isi otomatis.">' + svgHelp + '</span></label>'); | ||
$propGroup.append('<input type="text" id="md-inp-prop" class="md-input" value="' + sanitizeHTML(currentData.prop || '') + '">'); | |||
var $presetWrap = $('<div | var $presetWrap = $('<div class="md-preset-wrapper"></div>'); | ||
PRESET_LABELS.forEach(function(lbl) { | PRESET_LABELS.forEach(function(lbl) { | ||
var $pill = $('<span class="md-preset-pill">' + lbl + '</span>'); | var $pill = $('<span class="md-preset-pill">' + lbl + '</span>'); | ||
$pill.on('click', function() { $ | $pill.on('click', function() { $('#md-inp-prop').val(lbl); }); | ||
$presetWrap.append($pill); | $presetWrap.append($pill); | ||
}); | }); | ||
| Baris 652: | Baris 575: | ||
$form.append($propGroup); | $form.append($propGroup); | ||
var $valBox = $('<div class="md-form-group"></div>'); | var $valBox = $('<div class="md-form-group"></div>'); | ||
$valBox.append('<label>Nilai Data | $valBox.append('<label>Nilai Data <span class="md-help-icon" data-help="Ketik nilai. Jika halaman sudah ada di Wiki, saran akan muncul (pratinjau) dan otomatis mencentang opsi Wikilink.">' + svgHelp + '</span></label>'); | ||
$ | // FITUR AUTOCOMPLETE PRATINJAU DIMULAI DI SINI | ||
var $autoWrap = $('<div class="md-autocomplete-wrapper"></div>'); | |||
var $valInput = $('<input type="text" id="md-inp-val" class="md-input" autocomplete="off" value="' + sanitizeHTML(currentData.val || '') + '">'); | |||
var $autoList = $('<div class="md-autocomplete-list"></div>'); | |||
$autoWrap.append($valInput).append($autoList); | |||
$valBox.append($autoWrap); | |||
var $wikilinkOpts = $('<div style="margin-top:8px; display:flex; flex-direction:column; gap:8px; background: | var $wikilinkOpts = $('<div style="margin-top:8px; display:flex; flex-direction:column; gap:8px; background:#f8fafc; padding:10px; border-radius:8px; border:1px solid #e2e8f0;"></div>'); | ||
$ | var chkLink = currentData.link ? 'checked' : ''; | ||
$wikilinkOpts.append('<label style="display:flex; align-items:center; gap:8px; font-size:0.82rem; font-weight:700; cursor:pointer;"><input type="checkbox" id="md-inp-split" ' + | var chkSplit = currentData.linkSplit ? 'checked' : ''; | ||
$ | |||
var $linkCheck = $('<input type="checkbox" id="md-inp-link" ' + chkLink + '>'); | |||
var $lblLinkCheck = $('<label style="display:flex; align-items:center; gap:8px; font-size:0.82rem; font-weight:700; cursor:pointer;"></label>'); | |||
$lblLinkCheck.append($linkCheck).append(' Jadikan Wikilink Standar'); | |||
// Logika pratinjau ketika diketik | |||
var isAutoChecked = false; | |||
$valInput.on('input', function() { | |||
var q = $(this).val(); | |||
// Jika input dikosongkan/berubah, kembalikan centang link ke false jika tadinya otomatis centang | |||
if (isAutoChecked) { | |||
$linkCheck.prop('checked', false); | |||
isAutoChecked = false; | |||
} | |||
if(q.length < 1) { | |||
$autoList.empty().hide(); | |||
return; | |||
} | |||
// Panggil API MediaWiki Prefix Search | |||
api.get({ | |||
action: 'query', | |||
list: 'prefixsearch', | |||
pssearch: q, | |||
pslimit: 6, | |||
format: 'json' | |||
}).done(function(data) { | |||
$autoList.empty(); | |||
if(data.query && data.query.prefixsearch && data.query.prefixsearch.length > 0) { | |||
data.query.prefixsearch.forEach(function(res) { | |||
var $item = $('<div class="md-autocomplete-item">' + svgSearch + ' ' + sanitizeHTML(res.title) + '</div>'); | |||
$item.on('click', function() { | |||
$valInput.val(res.title); | |||
$autoList.empty().hide(); | |||
$linkCheck.prop('checked', true); // OTOMATIS TERCENTANG | |||
isAutoChecked = true; | |||
}); | |||
$autoList.append($item); | |||
}); | |||
$autoList.show(); | |||
} else { | |||
$autoList.hide(); | |||
} | |||
}); | |||
}); | |||
// Sembunyikan pratinjau saat klik di luar | |||
$(document).on('click.mdauto', function(e) { | |||
if(!$(e.target).closest('.md-autocomplete-wrapper').length) { | |||
$autoList.hide(); | |||
} | |||
}); | |||
$wikilinkOpts.append($lblLinkCheck); | |||
$wikilinkOpts.append('<label style="display:flex; align-items:center; gap:8px; font-size:0.82rem; font-weight:700; cursor:pointer;"><input type="checkbox" id="md-inp-split" ' + chkSplit + '> Smart Split Wikilink</label>'); | |||
var $pipedGroup = $('<div style="margin-top:4px;"><label style="font-size:0.78rem; color:#475569; margin-bottom:4px;">Target Halaman Khusus (Opsional):</label><input type="text" id="md-inp-target" class="md-input" style="padding:6px 10px; font-size:0.85rem;" placeholder="Contoh: Diskografi Rumi Haitami" value="' + sanitizeHTML(currentData.linkTarget || '') + '"></div>'); | |||
$wikilinkOpts.append($pipedGroup); | |||
$valBox.append($wikilinkOpts); | $valBox.append($wikilinkOpts); | ||
$form.append($valBox); | $form.append($valBox); | ||
var $socialSec = $('<div class="md-form-group" style="margin-top:16px;"></div>'); | var $socialSec = $('<div class="md-form-group" style="margin-top:16px;"></div>'); | ||
$socialSec.append('<label style="display:flex; align-items:center; gap:8px; cursor:pointer; background: | var chkPerson = currentData.isPersonOrg ? 'checked' : ''; | ||
$socialSec.append('<label style="display:flex; align-items:center; gap:8px; cursor:pointer; background:#e0e7ff; padding:10px; border-radius:8px; font-weight:700; color:#3730a3; font-size:0.85rem;"><input type="checkbox" id="md-chk-person" ' + chkPerson + '> Simpan Profil Media Sosial (Akan muncul di paling atas) <span class="md-help-icon" data-help="Centang ini untuk mengisi data sosial media. Datanya akan dihimpun dan dimunculkan di kotak teratas data struktur.">' + svgHelp + '</span></label>'); | |||
var $socialGrid = $('<div class="md-social-grid" id="md-social-box" style="display:' + (currentData.isPersonOrg ? 'grid' : 'none') + '; margin-top:8px;"></div>'); | var $socialGrid = $('<div class="md-social-grid" id="md-social-box" style="display:' + (currentData.isPersonOrg ? 'grid' : 'none') + '; margin-top:8px;"></div>'); | ||
Object.keys(SOCIAL_PLATFORMS).forEach(function(key) { | Object.keys(SOCIAL_PLATFORMS).forEach(function(key) { | ||
var p = SOCIAL_PLATFORMS[key]; | var p = SOCIAL_PLATFORMS[key]; | ||
$socialGrid.append('<div><label style="font-size:0. | var valUname = currentSocials[key] || ''; | ||
$socialGrid.append('<div><label style="font-size:0.78rem; color:#334155; font-weight:700; margin-bottom:4px;">' + p.name + '</label><input type="text" id="md-soc-' + key + '" class="md-input" placeholder="ID / Username" value="' + sanitizeHTML(valUname) + '"></div>'); | |||
}); | }); | ||
$socialSec.append($socialGrid); | $socialSec.append($socialGrid); | ||
$form.append($socialSec); | $form.append($socialSec); | ||
var $refArea = $('<div class="md-form-group" style="margin-top:20px;"></div>'); | var $refArea = $('<div class="md-form-group" style="margin-top:20px;"></div>'); | ||
$refArea.append('<label style="font-size:0.95rem; font-weight:800; color: | $refArea.append('<label style="font-size:0.95rem; font-weight:800; color:#0f172a; margin-bottom:10px;">Sumber Referensi <span class="md-help-icon" data-help="Mohon berikan sumber referensi yang valid, kredibel, dan dapat dipertanggungjawabkan (seperti portal berita resmi, jurnal akademik ilmiah, buku cetak ber-ISBN, atau pangkalan data terverifikasi). Referensi yang kuat sangat membantu memastikan keakuratan informasi. Jika URL rujukan Anda mengarah ke situs web besar yang terverifikasi, sistem keamanan akan secara otomatis menampilkan lencana centang biru premium sebagai tanda sumber tepercaya.">' + svgHelp + '</span></label>'); | ||
var $refGrid = $('<div class="md-ref-grid"></div>'); | |||
var $ | |||
var $typeSel = $('<select id="md-ref-type" class="md-select"><option value="">-- Pilih Tipe Utama --</option></select>'); | |||
Object.keys(REF_TYPES).forEach(function(t) { | |||
var sel = currentRef.type === t ? 'selected' : ''; | |||
$typeSel.append('<option value="' + t + '" ' + sel + '>' + t + '</option>'); | |||
}); | |||
var $subTypeSel = $('<select id="md-ref-subtype" class="md-select"><option value="">-- Pilih Sub-Tipe --</option></select>'); | |||
function updateSubTypes(mainType, selectedSub) { | function updateSubTypes(mainType, selectedSub) { | ||
$subTypeSel.empty().append('<option value="">-- Sub-Tipe --</option>'); | $subTypeSel.empty().append('<option value="">-- Pilih Sub-Tipe --</option>'); | ||
if (mainType && REF_TYPES[mainType]) { | if (mainType && REF_TYPES[mainType]) { | ||
REF_TYPES[mainType].forEach(function(st) { $subTypeSel.append('<option value="'+st+'" '+ | REF_TYPES[mainType].forEach(function(st) { | ||
var sel = selectedSub === st ? 'selected' : ''; | |||
$subTypeSel.append('<option value="' + st + '" ' + sel + '>' + st + '</option>'); | |||
}); | |||
} | } | ||
} | } | ||
| Baris 698: | Baris 686: | ||
updateSubTypes(currentRef.type, currentRef.subType); | updateSubTypes(currentRef.type, currentRef.subType); | ||
$refGrid.append('<div><label style="font-size:0. | $refGrid.append('<div><label style="font-size:0.78rem; margin-bottom:4px;">Tipe Utama</label></div>').find('div').last().append($typeSel); | ||
$refGrid.append('<div><label style="font-size:0. | $refGrid.append('<div><label style="font-size:0.78rem; margin-bottom:4px;">Sub-Tipe</label></div>').find('div').last().append($subTypeSel); | ||
var $urlInput = $('<input type="text" id="md-ref-url" class="md-input" placeholder="https://...." value="' + sanitizeHTML(currentRef.url || '') + '">'); | |||
var $urlInput = $('<input type=" | |||
$urlInput.on('blur', function() { | $urlInput.on('blur', function() { | ||
var u = ensureHttps($(this).val()); $(this).val(u); | var u = ensureHttps($(this).val()); | ||
$(this).val(u); | |||
var $siteInp = $('#md-ref-site'); | |||
if (u && !$siteInp.val().trim()) $siteInp.val(extractDomain(u)); | |||
}); | }); | ||
$refGrid.append('<div class="md-grid-full"><label style="font-size:0. | $refGrid.append('<div class="md-grid-full"><label style="font-size:0.78rem; margin-bottom:4px;">URL Rujukan</label></div>').find('div').last().append($urlInput); | ||
$refGrid.append('<div><label style="font-size:0. | $refGrid.append('<div><label style="font-size:0.78rem; margin-bottom:4px;">Judul Halaman</label><input type="text" id="md-ref-title" class="md-input" placeholder="Judul Rujukan" value="' + sanitizeHTML(currentRef.title || '') + '"></div>'); | ||
$refGrid.append('<div><label style="font-size:0. | $refGrid.append('<div><label style="font-size:0.78rem; margin-bottom:4px;">Nama Situs</label><input type="text" id="md-ref-site" class="md-input" placeholder="Situs" value="' + sanitizeHTML(currentRef.site || '') + '"></div>'); | ||
$refGrid.append('<div><label style="font-size:0. | $refGrid.append('<div><label style="font-size:0.78rem; margin-bottom:4px;">Penulis</label><input type="text" id="md-ref-author" class="md-input" placeholder="Penulis" value="' + sanitizeHTML(currentRef.author || '') + '"></div>'); | ||
$refGrid.append('<div><label style="font-size:0. | $refGrid.append('<div><label style="font-size:0.78rem; margin-bottom:4px;">Penerbit</label><input type="text" id="md-ref-publisher" class="md-input" placeholder="Penerbit" value="' + sanitizeHTML(currentRef.publisher || '') + '"></div>'); | ||
var $dateBox = $('<div><label style="font-size:0. | var $dateBox = $('<div><label style="font-size:0.78rem; margin-bottom:4px;">Tanggal Akses</label></div>'); | ||
var $dateInpWrap = $('<div style="display:flex; gap: | var $dateInpWrap = $('<div style="display:flex; gap:6px;"></div>'); | ||
var $dateInp = $('<input type="date" id="md-ref-date" class="md-input" value="' + (currentRef.date || '') + '">'); | var $dateInp = $('<input type="date" id="md-ref-date" class="md-input" value="' + sanitizeHTML(currentRef.date || '') + '">'); | ||
var $todayBtn = $('<button type="button" class="md-btn-icon" style="padding:4px | var $todayBtn = $('<button type="button" class="md-btn-icon" style="padding:4px 8px; font-size:0.75rem;" title="Set Hari Ini">Hari Ini</button>'); | ||
$todayBtn.on('click', function() { $dateInp.val(getTodayString()); }); | $todayBtn.on('click', function() { $dateInp.val(getTodayString()); }); | ||
$dateInpWrap.append($dateInp).append($todayBtn); | $dateInpWrap.append($dateInp).append($todayBtn); | ||
| Baris 736: | Baris 715: | ||
$form.append($refArea); | $form.append($refArea); | ||
// | $form.append(` | ||
<div class="md-cc0-notice"> | |||
<b>Perhatian Lisensi:</b> Setiap data yang diterbitkan di sini akan terlihat publik dan dirilis di bawah lisensi <b>Creative Commons Zero (CC0)</b>. Jika Anda tidak ingin suntingan Anda digunakan secara bebas dan publik tanpa batas, jangan klik Terbitkan. <span class="md-help-icon" data-help="Dengan menerbitkan, Anda melepaskan seluruh hak cipta atas input data ini ke domain publik murni sesuai prinsip Wikidata.">${svgHelp}</span> | |||
</div> | |||
<div class="md-edit-meta"> | |||
<label style="font-size:0.85rem; font-weight:700;">Ringkasan Suntingan <span class="md-help-icon" data-help="Jelaskan secara singkat apa yang Anda ubah atau tambahkan. Contoh: Menambah data tanggal lahir rumi.">${svgHelp}</span></label> | |||
<input type="text" id="md-edit-summary" class="md-input" placeholder="Jelaskan perubahan Anda secara singkat..."> | |||
<label style="font-size:0.85rem; cursor:pointer; font-weight:700; margin-top:5px; display:inline-flex; align-items:center; gap:6px;"><input type="checkbox" id="md-minor-edit"> Ini adalah suntingan kecil (minor)</label> | |||
</div> | |||
`); | |||
var $actions = $('<div class="md-form-actions"></div>'); | var $actions = $('<div class="md-form-actions"></div>'); | ||
var $btnSave = $('<button class="md-btn md-btn-save">Simpan | var $btnSave = $('<button class="md-btn md-btn-save">Terbitkan <span class="md-spinner"></span></button>'); | ||
var $btnDraft = $('<button class="md-btn md-btn-draft">Simpan Draft</button>'); | |||
var $btnCancel = $('<button class="md-btn md-btn-cancel">Batal</button>'); | var $btnCancel = $('<button class="md-btn md-btn-cancel">Batal</button>'); | ||
$actions.append($btnSave).append($btnDraft).append($btnCancel); | |||
if (draftData && isNew) { | |||
var $btnDelDraft = $('<button class="md-btn" style="background:#eab308; color:#fff;">Hapus Draft</button>'); | |||
$btnDelDraft.on('click', function() { | |||
localStorage.removeItem(DRAFT_KEY); | |||
alert("Draft berhasil dihapus!"); | |||
$(this).hide(); | |||
}); | |||
$actions.append($btnDelDraft); | |||
} | |||
if (!isNew) { | if (!isNew) { | ||
var $btnDelete = $('<button class="md-btn md-btn-delete">Hapus | var $btnDelete = $('<button class="md-btn md-btn-delete">Hapus Data <span class="md-spinner"></span></button>'); | ||
$btnDelete.on('click', function() { | $btnDelete.on('click', function() { | ||
if(confirm('Yakin ingin | if(confirm('Yakin ingin menghapus item data ini?')) { | ||
dataItems.splice(index, 1); | dataItems.splice(index, 1); | ||
saveData('Menghapus data | saveData('Menghapus data', $(this), rawContent, $('#md-edit-summary').val(), $('#md-minor-edit').is(':checked')); | ||
} | } | ||
}); | }); | ||
| Baris 760: | Baris 756: | ||
$form.append($actions); | $form.append($actions); | ||
$container.append($form); | $container.append($form); | ||
initTooltips(); | |||
$('#md-chk-person').on('change', function() { | $('#md-chk-person').on('change', function() { | ||
if($(this).is(':checked')) $('#md-social-box' | if($(this).is(':checked')) $('#md-social-box').css('display', 'grid'); | ||
else $('#md-social-box'). | else $('#md-social-box').hide(); | ||
}); | }); | ||
$('html, body').animate({ scrollTop: $form.offset().top - | $('html, body').animate({ scrollTop: $form.offset().top - 60 }, 250); | ||
$btnCancel.on('click', function() { $container.empty(); }); | $btnCancel.on('click', function() { $container.empty(); }); | ||
$btnDraft.on('click', function() { | |||
$ | var socObj = {}; | ||
if ( | if ($('#md-chk-person').is(':checked')) { | ||
Object.keys(SOCIAL_PLATFORMS).forEach(function(k) { | |||
var valU = $('#md-soc-' + k).val().trim(); | |||
if (valU) socObj[k] = valU; | |||
}); | |||
} | |||
var draftObj = { | |||
prop: $('#md-inp-prop').val().trim(), | |||
val: $('#md-inp-val').val().trim(), | |||
link: $('#md-inp-link').is(':checked'), | |||
linkSplit: $('#md-inp-split').is(':checked'), | |||
linkTarget: $('#md-inp-target').val().trim(), | |||
isPersonOrg: $('#md-chk-person').is(':checked'), | |||
socials: socObj, | |||
ref: { | |||
type: $('#md-ref-type').val(), subType: $('#md-ref-subtype').val(), | |||
url: ensureHttps($('#md-ref-url').val()), title: $('#md-ref-title').val().trim(), | |||
site: $('#md-ref-site').val().trim(), author: $('#md-ref-author').val().trim(), | |||
publisher: $('#md-ref-publisher').val().trim(), date: $('#md-ref-date').val() | |||
} | |||
}; | |||
localStorage.setItem(DRAFT_KEY, JSON.stringify(draftObj)); | |||
alert("Draft berhasil disimpan di memori peramban Anda!"); | |||
}); | }); | ||
$btnSave.on('click', function() { | $btnSave.on('click', function() { | ||
var newProp = $('#md-inp-prop').val().trim(); | |||
var newVal = $('#md-inp-val').val().trim(); | |||
var newVal = $ | |||
if (!newProp || !newVal) { | if (!newProp || !newVal) { | ||
alert("Label Data dan Nilai wajib diisi!"); return; | |||
} | } | ||
| Baris 800: | Baris 811: | ||
var refObj = { | var refObj = { | ||
type: $ | type: $('#md-ref-type').val(), | ||
url: ensureHttps($ | subType: $('#md-ref-subtype').val(), | ||
site: $ | url: ensureHttps($('#md-ref-url').val()), | ||
publisher: $('#md-ref-publisher').val().trim(), date: $ | title: $('#md-ref-title').val().trim(), | ||
site: $('#md-ref-site').val().trim(), | |||
author: $('#md-ref-author').val().trim(), | |||
publisher: $('#md-ref-publisher').val().trim(), | |||
date: $('#md-ref-date').val() | |||
}; | }; | ||
Object.keys(refObj).forEach(function(key) { if (!refObj[key]) delete refObj[key]; }); | Object.keys(refObj).forEach(function(key) { if (!refObj[key]) delete refObj[key]; }); | ||
if (Object.keys(refObj).length === 0) refObj = null; | if (Object.keys(refObj).length === 0) refObj = null; | ||
| Baris 812: | Baris 826: | ||
var newItem = { | var newItem = { | ||
prop: newProp, val: newVal, | prop: newProp, val: newVal, | ||
link: $('#md-inp-link').is(':checked'), linkSplit: $('#md-inp-split').is(':checked'), | link: $('#md-inp-link').is(':checked'), | ||
linkSplit: $('#md-inp-split').is(':checked'), | |||
linkTarget: $('#md-inp-target').val().trim(), | linkTarget: $('#md-inp-target').val().trim(), | ||
isPersonOrg: isPerson, socials: socObj, ref: refObj | isPersonOrg: isPerson, | ||
socials: socObj, | |||
ref: refObj | |||
}; | }; | ||
| Baris 820: | Baris 837: | ||
else dataItems[index] = newItem; | else dataItems[index] = newItem; | ||
saveData(isNew ? 'Tambah data' : 'Edit data', $(this), rawContent, $('#md-edit-summary').val().trim(), $('#md-minor-edit').is(':checked')); | |||
}); | }); | ||
} | } | ||
// 10. JSON EXPORT & IMPORT ( | // 10. JSON EXPORT & IMPORT MODAL (ASLI UTUH) | ||
function showJsonModal(rawContent) { | function showJsonModal(rawContent) { | ||
var $container = $('#md-form-container') | var $container = $('#md-form-container').empty(); | ||
var jsonStr = JSON.stringify(dataItems, null, 2); | var jsonStr = JSON.stringify(dataItems, null, 2); | ||
var $box = $('<div class="md-form-box"></div>'); | var $box = $('<div class="md-form-box"></div>'); | ||
$box.append('<h4 class="md-form-title"> | $box.append('<h4 class="md-form-title">Ekspor / Impor JSON Data</h4>'); | ||
$box.append('<p style="font-size:0. | $box.append('<p style="font-size:0.82rem; color:#64748b; margin-bottom:10px;">Anda dapat menyalin struktur JSON di bawah untuk cadangan atau menempelkan JSON dari artikel lain.</p>'); | ||
var $txt = $('<textarea class="md-textarea" rows=" | var $txt = $('<textarea class="md-textarea" rows="10" style="font-family:monospace; font-size:0.8rem;"></textarea>').val(jsonStr); | ||
$box.append($txt); | $box.append($txt); | ||
var $actions = $('<div class="md-form-actions"></div>'); | var $actions = $('<div class="md-form-actions"></div>'); | ||
var $btnImport = $('<button class="md-btn md-btn-save"> | var $btnImport = $('<button class="md-btn md-btn-save">Impor JSON & Simpan <span class="md-spinner"></span></button>'); | ||
var $btnCancel = $('<button class="md-btn md-btn-cancel">Tutup</button>'); | var $btnCancel = $('<button class="md-btn md-btn-cancel">Tutup</button>'); | ||
$actions.append($btnImport | $actions.append($btnImport).append($btnCancel); | ||
$box.append($actions); | $box.append($actions); | ||
$container.append($box); | $container.append($box); | ||
$btnCancel.on('click', function() { $container.empty(); }); | $btnCancel.on('click', function() { $container.empty(); }); | ||
$btnImport.on('click', function() { | $btnImport.on('click', function() { | ||
try { | try { | ||
| Baris 858: | Baris 867: | ||
if (Array.isArray(parsed)) { | if (Array.isArray(parsed)) { | ||
dataItems = parsed; | dataItems = parsed; | ||
saveData('Impor | saveData('Impor data dari JSON', $(this), rawContent, 'Impor struktur data JSON', false); | ||
} else | } else { | ||
} catch(e) { | alert("Format JSON harus berupa Array items!"); | ||
} | |||
} catch(e) { | |||
alert("Format JSON tidak valid: " + e.message); | |||
} | |||
}); | }); | ||
} | } | ||
// 11. | // 11. EXECUTE | ||
loadData().then(function(rawContent) { buildUI(rawContent); }); | loadData().then(function(rawContent) { buildUI(rawContent); }); | ||
}); | }); | ||
})(jQuery, mediaWiki) | })(jQuery, mediaWiki); | ||
/* ========================================================== | /* ========================================================== | ||
| Baris 1.344: | Baris 1.357: | ||
'<div style="font-size:11px; color:#718096; font-weight:800; text-transform:uppercase; letter-spacing:1px; margin-bottom:10px;">Lisensi, Atribusi & Penggunaan Data</div>' + | '<div style="font-size:11px; color:#718096; font-weight:800; text-transform:uppercase; letter-spacing:1px; margin-bottom:10px;">Lisensi, Atribusi & Penggunaan Data</div>' + | ||
'<p style="margin:0 0 10px; font-size:13px; color:#4a5568; line-height:1.6;">' + | '<p style="margin:0 0 10px; font-size:13px; color:#4a5568; line-height:1.6;">' + | ||
'Seluruh data terstruktur yang tersaji dalam repositori ini merupakan milik publik dan tersedia di bawah | 'Seluruh data terstruktur yang tersaji dalam repositori ini merupakan milik publik dan tersedia di bawah ' + | ||
'<a href="https://creativecommons.org/ | '<a href="https://creativecommons.org/publicdomain/zero/1.0/"target="_blank" style="color:#6a5acd; text-decoration:none; font-weight:700;">Lisensi Creative Commons CC0</a>.' + | ||
'</p>' + | '</p>' + | ||
'<p style="margin:0; font-size:12px; color:#a0aec0;">Akses API dan penggunaan data secara massal wajib mencantumkan atribusi kepada <strong>Mippedia Community</strong> selaku pengelola proyek.</p></div>'; | '<p style="margin:0; font-size:12px; color:#a0aec0;">Akses API dan penggunaan data secara massal wajib mencantumkan atribusi kepada <strong>Mippedia Community</strong> selaku pengelola proyek.</p></div>'; | ||