Wikikamus
idwiktionary
https://id.wiktionary.org/wiki/Wikikamus:Halaman_Utama
MediaWiki 1.47.0-wmf.10
case-sensitive
Media
Istimewa
Pembicaraan
Pengguna
Pembicaraan Pengguna
Wikikamus
Pembicaraan Wikikamus
Berkas
Pembicaraan Berkas
MediaWiki
Pembicaraan MediaWiki
Templat
Pembicaraan Templat
Bantuan
Pembicaraan Bantuan
Kategori
Pembicaraan Kategori
Indeks
Pembicaraan Indeks
Lampiran
Pembicaraan Lampiran
TimedText
TimedText talk
Modul
Pembicaraan Modul
Acara
Pembicaraan Acara
palak
0
2899
1463291
1461540
2026-07-10T17:58:11Z
Muhamad Izzul Fiqih
35471
1463291
wikitext
text/x-wiki
=={{bahasa|id}}==
{{kepala|id}}
{{-v-|id}}
# [[memeras]], meminta sesuatu (uang) dengan [[paksaan]]
{{-terjemahan-}}
{{t-atas}}
* {{fr}} : {{trad-|fr|rançonner}}, {{trad-|fr|extorquer}}
{{t-bawah}}
=={{bahasa|bjn}}==
{{kepala|bjn}}
{{-n-|bjn}}
# [[kabut asap]]
# [[mata perih terkena asap]]
=={{bahasa|pse}}==
{{kepala|pse}}
: {{suara|pse|LL-Q3367751 (pse)-Cloudy (Losstreak)-palak.wav}}
:{{suara|pse|LL-Q3367751_(pse)-Tarijushi-palak.wav}}
{{-n-|pse}}
# {{label|pse|Besemah}} kepala
#: ''palak aku benegh''
#: kepalaku pusing
# {{label|pse|Semende}} kepala
#: ‘’palak aku berase abut nian’’
#:: kepalaku terasa berat sekali
[[Kategori:WikiTutur - Besemah]]
[[Kategori:WikiTutur Palembang 18 Februari 2024]]
[[Kategori:WikiTutur - Semende]]
[[Kategori:WikiTutur Kopdar Bandar Lampung 29 Juni 2024]]
=={{bahasa|jax}}==
{{kepala|jax}}
{{-v-|jax}}
# [[menodong]]
# {{l|id|palak}}
{{-n-|jax}}
# [[kepala]]
=={{bahasa|min}}==
{{kepala|min}}
{{-adj-|min}}
# [[panas]]
# [[temperamen]]
=={{bahasa|mui-plm}}==
{{kepala|mui-plm}}
{{-n-|mui-plm}}
# kepala
{{-lafal-|mui-plm}}
* {{suara|mui-plm|LL-Q12497929 (mis)-Akbar Soepadhi-palak.wav}}
[[Kategori:WikiTutur - Palembang]]
[[Kategori:WikiTutur Daring 25 Februari 2024]]
=={{bahasa|min-ski}}==
{{kepala|min-ski}}
: {{suara|min-ski|nama file}}
{{-n-|min-ski}}
# [[emosi]]
ktxsm6sr0cfdi6g8qvunrbpja2qftg6
1463292
1463291
2026-07-10T17:59:16Z
Muhamad Izzul Fiqih
35471
/* Bahasa Melayu Barisan Selatan */
1463292
wikitext
text/x-wiki
=={{bahasa|id}}==
{{kepala|id}}
{{-v-|id}}
# [[memeras]], meminta sesuatu (uang) dengan [[paksaan]]
{{-terjemahan-}}
{{t-atas}}
* {{fr}} : {{trad-|fr|rançonner}}, {{trad-|fr|extorquer}}
{{t-bawah}}
=={{bahasa|bjn}}==
{{kepala|bjn}}
{{-n-|bjn}}
# [[kabut asap]]
# [[mata perih terkena asap]]
=={{bahasa|pse}}==
{{kepala|pse}}
: {{suara|pse|LL-Q3367751 (pse)-Cloudy (Losstreak)-palak.wav|q=Besemah}}
:{{suara|pse|LL-Q3367751_(pse)-Tarijushi-palak.wav|q=Semende}}
{{-n-|pse}}
# {{label|pse|Besemah}} kepala
#: ''palak aku benegh''
#: kepalaku pusing
# {{label|pse|Semende}} kepala
#: ‘’palak aku berase abut nian’’
#:: kepalaku terasa berat sekali
[[Kategori:WikiTutur - Besemah]]
[[Kategori:WikiTutur Palembang 18 Februari 2024]]
[[Kategori:WikiTutur - Semende]]
[[Kategori:WikiTutur Kopdar Bandar Lampung 29 Juni 2024]]
=={{bahasa|jax}}==
{{kepala|jax}}
{{-v-|jax}}
# [[menodong]]
# {{l|id|palak}}
{{-n-|jax}}
# [[kepala]]
=={{bahasa|min}}==
{{kepala|min}}
{{-adj-|min}}
# [[panas]]
# [[temperamen]]
=={{bahasa|mui-plm}}==
{{kepala|mui-plm}}
{{-n-|mui-plm}}
# kepala
{{-lafal-|mui-plm}}
* {{suara|mui-plm|LL-Q12497929 (mis)-Akbar Soepadhi-palak.wav}}
[[Kategori:WikiTutur - Palembang]]
[[Kategori:WikiTutur Daring 25 Februari 2024]]
=={{bahasa|min-ski}}==
{{kepala|min-ski}}
: {{suara|min-ski|nama file}}
{{-n-|min-ski}}
# [[emosi]]
op7o5qok9619j1zj0w2ewb52fk26va7
dawa
0
7410
1463311
1317375
2026-07-11T07:38:58Z
Wadaihangit
33201
[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+entri]] [bjn]
1463311
wikitext
text/x-wiki
=={{bahasa|ban}}==
{{kepala|ban}}
{{-adj-|ban}}
# [[panjang]]
#: ''batisné dawa sajan''
#: kakinya panjang sekali
{{-lafal-|ban}}
* {{suara|ban|LL-Q33070_(ban)-ima_(Luh_Gede_Krismayanti)-dawa.wav}}
[[Kategori:WikiTutur - Bali]]
[[Kategori:WikiTutur Daring 24 Maret 2024]]
=={{bahasa|bjn}}==
{{kepala|bjn}}
{{-v-|bjn}}
# [[tuduh]]
=={{bahasa|jv}}==
{{kepala|jv}}
{{-a-|jv}}
# [[panjang]]
#: ''Kéwan apa sing awaké '''dawa'''?''
#:: Hewan apa yang badannya '''panjang'''?
#: ''Rambutmu saiki wis '''dawa'''.''
#:: Rambutmu sekarang sudah '''panjang'''.
{{-lafal-|jv}}
* {{suara|jv|LL-Q33549 (jav)-Hanifahnd-dhowo.wav }}
* {{suara|jv|LL-Q33549 (jav)-Tetheow-dawa.wav|q=''Surabaya''}}
* {{suara|jv|LL-Q33549 (jav)-Almasiva Tirta Maedy-Dawa.wav|q=''Semarang''}}
* {{suara|jv|LL-Q33549 (jav)-Kanzcech-dowo.wav|Suara {{malang}}}}
[[Kategori:WikiTutur - Jawa]]
[[Kategori:WikiTutur Daring Umum 11 Februari 2024]]
[[Kategori:WikiTutur Yogyakarta 18 Februari 2024]]
[[Kategori:WikiTutur Kopdar Malang Hybrid 8 Juni 2024]]
=={{bahasa|kaw}}==
{{kepala|kaw}}
==={{k_s}}===
# [[panjang]]
=={{bahasa|tes}}==
{{kepala|tes}}
==={{k_s}}===
# [[panjang]]
r3let67enakzzr5da7iqc8latfkyiov
Kategori:Bahasa Orang Rimba
14
19417
1463287
955327
2026-07-10T17:54:13Z
Muhamad Izzul Fiqih
35471
Muhamad Izzul Fiqih memindahkan halaman [[Kategori:Bahasa Kubu]] ke [[Kategori:Bahasa Orang Rimba]]
899381
wikitext
text/x-wiki
[[Kategori:Bahasa|Kubu]]
[[Kategori:Bahasa di Indonesia]]
22sbkd1kfqhxk6d4uh1knthqf6hbg3i
baung
0
19825
1463326
1333745
2026-07-11T10:45:52Z
Raflinoer32
30361
[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+entri]] [id]
1463326
wikitext
text/x-wiki
=={{bahasa|id}}==
{{kepala|id}}
{{-n-|id}}
# ikan air tawar yang ukurannya dapat mencapai 57 cm, tidak bersisik, mata tertutup oleh kulit, mempunyai sirip lemak, sungut umumnya lebih pendek daripada kepala, warnanya perak kekuningan, tersebar di Sumatra, Jawa, dan Kalimantan〔Mystus spp.〕
=={{bahasa|ms}}==
{{kepala|ms}}
''Melayu Landak'' Kabupaten Landak
=== {{k_b}} ===
# [[baung]] (nama ikan)
=={{bahasa|kvb}}==
{{kepala|kvb}}
: {{pemenggalan|kvb|ba|ung}}
{{-v-|kvb}}
# [[ikan]] [[baung]]
[[Kategori:Edit-a-thon WikiKathā Maret 2026]]
8j33kmrehtpynx5m3fkgg9ltlc92d50
1463327
1463326
2026-07-11T10:46:07Z
Raflinoer32
30361
/* {{bahasa|id}} */
1463327
wikitext
text/x-wiki
=={{bahasa|id}}==
{{kepala|id}}
{{-n-|id}}
# ikan air tawar yang ukurannya dapat mencapai 57 cm, tidak bersisik, mata tertutup oleh kulit, mempunyai sirip lemak, sungut umumnya lebih pendek daripada kepala, warnanya perak kekuningan, tersebar di Sumatra, Jawa, dan Kalimantan〔''Mystus spp.''〕
=={{bahasa|ms}}==
{{kepala|ms}}
''Melayu Landak'' Kabupaten Landak
=== {{k_b}} ===
# [[baung]] (nama ikan)
=={{bahasa|kvb}}==
{{kepala|kvb}}
: {{pemenggalan|kvb|ba|ung}}
{{-v-|kvb}}
# [[ikan]] [[baung]]
[[Kategori:Edit-a-thon WikiKathā Maret 2026]]
sa1z3t793yj2v5x9s575m7uuegdtifb
tuduh
0
33130
1463312
1242834
2026-07-11T07:39:56Z
Wadaihangit
33201
[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+entri]] [bjn]
1463312
wikitext
text/x-wiki
=={{bahasa|id}}==
{{kepala|id}}
{{-n-|id}}
# {{rfdef|id}}
{{-sinonim-}}
* [[tuduhan]]
* [[penuduhan]]
{{-turunan-}}
* [[dituduh]]
* [[penuduh]]
{{-terjemahan-}}
{{t-atas}}
* {{fr}} : {{trad-|fr|accusation}}
{{t-bawah}}
{{-v-|id}}
# {{rfdef|id}}
{{-sinonim-}}
* [[menuduh]]
{{-terjemahan-}}
{{t-atas}}
* {{fr}} : {{trad-|fr|accuser}}
{{t-bawah}}
=={{bahasa|bjn}}==
{{kepala|bjn}}
{{-v-|bjn}}
# [[beri tahu]], [[jelas]] tentang informasi dsb
=={{bahasa|jv}}==
{{kepala|jv}}
{{-etimologi-}}
:{{Inh+|r=y|jv|PAN|tuzuq}}.
{{-v-|jv}}
#[[tunjuk]]
{{-rujukan-}}
* {{R:map:ACD|*tuzuq₁|id=30955|type=c}}
93js91kenz9dxj4y8lcrmeijczm0ziz
adigung
0
38771
1463335
1136085
2026-07-11T10:58:45Z
Raflinoer32
30361
/* {{bahasa|su}} */
1463335
wikitext
text/x-wiki
=={{bahasa|id}}==
{{kepala|id}}
{{-a-|id}}
# bersifat menonjolkan keluhuran, keturunan, dan kebangsawanan
{{-turunan-|id}}
{{-terjemahan-}}
{{t-atas}}
{{t-bawah}}
{{-bacaan-}}
* {{R:KBBI Daring}}
{{rfv|id|impor dari KBBI}}
=={{bahasa|su}}==
{{kepala|su}}
{{suara|su|LL-Q34002 (sun)-A. D. Prakasa-adigung.wav}}
{{-adj-|su}} ᮃᮓᮤᮌᮥᮀ
# [[angkuh]]
# [[sombong]]
[[Kategori:WikiTutur Kopdar Bandung 01 Juni 2024]]
70e1i8o4rd0ycy9cnnjih5tiako0q9m
beri
0
38871
1463298
1433769
2026-07-11T00:36:02Z
~2026-39341-95
48716
1463298
wikitext
text/x-wiki
=={{bahasa|id}}==
{{kepala|id}}
{{suara|id|LL-Q9240 (ind)-Cloudy (Losstreak)-beri.wav}}
{{-v-|id}} /bê ri/
# serahkan atau bagi sesuatu kepada orang lain
#* {{RQ:Mustikarasa
|page= 48
|text=Ikan ada jang diambil dari laut, dinamakan ikan laut dan ada pula jang diambil dari empang, rawa, kali, danau, pendek kata segala matjam perairan jang terdapat di daratan. Jang belakangan ini kami '''beri''' nama ikan darat sadja.
|norm=Ikan ada yang diambil dari laut, dinamakan ikan laut dan ada pula yang diambil dari empang, rawa, kali, danau, pendek kata segala macam perairan yang terdapat di daratan. Yang belakangan ini kami '''beri''' nama ikan darat saja.
|url=https://id.wikisource.org/wiki/Halaman:Mustikarasa.pdf/56#:~:text=Ikan%20ada%20jang%20diambil%20dari%20laut%2C%20dinamakan%20ikan%20laut%20dan%20ada%20pula%20jang%20diambil%20dari%20empang%2C%20rawa%2C%20kali%2C%20danau%2C%20pendek%20kata%20segala%20matjam%20perairan%20jang%20terdapat%20di%20daratan.%20Jang%20belakangan%20ini%20kami%20beri%20nama%20ikan%20darat%20sadja.}}
# ''bentuk [[transitif]] dari'' '''[[mem{{PAGENAME}}]]'''
{{-n-|id}} /bè ri/
# buah-buahan berukuran kecil, kenyal, tanpa biji, berair, dan dapat dimakan, seperti [[stroberi]], [[rasberi]], atau [[bluberi]]
#: {{hiponim|id|stroberi|rasberi|bluberi}}
{{-turunan-|id}}
{{-terjemahan-}}
{{t-atas}}
* {{nl}} : {{trad-|nl|geven}}
* {{en}} : {{trad-|en|give}}
* {{fr}} : {{trad-|fr|donner}}, {{trad-|fr|accorder}}
{{t-bawah}}
{{-bacaan-}}
* {{R:KBBI Daring}}
{{rfv|id|impor dari KBBI}}
[[Kategori:id:Ambigu]]
=={{bahasa|bew}}==
{{kepala|bew}}
{{-v-|bew}}
# memberikan sesuatu
#: ''dia diberi duit ama ema'nya''
#: dia diberikan uang oleh ibunya
# hajar, pukul
#: ''gua beri sekali, lu bisa mati''
#: saya pukul sekali, (maka) kamu bisa mati
=={{bahasa|mqg}}==
{{kepala|mqg}}
{{-v-|mqg}}
# [[beri]]
# [[serah]]
[[Kategori:Edit-a-thon WikiKathā Maret 2026]]
koaqip461bert1b3j2dgkjdwjkl4wur
berikan
0
45844
1463297
1153839
2026-07-11T00:33:25Z
~2026-39341-95
48716
1463297
wikitext
text/x-wiki
=={{bahasa|id}}==
{{kepala|id}}
{{imbuhan -kan|beri}}
# ''bentuk [[dasar]] dan [[imperatif]] dari'' '''[[memberikan]]'''
# ''bentuk [[imperatif]] dari'' '''[[beri]]'''<br>''berikan uangmu!''
#: {{sinonim|id|bagikan|serahkan|haturkan|kasihkan|distribusikan}}
{{imbuhan ber-|ikan}}
# ada [[ikan]]nya<br> ''ia pergi memancing ke danau yang berikan banyak''
{{-terjemahan-}}
{{t-atas}}
{{t-bawah}}
{{-bacaan-}}
* {{R:KBBI Daring}}
{{rfv|id|impor dari KBBI}}
[[Kategori:id:Ambigu]]
swk1kyhk7v8uwj6rpgvlcmn6h7vp0jn
cinta
0
46107
1463279
1462598
2026-07-10T16:31:44Z
~2026-38274-06
48639
/* */
1463279
wikitext
text/x-wiki
KANTOR BIRO JODOH KHUSUS WAJAH GANTENG CANTIK SAJA 0895 2483 7937 BER IJIN NOMOR NPWP NEGARA
GALANG DANA DONASI SUMBANGAN BUAT KANTORNYA DI INDRAMAYU PANTAI DEKAT JAKARTA
BANK BRI 0090 0102 9636 530 ADI FANDIYASA
=={{bhs|id}}==
{{suara|id|LL-Q9240 (ind)-Alfiyah Rizzy Afdiquni-cinta.wav}}
{{-etimologi-}}
Dari {{der|id|sa|चिन्ता|t=peduli, kasih}}.
{{-nom-|id}}
# rasa sayang atau kasih yang kuat
#* {{RQ:20 Mei Pelopor 17 Agustus
|text=semangat Dr. Wahidin dalam segala perdjuangannja, jalah '''tjinta''' jang dalam terhadap bangsanja
|page=34
|url=https://id.wikisource.org/wiki/Halaman:20_Mei_Pelopor_17_Agustus_-_Museum_Dewantara_Kirti_Griya.pdf/33#:~:text=semangat%20Dr.%20Wahidin%20dalam%20segala%20perdjuangannja%2C%20jalah%20tjinta%20jang%20dalam%20terhadap%20bangsanja
}}
## rasa [[sayang]], [[kasih]], [[peduli]] yang dalam dan kuat terhadap seseorang
##: {{ux|id|'''Cinta''' ibu tak terhingga sepanjang masa.}}
##: {{ux|id|Rasa '''cinta''' istriku adalah yang terpenting bagiku.}}
## sikap menyayang, mengasihi, memperhatikan, dan peduli terhadap kebaikan dan kebahagiaan sesuatu atau seseorang
## rasa ketertarikan yang besar terhadap seseorang.
## rasa [[suka]] yang mendalam; [[entusiasme]] terhadap sesuatu
##: {{ux|id|Aku '''cinta''' bulutangkis Indonesia.}}
# seseorang yang menjadi perhatian [[romantis]] orang lain
# panggilan kekasih, sayang
{{-turunan-|id}}
{{-terjemahan-}}
{{t-atas}}
* {{en}} : {{trad-|en|love}}
* {{es}} : {{trad-|es|amor}}
* {{fr}} : {{trad-|fr|amour}}
* {{it}} : {{trad-|it|amore}}
* {{mad}} : {{trad-|mad|trèsna}}
* {{pt}} : {{trad-|pt|amor}}
* {{sw}} : {{trad-|sw|upendo}}
*{{tr}}: {{trad-|it|aşk}}
{{t-bawah}}
lt2jz1twy7ek4rde1zilzw2q5rf1aul
sabat
0
47786
1463313
1235966
2026-07-11T07:43:18Z
Wadaihangit
33201
[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+entri]] [bjn]
1463313
wikitext
text/x-wiki
=={{bahasa|id}}==
{{kepala|id}}
{{lihat 2|v=y}}
{{-turunan-|id}}
{{-terjemahan-}}
<!--Anda dapat menyalin templat {{t-atas}} -- {{t-bawah}} di bawah berulang kali untuk masing masing arti kata, masing-masing dibedakan melalui parameter pertamanya (misalkan {{t-atas|arti 1}} dan {{t-atas|arti 2}} dst). Lihat [[Wiktionary:Terjemahan]] untuk panduan membuat lebih dari satu kolom terjemahan-->
{{t-atas}}
{{t-bawah}}
{{-bacaan-}}
* {{R:KBBI Daring}}
{{rfv|id|impor dari KBBI}}
=={{bahasa|bjn}}==
{{kepala|bjn}}
{{-n-|bjn}}
# [[belukar]]
q2ejqnyec8nbatn5yb8nyx82dnmavmm
talah
0
59473
1463315
1338475
2026-07-11T07:44:51Z
Wadaihangit
33201
[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+entri]] [bjn]
1463315
wikitext
text/x-wiki
=={{bahasa|id}}==
{{kepala|id}}
{{lihat 2|v=y}}
{{-turunan-|id}}
{{-sinonim-}}
{{t-atas}}
{{t-bawah}}
{{-bacaan-}}
* {{R:KBBI Daring}}
{{rfv|id|impor dari KBBI}}
=={{bahasa|bjn}}==
{{kepala|bjn}}
{{-adj-|bjn}}
# [[habis]], [[sudah]]
=={{bahasa|osi}}==
{{kepala|osi}}
{{pemenggalan|osi|ta|lah}}
{{-ptcl-|osi}}
# [[partikel]] yang dipakai untuk menekankan [[kata]] yg di depannya yang mengandung [[arti]] suruhan atau [[penekanan]]
f0nwv9lnzp53day59tzmo75hyrw3dcb
tuhu
0
67903
1463309
1242871
2026-07-11T07:34:57Z
Wadaihangit
33201
[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+entri]] [bjn]
1463309
wikitext
text/x-wiki
=={{bahasa|id}}==
{{kepala|id}}
{{-n-|id}}
# burung yang keluar dan mencari mangsa pada malam hari, mengeluarkan bunyi "tuhu" "tuhu" dengan nyaring;
{{-etimologi-}}
* [[burung tuhu]], [[burung culik]]/[[burung cakik]]
{{-turunan-|id}}
{{-sinonim-}}
{{t-atas}}
{{t-bawah}}
{{-bacaan-}}
* {{R:KBBI Daring}}
{{rfv|id|impor dari KBBI}}
=={{bahasa|bjn}}==
{{kepala|bjn}}
{{-v-|bjn}}
# [[muat]], [[masukkan]]
=={{bahasa|bnn}}==
{{kepala|bnn}}
{{-n-|bnn}}
# [[pantat]]
[[Kategori:id:Burung]]
d5slh6pd6vrbc6caf2wh00vgdlu1og4
baal
0
111074
1463320
1274727
2026-07-11T07:49:46Z
Wadaihangit
33201
[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+entri]] [bjn]
1463320
wikitext
text/x-wiki
=={{bahasa|id}}==
{{kepala|id}}
{{suara|id|LL-Q9240 (ind)-Dvnfit-baal.wav}}
{{-a-|id}}
# kebal (tentang rasa) karena kedinginan, disuntik, dsb. sehingga tidak lagi berasa dingin, sakit, dsb.; mati rasa
{{-etimologi-}}
=={{bahasa|bkr}}==
{{kepala|bkr}}
{{-adj-|bkr}}
# belum kering
#: ''salawar baal''
#: celana yang belum kering
[[Kategori:WikiBalalah - Bakumpai]]
{{-turunan-|id}}
{{-terjemahan-}}
=={{bahasa|bjn}}==
{{kepala|bjn}}
{{-adj-|bjn}}
# setengah kering, setengah basah
=={{bahasa|su}}==
{{kepala|su}}
{{-n-|su}}
# kebal (tentang rasa) karena kedinginan, disuntik, dsb. sehingga tidak lagi berasa dingin, sakit, dsb.; mati rasa
# [[kebas]]
{{-lafal-|su}}
* {{suara|su|LL-Q34002 (sun)-Saniskala-baal.wav}}
{{t-atas}}
{{t-bawah}}
{{-bacaan-}}
* {{R:KBBI Daring}}
{{rfv|id|impor dari KBBI}}
[[Kategori:WikiTutur - Sunda]]
[[Kategori:WikiTutur Jakarta 3 Februari 2024]]
6djjxzpai73m9ppmm6n92z825vujaer
tapah
0
115984
1463328
1240870
2026-07-11T10:47:02Z
Raflinoer32
30361
/* {{bahasa|id}} */
1463328
wikitext
text/x-wiki
=={{bahasa|id}}==
{{kepala|id}}
'''tapah'''
# {{ragam dari|id|tapa}}
# ikan air tawar yang ukurannya dapat mencapai 150 cm, tubuhnya pipih memanjang, mulut mengarah ke atas (cakil), warnanya kehitaman dengan garis putih dari belakang tutup insang hingga pangkal ekor, tersebar di perairan Indocina, Malaysia, dan Indonesia (Sumatra dan Kalimantan)〔''Wallago leerii''〕
{{-bacaan-}}
* {{R:KBBI Daring}}
{{rfv|id|impor dari KBBI}}
klgsjcii0iplo6ljrl6p7o94bsded8v
mengerjakan
0
154333
1463295
1227256
2026-07-11T00:23:49Z
~2026-39341-95
48716
/* Bahasa Indonesia */
1463295
wikitext
text/x-wiki
=={{bahasa|id}}==
{{kepala|id}}
{{imbuhan me-kan|kerja}}
# ''bentuk [[aktif]] dari [[bekerja]]''
# melakukan; melaksanakan; menjalankan; berbuat sesuatu
#:''ia selalu '''mengerjakan''' tugasnya dengan baik''
# mengusahakan; memperbuat sesuatu
#:''untuk '''mengerjakan''' pemukiman baru diperlukan biaya yang tidak sedikit''
# melaksanakan; menjalankan; menyelesaikan
#:''tim penelitian itu akan '''mengerjakan''' tugasnya dalam waktu dekat ini''
{{-terjemahan-}}
{{t-atas}}
{{t-bawah}}
{{-bacaan-}}
* {{R:KBBI Daring}}
{{rfv|id|impor dari KBBI}}
5ffnz3jpp0d93q63gwxfwu83abl1i2a
bantukan
0
194262
1463296
1300742
2026-07-11T00:30:26Z
~2026-39341-95
48716
1463296
wikitext
text/x-wiki
=={{bahasa|id}}==
{{kepala|id}}
{{imbuhan -kan|bantu}}
# ''bentuk [[dasar]] dan [[imperatif]] dari'' '''[[membantukan]]'''
# {{rfdef|id}}
{{-terjemahan-}}
{{t-atas}}
{{t-bawah}}
oaon1orjtp5u4ebqxzoaytryb0qiatw
Templat:pemenggalan
10
209192
1463299
1334467
2026-07-11T03:36:38Z
Swarabakti
18192
1463299
wikitext
text/x-wiki
<includeonly><span class="hyphenation" title="Pemenggalan "{{SUBPAGENAME}}" dalam {{#invoke:bahasa|getLangName|{{{1}}}|nocap=1}}">{{#switch:{{{1|}}}|=<strong class="error">Kode bahasa tidak sah</strong>[[Kategori:Halaman dengan parameter plain]]|#default=}}{{#if:{{{2|}}}
|{{{2}}}}}{{#if:{{{3|}}}
|‧{{{3}}}}}{{#if:{{{4|}}}
|‧{{{4}}}}}{{#if:{{{5|}}}
|‧{{{5}}}}}{{#if:{{{6|}}}
|‧{{{6}}}}}{{#if:{{{7|}}}
|‧{{{7}}}}}{{#if:{{{8|}}}
|‧{{{8}}}}}{{#if:{{{9|}}}
|‧{{{9}}}}}{{#if:{{{10|}}}
|‧{{{10}}}}}{{#if:{{{11|}}}
|‧{{{11}}}}}{{#if:{{{12|}}}
|‧{{{12}}}}}{{#if:{{{13|}}}
|‧{{{13}}}}}{{#if:{{{14|}}}
|‧{{{14}}}}}{{#if:{{{15|}}}
|‧{{{15}}}}}{{#if:{{{16|}}}
|‧{{{16}}}}}{{#if:{{{17|}}}
|‧{{{17}}}}}{{#if:{{{18|}}}
|‧{{{18}}}}}{{#if:{{{19|}}}
|‧{{{19}}}}}{{#if:{{{20|}}}
|‧{{{20}}}}}{{#if:{{{21|}}}
|‧{{{21}}}}}</span></includeonly><noinclude>
: {{pemenggalan|id|pe|meng|gal|an}}
{{documentation}}
e5jt31hxhjjz9to1tn6nrmfdayfuenu
Templat:pemenggalan/dokumentasi
10
209193
1463300
1051646
2026-07-11T03:37:27Z
Swarabakti
18192
1463300
wikitext
text/x-wiki
{{subhalaman dokumentasi}}
===Kegunaan===
Untuk pemisahan istilah dalam tulisan ketika disambung di baris baru, bukan pemisahan istilah berdasarkan lafal suku katanya.
Templat ini akan memunculkan tanda titik tengah (‧) di antara unsur yang dipenggal. Isi parameter pertama dengan kode bahasa, kemudian diikuti oleh pemenggalan unsur kata sesuai konvensi.
Setiap entri homonim (sama tulisan dan sama bunyi) dapat memiliki pemenggalan yang berbeda-beda tergantung unsur penyusunnya, contohnya beruang yang dapat dipenggal menjadi {{pemenggalan|id|be|ru|ang}} (hewan liar; mempunyai ruang) atau {{pemenggalan|id|ber|u|ang}} (mempunyai uang).
Untuk konvensi pemenggalan di setiap bahasa, liat halaman ketentuan entri bahasa masing-masing.
===Contoh pemanggilan===
<pre>
: {{pemenggalan|id|pe|meng|gal|an}}
</pre>
[[Kategori:Templat umum]]
awvfe5dap23jfo9irmtjdrjjjtcaby5
rajo
0
226472
1463283
1333262
2026-07-10T17:51:37Z
Muhamad Izzul Fiqih
35471
[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+entri]] [jax]
1463283
wikitext
text/x-wiki
=={{bahasa|jax}}==
{{kepala|jax}}
{{-n-|jax}}
# [[raja]]
=={{bahasa|min}}==
{{kepala|min}}
{{-n-|min}}
# [[raja]]
=={{bahasa|kvb}}==
{{kepala|kvb}}
{{-v-|kvb}}
# [[raja]]
# [[pemimpin]]
# [[roh yg dianggap berkuasa atas diri]]
{{-v-|kvb}}
# '''rajo godong'''= pejabat pemerintah seperti bupati, gubernur, menteri, atau presiden
# '''rajo brail'''= roh-roh jahat yang tinggal dalam jantung seseorang sehingga mengakibatkan orang tersebut menjadi tempramental dan kasar
[[Kategori:Edit-a-thon WikiKathā Maret 2026]]
f5ey23c39ckkf8d0u1rvlsroqc7tev5
sumpu
0
229601
1463308
1294881
2026-07-11T07:31:22Z
Wadaihangit
33201
[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+entri]] [bjn]
1463308
wikitext
text/x-wiki
=={{bahasa|bjn}}==
{{kepala|bjn}}
{{-adv-|bjn}}
# sangat [[puas]]
=={{bahasa|wlo}}==
{{kepala|wlo}}
{{-v-|wlo}}
# [[minum]]
0q21ump0ivukjliz62ym2r7cwo4slc9
liung
0
229846
1463322
1360905
2026-07-11T07:52:03Z
Wadaihangit
33201
[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+entri]] [bjn]
1463322
wikitext
text/x-wiki
==bahasa Hakka Hoiliukfung==
{{hak-forms|[[龍]]|-}}
*''[[Thòi-vân Hak-kâ-ngî Phîn-yîm Fông-on|pinˊ imˊ fongˊ on]]'' : liung (liung<sup>55</sup>)
{{-n-|hak}}
#[[naga]]
[[Kategori:Kata bahasa Hakka Hoiliukfung]]=={{bahasa|bjn}}==
{{kepala|bjn}}
{{-v-|bjn}}
# [[hindar]]
f97lj2tdyc5d8bnyfvejh872oatlh25
1463323
1463322
2026-07-11T07:52:17Z
Wadaihangit
33201
1463323
wikitext
text/x-wiki
==bahasa Hakka Hoiliukfung==
{{hak-forms|[[龍]]|-}}
*''[[Thòi-vân Hak-kâ-ngî Phîn-yîm Fông-on|pinˊ imˊ fongˊ on]]'' : liung (liung<sup>55</sup>)
{{-n-|hak}}
#[[naga]]
[[Kategori:Kata bahasa Hakka Hoiliukfung]]
=={{bahasa|bjn}}==
{{kepala|bjn}}
{{-v-|bjn}}
# [[hindar]]
abflv2vkchq1ramimukunktjoxq6eee
halam
0
251213
1463301
1187430
2026-07-11T07:24:16Z
Wadaihangit
33201
[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+entri]] [bjn]
1463301
wikitext
text/x-wiki
=={{bahasa|bjn}}==
{{kepala|bjn}}
{{-adj-|bjn}}
# dahulu kala
=={{bahasa|ljp}}==
{{kepala|ljp}}
{{-n-|ljp}}
# [[hitam]]
{{-lafal-|ljp}}
* {{suara|ljp|LL-Q49215 (ljp)-MiraEria-halam.wav}}
[[Kategori:WikiTutur - Lampung Api]]
[[Kategori:WikiTutur Jakarta 3 Februari 2024]]
ji4wtnhycgkc1e6khzejk5rbr9wxi8s
abrag
0
252515
1463331
1335738
2026-07-11T10:52:52Z
Raflinoer32
30361
[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+entri]] [su]
1463331
wikitext
text/x-wiki
=={{bahasa|jv}}==
{{kepala|jv}}
{{-n-|jv}}
# {{banyumas}} barang-barang
#: ‘’tuku abrag nang pasar miki’’
#:beli barang-barang dipasar tadi
{{-lafal-|jv-bms}}
* {{suara|jv-bms|LL-Q33549_(jav)-Sabil_Khoer_Al_Munawar-Abrag(nomina).wav}}
[[Kategori:WikiTutur - Jawa Banyumasan]]
[[Kategori:WikiTutur Daring 10 Maret 2024]]
=={{bahasa|su}}==
{{kepala|su}}
{{-adj-|su}}
# kasar
=={{bahasa|osi}}==
{{kepala|osi}}
: {{suara|osi|LL-Q2701322 (osi)-Nurholipah (Dinda Anggun)-Abrag.wav}}
{{-v-|osi}}
# [[didorong]] agar segera dikerjakan
[[Kategori:Edit-a-thon WikiKathā Maret 2026]]
rq6h64w56mxt0cppaay8uembamopdrh
abeh
0
259410
1463329
1284285
2026-07-11T10:50:47Z
Raflinoer32
30361
[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+entri]] [su]
1463329
wikitext
text/x-wiki
=={{bahasa|eno}}==
{{kepala|eno}}
{{-n-|eno}}
# [[bambu]]
#: '''''abeh''' eap''
#:: '''bambu''' betung
{{-v-|eno}}
# [[curi]]
{{-turunan-}}
* {{l|eno|yahabeh}}
=={{bahasa|min}}==
{{kepala|min}}
{{-lafal-}}
* {{suara|min|LL-Q13324 (min)-Maronsita-abeh.wav}}
{{-a-|min}}
# [[paham]]
#: {{suara|min|LL-Q13324 (min)-Maronsita-lai abeh kecek den dek ang?.wav|''lai '''abeh''' kecek den dek ang?''}}
#:: apakah kamu '''paham''' dengan perkataan saya?
{{-ragam-}}
* {{l|min|obeh}}
{{-adj-|min}}
# teratur dan bersih (tentang pekerjaan atau keadaan rumah); [[rapi]]; [[bersih]]; [[kemas]]
#: {{syn|min|kameh|taratik|barasiah}}
#: '''''abeh''' rumah ko dek nyo''
#:: rumah ini '''rapi''' karena dia
{{-turunan-}}
* {{l|min|baabeh-abeh}}
* {{l|min|maabehan}}
* {{l|min|taabehan}}
* {{l|min|paabeh}}
{{-adv-|min}}
# [[teliti]]
{{-turunan-}}
* {{l|min|abeh-abeh}}
[[Kategori:WikiTutur 2.0 - Minangkabau]]
[[Kategori:WikiTutur 2.0 Padang 22 Oktober 2024]]
=={{bahasa|su}}==
{{kepala|su}}
{{-conj-|su}}
# supaya
98abotsczhvm491tho173lt3t4czhpq
bakah
0
264201
1463319
1143024
2026-07-11T07:49:14Z
Wadaihangit
33201
[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+entri]] [bjn]
1463319
wikitext
text/x-wiki
=={{bahasa|bjn}}==
{{kepala|bjn}}
{{-adj-|bjn}}
# [[basah]]
=={{bahasa|knl}}==
{{kepala|knl}}
{{-lafal-}}
* {{suara|knl|LL-Q6389309 (knl)-Mario Manggit (Neyana999)-bakah.wav}}
{{-adj-|knl}}
# [[besar]]
[[Kategori:WikiTutur 2.0 - Keninjal]]
[[Kategori:Kopdar WikiTutur 2.0 Jakarta 30 April 2025]]
5pcjx5n1w7iuo294h5yxk0ge5o3ujg6
Pengguna:Swarabakti/Gadget-EntryAdder.js
2
267654
1463338
1456602
2026-07-11T11:40:05Z
Swarabakti
18192
1463338
javascript
text/javascript
mw.loader.using(["mediawiki.api", "mediawiki.util"]).then(function () {
"use strict";
mw.loader.load(
"https://id.wiktionary.org/w/index.php?title=Pengguna:Swarabakti/Gadget-EntryAdder.css&action=raw&ctype=text/css",
"text/css"
);
// ---------------------
// Add more languages by adding keys here.
// ---------------------
const EG_I18N = {
id: {
btn_add_language: "Tambahkan entri baru pada halaman ini",
modal_title_add: "Tambahkan entri baru — ",
fill_langcode: "(isi dengan kode bahasa)",
select_pos: "(pilih kelas kata)",
fill_meaning: "(isi dengan maknanya)",
add_hom: "Tambah entri homonim",
remove_hom: "Hapus entri homonim",
add_pos: "Tambah kelas kata",
remove_pos: "Hapus kelas kata",
add_def: "Tambah definisi",
remove_def: "Hapus definisi",
preview: "Pratayang",
undo: "Balikkan",
save: "Simpan",
cancel: "Batalkan",
preview_loading: "Pratayang...",
save_loading: "Menyimpan...",
err_fill_all: "Isi dulu semua baris yang tersedia, atau hapus baris yang kosong.",
err_lang_exists: "Subjudul untuk bahasa ini sudah tersedia, gunakan kode bahasa lain atau sunting langsung bagiannya.",
err_fetch: "Gagal mengambil isi halaman.",
err_preview: "Gagal memuat pratayang.",
err_save_prefix: "Gagal menyimpan:",
err_no_change: "Tiada perubahan untuk disimpan.",
prompt_fill_langcode: "Isi kode bahasa dulu.",
save_summary_prefix: "[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+entri]]",
pos_labels: {
n: "nomina",
v: "verba",
adj: "adjektiva",
adv: "adverbia",
num: "numeralia",
pron: "pronomina",
pn: "nama diri",
ptcp: "partisipel",
conj: "konjungsi",
intj: "interjeksi",
ptcl: "partikula",
det: "penentu",
art: "artikula",
clf: "penggolong",
prep: "preposisi",
postp: "postposisi",
circp: "sirkumposisi",
ambip: "ambiposisi",
pref: "awalan",
suf: "akhiran",
inf: "sisipan",
circf: "apitan",
intf: "sambungan",
rt: "akar",
ph: "frasa",
pb: "peribahasa",
rom: "romanisasi",
ltr: "huruf",
sym: "simbol"
}
},
bew: {
btn_add_language: "Tambahin pentol kata anyar bakal ni halaman",
modal_title_add: "Tambahin pentol anyar — ",
fill_langcode: "(isi ama kode basanya)",
select_pos: "(pili rungkun katanya)",
fill_meaning: "(isi ama pema'naannya)",
add_hom: "Tambah pentol kata sejogrokan",
remove_hom: "Apus pentol kata sejogrokan",
add_pos: "Tambah rungkun katanya",
remove_pos: "Apus rungkun katanya",
add_def: "Tambah pema'naannya",
remove_def: "Apus pema'naannya",
preview: "Terawang",
undo: "Balikin",
save: "Simpen",
cancel: "Urungin",
preview_loading: "Nerawangin...",
save_loading: "Nyimpenin...",
err_fill_all: "Isiin dulu semua baris rungkun ama ma'na nyang derak, atawa apus-apusin nyang kopong.",
err_lang_exists: "Anakjudul bakal entu basa udah derak — jal paké laèn kode basa atawa permak langsung entu bagèan.",
err_fetch: "Kaga' bisa nyomot isi halaman.",
err_preview: "Kaga' bisa munggah terawangan.",
err_save_prefix: "Kaga' bisa nyimpen:",
err_no_change: "Kaga' ada perobahan bakal disimpen.",
prompt_fill_langcode: "Isi kode basanya dulu.",
save_summary_prefix: "[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+pentol]]",
pos_labels: {
n: "kata peada",
v: "kata kereja",
adj: "kata penyipat",
adv: "kata penerang",
num: "kata pengangka",
pron: "kata pengganti",
pn: "kata nama",
ptcp: "dumanan",
conj: "penjuntrung",
intj: "gegeroan",
ptcl: "petilan",
det: "pemuguhan",
art: "penimpalan",
clf: "pemantar",
prep: "pengawalan",
postp: "pengahiran",
circp: "pendengkèkan",
ambip: "ambiposisi",
pref: "depanan",
suf: "kintilan",
inf: "selesepan",
circf: "apitan",
intf: "sambungan",
rt: "bongkot",
ph: "gabungan kata",
pb: "peribasa kata",
rom: "penglatènan",
ltr: "hurup",
sym: "lambang"
}
},
bjn: {
btn_add_language: "Tambahakan entri puga di halaman ngini",
modal_title_add: "Tambahakan entri puga — ",
fill_langcode: "(isi pakai kode bahasa)",
select_pos: "(pilih kalas kata)",
fill_meaning: "(isii maknanya)",
add_hom: "Tambahi entri homonim",
remove_hom: "Hapus entri homonim",
add_pos: "Tambahi kalas kata",
remove_pos: "Hapus kalas kata",
add_def: "Tambahi artinya",
remove_def: "Hapus artinya",
preview: "Pratayang",
undo: "Bulikakan",
save: "Simpan",
cancel: "Pasahakan",
preview_loading: "Tatilikan...",
save_loading: "Manyimpan...",
err_fill_all: "Isi dulu samunyaan baris nang ada, atawa hapus baris nang puang.",
err_lang_exists: "Subjudul gasan basa ngini sudah ada, pakai kode basa lain ataw babak langsung bagiannya.",
err_fetch: "Kada kawa maambil isi halaman.",
err_preview: "Kada kawa manapaiakan pratayang.",
err_save_prefix: "Kada kawa manyimpan:",
err_no_change: "Kadada paubahan gasan disimpan.",
prompt_fill_langcode: "Isi kode basanya dulu.",
save_summary_prefix: "[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+entri]]",
pos_labels: {
n: "kata banda",
v: "kata gawi",
adj: "kata sipat",
adv: "kata panjalas",
num: "kata bilangan",
pron: "kata ganti",
pn: "ngaran diri",
ptcp: "partisip",
conj: "kata panyambung",
intj: "kata saru",
ptcl: "kata tugas",
det: "panantu",
art: "kata sandang",
clf: "panggulung",
prep: "kata muka",
postp: "postposisi",
circp: "sirkumposisi",
ambip: "ambiposisi",
pref: "awalan",
suf: "akhiran",
inf: "sisipan",
circf: "apitan",
intf: "sambungan",
rt: "akar",
ph: "prasa",
pb: "paribasa",
rom: "romanisasi",
ltr: "hurup",
sym: "simbul"
}
},
min: {
btn_add_language: "Tambahkan entri baru untuak halaman iko",
modal_title_add: "Tambahkan entri baru — ",
fill_langcode: "(isi jo kode bahaso)",
select_pos: "(piliah kelas kato)",
fill_meaning: "(isi jo mukasuiknyo)",
add_hom: "Tambah entri homonim",
remove_hom: "Apuih entri homonim",
add_pos: "Tambah kelas kato",
remove_pos: "Apuih kelas kato",
add_def: "Tambah definisi",
remove_def: "Apuih definisi",
preview: "Pratinjau",
undo: "Baliakkan",
save: "Simpan",
cancel: "Uruangkan",
preview_loading: "Pratinjau...",
save_loading: "Manyimpan...",
err_fill_all: "Isi dulu sadoalah barih nan tasadio, atau apuih barih nan kosong.",
err_lang_exists: "Subjudul untuak bahaso iko alah tasadio, pakai kode bahaso lain atau suntiang lansuang bagiannyo.",
err_fetch: "Indak dapek maambiak isi laman.",
err_preview: "Indak dapek mamuek pratinjau.",
err_save_prefix: "Indak dapek menyimpan:",
err_no_change: "Indak ado parubahan untuak disimpan.",
prompt_fill_langcode: "Isi kode bahaso dulu.",
save_summary_prefix: "[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+entri]]",
pos_labels: {
n: "kato bando",
v: "kato karajo",
adj: "kato sipaik",
adv: "kato katarangan",
num: "kato bilangan",
pron: "kato ganti",
pn: "namo diri",
ptcp: "partisipel",
conj: "kato sambuang",
intj: "kato saru",
ptcl: "partikel",
det: "panantu",
art: "artikel",
clf: "panggoloang",
prep: "kato mulo",
postp: "kato balakang",
circp: "kato apik",
ambip: "ambiposisi",
pref: "awalan",
suf: "akiaran",
inf: "sisipan",
circf: "apitan",
intf: "sambuangan",
rt: "aka",
ph: "frasa",
pb: "papatah-patitiah",
rom: "latin",
ltr: "uruih",
sym: "lambang"
}
}
};
const USER_LANG = (mw.config.get("wgUserLanguage") || "id").toLowerCase();
const T = EG_I18N[USER_LANG] || EG_I18N.id;
const api = new mw.Api();
let languageMap = null;
function fetchLanguageMap() {
if (languageMap) return Promise.resolve(languageMap);
return api.get({
action: "query",
prop: "revisions",
rvprop: "content",
rvslots: "main",
titles: "Module:bahasa",
formatversion: 2
}).then(res => {
const text = res.query.pages[0].revisions[0].slots.main.content;
const out = {};
text.replace(/\[\s*"([^"]+)"\s*\]\s*=\s*"([^"]+)"/g, (m,k,v) => out[k.toLowerCase()] = v);
languageMap = out;
return languageMap;
});
}
const ns = mw.config.get("wgNamespaceNumber");
const action = mw.config.get("wgAction");
const pageName = mw.config.get("wgPageName");
const pageTitle = mw.config.get("wgTitle");
if (!(ns === 0 && (action === "view" || action === "edit"))) return;
if ($(".entry-gadget-root").length) return;
// --- Simple title button ---
const $btn = $('<button type="button" class="entry-new-btn" title="' + T.btn_add_language + '">+</button>')
.on("click", () => openModalForNew());
// Find title element (cross-skin)
let $title = $("#firstHeading, h1.firstHeading, .page-heading").first();
if ($title.length) {
$title.append($btn);
} else {
// fallback if somehow not found
$("#siteSub, #mw-content-text").first().prepend($btn);
}
// appState: holds undo preview stacks and current parsed sections for saving
const appState = {
htmlStack: [],
scrollStack: [],
currentSections: null,
newLang: null,
lastFetchedTimestamp: null
};
let suppressLangAutofocus = false;
// ---------------------
// Helpers
// ---------------------
function htmlEscape(s) {
return String(s || "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
}
function canonicalizeNewlines(text) {
return String(text || "").replace(/\r\n/g, "\n");
}
function normalizeSpacing(text) {
return canonicalizeNewlines(text)
.replace(/\n{4,}/g, "\n\n\n");
}
function normalizeForCompare(text) {
return normalizeSpacing(text).trim();
}
function makeDraggable($modal, $handle) {
let dragging = false;
let started = false;
let startX = 0;
let startY = 0;
let origLeft = 0;
let origTop = 0;
let pointerId = null;
const THRESHOLD = 6;
$handle.css({
"touch-action": "none",
"user-select": "none",
"-webkit-user-select": "none"
});
$handle.on("pointerdown.entryDrag", function (ev) {
if ($(ev.target).closest("button, input, select, textarea, a").length) return;
ev.preventDefault();
const rect = $modal[0].getBoundingClientRect();
dragging = true;
started = false;
startX = ev.clientX;
startY = ev.clientY;
origLeft = rect.left;
origTop = rect.top;
pointerId = ev.pointerId;
$("body").addClass("entry-dragging");
$handle[0].setPointerCapture?.(pointerId);
});
$(document).on("pointermove.entryDrag", function (ev) {
if (!dragging) return;
if (pointerId !== null && ev.pointerId !== pointerId) return;
const dx = ev.clientX - startX;
const dy = ev.clientY - startY;
if (!started) {
if (Math.hypot(dx, dy) < THRESHOLD) return;
started = true;
}
const winW = window.innerWidth;
const winH = window.innerHeight;
const modalW = $modal.outerWidth();
const modalH = $modal.outerHeight();
let newLeft = origLeft + dx;
let newTop = origTop + dy;
newLeft = Math.max(8, Math.min(newLeft, winW - modalW - 8));
newTop = Math.max(8, Math.min(newTop, winH - modalH - 8));
$modal.css({
left: newLeft + "px",
top: newTop + "px",
transform: "none"
});
});
$(document).on("pointerup.entryDrag pointercancel.entryDrag", function (ev) {
if (pointerId !== null && ev.pointerId !== pointerId) return;
dragging = false;
started = false;
pointerId = null;
$("body").removeClass("entry-dragging");
});
}
function removeModal() {
$(".entry-modal").remove();
$(".entry-modal-overlay").remove();
}
function cancelAndCloseModal() {
removeModal();
$(".entry-highlight").removeClass("entry-highlight");
if (appState.htmlStack.length) {
const prevHtml = appState.htmlStack[0];
const prevScroll = appState.scrollStack[0];
const $target = $("#mw-content-text");
if ($target.length) {
$target.html(prevHtml);
if (typeof prevScroll === "number") $(window).scrollTop(prevScroll);
// reinitialize collapsibles and gadgets
if (window.mw && mw.loader) {
mw.loader.using(["jquery.makeCollapsible"]).then(function () {
$(".mw-collapsible").makeCollapsible();
if (window.mw && mw.hook) {
mw.hook("wikipage.content").fire($("#mw-content-text"));
}
});
}
}
appState.htmlStack = [];
appState.scrollStack = [];
appState.currentSections = null;
}
}
function renumberHomonyms(model) {
if (!model || !Array.isArray(model.homonyms)) return;
model.homonyms.forEach((h, i) => { h.num = i + 1; });
}
// ---------------------
// Wikitext parsing helpers (preview/save)
// ---------------------
// Extract language sections
function extractLangSections(wikitext) {
const regex = /^==\s*\{\{\s*[^|}\s]+\s*\|\s*(?:1\s*=\s*)?([^|}\s]+).*?\}\}\s*==/gim;
const headings = [];
let match;
while ((match = regex.exec(wikitext)) !== null) {
headings.push({ code: match[1], index: match.index, matchText: match[0], lastMatchIndex: regex.lastIndex });
}
if (headings.length === 0) {
return [ { code: null, heading: null, contentWikitext: wikitext, start: 0, end: wikitext.length } ];
}
const sections = [];
for (let i = 0; i < headings.length; i++) {
const h = headings[i];
const start = h.index;
const contentStart = h.lastMatchIndex;
const contentEnd = (i + 1 < headings.length) ? headings[i + 1].index : wikitext.length;
sections.push({
code: h.code,
heading: h.matchText,
contentWikitext: wikitext.slice(contentStart, contentEnd),
start: start,
contentStart: contentStart,
end: contentEnd
});
}
return sections;
}
// ---------------------
// Get display name for language code (DOM-first, cached)
// ---------------------
function getDisplayNameForCode(code) {
if (!code) return "";
const key = String(code).toLowerCase();
if (languageMap && languageMap[key]) return languageMap[key];
// Fallback if module not yet loaded
return key;
}
// ---------------------
// Rebuild text from sections
// ---------------------
function rebuildFullTextFromSections(_originalText, sections) {
if (!Array.isArray(sections)) return "";
let out = "";
let seenLang = false;
for (const s of sections) {
const h = s.heading || "";
const c = s.contentWikitext || "";
if (h) {
if (seenLang && !out.endsWith("\n\n")) {
out = out.replace(/\n?$/, "\n\n");
}
seenLang = true;
}
out += h + c;
}
return out;
}
// ---------------------
// Helpers that build wikitext blocks
// ---------------------
function buildHomonymBlock(lang, hom) {
let out = "";
// include num= only if there are multiple homonyms or num>1
out += "{{kepala|" + lang + ((hom.num && hom.num > 1) ? "|num=" + hom.num : "") + "}}\n\n";
hom.posGroups.forEach(function (pg) {
out += "{{-" + (pg.pos || "") + "-|" + lang + "}}\n";
pg.defs.forEach(function (d) { if (d && d.trim()) out += "# " + d + "\n"; });
out += "";
});
return out;
}
// ---------------------
// Insert model into sections (DOM-based sort)
// ---------------------
function insertModelIntoSections(sections, model) {
sections = Array.isArray(sections) ? sections.slice() : [];
const incomingCode = (model.lang || "").toString().trim();
const incomingKey = incomingCode.toLowerCase();
// map existing codes to indices
const codeToIndex = {};
for (let i = 0; i < sections.length; i++) {
const s = sections[i];
if (s && s.code) codeToIndex[(s.code || "").toLowerCase()] = i;
}
// build entry text
const entryLines = model.homonyms
.map(h => buildHomonymBlock(model.lang, h))
.filter(Boolean)
.join("\n");
if (incomingKey && codeToIndex.hasOwnProperty(incomingKey)) {
const idx = codeToIndex[incomingKey];
const sec = sections[idx];
const existing = canonicalizeNewlines(sec.contentWikitext || "");
const toAppend = canonicalizeNewlines(entryLines);
// duplicate check
const existingNorm = normalizeForCompare(existing);
const appendNorm = normalizeForCompare(toAppend);
if (!existingNorm.endsWith(appendNorm)) {
let content = normalizeSpacing(existing);
if (content && !content.endsWith("\n")) {
content += "\n";
}
sec.contentWikitext = normalizeSpacing(
content + toAppend + "\n"
);
}
} else {
let newHeading = "=={{bahasa|" + model.lang + "}}==\n";
sections.push({
code: model.lang,
heading: canonicalizeNewlines(newHeading),
contentWikitext: normalizeSpacing(entryLines) + "\n",
start: null,
contentStart: null,
end: null
});
}
// separate non-lang and lang sections
const nonLang = sections.filter(s => !s.code);
const langSections = sections.filter(s => !!s.code);
// sort by display name after mul and site language
// auto-detect site language for sorting priority
const siteLang = (mw.config.get("wgContentLanguage") || "").toLowerCase();
const order = {
mul: 1,
[siteLang]: 2
};
langSections.forEach(s => {
s._codeNorm = (s.code || "").toLowerCase();
});
langSections.sort(function (a, b) {
const aCode = a._codeNorm || "";
const bCode = b._codeNorm || "";
const aRank = order[aCode] || 3;
const bRank = order[bCode] || 3;
if (aRank !== bRank) return aRank - bRank;
const aName = getDisplayNameForCode(a.code || aCode) || aCode;
const bName = getDisplayNameForCode(b.code || bCode) || bCode;
return aName.localeCompare(bName, undefined, { sensitivity: "base" });
});
langSections.forEach(s => { delete s._codeNorm; });
const rebuilt = nonLang.concat(langSections);
return rebuilt.map(s => ({
code: s.code || null,
heading: s.heading || null,
contentWikitext: s.contentWikitext || "",
start: (typeof s.start !== "undefined") ? s.start : null,
end: (typeof s.end !== "undefined") ? s.end : null
}));
}
function replacePageContentWith(parsedHtml) {
const $parsed = $("<div>").html(parsedHtml);
const $target = $("#mw-content-text");
if ($target.length) {
$target.data("savedScrollTop", $(window).scrollTop());
$target.html($parsed);
} else {
$("body").html($parsed);
}
}
function highlightInserted(lang) {
$(".entry-highlight").removeClass("entry-highlight");
const $langEl = $("#mw-content-text").find(".lang[data-code='" + lang + "']").first();
if ($langEl && $langEl.length) {
const $h2 = $langEl.closest("h2");
if ($h2.length) {
const $toHighlight = $h2.nextUntil("h2").addBack();
$toHighlight.addClass("entry-highlight");
const top = $h2.offset() ? $h2.offset().top : null;
if (top) $("html,body").animate({ scrollTop: top - 60 }, 250);
setTimeout(function () { $toHighlight.removeClass("entry-highlight"); }, 7000);
}
}
}
function fetchLatestWikitext() {
return api.get({
action: "query",
prop: "revisions",
rvprop: "content|timestamp",
rvslots: "main",
titles: pageName,
formatversion: 2
}).then(function (data) {
const page = data.query.pages[0];
const fullText = page.revisions && page.revisions[0] && page.revisions[0].slots && page.revisions[0].slots.main
? page.revisions[0].slots.main.content || ""
: "";
const timestamp = page.revisions && page.revisions[0] ? page.revisions[0].timestamp : null;
return { text: fullText, timestamp: timestamp };
});
}
// ---------------------
// Modal + form rendering
// ---------------------
function createEmptyModel() {
return {
lang: "",
homonyms: [ { num: 1, posGroups: [ { pos: "", defs: [""] } ] } ]
};
}
// POS canonical short codes
const POS_CODES = [
"n","v","adj","adv","num","pron","pn","ptcp",
"conj","intj","ptcl","det","art","clf","prep",
"postp","circp","ambip","pref","suf","inf",
"circf","intf","rt","ph","pb","rom",
"ltr","sym"
];
// Render modal from model
function renderModal(model) {
let justAddedFocus = false;
removeModal();
const $overlay = $('<div class="entry-modal-overlay" />');
const $modal = $('<div class="entry-modal" role="dialog" aria-modal="true"></div>');
const $header = $('<div class="entry-header"><div class="entry-title">' + T.modal_title_add + htmlEscape(pageTitle || "") + '</div><div><button class="entry-mini-btn entry-mini-rem" title="' + T.cancel + '">✕</button></div></div>');
const $body = $('<div class="entry-body"></div>');
const $controls = $('<div class="entry-controls"></div>');
$modal.append($header, $body, $controls);
$("body").append($overlay, $modal);
makeDraggable($modal, $header);
$header.find(".entry-mini-rem").on("click", function (e) { e.preventDefault(); cancelAndCloseModal(); });
// Top-level inputs
const $langRow = $(`<div class="entry-block"><div class="row" style="align-items:center;">
<div style="flex:1; display:flex; align-items:center; gap:0.5em;">
<input type="text" id="eg-lang" maxlength="11" style="max-width:3em;" value="${htmlEscape(model.lang || "")}">
<span style="opacity:0.8;">${T.fill_langcode}</span>
</div></div></div>`);
$body.append($langRow);
// Homonyms container
const $homContainer = $('<div id="eg-homonyms"></div>');
$body.append($homContainer);
// For each homonym, render a block
model.homonyms.forEach(function (hom, hIdx) {
const homNum = hom.num || (hIdx + 1);
const $hb = $('<div class="entry-block eg-homonym" data-hidx="' + hIdx + '"></div>');
// hom header: [+] [-] pagename (homonym #) -- pagename is plain bold text from pageTitle
const $hdr = $('<div class="row" style="align-items:center;"></div>');
const $addHomBtn = $('<button class="entry-mini-btn entry-mini-add" title="' + T.add_hom + '">+</button>');
const $rmHomBtn = $('<button class="entry-mini-btn entry-mini-rem" title="' + T.remove_hom + '">–</button>');
const $homTitle = $('<div style="flex:1"><b style="display:inline-block;">' + htmlEscape(pageTitle || "").replace(/_/g, " ") + '</b> <span style="opacity:0.8">(' + homNum + ')</span></div>');
$hdr.append($addHomBtn, $rmHomBtn, $homTitle);
// disable if only one homonym
if (model.homonyms.length <= 1) {
$rmHomBtn.prop("disabled", true);
} else {
$rmHomBtn.prop("disabled", false).css("opacity", "1.0");
}
$hb.append($hdr);
// POS groups container
const $posContainer = $('<div class="eg-pos-container"></div>');
hom.posGroups.forEach(function (pg, pIdx) {
const $pblock = $('<div class="entry-block" data-pidx="' + pIdx + '"></div>');
const $prow = $('<div class="row" style="align-items:center;"></div>');
const $addPosBtn = $('<button class="entry-mini-btn entry-mini-add" title="' + T.add_pos + '">+</button>');
const $rmPosBtn = $('<button class="entry-mini-btn entry-mini-rem" title="' + T.remove_pos + '">–</button>');
// Build POS dropdown
const posLabels = T.pos_labels || {};
const $posSelect = $('<select class="eg-pos"></select>');
$posSelect.append('<option value="">' + T.select_pos + '</option>');
POS_CODES.forEach(code => {
const label = posLabels[code] || code;
const $opt = $('<option></option>')
.val(code)
.text(label + " (" + code + ")");
if (pg.pos === code) $opt.prop("selected", true);
$posSelect.append($opt);
});
const $posInputWrap = $('<div style="flex:1; min-width:10em;"></div>').append($posSelect);
$prow.append($addPosBtn, $rmPosBtn, $posInputWrap);
$pblock.append($prow);
// disable if only one POS in homonym
if (hom.posGroups.length <= 1) {
$rmPosBtn.prop("disabled", true);
} else {
$rmPosBtn.prop("disabled", false).css("opacity", "1.0");
}
// Definitions
const $defsContain = $('<div class="eg-defs"></div>');
pg.defs.forEach(function (d, dIdx) {
const $drow = $('<div class="row" style="align-items:center;"></div>');
const $addDefBtn = $('<button class="entry-mini-btn entry-mini-add" title="' + T.add_def + '">+</button>');
const $rmDefBtn = $('<button class="entry-mini-btn entry-mini-rem" title="' + T.remove_def + '">–</button>');
const $defInput = $('<div style="flex:1"><input type="text" class="eg-def" placeholder="' + T.fill_meaning + '" value="' + htmlEscape(d || "") + '"></div>');
$drow.append($addDefBtn, $rmDefBtn, $defInput);
$defsContain.append($drow);
// disable if only one definition
if (pg.defs.length <= 1) {
$rmDefBtn.prop("disabled", true);
} else {
$rmDefBtn.prop("disabled", false).css("opacity", "1.0");
}
// Def-level events
$addDefBtn.on("click", function (ev) {
ev.preventDefault();
const read = $modal.data("readForm");
if (read) read();
const mdl = $modal.data("model");
mdl.homonyms[hIdx].posGroups[pIdx].defs.splice(dIdx + 1, 0, "");
suppressLangAutofocus = true;
renderModal(mdl);
// Focus new definition input
setTimeout(() => {
const $newDef = $(`.eg-homonym[data-hidx='${hIdx}'] .entry-block[data-pidx='${pIdx}'] .eg-def:eq(${dIdx + 1})`);
if ($newDef.length) justAddedFocus = true;
$newDef.focus();
setTimeout(() => { justAddedFocus = false; }, 200);
}, 100);
});
$rmDefBtn.on("click", function (ev) {
ev.preventDefault();
const read = $modal.data("readForm");
if (read) read();
const mdl = $modal.data("model");
const defsArr = mdl.homonyms[hIdx].posGroups[pIdx].defs;
suppressLangAutofocus = true;
if (defsArr.length > 1) defsArr.splice(dIdx, 1);
else defsArr[0] = "";
renderModal(mdl);
setTimeout(() => {
const $prev = $(`.eg-homonym[data-hidx='${hIdx}'] .entry-block[data-pidx='${pIdx}'] .eg-def:eq(${Math.max(dIdx - 1, 0)})`);
if ($prev.length) $prev.focus();
}, 100);
});
});
// POS-level events
$addPosBtn.on("click", function (ev) {
ev.preventDefault();
const read = $modal.data("readForm");
if (read) read();
const mdl = $modal.data("model");
mdl.homonyms[hIdx].posGroups.splice(pIdx + 1, 0, { pos: "", defs: [""] });
suppressLangAutofocus = true;
renderModal(mdl);
// Focus the new POS dropdown
setTimeout(() => {
const $newPos = $(`.eg-homonym[data-hidx='${hIdx}'] .entry-block[data-pidx='${pIdx + 1}'] .eg-pos`);
if ($newPos.length) justAddedFocus = true;
$newPos.focus();
setTimeout(() => { justAddedFocus = false; }, 200);
}, 100);
});
$rmPosBtn.on("click", function (ev) {
ev.preventDefault();
const read = $modal.data("readForm");
if (read) read();
const mdl = $modal.data("model");
suppressLangAutofocus = true;
if (mdl.homonyms[hIdx].posGroups.length > 1) mdl.homonyms[hIdx].posGroups.splice(pIdx, 1);
else mdl.homonyms[hIdx].posGroups[0] = { pos: "", defs: [""] };
renderModal(mdl);
setTimeout(() => {
const $prev = $(`.eg-homonym[data-hidx='${hIdx}'] .entry-block[data-pidx='${Math.max(pIdx - 1, 0)}'] .eg-pos`).first();
if ($prev.length) $prev.focus();
}, 100);
});
$pblock.append($defsContain);
$posContainer.append($pblock);
});
$hb.append($posContainer);
// Hom-level events
$addHomBtn.on("click", function (ev) {
ev.preventDefault();
const read = $modal.data("readForm");
if (read) read();
const mdl = $modal.data("model");
const insertionIndex = hIdx + 1;
mdl.homonyms.splice(insertionIndex, 0, { num: 0, posGroups: [{ pos: "", defs: [""] }] });
renumberHomonyms(mdl);
suppressLangAutofocus = true;
renderModal(mdl);
// Focus first POS dropdown of new homonym
setTimeout(() => {
const $newPos = $(`.eg-homonym[data-hidx='${insertionIndex}'] .eg-pos`).first();
if ($newPos.length) justAddedFocus = true;
$newPos.focus();
setTimeout(() => { justAddedFocus = false; }, 200);
}, 100);
});
$rmHomBtn.on("click", function (ev) {
ev.preventDefault();
const read = $modal.data("readForm");
if (read) read();
const mdl = $modal.data("model");
suppressLangAutofocus = true;
if (mdl.homonyms.length > 1) {
mdl.homonyms.splice(hIdx, 1);
} else {
mdl.homonyms[0] = { num: 1, posGroups: [ { pos: "", defs: [""] } ] };
}
renumberHomonyms(mdl);
renderModal(mdl);
setTimeout(() => {
const $prev = $(`.eg-homonym[data-hidx='${Math.max(hIdx - 1, 0)}'] .eg-pos`).first();
if ($prev.length) $prev.focus();
}, 100);
});
$homContainer.append($hb);
}); // end homonyms.forEach
// Controls: Preview, Undo, Save, Cancel
const $previewBtn = $('<button class="entry-preview-btn" type="button">' + T.preview + '</button>');
const $undoBtn = $('<button class="entry-undo-btn" type="button" disabled>' + T.undo + '</button>');
const $saveBtn = $('<button class="entry-save-btn" type="button" disabled>' + T.save + '</button>');
const $cancelBtn = $('<button class="entry-cancel-btn" type="button">' + T.cancel + '</button>');
$controls.append($previewBtn, $undoBtn, $saveBtn, $cancelBtn);
$cancelBtn.on("click", function (e) { e.preventDefault(); cancelAndCloseModal(); });
// Read form into model helper
function readFormToModel() {
const mdl = { lang: "", homonyms: [] };
mdl.lang = $modal.find("#eg-lang").val().trim();
$modal.find("#eg-homonyms > .eg-homonym").each(function (hIndex) {
const $hb = $(this);
const homModel = { num: hIndex + 1, posGroups: [] };
$hb.find(".eg-pos-container > .entry-block").each(function (pIndex) {
const $pblock = $(this);
const pos = $pblock.find(".eg-pos").val().trim();
const defs = [];
$pblock.find(".eg-def").each(function () { defs.push($(this).val().trim()); });
homModel.posGroups.push({ pos: pos, defs: defs.length ? defs : [""] });
});
if (homModel.posGroups.length === 0) homModel.posGroups = [ { pos: "", defs: [""] } ];
mdl.homonyms.push(homModel);
});
renumberHomonyms(mdl);
// update modal-stored model so event handlers can use current state
$modal.data("model", mdl);
return mdl;
}
$modal.data("readForm", readFormToModel);
$modal.data("model", model);
// Preview handler
$previewBtn.on("click", function () {
const read = $modal.data("readForm");
if (read) read();
const mdl = $modal.data("model");
if (!mdl.lang) { alert(T.prompt_fill_langcode); return; }
// check at least one pos+definition non-empty
// validation: all visible inputs must be filled
let allFilled = true;
mdl.homonyms.forEach(h => {
h.posGroups.forEach(pg => {
const posFilled = pg.pos && pg.pos.trim().length > 0;
const defsFilled = pg.defs.length > 0 && pg.defs.every(d => d && d.trim().length > 0);
if (!posFilled || !defsFilled) allFilled = false;
});
});
if (!allFilled) {
alert(T.err_fill_all);
return;
}
$previewBtn.prop("disabled", true).text(T.preview_loading);
fetchLatestWikitext().then(function (res) {
const sections = extractLangSections(res.text);
// if language already exists, warn user (since edit-data flow removed)
const existingIdx = sections.findIndex(s => s.code && s.code.toLowerCase() === mdl.lang.toLowerCase());
if (existingIdx >= 0) {
alert(T.err_lang_exists);
$previewBtn.prop("disabled", false).text(T.preview);
return;
}
// push snapshot for undo
appState.htmlStack.push($("#mw-content-text").html() || "");
appState.scrollStack.push($(window).scrollTop());
appState.lastFetchedTimestamp = res.timestamp;
fetchLanguageMap().then(() => {
const updatedSections = insertModelIntoSections(sections, mdl);
appState.currentSections = updatedSections;
appState.newLang = mdl.lang;
const newFull = rebuildFullTextFromSections(res.text, updatedSections);
api.post({
action: "parse",
title: pageName,
text: newFull,
contentmodel: "wikitext",
prop: "text",
format: "json"
}).done(function (resp) {
const html = (resp && resp.parse && resp.parse.text)
? resp.parse.text["*"] || ""
: "";
replacePageContentWith(html);
highlightInserted(mdl.lang);
$undoBtn.prop("disabled", false).css("opacity", "1.0");
$saveBtn.prop("disabled", false).css("opacity", "1.0");
$previewBtn.prop("disabled", false).text(T.preview);
}).fail(function () {
alert(T.err_preview);
$previewBtn.prop("disabled", false).text(T.preview);
});
});
}).catch(function () {
alert(T.err_fetch);
$previewBtn.prop("disabled", false).text(T.preview);
});
});
// Undo handler (Balikin)
$undoBtn.on("click", function () {
if (!appState.htmlStack.length) {
$undoBtn.prop("disabled", true);
$saveBtn.prop("disabled", true);
return;
}
const prevHtml = appState.htmlStack.pop();
const prevScroll = appState.scrollStack.pop();
const $target = $("#mw-content-text");
if ($target.length) {
$target.html(prevHtml);
if (typeof prevScroll === "number") $(window).scrollTop(prevScroll);
}
if (appState.htmlStack.length === 0) {
$undoBtn.prop("disabled", true);
$saveBtn.prop("disabled", true);
} else {
$undoBtn.prop("disabled", false).css("opacity", "1.0");
$saveBtn.prop("disabled", false).css("opacity", "1.0");
}
});
// Save handler
$saveBtn.on("click", function () {
const read = $modal.data("readForm");
if (read) read();
if (!appState.currentSections) {
alert(T.err_no_change);
return;
}
$saveBtn.prop("disabled", true).text(T.save_loading);
fetchLatestWikitext().then(function (res) {
const latestText = res.text;
const latestTimestamp = res.timestamp;
// Rebuild final full text from appState.currentSections but apply to latest text
const newText = normalizeSpacing(
rebuildFullTextFromSections(latestText, appState.currentSections)
);
const lang = appState.newLang || "";
const summary = lang ? `${T.save_summary_prefix} [${lang}]` : T.save_summary_prefix;
api.postWithToken("csrf", {
action: "edit",
title: pageName,
text: newText,
summary: summary,
minor: true,
basetimestamp: latestTimestamp
}).done(function () {
location.reload();
}).fail(function (err) {
const info = err && err.error && err.error.info ? err.error.info : String(err);
alert(T.err_save_prefix + " " + info);
$saveBtn.prop("disabled", false).text(T.save);
});
}).catch(function () {
alert(T.err_fetch);
$saveBtn.prop("disabled", false).text(T.save);
});
});
// Focus language input only once when modal first opens
if (!appState.modalEverOpened) {
setTimeout(() => $modal.find("#eg-lang").focus(), 25);
appState.modalEverOpened = true;
}
// ---------------------
// Keyboard accessibility & shortcuts
// ---------------------
(function setupKeyboard() {
const focusables = () =>
$modal.find('input[type="text"], select').filter(':visible');
function focusNext(current, dir) {
const f = focusables().toArray();
const idx = f.indexOf(current);
if (idx === -1) return;
const next = f[idx + dir];
if (next) next.focus();
}
$modal.on("keydown", function (e) {
const target = e.target;
// Ctrl+Enter => Save
if (e.ctrlKey && e.key === "Enter") {
e.preventDefault();
$modal.find(".entry-save-btn").trigger("click");
return;
}
// Enter in definition input => trigger Preview
if (
e.key === "Enter" &&
$(target).hasClass("eg-def") &&
!e.ctrlKey &&
!e.shiftKey
) {
e.preventDefault();
$modal.find(".entry-preview-btn").trigger("click");
return;
}
// Shift+ArrowDown in definition input => add new definition below
if (e.key === "ArrowDown" && e.shiftKey && $(target).hasClass("eg-def")) {
e.preventDefault();
const $row = $(target).closest(".row");
const hidx = $row.closest(".eg-homonym").data("hidx");
const pidx = $row.closest(".entry-block").data("pidx");
const didx = $row.closest('.eg-defs').find('.row').index($row);
const read = $modal.data("readForm");
if (read) read();
const mdl = $modal.data("model");
suppressLangAutofocus = true;
mdl.homonyms[hidx].posGroups[pidx].defs.splice(didx + 1, 0, "");
renderModal(mdl);
setTimeout(() => {
const newDef = $(
`.eg-homonym[data-hidx='${hidx}'] .entry-block[data-pidx='${pidx}'] .eg-def:eq(${didx + 1})`
).get(0);
if (newDef) {
justAddedFocus = true;
newDef.focus();
setTimeout(() => { justAddedFocus = false; }, 250);
}
}, 100);
return;
}
// Shift+ArrowUp on blank definition line => remove it
if (e.key === "ArrowUp" && e.shiftKey && $(target).hasClass("eg-def")) {
const value = $(target).val().trim();
if (value === "") {
e.preventDefault();
const $row = $(target).closest(".row");
const hidx = $row.closest(".eg-homonym").data("hidx");
const pidx = $row.closest(".entry-block").data("pidx");
const didx = $row.closest('.eg-defs').find('.row').index($row);
const read = $modal.data("readForm");
if (read) read();
const mdl = $modal.data("model");
const defs = mdl.homonyms[hidx].posGroups[pidx].defs;
suppressLangAutofocus = true;
if (defs.length > 1) defs.splice(didx, 1);
else defs[0] = "";
renderModal(mdl);
setTimeout(() => {
const prev = $(
`.eg-homonym[data-hidx='${hidx}'] .entry-block[data-pidx='${pidx}'] .eg-def:eq(${Math.max(didx - 1, 0)})`
).get(0);
if (prev) {
justAddedFocus = true;
prev.focus();
setTimeout(() => { justAddedFocus = false; }, 250);
}
}, 100);
return;
}
}
// Plain ArrowDown/Up navigation between inputs
if ((e.key === "ArrowDown" || e.key === "ArrowUp") && !e.shiftKey && !justAddedFocus) {
e.preventDefault();
focusNext(target, e.key === "ArrowDown" ? +1 : -1);
return;
}
});
})();
return $modal;
}
// Modal openers
function openModalForNew() {
const model = createEmptyModel();
renderModal(model);
}
// Expose debug hooks if needed
window.EntryAdderGadget = {
openNew: openModalForNew,
removeModal: removeModal
};
});
kmq5f7zeuq21dmnzxenu6twd5r0tl0j
lalaran
0
269386
1463316
1105996
2026-07-11T07:46:05Z
Wadaihangit
33201
[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+entri]] [bjn]
1463316
wikitext
text/x-wiki
== [[bahasa Jawa|Bahasa Jawa]] ==
Lalaran di Pondok Pesantren adalah metode belajar dengan cara membaca atau melantunkan bait atai nadhom secara terus menerus hingga hafal.https://siskesakti.com/tradisi-lalaran-nadhom-dalam-pendidikan-pesantren/=={{bahasa|bjn}}==
{{kepala|bjn}}
{{-n-|bjn}}
# tali [[jemuran]]
9d1gpnsrz9xqvp5tfbs5gsis0cowelx
1463317
1463316
2026-07-11T07:46:26Z
Wadaihangit
33201
1463317
wikitext
text/x-wiki
=={{bahasa|bjn}}==
{{kepala|bjn}}
{{-n-|bjn}}
# tali [[jemuran]]
3sfeykhaxu5beyd2xa3spestkndv8mc
Kategori:kvb:Lema
14
270955
1463284
1172838
2026-07-10T17:52:39Z
Muhamad Izzul Fiqih
35471
added [[Category:Bahasa Orang Rimba]] using [[Help:Gadget-HotCat|HotCat]]
1463284
wikitext
text/x-wiki
[[Kategori:Bahasa Orang Rimba]]
nv50skcu1t6sehrqp0wjhdfsuyrjino
1463285
1463284
2026-07-10T17:52:47Z
Muhamad Izzul Fiqih
35471
removed [[Category:Bahasa Orang Rimba]] using [[Help:Gadget-HotCat|HotCat]]
1463285
wikitext
text/x-wiki
phoiac9h4m842xq45sp7s6u21eteeq1
1463286
1463285
2026-07-10T17:53:04Z
Muhamad Izzul Fiqih
35471
added [[Category:Bahasa Kubu]] using [[Help:Gadget-HotCat|HotCat]]
1463286
wikitext
text/x-wiki
[[Kategori:Bahasa Kubu]]
pjhqw5l4bqc5vi20fuepfx6itamesks
1463289
1463286
2026-07-10T17:54:55Z
Muhamad Izzul Fiqih
35471
removed [[Category:Bahasa Kubu]] using [[Help:Gadget-HotCat|HotCat]]
1463289
wikitext
text/x-wiki
phoiac9h4m842xq45sp7s6u21eteeq1
1463290
1463289
2026-07-10T17:55:06Z
Muhamad Izzul Fiqih
35471
added [[Category:Bahasa Orang Rimba]] using [[Help:Gadget-HotCat|HotCat]]
1463290
wikitext
text/x-wiki
[[Kategori:Bahasa Orang Rimba]]
nv50skcu1t6sehrqp0wjhdfsuyrjino
anai-anai
0
272285
1463325
1284380
2026-07-11T10:43:36Z
Raflinoer32
30361
[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+entri]] [id]
1463325
wikitext
text/x-wiki
=={{bahasa|id}}==
{{kepala|id}}
{{-n-|id}}
# rayap
=={{bahasa|bjn}}==
{{kepala|bjn}}
{{-lafal-|bjn}}
* {{suara|bjn|LL-Q33151 (bjn)-Radramboo-anai-anai.wav}}
{{-n-|bjn}}
# anai-anai; rayap
#: {{ux|bjn|Buku-buku nang ku simpani dimakani anai-anai|Buku-buku yang ku simpan dimakan anai-anai.}}
[[Kategori:WikiTutur 3.0 - Banjar]]
[[Kategori:WikiTutur 3.0 Banjarmasin 15 Februari 2026]]
9irnshy2fba5kl0wp5zcq1zfzqclhs4
tagah
0
350436
1463314
1454390
2026-07-11T07:43:48Z
Wadaihangit
33201
[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+entri]] [bjn]
1463314
wikitext
text/x-wiki
=={{bahasa|bjn}}==
{{kepala|bjn}}
{{-adj-|bjn}}
# [[rimbun]]
=={{bahasa|knx}}==
{{kepala|knx}}
{{-adv-|knx}}
# [[sedang]]
[[Kategori:WikiTutur 3.0 - Kanayatn]]
[[Kategori:WikiTutur 3.0 - Lokakarya Daring 2026-05-03]]
3n51cg3jayndify8xe0pf0u96kormqu
aherat
0
350863
1463293
1455632
2026-07-10T18:00:09Z
Muhamad Izzul Fiqih
35471
[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+entri]] [jax]
1463293
wikitext
text/x-wiki
=={{bahasa|jax}}==
{{kepala|jax}}
{{-n-|jax}}
# [[akhirat]]
=={{bahasa|sas}}==
{{kepala|sas}}
: {{suara|sas|LL-Q1294047 (sas)-Kamardi (Nining123)-Aherat.wav}}
{{-adj-|aherat}}
# [[akhirat]]
2a51aqzdj43ap5qivm1f2cebn9gytcw
1463294
1463293
2026-07-10T18:00:32Z
Muhamad Izzul Fiqih
35471
/* Bahasa Sasak */
1463294
wikitext
text/x-wiki
=={{bahasa|jax}}==
{{kepala|jax}}
{{-n-|jax}}
# [[akhirat]]
=={{bahasa|sas}}==
{{kepala|sas}}
: {{suara|sas|LL-Q1294047 (sas)-Kamardi (Nining123)-Aherat.wav}}
{{-n-|sas}}
# [[akhirat]]
234w5arkvq39rexptrv39vqly3i0z63
rajo batu
0
352894
1463280
2026-07-10T17:48:35Z
Muhamad Izzul Fiqih
35471
[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+entri]] [jax]
1463280
wikitext
text/x-wiki
=={{bahasa|jax}}==
{{kepala|jax}}
{{-n-|jax}}
# [[pemenang]] berturut-turut
7wmvp42u5p5wfn60lf0guwinq309pxa
1463281
1463280
2026-07-10T17:50:50Z
Muhamad Izzul Fiqih
35471
/* Bahasa Melayu Jambi */
1463281
wikitext
text/x-wiki
=={{bahasa|jax}}==
{{kepala|jax}}
{{-n-|jax}}
# [[pemenang]] berturut-turut
#: {{contoh|jax|dio tu la jadi rajo batu bekali-kali.|dia sudah jadi pemenang beruntung berkali-kali.}}
1hv300xrteh5t3pzaqza2vz93zkufm2
Kategori:Frasa mengandung kata rajo
14
352895
1463282
2026-07-10T17:51:04Z
Muhamad Izzul Fiqih
35471
←Membuat halaman berisi '{{kategori otomatis}}'
1463282
wikitext
text/x-wiki
{{kategori otomatis}}
tka0fgn87px6unlh07hbwnzmlebon2d
Kategori:Bahasa Kubu
14
352896
1463288
2026-07-10T17:54:13Z
Muhamad Izzul Fiqih
35471
Muhamad Izzul Fiqih memindahkan halaman [[Kategori:Bahasa Kubu]] ke [[Kategori:Bahasa Orang Rimba]]
1463288
wikitext
text/x-wiki
#ALIH [[:Kategori:Bahasa Orang Rimba]]
b1ln26csw2gsa24vmlgvjhi4c197dt2
halin
0
352897
1463302
2026-07-11T07:24:51Z
Wadaihangit
33201
[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+entri]] [bjn]
1463302
wikitext
text/x-wiki
=={{bahasa|bjn}}==
{{kepala|bjn}}
{{-adj-|bjn}}
# [[jarang]] ada, muncul, dsb
jx24cdb50otwat153sm1t1nvjqgb7ka
garagataman
0
352898
1463303
2026-07-11T07:26:18Z
Wadaihangit
33201
[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+entri]] [bjn]
1463303
wikitext
text/x-wiki
=={{bahasa|bjn}}==
{{kepala|bjn}}
{{-adj-|bjn}}
# tidak terkontrol
rblgo9k5j1zl6i7845qhl6be24mloby
rahup
0
352899
1463304
2026-07-11T07:27:23Z
Wadaihangit
33201
[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+entri]] [bjn]
1463304
wikitext
text/x-wiki
=={{bahasa|bjn}}==
{{kepala|bjn}}
{{-adj-|bjn}}
# muka yang [[cemberut]] atau bertekuk
s7vrm13ru743ree7ie09hpqqfjah26t
tajungpipir
0
352900
1463305
2026-07-11T07:29:13Z
Wadaihangit
33201
[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+entri]] [bjn]
1463305
wikitext
text/x-wiki
=={{bahasa|bjn}}==
{{kepala|bjn}}
{{-adj-|bjn}}
# keadaan benda yang ingin roboh atau jatuh karena dalam keadaan cepat, laju atau melaju kencang
5lpagpprvx2gsuju876i6itw67xdk7e
udit
0
352901
1463306
2026-07-11T07:29:48Z
Wadaihangit
33201
[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+entri]] [bjn]
1463306
wikitext
text/x-wiki
=={{bahasa|bjn}}==
{{kepala|bjn}}
{{-adj-|bjn}}
# [[kalah]], pihak yang [[kalah]]
dk6sddu0bj6ymka0owwi4m407e1oyi1
tasimbaya
0
352902
1463307
2026-07-11T07:30:55Z
Wadaihangit
33201
[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+entri]] [bjn]
1463307
wikitext
text/x-wiki
=={{bahasa|bjn}}==
{{kepala|bjn}}
{{-adj-|bjn}}
# dalam keadaan setengah [[kepuhunan]]
fi7qql5katnrjjtzx3i9ia0d86utin9
nuhu
0
352903
1463310
2026-07-11T07:36:33Z
Wadaihangit
33201
[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+entri]] [bjn]
1463310
wikitext
text/x-wiki
=={{bahasa|bjn}}==
{{kepala|bjn}}
{{-adj-|bjn}}
# [[istiqomah]]
# [[konsisten]]
5eop8yr2pn6por08xgdgtaioukxewes
jutu
0
352904
1463318
2026-07-11T07:48:48Z
Wadaihangit
33201
[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+entri]] [bjn]
1463318
wikitext
text/x-wiki
=={{bahasa|bjn}}==
{{kepala|bjn}}
{{-adv-|bjn}}
# basah [[kuyup]]
tq01egdk9qpbkqx3lezn9loiit36hw3
lunglup
0
352905
1463321
2026-07-11T07:50:13Z
Wadaihangit
33201
[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+entri]] [bjn]
1463321
wikitext
text/x-wiki
=={{bahasa|bjn}}==
{{kepala|bjn}}
{{-v-|bjn}}
# [[tenggelam]]
rnstl0pxg7lsx1oclk9ux8i28m9ob3w
lihat tiring
0
352906
1463324
2026-07-11T07:59:58Z
Wadaihangit
33201
[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+entri]] [bjn]
1463324
wikitext
text/x-wiki
=={{bahasa|bjn}}==
{{kepala|bjn}}
{{-adj-|bjn}}
# [[acuh]] karena kecewa, tidak memperdulikan lagi, [[mengabaikan]], [[membiarkan]]
nv97qte1h9yeeld9jhu2lqh5wzo0ouh
ablu
0
352907
1463330
2026-07-11T10:52:23Z
Raflinoer32
30361
[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+entri]] [su]
1463330
wikitext
text/x-wiki
=={{bahasa|su}}==
{{kepala|su}}
{{-v-|su}}
# main ke mana-mana
qgm5h6wfiqc9ihh1mj4yt7lk2hs849k
abrag-abragan
0
352908
1463332
2026-07-11T10:54:23Z
Raflinoer32
30361
[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+entri]] [su]
1463332
wikitext
text/x-wiki
=={{bahasa|su}}==
{{kepala|su}}
{{-v-|su}}
# berlarian
mzy4fqng36buog986rf4drxgkwhpzuk
abrét
0
352909
1463333
2026-07-11T10:55:30Z
Raflinoer32
30361
[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+entri]] [su]
1463333
wikitext
text/x-wiki
=={{bahasa|su}}==
{{kepala|su}}
{{-v-|su}}
# berlari kencang (kuda)
1ky0rigkroxew9k95gmbm1xkszmiwvn
aclog
0
352910
1463334
2026-07-11T10:57:39Z
Raflinoer32
30361
[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+entri]] [su]
1463334
wikitext
text/x-wiki
=={{bahasa|su}}==
{{kepala|su}}
{{-v-|su}}
# loncat
mlxwtux8ct2g6e9j9bew3ut8xqx5a6q
Pengguna:Sibiru45/Gadget-EntryAdder.js
2
352911
1463336
2026-07-11T11:26:48Z
Sibiru45
40479
←Membuat halaman berisi 'mw.loader.using(["mediawiki.api", "mediawiki.util"]).then(function () { "use strict"; mw.loader.load( "https://id.wiktionary.org/w/index.php?title=Pengguna:Swarabakti/Gadget-EntryAdder.css&action=raw&ctype=text/css", "text/css" ); // --------------------- // Add more languages by adding keys here. // --------------------- const EG_I18N = { id: { btn_add_language: "Tambahkan entri baru pada halaman ini", modal_title_add: "Ta...'
1463336
javascript
text/javascript
mw.loader.using(["mediawiki.api", "mediawiki.util"]).then(function () {
"use strict";
mw.loader.load(
"https://id.wiktionary.org/w/index.php?title=Pengguna:Swarabakti/Gadget-EntryAdder.css&action=raw&ctype=text/css",
"text/css"
);
// ---------------------
// Add more languages by adding keys here.
// ---------------------
const EG_I18N = {
id: {
btn_add_language: "Tambahkan entri baru pada halaman ini",
modal_title_add: "Tambahkan entri baru — ",
fill_langcode: "(isi dengan kode bahasa)",
select_pos: "(pilih kelas kata)",
fill_meaning: "(isi dengan maknanya)",
add_hom: "Tambah entri homonim",
remove_hom: "Hapus entri homonim",
add_pos: "Tambah kelas kata",
remove_pos: "Hapus kelas kata",
add_def: "Tambah definisi",
remove_def: "Hapus definisi",
preview: "Pratayang",
undo: "Balikkan",
save: "Simpan",
cancel: "Batalkan",
preview_loading: "Pratayang...",
save_loading: "Menyimpan...",
err_fill_all: "Isi dulu semua baris yang tersedia, atau hapus baris yang kosong.",
err_lang_exists: "Subjudul untuk bahasa ini sudah tersedia, gunakan kode bahasa lain atau sunting langsung bagiannya.",
err_fetch: "Gagal mengambil isi halaman.",
err_preview: "Gagal memuat pratayang.",
err_save_prefix: "Gagal menyimpan:",
err_no_change: "Tiada perubahan untuk disimpan.",
prompt_fill_langcode: "Isi kode bahasa dulu.",
save_summary_prefix: "[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+entri]]",
pos_labels: {
n: "nomina",
v: "verba",
adj: "adjektiva",
adv: "adverbia",
num: "numeralia",
pron: "pronomina",
pn: "nama diri",
ptcp: "partisipel",
conj: "konjungsi",
intj: "interjeksi",
ptcl: "partikula",
det: "penentu",
art: "artikula",
clf: "penggolong",
prep: "preposisi",
postp: "postposisi",
circp: "sirkumposisi",
ambip: "ambiposisi",
pref: "awalan",
suf: "akhiran",
inf: "sisipan",
circf: "apitan",
intf: "sambungan",
rt: "akar",
ph: "frasa",
pb: "peribahasa",
rom: "romanisasi",
ltr: "huruf",
sym: "simbol"
}
},
bew: {
btn_add_language: "Tambahin pentol kata anyar bakal ni halaman",
modal_title_add: "Tambahin pentol anyar — ",
fill_langcode: "(isi ama kode basanya)",
select_pos: "(pili rungkun katanya)",
fill_meaning: "(isi ama pema'naannya)",
add_hom: "Tambah pentol kata sejogrokan",
remove_hom: "Apus pentol kata sejogrokan",
add_pos: "Tambah rungkun katanya",
remove_pos: "Apus rungkun katanya",
add_def: "Tambah pema'naannya",
remove_def: "Apus pema'naannya",
preview: "Terawang",
undo: "Balikin",
save: "Simpen",
cancel: "Urungin",
preview_loading: "Nerawangin...",
save_loading: "Nyimpenin...",
err_fill_all: "Isiin dulu semua baris rungkun ama ma'na nyang derak, atawa apus-apusin nyang kopong.",
err_lang_exists: "Anakjudul bakal entu basa udah derak — jal paké laèn kode basa atawa permak langsung entu bagèan.",
err_fetch: "Kaga' bisa nyomot isi halaman.",
err_preview: "Kaga' bisa munggah terawangan.",
err_save_prefix: "Kaga' bisa nyimpen:",
err_no_change: "Kaga' ada perobahan bakal disimpen.",
prompt_fill_langcode: "Isi kode basanya dulu.",
save_summary_prefix: "[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+pentol]]",
pos_labels: {
n: "kata peada",
v: "kata kereja",
adj: "kata penyipat",
adv: "kata penerang",
num: "kata pengangka",
pron: "kata pengganti",
pn: "kata nama",
ptcp: "dumanan",
conj: "penjuntrung",
intj: "gegeroan",
ptcl: "petilan",
det: "pemuguhan",
art: "penimpalan",
clf: "pemantar",
prep: "pengawalan",
postp: "pengahiran",
circp: "pendengkèkan",
ambip: "ambiposisi",
pref: "depanan",
suf: "kintilan",
inf: "selesepan",
circf: "apitan",
intf: "sambungan",
rt: "bongkot",
ph: "gabungan kata",
pb: "peribasa kata",
rom: "penglatènan",
ltr: "hurup",
sym: "lambang"
}
},
bjn: {
btn_add_language: "Tambahakan entri puga di halaman ngini",
modal_title_add: "Tambahakan entri puga — ",
fill_langcode: "(isi pakai kode bahasa)",
select_pos: "(pilih kalas kata)",
fill_meaning: "(isii maknanya)",
add_hom: "Tambahi entri homonim",
remove_hom: "Hapus entri homonim",
add_pos: "Tambahi kalas kata",
remove_pos: "Hapus kalas kata",
add_def: "Tambahi artinya",
remove_def: "Hapus artinya",
preview: "Pratayang",
undo: "Bulikakan",
save: "Simpan",
cancel: "Pasahakan",
preview_loading: "Pratayang...",
save_loading: "Manyimpan...",
err_fill_all: "Isi dulu samunyaan baris nang ada, atawa hapus baris nang puang.",
err_lang_exists: "Subjudul gasan basa ngini sudah ada, pakai kode basa lain ataw babak langsung bagiannya.",
err_fetch: "Kada kawa maambil isi halaman.",
err_preview: "Kada kawa manapaiakan pratayang.",
err_save_prefix: "Kada kawa manyimpan:",
err_no_change: "Kadada paubahan gasan disimpan.",
prompt_fill_langcode: "Isi kode basanya dulu.",
save_summary_prefix: "[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+entri]]",
pos_labels: {
n: "kata banda",
v: "kata gawi",
adj: "kata sipat",
adv: "kata panjalas",
num: "kata bilangan",
pron: "kata ganti",
pn: "ngaran diri",
ptcp: "partisip",
conj: "kata panyambung",
intj: "kata saru",
ptcl: "kata tugas",
det: "panantu",
art: "kata sandang",
clf: "panggulung",
prep: "kata muka",
postp: "postposisi",
circp: "sirkumposisi",
ambip: "ambiposisi",
pref: "awalan",
suf: "akhiran",
inf: "sisipan",
circf: "apitan",
intf: "sambungan",
rt: "akar",
ph: "prasa",
pb: "paribasa",
rom: "romanisasi",
ltr: "hurup",
sym: "simbul"
}
},
min: {
btn_add_language: "Tambahkan entri baru untuak halaman iko",
modal_title_add: "Tambahkan entri baru — ",
fill_langcode: "(isi jo kode bahaso)",
select_pos: "(piliah kelas kato)",
fill_meaning: "(isi jo mukasuiknyo)",
add_hom: "Tambah entri homonim",
remove_hom: "Apuih entri homonim",
add_pos: "Tambah kelas kato",
remove_pos: "Apuih kelas kato",
add_def: "Tambah definisi",
remove_def: "Apuih definisi",
preview: "Pratinjau",
undo: "Baliakkan",
save: "Simpan",
cancel: "Uruangkan",
preview_loading: "Pratinjau...",
save_loading: "Manyimpan...",
err_fill_all: "Isi dulu sadoalah barih nan tasadio, atau apuih barih nan kosong.",
err_lang_exists: "Subjudul untuak bahaso iko alah tasadio, pakai kode bahaso lain atau suntiang lansuang bagiannyo.",
err_fetch: "Indak dapek maambiak isi laman.",
err_preview: "Indak dapek mamuek pratinjau.",
err_save_prefix: "Indak dapek menyimpan:",
err_no_change: "Indak ado parubahan untuak disimpan.",
prompt_fill_langcode: "Isi kode bahaso dulu.",
save_summary_prefix: "[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+entri]]",
pos_labels: {
n: "kato bando",
v: "kato karajo",
adj: "kato sipaik",
adv: "kato katarangan",
num: "kato bilangan",
pron: "kato ganti",
pn: "namo diri",
ptcp: "partisipel",
conj: "kato sambuang",
intj: "kato saru",
ptcl: "partikel",
det: "panantu",
art: "artikel",
clf: "panggoloang",
prep: "kato mulo",
postp: "kato balakang",
circp: "kato apik",
ambip: "ambiposisi",
pref: "awalan",
suf: "akiaran",
inf: "sisipan",
circf: "apitan",
intf: "sambuangan",
rt: "aka",
ph: "frasa",
pb: "papatah-patitiah",
rom: "latin",
ltr: "uruih",
sym: "lambang"
}
}
};
const USER_LANG = (mw.config.get("wgUserLanguage") || "id").toLowerCase();
const T = EG_I18N[USER_LANG] || EG_I18N.id;
const api = new mw.Api();
let languageMap = null;
function fetchLanguageMap() {
if (languageMap) return Promise.resolve(languageMap);
return api.get({
action: "query",
prop: "revisions",
rvprop: "content",
rvslots: "main",
titles: "Module:bahasa",
formatversion: 2
}).then(res => {
const text = res.query.pages[0].revisions[0].slots.main.content;
const out = {};
text.replace(/\[\s*"([^"]+)"\s*\]\s*=\s*"([^"]+)"/g, (m,k,v) => out[k.toLowerCase()] = v);
languageMap = out;
return languageMap;
});
}
const ns = mw.config.get("wgNamespaceNumber");
const action = mw.config.get("wgAction");
const pageName = mw.config.get("wgPageName");
const pageTitle = mw.config.get("wgTitle");
if (!(ns === 0 && (action === "view" || action === "edit"))) return;
if ($(".entry-gadget-root").length) return;
// --- Simple title button ---
const $btn = $('<button type="button" class="entry-new-btn" title="' + T.btn_add_language + '">+</button>')
.on("click", () => openModalForNew());
// Find title element (cross-skin)
let $title = $("#firstHeading, h1.firstHeading, .page-heading").first();
if ($title.length) {
$title.append($btn);
} else {
// fallback if somehow not found
$("#siteSub, #mw-content-text").first().prepend($btn);
}
// appState: holds undo preview stacks and current parsed sections for saving
const appState = {
htmlStack: [],
scrollStack: [],
currentSections: null,
newLang: null,
lastFetchedTimestamp: null
};
let suppressLangAutofocus = false;
// ---------------------
// Helpers
// ---------------------
function htmlEscape(s) {
return String(s || "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
}
function canonicalizeNewlines(text) {
return String(text || "").replace(/\r\n/g, "\n");
}
function normalizeSpacing(text) {
return canonicalizeNewlines(text)
.replace(/\n{4,}/g, "\n\n\n");
}
function normalizeForCompare(text) {
return normalizeSpacing(text).trim();
}
function makeDraggable($modal, $handle) {
let dragging = false;
let started = false;
let startX = 0;
let startY = 0;
let origLeft = 0;
let origTop = 0;
let pointerId = null;
const THRESHOLD = 6;
$handle.css({
"touch-action": "none",
"user-select": "none",
"-webkit-user-select": "none"
});
$handle.on("pointerdown.entryDrag", function (ev) {
if ($(ev.target).closest("button, input, select, textarea, a").length) return;
ev.preventDefault();
const rect = $modal[0].getBoundingClientRect();
dragging = true;
started = false;
startX = ev.clientX;
startY = ev.clientY;
origLeft = rect.left;
origTop = rect.top;
pointerId = ev.pointerId;
$("body").addClass("entry-dragging");
$handle[0].setPointerCapture?.(pointerId);
});
$(document).on("pointermove.entryDrag", function (ev) {
if (!dragging) return;
if (pointerId !== null && ev.pointerId !== pointerId) return;
const dx = ev.clientX - startX;
const dy = ev.clientY - startY;
if (!started) {
if (Math.hypot(dx, dy) < THRESHOLD) return;
started = true;
}
const winW = window.innerWidth;
const winH = window.innerHeight;
const modalW = $modal.outerWidth();
const modalH = $modal.outerHeight();
let newLeft = origLeft + dx;
let newTop = origTop + dy;
newLeft = Math.max(8, Math.min(newLeft, winW - modalW - 8));
newTop = Math.max(8, Math.min(newTop, winH - modalH - 8));
$modal.css({
left: newLeft + "px",
top: newTop + "px",
transform: "none"
});
});
$(document).on("pointerup.entryDrag pointercancel.entryDrag", function (ev) {
if (pointerId !== null && ev.pointerId !== pointerId) return;
dragging = false;
started = false;
pointerId = null;
$("body").removeClass("entry-dragging");
});
}
function removeModal() {
$(".entry-modal").remove();
$(".entry-modal-overlay").remove();
}
function cancelAndCloseModal() {
removeModal();
$(".entry-highlight").removeClass("entry-highlight");
if (appState.htmlStack.length) {
const prevHtml = appState.htmlStack[0];
const prevScroll = appState.scrollStack[0];
const $target = $("#mw-content-text");
if ($target.length) {
$target.html(prevHtml);
if (typeof prevScroll === "number") $(window).scrollTop(prevScroll);
// reinitialize collapsibles and gadgets
if (window.mw && mw.loader) {
mw.loader.using(["jquery.makeCollapsible"]).then(function () {
$(".mw-collapsible").makeCollapsible();
if (window.mw && mw.hook) {
mw.hook("wikipage.content").fire($("#mw-content-text"));
}
});
}
}
appState.htmlStack = [];
appState.scrollStack = [];
appState.currentSections = null;
}
}
function renumberHomonyms(model) {
if (!model || !Array.isArray(model.homonyms)) return;
model.homonyms.forEach((h, i) => { h.num = i + 1; });
}
// ---------------------
// Wikitext parsing helpers (preview/save)
// ---------------------
// Extract language sections
function extractLangSections(wikitext) {
const regex = /^==\s*\{\{\s*[^|}\s]+\s*\|\s*(?:1\s*=\s*)?([^|}\s]+).*?\}\}\s*==/gim;
const headings = [];
let match;
while ((match = regex.exec(wikitext)) !== null) {
headings.push({ code: match[1], index: match.index, matchText: match[0], lastMatchIndex: regex.lastIndex });
}
if (headings.length === 0) {
return [ { code: null, heading: null, contentWikitext: wikitext, start: 0, end: wikitext.length } ];
}
const sections = [];
for (let i = 0; i < headings.length; i++) {
const h = headings[i];
const start = h.index;
const contentStart = h.lastMatchIndex;
const contentEnd = (i + 1 < headings.length) ? headings[i + 1].index : wikitext.length;
sections.push({
code: h.code,
heading: h.matchText,
contentWikitext: wikitext.slice(contentStart, contentEnd),
start: start,
contentStart: contentStart,
end: contentEnd
});
}
return sections;
}
// ---------------------
// Get display name for language code (DOM-first, cached)
// ---------------------
function getDisplayNameForCode(code) {
if (!code) return "";
const key = String(code).toLowerCase();
if (languageMap && languageMap[key]) return languageMap[key];
// Fallback if module not yet loaded
return key;
}
// ---------------------
// Rebuild text from sections
// ---------------------
function rebuildFullTextFromSections(_originalText, sections) {
if (!Array.isArray(sections)) return "";
let out = "";
let seenLang = false;
for (const s of sections) {
const h = s.heading || "";
const c = s.contentWikitext || "";
if (h) {
if (seenLang && !out.endsWith("\n\n")) {
out = out.replace(/\n?$/, "\n\n");
}
seenLang = true;
}
out += h + c;
}
return out;
}
// ---------------------
// Helpers that build wikitext blocks
// ---------------------
function buildHomonymBlock(lang, hom) {
let out = "";
// include num= only if there are multiple homonyms or num>1
out += "{{kepala|" + lang + ((hom.num && hom.num > 1) ? "|num=" + hom.num : "") + "}}\n\n";
hom.posGroups.forEach(function (pg) {
out += "{{-" + (pg.pos || "") + "-|" + lang + "}}\n";
pg.defs.forEach(function (d) { if (d && d.trim()) out += "# " + d + "\n"; });
out += "";
});
return out;
}
// ---------------------
// Insert model into sections (DOM-based sort)
// ---------------------
function insertModelIntoSections(sections, model) {
sections = Array.isArray(sections) ? sections.slice() : [];
const incomingCode = (model.lang || "").toString().trim();
const incomingKey = incomingCode.toLowerCase();
// map existing codes to indices
const codeToIndex = {};
for (let i = 0; i < sections.length; i++) {
const s = sections[i];
if (s && s.code) codeToIndex[(s.code || "").toLowerCase()] = i;
}
// build entry text
const entryLines = model.homonyms
.map(h => buildHomonymBlock(model.lang, h))
.filter(Boolean)
.join("\n");
if (incomingKey && codeToIndex.hasOwnProperty(incomingKey)) {
const idx = codeToIndex[incomingKey];
const sec = sections[idx];
const existing = canonicalizeNewlines(sec.contentWikitext || "");
const toAppend = canonicalizeNewlines(entryLines);
// duplicate check
const existingNorm = normalizeForCompare(existing);
const appendNorm = normalizeForCompare(toAppend);
if (!existingNorm.endsWith(appendNorm)) {
let content = normalizeSpacing(existing);
if (content && !content.endsWith("\n")) {
content += "\n";
}
sec.contentWikitext = normalizeSpacing(
content + toAppend + "\n"
);
}
} else {
let newHeading = "=={{bahasa|" + model.lang + "}}==\n";
sections.push({
code: model.lang,
heading: canonicalizeNewlines(newHeading),
contentWikitext: normalizeSpacing(entryLines) + "\n",
start: null,
contentStart: null,
end: null
});
}
// separate non-lang and lang sections
const nonLang = sections.filter(s => !s.code);
const langSections = sections.filter(s => !!s.code);
// sort by display name after mul and site language
// auto-detect site language for sorting priority
const siteLang = (mw.config.get("wgContentLanguage") || "").toLowerCase();
const order = {
mul: 1,
[siteLang]: 2
};
langSections.forEach(s => {
s._codeNorm = (s.code || "").toLowerCase();
});
langSections.sort(function (a, b) {
const aCode = a._codeNorm || "";
const bCode = b._codeNorm || "";
const aRank = order[aCode] || 3;
const bRank = order[bCode] || 3;
if (aRank !== bRank) return aRank - bRank;
const aName = getDisplayNameForCode(a.code || aCode) || aCode;
const bName = getDisplayNameForCode(b.code || bCode) || bCode;
return aName.localeCompare(bName, undefined, { sensitivity: "base" });
});
langSections.forEach(s => { delete s._codeNorm; });
const rebuilt = nonLang.concat(langSections);
return rebuilt.map(s => ({
code: s.code || null,
heading: s.heading || null,
contentWikitext: s.contentWikitext || "",
start: (typeof s.start !== "undefined") ? s.start : null,
end: (typeof s.end !== "undefined") ? s.end : null
}));
}
function replacePageContentWith(parsedHtml) {
const $parsed = $("<div>").html(parsedHtml);
const $target = $("#mw-content-text");
if ($target.length) {
$target.data("savedScrollTop", $(window).scrollTop());
$target.html($parsed);
} else {
$("body").html($parsed);
}
}
function highlightInserted(lang) {
$(".entry-highlight").removeClass("entry-highlight");
const $langEl = $("#mw-content-text").find(".lang[data-code='" + lang + "']").first();
if ($langEl && $langEl.length) {
const $h2 = $langEl.closest("h2");
if ($h2.length) {
const $toHighlight = $h2.nextUntil("h2").addBack();
$toHighlight.addClass("entry-highlight");
const top = $h2.offset() ? $h2.offset().top : null;
if (top) $("html,body").animate({ scrollTop: top - 60 }, 250);
setTimeout(function () { $toHighlight.removeClass("entry-highlight"); }, 7000);
}
}
}
function fetchLatestWikitext() {
return api.get({
action: "query",
prop: "revisions",
rvprop: "content|timestamp",
rvslots: "main",
titles: pageName,
formatversion: 2
}).then(function (data) {
const page = data.query.pages[0];
const fullText = page.revisions && page.revisions[0] && page.revisions[0].slots && page.revisions[0].slots.main
? page.revisions[0].slots.main.content || ""
: "";
const timestamp = page.revisions && page.revisions[0] ? page.revisions[0].timestamp : null;
return { text: fullText, timestamp: timestamp };
});
}
// ---------------------
// Modal + form rendering
// ---------------------
function createEmptyModel() {
return {
lang: "",
homonyms: [ { num: 1, posGroups: [ { pos: "", defs: [""] } ] } ]
};
}
// POS canonical short codes
const POS_CODES = [
"n","v","adj","adv","num","pron","pn","ptcp",
"conj","intj","ptcl","det","art","clf","prep",
"postp","circp","ambip","pref","suf","inf",
"circf","intf","rt","ph","pb","rom",
"ltr","sym"
];
// Render modal from model
function renderModal(model) {
let justAddedFocus = false;
removeModal();
const $overlay = $('<div class="entry-modal-overlay" />');
const $modal = $('<div class="entry-modal" role="dialog" aria-modal="true"></div>');
const $header = $('<div class="entry-header"><div class="entry-title">' + T.modal_title_add + htmlEscape(pageTitle || "") + '</div><div><button class="entry-mini-btn entry-mini-rem" title="' + T.cancel + '">✕</button></div></div>');
const $body = $('<div class="entry-body"></div>');
const $controls = $('<div class="entry-controls"></div>');
$modal.append($header, $body, $controls);
$("body").append($overlay, $modal);
makeDraggable($modal, $header);
$header.find(".entry-mini-rem").on("click", function (e) { e.preventDefault(); cancelAndCloseModal(); });
// Top-level inputs
const $langRow = $(`<div class="entry-block"><div class="row" style="align-items:center;">
<div style="flex:1; display:flex; align-items:center; gap:0.5em;">
<input type="text" id="eg-lang" maxlength="11" style="max-width:3em;" value="${htmlEscape(model.lang || "")}">
<span style="opacity:0.8;">${T.fill_langcode}</span>
</div></div></div>`);
$body.append($langRow);
// Homonyms container
const $homContainer = $('<div id="eg-homonyms"></div>');
$body.append($homContainer);
// For each homonym, render a block
model.homonyms.forEach(function (hom, hIdx) {
const homNum = hom.num || (hIdx + 1);
const $hb = $('<div class="entry-block eg-homonym" data-hidx="' + hIdx + '"></div>');
// hom header: [+] [-] pagename (homonym #) -- pagename is plain bold text from pageTitle
const $hdr = $('<div class="row" style="align-items:center;"></div>');
const $addHomBtn = $('<button class="entry-mini-btn entry-mini-add" title="' + T.add_hom + '">+</button>');
const $rmHomBtn = $('<button class="entry-mini-btn entry-mini-rem" title="' + T.remove_hom + '">–</button>');
const $homTitle = $('<div style="flex:1"><b style="display:inline-block;">' + htmlEscape(pageTitle || "").replace(/_/g, " ") + '</b> <span style="opacity:0.8">(' + homNum + ')</span></div>');
$hdr.append($addHomBtn, $rmHomBtn, $homTitle);
// disable if only one homonym
if (model.homonyms.length <= 1) {
$rmHomBtn.prop("disabled", true);
} else {
$rmHomBtn.prop("disabled", false).css("opacity", "1.0");
}
$hb.append($hdr);
// POS groups container
const $posContainer = $('<div class="eg-pos-container"></div>');
hom.posGroups.forEach(function (pg, pIdx) {
const $pblock = $('<div class="entry-block" data-pidx="' + pIdx + '"></div>');
const $prow = $('<div class="row" style="align-items:center;"></div>');
const $addPosBtn = $('<button class="entry-mini-btn entry-mini-add" title="' + T.add_pos + '">+</button>');
const $rmPosBtn = $('<button class="entry-mini-btn entry-mini-rem" title="' + T.remove_pos + '">–</button>');
// Build POS dropdown
const posLabels = T.pos_labels || {};
const $posSelect = $('<select class="eg-pos"></select>');
$posSelect.append('<option value="">' + T.select_pos + '</option>');
POS_CODES.forEach(code => {
const label = posLabels[code] || code;
const $opt = $('<option></option>')
.val(code)
.text(label + " (" + code + ")");
if (pg.pos === code) $opt.prop("selected", true);
$posSelect.append($opt);
});
const $posInputWrap = $('<div style="flex:1; min-width:10em;"></div>').append($posSelect);
$prow.append($addPosBtn, $rmPosBtn, $posInputWrap);
$pblock.append($prow);
// disable if only one POS in homonym
if (hom.posGroups.length <= 1) {
$rmPosBtn.prop("disabled", true);
} else {
$rmPosBtn.prop("disabled", false).css("opacity", "1.0");
}
// Definitions
const $defsContain = $('<div class="eg-defs"></div>');
pg.defs.forEach(function (d, dIdx) {
const $drow = $('<div class="row" style="align-items:center;"></div>');
const $addDefBtn = $('<button class="entry-mini-btn entry-mini-add" title="' + T.add_def + '">+</button>');
const $rmDefBtn = $('<button class="entry-mini-btn entry-mini-rem" title="' + T.remove_def + '">–</button>');
const $defInput = $('<div style="flex:1"><input type="text" class="eg-def" placeholder="' + T.fill_meaning + '" value="' + htmlEscape(d || "") + '"></div>');
$drow.append($addDefBtn, $rmDefBtn, $defInput);
$defsContain.append($drow);
// disable if only one definition
if (pg.defs.length <= 1) {
$rmDefBtn.prop("disabled", true);
} else {
$rmDefBtn.prop("disabled", false).css("opacity", "1.0");
}
// Def-level events
$addDefBtn.on("click", function (ev) {
ev.preventDefault();
const read = $modal.data("readForm");
if (read) read();
const mdl = $modal.data("model");
mdl.homonyms[hIdx].posGroups[pIdx].defs.splice(dIdx + 1, 0, "");
suppressLangAutofocus = true;
renderModal(mdl);
// Focus new definition input
setTimeout(() => {
const $newDef = $(`.eg-homonym[data-hidx='${hIdx}'] .entry-block[data-pidx='${pIdx}'] .eg-def:eq(${dIdx + 1})`);
if ($newDef.length) justAddedFocus = true;
$newDef.focus();
setTimeout(() => { justAddedFocus = false; }, 200);
}, 100);
});
$rmDefBtn.on("click", function (ev) {
ev.preventDefault();
const read = $modal.data("readForm");
if (read) read();
const mdl = $modal.data("model");
const defsArr = mdl.homonyms[hIdx].posGroups[pIdx].defs;
suppressLangAutofocus = true;
if (defsArr.length > 1) defsArr.splice(dIdx, 1);
else defsArr[0] = "";
renderModal(mdl);
setTimeout(() => {
const $prev = $(`.eg-homonym[data-hidx='${hIdx}'] .entry-block[data-pidx='${pIdx}'] .eg-def:eq(${Math.max(dIdx - 1, 0)})`);
if ($prev.length) $prev.focus();
}, 100);
});
});
// POS-level events
$addPosBtn.on("click", function (ev) {
ev.preventDefault();
const read = $modal.data("readForm");
if (read) read();
const mdl = $modal.data("model");
mdl.homonyms[hIdx].posGroups.splice(pIdx + 1, 0, { pos: "", defs: [""] });
suppressLangAutofocus = true;
renderModal(mdl);
// Focus the new POS dropdown
setTimeout(() => {
const $newPos = $(`.eg-homonym[data-hidx='${hIdx}'] .entry-block[data-pidx='${pIdx + 1}'] .eg-pos`);
if ($newPos.length) justAddedFocus = true;
$newPos.focus();
setTimeout(() => { justAddedFocus = false; }, 200);
}, 100);
});
$rmPosBtn.on("click", function (ev) {
ev.preventDefault();
const read = $modal.data("readForm");
if (read) read();
const mdl = $modal.data("model");
suppressLangAutofocus = true;
if (mdl.homonyms[hIdx].posGroups.length > 1) mdl.homonyms[hIdx].posGroups.splice(pIdx, 1);
else mdl.homonyms[hIdx].posGroups[0] = { pos: "", defs: [""] };
renderModal(mdl);
setTimeout(() => {
const $prev = $(`.eg-homonym[data-hidx='${hIdx}'] .entry-block[data-pidx='${Math.max(pIdx - 1, 0)}'] .eg-pos`).first();
if ($prev.length) $prev.focus();
}, 100);
});
$pblock.append($defsContain);
$posContainer.append($pblock);
});
$hb.append($posContainer);
// Hom-level events
$addHomBtn.on("click", function (ev) {
ev.preventDefault();
const read = $modal.data("readForm");
if (read) read();
const mdl = $modal.data("model");
const insertionIndex = hIdx + 1;
mdl.homonyms.splice(insertionIndex, 0, { num: 0, posGroups: [{ pos: "", defs: [""] }] });
renumberHomonyms(mdl);
suppressLangAutofocus = true;
renderModal(mdl);
// Focus first POS dropdown of new homonym
setTimeout(() => {
const $newPos = $(`.eg-homonym[data-hidx='${insertionIndex}'] .eg-pos`).first();
if ($newPos.length) justAddedFocus = true;
$newPos.focus();
setTimeout(() => { justAddedFocus = false; }, 200);
}, 100);
});
$rmHomBtn.on("click", function (ev) {
ev.preventDefault();
const read = $modal.data("readForm");
if (read) read();
const mdl = $modal.data("model");
suppressLangAutofocus = true;
if (mdl.homonyms.length > 1) {
mdl.homonyms.splice(hIdx, 1);
} else {
mdl.homonyms[0] = { num: 1, posGroups: [ { pos: "", defs: [""] } ] };
}
renumberHomonyms(mdl);
renderModal(mdl);
setTimeout(() => {
const $prev = $(`.eg-homonym[data-hidx='${Math.max(hIdx - 1, 0)}'] .eg-pos`).first();
if ($prev.length) $prev.focus();
}, 100);
});
$homContainer.append($hb);
}); // end homonyms.forEach
// Controls: Preview, Undo, Save, Cancel
const $previewBtn = $('<button class="entry-preview-btn" type="button">' + T.preview + '</button>');
const $undoBtn = $('<button class="entry-undo-btn" type="button" disabled>' + T.undo + '</button>');
const $saveBtn = $('<button class="entry-save-btn" type="button" disabled>' + T.save + '</button>');
const $cancelBtn = $('<button class="entry-cancel-btn" type="button">' + T.cancel + '</button>');
$controls.append($previewBtn, $undoBtn, $saveBtn, $cancelBtn);
$cancelBtn.on("click", function (e) { e.preventDefault(); cancelAndCloseModal(); });
// Read form into model helper
function readFormToModel() {
const mdl = { lang: "", homonyms: [] };
mdl.lang = $modal.find("#eg-lang").val().trim();
$modal.find("#eg-homonyms > .eg-homonym").each(function (hIndex) {
const $hb = $(this);
const homModel = { num: hIndex + 1, posGroups: [] };
$hb.find(".eg-pos-container > .entry-block").each(function (pIndex) {
const $pblock = $(this);
const pos = $pblock.find(".eg-pos").val().trim();
const defs = [];
$pblock.find(".eg-def").each(function () { defs.push($(this).val().trim()); });
homModel.posGroups.push({ pos: pos, defs: defs.length ? defs : [""] });
});
if (homModel.posGroups.length === 0) homModel.posGroups = [ { pos: "", defs: [""] } ];
mdl.homonyms.push(homModel);
});
renumberHomonyms(mdl);
// update modal-stored model so event handlers can use current state
$modal.data("model", mdl);
return mdl;
}
$modal.data("readForm", readFormToModel);
$modal.data("model", model);
// Preview handler
$previewBtn.on("click", function () {
const read = $modal.data("readForm");
if (read) read();
const mdl = $modal.data("model");
if (!mdl.lang) { alert(T.prompt_fill_langcode); return; }
// check at least one pos+definition non-empty
// validation: all visible inputs must be filled
let allFilled = true;
mdl.homonyms.forEach(h => {
h.posGroups.forEach(pg => {
const posFilled = pg.pos && pg.pos.trim().length > 0;
const defsFilled = pg.defs.length > 0 && pg.defs.every(d => d && d.trim().length > 0);
if (!posFilled || !defsFilled) allFilled = false;
});
});
if (!allFilled) {
alert(T.err_fill_all);
return;
}
$previewBtn.prop("disabled", true).text(T.preview_loading);
fetchLatestWikitext().then(function (res) {
const sections = extractLangSections(res.text);
// if language already exists, warn user (since edit-data flow removed)
const existingIdx = sections.findIndex(s => s.code && s.code.toLowerCase() === mdl.lang.toLowerCase());
if (existingIdx >= 0) {
alert(T.err_lang_exists);
$previewBtn.prop("disabled", false).text(T.preview);
return;
}
// push snapshot for undo
appState.htmlStack.push($("#mw-content-text").html() || "");
appState.scrollStack.push($(window).scrollTop());
appState.lastFetchedTimestamp = res.timestamp;
fetchLanguageMap().then(() => {
const updatedSections = insertModelIntoSections(sections, mdl);
appState.currentSections = updatedSections;
appState.newLang = mdl.lang;
const newFull = rebuildFullTextFromSections(res.text, updatedSections);
api.post({
action: "parse",
title: pageName,
text: newFull,
contentmodel: "wikitext",
prop: "text",
format: "json"
}).done(function (resp) {
const html = (resp && resp.parse && resp.parse.text)
? resp.parse.text["*"] || ""
: "";
replacePageContentWith(html);
highlightInserted(mdl.lang);
$undoBtn.prop("disabled", false).css("opacity", "1.0");
$saveBtn.prop("disabled", false).css("opacity", "1.0");
$previewBtn.prop("disabled", false).text(T.preview);
}).fail(function () {
alert(T.err_preview);
$previewBtn.prop("disabled", false).text(T.preview);
});
});
}).catch(function () {
alert(T.err_fetch);
$previewBtn.prop("disabled", false).text(T.preview);
});
});
// Undo handler (Balikin)
$undoBtn.on("click", function () {
if (!appState.htmlStack.length) {
$undoBtn.prop("disabled", true);
$saveBtn.prop("disabled", true);
return;
}
const prevHtml = appState.htmlStack.pop();
const prevScroll = appState.scrollStack.pop();
const $target = $("#mw-content-text");
if ($target.length) {
$target.html(prevHtml);
if (typeof prevScroll === "number") $(window).scrollTop(prevScroll);
}
if (appState.htmlStack.length === 0) {
$undoBtn.prop("disabled", true);
$saveBtn.prop("disabled", true);
} else {
$undoBtn.prop("disabled", false).css("opacity", "1.0");
$saveBtn.prop("disabled", false).css("opacity", "1.0");
}
});
// Save handler
$saveBtn.on("click", function () {
const read = $modal.data("readForm");
if (read) read();
if (!appState.currentSections) {
alert(T.err_no_change);
return;
}
$saveBtn.prop("disabled", true).text(T.save_loading);
fetchLatestWikitext().then(function (res) {
const latestText = res.text;
const latestTimestamp = res.timestamp;
// Rebuild final full text from appState.currentSections but apply to latest text
const newText = normalizeSpacing(
rebuildFullTextFromSections(latestText, appState.currentSections)
);
const lang = appState.newLang || "";
const summary = lang ? `${T.save_summary_prefix} [${lang}]` : T.save_summary_prefix;
api.postWithToken("csrf", {
action: "edit",
title: pageName,
text: newText,
summary: summary,
minor: true,
basetimestamp: latestTimestamp
}).done(function () {
location.reload();
}).fail(function (err) {
const info = err && err.error && err.error.info ? err.error.info : String(err);
alert(T.err_save_prefix + " " + info);
$saveBtn.prop("disabled", false).text(T.save);
});
}).catch(function () {
alert(T.err_fetch);
$saveBtn.prop("disabled", false).text(T.save);
});
});
// Focus language input only once when modal first opens
if (!appState.modalEverOpened) {
setTimeout(() => $modal.find("#eg-lang").focus(), 25);
appState.modalEverOpened = true;
}
// ---------------------
// Keyboard accessibility & shortcuts
// ---------------------
(function setupKeyboard() {
const focusables = () =>
$modal.find('input[type="text"], select').filter(':visible');
function focusNext(current, dir) {
const f = focusables().toArray();
const idx = f.indexOf(current);
if (idx === -1) return;
const next = f[idx + dir];
if (next) next.focus();
}
$modal.on("keydown", function (e) {
const target = e.target;
// Ctrl+Enter => Save
if (e.ctrlKey && e.key === "Enter") {
e.preventDefault();
$modal.find(".entry-save-btn").trigger("click");
return;
}
// Enter in definition input => trigger Preview
if (
e.key === "Enter" &&
$(target).hasClass("eg-def") &&
!e.ctrlKey &&
!e.shiftKey
) {
e.preventDefault();
$modal.find(".entry-preview-btn").trigger("click");
return;
}
// Shift+ArrowDown in definition input => add new definition below
if (e.key === "ArrowDown" && e.shiftKey && $(target).hasClass("eg-def")) {
e.preventDefault();
const $row = $(target).closest(".row");
const hidx = $row.closest(".eg-homonym").data("hidx");
const pidx = $row.closest(".entry-block").data("pidx");
const didx = $row.closest('.eg-defs').find('.row').index($row);
const read = $modal.data("readForm");
if (read) read();
const mdl = $modal.data("model");
suppressLangAutofocus = true;
mdl.homonyms[hidx].posGroups[pidx].defs.splice(didx + 1, 0, "");
renderModal(mdl);
setTimeout(() => {
const newDef = $(
`.eg-homonym[data-hidx='${hidx}'] .entry-block[data-pidx='${pidx}'] .eg-def:eq(${didx + 1})`
).get(0);
if (newDef) {
justAddedFocus = true;
newDef.focus();
setTimeout(() => { justAddedFocus = false; }, 250);
}
}, 100);
return;
}
// Shift+ArrowUp on blank definition line => remove it
if (e.key === "ArrowUp" && e.shiftKey && $(target).hasClass("eg-def")) {
const value = $(target).val().trim();
if (value === "") {
e.preventDefault();
const $row = $(target).closest(".row");
const hidx = $row.closest(".eg-homonym").data("hidx");
const pidx = $row.closest(".entry-block").data("pidx");
const didx = $row.closest('.eg-defs').find('.row').index($row);
const read = $modal.data("readForm");
if (read) read();
const mdl = $modal.data("model");
const defs = mdl.homonyms[hidx].posGroups[pidx].defs;
suppressLangAutofocus = true;
if (defs.length > 1) defs.splice(didx, 1);
else defs[0] = "";
renderModal(mdl);
setTimeout(() => {
const prev = $(
`.eg-homonym[data-hidx='${hidx}'] .entry-block[data-pidx='${pidx}'] .eg-def:eq(${Math.max(didx - 1, 0)})`
).get(0);
if (prev) {
justAddedFocus = true;
prev.focus();
setTimeout(() => { justAddedFocus = false; }, 250);
}
}, 100);
return;
}
}
// Plain ArrowDown/Up navigation between inputs
if ((e.key === "ArrowDown" || e.key === "ArrowUp") && !e.shiftKey && !justAddedFocus) {
e.preventDefault();
focusNext(target, e.key === "ArrowDown" ? +1 : -1);
return;
}
});
})();
return $modal;
}
// Modal openers
function openModalForNew() {
const model = createEmptyModel();
renderModal(model);
}
// Expose debug hooks if needed
window.EntryAdderGadget = {
openNew: openModalForNew,
removeModal: removeModal
};
});
0o2m8v6vduh68ek1sfpf6t5jsyhyf70
1463337
1463336
2026-07-11T11:27:48Z
Sibiru45
40479
1463337
javascript
text/javascript
mw.loader.using(["mediawiki.api", "mediawiki.util"]).then(function () {
"use strict";
mw.loader.load(
"https://id.wiktionary.org/w/index.php?title=Pengguna:Swarabakti/Gadget-EntryAdder.css&action=raw&ctype=text/css",
"text/css"
);
// ---------------------
// Add more languages by adding keys here.
// ---------------------
const EG_I18N = {
id: {
btn_add_language: "Tambahkan entri baru pada halaman ini",
modal_title_add: "Tambahkan entri baru — ",
fill_langcode: "(isi dengan kode bahasa)",
select_pos: "(pilih kelas kata)",
fill_meaning: "(isi dengan maknanya)",
add_hom: "Tambah entri homonim",
remove_hom: "Hapus entri homonim",
add_pos: "Tambah kelas kata",
remove_pos: "Hapus kelas kata",
add_def: "Tambah definisi",
remove_def: "Hapus definisi",
preview: "Pratayang",
undo: "Balikkan",
save: "Simpan",
cancel: "Batalkan",
preview_loading: "Pratayang...",
save_loading: "Menyimpan...",
err_fill_all: "Isi dulu semua baris yang tersedia, atau hapus baris yang kosong.",
err_lang_exists: "Subjudul untuk bahasa ini sudah tersedia, gunakan kode bahasa lain atau sunting langsung bagiannya.",
err_fetch: "Gagal mengambil isi halaman.",
err_preview: "Gagal memuat pratayang.",
err_save_prefix: "Gagal menyimpan:",
err_no_change: "Tiada perubahan untuk disimpan.",
prompt_fill_langcode: "Isi kode bahasa dulu.",
save_summary_prefix: "[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+entri]]",
pos_labels: {
n: "nomina",
v: "verba",
adj: "adjektiva",
adv: "adverbia",
num: "numeralia",
pron: "pronomina",
pn: "nama diri",
ptcp: "partisipel",
conj: "konjungsi",
intj: "interjeksi",
ptcl: "partikula",
det: "penentu",
art: "artikula",
clf: "penggolong",
prep: "preposisi",
postp: "postposisi",
circp: "sirkumposisi",
ambip: "ambiposisi",
pref: "awalan",
suf: "akhiran",
inf: "sisipan",
circf: "apitan",
intf: "sambungan",
rt: "akar",
ph: "frasa",
pb: "peribahasa",
rom: "romanisasi",
ltr: "huruf",
sym: "simbol"
}
},
bew: {
btn_add_language: "Tambahin pentol kata anyar bakal ni halaman",
modal_title_add: "Tambahin pentol anyar — ",
fill_langcode: "(isi ama kode basanya)",
select_pos: "(pili rungkun katanya)",
fill_meaning: "(isi ama pema'naannya)",
add_hom: "Tambah pentol kata sejogrokan",
remove_hom: "Apus pentol kata sejogrokan",
add_pos: "Tambah rungkun katanya",
remove_pos: "Apus rungkun katanya",
add_def: "Tambah pema'naannya",
remove_def: "Apus pema'naannya",
preview: "Terawang",
undo: "Balikin",
save: "Simpen",
cancel: "Urungin",
preview_loading: "Nerawangin...",
save_loading: "Nyimpenin...",
err_fill_all: "Isiin dulu semua baris rungkun ama ma'na nyang derak, atawa apus-apusin nyang kopong.",
err_lang_exists: "Anakjudul bakal entu basa udah derak — jal paké laèn kode basa atawa permak langsung entu bagèan.",
err_fetch: "Kaga' bisa nyomot isi halaman.",
err_preview: "Kaga' bisa munggah terawangan.",
err_save_prefix: "Kaga' bisa nyimpen:",
err_no_change: "Kaga' ada perobahan bakal disimpen.",
prompt_fill_langcode: "Isi kode basanya dulu.",
save_summary_prefix: "[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+pentol]]",
pos_labels: {
n: "kata peada",
v: "kata kereja",
adj: "kata penyipat",
adv: "kata penerang",
num: "kata pengangka",
pron: "kata pengganti",
pn: "kata nama",
ptcp: "dumanan",
conj: "penjuntrung",
intj: "gegeroan",
ptcl: "petilan",
det: "pemuguhan",
art: "penimpalan",
clf: "pemantar",
prep: "pengawalan",
postp: "pengahiran",
circp: "pendengkèkan",
ambip: "ambiposisi",
pref: "depanan",
suf: "kintilan",
inf: "selesepan",
circf: "apitan",
intf: "sambungan",
rt: "bongkot",
ph: "gabungan kata",
pb: "peribasa kata",
rom: "penglatènan",
ltr: "hurup",
sym: "lambang"
}
},
bjn: {
btn_add_language: "Tambahakan entri puga di halaman ngini",
modal_title_add: "Tambahakan entri puga — ",
fill_langcode: "(isi pakai kode bahasa)",
select_pos: "(pilih kalas kata)",
fill_meaning: "(isii maknanya)",
add_hom: "Tambahi entri homonim",
remove_hom: "Hapus entri homonim",
add_pos: "Tambahi kalas kata",
remove_pos: "Hapus kalas kata",
add_def: "Tambahi artinya",
remove_def: "Hapus artinya",
preview: "Tatilikan",
undo: "Bulikakan",
save: "Simpan",
cancel: "Pasahakan",
preview_loading: "Tatilikan...",
save_loading: "Manyimpan...",
err_fill_all: "Isi dulu samunyaan baris nang ada, atawa hapus baris nang puang.",
err_lang_exists: "Subjudul gasan basa ngini sudah ada, pakai kode basa lain atawa babak langsung bagiannya.",
err_fetch: "Kada kawa maambil isi halaman.",
err_preview: "Kada kawa manapaiakan pratayang.",
err_save_prefix: "Kada kawa manyimpan:",
err_no_change: "Kadada paubahan gasan disimpan.",
prompt_fill_langcode: "Isi kode basanya dulu.",
save_summary_prefix: "[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+entri]]",
pos_labels: {
n: "kata banda",
v: "kata gawi",
adj: "kata sipat",
adv: "kata panjalas",
num: "kata bilangan",
pron: "kata ganti",
pn: "ngaran diri",
ptcp: "partisip",
conj: "kata panyambung",
intj: "kata saru",
ptcl: "kata tugas",
det: "panantu",
art: "kata sandang",
clf: "panggulung",
prep: "kata muka",
postp: "postposisi",
circp: "sirkumposisi",
ambip: "ambiposisi",
pref: "awalan",
suf: "akhiran",
inf: "sisipan",
circf: "apitan",
intf: "sambungan",
rt: "akar",
ph: "prasa",
pb: "paribasa",
rom: "romanisasi",
ltr: "hurup",
sym: "simbul"
}
},
min: {
btn_add_language: "Tambahkan entri baru untuak halaman iko",
modal_title_add: "Tambahkan entri baru — ",
fill_langcode: "(isi jo kode bahaso)",
select_pos: "(piliah kelas kato)",
fill_meaning: "(isi jo mukasuiknyo)",
add_hom: "Tambah entri homonim",
remove_hom: "Apuih entri homonim",
add_pos: "Tambah kelas kato",
remove_pos: "Apuih kelas kato",
add_def: "Tambah definisi",
remove_def: "Apuih definisi",
preview: "Pratinjau",
undo: "Baliakkan",
save: "Simpan",
cancel: "Uruangkan",
preview_loading: "Pratinjau...",
save_loading: "Manyimpan...",
err_fill_all: "Isi dulu sadoalah barih nan tasadio, atau apuih barih nan kosong.",
err_lang_exists: "Subjudul untuak bahaso iko alah tasadio, pakai kode bahaso lain atau suntiang lansuang bagiannyo.",
err_fetch: "Indak dapek maambiak isi laman.",
err_preview: "Indak dapek mamuek pratinjau.",
err_save_prefix: "Indak dapek menyimpan:",
err_no_change: "Indak ado parubahan untuak disimpan.",
prompt_fill_langcode: "Isi kode bahaso dulu.",
save_summary_prefix: "[[:wikt:id:Pengguna:Swarabakti/Gadget-EntryAdder.js|+entri]]",
pos_labels: {
n: "kato bando",
v: "kato karajo",
adj: "kato sipaik",
adv: "kato katarangan",
num: "kato bilangan",
pron: "kato ganti",
pn: "namo diri",
ptcp: "partisipel",
conj: "kato sambuang",
intj: "kato saru",
ptcl: "partikel",
det: "panantu",
art: "artikel",
clf: "panggoloang",
prep: "kato mulo",
postp: "kato balakang",
circp: "kato apik",
ambip: "ambiposisi",
pref: "awalan",
suf: "akiaran",
inf: "sisipan",
circf: "apitan",
intf: "sambuangan",
rt: "aka",
ph: "frasa",
pb: "papatah-patitiah",
rom: "latin",
ltr: "uruih",
sym: "lambang"
}
}
};
const USER_LANG = (mw.config.get("wgUserLanguage") || "id").toLowerCase();
const T = EG_I18N[USER_LANG] || EG_I18N.id;
const api = new mw.Api();
let languageMap = null;
function fetchLanguageMap() {
if (languageMap) return Promise.resolve(languageMap);
return api.get({
action: "query",
prop: "revisions",
rvprop: "content",
rvslots: "main",
titles: "Module:bahasa",
formatversion: 2
}).then(res => {
const text = res.query.pages[0].revisions[0].slots.main.content;
const out = {};
text.replace(/\[\s*"([^"]+)"\s*\]\s*=\s*"([^"]+)"/g, (m,k,v) => out[k.toLowerCase()] = v);
languageMap = out;
return languageMap;
});
}
const ns = mw.config.get("wgNamespaceNumber");
const action = mw.config.get("wgAction");
const pageName = mw.config.get("wgPageName");
const pageTitle = mw.config.get("wgTitle");
if (!(ns === 0 && (action === "view" || action === "edit"))) return;
if ($(".entry-gadget-root").length) return;
// --- Simple title button ---
const $btn = $('<button type="button" class="entry-new-btn" title="' + T.btn_add_language + '">+</button>')
.on("click", () => openModalForNew());
// Find title element (cross-skin)
let $title = $("#firstHeading, h1.firstHeading, .page-heading").first();
if ($title.length) {
$title.append($btn);
} else {
// fallback if somehow not found
$("#siteSub, #mw-content-text").first().prepend($btn);
}
// appState: holds undo preview stacks and current parsed sections for saving
const appState = {
htmlStack: [],
scrollStack: [],
currentSections: null,
newLang: null,
lastFetchedTimestamp: null
};
let suppressLangAutofocus = false;
// ---------------------
// Helpers
// ---------------------
function htmlEscape(s) {
return String(s || "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
}
function canonicalizeNewlines(text) {
return String(text || "").replace(/\r\n/g, "\n");
}
function normalizeSpacing(text) {
return canonicalizeNewlines(text)
.replace(/\n{4,}/g, "\n\n\n");
}
function normalizeForCompare(text) {
return normalizeSpacing(text).trim();
}
function makeDraggable($modal, $handle) {
let dragging = false;
let started = false;
let startX = 0;
let startY = 0;
let origLeft = 0;
let origTop = 0;
let pointerId = null;
const THRESHOLD = 6;
$handle.css({
"touch-action": "none",
"user-select": "none",
"-webkit-user-select": "none"
});
$handle.on("pointerdown.entryDrag", function (ev) {
if ($(ev.target).closest("button, input, select, textarea, a").length) return;
ev.preventDefault();
const rect = $modal[0].getBoundingClientRect();
dragging = true;
started = false;
startX = ev.clientX;
startY = ev.clientY;
origLeft = rect.left;
origTop = rect.top;
pointerId = ev.pointerId;
$("body").addClass("entry-dragging");
$handle[0].setPointerCapture?.(pointerId);
});
$(document).on("pointermove.entryDrag", function (ev) {
if (!dragging) return;
if (pointerId !== null && ev.pointerId !== pointerId) return;
const dx = ev.clientX - startX;
const dy = ev.clientY - startY;
if (!started) {
if (Math.hypot(dx, dy) < THRESHOLD) return;
started = true;
}
const winW = window.innerWidth;
const winH = window.innerHeight;
const modalW = $modal.outerWidth();
const modalH = $modal.outerHeight();
let newLeft = origLeft + dx;
let newTop = origTop + dy;
newLeft = Math.max(8, Math.min(newLeft, winW - modalW - 8));
newTop = Math.max(8, Math.min(newTop, winH - modalH - 8));
$modal.css({
left: newLeft + "px",
top: newTop + "px",
transform: "none"
});
});
$(document).on("pointerup.entryDrag pointercancel.entryDrag", function (ev) {
if (pointerId !== null && ev.pointerId !== pointerId) return;
dragging = false;
started = false;
pointerId = null;
$("body").removeClass("entry-dragging");
});
}
function removeModal() {
$(".entry-modal").remove();
$(".entry-modal-overlay").remove();
}
function cancelAndCloseModal() {
removeModal();
$(".entry-highlight").removeClass("entry-highlight");
if (appState.htmlStack.length) {
const prevHtml = appState.htmlStack[0];
const prevScroll = appState.scrollStack[0];
const $target = $("#mw-content-text");
if ($target.length) {
$target.html(prevHtml);
if (typeof prevScroll === "number") $(window).scrollTop(prevScroll);
// reinitialize collapsibles and gadgets
if (window.mw && mw.loader) {
mw.loader.using(["jquery.makeCollapsible"]).then(function () {
$(".mw-collapsible").makeCollapsible();
if (window.mw && mw.hook) {
mw.hook("wikipage.content").fire($("#mw-content-text"));
}
});
}
}
appState.htmlStack = [];
appState.scrollStack = [];
appState.currentSections = null;
}
}
function renumberHomonyms(model) {
if (!model || !Array.isArray(model.homonyms)) return;
model.homonyms.forEach((h, i) => { h.num = i + 1; });
}
// ---------------------
// Wikitext parsing helpers (preview/save)
// ---------------------
// Extract language sections
function extractLangSections(wikitext) {
const regex = /^==\s*\{\{\s*[^|}\s]+\s*\|\s*(?:1\s*=\s*)?([^|}\s]+).*?\}\}\s*==/gim;
const headings = [];
let match;
while ((match = regex.exec(wikitext)) !== null) {
headings.push({ code: match[1], index: match.index, matchText: match[0], lastMatchIndex: regex.lastIndex });
}
if (headings.length === 0) {
return [ { code: null, heading: null, contentWikitext: wikitext, start: 0, end: wikitext.length } ];
}
const sections = [];
for (let i = 0; i < headings.length; i++) {
const h = headings[i];
const start = h.index;
const contentStart = h.lastMatchIndex;
const contentEnd = (i + 1 < headings.length) ? headings[i + 1].index : wikitext.length;
sections.push({
code: h.code,
heading: h.matchText,
contentWikitext: wikitext.slice(contentStart, contentEnd),
start: start,
contentStart: contentStart,
end: contentEnd
});
}
return sections;
}
// ---------------------
// Get display name for language code (DOM-first, cached)
// ---------------------
function getDisplayNameForCode(code) {
if (!code) return "";
const key = String(code).toLowerCase();
if (languageMap && languageMap[key]) return languageMap[key];
// Fallback if module not yet loaded
return key;
}
// ---------------------
// Rebuild text from sections
// ---------------------
function rebuildFullTextFromSections(_originalText, sections) {
if (!Array.isArray(sections)) return "";
let out = "";
let seenLang = false;
for (const s of sections) {
const h = s.heading || "";
const c = s.contentWikitext || "";
if (h) {
if (seenLang && !out.endsWith("\n\n")) {
out = out.replace(/\n?$/, "\n\n");
}
seenLang = true;
}
out += h + c;
}
return out;
}
// ---------------------
// Helpers that build wikitext blocks
// ---------------------
function buildHomonymBlock(lang, hom) {
let out = "";
// include num= only if there are multiple homonyms or num>1
out += "{{kepala|" + lang + ((hom.num && hom.num > 1) ? "|num=" + hom.num : "") + "}}\n\n";
hom.posGroups.forEach(function (pg) {
out += "{{-" + (pg.pos || "") + "-|" + lang + "}}\n";
pg.defs.forEach(function (d) { if (d && d.trim()) out += "# " + d + "\n"; });
out += "";
});
return out;
}
// ---------------------
// Insert model into sections (DOM-based sort)
// ---------------------
function insertModelIntoSections(sections, model) {
sections = Array.isArray(sections) ? sections.slice() : [];
const incomingCode = (model.lang || "").toString().trim();
const incomingKey = incomingCode.toLowerCase();
// map existing codes to indices
const codeToIndex = {};
for (let i = 0; i < sections.length; i++) {
const s = sections[i];
if (s && s.code) codeToIndex[(s.code || "").toLowerCase()] = i;
}
// build entry text
const entryLines = model.homonyms
.map(h => buildHomonymBlock(model.lang, h))
.filter(Boolean)
.join("\n");
if (incomingKey && codeToIndex.hasOwnProperty(incomingKey)) {
const idx = codeToIndex[incomingKey];
const sec = sections[idx];
const existing = canonicalizeNewlines(sec.contentWikitext || "");
const toAppend = canonicalizeNewlines(entryLines);
// duplicate check
const existingNorm = normalizeForCompare(existing);
const appendNorm = normalizeForCompare(toAppend);
if (!existingNorm.endsWith(appendNorm)) {
let content = normalizeSpacing(existing);
if (content && !content.endsWith("\n")) {
content += "\n";
}
sec.contentWikitext = normalizeSpacing(
content + toAppend + "\n"
);
}
} else {
let newHeading = "=={{bahasa|" + model.lang + "}}==\n";
sections.push({
code: model.lang,
heading: canonicalizeNewlines(newHeading),
contentWikitext: normalizeSpacing(entryLines) + "\n",
start: null,
contentStart: null,
end: null
});
}
// separate non-lang and lang sections
const nonLang = sections.filter(s => !s.code);
const langSections = sections.filter(s => !!s.code);
// sort by display name after mul and site language
// auto-detect site language for sorting priority
const siteLang = (mw.config.get("wgContentLanguage") || "").toLowerCase();
const order = {
mul: 1,
[siteLang]: 2
};
langSections.forEach(s => {
s._codeNorm = (s.code || "").toLowerCase();
});
langSections.sort(function (a, b) {
const aCode = a._codeNorm || "";
const bCode = b._codeNorm || "";
const aRank = order[aCode] || 3;
const bRank = order[bCode] || 3;
if (aRank !== bRank) return aRank - bRank;
const aName = getDisplayNameForCode(a.code || aCode) || aCode;
const bName = getDisplayNameForCode(b.code || bCode) || bCode;
return aName.localeCompare(bName, undefined, { sensitivity: "base" });
});
langSections.forEach(s => { delete s._codeNorm; });
const rebuilt = nonLang.concat(langSections);
return rebuilt.map(s => ({
code: s.code || null,
heading: s.heading || null,
contentWikitext: s.contentWikitext || "",
start: (typeof s.start !== "undefined") ? s.start : null,
end: (typeof s.end !== "undefined") ? s.end : null
}));
}
function replacePageContentWith(parsedHtml) {
const $parsed = $("<div>").html(parsedHtml);
const $target = $("#mw-content-text");
if ($target.length) {
$target.data("savedScrollTop", $(window).scrollTop());
$target.html($parsed);
} else {
$("body").html($parsed);
}
}
function highlightInserted(lang) {
$(".entry-highlight").removeClass("entry-highlight");
const $langEl = $("#mw-content-text").find(".lang[data-code='" + lang + "']").first();
if ($langEl && $langEl.length) {
const $h2 = $langEl.closest("h2");
if ($h2.length) {
const $toHighlight = $h2.nextUntil("h2").addBack();
$toHighlight.addClass("entry-highlight");
const top = $h2.offset() ? $h2.offset().top : null;
if (top) $("html,body").animate({ scrollTop: top - 60 }, 250);
setTimeout(function () { $toHighlight.removeClass("entry-highlight"); }, 7000);
}
}
}
function fetchLatestWikitext() {
return api.get({
action: "query",
prop: "revisions",
rvprop: "content|timestamp",
rvslots: "main",
titles: pageName,
formatversion: 2
}).then(function (data) {
const page = data.query.pages[0];
const fullText = page.revisions && page.revisions[0] && page.revisions[0].slots && page.revisions[0].slots.main
? page.revisions[0].slots.main.content || ""
: "";
const timestamp = page.revisions && page.revisions[0] ? page.revisions[0].timestamp : null;
return { text: fullText, timestamp: timestamp };
});
}
// ---------------------
// Modal + form rendering
// ---------------------
function createEmptyModel() {
return {
lang: "",
homonyms: [ { num: 1, posGroups: [ { pos: "", defs: [""] } ] } ]
};
}
// POS canonical short codes
const POS_CODES = [
"n","v","adj","adv","num","pron","pn","ptcp",
"conj","intj","ptcl","det","art","clf","prep",
"postp","circp","ambip","pref","suf","inf",
"circf","intf","rt","ph","pb","rom",
"ltr","sym"
];
// Render modal from model
function renderModal(model) {
let justAddedFocus = false;
removeModal();
const $overlay = $('<div class="entry-modal-overlay" />');
const $modal = $('<div class="entry-modal" role="dialog" aria-modal="true"></div>');
const $header = $('<div class="entry-header"><div class="entry-title">' + T.modal_title_add + htmlEscape(pageTitle || "") + '</div><div><button class="entry-mini-btn entry-mini-rem" title="' + T.cancel + '">✕</button></div></div>');
const $body = $('<div class="entry-body"></div>');
const $controls = $('<div class="entry-controls"></div>');
$modal.append($header, $body, $controls);
$("body").append($overlay, $modal);
makeDraggable($modal, $header);
$header.find(".entry-mini-rem").on("click", function (e) { e.preventDefault(); cancelAndCloseModal(); });
// Top-level inputs
const $langRow = $(`<div class="entry-block"><div class="row" style="align-items:center;">
<div style="flex:1; display:flex; align-items:center; gap:0.5em;">
<input type="text" id="eg-lang" maxlength="11" style="max-width:3em;" value="${htmlEscape(model.lang || "")}">
<span style="opacity:0.8;">${T.fill_langcode}</span>
</div></div></div>`);
$body.append($langRow);
// Homonyms container
const $homContainer = $('<div id="eg-homonyms"></div>');
$body.append($homContainer);
// For each homonym, render a block
model.homonyms.forEach(function (hom, hIdx) {
const homNum = hom.num || (hIdx + 1);
const $hb = $('<div class="entry-block eg-homonym" data-hidx="' + hIdx + '"></div>');
// hom header: [+] [-] pagename (homonym #) -- pagename is plain bold text from pageTitle
const $hdr = $('<div class="row" style="align-items:center;"></div>');
const $addHomBtn = $('<button class="entry-mini-btn entry-mini-add" title="' + T.add_hom + '">+</button>');
const $rmHomBtn = $('<button class="entry-mini-btn entry-mini-rem" title="' + T.remove_hom + '">–</button>');
const $homTitle = $('<div style="flex:1"><b style="display:inline-block;">' + htmlEscape(pageTitle || "").replace(/_/g, " ") + '</b> <span style="opacity:0.8">(' + homNum + ')</span></div>');
$hdr.append($addHomBtn, $rmHomBtn, $homTitle);
// disable if only one homonym
if (model.homonyms.length <= 1) {
$rmHomBtn.prop("disabled", true);
} else {
$rmHomBtn.prop("disabled", false).css("opacity", "1.0");
}
$hb.append($hdr);
// POS groups container
const $posContainer = $('<div class="eg-pos-container"></div>');
hom.posGroups.forEach(function (pg, pIdx) {
const $pblock = $('<div class="entry-block" data-pidx="' + pIdx + '"></div>');
const $prow = $('<div class="row" style="align-items:center;"></div>');
const $addPosBtn = $('<button class="entry-mini-btn entry-mini-add" title="' + T.add_pos + '">+</button>');
const $rmPosBtn = $('<button class="entry-mini-btn entry-mini-rem" title="' + T.remove_pos + '">–</button>');
// Build POS dropdown
const posLabels = T.pos_labels || {};
const $posSelect = $('<select class="eg-pos"></select>');
$posSelect.append('<option value="">' + T.select_pos + '</option>');
POS_CODES.forEach(code => {
const label = posLabels[code] || code;
const $opt = $('<option></option>')
.val(code)
.text(label + " (" + code + ")");
if (pg.pos === code) $opt.prop("selected", true);
$posSelect.append($opt);
});
const $posInputWrap = $('<div style="flex:1; min-width:10em;"></div>').append($posSelect);
$prow.append($addPosBtn, $rmPosBtn, $posInputWrap);
$pblock.append($prow);
// disable if only one POS in homonym
if (hom.posGroups.length <= 1) {
$rmPosBtn.prop("disabled", true);
} else {
$rmPosBtn.prop("disabled", false).css("opacity", "1.0");
}
// Definitions
const $defsContain = $('<div class="eg-defs"></div>');
pg.defs.forEach(function (d, dIdx) {
const $drow = $('<div class="row" style="align-items:center;"></div>');
const $addDefBtn = $('<button class="entry-mini-btn entry-mini-add" title="' + T.add_def + '">+</button>');
const $rmDefBtn = $('<button class="entry-mini-btn entry-mini-rem" title="' + T.remove_def + '">–</button>');
const $defInput = $('<div style="flex:1"><input type="text" class="eg-def" placeholder="' + T.fill_meaning + '" value="' + htmlEscape(d || "") + '"></div>');
$drow.append($addDefBtn, $rmDefBtn, $defInput);
$defsContain.append($drow);
// disable if only one definition
if (pg.defs.length <= 1) {
$rmDefBtn.prop("disabled", true);
} else {
$rmDefBtn.prop("disabled", false).css("opacity", "1.0");
}
// Def-level events
$addDefBtn.on("click", function (ev) {
ev.preventDefault();
const read = $modal.data("readForm");
if (read) read();
const mdl = $modal.data("model");
mdl.homonyms[hIdx].posGroups[pIdx].defs.splice(dIdx + 1, 0, "");
suppressLangAutofocus = true;
renderModal(mdl);
// Focus new definition input
setTimeout(() => {
const $newDef = $(`.eg-homonym[data-hidx='${hIdx}'] .entry-block[data-pidx='${pIdx}'] .eg-def:eq(${dIdx + 1})`);
if ($newDef.length) justAddedFocus = true;
$newDef.focus();
setTimeout(() => { justAddedFocus = false; }, 200);
}, 100);
});
$rmDefBtn.on("click", function (ev) {
ev.preventDefault();
const read = $modal.data("readForm");
if (read) read();
const mdl = $modal.data("model");
const defsArr = mdl.homonyms[hIdx].posGroups[pIdx].defs;
suppressLangAutofocus = true;
if (defsArr.length > 1) defsArr.splice(dIdx, 1);
else defsArr[0] = "";
renderModal(mdl);
setTimeout(() => {
const $prev = $(`.eg-homonym[data-hidx='${hIdx}'] .entry-block[data-pidx='${pIdx}'] .eg-def:eq(${Math.max(dIdx - 1, 0)})`);
if ($prev.length) $prev.focus();
}, 100);
});
});
// POS-level events
$addPosBtn.on("click", function (ev) {
ev.preventDefault();
const read = $modal.data("readForm");
if (read) read();
const mdl = $modal.data("model");
mdl.homonyms[hIdx].posGroups.splice(pIdx + 1, 0, { pos: "", defs: [""] });
suppressLangAutofocus = true;
renderModal(mdl);
// Focus the new POS dropdown
setTimeout(() => {
const $newPos = $(`.eg-homonym[data-hidx='${hIdx}'] .entry-block[data-pidx='${pIdx + 1}'] .eg-pos`);
if ($newPos.length) justAddedFocus = true;
$newPos.focus();
setTimeout(() => { justAddedFocus = false; }, 200);
}, 100);
});
$rmPosBtn.on("click", function (ev) {
ev.preventDefault();
const read = $modal.data("readForm");
if (read) read();
const mdl = $modal.data("model");
suppressLangAutofocus = true;
if (mdl.homonyms[hIdx].posGroups.length > 1) mdl.homonyms[hIdx].posGroups.splice(pIdx, 1);
else mdl.homonyms[hIdx].posGroups[0] = { pos: "", defs: [""] };
renderModal(mdl);
setTimeout(() => {
const $prev = $(`.eg-homonym[data-hidx='${hIdx}'] .entry-block[data-pidx='${Math.max(pIdx - 1, 0)}'] .eg-pos`).first();
if ($prev.length) $prev.focus();
}, 100);
});
$pblock.append($defsContain);
$posContainer.append($pblock);
});
$hb.append($posContainer);
// Hom-level events
$addHomBtn.on("click", function (ev) {
ev.preventDefault();
const read = $modal.data("readForm");
if (read) read();
const mdl = $modal.data("model");
const insertionIndex = hIdx + 1;
mdl.homonyms.splice(insertionIndex, 0, { num: 0, posGroups: [{ pos: "", defs: [""] }] });
renumberHomonyms(mdl);
suppressLangAutofocus = true;
renderModal(mdl);
// Focus first POS dropdown of new homonym
setTimeout(() => {
const $newPos = $(`.eg-homonym[data-hidx='${insertionIndex}'] .eg-pos`).first();
if ($newPos.length) justAddedFocus = true;
$newPos.focus();
setTimeout(() => { justAddedFocus = false; }, 200);
}, 100);
});
$rmHomBtn.on("click", function (ev) {
ev.preventDefault();
const read = $modal.data("readForm");
if (read) read();
const mdl = $modal.data("model");
suppressLangAutofocus = true;
if (mdl.homonyms.length > 1) {
mdl.homonyms.splice(hIdx, 1);
} else {
mdl.homonyms[0] = { num: 1, posGroups: [ { pos: "", defs: [""] } ] };
}
renumberHomonyms(mdl);
renderModal(mdl);
setTimeout(() => {
const $prev = $(`.eg-homonym[data-hidx='${Math.max(hIdx - 1, 0)}'] .eg-pos`).first();
if ($prev.length) $prev.focus();
}, 100);
});
$homContainer.append($hb);
}); // end homonyms.forEach
// Controls: Preview, Undo, Save, Cancel
const $previewBtn = $('<button class="entry-preview-btn" type="button">' + T.preview + '</button>');
const $undoBtn = $('<button class="entry-undo-btn" type="button" disabled>' + T.undo + '</button>');
const $saveBtn = $('<button class="entry-save-btn" type="button" disabled>' + T.save + '</button>');
const $cancelBtn = $('<button class="entry-cancel-btn" type="button">' + T.cancel + '</button>');
$controls.append($previewBtn, $undoBtn, $saveBtn, $cancelBtn);
$cancelBtn.on("click", function (e) { e.preventDefault(); cancelAndCloseModal(); });
// Read form into model helper
function readFormToModel() {
const mdl = { lang: "", homonyms: [] };
mdl.lang = $modal.find("#eg-lang").val().trim();
$modal.find("#eg-homonyms > .eg-homonym").each(function (hIndex) {
const $hb = $(this);
const homModel = { num: hIndex + 1, posGroups: [] };
$hb.find(".eg-pos-container > .entry-block").each(function (pIndex) {
const $pblock = $(this);
const pos = $pblock.find(".eg-pos").val().trim();
const defs = [];
$pblock.find(".eg-def").each(function () { defs.push($(this).val().trim()); });
homModel.posGroups.push({ pos: pos, defs: defs.length ? defs : [""] });
});
if (homModel.posGroups.length === 0) homModel.posGroups = [ { pos: "", defs: [""] } ];
mdl.homonyms.push(homModel);
});
renumberHomonyms(mdl);
// update modal-stored model so event handlers can use current state
$modal.data("model", mdl);
return mdl;
}
$modal.data("readForm", readFormToModel);
$modal.data("model", model);
// Preview handler
$previewBtn.on("click", function () {
const read = $modal.data("readForm");
if (read) read();
const mdl = $modal.data("model");
if (!mdl.lang) { alert(T.prompt_fill_langcode); return; }
// check at least one pos+definition non-empty
// validation: all visible inputs must be filled
let allFilled = true;
mdl.homonyms.forEach(h => {
h.posGroups.forEach(pg => {
const posFilled = pg.pos && pg.pos.trim().length > 0;
const defsFilled = pg.defs.length > 0 && pg.defs.every(d => d && d.trim().length > 0);
if (!posFilled || !defsFilled) allFilled = false;
});
});
if (!allFilled) {
alert(T.err_fill_all);
return;
}
$previewBtn.prop("disabled", true).text(T.preview_loading);
fetchLatestWikitext().then(function (res) {
const sections = extractLangSections(res.text);
// if language already exists, warn user (since edit-data flow removed)
const existingIdx = sections.findIndex(s => s.code && s.code.toLowerCase() === mdl.lang.toLowerCase());
if (existingIdx >= 0) {
alert(T.err_lang_exists);
$previewBtn.prop("disabled", false).text(T.preview);
return;
}
// push snapshot for undo
appState.htmlStack.push($("#mw-content-text").html() || "");
appState.scrollStack.push($(window).scrollTop());
appState.lastFetchedTimestamp = res.timestamp;
fetchLanguageMap().then(() => {
const updatedSections = insertModelIntoSections(sections, mdl);
appState.currentSections = updatedSections;
appState.newLang = mdl.lang;
const newFull = rebuildFullTextFromSections(res.text, updatedSections);
api.post({
action: "parse",
title: pageName,
text: newFull,
contentmodel: "wikitext",
prop: "text",
format: "json"
}).done(function (resp) {
const html = (resp && resp.parse && resp.parse.text)
? resp.parse.text["*"] || ""
: "";
replacePageContentWith(html);
highlightInserted(mdl.lang);
$undoBtn.prop("disabled", false).css("opacity", "1.0");
$saveBtn.prop("disabled", false).css("opacity", "1.0");
$previewBtn.prop("disabled", false).text(T.preview);
}).fail(function () {
alert(T.err_preview);
$previewBtn.prop("disabled", false).text(T.preview);
});
});
}).catch(function () {
alert(T.err_fetch);
$previewBtn.prop("disabled", false).text(T.preview);
});
});
// Undo handler (Balikin)
$undoBtn.on("click", function () {
if (!appState.htmlStack.length) {
$undoBtn.prop("disabled", true);
$saveBtn.prop("disabled", true);
return;
}
const prevHtml = appState.htmlStack.pop();
const prevScroll = appState.scrollStack.pop();
const $target = $("#mw-content-text");
if ($target.length) {
$target.html(prevHtml);
if (typeof prevScroll === "number") $(window).scrollTop(prevScroll);
}
if (appState.htmlStack.length === 0) {
$undoBtn.prop("disabled", true);
$saveBtn.prop("disabled", true);
} else {
$undoBtn.prop("disabled", false).css("opacity", "1.0");
$saveBtn.prop("disabled", false).css("opacity", "1.0");
}
});
// Save handler
$saveBtn.on("click", function () {
const read = $modal.data("readForm");
if (read) read();
if (!appState.currentSections) {
alert(T.err_no_change);
return;
}
$saveBtn.prop("disabled", true).text(T.save_loading);
fetchLatestWikitext().then(function (res) {
const latestText = res.text;
const latestTimestamp = res.timestamp;
// Rebuild final full text from appState.currentSections but apply to latest text
const newText = normalizeSpacing(
rebuildFullTextFromSections(latestText, appState.currentSections)
);
const lang = appState.newLang || "";
const summary = lang ? `${T.save_summary_prefix} [${lang}]` : T.save_summary_prefix;
api.postWithToken("csrf", {
action: "edit",
title: pageName,
text: newText,
summary: summary,
minor: true,
basetimestamp: latestTimestamp
}).done(function () {
location.reload();
}).fail(function (err) {
const info = err && err.error && err.error.info ? err.error.info : String(err);
alert(T.err_save_prefix + " " + info);
$saveBtn.prop("disabled", false).text(T.save);
});
}).catch(function () {
alert(T.err_fetch);
$saveBtn.prop("disabled", false).text(T.save);
});
});
// Focus language input only once when modal first opens
if (!appState.modalEverOpened) {
setTimeout(() => $modal.find("#eg-lang").focus(), 25);
appState.modalEverOpened = true;
}
// ---------------------
// Keyboard accessibility & shortcuts
// ---------------------
(function setupKeyboard() {
const focusables = () =>
$modal.find('input[type="text"], select').filter(':visible');
function focusNext(current, dir) {
const f = focusables().toArray();
const idx = f.indexOf(current);
if (idx === -1) return;
const next = f[idx + dir];
if (next) next.focus();
}
$modal.on("keydown", function (e) {
const target = e.target;
// Ctrl+Enter => Save
if (e.ctrlKey && e.key === "Enter") {
e.preventDefault();
$modal.find(".entry-save-btn").trigger("click");
return;
}
// Enter in definition input => trigger Preview
if (
e.key === "Enter" &&
$(target).hasClass("eg-def") &&
!e.ctrlKey &&
!e.shiftKey
) {
e.preventDefault();
$modal.find(".entry-preview-btn").trigger("click");
return;
}
// Shift+ArrowDown in definition input => add new definition below
if (e.key === "ArrowDown" && e.shiftKey && $(target).hasClass("eg-def")) {
e.preventDefault();
const $row = $(target).closest(".row");
const hidx = $row.closest(".eg-homonym").data("hidx");
const pidx = $row.closest(".entry-block").data("pidx");
const didx = $row.closest('.eg-defs').find('.row').index($row);
const read = $modal.data("readForm");
if (read) read();
const mdl = $modal.data("model");
suppressLangAutofocus = true;
mdl.homonyms[hidx].posGroups[pidx].defs.splice(didx + 1, 0, "");
renderModal(mdl);
setTimeout(() => {
const newDef = $(
`.eg-homonym[data-hidx='${hidx}'] .entry-block[data-pidx='${pidx}'] .eg-def:eq(${didx + 1})`
).get(0);
if (newDef) {
justAddedFocus = true;
newDef.focus();
setTimeout(() => { justAddedFocus = false; }, 250);
}
}, 100);
return;
}
// Shift+ArrowUp on blank definition line => remove it
if (e.key === "ArrowUp" && e.shiftKey && $(target).hasClass("eg-def")) {
const value = $(target).val().trim();
if (value === "") {
e.preventDefault();
const $row = $(target).closest(".row");
const hidx = $row.closest(".eg-homonym").data("hidx");
const pidx = $row.closest(".entry-block").data("pidx");
const didx = $row.closest('.eg-defs').find('.row').index($row);
const read = $modal.data("readForm");
if (read) read();
const mdl = $modal.data("model");
const defs = mdl.homonyms[hidx].posGroups[pidx].defs;
suppressLangAutofocus = true;
if (defs.length > 1) defs.splice(didx, 1);
else defs[0] = "";
renderModal(mdl);
setTimeout(() => {
const prev = $(
`.eg-homonym[data-hidx='${hidx}'] .entry-block[data-pidx='${pidx}'] .eg-def:eq(${Math.max(didx - 1, 0)})`
).get(0);
if (prev) {
justAddedFocus = true;
prev.focus();
setTimeout(() => { justAddedFocus = false; }, 250);
}
}, 100);
return;
}
}
// Plain ArrowDown/Up navigation between inputs
if ((e.key === "ArrowDown" || e.key === "ArrowUp") && !e.shiftKey && !justAddedFocus) {
e.preventDefault();
focusNext(target, e.key === "ArrowDown" ? +1 : -1);
return;
}
});
})();
return $modal;
}
// Modal openers
function openModalForNew() {
const model = createEmptyModel();
renderModal(model);
}
// Expose debug hooks if needed
window.EntryAdderGadget = {
openNew: openModalForNew,
removeModal: removeModal
};
});
k6qj1f8e8czyj3n0n8ora729jsi4md4
Kategori:jv:Slang balik
14
352912
1463339
2026-07-11T11:42:43Z
Swarabakti
18192
←Membuat halaman berisi '{{kategori otomatis}}'
1463339
wikitext
text/x-wiki
{{kategori otomatis}}
tka0fgn87px6unlh07hbwnzmlebon2d