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 &quot;{{SUBPAGENAME}}&quot; 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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;"); } 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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;"); } 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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;"); } 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