MediaWiki:Common.js: Perbedaan antara revisi
Tidak ada ringkasan suntingan |
Tidak ada ringkasan suntingan Tanda: Suntingan perangkat seluler Suntingan peramban seluler |
||
| Baris 1: | Baris 1: | ||
/** | /** | ||
* ==================================================================== | * ==================================================================== | ||
* MIPPEDIA DATA - ADVANCED STRUCTURED DATA SYSTEM ( | * MIPPEDIA DATA - ADVANCED STRUCTURED DATA SYSTEM (V8.0 TITANIUM) | ||
* Fitur: | * Fitur: 25+ Fitur Baru, Collapsible References, Direct Link Button, | ||
* | * Dark Mode, Toast Notifications, Drag & Drop, Export CSV, Auto-Fill | ||
* ==================================================================== | * ==================================================================== | ||
*/ | */ | ||
| Baris 19: | Baris 19: | ||
var isLocked = false; | var isLocked = false; | ||
var api = new mw.Api(); | var api = new mw.Api(); | ||
var isDarkMode = localStorage.getItem('md-dark-mode') === 'true'; | |||
// 1. DATA MASTER PLATFORM MEDIA SOSIAL & MUSIK | // 1. DATA MASTER PLATFORM MEDIA SOSIAL & MUSIK (+5 Baru) | ||
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 26: | 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 | 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>' }, | ||
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 34: | 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"/></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"/></svg>' }, | ||
twitch: { name: 'Twitch', base: 'https://twitch.tv/', color: '#9146ff', icon: '<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M11.571 4.714h1.715v5.143H11.57zm4.715 0H18v5.143h-1.714zM6 0L1.714 4.286v15.428h5.143V24l4.286-4.286h3.428L22.286 12V0zm14.571 11.143l-3.428 3.428h-3.429l-3 3v-3H6.857V1.714h13.714Z"/></svg>' }, | |||
reddit: { name: 'Reddit', base: 'https://reddit.com/user/', color: '#ff4500', icon: '<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M24 11.779c0-1.459-1.192-2.645-2.657-2.645-.715 0-1.363.275-1.84.734-1.605-1.056-3.868-1.739-6.38-1.815l1.341-4.218 3.238.683c.038 1.168 1.002 2.1 2.189 2.1 1.212 0 2.193-.984 2.193-2.2s-.981-2.2-2.193-2.2c-1.01 0-1.868.682-2.131 1.606l-3.619-.763c-.156-.033-.311.042-.366.19l-1.547 4.86c-2.61.037-4.978.711-6.643 1.785-.477-.47-1.135-.754-1.861-.754-1.465 0-2.656 1.187-2.656 2.646 0 .97.533 1.811 1.317 2.271-.052.282-.086.567-.086.857 0 3.911 4.808 7.093 10.719 7.093s10.72-3.182 10.72-7.093c0-.274-.029-.544-.075-.81.832-.447 1.405-1.312 1.405-2.318zm-17.224 1.816c0-.868.71-1.575 1.582-1.575.872 0 1.581.707 1.581 1.575s-.709 1.574-1.581 1.574-1.582-.706-1.582-1.574zm9.014 4.597c-1.233 1.234-3.535 1.294-5.068 1.294-1.531 0-3.834-.06-5.068-1.294-.216-.216-.216-.566 0-.783.216-.217.567-.217.784 0 1.019 1.018 3.033 1.109 4.284 1.109 1.253 0 3.266-.09 4.284-1.109.217-.217.568-.217.784 0 .216.217.216.566 0 .783zm.18-2.923c-.872 0-1.582-.706-1.582-1.574 0-.868.71-1.575 1.582-1.575.871 0 1.581.707 1.581 1.575s-.71 1.574-1.581 1.574z"/></svg>' }, | |||
discord: { name: 'Discord', base: 'https://discord.com/users/', color: '#5865f2', icon: '<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189z"/></svg>' }, | |||
wikipedia: { name: 'Wikipedia', base: 'https://id.wikipedia.org/wiki/', color: '#000000', icon: '<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M11.964 1.488C5.467 1.488.2 6.756.2 13.253s5.267 11.765 11.764 11.765 11.765-5.268 11.765-11.765S18.461 1.488 11.964 1.488zm-2.003 18.06l-4.706-9.412h1.412l2.353 5.378 1.954-5.378h1.41l2.42 5.513 2.164-5.513h1.365l-4.47 9.412h-1.554l-1.954-5.464-2.046 5.464h-1.348z"/></svg>' } | |||
}; | }; | ||
| Baris 41: | Baris 46: | ||
"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", "Agama", "Pendidikan" | ||
]; | ]; | ||
// 3. MASTER TIPE RUJUKAN | // 3. MASTER TIPE RUJUKAN | ||
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"], | ||
"Website": ["Situs Resmi", "Portal Informasi", "Database Web | "Website": ["Situs Resmi", "Portal Informasi", "Database Web", "Forum Diskusi"], | ||
"Buku": ["Buku Cetak", "E-Book", "Bab / Bagian Buku", "Monograf | "Buku": ["Buku Cetak", "E-Book", "Bab / Bagian Buku", "Monograf"], | ||
"Jurnal": ["Jurnal Ilmiah Peer-Reviewed", "Paper Konferensi | "Jurnal": ["Jurnal Ilmiah Peer-Reviewed", "Paper Konferensi", "Tesis / Disertasi"], | ||
"Siaran Pers": ["Siaran Pers Resmi", "Pengumuman Instansi / BUMN | "Siaran Pers": ["Siaran Pers Resmi", "Pengumuman Instansi / BUMN"], | ||
"Dokumen PDF": ["Laporan Tahunan / Annual Report", "Dokumen Kebijakan | "Dokumen PDF": ["Laporan Tahunan / Annual Report", "Dokumen Kebijakan"], | ||
"Studio / Database Musik": ["MusicBrainz", "Discogs", "Genius", "AllMusic", "Spotify For Artists | "Studio / Database Musik": ["MusicBrainz", "Discogs", "Genius", "AllMusic", "Spotify For Artists"], | ||
"Video": ["YouTube Video", "Film Dokumenter", "Wawancara Video | "Video": ["YouTube Video", "Film Dokumenter", "Wawancara Video"], | ||
"Podcast": ["Episode Podcast", "Siniar Audio Resmi | "Podcast": ["Episode Podcast", "Siniar Audio Resmi"], | ||
"Media Sosial": ["Postingan / Tweet", "Video Singkat | "Media Sosial": ["Postingan / Tweet", "Video Singkat", "Live Stream"], | ||
"Ensiklopedia": ["Ensiklopedia Cetak", "Ensiklopedia Online | "Ensiklopedia": ["Ensiklopedia Cetak", "Ensiklopedia Online"], | ||
"Arsip": ["Arsip Nasional | "Arsip": ["Arsip Nasional", "Web Archive (Wayback Machine)"] | ||
}; | }; | ||
// 4. IKON SISTEM | // 4. IKON SISTEM (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 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 68: | Baris 73: | ||
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 svgExternal = '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><polyline points="15 3 21 3 21 9"></polyline><line x1="10" y1="14" x2="21" y2="3"></line></svg>'; | |||
var svgMoon = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg>'; | |||
var svgSun = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg>'; | |||
var svgDownload = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg>'; | |||
var svgClone = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>'; | |||
var svgDrag = '<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="cursor:grab; color:#94a3b8;"><line x1="8" y1="6" x2="21" y2="6"></line><line x1="8" y1="12" x2="21" y2="12"></line><line x1="8" y1="18" x2="21" y2="18"></line><line x1="3" y1="6" x2="3.01" y2="6"></line><line x1="3" y1="12" x2="3.01" y2="12"></line><line x1="3" y1="18" x2="3.01" y2="18"></line></svg>'; | |||
// 5. HELPER UTILITY | // 5. HELPER UTILITY | ||
function showToast(msg, type) { | |||
var bg = type === 'error' ? '#ef4444' : '#10b981'; | |||
var $toast = $('<div style="position:fixed; bottom:20px; right:20px; background:'+bg+'; color:white; padding:12px 20px; border-radius:8px; box-shadow:0 10px 25px rgba(0,0,0,0.2); z-index:9999; font-weight:600; font-size:0.9rem; transform:translateY(100px); opacity:0; transition:all 0.3s ease;">' + msg + '</div>'); | |||
$('body').append($toast); | |||
setTimeout(function() { $toast.css({transform: 'translateY(0)', opacity: 1}); }, 50); | |||
setTimeout(function() { $toast.css({transform: 'translateY(100px)', opacity: 0}); setTimeout(function(){ $toast.remove(); }, 300); }, 3000); | |||
} | |||
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 = ['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul', 'Agu', 'Sep', 'Okt', 'Nov', 'Des']; | ||
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; | ||
| Baris 85: | Baris 104: | ||
try { | try { | ||
if (!urlStr.match(/^https?:\/\//i)) urlStr = 'https://' + urlStr; | if (!urlStr.match(/^https?:\/\//i)) urlStr = 'https://' + urlStr; | ||
return new URL(urlStr).hostname.replace(/^www\./, ''); | |||
} catch(e) { return urlStr.replace(/^https?:\/\/(www\.)?/, '').split('/')[0]; } | |||
} catch(e) { | |||
} | } | ||
| Baris 95: | Baris 111: | ||
if (!urlStr) return ''; | if (!urlStr) return ''; | ||
urlStr = urlStr.trim(); | urlStr = urlStr.trim(); | ||
if (urlStr && !urlStr.match(/^https?:\/\//i)) | if (urlStr && !urlStr.match(/^https?:\/\//i)) return 'https://' + urlStr; | ||
return urlStr; | return urlStr; | ||
} | } | ||
| Baris 107: | Baris 121: | ||
var d = String(now.getDate()).padStart(2, '0'); | var d = String(now.getDate()).padStart(2, '0'); | ||
return y + '-' + m + '-' + d; | return y + '-' + m + '-' + d; | ||
} | |||
function toTitleCase(str) { | |||
return str.replace(/\w\S*/g, function(txt){ return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); }); | |||
} | } | ||
// 6. STYLESHEET MODERN | function getRefQuality(ref) { | ||
if (!ref || (!ref.url && !ref.title)) return 0; | |||
var score = 0; | |||
if(ref.url) score++; if(ref.title) score++; if(ref.publisher || ref.site) score++; | |||
if(ref.date) score++; if(ref.author) score++; | |||
return score; | |||
} | |||
// 6. STYLESHEET MODERN V8.0 TITANIUM (Dark Mode Ready & Animations) | |||
var css = ` | var css = ` | ||
:root { | |||
--md-bg: #ffffff; --md-surface: #f8fafc; --md-border: #e2e8f0; | |||
--md-text-main: #0f172a; --md-text-sub: #64748b; --md-primary: #4f46e5; | |||
--md-primary-hover: #4338ca; --md-shadow: rgba(0,0,0,0.06); | |||
--md-glass: rgba(255, 255, 255, 0.8); | |||
} | |||
.md-dark { | |||
--md-bg: #1e1e2f; --md-surface: #2a2a40; --md-border: #3b3b5c; | |||
--md-text-main: #f1f5f9; --md-text-sub: #94a3b8; --md-primary: #6366f1; | |||
--md-primary-hover: #818cf8; --md-shadow: rgba(0,0,0,0.3); | |||
--md-glass: rgba(30, 30, 47, 0.8); | |||
} | |||
#mippedia-data-app { | #mippedia-data-app { | ||
background: | background: var(--md-bg); border-radius: 16px; box-shadow: 0 4px 24px var(--md-shadow); | ||
padding: 20px; margin: 20px 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; | padding: 20px; margin: 20px 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; | ||
border: 1px solid | border: 1px solid var(--md-border); max-width: 100%; box-sizing: border-box; overflow: hidden; | ||
color: var(--md-text-main); transition: background 0.3s, color 0.3s; | |||
} | } | ||
/* Sticky Header */ | |||
.md-header { | .md-header { | ||
display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; | display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; | ||
gap: 12px; margin-bottom: 16px; padding-bottom: 14px; border-bottom: 1px solid | gap: 12px; margin-bottom: 16px; padding-bottom: 14px; border-bottom: 1px solid var(--md-border); | ||
position: sticky; top: 0; background: var(--md-glass); backdrop-filter: blur(10px); z-index: 10; | |||
} | } | ||
.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 | .md-title { font-size: 1.25rem; font-weight: 800; margin: 0; white-space: nowrap; letter-spacing: -0.3px; } | ||
.md-actions { display: flex; gap: | .md-actions { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; } | ||
.md-btn-icon { | .md-btn-icon { | ||
background: | background: var(--md-surface); border: 1px solid var(--md-border); cursor: pointer; color: var(--md-primary); | ||
padding: 8px | padding: 8px 12px; border-radius: 8px; transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); | ||
display: inline-flex; align-items: center; gap: 6px; font-size: 0. | display: inline-flex; align-items: center; gap: 6px; font-size: 0.8rem; font-weight: 600; white-space: nowrap; | ||
} | } | ||
.md-btn-icon:hover:not(:disabled) { background: | .md-btn-icon:hover:not(:disabled) { background: var(--md-border); transform: translateY(-2px); box-shadow: 0 4px 10px var(--md-shadow); } | ||
.md- | |||
/* Tab Filters */ | |||
.md-tabs { display: flex; gap: 10px; margin-bottom: 15px; border-bottom: 2px solid var(--md-surface); padding-bottom: 5px; } | |||
.md-tab { background: none; border: none; font-weight: 700; color: var(--md-text-sub); cursor: pointer; padding: 5px 10px; border-bottom: 2px solid transparent; transition: 0.2s; font-size: 0.85rem;} | |||
.md-tab.active { color: var(--md-primary); border-bottom-color: var(--md-primary); } | |||
.md-tab:hover { color: var(--md-primary); } | |||
.md-search-box { position: relative; margin-bottom: 16px; width: 100%; box-sizing: border-box; } | .md-search-box { position: relative; margin-bottom: 16px; width: 100%; box-sizing: border-box; } | ||
.md-search-input { | .md-search-input { | ||
width: 100%; padding: | width: 100%; padding: 10px 12px 10px 34px; border: 1px solid var(--md-border); border-radius: 8px; | ||
font-size: 0. | font-size: 0.9rem; box-sizing: border-box; background: var(--md-surface); color: var(--md-text-main); transition: 0.2s; | ||
} | } | ||
.md-search-input:focus { background: | .md-search-input:focus { background: var(--md-bg); border-color: var(--md-primary); outline: none; box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.2); } | ||
.md-search-icon { position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: | .md-search-icon { position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: var(--md-text-sub); } | ||
/* Row Display | /* Row Display & Drag */ | ||
.md-row-display { | .md-row-display { | ||
display: flex; justify-content: space-between; align-items: flex-start; | display: flex; justify-content: space-between; align-items: flex-start; | ||
padding: | padding: 14px 12px; border-bottom: 1px solid var(--md-border); transition: all 0.3s; | ||
border-radius: 12px; gap: 12px; margin-bottom: 8px; background: | border-radius: 12px; gap: 12px; margin-bottom: 8px; background: var(--md-bg); | ||
} | } | ||
.md-row-display:hover { background: | .md-row-display.dragging { opacity: 0.5; background: var(--md-surface); } | ||
.md-row-display:hover { background: var(--md-surface); box-shadow: inset 4px 0 0 var(--md-primary); } | |||
.md-drag-handle { cursor: grab; padding-top: 5px; color: var(--md-text-sub); display:flex; align-items:center;} | |||
.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: var(--md-text-sub); 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.05rem; font-weight: 600; word-break: break-word; color: var(--md-text-main); } | ||
.md-data-value a { color: var(--md-primary); text-decoration: none; } | |||
.md- | .md-data-value a:hover { text-decoration: underline; } | ||
.md- | |||
/* | /* AKORDEON REFERENSI V8.0 */ | ||
.md-ref-container { margin-top: | .md-ref-container { margin-top: 6px; width: 100%; box-sizing: border-box; } | ||
.md-ref- | .md-ref-toggle { | ||
background: | background: var(--md-surface); border: 1px solid var(--md-border); padding: 6px 12px; border-radius: 20px; | ||
font-size: 0.78rem; font-weight: 700; color: var(--md-text-sub); cursor: pointer; display: inline-flex; align-items: center; gap: 6px; | |||
transition: 0.2s; user-select: none; | |||
} | } | ||
.md-ref- | .md-ref-toggle:hover { background: var(--md-border); color: var(--md-primary); } | ||
.md-ref-toggle.open { background: var(--md-primary); color: white; border-color: var(--md-primary); } | |||
.md-ref-panel { | |||
display: none; /* Disembunyikan by default */ | |||
background: var(--md-surface); padding: 12px 14px; border-radius: 10px; margin-top: 8px; | |||
border-left: 4px solid var(--md-primary); font-size: 0.85rem; flex-direction: column; gap: 8px; | |||
border-top: 1px solid var(--md-border); border-right: 1px solid var(--md-border); border-bottom: 1px solid var(--md-border); | |||
animation: mdFadeIn 0.3s ease-out; | |||
} | } | ||
.md-ref-header { font-weight: | @keyframes mdFadeIn { from { opacity: 0; transform: translateY(-5px); } to { opacity: 1; transform: translateY(0); } } | ||
.md-ref- | |||
.md-ref-header { font-weight: 800; font-size: 0.9rem; color: var(--md-text-main); word-break: break-word; } | |||
.md-ref-details-grid { display: flex; flex-direction: column; gap: 4px; } | |||
.md-ref-detail-row { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 6px; font-size: 0.82rem; } | |||
.md-ref-lbl { font-weight: 700; color: var(--md-text-sub); white-space: nowrap; flex-shrink: 0; } | |||
.md-ref-val { color: var(--md-text-main); font-weight: 500; } | |||
.md-ref- | .md-ref-bottom { display: flex; justify-content: space-between; align-items: center; margin-top: 8px; border-top: 1px dashed var(--md-border); padding-top: 8px; } | ||
.md-ref-badge { display: inline-block; background: var(--md-border); color: var(--md-text-main); padding: 3px 8px; border-radius: 6px; font-weight: 700; font-size: 0.72rem; } | |||
.md-ref- | .md-btn-direct { | ||
background: var(--md-primary); color: white !important; text-decoration: none; padding: 5px 12px; border-radius: 6px; | |||
.md- | font-weight: 700; font-size: 0.78rem; display: inline-flex; align-items: center; gap: 5px; transition: 0.2s; | ||
} | } | ||
.md-btn-direct:hover { background: var(--md-primary-hover); transform: scale(1.05); text-decoration: none; } | |||
.md-q-indicator { display:inline-block; width:8px; height:8px; border-radius:50%; margin-left:5px; } | |||
/* Media Sosial Badges | /* Media Sosial Badges */ | ||
.md-social-group { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; } | .md-social-group { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; } | ||
.md-social-badge { | .md-social-badge { | ||
display: inline-flex; align-items: center; gap: 6px; padding: 5px 11px; border-radius: 18px; | display: inline-flex; align-items: center; gap: 6px; padding: 5px 11px; border-radius: 18px; | ||
font-size: 0.78rem; font-weight: 600; color: white; text-decoration: none; transition: transform 0.2s, opacity 0.2s; | font-size: 0.78rem; font-weight: 600; color: white !important; text-decoration: none; transition: transform 0.2s, opacity 0.2s; | ||
box-shadow: 0 2px 4px rgba(0,0,0,0. | box-shadow: 0 2px 4px rgba(0,0,0,0.1); | ||
} | } | ||
.md- | .md-social-badge:hover { transform: translateY(-2px); opacity: 0.9; text-decoration: none; } | ||
/* Form Elements | /* Form Elements */ | ||
.md-form-box { background: | .md-form-box { background: var(--md-bg); border-radius: 12px; padding: 18px; margin-top: 16px; border: 1px solid var(--md-border); box-shadow: 0 8px 30px var(--md-shadow); animation: mdFadeIn 0.3s; } | ||
.md-form-title { font-size: 1. | .md-form-title { font-size: 1.1rem; font-weight: 800; margin: 0 0 16px 0; border-bottom: 2px solid var(--md-border); padding-bottom: 8px; display:flex; justify-content:space-between;} | ||
.md-form-group { margin-bottom: 14px; } | .md-form-group { margin-bottom: 14px; position:relative;} | ||
.md-form-group label { display: block; font-size: 0.82rem; font-weight: 700; margin-bottom: 6px; color: | .md-form-group label { display: block; font-size: 0.82rem; font-weight: 700; margin-bottom: 6px; color: var(--md-text-sub); } | ||
.md-input, .md-select, .md-textarea { | .md-input, .md-select, .md-textarea { | ||
width: 100%; padding: 9px 12px; border: 1px solid | width: 100%; padding: 9px 12px; border: 1px solid var(--md-border); border-radius: 8px; | ||
box-sizing: border-box; font-family: inherit; font-size: 0.9rem; transition: 0.2s; background: | box-sizing: border-box; font-family: inherit; font-size: 0.9rem; transition: 0.2s; background: var(--md-surface); color: var(--md-text-main); | ||
} | } | ||
.md-input:focus, .md-select:focus, .md-textarea:focus { border-color: | .md-input:focus, .md-select:focus, .md-textarea:focus { border-color: var(--md-primary); outline: none; box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.15); } | ||
.md-input.error { border-color: #ef4444; background: #fef2f2; } | |||
.md-dark .md-input.error { background: #3f1a1a; } | |||
.md-ref-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; background: | .md-preset-pill { background: var(--md-surface); border: 1px solid var(--md-border); padding: 4px 10px; border-radius: 12px; font-size: 0.75rem; font-weight: 600; cursor: pointer; transition: 0.2s; display:inline-block; margin:2px;} | ||
.md-social-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; background: | .md-preset-pill:hover { background: var(--md-primary); color:white; border-color:var(--md-primary);} | ||
.md-ref-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; background: var(--md-surface); padding: 12px; border-radius: 8px; border: 1px dashed var(--md-border); } | |||
.md-social-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; background: var(--md-surface); padding: 12px; border-radius: 8px; border: 1px solid var(--md-border); 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-form-actions { display: flex; gap: 10px; align-items: center; margin-top: 20px; padding-top: 14px; border-top: 1px solid var(--md-border); flex-wrap: wrap; } | ||
.md-btn { | .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(-2px); box-shadow: 0 4px 10px rgba(0,0,0,0.15); } | |||
.md-btn:hover:not(:disabled) { transform: translateY(- | |||
.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-delete { background: #dc2626; margin-left: auto; } | .md-btn-delete { background: #dc2626; margin-left: auto; } | ||
.md- | .md-char-count { position:absolute; right:10px; top:35px; font-size:0.7rem; color:var(--md-text-sub); } | ||
@media (max-width: 600px) { | @media (max-width: 600px) { | ||
#mippedia-data-app { padding: 14px; margin: | #mippedia-data-app { padding: 14px; margin: 10px 0; border-radius: 12px; } | ||
.md-header { | .md-header { position: relative; backdrop-filter: none; } | ||
.md-actions { width: 100%; justify-content: flex-start; } | .md-actions { width: 100%; justify-content: flex-start; overflow-x: auto; padding-bottom: 5px;} | ||
.md-btn-icon { padding: 7px | .md-btn-icon { padding: 7px 10px; font-size: 0.75rem; } | ||
.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 8px; } | .md-row-display { padding: 12px 8px; gap: 8px; } | ||
.md-data-value { font-size: 0.95rem; } | |||
.md-drag-handle { display:none; } /* Hide drag on mobile to prevent scrolling issues */ | |||
} | } | ||
.md-native-render { display: none; } | .md-native-render { display: none; } | ||
`; | `; | ||
$('<style>').text(css).appendTo('head'); | $('<style id="md-styles">').text(css).appendTo('head'); | ||
if(isDarkMode) $('body').addClass('md-dark'); | |||
// 7. PARSER & PENYIMPANAN DATA | // 7. PARSER & PENYIMPANAN DATA | ||
| Baris 264: | Baris 315: | ||
function buildWikitextVal(item) { | function buildWikitextVal(item) { | ||
var val = item.val; | var val = item.val; | ||
if (item.linkTarget && item.linkTarget.trim() !== '') | 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') | if (trimmed.length > 2 && trimmed.toLowerCase() !== 'dan' && trimmed.toLowerCase() !== 'atau') return "[[" + trimmed + "]]"; | ||
return match; | return match; | ||
}); | }); | ||
} | } | ||
if (item.link) | if (item.link) return "[[" + val + "]]"; | ||
return val; | return val; | ||
} | } | ||
| Baris 284: | Baris 329: | ||
function buildWikitextRef(refObj) { | function buildWikitextRef(refObj) { | ||
if (!refObj) return ""; | if (!refObj) return ""; | ||
var title = refObj.title || "Rujukan"; | var title = refObj.title || extractDomain(refObj.url) || "Rujukan"; | ||
var text = refObj.url ? "[" + ensureHttps(refObj.url) + " " + title + "]" : "'''" + title + "'''"; | var text = refObj.url ? "[" + ensureHttps(refObj.url) + " " + title + "]" : "'''" + title + "'''"; | ||
var details = []; | var details = []; | ||
| Baris 297: | Baris 342: | ||
} | } | ||
function saveData( | function saveData(actionMsg, $btnEl, originalContent, isMinor) { | ||
if ($btnEl) $btnEl. | if ($btnEl) { $btnEl.prop('disabled', true).html('Menyimpan...⏳'); } | ||
var jsonStr = JSON.stringify({ locked: isLocked, items: dataItems }); | var jsonStr = JSON.stringify({ locked: isLocked, items: dataItems }); | ||
var wt = "<!-- MIPPEDIA_DATA_START | " + jsonStr + " -->\n | var wt = "<!-- MIPPEDIA_DATA_START | " + jsonStr + " -->\n<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 + " || " + buildWikitextVal(item) + " || " + buildWikitextRef(item.ref) + "\n"; | |||
wt += "|-\n| " + item.prop + " || " + | |||
}); | }); | ||
wt += "|}\n</div>\n<!-- MIPPEDIA_DATA_END -->"; | wt += "|}\n</div>\n<!-- MIPPEDIA_DATA_END -->"; | ||
| Baris 313: | Baris 355: | ||
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)) | if (regex.test(originalContent)) newContent = originalContent.replace(regex, wt); | ||
else newContent = originalContent.trim() + "\n\n" + wt; | |||
api.postWithToken('csrf', { | api.postWithToken('csrf', { | ||
action: 'edit', title: pageName, text: newContent, summary: 'Mippedia | action: 'edit', title: pageName, text: newContent, | ||
summary: 'Mippedia V8: ' + actionMsg, minor: isMinor || false | |||
}).done(function() { | }).done(function() { | ||
showToast('Berhasil disimpan! Memuat ulang...', 'success'); | |||
window.location. | setTimeout(function(){ window.location.reload(true); }, 1000); | ||
}).fail(function(err) { | }).fail(function(err) { | ||
if ($btnEl) $btnEl. | if ($btnEl) $btnEl.prop('disabled', false).html('Coba Lagi'); | ||
showToast("Gagal menyimpan: " + err, 'error'); | |||
}); | }); | ||
} | } | ||
| Baris 334: | Baris 374: | ||
var $app = $('<div id="mippedia-data-app"></div>'); | var $app = $('<div id="mippedia-data-app"></div>'); | ||
// HEADER | // HEADER & ACTION BAR | ||
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</h3></div>'); | var $titleContainer = $('<div class="md-title-container"><h3 class="md-title">' + svgEdit + ' Data Terstruktur V8</h3></div>'); | ||
var $actions = $('<div class="md-actions"></div>'); | var $actions = $('<div class="md-actions"></div>'); | ||
// | // Toggle Dark Mode | ||
var $jsonBtn = $('<button class="md-btn-icon" title=" | var $darkBtn = $('<button class="md-btn-icon" title="Mode Gelap">' + (isDarkMode ? svgSun : svgMoon) + '</button>'); | ||
$darkBtn.on('click', function() { | |||
isDarkMode = !isDarkMode; | |||
localStorage.setItem('md-dark-mode', isDarkMode); | |||
$('body').toggleClass('md-dark', isDarkMode); | |||
$(this).html(isDarkMode ? svgSun : svgMoon); | |||
}); | |||
$actions.append($darkBtn); | |||
// Export CSV | |||
var $csvBtn = $('<button class="md-btn-icon" title="Ekspor CSV">' + svgDownload + ' CSV</button>'); | |||
$csvBtn.on('click', function() { exportCSV(); }); | |||
$actions.append($csvBtn); | |||
var $jsonBtn = $('<button class="md-btn-icon" title="JSON Tool">' + svgCode + ' JSON</button>'); | |||
$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 | var $lockBtn = $('<button class="md-btn-icon">' + (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, true); | ||
}); | }); | ||
$actions.append($lockBtn); | $actions.append($lockBtn); | ||
| Baris 354: | Baris 408: | ||
if (!isLocked || isSysop) { | if (!isLocked || isSysop) { | ||
var $addBtn = $('<button class="md-btn-icon" | var $addBtn = $('<button class="md-btn-icon" style="background:var(--md-primary); color:white; border-color:var(--md-primary);">' + svgEdit + ' Tambah</button>'); | ||
$addBtn.on('click', function() { showForm(-1, rawContent); }); | $addBtn.on('click', function() { showForm(-1, rawContent); }); | ||
$actions.append($addBtn); | $actions.append($addBtn); | ||
| Baris 362: | Baris 416: | ||
$app.append($header); | $app.append($header); | ||
// SEARCH | // FILTER TABS & SEARCH | ||
if (dataItems.length > | if (dataItems.length > 1) { | ||
var $searchBox = $('<div class="md-search-box"><span class="md-search-icon">' + svgSearch + '</span><input type="text" class="md-search-input" placeholder="Cari data | var $tabArea = $('<div class="md-tabs"><button class="md-tab active" data-filter="all">Semua Data</button><button class="md-tab" data-filter="social">Punya Sosmed</button><button class="md-tab" data-filter="ref">Ada Referensi</button></div>'); | ||
$searchBox.find('input').on('keyup input', function() { | var $searchBox = $('<div class="md-search-box"><span class="md-search-icon">' + svgSearch + '</span><input type="text" class="md-search-input" placeholder="Cari data (label/nilai)..."></div>'); | ||
var q = $( | |||
$tabArea.find('.md-tab').on('click', function() { | |||
$tabArea.find('.md-tab').removeClass('active'); $(this).addClass('active'); | |||
filterList(); | |||
}); | |||
$searchBox.find('input').on('keyup input', filterList); | |||
$app.append($tabArea).append($searchBox); | |||
function filterList() { | |||
var q = $searchBox.find('input').val().toLowerCase(); | |||
var filter = $tabArea.find('.active').data('filter'); | |||
$app.find('.md-row-display').each(function() { | $app.find('.md-row-display').each(function() { | ||
var | var $r = $(this); | ||
$( | var text = $r.text().toLowerCase(); | ||
var hasSocial = $r.find('.md-social-group').length > 0; | |||
var hasRef = $r.find('.md-ref-container').length > 0; | |||
var matchSearch = text.indexOf(q) !== -1; | |||
var matchTab = filter === 'all' ? true : (filter === 'social' ? hasSocial : hasRef); | |||
$r.toggle(matchSearch && matchTab); | |||
}); | }); | ||
} | } | ||
} | } | ||
// DAFTAR BARIS DATA | // DAFTAR BARIS DATA (List) | ||
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=" | $list.append('<div style="text-align:center; padding: 30px 10px; color:var(--md-text-sub); border:2px dashed var(--md-border); border-radius:12px; margin-top:10px;">Belum ada data. Klik <b>Tambah</b> untuk mulai menyusun mahakarya.</div>'); | ||
} else { | } else { | ||
dataItems.forEach(function(item, index) { | dataItems.forEach(function(item, index) { | ||
var $row = $('<div class="md-row-display"></div>'); | var $row = $('<div class="md-row-display" draggable="true" data-index="'+index+'"></div>'); | ||
var $dragHandle = $('<div class="md-drag-handle" title="Tahan & Geser">' + svgDrag + '</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">' + item.prop + '</span>'); | ||
// | // Render Nilai Data | ||
var rawVal = item.val || ''; | var rawVal = item.val || ''; | ||
var displayValHtml = rawVal; | |||
if (rawVal.match(/^https?:\/\//i)) { | if (rawVal.match(/^https?:\/\//i)) { | ||
displayValHtml = '<a href="' + rawVal + '" target="_blank">' + extractDomain(rawVal) + ' ' + svgExternal + '</a>'; | |||
displayValHtml = '<a href="' + rawVal + '" target="_blank | |||
} else if (item.linkTarget && item.linkTarget.trim() !== '') { | } else if (item.linkTarget && item.linkTarget.trim() !== '') { | ||
displayValHtml = '<a href="' + mw.util.getUrl(item.linkTarget.trim()) + ' | displayValHtml = '<a href="' + mw.util.getUrl(item.linkTarget.trim()) + '">' + 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]+$/) && !/^(dan|atau)$/i.test(trimmed)) { | ||
return '<a href="' + mw.util.getUrl(trimmed) + ' | return '<a href="' + mw.util.getUrl(trimmed) + '">' + trimmed + '</a>'; | ||
} | } | ||
return p; | return p; | ||
}).join(''); | }).join(''); | ||
} else if (item.link) { | } else if (item.link) { | ||
displayValHtml = '<a href="' + mw.util.getUrl(rawVal) + ' | displayValHtml = '<a href="' + mw.util.getUrl(rawVal) + '">' + rawVal + '</a>'; | ||
} | } | ||
| Baris 412: | Baris 482: | ||
$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-icon" style="padding:4px; border:none; background:transparent;" title="Salin teks">' + svgCopy + '</button>'); | ||
$copyBtn.on('click', function() { | $copyBtn.on('click', function() { navigator.clipboard.writeText(rawVal); showToast('Teks disalin: ' + rawVal, 'success'); }); | ||
$valWrap.append($copyBtn); | $valWrap.append($copyBtn); | ||
$info.append($valWrap); | $info.append($valWrap); | ||
// | // Render Media Sosial (+ Twitch dll) | ||
if (item.isPersonOrg && item.socials) { | if (item.isPersonOrg && item.socials) { | ||
var $socialGroup = $('<div class="md-social-group"></div>'); | var $socialGroup = $('<div class="md-social-group"></div>'); | ||
| Baris 428: | Baris 494: | ||
var uname = item.socials[key]; | var uname = item.socials[key]; | ||
if (uname) { | if (uname) { | ||
hasSocial = true; | hasSocial = true; var platform = SOCIAL_PLATFORMS[key]; | ||
var fullUrl = platform.base + uname.replace(/^@/, ''); | var fullUrl = platform.base + uname.replace(/^@/, ''); | ||
$socialGroup.append('<a class="md-social-badge" href="'+fullUrl+'" target="_blank" style="background:'+platform.color+';">'+platform.icon+' '+platform.name+'</a>'); | |||
} | } | ||
}); | }); | ||
| Baris 438: | Baris 502: | ||
} | } | ||
// | // Render FITUR AKORDEON REFERENSI | ||
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 $refContainer = $('<div class="md-ref-container"></div>'); | var $refContainer = $('<div class="md-ref-container"></div>'); | ||
var | var refTitle = ref.title || extractDomain(ref.url) || "Rujukan Eksternal"; | ||
// | // Toggle Button | ||
$ | var qScore = getRefQuality(ref); | ||
var qColor = qScore >= 4 ? '#10b981' : (qScore >= 2 ? '#f59e0b' : '#ef4444'); | |||
var $toggleBtn = $('<button class="md-ref-toggle">' + svgRef + ' Lihat Referensi <span class="md-q-indicator" style="background:'+qColor+';" title="Kualitas Ref: '+qScore+'/5"></span></button>'); | |||
// Panel Detail | |||
var $refPanel = $('<div class="md-ref-panel"></div>'); | |||
$refPanel.append('<div class="md-ref-header">' + refTitle + '</div>'); | |||
var $detailsGrid = $('<div class="md-ref-details-grid"></div>'); | |||
if(ref.author) $detailsGrid.append('<div class="md-ref-detail-row"><span class="md-ref-lbl">Penulis:</span><span class="md-ref-val">'+ref.author+'</span></div>'); | |||
if(ref.site) $detailsGrid.append('<div class="md-ref-detail-row"><span class="md-ref-lbl">Situs:</span><span class="md-ref-val">'+ref.site+'</span></div>'); | |||
if(ref.publisher) $detailsGrid.append('<div class="md-ref-detail-row"><span class="md-ref-lbl">Penerbit:</span><span class="md-ref-val">'+ref.publisher+'</span></div>'); | |||
if(ref.date) $detailsGrid.append('<div class="md-ref-detail-row"><span class="md-ref-lbl">Akses:</span><span class="md-ref-val">'+formatIndonesianDate(ref.date)+'</span></div>'); | |||
$refPanel.append($detailsGrid); | |||
var | var $refBottom = $('<div class="md-ref-bottom"></div>'); | ||
if (ref. | if (ref.type) { | ||
var | var tLbl = ref.type + (ref.subType ? ' • ' + ref.subType : ''); | ||
$ | $refBottom.append('<span class="md-ref-badge">' + tLbl + '</span>'); | ||
} else { | } else { | ||
$ | $refBottom.append('<span></span>'); // spacer | ||
} | } | ||
// Tombol Kunjungi Tautan Langsung | |||
if (ref. | if (ref.url) { | ||
$ | var safeUrl = ensureHttps(ref.url); | ||
$refBottom.append('<a href="' + safeUrl + '" target="_blank" class="md-btn-direct">' + svgExternal + ' Kunjungi</a>'); | |||
} | } | ||
$refPanel.append($refBottom); | |||
$ | |||
// Logic Buka-Tutup (Akordeon) | |||
var | $toggleBtn.on('click', function() { | ||
$ | var isOpen = $(this).hasClass('open'); | ||
} | $(this).toggleClass('open'); | ||
if(isOpen) { | |||
$refPanel.slideUp(200); | |||
$(this).html(svgRef + ' Lihat Referensi <span class="md-q-indicator" style="background:'+qColor+';"></span>'); | |||
} else { | |||
$refPanel.slideDown(200).css('display', 'flex'); | |||
$(this).html(svgRef + ' Tutup Detail'); | |||
} | |||
}); | |||
$refContainer.append($ | $refContainer.append($toggleBtn).append($refPanel); | ||
$info.append($refContainer); | $info.append($refContainer); | ||
} | } | ||
$row.append($info); | $row.append($dragHandle).append($info); | ||
if (!isLocked || isSysop) { | if (!isLocked || isSysop) { | ||
var $editIcon = $('<button class="md-btn-icon | var $actionGroup = $('<div style="display:flex; flex-direction:column; gap:4px; align-items:flex-end;"></div>'); | ||
var $editIcon = $('<button class="md-btn-icon" style="padding:6px;">' + svgEdit + ' Edit</button>'); | |||
$editIcon.on('click', function() { showForm(index, rawContent); }); | $editIcon.on('click', function() { showForm(index, rawContent); }); | ||
$row.append($ | |||
var $cloneIcon = $('<button class="md-btn-icon" style="padding:6px; background:transparent; border:none; color:var(--md-text-sub);" title="Duplikasi Data">' + svgClone + '</button>'); | |||
$cloneIcon.on('click', function() { | |||
var cloned = JSON.parse(JSON.stringify(item)); | |||
dataItems.splice(index+1, 0, cloned); | |||
saveData('Menduplikasi baris', null, rawContent, true); | |||
}); | |||
$actionGroup.append($editIcon).append($cloneIcon); | |||
$row.append($actionGroup); | |||
} | } | ||
// Simple Drag and Drop Event Binding | |||
$row.on('dragstart', function(e) { $(this).addClass('dragging'); e.originalEvent.dataTransfer.setData('text/plain', index); }); | |||
$row.on('dragend', function() { $(this).removeClass('dragging'); }); | |||
$row.on('dragover', function(e) { e.preventDefault(); $(this).css('border-top', '3px solid var(--md-primary)'); }); | |||
$row.on('dragleave', function() { $(this).css('border-top', ''); }); | |||
$row.on('drop', function(e) { | |||
e.preventDefault(); $(this).css('border-top', ''); | |||
var oldIdx = parseInt(e.originalEvent.dataTransfer.getData('text/plain'), 10); | |||
var newIdx = parseInt($(this).data('index'), 10); | |||
if (oldIdx === newIdx) return; | |||
var movedItem = dataItems.splice(oldIdx, 1)[0]; | |||
dataItems.splice(newIdx, 0, movedItem); | |||
saveData('Mengubah urutan baris', null, rawContent, true); | |||
}); | |||
$list.append($row); | $list.append($row); | ||
}); | }); | ||
| Baris 499: | Baris 601: | ||
} | } | ||
// 9. FORM EDIT | // Ekspor ke CSV Generator | ||
function exportCSV() { | |||
if(dataItems.length === 0) return showToast('Tidak ada data untuk diekspor', 'error'); | |||
var csv = 'Label,Nilai,Tipe Referensi,URL Referensi\n'; | |||
dataItems.forEach(function(item) { | |||
var l = '"'+(item.prop||'').replace(/"/g,'""')+'"'; | |||
var v = '"'+(item.val||'').replace(/"/g,'""')+'"'; | |||
var rt = '"'+((item.ref && item.ref.type) ? item.ref.type : '')+'"'; | |||
var ru = '"'+((item.ref && item.ref.url) ? item.ref.url : '')+'"'; | |||
csv += [l, v, rt, ru].join(',') + '\n'; | |||
}); | |||
var blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' }); | |||
var link = document.createElement('a'); | |||
var url = URL.createObjectURL(blob); | |||
link.setAttribute('href', url); link.setAttribute('download', 'Data_Mippedia_' + getTodayString() + '.csv'); | |||
link.style.visibility = 'hidden'; document.body.appendChild(link); link.click(); document.body.removeChild(link); | |||
showToast('File CSV berhasil diunduh!', 'success'); | |||
} | |||
// 9. FORM EDIT V8 | |||
function showForm(index, rawContent) { | function showForm(index, rawContent) { | ||
var $container = $('#md-form-container'); | var $container = $('#md-form-container'); | ||
| Baris 510: | Baris 631: | ||
var $form = $('<div class="md-form-box"></div>'); | var $form = $('<div class="md-form-box"></div>'); | ||
$ | var $closeIcon = $('<button style="background:none;border:none;cursor:pointer;color:var(--md-text-sub);font-size:1.2rem;line-height:1;">×</button>'); | ||
$closeIcon.on('click', function(){ $container.empty(); }); | |||
var $titleWrap = $('<div class="md-form-title"></div>').append('<span>' + (isNew ? 'Tambah Data Baru' : 'Edit Data Terstruktur') + '</span>').append($closeIcon); | |||
$form.append($titleWrap); | |||
// Label Data + Auto Capitalize + Presets | |||
var $propGroup = $('<div class="md-form-group"><label>Label Data (Kategori)</label></div>'); | |||
var $propInp = $('<input type="text" id="md-inp-prop" class="md-input" placeholder="Misal: Tanggal Lahir" value="' + (currentData.prop || '') + '">'); | |||
$propInp.on('blur', function() { $(this).val(toTitleCase($(this).val())); }); // Auto Format | |||
$propGroup.append($propInp); | |||
var $presetWrap = $('<div style="margin-top:6px;"></div>'); | |||
var $ | |||
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() { $propInp.val(lbl); $propInp.removeClass('error'); }); | ||
$presetWrap.append($pill); | $presetWrap.append($pill); | ||
}); | }); | ||
| Baris 523: | Baris 652: | ||
$form.append($propGroup); | $form.append($propGroup); | ||
// Nilai Data & | // Nilai Data & Char Counter | ||
var $valBox = $('<div class="md-form-group"></div>'); | var $valBox = $('<div class="md-form-group"></div>'); | ||
$valBox.append('<label>Nilai Data</label><input type="text" id="md-inp-val" class="md-input" value="' + (currentData.val || '') + '">'); | $valBox.append('<label>Nilai Data Utama</label>'); | ||
var $valInp = $('<input type="text" id="md-inp-val" class="md-input" value="' + (currentData.val || '') + '">'); | |||
var $charCount = $('<span class="md-char-count">0</span>'); | |||
$valInp.on('input', function() { $charCount.text($(this).val().length); }).trigger('input'); | |||
$valBox.append($valInp).append($charCount); | |||
var $wikilinkOpts = $('<div style="margin-top:8px; display:flex; flex-direction:column; gap:8px; background:var(--md-surface); padding:10px; border-radius:8px; border:1px solid var(--md-border);"></div>'); | |||
$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-link" ' + (currentData.link?'checked':'') + '> Buat Wikilink Standar</label>'); | |||
$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" ' + (currentData.linkSplit?'checked':'') + '> Pisah Otomatis (Dan/Atau/Koma)</label>'); | |||
$wikilinkOpts.append('<div style="margin-top:4px;"><label style="font-size:0.75rem; color:var(--md-text-sub);">Halaman Target (Opsional):</label><input type="text" id="md-inp-target" class="md-input" style="padding:6px; font-size:0.8rem;" value="' + (currentData.linkTarget || '') + '"></div>'); | |||
$valBox.append($wikilinkOpts); | $valBox.append($wikilinkOpts); | ||
$form.append($valBox); | $form.append($valBox); | ||
// MEDIA | // SOSIAL MEDIA | ||
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:rgba(79,70,229,0.1); padding:10px; border-radius:8px; font-weight:700; color:var(--md-primary);"><input type="checkbox" id="md-chk-person" ' + (currentData.isPersonOrg?'checked':'') + '> Aktifkan Profil Sosial / Musik</label>'); | |||
$socialSec.append('<label style="display:flex; align-items:center; gap:8px; cursor:pointer; background: | |||
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.75rem; color:var(--md-text-sub);">' + p.name + '</label><input type="text" id="md-soc-' + key + '" class="md-input" placeholder="@username" value="' + (currentSocials[key]||'') + '"></div>'); | |||
$socialGrid.append('<div><label style="font-size:0. | |||
}); | }); | ||
$socialSec.append($socialGrid); | $socialSec.append($socialGrid); | ||
$form.append($socialSec); | $form.append($socialSec); | ||
// | // REFERENSI | ||
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:var(--md-text-main); border-bottom:1px solid var(--md-border); padding-bottom:5px;">Sumber Referensi</label>'); | ||
var $refGrid = $('<div class="md-ref-grid"></div>'); | var $refGrid = $('<div class="md-ref-grid" style="margin-top:10px;"></div>'); | ||
var $typeSel = $('<select id="md-ref-type" class="md-select"><option value="">-- Tipe Utama --</option></select>'); | |||
var $typeSel = $('<select id="md-ref-type" class="md-select"><option value="">-- | Object.keys(REF_TYPES).forEach(function(t) { $typeSel.append('<option value="'+t+'" '+(currentRef.type===t?'selected':'')+'>'+t+'</option>'); }); | ||
Object.keys(REF_TYPES).forEach(function(t) { | var $subTypeSel = $('<select id="md-ref-subtype" class="md-select"><option value="">-- Sub-Tipe --</option></select>'); | ||
var $subTypeSel = $('<select id="md-ref-subtype" class="md-select"><option value="">-- | |||
function updateSubTypes(mainType, selectedSub) { | function updateSubTypes(mainType, selectedSub) { | ||
$subTypeSel.empty().append('<option value="">-- | $subTypeSel.empty().append('<option value="">-- Sub-Tipe --</option>'); | ||
if (mainType && REF_TYPES[mainType]) { | if (mainType && REF_TYPES[mainType]) { | ||
REF_TYPES[mainType].forEach(function(st) { | REF_TYPES[mainType].forEach(function(st) { $subTypeSel.append('<option value="'+st+'" '+(selectedSub===st?'selected':'')+'>'+st+'</option>'); }); | ||
} | } | ||
} | } | ||
$typeSel.on('change', function() { updateSubTypes($(this).val(), ''); }); | $typeSel.on('change', function() { updateSubTypes($(this).val(), ''); }); | ||
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.75rem;">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.75rem;">Sub-Tipe</label></div>').find('div').last().append($subTypeSel); | ||
var $urlInput = $('<input type="url" id="md-ref-url" class="md-input" placeholder="https://...." value="' + (currentRef. | // Auto-Fill URL Logic | ||
var $urlInput = $('<input type="url" id="md-ref-url" class="md-input" placeholder="https://..." value="' + (currentRef.url || '') + '">'); | |||
var $siteInp = $('<input type="text" id="md-ref-site" class="md-input" placeholder="Nama Situs" value="' + (currentRef.site || '') + '">'); | |||
var $titleInp = $('<input type="text" id="md-ref-title" class="md-input" placeholder="Judul Halaman" value="' + (currentRef.title || '') + '">'); | |||
$urlInput.on('blur', function() { | $urlInput.on('blur', function() { | ||
var u = ensureHttps($(this).val()); | var u = ensureHttps($(this).val()); $(this).val(u); | ||
if (u) { | |||
var $siteInp | var domain = extractDomain(u); | ||
if (!$siteInp.val().trim()) $siteInp.val(domain); | |||
$ | if (!$titleInp.val().trim()) $titleInp.val(domain); | ||
// Auto Select Tipe | |||
if(u.indexOf('youtube.com') !== -1 || u.indexOf('youtu.be') !== -1) $typeSel.val('Video').trigger('change'); | |||
else if(u.indexOf('instagram.com') !== -1 || u.indexOf('twitter.com') !== -1 || u.indexOf('x.com') !== -1) $typeSel.val('Media Sosial').trigger('change'); | |||
} | } | ||
}); | }); | ||
$refGrid.append('<div class="md-grid-full"><label style="font-size:0. | $refGrid.append('<div class="md-grid-full"><label style="font-size:0.75rem;">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.75rem;">Judul Halaman</label></div>').find('div').last().append($titleInp); | ||
$refGrid.append('<div><label style="font-size:0. | $refGrid.append('<div><label style="font-size:0.75rem;">Nama Situs</label></div>').find('div').last().append($siteInp); | ||
$refGrid.append('<div><label style="font-size:0. | $refGrid.append('<div><label style="font-size:0.75rem;">Penulis/Kreator</label><input type="text" id="md-ref-author" class="md-input" value="' + (currentRef.author || '') + '"></div>'); | ||
$refGrid.append('<div><label style="font-size:0. | $refGrid.append('<div><label style="font-size:0.75rem;">Penerbit (Opsional)</label><input type="text" id="md-ref-publisher" class="md-input" value="' + (currentRef.publisher || '') + '"></div>'); | ||
var $dateBox = $('<div><label style="font-size:0.75rem;">Tgl Akses</label></div>'); | |||
var $dateBox = $('<div><label style="font-size:0. | var $dateInpWrap = $('<div style="display:flex; gap:4px;"></div>'); | ||
var $dateInpWrap = $('<div style="display:flex; gap: | |||
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="' + (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 6px;" title="Set Hari Ini">Cetak H.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 614: | Baris 736: | ||
$form.append($refArea); | $form.append($refArea); | ||
// | // Edit Summary & Actions | ||
var $summaryBox = $('<div class="md-form-group" style="margin-top:10px;"><input type="text" id="md-edit-summary" class="md-input" placeholder="Ringkasan suntingan (opsional)..." style="font-size:0.8rem; background:var(--md-bg);"></div>'); | |||
var $minorChk = $('<label style="font-size:0.75rem; margin-left:10px;"><input type="checkbox" id="md-edit-minor"> Suntingan Kecil</label>'); | |||
$summaryBox.append($minorChk); | |||
$form.append($summaryBox); | |||
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"> | var $btnSave = $('<button class="md-btn md-btn-save">Simpan Perubahan</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($btnCancel); | $actions.append($btnSave).append($btnCancel); | ||
if (!isNew) { | if (!isNew) { | ||
var $btnDelete = $('<button class="md-btn md-btn-delete">Hapus | var $btnDelete = $('<button class="md-btn md-btn-delete">Hapus Baris</button>'); | ||
$btnDelete.on('click', function() { | $btnDelete.on('click', function() { | ||
if(confirm('Yakin ingin | if(confirm('Yakin ingin membuang data ini ke tong sampah?')) { | ||
dataItems.splice(index, 1); | dataItems.splice(index, 1); | ||
saveData('Menghapus data', $(this), rawContent); | saveData('Menghapus data (' + currentData.prop + ')', $(this), rawContent, false); | ||
} | } | ||
}); | }); | ||
| Baris 635: | Baris 761: | ||
$container.append($form); | $container.append($form); | ||
// Interaksi Form | |||
$('#md-chk-person').on('change', function() { | $('#md-chk-person').on('change', function() { | ||
if($(this).is(':checked')) $('#md-social-box').css('display', 'grid'); | if($(this).is(':checked')) $('#md-social-box').slideDown(200).css('display', 'grid'); | ||
else $('#md-social-box'). | else $('#md-social-box').slideUp(200); | ||
}); | }); | ||
$('html, body').animate({ scrollTop: $form.offset().top - | $('html, body').animate({ scrollTop: $form.offset().top - 80 }, 300); | ||
$btnCancel.on('click', function() { $container.empty(); }); | $btnCancel.on('click', function() { $container.empty(); }); | ||
// Hotkeys (Ctrl+S & Esc) | |||
$form.on('keydown', function(e) { | |||
if (e.ctrlKey && e.key === 's') { e.preventDefault(); $btnSave.click(); } | |||
if (e.key === 'Escape') $btnCancel.click(); | |||
}); | |||
$propInp.focus(); // Auto focus | |||
$btnSave.on('click', function() { | $btnSave.on('click', function() { | ||
// Validation | |||
var newVal = $ | $propInp.removeClass('error'); $valInp.removeClass('error'); | ||
var newProp = $propInp.val().trim(); | |||
var newVal = $valInp.val().trim(); | |||
if (!newProp || !newVal) { | if (!newProp || !newVal) { | ||
if(!newProp) $propInp.addClass('error'); | |||
if(!newVal) $valInp.addClass('error'); | |||
showToast("Label dan Nilai wajib diisi!", "error"); | |||
return; | |||
} | } | ||
| Baris 661: | Baris 800: | ||
var refObj = { | var refObj = { | ||
type: $ | type: $typeSel.val(), subType: $subTypeSel.val(), | ||
url: ensureHttps($urlInput.val()), title: $titleInp.val().trim(), | |||
url: ensureHttps($ | site: $siteInp.val().trim(), author: $('#md-ref-author').val().trim(), | ||
publisher: $('#md-ref-publisher').val().trim(), date: $dateInp.val() | |||
site: $ | |||
publisher: $('#md-ref-publisher').val().trim(), | |||
}; | }; | ||
// Clean empty refs | |||
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; | ||
var newItem = { | var newItem = { | ||
prop: newProp, | prop: newProp, val: newVal, | ||
link: $('#md-inp-link').is(':checked'), linkSplit: $('#md-inp-split').is(':checked'), | |||
link: $('#md-inp-link').is(':checked'), | |||
linkTarget: $('#md-inp-target').val().trim(), | linkTarget: $('#md-inp-target').val().trim(), | ||
isPersonOrg: isPerson, | isPersonOrg: isPerson, socials: socObj, ref: refObj | ||
}; | }; | ||
| Baris 688: | Baris 820: | ||
else dataItems[index] = newItem; | else dataItems[index] = newItem; | ||
var cSum = $('#md-edit-summary').val().trim(); | |||
var sumMsg = cSum ? cSum : (isNew ? 'Tambah data ' + newProp : 'Edit data ' + newProp); | |||
var isMin = $('#md-edit-minor').is(':checked'); | |||
saveData(sumMsg, $(this), rawContent, isMin); | |||
}); | }); | ||
} | } | ||
// 10. JSON EXPORT & IMPORT MODAL | // 10. JSON EXPORT & IMPORT (MODAL V8) | ||
function showJsonModal(rawContent) { | function showJsonModal(rawContent) { | ||
var $container = $('#md-form-container'); | var $container = $('#md-form-container'); | ||
| Baris 699: | Baris 835: | ||
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">Console Data (JSON)</h4>'); | ||
$box.append('<p style="font-size:0. | $box.append('<p style="font-size:0.8rem; color:var(--md-text-sub); margin-bottom:10px;">Gunakan ini untuk backup data atau migrasi cepat antar halaman.</p>'); | ||
var $txt = $('<textarea class="md-textarea" rows=" | var $txt = $('<textarea class="md-textarea" rows="12" style="font-family:monospace; font-size:0.8rem; background:#1e1e1e; color:#d4d4d4;"></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">Timpa Data dengan JSON di Atas</button>'); | ||
var $btnCopy = $('<button class="md-btn" style="background:#10b981;">Salin Kode</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).append($btnCancel); | $actions.append($btnImport).append($btnCopy).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(); }); | ||
$btnCopy.on('click', function() { navigator.clipboard.writeText($txt.val()); showToast('JSON Disalin!', 'success'); }); | |||
$btnImport.on('click', function() { | $btnImport.on('click', function() { | ||
try { | try { | ||
| Baris 719: | Baris 858: | ||
if (Array.isArray(parsed)) { | if (Array.isArray(parsed)) { | ||
dataItems = parsed; | dataItems = parsed; | ||
saveData('Impor | saveData('Impor massal dari JSON', $(this), rawContent, false); | ||
} else | } else showToast("Format JSON salah! Harus berawal [ dan berakhir ]", "error"); | ||
} catch(e) { showToast("Error JSON: " + e.message, "error"); } | |||
} catch(e) { | |||
}); | }); | ||
} | } | ||
// 11. | // 11. EKSEKUSI UTAMA | ||
loadData().then(function(rawContent) { buildUI(rawContent); }); | loadData().then(function(rawContent) { buildUI(rawContent); }); | ||
}); | }); | ||
})(jQuery, mediaWiki) | })(jQuery, mediaWiki) | ||
/* ========================================================== | /* ========================================================== | ||